Fixed Camera

I’ve a platform game where I want to fix the camera so it doesn’t change when the ‘hero’ jump - so the player can still see the level beneath, or maybe just changes by a smaller amount. At the moment it’s locked to the hero sprite. Do I need to create it as it’s own object and track the hero in a different way?

Code is here - 14 november

1 Like

Yes, if the camera shouldn’t be fully locked to the position of the player then you need to move the camera when the player object moves and also apply inertia or other limitations to your camera logic that you need.

1 Like

Thanks. So, do I need to move it out of the hero object?

1 Like

Yes. Put the camera in a game object separate from the player. You then need to add code to do the camera movement you want. There are lots of ways of doing the movement; there are a number of camera movement assets, these will show you how to do the movement of the camera.

Do you have an example of how this works?

I’m afraid there is no example like this. I’m working on a camera handling module for 2D/2.5D games and such thing like this is possible, but I’m not yet ready for a release :wink:

Few tips:

I believe Defold built-in camera component is now perfect to use in 2/2.5D games :wink:
As mentioned earlier, I have a separate form my player game object (operator) with another nested game object (offset) with camera component:

image

Camera component has cool properties that you can set, e.g. you can use perspective (I’m using perspective for “free” parallax effect) or orthographic, explained the best in here:

My camera module is following all objects (or none) defined in actors table, as you can see I have separation of following on x and y axis:

The idea of not following when jumping is either by manually in your code changing these properties, so that it is not following hero, when jumping and following again, when landing, etc, but you also have to take into account some deadzone - when hero is outside of this deadzone, you should start following it anyway, in order not to allow it to escape the screen. Adjusting parameters might be difficult :smiley:

Dead zone can be defined either around player’s position or camera’s position.

Finally with modifying the position of the offset game object you can “lean” the camera in the direction of movement, zoom in/out or shake it (translational and rotational)!

Everything is just plain code executed in update with all the data you need (actors positions, camera, operator and offet settings)

4 Likes

My camera isn’t perfect but it’s coming along.

local input = require "modules.input"

local x_follow_threshold = 6

local look_down_duration_threshold = 1.0
local look_down_move_speed = 2

function init(self)
    self.follow_player_x = true
    self.follow_player_y = false

    self.original_y = go.get_position().y
    self.url = msg.url('/camera#camera')

    self.look_down_timer = 0
end

local function is_position_on_camera(self, position)
    local projection = camera.get_projection(self.url)
    local view = camera.get_view(self.url)

    local view_projection = projection * view

    local ndc_position = view_projection * vmath.vector4(position.x, position.y, position.z or 0, 1)

    -- Normalize to NDC space
    ndc_position.x = ndc_position.x / ndc_position.w
    ndc_position.y = ndc_position.y / ndc_position.w
    ndc_position.z = ndc_position.z / ndc_position.w

    -- Check relative position to camera bounds
    local result = {
        on_screen = ndc_position.x >= -1 and ndc_position.x <= 1 and
                    ndc_position.y >= -1 and ndc_position.y <= 1 and
                    ndc_position.z >= 0 and ndc_position.z <= 1,
        above = ndc_position.y > 1,
        below = ndc_position.y < -1,
        left = ndc_position.x < -1,
        right = ndc_position.x > 1,
    }

    return result
end


local function lerp(a, b, t)
    return a + (b - a) * t
end

function update(self, dt)
    local camera_position = go.get_position()
    local player_position = go.get_position("/player")

    -- Check if the player is not above the screen
    local camera_status = is_position_on_camera(self, player_position)

    if camera_status.below or (not camera_status.on_screen and not camera_status.above) then
        -- Move the camera to center on the player without lerp
        camera_position.x = player_position.x
        if self.follow_player_y then
            camera_position.y = player_position.y
        end
    else
        -- Apply smooth following when the player is now below camera
        local x_diff = math.abs(camera_position.x - player_position.x)
        if self.follow_player_x and x_diff > x_follow_threshold then
            camera_position.x = lerp(camera_position.x, player_position.x, dt * 10)
        end
        if self.follow_player_y then
            camera_position.y = lerp(camera_position.y, player_position.y, dt * 10)
        end
    end

    if input.key_state.crouch then
        self.look_down_timer = self.look_down_timer + dt
        if self.look_down_timer >= look_down_duration_threshold and camera_status.on_screen then
            camera_position.y = camera_position.y - look_down_move_speed
        end
    else
        self.look_down_timer = 0 -- Reset timer if crouch is released
    end

    -- Ensure the camera doesn't drop below the original Y
    if player_position.y < self.original_y then
        camera_position.y = self.original_y
    end

    -- Prevent player scrolling from going too low
    if camera_position.y < self.original_y then
        camera_position.y = self.original_y
    end

    go.set_position(camera_position)
end


function on_message(self, message_id, message, sender)

    if message_id == hash("follow_player_x") then
        self.follow_player_x = message.toggle
    end

    if message_id == hash("follow_player_y") then
        self.follow_player_y = message.toggle
    end
end

2 Likes