Idle Animations for top down movement (SOLVED)

I’m using a sprite the moves 8 directions, and I also have a 4 Idle animation for him, Up, Down Left, Right.
The problem is that the up animation the only one that’ll show after a while.
For example If I go right and stop it’ll show the Right Idle animation then start to move left It’ll show the left Idle animation and won’t show the right Idle animation then the down won’t show both the left and right Idle animation, and the up doesn’t show the Right, Left and, Down Idle animation

function update(self, dt)
	if self.actions[hash("Left")] then
		play_animation(self, hash("Player Left"))
		self.direction.x = -self.speed
	elseif self.actions[hash("Right")] then
		play_animation(self, hash("Player Right"))
		self.direction.x = self.speed
	else
		self.direction.x = 0
	end

	if self.actions[hash("Up")] then
		play_animation(self, hash("Player Up"))
		self.direction.y = self.speed
	elseif self.actions[hash("Down")] then
		play_animation(self, hash("Player Down"))
		self.direction.y = -self.speed 
	else
		self.direction.y = 0 
	end

	if self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Up")] == false then
		play_animation(self, hash("Idle Up"))
	elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Down")] == false then
		play_animation(self, hash("Idle Down"))
	elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Left")] == false then
		play_animation(self, hash("Idle Left")) 
	elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Right")] == false then
		play_animation(self, hash("Idle Right"))                         

	end                

I’d store the current actions in a table, and process them like this:

self.keypress = {
     up = false,
     down = false,
     left = false,
     right = false
}

if action_id == hash("<any_keypress_here>") and action.pressed then
    self.keypress.<the_opposite_keypress> = false
    self.keypress.<that_keypress> = true
-- and repeat for each keypress

-- THEN
if action_id == hash("<any_keypress_here>") and action.released then
     self.keypress.<that_keypress> = false



-- Now you process the keypresses
if action_id == hash("<keypress1>") or action_id == hash("<keypress2>") or action_id == hash("<keypress3>") or action_id == hash("<keypress4>") then
     if self.keypress.up == true and <nothing else is true> then
          -- North animation
     -- repeat for West, South, and East movements

     -- Now, combos:
     if self.keypress.up == true and self.keypress.left = true then
          -- Northwest animation
     -- and repeat for the 4 combinations

This works in theory. I didn’t test it out.

Is this just for the idle code or the whole thing?

Woops, I’m sorry. I was basing my answer off of your post in another topic.


Let me take a look at your code. I re-read what you said. I was confused when you were trying to give an example, but now I understand. The problem you’re having is that the Up animation is getting stuck after a while? Oh, or did you mean that when you press Up, all the other idle animations no longer work?

Could you include the code for how you set the self.actions table?

when I press Up, all the other idle animations no longer work

go.property("speed", 5)

local function play_animation(self, animation)
	if self.current_animation ~= animation then
		self.current_animation = animation
		msg.post("#sprite", "play_animation", { id = animation })
	end
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.direction = vmath.vector3(0,0,0)
	self.actions = {} 
	self.current_animation = nil
end

function final(self)
	msg.post(".", "release_input_focus")
end

function update(self, dt)
	if self.actions[hash("Left")] then
		play_animation(self, hash("Player Left"))
		self.direction.x = -self.speed
	elseif self.actions[hash("Right")] then
		play_animation(self, hash("Player Right"))
		self.direction.x = self.speed
	else
		self.direction.x = 0
	end

	if self.actions[hash("Up")] then
		play_animation(self, hash("Player Up"))
		self.direction.y = self.speed
	elseif self.actions[hash("Down")] then
		play_animation(self, hash("Player Down"))
		self.direction.y = -self.speed 
	else
		self.direction.y = 0 
	end

	if self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Up")] == false then
		play_animation(self, hash("Idle Up"))
	elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Down")] == false then
		play_animation(self, hash("Idle Down"))
	elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Left")] == false then
		play_animation(self, hash("Idle Left")) 
	elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Right")] == false then
		play_animation(self, hash("Idle Right"))                         

	end                

	go.set_position(go.get_position() + self.direction * dt)   

end       

function on_input(self, action_id, action)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
		elseif action.released then
			self.actions[action_id] = false
		end 
	end
end

Oh, I think I found your problem:

if self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Up")] == false then
	play_animation(self, hash("Idle Up"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Down")] == false then
	play_animation(self, hash("Idle Down"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Left")] == false then
	play_animation(self, hash("Idle Left")) 
elseif self.direction.x == 0 and self.direction.y == 0 and self.actions[hash("Right")] == false then
	play_animation(self, hash("Idle Right"))                         
end 

This block here is running the same check for each statement, which essentially defaults it to the first statement that returns correctly. The reason that Up breaks all the other idle animations is because it’s the first statement. Another thing: It doesn’t break it until you press the Up key because self.actions[hash(“Up”)] is equal to nil until it’s first pressed.

This should fix your issue:

-- You can add this for simplicity,
-- or just put the values back into your if statements. Place this in function init()
self.actions = {
     up = hash("Up"),
     down = hash("Down"),
     left = hash("Left"),
     right = hash("Right")
}

-- State this variable in function init(), otherwise you'll have errors.
self.last_pressed = ""

function on_input(self, action_id, action)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
            -- ADD THIS:
            if action_id == self.actions.up or action_id == self.actions.down or action_id == self.actions.left or action_id == self.actions.right then
                self.last_pressed = action_id
            end
            --
		elseif action.released then
			self.actions[action_id] = false
		end 
	end
end

if self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.up then
	play_animation(self, hash("Idle Up"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.down then
	play_animation(self, hash("Idle Down"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.left then
	play_animation(self, hash("Idle Left")) 
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.right then
	play_animation(self, hash("Idle Right"))                         
end 
2 Likes

Like this

go.property("speed", 5)

local function play_animation(self, animation)
	if self.current_animation ~= animation then
		self.current_animation = animation
		msg.post("#sprite", "play_animation", { id = animation })
	end
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.direction = vmath.vector3(0,0,0)
	self.actions = {} 
	self.current_animation = nil
end
self.actions = {
	up = hash("Up"),
	down = hash("Down"),
	left = hash("Left"),
	right = hash("Right")
}
	self.last_pressed = ""

function final(self)
	msg.post(".", "release_input_focus")
end

function update(self, dt)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
			-- ADD THIS:
			if action_id == self.actions.up or action_id == self.actions.down or action_id == self.actions.left or action_id == self.actions.right then
				self.last_pressed = action_id
			end
			--
		elseif action.released then
			self.actions[action_id] = false
		end 
	end
end

if self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.up then
	play_animation(self, hash("Idle Up"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.down then
	play_animation(self, hash("Idle Down"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.left then
	play_animation(self, hash("Idle Left")) 
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.right then
	play_animation(self, hash("Idle Right"))                         
end 

Almost. Like this:

go.property("speed", 5)

local function play_animation(self, animation)
	if self.current_animation ~= animation then
		self.current_animation = animation
		msg.post("#sprite", "play_animation", { id = animation })
	end
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.direction = vmath.vector3(0,0,0)
	
    self.actions = {
        up = hash("Up"),
        down = hash("Down"),
        left = hash("Left"),
        right = hash("Right")
    }
	self.last_pressed = ""
        
	self.current_animation = nil
end

function final(self)
	msg.post(".", "release_input_focus")
end

function update(self, dt)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
			-- ADD THIS:
			if action_id == self.actions.up or action_id == self.actions.down or action_id == self.actions.left or action_id == self.actions.right then
				self.last_pressed = action_id
			end
			--
		elseif action.released then
			self.actions[action_id] = false
		end 
	end
end

if self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.up then
	play_animation(self, hash("Idle Up"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.down then
	play_animation(self, hash("Idle Down"))
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.left then
	play_animation(self, hash("Idle Left")) 
elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.right then
	play_animation(self, hash("Idle Right"))                         
end 
1 Like


This popped up when I pasted the code in

Would you like to try

Sorry, I’m on my phone right now, so I cant. If you want to wait a few hours I could get to it.

The error you’re getting is because the if statements are outside the function with the “self” argument. I didn’t notice because I was on my phone.

EDIT: I see what happened. I was changing specific segments of your code, not the entire thing. Let me see if I can fix it all.

1 Like

Ok and I can wait a few hours

This should be fixed up, but no guarantees since I’m on my phone :confused:

go.property("speed", 5)

local function play_animation(self, animation)
	if self.current_animation ~= animation then
		self.current_animation = animation
		msg.post("#sprite", "play_animation", { id = animation })
	end
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.direction = vmath.vector3(0,0,0)
	
    self.actions = {
        up = hash("Up"),
        down = hash("Down"),
        left = hash("Left"),
        right = hash("Right")
    }
	self.last_pressed = ""
        
	self.current_animation = nil
end

function final(self)
	msg.post(".", "release_input_focus")
end

function update(self, dt)
	if self.actions[hash("Left")] then
		play_animation(self, hash("Player Left"))
		self.direction.x = -self.speed
	elseif self.actions[hash("Right")] then
		play_animation(self, hash("Player Right"))
		self.direction.x = self.speed
	else
		self.direction.x = 0
	end

	if self.actions[hash("Up")] then
		play_animation(self, hash("Player Up"))
		self.direction.y = self.speed
	elseif self.actions[hash("Down")] then
		play_animation(self, hash("Player Down"))
		self.direction.y = -self.speed 
	else
		self.direction.y = 0 
	end

	if self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.up then
	    play_animation(self, hash("Idle Up"))
    elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.down then
	    play_animation(self, hash("Idle Down"))
    elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.left then
	    play_animation(self, hash("Idle Left")) 
    elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.actions.right then
	    play_animation(self, hash("Idle Right"))                         
end                

	go.set_position(go.get_position() + self.direction * dt)   

end       

function update(self, dt)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
			-- ADD THIS:
			if action_id == self.actions.up or action_id == self.actions.down or action_id == self.actions.left or action_id == self.actions.right then
				self.last_pressed = action_id
			end
			--
		elseif action.released then
			self.actions[action_id] = false
		end 
	end
end

Ok I’ll check to see if it works in a few hours because I left my house just a few minutes ago.

Ok so I just tested the code and Now it won’t let me move

Ok I found why it wasn’t moving you had the
function on_input(self, action_id, action) as a update. But still when I fixed it, it stopped showing the Idle animations and will just play the walking animations

Would you like me to add you to the project so you can have a go at it

I think you need to rethink where you put the movement logic. You’re duplicating a lot of information by storing key state in a variable and the rechecking it in update. It’s better to deal with setting movement direction and animation in on_input() where you have all of the information you need available. I’ve created an example here:

CODE: https://github.com/britzl/publicexamples/tree/master/examples/top_down_movement
DEMO: http://britzl.github.io/publicexamples/top_down_movement/index.html

2 Likes

Alright, I’m back on my PC. Sorry for the wait. I agree with britzl. Your code isn’t optimal, but it’s a learning process. I overwrote one of your variables by accident.

I tested this in an empty project, and it worked! Let me know if you have any issues:

go.property("speed", 5)

local function play_animation(self, animation)
	if self.current_animation ~= animation then
		self.current_animation = animation
		msg.post("#sprite", "play_animation", { id = animation })
	end
end

function init(self)
	msg.post(".", "acquire_input_focus")
	self.direction = vmath.vector3(0,0,0)
	self.actions = {}

	self.key = {
		up = hash("Up"),
		down = hash("Down"),
		left = hash("Left"),
		right = hash("Right")
	}
	self.last_pressed = ""

	self.current_animation = nil
end

function final(self)
	msg.post(".", "release_input_focus")
end

function update(self, dt)
	if self.actions[hash("Left")] then
		play_animation(self, hash("Player Left"))
		self.direction.x = -self.speed
	elseif self.actions[hash("Right")] then
		play_animation(self, hash("Player Right"))
		self.direction.x = self.speed
	else
		self.direction.x = 0
	end

	if self.actions[hash("Up")] then
		play_animation(self, hash("Player Up"))
		self.direction.y = self.speed
	elseif self.actions[hash("Down")] then
		play_animation(self, hash("Player Down"))
		self.direction.y = -self.speed 
	else
		self.direction.y = 0 
	end

	if self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.key.up then
		play_animation(self, hash("Idle Up"))
	elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.key.down then
		play_animation(self, hash("Idle Down"))
	elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.key.left then
		play_animation(self, hash("Idle Left")) 
	elseif self.direction.x == 0 and self.direction.y == 0 and self.last_pressed == self.key.right then
		play_animation(self, hash("Idle Right"))
	end

	go.set_position(go.get_position() + self.direction * dt)   

end

function on_input(self, action_id, action)
	if action_id then
		if action.pressed then
			self.actions[action_id] = true
			-- ADD THIS:
			if action_id == self.key.up or action_id == self.key.down or action_id == self.key.left or action_id == self.key.right then
				self.last_pressed = action_id
			end
			--
		elseif action.released then
			self.actions[action_id] = false
		end 
	end
end
5 Likes

Thank you so much It worked
Sorry for having you do this for me

2 Likes