Perspective Virtual Camera System | |
Licensed under the MIT license, which basically means you can do anything you want with it. |
------------------------------------------------------------------------------- | |
-- Perspective Demo | |
-------------------------------------------------------------------------------- | |
display.setStatusBar(display.HiddenStatusBar) | |
-------------------------------------------------------------------------------- | |
-- Localize | |
-------------------------------------------------------------------------------- | |
local require = require | |
local perspective = require("perspective") | |
local function forcesByAngle(totalForce, angle) local forces = {} local radians = -math.rad(angle) forces.x = math.cos(radians) * totalForce forces.y = math.sin(radians) * totalForce return forces end | |
-------------------------------------------------------------------------------- | |
-- Build Camera | |
-------------------------------------------------------------------------------- | |
local camera = perspective.createView() | |
-------------------------------------------------------------------------------- | |
-- Build Player | |
-------------------------------------------------------------------------------- | |
local player = display.newPolygon(0, 0, {-50,-30, -50,30, 50,0}) | |
player.strokeWidth = 6 | |
player:setFillColor(0, 0, 0, 0) | |
player.anchorX = 0.2 -- Slightly more "realistic" than center-point rotating | |
-- Some various movement parameters | |
player.angularVelocity = 0 -- Speed at which player rotates | |
player.angularAcceleration = 1.05 -- Angular acceleration rate | |
player.angularDamping = 0.9 -- Angular damping rate | |
player.angularMax = 10 -- Max angular velocity | |
player.moveSpeed = 0 -- Current movement speed | |
player.linearDamping = 0 -- Linear damping rate | |
player.linearAcceleration = 1.05 -- Linear acceleration rate | |
player.linearMax = 10 -- Max linear velocity | |
camera:add(player, 1) -- Add player to layer 1 of the camera | |
camera:prependLayer() | |
-------------------------------------------------------------------------------- | |
-- "Scenery" | |
-------------------------------------------------------------------------------- | |
local scene = {} | |
for i = 1, 100 do | |
scene[i] = display.newCircle(0, 0, 10) | |
scene[i].x = math.random(display.screenOriginX, display.contentWidth * 3) | |
scene[i].y = math.random(display.screenOriginY, display.contentHeight) | |
scene[i]:setFillColor(math.random(100) * 0.01, math.random(100) * 0.01, math.random(100) * 0.01) | |
camera:add(scene[i], math.random(0, camera:layerCount())) | |
end | |
camera:setParallax(1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3) -- Here we set parallax for each layer in descending order | |
-------------------------------------------------------------------------------- | |
-- Movement Buttons | |
-------------------------------------------------------------------------------- | |
local m = {} | |
m.result = "none" | |
m.rotate = {} | |
m.rotate.left = display.newRect(0, 0, 60, 60) | |
m.rotate.left.x = display.screenOriginX + m.rotate.left.contentWidth + 10 | |
m.rotate.left.y = display.contentHeight - m.rotate.left.contentHeight - 10 | |
m.rotate.left.result = "rotate:left" | |
m.rotate.right = display.newRect(0, 0, 60, 60) | |
m.rotate.right.x = display.contentWidth - display.screenOriginX - m.rotate.right.contentWidth - 10 | |
m.rotate.right.y = display.contentHeight - m.rotate.right.contentHeight - 10 | |
m.rotate.right.result = "rotate:right" | |
m.forward = display.newRect(0, 0, display.contentWidth * 0.75, 60) | |
m.forward.x = display.contentCenterX | |
m.forward.y = display.contentHeight - m.forward.contentHeight - 10 | |
m.forward.result = "move" | |
-------------------------------------------------------------------------------- | |
-- Touch Movement Buttons | |
-------------------------------------------------------------------------------- | |
function m.touch(event) | |
local t = event.target | |
if "began" == event.phase then | |
display.getCurrentStage():setFocus(t) | |
t.isFocus = true | |
m.result = t.result | |
if t.result == "rotate:left" then | |
player.angularVelocity = -2 | |
elseif t.result == "rotate:right" then | |
player.angularVelocity = 2 | |
elseif t.result == "move" then | |
player.moveSpeed = 2 | |
end | |
elseif t.isFocus then | |
if "moved" == event.phase then | |
elseif "ended" == event.phase then | |
display.getCurrentStage():setFocus(nil) | |
t.isFocus = false | |
m.result = "none" | |
end | |
end | |
end | |
m.rotate.left:addEventListener("touch", m.touch) | |
m.rotate.right:addEventListener("touch", m.touch) | |
m.forward:addEventListener("touch", m.touch) | |
-------------------------------------------------------------------------------- | |
-- Runtime Loop | |
-------------------------------------------------------------------------------- | |
local function enterFrame(event) | |
if m.result == "rotate:left" then | |
player.angularVelocity = player.angularVelocity * player.angularAcceleration | |
player.angularVelocity = math.max(player.angularVelocity, -player.angularMax) | |
player.moveSpeed = player.moveSpeed * player.linearDamping | |
elseif m.result == "rotate:right" then | |
player.angularVelocity = player.angularVelocity * player.angularAcceleration | |
player.angularVelocity = math.min(player.angularVelocity, player.angularMax) | |
player.moveSpeed = player.moveSpeed * player.linearDamping | |
elseif m.result == "move" then | |
player.moveSpeed = player.moveSpeed * player.linearAcceleration | |
player.moveSpeed = math.min(player.moveSpeed, player.linearMax) | |
player.angularVelocity = player.angularVelocity * player.angularDamping | |
elseif m.result == "none" then | |
player.angularVelocity = player.angularVelocity * player.angularDamping | |
player.moveSpeed = player.moveSpeed * player.linearDamping | |
end | |
local forces = forcesByAngle(player.moveSpeed, 360 - player.rotation) | |
player:translate(forces.x, forces.y) | |
player:rotate(player.angularVelocity) | |
end | |
-------------------------------------------------------------------------------- | |
-- Add Listeners | |
-------------------------------------------------------------------------------- | |
Runtime:addEventListener("enterFrame", enterFrame) | |
camera.damping = 10 -- A bit more fluid tracking | |
camera:setFocus(player) -- Set the focus to the player | |
camera:track() -- Begin auto-tracking |
--[[ | |
Perspective v2.1.0 | |
A library for easily and smoothly integrating a virtual camera into your game. | |
Based on modified version of the Dusk camera system. | |
v2.1.0 allows you to prepend layers in front, starting with index 0, and fixes | |
offset behavior. | |
Perspective is licensed under the MIT license. Enjoy. | |
--]] | |
local lib_perspective = {} | |
-------------------------------------------------------------------------------- | |
-- Localize | |
-------------------------------------------------------------------------------- | |
local display_newGroup = display.newGroup | |
local display_remove = display.remove | |
local type = type | |
local table_insert = table.insert | |
local math_huge = math.huge | |
local math_nhuge = -math.huge | |
local clamp = function(v, l, h) return (v < l and l) or (v > h and h) or v end | |
-------------------------------------------------------------------------------- | |
-- Create View | |
-------------------------------------------------------------------------------- | |
lib_perspective.createView = function(layerCount) | |
------------------------------------------------------------------------------ | |
-- Create view, internal object, and layers | |
------------------------------------------------------------------------------ | |
local view = display_newGroup() | |
view.damping = 1 | |
view.snapWhenFocused = true -- Do we instantly snap to the object when :setFocus() is called? | |
local isTracking | |
local prependedLayers = 0 | |
local internal -- So we can access it from inside the declaration | |
internal = { | |
trackingLevel = 1, | |
damping = 1, | |
scaleBoundsToScreen = true, | |
xScale = 1, | |
yScale = 1, | |
xOffset = 0, | |
yOffset = 0, | |
addX = display.contentCenterX, | |
addY = display.contentCenterY, | |
bounds = { | |
xMin = math_nhuge, | |
xMax = math_huge, | |
yMin = math_nhuge, | |
yMax = math_huge | |
}, | |
scaledBounds = { | |
xMin = math_nhuge, | |
xMax = math_huge, | |
yMin = math_nhuge, | |
yMax = math_huge | |
}, | |
trackFocus = true, | |
focus = nil, | |
viewX = 0, | |
viewY = 0, | |
getViewXY = function() if internal.focus then return internal.focus.x, internal.focus.y else return internal.viewX, internal.viewY end end, | |
layer = {}, | |
updateAddXY = function() internal.addX = display.contentCenterX / view.xScale internal.addY = display.contentCenterY / view.yScale end | |
} | |
local layers = {} | |
------------------------------------------------------------------------------ | |
------------------------------------------------------------------------------ | |
-- Internal Methods | |
------------------------------------------------------------------------------ | |
------------------------------------------------------------------------------ | |
------------------------------------------------------------------------------ | |
-- Scale Bounds | |
------------------------------------------------------------------------------ | |
internal.scaleBounds = function(doX, doY) | |
if internal.scaleBoundsToScreen then | |
local xMin = internal.bounds.xMin | |
local xMax = internal.bounds.xMax | |
local yMin = internal.bounds.yMin | |
local yMax = internal.bounds.yMax | |
local doX = doX and not ((xMin == math_nhuge) or (xMax == math_huge)) | |
local doY = doY and not ((yMin == math_nhuge) or (yMax == math_huge)) | |
if doX then | |
local scaled_xMin = xMin / view.xScale | |
local scaled_xMax = xMax - (scaled_xMin - xMin) | |
if scaled_xMax < scaled_xMin then | |
local hopDist = scaled_xMin - scaled_xMax | |
local halfDist = hopDist * 0.5 | |
scaled_xMax = scaled_xMax + halfDist | |
scaled_xMin = scaled_xMin - halfDist | |
end | |
internal.scaledBounds.xMin = scaled_xMin | |
internal.scaledBounds.xMax = scaled_xMax | |
end | |
if doY then | |
local scaled_yMin = yMin / view.yScale | |
local scaled_yMax = yMax - (scaled_yMin - yMin) | |
if scaled_yMax < scaled_yMin then | |
local hopDist = scaled_yMin - scaled_yMax | |
local halfDist = hopDist * 0.5 | |
scaled_yMax = scaled_yMax + halfDist | |
scaled_yMin = scaled_yMin - halfDist | |
end | |
internal.scaledBounds.yMin = scaled_yMin | |
internal.scaledBounds.yMax = scaled_yMax | |
end | |
else | |
camera.scaledBounds.xMin, camera.scaledBounds.xMax, camera.scaledBounds.yMin, camera.scaledBounds.yMax = camera.bounds.xMin, camera.bounds.xMax, camera.bounds.yMin, camera.bounds.yMax | |
end | |
end | |
------------------------------------------------------------------------------ | |
-- Process Viewpoint | |
------------------------------------------------------------------------------ | |
internal.processViewpoint = function() | |
if internal.damping ~= view.damping then internal.trackingLevel = 1 / view.damping internal.damping = view.damping end | |
if internal.trackFocus then | |
local x, y = internal.getViewXY() | |
if view.xScale ~= internal.xScale or view.yScale ~= internal.yScale then internal.updateAddXY() end | |
if view.xScale ~= internal.xScale then internal.xScale = view.xScale internal.scaleBounds(true, false) end | |
if view.yScale ~= internal.yScale then internal.yScale = view.yScale internal.scaleBounds(false, true) end | |
x = clamp(x, internal.scaledBounds.xMin, internal.scaledBounds.xMax) | |
y = clamp(y, internal.scaledBounds.yMin, internal.scaledBounds.yMax) | |
internal.viewX, internal.viewY = x, y | |
end | |
end | |
------------------------------------------------------------------------------ | |
------------------------------------------------------------------------------ | |
-- Public Methods | |
------------------------------------------------------------------------------ | |
------------------------------------------------------------------------------ | |
------------------------------------------------------------------------------ | |
-- Append Layer | |
------------------------------------------------------------------------------ | |
view.appendLayer = function() | |
local layer = display_newGroup() | |
layer.xParallax, layer.yParallax = 1, 1 | |
view:insert(layer) | |
layer:toBack() | |
table_insert(layers, layer) | |
layer._perspectiveIndex = #layers | |
internal.layer[#layers] = { | |
x = 0, | |
y = 0, | |
xOffset = 0, | |
yOffset = 0 | |
} | |
function layer:setCameraOffset(x, y) internal.layer[layer._perspectiveIndex].xOffset, internal.layer[layer._perspectiveIndex].yOffset = x, y end | |
end | |
view.prependLayer = function() | |
view.appendLayer() | |
layers[#layers]:toFront() | |
layers[-prependedLayers] = layers[#layers] | |
internal.layer[-prependedLayers] = internal.layer[#internal.layer] | |
table.remove(layers, #layers) | |
table.remove(internal.layer, #internal.layer) | |
layers[-prependedLayers]._perspectiveIndex = -prependedLayers | |
prependedLayers = prependedLayers + 1 | |
end | |
------------------------------------------------------------------------------ | |
-- Add an Object to the Camera | |
------------------------------------------------------------------------------ | |
function view:add(obj, l, isFocus) | |
local l = l or 4 | |
layers[l]:insert(obj) | |
obj._perspectiveLayer = l | |
if isFocus then view:setFocus(obj) end | |
-- Move an object to a layer | |
function obj:toLayer(newLayer) if layer[newLayer] then layer[newLayer]:insert(obj) obj._perspectiveLayer = newLayer end end | |
--Move an object back a layer | |
function obj:back() if layer[obj._perspectiveLayer + 1] then layer[obj._perspectiveLayer + 1]:insert(obj) obj._perspectiveLayer = obj.layer + 1 end end | |
--Moves an object forwards a layer | |
function obj:forward() if layer[obj._perspectiveLayer - 1] then layer[obj._perspectiveLayer - 1]:insert(obj) obj._perspectiveLayer = obj.layer - 1 end end | |
--Moves an object to the very front of the camera | |
function obj:toCameraFront() layer[1]:insert(obj) obj._perspectiveLayer = 1 obj:toFront() end | |
--Moves an object to the very back of the camera | |
function obj:toCameraBack() layer[#layers]:insert(obj) obj._perspectiveLayer = #layers obj:toBack() end | |
end | |
------------------------------------------------------------------------------ | |
-- Main Tracking Function | |
------------------------------------------------------------------------------ | |
function view:trackFocus() | |
internal.processViewpoint() | |
local viewX, viewY = internal.viewX, internal.viewY | |
layers[1].xParallax, layers[1].yParallax = 1, 1 | |
for i = -prependedLayers + 1, #layers do | |
local addX, addY = internal.addX, internal.addY | |
local layerX, layerY = internal.layer[i].x, internal.layer[i].y | |
local diffX = (-viewX - layerX) | |
local diffY = (-viewY - layerY) | |
local incrX = diffX | |
local incrY = diffY | |
internal.layer[i].x = layerX + incrX + internal.layer[i].xOffset + internal.xOffset | |
internal.layer[i].y = layerY + incrY + internal.layer[i].yOffset + internal.yOffset | |
layers[i].x = (layers[i].x - (layers[i].x - (internal.layer[i].x + addX) * layers[i].xParallax) * internal.trackingLevel) | |
layers[i].y = (layers[i].y - (layers[i].y - (internal.layer[i].y + addY) * layers[i].yParallax) * internal.trackingLevel) | |
end | |
view.scrollX, view.scrollY = layers[1].x, layers[1].y | |
end | |
------------------------------------------------------------------------------ | |
-- Set the Camera Bounds | |
------------------------------------------------------------------------------ | |
function view:setBounds(x1, x2, y1, y2) | |
local xMin, xMax, yMin, yMax | |
if x1 ~= nil then if not x1 then xMin = math_nhuge else xMin = x1 end end | |
if x2 ~= nil then if not x2 then xMax = math_huge else xMax = x2 end end | |
if y1 ~= nil then if not y1 then yMin = math_nhuge else yMin = y1 end end | |
if y2 ~= nil then if not y2 then yMax = math_huge else yMax = y2 end end | |
internal.bounds.xMin = xMin | |
internal.bounds.xMax = xMax | |
internal.bounds.yMin = yMin | |
internal.bounds.yMax = yMax | |
internal.scaleBounds(true, true) | |
end | |
------------------------------------------------------------------------------ | |
-- Miscellaneous Functions | |
------------------------------------------------------------------------------ | |
-- Begin auto-tracking | |
function view:track() if not isTracking then Runtime:addEventListener("enterFrame", view.trackFocus) isTracking = true end end | |
-- Stop auto-tracking | |
function view:cancel() if isTracking then Runtime:removeEventListener("enterFrame", view.trackFocus) isTracking = false end end | |
-- Remove an object from the view | |
function view:remove(obj) if obj and obj._perspectiveLayer then layers[obj._perspectiveLayer]:remove(obj) end end | |
-- Set the view's focus | |
function view:setFocus(obj) if obj then internal.focus = obj end if view.snapWhenFocused then view.snap() end end | |
-- Snap the view to the focus point | |
function view:snap() local t = internal.trackingLevel local d = internal.damping internal.trackingLevel = 1 internal.damping = view.damping view:trackFocus() internal.trackingLevel = t internal.damping = d end | |
-- Move the view to a point | |
function view:toPoint(x, y) view:cancel() local newFocus = {x = x, y = y} view:setFocus(newFocus) view:track() return newFocus end | |
-- Get a layer of the view | |
function view:layer(n) return layers[n] end | |
-- Get the layer container of the view | |
function view:layers() return layers end | |
-- Destroy the view | |
function view:destroy() view:cancel() for i = 1, #layers do display_remove(layers[i]) end display_remove(view) view = nil return true end | |
-- Set layer parallax | |
function view:setParallax(...) for i = 1, #arg do if type(arg[i]) == "table" then layers[i].xParallax, layers[i].yParallax = arg[i][1], arg[i][2] else layers[i].xParallax, layers[i].yParallax = arg[i], arg[i] end end end | |
-- Get number of layers | |
function view:layerCount() return #layers end | |
-- Set the view's master offset | |
function view:setMasterOffset(x, y) internal.xOffset, internal.yOffset = x, y end | |
------------------------------------------------------------------------------ | |
-- Build Layers | |
------------------------------------------------------------------------------ | |
for i = layerCount or 8, 1, -1 do view.appendLayer() end | |
return view | |
end | |
return lib_perspective |
local perspective = require("perspective") -- Include the library | |
local camera = perspective.createView([numLayers]) -- Optional parameter is the number of layers | |
camera:appendLayer() -- Add a new layer to the back of the camera | |
camera:prependLayer() -- Add a new layer to the front of the camera indexed with 0, -1, -2, etc. | |
camera:add(obj, layer, [isFocus]) -- obj is any display object; layer is the layer to add the object to (lower numbers = front of camera); isFocus is a convenience value for whether to initially set the focus to this object | |
camera:trackFocus() -- "Tick" the camera once | |
camera:setBounds(x1, x2, y1, y2) -- Set the bounding box that tracking is confined to. Any values that evaluate to Boolean negative are interpreted as infinite; infinite values apply to an entire axis (if x2 is infinite and x1 is not, X-axis constraint will be disabled) | |
camera:track() -- Begin auto-tracking. This eliminates the need to update the camera every frame | |
camera:cancel() -- Stop auto-tracking | |
camera:remove(obj) -- Remove an object from the camera | |
camera:setFocus(obj) -- Set the camera focus to an object | |
camera:snap() -- Snap the camera to the focus point (ignores damping) | |
camera:toPoint(x, y) -- Change the camera's focus to an X,Y point | |
camera:layer(n) -- Get a layer object of the camera | |
camera:layers() -- Get the array holding each layer | |
camera:destroy() -- Destroy the camera and clear memory | |
camera:setParallax(...) -- Set parallax quickly for multiple layers. Each value provided will apply to the correspondingly indexed layer. Provide a table with {x, y} values for an argument to set X and Y parallax independently | |
camera:layerCount() -- Get the number of layers in the camera | |
camera:setMasterOffset(x, y) -- Set a camera-wide offset (for layer offset, use layer:setCameraOffset()) | |
---------------------- | |
camera.damping = n -- "Fluidity" the camera implements with tracking. Higher values will make the camera move more slowly; values approaching 1 will make the camera move more rigidly | |
layer.xParallax = n -- X-parallax ratio of a layer; expressed as fraction of "normal" movement | |
layer.yParallax = n -- Y-parallax ratio of a layer; expressed as fraction of "normal" movement | |
layer:setCameraOffset(x, y) -- Set an offset for the layer's tracking |
-------------------------------------------------------------------------------- | |
-- Perspective Demo | |
-------------------------------------------------------------------------------- | |
display.setStatusBar(display.HiddenStatusBar) | |
-------------------------------------------------------------------------------- | |
-- Localize | |
-------------------------------------------------------------------------------- | |
local require = require | |
local perspective = require("perspective") | |
local function forcesByAngle(totalForce, angle) local forces = {} local radians = -math.rad(angle) forces.x = math.cos(radians) * totalForce forces.y = math.sin(radians) * totalForce return forces end | |
-------------------------------------------------------------------------------- | |
-- Build Camera | |
-------------------------------------------------------------------------------- | |
local camera = perspective.createView() | |
-------------------------------------------------------------------------------- | |
-- Build Player | |
-------------------------------------------------------------------------------- | |
local player = display.newPolygon(0, 0, {-50,-30, -50,30, 50,0}) | |
player.strokeWidth = 6 | |
player:setFillColor(0, 0, 0, 0) | |
player.anchorX = 0.2 -- Slightly more "realistic" than center-point rotating | |
-- Some various movement parameters | |
player.angularVelocity = 0 -- Speed at which player rotates | |
player.angularAcceleration = 1.05 -- Angular acceleration rate | |
player.angularDamping = 0.9 -- Angular damping rate | |
player.angularMax = 10 -- Max angular velocity | |
player.moveSpeed = 0 -- Current movement speed | |
player.linearDamping = 0 -- Linear damping rate | |
player.linearAcceleration = 1.05 -- Linear acceleration rate | |
player.linearMax = 10 -- Max linear velocity | |
camera:add(player, 1) -- Add player to layer 1 of the camera | |
-------------------------------------------------------------------------------- | |
-- "Scenery" | |
-------------------------------------------------------------------------------- | |
local scene = {} | |
for i = 1, 100 do | |
scene[i] = display.newCircle(0, 0, 10) | |
scene[i].x = math.random(display.screenOriginX, display.contentWidth * 3) | |
scene[i].y = math.random(display.screenOriginY, display.contentHeight) | |
scene[i]:setFillColor(math.random(100) * 0.01, math.random(100) * 0.01, math.random(100) * 0.01) | |
camera:add(scene[i], math.random(2, camera:layerCount())) | |
end | |
camera:setParallax(1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3) -- Here we set parallax for each layer in descending order | |
-------------------------------------------------------------------------------- | |
-- Movement Buttons | |
-------------------------------------------------------------------------------- | |
local m = {} | |
m.result = "none" | |
m.rotate = {} | |
m.rotate.left = display.newRect(0, 0, 60, 60) | |
m.rotate.left.x = display.screenOriginX + m.rotate.left.contentWidth + 10 | |
m.rotate.left.y = display.contentHeight - m.rotate.left.contentHeight - 10 | |
m.rotate.left.result = "rotate:left" | |
m.rotate.right = display.newRect(0, 0, 60, 60) | |
m.rotate.right.x = display.contentWidth - display.screenOriginX - m.rotate.right.contentWidth - 10 | |
m.rotate.right.y = display.contentHeight - m.rotate.right.contentHeight - 10 | |
m.rotate.right.result = "rotate:right" | |
m.forward = display.newRect(0, 0, display.contentWidth * 0.75, 60) | |
m.forward.x = display.contentCenterX | |
m.forward.y = display.contentHeight - m.forward.contentHeight - 10 | |
m.forward.result = "move" | |
-------------------------------------------------------------------------------- | |
-- Touch Movement Buttons | |
-------------------------------------------------------------------------------- | |
function m.touch(event) | |
local t = event.target | |
if "began" == event.phase then | |
display.getCurrentStage():setFocus(t) | |
t.isFocus = true | |
m.result = t.result | |
if t.result == "rotate:left" then | |
player.angularVelocity = -2 | |
elseif t.result == "rotate:right" then | |
player.angularVelocity = 2 | |
elseif t.result == "move" then | |
player.moveSpeed = 2 | |
end | |
elseif t.isFocus then | |
if "moved" == event.phase then | |
elseif "ended" == event.phase then | |
display.getCurrentStage():setFocus(nil) | |
t.isFocus = false | |
m.result = "none" | |
end | |
end | |
end | |
m.rotate.left:addEventListener("touch", m.touch) | |
m.rotate.right:addEventListener("touch", m.touch) | |
m.forward:addEventListener("touch", m.touch) | |
-------------------------------------------------------------------------------- | |
-- Runtime Loop | |
-------------------------------------------------------------------------------- | |
local function enterFrame(event) | |
if m.result == "rotate:left" then | |
player.angularVelocity = player.angularVelocity * player.angularAcceleration | |
player.angularVelocity = math.max(player.angularVelocity, -player.angularMax) | |
player.moveSpeed = player.moveSpeed * player.linearDamping | |
elseif m.result == "rotate:right" then | |
player.angularVelocity = player.angularVelocity * player.angularAcceleration | |
player.angularVelocity = math.min(player.angularVelocity, player.angularMax) | |
player.moveSpeed = player.moveSpeed * player.linearDamping | |
elseif m.result == "move" then | |
player.moveSpeed = player.moveSpeed * player.linearAcceleration | |
player.moveSpeed = math.min(player.moveSpeed, player.linearMax) | |
player.angularVelocity = player.angularVelocity * player.angularDamping | |
elseif m.result == "none" then | |
player.angularVelocity = player.angularVelocity * player.angularDamping | |
player.moveSpeed = player.moveSpeed * player.linearDamping | |
end | |
local forces = forcesByAngle(player.moveSpeed, 360 - player.rotation) | |
player:translate(forces.x, forces.y) | |
player:rotate(player.angularVelocity) | |
end | |
-------------------------------------------------------------------------------- | |
-- Add Listeners | |
-------------------------------------------------------------------------------- | |
Runtime:addEventListener("enterFrame", enterFrame) | |
camera.damping = 10 -- A bit more fluid tracking | |
camera:setFocus(player) -- Set the focus to the player | |
camera:track() -- Begin auto-tracking |
hi Caleb,
i'm a beginner and i have a question ;
Is the position of objects change?
or is it really the camera that moves around the object?
Because at the moment I'm working on a grid and it seems that the camera causes a shift of the coordinates of my grid.
Can you tell me more?
thank you
Hi! Sorry for not posting sooner, I don't get notifications for this :)
@kadlugan: Please open a new forum topic for your scaling issue.
@espace3d: The position of the objects does not change. When you add an object to the camera, it really just inserts it into a group. Then, Perspective just moves the camera group. You're likely getting grid coordinate shift because the group position is changing. You can get an object's "real" position (relative to the screen, not its parent group) with object:localToContent(0, 0)
, or you can go the other way and convert a screen coordinate to an object coordinate with object:contentToLocal(0, 0)
.
Hi @GymbylCoding,
I want to use parallax background with repeated scrollX or scrollY (like JungleScene in corona sdk example folder, where grass.png is looping). Can you add this feature into Perspective? By the way, your lib is awesome!
How to organize layers from array of display objects, for best future?)
Like foreground, background and etc...
How would i get the position of an object inside the parallax camera layer. I need to raycast from an object but when i do it from the objects x,y positions it returns the nonparalaxed position.
Never mind I figured it out. If you would like the code that I added here it is.
function view:add(obj, l, isFocus)
local l = l or 4
layers[l]:insert(obj)
obj._perspectiveLayer = l
if isFocus then view:setFocus(obj) end
-- Move an object to a layer
function obj:toLayer(newLayer) if layer[newLayer] then layer[newLayer]:insert(obj) obj._perspectiveLayer = newLayer end end
--Move an object back a layer
function obj:back() if layer[obj._perspectiveLayer + 1] then layer[obj._perspectiveLayer + 1]:insert(obj) obj._perspectiveLayer = obj.layer + 1 end end
--Moves an object forwards a layer
function obj:forward() if layer[obj._perspectiveLayer - 1] then layer[obj._perspectiveLayer - 1]:insert(obj) obj._perspectiveLayer = obj.layer - 1 end end
--Moves an object to the very front of the camera
function obj:toCameraFront() layer[1]:insert(obj) obj._perspectiveLayer = 1 obj:toFront() end
--Moves an object to the very back of the camera
function obj:toCameraBack() layer[#layers]:insert(obj) obj._perspectiveLayer = #layers obj:toBack() end
--Returns objects paralaxed position
function obj:getPosition() local X, Y = view.scrollX+obj.x, view.scrollY+obj.y local t = {x=X,y=Y} return t end -- Added by ReverseStudio
end
Can I embed "perspective.lua" in my commercial project?
Hey all, sorry for the delay in answering, but as I mentioned, I do not get notifications for stuff posted on this Gist. Please post in the Corona SDK forum under the "Gymbyl Coding" subforum if you want me to see what you're asking.
Is this system compatible with Corona physics bodies? I'm having a hard time having my physics bodies (collision boxes) anchor to their associated objects. In other words, when I move my focused character, the collision boxes don't stay with their associated display objects. I'm guessing that this is due to the fact that the library manually moves your objects, correct? Thank you!
Great thanks to this!!!
im going to use it in my game project
Hi, what do if background overlaps player?
P.S Sorry, i very bad speak English
Hi Caleb,
Firstly thanks for coding the perspective library. I have been looking at using it for a game, however I wanted to have the ability to control the zoom allowing. unless i am mistaken this isn't possible currently, but let me know if theres a trick i am missing.
I have read your previous posts here also http://developer.coronalabs.com/code/perspective
Anyway I have been experimenting with this code http://forums.coronalabs.com/topic/41529-zooming-camera-anchor-point-hell/?p=216788, and trying to get it to work.
But whilst i can transition using the following,
transition.scaleBy( view, { time=zoomTime, xScale=factor-1, yScale=factor-1 } )
and get the ship and dots to scale, i cant adjust the parallax centering properly and the dots drift incorrectly. Any Ideas??
BTW I have also added an autoscroll feature. that seems to work. not sure if its a hack or good code?
camera:autoScroll(scrollSpeed, 0) -- called in enterframe
Can you help, or advise whats best.
cheers
Nick