War Battles tutorial support thread - post your questions here

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

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

2 Likes

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

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.

1 Like

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

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?

3 Likes

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.

1 Like

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

1 Like

Who knows, what’s going on with my viewport and how can I fix it?

I’m pretty sure I did nothing, just tried to follow the tutorial.

Looks like your content might be placed a bit off, in relation to the x/y axes – could you check to see if this might bee the case?

5 Likes

Thanks for the reply.
Indeed, the map has been placed on 0,0,0 position:

Even if I place it as you suggest, it still looks weird:

How can I know the size of the screen to place the map correctly? I see no frame or any other origin anchor.
What about the distance to the player eyes? Should I zoom the map and all my other sprites instead of set some viewport settings?

Should I move and zoom the map manually in the Editor every time when I change the map size? Aren’t there any virtual camera settings?

I’m trying to make the tanks chase the player and i have added a spherical collision object to the tanks connected to a box shaped collision object on the player. I’ve created a tanks.script that contains the following:

function init(self)
	self.speed = 200	
end

function update(self, dt)
	local pos = go.get_position()
	pos = pos + self.speed * dt
	go.set_position(pos)
end

function on_message(self, message_id, message, sender)
	if message_id == hash("colllision_response") then
		
	end	
end

and i dont know what to write under the if statement, i want to write something like pos = player.pos or similar to make the tank move towards the player but that doesnt seem to work and i dont really have enough knowledge i feel. Any help would be appreciated! =)

Here’s an example of movement towards a moving target: https://github.com/britzl/publicexamples/tree/master/examples/simple_ai

I’ve been looking at the example but i dont understand so much of it ;/ i’ve read zombie.script many times but i understand how i should write my code in the defold project to make the tanks move. I’ve failed at trying to make the tanks respawn also, aswell as making them select a random point to move towards. I feel like i just lack the knowledge and any help would be so apprecieted ! maybe you can try to add more explanations in the code for the example you linked or try to help with the things i’ve been trying to code? =)

You typically spawn new items using a factory. We have a manual describing how to work with factories:

You select a random point like this:

local random_x = math.random(1, 1000) --  random value between 1 and 1000
local random_y = math.random(100, 800) -- between 100 to 800
local random_point = vmath.vector3(random_x, random_y, 0)

The easiest way to move from one point to another is to start an animation:

local duration = 2 -- seconds
go.animate(".", "position", go.PLAYBACK_ONCE_FORWARD, random_point, go.EASING_LINEAR, duration)

Read more here: https://www.defold.com/manuals/animation/#_property_animation

You can also animate in the update() function, step by step, frame by frame, but if you can it is recommended to use go.animate().

1 Like

thanks i will look into it, i appreciate the help so much!

Following the War Battles tutorial, and the rocket explosion animation plays but loops forever. Here’s the contents of rocket.script, it seems to match the example code.

I added a couple of print debugging statements, and it doesn’t look like this object ever receives any message, so never gets the the go.delete() call. Any ideas?

go.property("dir", vmath.vector3())

function init(self)
	self.speed = 200
	self.life = 1
end

function update(self, dt)
	local pos = go.get_position()
	pos = pos + self.dir * self.speed * dt
	go.set_position(pos)

	self.life = self.life - dt
	if self.life < 0 then
		self.life = 1000
		go.set_rotation(vmath.quat())
		self.speed = 0
		print("playing explosion")
		msg.post("#sprite", "play_animation", { id = hash("explosion") })
	end
end

function on_message(self, message_id, message, sender)
	print("got message of any kind")
	if message_id == hash("animation_done") then
		go.delete()
	end 
end

I think your explosion animation is on ‘Loop Forward’ instead of ‘Once Forward’ in the sprites atlas.
This is what it should look like.

Because the animation is on loop it never finishes and the animation_done message never gets send to the on_message function.

4 Likes

D’oh! That was it. Thanks so much @codescapade :slight_smile:

1 Like

You’re welcome :slight_smile: