[SOLVED] How to create a queue of spawning game objects after one another and stopping based on a countdown timer

I’m trying to spawn objects in a queue. Like I do something, that’ll get rid of the object, then there’ll be a next instance of that object, over and over. In the background there’ll be a timer, when that timer ends, the game will stop and return back to the main collection. The object instances don’t need to be stored as data before runtime, there doesn’t need to be any data stored about they’re destroyed.

Currently, I have a collection that contains a factory that’s intended to spawn a game object ( inside I have a temp mesh, sound component, and a script that holds its properties and functions). I also have a script inside of that collection under a separate game object and am also using Ludobits and the timer script.

I’m not sure how you would create a queue though nor how to use the timer properly.

local countdown = require "ludobits.m.timer"

local function create_object()
	-- create object by way of factory.create
end

local function delete_object()
	-- delete object
	-- call create_object() again, but what about the timer?
end

function init(self)
	-- Once countdown is done, return to main collection, however am not using .once correctly
	--- Create a timer that will call a function after a certain time
	-- has elapsed.
	-- @param seconds The number of seconds before the callback should be invoked
	-- @param callback The function to invoke when the time has elapsed
	-- @return The timer instance
	countdown.once(8, msg.post("main:/main", "game_over"))
end

function on_message(self, message_id, message, sender)
	if message_id == hash("start_minigame") then
		-- start the minigame from this script
	end
end

Oh, this is probably a very old version of Ludobits. The latest version has no timer/countdown module. There is no need for it since Defold has a built in timer nowadays. Here’s how I’d do it:

-- minigame.script

local function delete_object(self)
	if self.current then
		go.delete(self.current)
		self.current = nil
	end
end

local function create_object(self)
	-- make sure there is no object
	delete_object(self)
	self.current = factory.create("#factory")
end

function on_input(self, action_id, action)
	-- spawn a new item on each click/touch
	if action_id == hash("touch") and action.pressed then
		msg.post("/spawner", "spawn")
	end
end

function on_message(self, message_id, message, sender)
	if message_id == hash("start_minigame") then
		msg.post(".", "acquire_input_focus")
		create_object(self)
		timer.delay(8, false, function()
			msg.post("main:/main", "game_over")
		end)
	end
end
1 Like

Ah I see, thank you, I was looking at an example, the Mini Mega Games, I think that’s what it’s called, because I was also trying to do something Warioware-esque. Thanks Britzl. Speaking of that, would flow from ludobits be necessary? From what I understand it’s basically, do this then do this, like a sequence.

Mini Mega Party, oops.

Yeah, the flow module can help when you need to run several asynchronous things one after the other. It is useful sometimes, other times it only complicates things.

1 Like
local function create_book(self)
	-- attempt to call global 'delete_book' (a nil value)
	delete_book(self)
	self.book_id = factory.create("#random-book-factory",  vmath.vector3(-1.0, 0, 0))
	
	go.set(self.book_id, "position.x", 0.0)
	go.animate(self.book_id, "position.x", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_INQUART, 2)
	sound.play(self.book_id.."#sliding-sound")
end

local function delete_book(self)
	if self.book_id then
		go.set(self.book_id, "position.x", -1.0)
		go.animate(self.book_id, "position.x", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_INQUART, 2)
		sound.play(self.book_id.."#sliding-sound")
		go.delete(self.book_id)
		self.book_id = nil
	end
end

So I’m getting an error -- attempt to call global 'delete_book' (a nil value), but I don’t think that should be happening since there’s the if statement, so I’m confused?

Lua runs top->down. Move delete_book function before the create_book

3 Likes

Oh I see, thank you, I didn’t realise that.

1 Like
ERROR:GAMEOBJECT: Could not initialize when spawning /minigames/inventory/book/book.goc.
ERROR:GAMEOBJECT: Could not spawn an instance of prototype /minigames/inventory/book/book.goc.
ERROR:SCRIPT: ...games/inventory/insert-book-code/insert-book-code.script:16: 'nil' does not have any property called 'position.x'
stack traceback:

I put the delete_book function before the create_book function.


This is the game object, so I’m confused as to why it’s not able to spawn an instance of it.

It is hard to say. But I guess your previous code is from /minigames/inventory/book/book.script file which is in the Gameobject. If so, you are trying to remove Gameobject, inside the Gameobject and trying to spawn a new one from already deleted script file. Very confusing :smiley:

This logic is not correct. You need to have a central script which spawns and deletes gameobjects.

Like this:

1 Like

Ah I see, I was reading the link you sent me, I did make a script though that’s supposed to call the factory.


insert-book-code is what’s housing the code I sent you, not the book.script. Unless I’m misunderstanding how that works.

Again, it is not easy for us to answer this question without seen all the code(I don’t know what is on line x). But error is caused by (probably) self.book_id is “nil” which you can’t set it is position.x. It is better to use pprint() and print() to figure out the problem. If it is nil before you are setting its position then you are doing something wrong by setting self.book_id = nil on somewhere.

No problem, that’s my bad, I’ll just post all of what I have.


image

image
book.script of book.go

function init(self)
	generate_book_appearance()
	generate_book_code()
end

local function generate_book_code()
	self.book_code = ""
	local charset = {} do
		-- Ký tự [0-9a-zA-Z] dưới dạng  mã ASCII: https://www.rapidtables.com/code/text/ascii-table.html
		for c = 48, 57  do table.insert(charset, string.char(c)) end
		for c = 65, 90  do table.insert(charset, string.char(c)) end
		for c = 97, 122 do table.insert(charset, string.char(c)) end
	end
	-- [0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}
	while (string.len(self.book_code) <= 19) do 
		if (#self.book_code % 5 == 0) then
			self.book_code = self.book_code .."-" 
		else
			self.book_code = self.book_code .. charset[math.random(1, #charset)] 
		end
	end
	label.set_text("#random_book_code_label", self.book_code)
	msg.post("/computer#computer", "actual_book_code", self.book_code)
end

local function generate_book_appearance()
	go.set("#random-book-model", "tint", vmath.vector4(1,0,0,1))
end

which is being spawned via random_book_factory from insert-book-code.script.

local function delete_book(self)
	if self.book_id then
		go.set(self.book_id, "position.x", -1.0)
		go.animate(self.book_id, "position.x", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_INQUART, 2)
		sound.play(self.book_id.."#sliding-sound")
		go.delete(self.book_id)
		self.book_id = nil
	end
end

local function create_book(self)
	-- attempt to call global 'delete_book' (a nil value)
	delete_book(self)
	self.book_id = factory.create("#random_book_factory",  vmath.vector3(-1.0, 0, 0))
	
	go.set(self.book_id, "position.x", 0.0)
	go.animate(self.book_id, "position.x", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_INQUART, 2)
	sound.play(self.book_id.."#sliding-sound")
end

function init(self)
	msg.post("/computer#computer", "acquire_input_focus")
	create_book(self)
	timer.delay(8, false, function()
		msg.post("main:/main", "game_over")
	end)
end

function on_message(self, message_id, message, sender)
        -- still trying to figure how to delete book then respawn it again after the code's accepted
	if message_id == hash("delete_book") then
		delete_book()
	end
end

then inside another game object in the same collection, computer.go, I have a script called computer.script. Basically I’m trying to make a ‘check in’ system. A random book spawns, the player types in the computer and has to match the code on it, then if it matches, it destroys the book and the book gets created again until the timer runs out and the collection unloads.

local actual_book_code = ""

function init(self)
	self.message = ""
end

function on_input(self, action_id, action)
	if action_id == hash("type") then
		msg.post("/computer_sound#computer-sound", "play_keyboard")
		self.message = self.message .. action.text
		label.set_text("#code_input_box", self.message)
	elseif action_id == hash("backspace") and action.repeated then
		msg.post("/computer_sound#computer-sound", "play_keyboard")
		local l = string.len(self.message)
		self.message = string.sub(self.message, 0, l-1) 
		label.set_text("#code_input_box", self.message) 
	elseif action_id==hash("enter") then
		msg.post("/computer_sound#computer-sound", "play_keyboard")
		if self.message == actual_book_code then
			msg.post("/minigame_go#insert-book_code", "delete_book")
		end
	end
end

function on_message(self, message_id, message, sender)
	if message_id == hash("actual_book_code") then
		actual_book_code = message.book_code 
	end
end

I was reading Game object API documentation at go.animate and seeing property: id of the property to animateso I thought I could pass in the position, and I remember seeing an example too. I’m probably making a lot of errors in the code and am trying to figure out how to decouple.

This is correct.

At least by using pprint() and print() functions. Lets try to find the problem by using these functions.

3 Likes

Remember this and check out book.script of book.go:

1 Like

I see, I’m still slightly a bit confused by this. So does that mean that it doesn’t matter when on_message is called? What I’m understanding when you say lua is top down is that it means the functions get built in order and has to be called from the top down? Should I be making self.book_id somewhere else.


I ended up debugging it, the typing works, the sound for it works. You seem to be right though, it’s got to do with the book.script so I tried reordering the init to be last. Wait, shouldn’t generate_book_code be run first, before even creating the book? Wait that doesn’t make sense. When you create the book, you’re running the initialising method.

The issue you’re having right now is not because of ordering, you already fixed that. You need to fix the errors you’re getting in the console. Nothing will work if your script crashes. Start from the very first one and fix what it tells you is wrong.

book.script:7: attempt to index global 'self' (a nil value)

1 Like

I see, thank you. About that error though, if the problem is that self.book_code = "" is nil, then I’m either thinking that my goal is to make it either a local variable or to reorder when I create that variable. With local variables, can you pass them through msg.post? I’m slightly confused on how to use it. I was thinking of passing it to my computer.script
image
but I don’t know if message call the book_code variable? I did read the documentation, I’m just confused about the message parameter. Yea no, reordering it wouldn’t work I think, I also just tried that.

No no, back up. Read the error message more carefully.

attempt to index

Attempting to index something means you’re trying to use it as a table and access a value inside it using a key.

attempt to index global

This means that the error is happening when your script attempts to access a global variable.

attempt to index global 'self'

The global variable that you’re trying to use is named: ‘self’.

'self' (a nil value)

The value of the global variable that you were trying to use is nil.
Since you were trying to get a value from the variable using a key, the variable was expected to be a table. Instead of a table, it was nil, hence the error.

[Edit]: Looking for a global variable means that no local variable was found with that name, in the current scope.

1 Like

I see. Oh I see, I’m reading your previous post on Explanation of the “self” argument for dummies and I think I’m somewhat understanding it. I originally used self to make it so the variable be different for every instance so I think I’m doing it right.So line 7 is self.book_code = "". So I’m debugging it again, reaching to the original error, clicking the triangle to forward it and jumping to here.

image

So if self is a table, and I’m indexing a global self from my book.script, then does that mean I was trying to access the self table inside of insert-book-code.script or vice versa?
Sorry, I’m just having a hard time grasping this.

I guess another question would be, what is the key in this case.

Wait, so I’m using the factory to create an instance of a game object, and I’m using self to make sure each variable of each instance of it is unique and not shared, but then inside of that game object, I have a script that’s also attempting to use self to make sure each instance of it has unique variable values. :thinking:

Wait a minute, if I’m already making a new instance of book.go inside of insert-book-code.script, why would I need self.book_code inside of book.script.

1 Like

Line 7 from the error is:

self.book_code = ""

So the key in this case is: "book_code", since

self.book_code

is just shorthand for:

self["book_code"]

The one and only problem is: no local variable named ‘self’ exists in the context where you expected it to. Just take a look at these few lines:

function init(self)
	generate_book_appearance()
	generate_book_code()
end

local function generate_book_code()
	self.book_code = ""

Where’s self?

2 Likes