diff --git a/README.md b/README.md index eec0c4d..ea4824d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ Id of the game object to follow. See ```camera.follow()``` for details. #### follow_lerp (number) Amount of lerp when following a target. See ```camera.follow()``` for details. +#### follow_offset (vector3) +Camera offset from the position of the followed target. See ```camera.follow()``` for details. + #### bounds_left (number), bounds_right (number), bounds_top (number), bounds_bottom (number) The camera bounds. See ```camera.bounds()``` for details. @@ -169,6 +172,7 @@ Follow a game object. * ```camera_id``` (hash|url) * ```target``` (hash|url) - Game object to follow * ```lerp``` (number) - Lerp from current position to target position with ```lerp``` as t. Optional. +* ```offset``` (vector3) - Camera offset from target position. Optional. ### camera.unfollow(camera_id) Stop following a game object. diff --git a/example/player.script b/example/player.script index e097639..1ec8487 100644 --- a/example/player.script +++ b/example/player.script @@ -2,9 +2,14 @@ local camera = require "orthographic.camera" local SPEED = 400 +local function lerp(t, dt, from, to) + return vmath.lerp(1 - math.pow(t, dt), from, to) +end + function init(self) self.input = {} self.crosshair = vmath.vector3() + self.camera_offset = vmath.vector3() end function update(self, dt) @@ -16,14 +21,19 @@ function update(self, dt) local pos = go.get_position() if self.input[hash("up")] then pos.y = pos.y + SPEED * dt + self.camera_offset.y = lerp(0.25, dt, self.camera_offset.y, 300) elseif self.input[hash("down")] then pos.y = pos.y - SPEED * dt + self.camera_offset.y = lerp(0.25, dt, self.camera_offset.y, -300) end if self.input[hash("left")] then pos.x = pos.x - SPEED * dt + self.camera_offset.x = lerp(0.25, dt, self.camera_offset.x, -500) elseif self.input[hash("right")] then pos.x = pos.x + SPEED * dt + self.camera_offset.x = lerp(0.25, dt, self.camera_offset.x, 500) end + camera.follow(go.get_id("camera"), go.get_id(), 0.1, self.camera_offset) go.set_position(pos) end diff --git a/orthographic/camera.lua b/orthographic/camera.lua index 776b2c9..6247422 100644 --- a/orthographic/camera.lua +++ b/orthographic/camera.lua @@ -62,6 +62,11 @@ projectors[M.PROJECTOR.FIXED_ZOOM] = function(camera_id, near_z, far_z, zoom) return vmath.matrix4_orthographic(xoffset, xoffset + projected_width, yoffset, yoffset + projected_height, near_z, far_z) end +-- http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ +local function lerp_with_dt(t, dt, v1, v2) + return vmath.lerp(1 - math.pow(t, dt), v1, v2) +end + --- Add a custom projector -- @param projector_id Unique id of the projector (hash) @@ -160,7 +165,6 @@ function M.final(camera_id) cameras[camera_id] = nil end - --- Update a camera -- When calling this function a number of things happen: -- * Follow target game object (if any) @@ -183,8 +187,8 @@ function M.update(camera_id, dt) local follow_enabled = go.get(camera.url, "follow") if follow_enabled then local follow = go.get(camera.url, "follow_target") - local target_pos = go.get_position(follow) - local target_world_pos = go.get_world_position(follow) + local follow_offset = go.get(camera.url, "follow_offset") + local target_world_pos = go.get_world_position(follow) + follow_offset local new_pos local deadzone_top = go.get(camera.url, "deadzone_top") local deadzone_left = go.get(camera.url, "deadzone_left") @@ -211,7 +215,7 @@ function M.update(camera_id, dt) end new_pos.z = camera_world_pos.z local follow_lerp = go.get(camera.url, "follow_lerp") - camera_world_pos = vmath.lerp(follow_lerp, camera_world_pos, new_pos) + camera_world_pos = lerp_with_dt(follow_lerp, dt, camera_world_pos, new_pos) camera_world_pos.z = new_pos.z end @@ -256,8 +260,7 @@ function M.update(camera_id, dt) camera.recoil = nil else local t = camera.recoil.time_left / camera.recoil.duration - camera.recoil.offset.x = vmath.lerp(t, 0, camera.recoil.offset.x) - camera.recoil.offset.y = vmath.lerp(t, 0, camera.recoil.offset.y) + camera.recoil.offset = vmath.lerp(t, VECTOR3_ZERO, camera.recoil.offset) end end @@ -281,10 +284,11 @@ end -- @param camera_id -- @param target The game object to follow -- @param lerp Optional lerp to smoothly move the camera towards the target -function M.follow(camera_id, target, lerp) +-- @param offset Optional offset from target position +function M.follow(camera_id, target, lerp, offset) assert(camera_id, "You must provide a camera id") assert(target, "You must provide a target") - msg.post(cameras[camera_id].url, "follow", { target = target, lerp = lerp }) + msg.post(cameras[camera_id].url, "follow", { target = target, lerp = lerp, offset = offset }) end diff --git a/orthographic/camera.script b/orthographic/camera.script index c250d9e..34d07e5 100644 --- a/orthographic/camera.script +++ b/orthographic/camera.script @@ -9,6 +9,7 @@ go.property("offset_gui", false) go.property("follow", false) go.property("follow_target", hash("")) go.property("follow_lerp", 0.5) +go.property("follow_offset", vmath.vector3(0, 0, 0)) go.property("bounds_left", 0) go.property("bounds_bottom", 0) @@ -81,6 +82,7 @@ function on_message(self, message_id, message, sender) self.follow = true self.follow_target = type(message.target) == "string" and hash(message.target) or message.target self.follow_lerp = message.lerp or 1 + self.follow_offset = message.offset or vmath.vector3() elseif message_id == DEADZONE then self.deadzone_right = message.right or 0 self.deadzone_top = message.top or 0