Animations bugging out

I’ve been making a platformer and everything is going smoothly however my animations seem to be a bit odd.


As you can see in the first few seconds the animations seem to be working fine but as my speed increases the animations starts to alter weirdly, this is obviously due different animations merging in between seen by my print statements, I am not sure how to fix this as animations is not really my thing.

local move_acceleration = 100
local air_acceleration_factor = 0.25
local max_speed = 150
local gravity = -400
local jump_takeoff_speed = 220
local touch_jump_timeout = 0.2

-- pre-hashing ids improves performance
local msg_contact_point_response = hash("contact_point_response")
local msg_animation_done = hash("animation_done")
local group_obstacle = hash("ground")
local input_left = hash("left")
local input_right = hash("right")
local input_jump = hash("up")
local input_touch = hash("touch")
local anim_run = hash("run")
local anim_idle = hash("idle")
local anim_jump = hash("jump")
local anim_fall = hash("fall")

function init(self)
    msg.post(".", "acquire_input_focus")
    msg.post("/camera#camera", "acquire_camera_focus")
    msg.post("@render:", "use_fixed_projection", { zoom = 1.5, near = -1, far = 1 })

self.velocity = vmath.vector3(0, 0, 0)
-- support variable to keep track of collisions
self.correction = vmath.vector3()
self.ground_contact = false
self.move_input = 0
self.anim = nil
self.touch_jump_timer = 0 
end

local function play_animation(self, anim)
    -- only play animations which are not already playing
    if self.anim ~= anim then
	    sprite.play_flipbook("#sprite", anim)
	    self.anim = anim
    end
    end



local function update_animations(self)
    -- make sure the player character faces the right way
    sprite.set_hflip("#sprite", self.move_input < 0)
    if self.ground_contact then

	    if self.velocity.x == 0 then
		    play_animation(self, anim_idle)
		    print("Idle")
	    else
		    play_animation(self, anim_run)
		    print("Run")
	    end

    elseif self.velocity.y >0 then
	    play_animation(self, anim_jump)
	    print("Jump")
	    else
		    play_animation(self, anim_fall)
		    print("Fall")
	    end

end
local function handle_obstacle_contact(self, normal, distance)
    local proj = vmath.dot(self.correction, normal)
    local comp = (distance - proj) * normal
    -- add it to the correction vector
    self.correction = self.correction + comp -- compensate
    go.set_position(go.get_position() + comp)
    if normal.y > 0.7 then -- i did a trial and error to get this value
	    self.ground_contact = true
    end
    -- project the velocity onto the normal
    proj = vmath.dot(self.velocity, normal)
    if proj < 0 then
	    -- remove that component in that case
	    self.velocity = self.velocity - proj * normal
    end
end

function on_message(self, message_id, message, sender)

    -- check if we received a contact point message
    if message_id == msg_contact_point_response then
	    if message.group == group_obstacle then
		    handle_obstacle_contact(self, message.normal, message.distance)
	    end
    end
end




local function jump(self)
    -- only allow jump from ground
    if self.ground_contact then
	    self.velocity.y = jump_takeoff_speed
	    play_animation(self, anim_jump)
    end
end

local function abort_jump(self)
    -- cut the jump short if we are still going up
    if self.velocity.y > 0 then
	    self.velocity.y = self.velocity.y * 0.5
    end
end

function on_input(self, action_id, action)
    if action_id == input_left then
	    self.move_input = -action.value
    elseif action_id == input_right then
	    self.move_input = action.value
    elseif action_id == input_jump then
	    if action.pressed then
		    jump(self)
	    elseif action.released then
		    abort_jump(self)
	    end
    elseif action_id == input_touch then
	    local diff = action.x - go.get_position().x
	    if math.abs(diff) > 10 then
		    -- slow down when less than 100 pixels away
		    self.move_input = diff / 100
		    self.move_input = math.min(1, math.max(-1, self.move_input))
	    end
	    if action.released then
		    -- start timing the last release to see if we are about to jump
		    self.touch_jump_timer = touch_jump_timeout
	    elseif action.pressed then
		    if self.touch_jump_timer > 0 then
			    jump(self)
		    end
	    end
    end
end



function update(self, dt)
    local target_speed = self.move_input * max_speed
    local speed_diff = target_speed - self.velocity.x -- differences of velocity
    -- the complete acceleration to integrate over this frame
    local acceleration = vmath.vector3(0, gravity, 0)
    if speed_diff ~= 0 then
	    if speed_diff < 0 then
		    acceleration.x = -move_acceleration
	    else
		    acceleration.x = move_acceleration
	    end
	    if not self.ground_contact then -- decreases acceleration for money
		    acceleration.x = air_acceleration_factor * acceleration.x
	    end
    end
    local dv = acceleration * dt
    if math.abs(dv.x) > math.abs(speed_diff) then
	    dv.x = speed_diff
    end
    -- save the current velocity for later use
    local v0 = self.velocity
    self.velocity = self.velocity + dv
    local dp = (v0 + self.velocity) * dt * 0.5
    -- apply it to the player character
    go.set_position(go.get_position() + dp)

    if self.touch_jump_timer > 0 then
	    self.touch_jump_timer = self.touch_jump_timer - dt
    end
    update_animations(self)

    self.correction = vmath.vector3() --correcting collisions
    self.move_input = 0
    self.ground_contact = false
end	

Decided to give the whole code as I have no idea

Update: I’ve managed to fix this by making two really small circles (diameter: 0.1) on the bottom left and bottom right on the player and a box in the middle that isn’t touching the ground for a body. I assume this is due to the rough edges maybe?

Another solution might be to use RayCasting and/or have function that sets the player to an on_ground state when they have been on the ground for a few frames. It’s getting expensive reading the physics collisions constantly when the player is only moving on flat ground. Just a suggestion.

1 Like

Thank you for the suggestion. And further update the movement still seems a bit odd but better then before since it can go through the tile map collision completely if the downward force is accelerated but the animations are crisp. I was thinking of making it so if the central body is collided with the ground it positions the player y amount of coordinate upwards in order to stay ontop of the ground but it seems a bit of a bad idea for performance. I will try out the ray casts today