armory icon indicating copy to clipboard operation
armory copied to clipboard

write/read nodes / load save levels

Open zartan9 opened this issue 3 years ago • 14 comments

hi im amazed at all the features of armory! figuring out level loading (through all your support), now saving... I looked around a bit, but cant find a working way to load game levels (scenes) if there is a link to it please let me know , also would it work for all export options (mobile/desktop etc). Thanks happy holidays

zartan9 avatar Dec 25 '20 21:12 zartan9

Hi,

there are a bunch of options (and they are badly/not documented 😨):

  • Read/Write File node: Reads or writes a string from a file, looking in the code revealed that it's only implemented in Krom. I guess it should be possible to implement this for other targets as well, html5 might be a bit tricky because of browser restrictions. With this node you would need to impement all the decoding/encoding of the level into a string yourself.

  • Read/Write JSON node: Lets you read/write .json files. This is a bit easier to use as you can use it to store types like (nested) dynamic arrays. However, this is Krom only as well :(

  • Read/Write Storage node: Those nodes should work on all platforms as they rely on a Kha API. They allow you to access a dictionary/map (key value pairs) that (I guess?) is stored between sessions. It might be a bit tricky/tedious though to store levels in there, but for just storing some informations about the player character for example should be very fast to implement for you. But this seems to be the way many html5 games save the player's progress for example due to the restrictions I mentioned above: https://stackoverflow.com/a/34847739/9985959.

There is no generalized built-in way of storing levels because it depends from game to game what should be saved and what not.

I will add a docs label so that we know that the documentation must be improved for this. Also we should think about implementing those nodes for other targets and make them more intuitive to use. Maybe we can utilize the storage API also for the other nodes above, it seems to have a range of useful functions.

Thanks :)

MoritzBrueckner avatar Dec 27 '20 23:12 MoritzBrueckner

thanks! Wow Armory is like a diamond in the rough...so much power but trying to harness it takes time!! I think ill go with option 3 since id like to make game for many formats... i found the right nodes... not sure of "dictionary/map/key" thing... Im getting confused on Keyboard (press "b" to load) with "key" is this the "scene001" or some string to type in to load... i like visual scripting nodes since i dont really know C# etc but i guess ill learn some things load as i go along.. Screenshot attached..

zartan9 avatar Dec 28 '20 02:12 zartan9

Imagine a bunch of boxes with names on them. Those names are called keys. In each box there can be one and only one item, which is called a value. If you set a value and there already exists one, the old value for that key is overridden. So for example you would save things like: player_health as a key with 100 being the value. Or player_position being [5.0, 10.0, 1.0] for example.

A common name for that box/item thing (multiple boxes together!) is a map (this is how it is called in Haxe or Java for example) or a dictionary (Python e.g.).

However, such questions are better suited for the forum, Github is mainly used for bugs and feature requests. But please let this issue open for know until there is at least some documentation about the current state of those nodes mentioned above :)

MoritzBrueckner avatar Dec 28 '20 21:12 MoritzBrueckner

ok ill try to wrap my head around this! ...for a scene loader, the map would be "current_scene" with the key of 1... (First level/scene)... and next level would have key of 2 (2nd level)... and each value is auto over ridden so whereever the player stops playing, the latest level is the last value (2 for example) so... when it loads (pressing button b) current_scene i dont have to put anything in the default (its automatic/ like leaving cookies or something??) sorry if what im saying doesnt make sense just trying to figure it. thanks!

zartan9 avatar Dec 28 '20 22:12 zartan9

for a scene loader, the map would be "current_scene" with the key of 1

Not really, the map is the entire set of key/value pairs (I improved my answer above). The key would be current_scene and the value could be 1 or 2 depending on the scene for example.

and each value is auto over ridden so whereever the player stops playing

That depends on what you programmed. If you update the value each time the player moves, then yes (like auto save, but better do something like that only every few seconds for example for better overall performance). You could also only set that value if the player clicks on a Save button for example.

If you then load the game you have to read that value and set the current scene according to that (it's a lot of manual work...).

like leaving cookies or something??

Yes pretty much like that. If I'm not mistaken the storage system uses local storage on the html5 target which is similar.

MoritzBrueckner avatar Dec 28 '20 22:12 MoritzBrueckner

@zartan9 Hi, If you like to load a level/scene, then use the Spawn Scene node . This is when you have already created multiple game levels with different objects placements, materials, and so on in different Blender scenes. Then during the gameplay, you could use Remove Scene Active (node) to remove the current scene and then spawn the new scene.

If you like to store player information(Ex: Health, Name, etc.), statistics(Ex: High Scores, time, etc), transforms of certain objects and so on, you could use the Read/Write storage Node

Hope this helps.

QuantumCoderQC avatar Dec 28 '20 22:12 QuantumCoderQC

Thanks everyone for clarifying but sounds like this may be over my head (Im more of a modeller)... can there be a simple working example uploaded to see how when pressing "load" button it loads the "spawned" scene/last played level?...

zartan9 avatar Dec 28 '20 23:12 zartan9

LoadSaveScene.zip

Made you a small example file, you can switch scenes with "1" and "2" and there is auto-save. However, it doesn't seem to work on Krom (in html5 it works). Also, reading from storage on game init doesn't work because the page probably hasn't completely loaded. That's why there is a small timer before the scene gets set. I'm not sure how to fix this, adding some workaround behaviour would need to be documented, it's probably best to add an option to the On Init node that's activated when the page has loaded on html5.

MoritzBrueckner avatar Dec 29 '20 17:12 MoritzBrueckner

@MoritzBrueckner Thanks for the file. The interesting thig is when the example is run in Krom, there is in fact a file called default.kha created under C:\Users\USER\Saved Games\LoadSaveScene on Windows.

When opened with notepad, it reads oy8:scene_idi2g. This is the case when game was closed with Scene 2 active.

However, it does not seem to load the file when the game starts again.

Edit A recently published game "Dino Run" on discord by user "!Cyclone" is successful in saving game high scores and retrieving them. It was also made fully with logic nodes.

QuantumCoderQC avatar Dec 29 '20 18:12 QuantumCoderQC

Interesting. I printed out the values and it even correctly reads the storage, there must be some other issue. It would be awesome if we had automated unit tests for logic nodes on all targets but that requires a bunch of work :/

Edit: Somehow we also need a timer here, but it needs to be set to a higher value. I guess there are some problems with setting scenes during initialization.

Edit 2: This is probably the problematic line of code: https://github.com/armory3d/armory/blob/master/Sources/armory/system/Starter.hx#L54 (tabs and spaces in that file are weird but it is called in the iron.App.init() callback, so the scene is set after the traits are initialized if the first update() timetask is executed before the init call ends.

MoritzBrueckner avatar Dec 29 '20 20:12 MoritzBrueckner

hey thanks! I pressed scene 2, and when compiled again, it loaded as scene 2. but after that i loaded it it went back to1! I also then did the same thing (pressed to scene 2) and it was 2 twice, but then went back to 1!!! Im on firefox! at least i can see how it is done (integers!?!) thanks.

zartan9 avatar Dec 30 '20 03:12 zartan9

I guess its a case of a race condition, the outcome depends on whether Iron first initializes all traits or first sets the default scene. Increasing the timer could help, but its not a good solution, only an ugly workaround.

at least i can see how it is done (integers!?!)

You can use integers, yes, but all primitive data types such as floats, booleans, strings (not really a primitive type though) should work. Arrays and maps should work as well.

This issue should probably get split into multiple:

  • Better documentation for the read/write file nodes
  • Add support for more targets for those nodes
  • Fix the Iron scene init issue (over on the Iron repository)

MoritzBrueckner avatar Dec 30 '20 15:12 MoritzBrueckner

wow! complex stuff!! i heard of arrays i think that may work best if i have many levels... ill look into armory documentation on arrays (woa, array intergers/array vector/etc where did you say i can hire help for my game !?) thanks!! happy new year soon

zartan9 avatar Dec 30 '20 20:12 zartan9

Case someone would like to know it in the docs, In Linux the directory used by Read/Write storage is different. It is in a hidden folder named as the .blend file inside Home folder.

@MoritzBrueckner, i guess this issue with on init can be also solved with your refactor, so i am leaving this reminder.

knowledgenude avatar Mar 01 '21 11:03 knowledgenude