War Battles Tutorial - Extra - Idle Position

I’ve been working through the tutorials and digging through the forums. I’ve been trying to make the War Battle player respond so that he is in an Idle position when no button is pressed. I have looked at some other examples for ideas. What I have technically works but there is a problem where if you switch buttons too quickly, the input results in a 0 due to releasing buttons and the character stops while you are pressing another button still. So you have to release and press the desired direction again to get moving. Also, I’m pretty certain I’m complicating the code so ideas to improve what I’m doing are quite welcome.

function init(self)
	msg.post(".", "acquire_input_focus")
	sprite.play_flipbook("#sprite", "player_idle")
	self.moving = false
	self.firing = false
	self.input = vmath.vector3()
	self.dir = vmath.vector3(0,1,0)
	self.speed = 50
end

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

local current_animation 
local function update_animation(self, anim)
	if anim ~= current_animation then
		sprite.play_flipbook("#sprite", anim)
		current_animation = anim
	end
end

function update(self, dt)
	
	if self.moving then
		local pos = go.get_position()
		pos = pos + self.dir * self.speed * dt
		go.set_position(pos)
		self.moving = false
	end

	if self.firing then
		local angle = math.atan2(self.dir.y, self.dir.x)
		local rot = vmath.quat_rotation_z(angle)
		local props = { dir = self.dir } 
		factory.create("#rocketfactory", nil, rot, props)
	end

	--self.input.x = 0
	--self.input.y = 0
	--self.moving = false
	self.firing = false 

end

function on_input(self, action_id, action)
	if action.pressed then

		
		if action_id == hash("up") then
			self.input.y = 1
			update_animation(self, "player_up")
		elseif action_id == hash("down") then
			self.input.y = -1
			update_animation(self, "player_down")
		elseif action_id == hash("left") then
			self.input.x = -1
			update_animation(self, "player_left")
		elseif action_id == hash("right") then
			self.input.x = 1
			update_animation(self, "player_right")
		elseif action_id == hash("fire") and action.pressed then
			self.firing = true
		end
		
	elseif action.released then

		if action_id == hash("left") then
			self.input.x = 0
			if vmath.length(self.input) == 0 then update_animation(self, hash("player_idle")) end
		elseif action_id == hash("right") then
			self.input.x = 0
			if vmath.length(self.input) == 0 then update_animation(self, hash("player_idle")) end
		elseif action_id == hash("up") then
			self.input.y = 0
			if vmath.length(self.input) == 0 then update_animation(self, hash("player_idle")) end
		elseif action_id == hash("down") then
			self.input.y = 0
			if vmath.length(self.input) == 0 then update_animation(self, hash("player_idle")) end
		end
	end
	if vmath.length(self.input) > 0 then
		self.moving = true
		self.dir = vmath.normalize(self.input)
	end
end

Here’s my take on this (note: untested code). The main difference is that I track all actions in a Lua table where is set an action to true when pressed and clear it on released. Then I use this information in update() to update animation states and direction of movement. I find that it is easier to do this kind of evaluation in update() when you know all input states.

function init(self)
	msg.post(".", "acquire_input_focus")
	sprite.play_flipbook("#sprite", "player_idle")
	self.current_animation = nil
	self.moving = false
	self.firing = false
	self.actions = {} -- all inputs tracked here, keyed on action_id
	self.input = vmath.vector3()
	self.dir = vmath.vector3(0,1,0)
	self.speed = 50
end

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

local function update_animation(self, anim)
	if anim ~= self.current_animation then
		sprite.play_flipbook("#sprite", anim)
		self.current_animation = anim
	end
end

function update(self, dt)
	if self.actions[hash("up")] then
		self.input.y = 1
		update_animation(self, "player_up")
	elseif self.actions[hash("down")] then
		self.input.y = -1
		update_animation(self, "player_down")
	else
		self.input.y = 0
	end
	if self.actions[hash("left")] then
		self.input.x = -1
		update_animation(self, "player_left")
	elseif self.actions[hash("right")] then
		self.input.x = 1
		update_animation(self, "player_right")
	else
		self.input.x = 0
	end

	self.moving = vmath.length(self.input) > 0

	if self.moving then
		self.dir = vmath.normalize(self.input)
		local pos = go.get_position()
		pos = pos + self.dir * self.speed * dt
		go.set_position(pos)
	else
		update_animation(self, hash("player_idle"))
	end

	if self.firing then
		local angle = math.atan2(self.dir.y, self.dir.x)
		local rot = vmath.quat_rotation_z(angle)
		local props = { dir = self.dir } 
		factory.create("#rocketfactory", nil, rot, props)
		self.firing = false 
	end
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

	if action_id == hash("fire") and action.pressed then
		self.firing = true
	end
end
5 Likes

That’s it! That works well and I’ve learned a new way to do some things. Thanks for taking time to show me this!

2 Likes