Level switch (change level)

Guys, i read something about proxies to change levels, but its hard to understand, the script files are incomplete so we can guess what to do, also the structure of collections is not showed on images making dificult to understand.

I would like some kind of help, nothing to much, i’m not professional programmer or something, but i have love2d background and already made a Random dungeon generator and i want remake that sort of game on DEFOLD.

If possible could someone do a small Tutorial on how to change levels?

I still working on this if i find the answer i will post … my idea is make level1 and level 2, both with a GO that creates procedurally the level … so we change from one to another as the deep of the dungeon increases, and the character info is preserved … like on unity “dont destroy on load”.

I hope i find the answer to share but if someone know we appreciate any help :smiley:

3 Likes

The manual on collection proxies should describe the concepts behind it. The short version is:

A collection is a collection (duh!) of game objects that have some kind of relationship. A typical example is a game level with the map, monsters, factories and scripts.

A collection proxy references a collection. A collection proxy can load and unload the collection it references. You load the collection by doing msg.post(“path/to/your#proxy”, “load”). When the proxy is loaded you get a “proxy_loaded” message back.

So create your levels as individual collections. Create one collection proxy per collection/level. Create a controller script of some kind that can load/unload levels depending upon the progress of your player.

7 Likes

Ty my friend, but as i said, when you dont make explanation for 4 year old kids, ppl with no experience on subject get lost… the collection proxies was read by me before ask … i also try implement … since i’m not programmer i had bad times to deal with it… that part of manual, i risk to say almost all the other should at least show the scripts in complete form … so we can understand better( i mean for guys like me that understand programming as a kind of recipe).

Ty for you help…

Until now i could manage to make the change like this : in main.colection i put a GO with the relevant proxy collection component, attached also a script to the GO where the init function call the level1.

At level1 i add a GO similar to main collection but send us to level 2.
At level2 i did the same but sending us to level1 again.

Its for test purposes only but i could kind figured out how to do it, but i believe i should be capable to have the “level switcher” on main collection and be capable to access it from the level.collection actual.
Also i still working on proper disabling “last level” part of the problem… once it done i believe i can go for “dont destroy on load” function to keep player his data while switching levels.

But in the and the collection proxies manual didnt helped … i could not figured out from reading i needed to past the code in very different parts of the script until be capable to implement.

so i still in trouble, any one wanting to give a hand i will appreciate.
BRITZL ty alot for your help.

OK i actually found some kind of solution, i dont know if this is the best solution but is better then the trick i used to solve the problem before.

There it goes:

Create a GO in main.collection … name it : nextlevel.
Add 3 components on it: 2 collection proxy ( one for each level), 1 script

edit the script with what you think is necessary, like imput for change levels … and this:
function on_message(self, message_id, message, sender)
if message_id == hash("proxy_loaded") then
-- New world is loaded. Init and enable it.
msg.post(sender, "init")
msg.post(sender, "enable")
end

end

  • observation you will need to learn disable the previous level … and add here to.

inside the “input” or “collider” or whatever you use to activate the level change you edit this.

msg.post("main:/nextlevel#level2", "load")

ok the explanation for this is: the first part “main:” refers to a collection …you can do like this: msg.post("/nextlevel#level2", "load")
to use the absolute path if is inside of the same collection or:
msg.post("nextlevel#level2", "load")
to use the relative path, this mean only for the GO its calling the function.

So since all the game is wrapped inside the main collection, make sense you put the “nextlevel” GO on it and make it call other "level.collection"s from it … but if in you game like in mine some part inside the “level” that will call the “nextlevel”, then you will need to refer to something outside the collection where you are so you need edit like this:

msg.post("main:/nextlevel#level2", "load")

I dont know if this is the best option, but it works for me … and i dont know if is properly explained, but i hope it helps someone out there.

The only remaining thing if how to keep data from one level to another … i still didnt get there, but will be my next step, until only had the idea of keep in “main.collection” a “datatransfer” GO and update it before switch levels, destroy the “player” GO … and on next level when it is loaded load a “player” GO and use the “datatransfer” GO to download the data on it.

anyone in the mood to help will be very welcome.

I also use this method. It works fine. But when the levels count is growing, I need to increase the max proxy count. I don’t know whether it will affect performance or not.

I would love to know the recommended method to doing it too.

Yes this method works fine for me too. I also use the script to handle load and unload of each (logical) scene/level.

Name your GO as ‘loader’ and name its script as ‘script’.

in the script

local function load_title(self)
	msg.post("#title", "load")
end

local function unload_title(self)
	msg.post("#title", "unload")
end

local function load_battle(self)
	msg.post("#battle", "load")
end

local function unload_battle(self)
	msg.post("#battle", "unload")
end

function init(self)
	msg.post(".", "acquire_input_focus")
	load_title(self)
end

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

function on_message(self, message_id, message, sender)
    if message_id == hash("start_game") then
    	unload_title(self)
    	load_battle(self)
    elseif message_id == hash("back_to_title") then
    	unload_battle(self)
    	load_title(self)
    elseif message_id == hash("proxy_loaded") then
    	msg.post(sender, "enable")
    end 
end

Then from your other scripts in each scene/level:

msg.post("main:/loader#script", "start_game")

msg.post("main:/loader#script", "back_to_title")

For the shared data you can simple use lua table to stored your data. Make it a lua module and then you can require it everywhere from your program. But I’m not sure this is a recommend way in Defold or not.

shared_data.lua

local M = {
  player = {
    gold = 0,
    silver = 0
  }
}
return M

any script files:

local shared_data = require('shared_data.lua')
print(shared_data.player.gold)
shared_data.player.gold = 50

And there is a restrict that game objects in one scene/level (in one collection proxies) cannot post message to other game objects in other scene/level (in other collection proxies) in the case that you’ve loaded multiple collection proxies at the same time.

5 Likes

This is more or less exactly how I’d do it as well. There really isn’t more to it than this. You need a central script that loads and unloads collections, and that’s it.

1 Like

Would it work to create a static map like object that associated string variables with map collections, so one could theoretically parse messages starting with “Load:{Level}” and load the level indicated?

Not exactly sure how you mean, but it sounds like you want some kind of data structure that maps level ids to collection proxies and load the correct proxy based on the current level?

I’ve been reading up on the collection proxy manual and it seems like you can load proxy content in function init and when it is time to move onto the the next scene - you can call the message

if message_id == hash("proxy_loaded") and toNextLevel == true then
   msg.post(sender, "init")
   msg.post(sender, "enable")
end 
-- Then call the unload msg for the current collection

However, I understand the “proxy_loaded” message is only called when the “load” msg is sent to the proxy. So in this case it’ll be called once on initialisation.

So, is it possible to load an invisible disabled collection and have it initialised when a certain event occurs?

I’m not sure I understand. You load a collection from a proxy using either of:

msg.post("#myproxy", "load")
msg.post("#myproxy", "async_load")

When the proxy has loaded you get a message back:

function on_message(self, message_id, message, sender)
    if message_id == hash("proxy_loaded") then
        print("#myproxy has loaded")
    end
end

At this point all resources used by the collection has been loaded into memory but any game object components in the collection is still inactive. The collection is still “hidden”.

You can trigger the init() function of any script run and any initialization code needed by components using the init message:

msg.post("#myproxy", "init")

This will still not show anything. Every component in the collection is still disabled.

You can enable all components in the collection using the enable message. If you haven’t sent an init message then initialization will also happen.

msg.post("#myproxy", "enable")

At this point the contents of the collection is initialized and visible.

If you keep track of the fact that you have received a proxy_loaded message for a collection then you have full control over when to initialize and enable the collection.

2 Likes