LUA Naming Collision Issue...maybe? (SOLVED)

A question on function naming.

I’m doing the War battle tutorial, running through the extras, and I have a function called move(self, dt) that updates the location of the tanks.

I also have the same function for the rocket, and the same function for the player.

Everything seems to be fine, until I launch the rocket, when the rocket seems to call the tanks move() and I don’t really understand why.

If I rename the rockets move to rocket_move() then it all works perfectly. It feels like some kind of function naming collision, but I don’t really understand why.

My rocket script in player_rocket.script:

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

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

function update(self, dt)
	self.life = self.life - dt
	if self.life < 0 then
		explode(self)
	else
		move(self, dt)
	end	
end

function on_message(self, message_id, message, sender)
	if message_id == hash("animation_done") then
		go.delete()
	elseif message_id == hash("collision_response") then
		explode(self)
		go.delete(message.other_id)
		msg.post("/gui#ui", "add_score", {score = 100})
	end
end

function explode(self)
	-- Give ourselves some time to complete the animation
	self.life = 10000
	-- Reset rotation otherwise the explosion will be rotated
	go.set_rotation(vmath.quat())
	-- Stop moving
	self.speed = 0
	-- Play explosion
	msg.post("#sprite", "play_animation", {id = hash("explosion")})
end

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

My tank script in tank.script:

local defs = require "modules/defines"

go.property("parent", msg.url())
go.property("dead_zone", 5)

function init(self)
	self.target = nil
	self.speed = 50
end

function final(self)
	msg.post(self.parent, "tank_dead", { id = go.get_id() })
end

function update(self, dt)
	if self.target == nil then
		new_target(self)
	else
		-- move towards the target at some speed
		move(self, dt)
		
		-- If we're close enough, we've arrived, drop the target
		check_arrived(self)
	end

	
end

function on_message(self, message_id, message, sender)
	-- Add message-handling code here
	-- Remove this function if not needed
end

function on_input(self, action_id, action)
	-- Add input-handling code here
	-- Remove this function if not needed
end

function on_reload(self)
	-- Add reload-handling code here
	-- Remove this function if not needed
end

function new_target(self)
	print(go.get_id() .. " has no target, finding new target")
	-- If we don't have a target, pick a random location and move towards it
	-- TODO: Find a way to get a random location within the level bounds
	x, y, w, h = tilemap.get_bounds("/map#map")
	local xt = math.random(w * defs.tile_size)
	local yt = math.random(h * defs.tile_size)
	self.target = vmath.vector3(xt, yt, 1)
end

function move(self, dt) 
	if self.target == nil then
		print("WTF " .. go.get_id())
	else
		print("we have target")
		local p = go.get_position()
		local dir = vmath.normalize(self.target - p)
		p = p + (dir * self.speed * dt)
		go.set_position(p)
	end
end

function check_arrived(self)
	local sqr_dist = vmath.length_sqr(self.target - go.get_position())
	local sqr_zone = self.dead_zone * self.dead_zone
	if sqr_zone > sqr_dist then
		print(go.get_id() .. " arrived at target")
		self.target = nil
	end
end

You need to make them local.

local function move(self, dt)
    -- stuff
end

If you don’t write local then they will be global functions that any script can call.

Note that once you make them local you have to define them before (above) where you call them, or forward declare them. See: Locals, globals and lexical scoping

4 Likes

If you create a function in one script without prefixing it with local it will be be defined globally (this goes for regular variables as well).

You are overwriting the same global function multiple times from different scripts. (I agree it might be a bit unexpected since the script specific init/update/etc functions are not overwritten.)

Instead you should do something like this:

-- rocket.script
local function move()
    -- do rocket movement
end

-- rocket.script
local function move()
    -- do player movement
end

Then you would be able to use the different move functions separately in each script.

edit: Damn, @ross.grams you are fast!

4 Likes

Thank you both. I was suspicious ti was something like that, but I’d read that function naming between lua scripts that wasn’t an issue.

Thank you for clarifying.

2 Likes

Boom! Beat Sven!

4 Likes

That fixed it, thanks folks. Now I know…and knowing is half the battle.

1 Like

Yeah, I didn’t know this for a long time either. It makes the weirdest bugs.

Hey now, we both beat @britzl, at least I have that!

4 Likes

Man, y’all are so friendly, and don’t mind my dumb questions.

How come though that we don’t write local in front of the common functions, init, update, on_message and so on?

“There are naive questions, tedious questions, ill-phrased questions, questions put after inadequate self-criticism. But every question is a cry to understand the world. There is no such thing as a dumb question” :slight_smile:

2 Likes

Agreed, it is a bit unexpected since any other functions land in global space… I don’t have any better answer than that those are special instance specific functions that the engine will “remember” and call for that specific component/instance.

2 Likes

He is, quite literally, my hero.

Unrelated question, how many atlases is too many atlases, is it poor form to give hte tanks, and say helicopters seperate atlases?

It depends. If you have a lot of images for the tank it might need a separate atlas (like if it’s 4096x4096), but if both are small you can put the images in the same atlas, that have some performance benefits because the renderer can reduce the number of draw calls.