War Battles tutorial support thread - post your questions here


#21

Yes, I saw that as well. And it’s either self.dir or self.speed that is nil. Using print() to figure out which one is your next step. Once you know which variable it is you need to figure out why it’s nil. Could it be that you forgot to initialise it in the init() function as is done in the tutorial? Perhaps you have some error in your on_input() function? Do you see any error in the console, prior to the error message?


#22

Hmm, it must be the init, I deleted it thinking, as later in the tutorial, the init isn’t present in the code example, then one of the other code functions replaces it or something like that. Later today I’ll put the init string back in and let you know what happens.


#23

Yep, that’s surely it!


#24

Okay, now the rockets don’t play the explosion animation, they move correctly, but when they reach the distance theryre supposed to explode, three just rotate and stay still. The error I’m now getting is error:gameobject: instance unknown could my be found when displaying message’ play_animation" sent from main:/instance1#rocket


#25

Ok, so you’re sending a play_animation message to a sprite component on the rocket and the URL part of the msg.post() call doesn’t match what you’ve set in the editor for game object and component id. Read more about URLs in the manual and double check against the tutorial that you’ve named everything the same way.


#26

Thanks, I figured it out, whilst changing the code for collisions, I forgot to put a hash in front of the sprite XD


#27

Guess I’ll highjack this nice thread for all the small/beginner questions related to the War Battles tutorial, both text and video. Everybody is welcome to ask questions.

War Battles tutorial:
Text: https://www.defold.com/tutorials/war-battles/
Video: https://www.youtube.com/playlist?list=PLXsXu5srjNlz1HvCPytEVZggLqWBoF1pz
Github: https://github.com/defold/defold-examples/tree/master/war-battles


#28

Has anyone else noticed that the game fires too many rockets at once? It seems as though I’m shooting at least two rockets per input. Any thoughts where I could look in my code? I know I’m firing at least two b/c my logic for when self.firing = true looks like this:

if self.firing then
   print("here\n")
   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

#29

Check your input message that you should react the button fire with the message.pressed so that it will fire one per press or click. Or you can make a timer that controls the fire cooldown time, for example 0.5 tps.


#30

Hey!

So, I finished the tutorial and now I’m trying to implement the suggestions it makes at the end, starting with the other animations for the player. The code I wrote works fine with the one-direction movements, playing the animation only once every time the player changes direction, but with the diagonal movements it seems like it’s trying to replay the animation at every frame. I don’t know how to fix it.

Also (and this also happens in the web version in this page), I can’t fire rockets while moving in two specific diagonal directions (down-right and up-left), and I have no idea how to fix that.

Please help me with these, and any feedback on the code that the tutorial didn’t tell me how to make is very appreciated.

function init(self)
	msg.post(".", "acquire_input_focus")
	self.moving = false
	self.speed = 100
	self.input = vmath.vector3()
	self.dir = vmath.vector3(0, 1, 0)
	self.firing = false
	self.anim_updated = false
end

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

local function update_anim(self)
	if self.dir.x > 0 then
		if self.dir.y > 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_upright") } )
		elseif self.dir.y < 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_downright") } )
		elseif self.dir.y == 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_right") } )
		end
	elseif self.dir.x < 0 then
		if self.dir.y > 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_upleft") } )
		elseif self.dir.y < 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_downleft") } )
		elseif self.dir.y == 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_left") } )
		end
	elseif self.dir.x == 0 then
		if self.dir.y > 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_up") } )
		elseif self.dir.y < 0 then
			msg.post("#sprite", "play_animation", { id = hash("player_down") } )
		end
	end

	self.anim_updated = true
end

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

	if not self.anim_updated then
		update_anim(self)
	end

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

	self.input = vmath.vector3()
	self.moving = false
	self.firing = false
end

function on_input(self, action_id, action)
	if action_id == hash("up") then
		self.input.y = 1
	end
	if action_id == hash("down") then
		self.input.y = -1
	end
	if action_id == hash("left") then
		self.input.x = -1
	end
	if action_id == hash("right") then
		self.input.x = 1
	end
	if action_id == hash("fire") and action.pressed then
		self.firing = true
	end

	if vmath.length_sqr(self.input) > 0 then
		self.moving = true

		local temp = vmath.normalize(self.input)
		
		if self.dir ~= temp then
			self.anim_updated = false
		end
	
		self.dir = temp
	end
end


#31

Hi,

Yes, that is what happens. Your main problem stems from the following:

When you press “up” and “left” at the same time, two separate calls are made to on_input() each frame before update() is called. On each call, a different component of the self.input vector is set. So looking at the value of self.input if you hold “up” and “left”, you would see:

DEBUG:SCRIPT: vmath.vector3(-1, 0, 0)
DEBUG:SCRIPT: vmath.vector3(-1, 1, 0)
-- new frame
DEBUG:SCRIPT: vmath.vector3(-1, 0, 0)
DEBUG:SCRIPT: vmath.vector3(-1, 1, 0)
-- new frame
DEBUG:SCRIPT: vmath.vector3(-1, 0, 0)
DEBUG:SCRIPT: vmath.vector3(-1, 1, 0)
-- new frame
DEBUG:SCRIPT: vmath.vector3(-1, 0, 0)
DEBUG:SCRIPT: vmath.vector3(-1, 1, 0)
-- new frame
DEBUG:SCRIPT: vmath.vector3(-1, 0, 0)
DEBUG:SCRIPT: vmath.vector3(-1, 1, 0)
...

Since self.dir is updated each of these calls, the flag self.anim_updated will be set to false each call too and you get your problem.

A way to solve this, and also simplify the code is to remove the flag setting in on_input() and do a check right with the animation code. For instance (4-way movement, but the idea works with 8-way too):

local function update_anim(self)
   local anim = hash("player_idle") -- default

    if self.dir.x > 0 then
        anim = hash("player_right")
    elseif self.dir.x < 0 then
        anim = hash("player_left")
    elseif self.dir.y > 0 then
        anim = hash("player_up")
    elseif self.dir.y < 0 then
        anim = hash("player_down")
    end

    if anim ~= self.current_anim then
        msg.post("#sprite", "play_animation", { id = anim })
        self.current_anim = anim
    end
end

I could not reproduce this which makes me suspect that it may be an issue with your keyboard. Check this thread for more info: Input behavior multiple buttons pressed (SOLVED)

It may be solved by using different keys for movement. Try WASD?


#32

That actually makes a lot of sense. I tried your code and it works just fine with the 8 directions too.

It does work with WASD for some reason. As said in the thread you sent, it’s probably about the keyboard.

Thank you so much! I didn’t think I would be answered so fast.


#33

I think that since most games use WASD, they treat those keys specially when manufacturing.