Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Saving levels (only) help!
Started by dodi_games Sep 08 2018 05:50 PM

25 replies to this topic
[TOPIC CONTROLS]
Page 1 of 2 1 2
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

Hi guys! This time I do not have any code. I'm stuck. This is what I want to do and I do not know how to start. I want to have a menu scene where the word "play" appears the first time and when the player came back the word "continue" appears. Easy I can do this button and listener. What I do not know where to start is save current level, so the next time the player enters to play, press continue button and go to the level he left. My game is super simple, two objects, and different challenges per level, nothing more. I have read what I found in google and the Corona "higscores" guide but I can not understand how to do it so that only the level is saved, my game does not contain coins, scores, lives, nothing of that, only levels.

Thanks in advance
DoDi

[TOPIC: post.html]
#2

fungrip

[GLOBAL: userInfoPane.html]
fungrip
  • Enthusiast

  • 53 posts
  • Corona SDK

Not knowing the exact state of your data during gameplay, it's hard to give specific advice. You might want to check out this post to get some idea



[TOPIC: post.html]
#3

richard11

[GLOBAL: userInfoPane.html]
richard11
  • Contributor

  • 162 posts
  • Corona SDK

My general game template in the marketplace does this for you (wink wink), but in a nutshell what I tend to do is store all of the game variables in a table, and then just write that whole table out to a save file either whenever something is updated (like the level you're on) or manually via a save button.

The menu scene simply checks for the existence of that file and replaces your table with its data when the continue button is used, or replaces with some default values if the new button is used.

Subject to the game, it might actually be preferable to have a continue button that just returns you to the play scene without changing the data, and then a load button to do the above. Perhaps with a list of save files rather than just the one. The mechanic is the same though.

[TOPIC: post.html]
#4

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

what I have has doubts to be able to start is for example:

local scores = 1000

here I have a value that I can handle in any scene, but how do I get the value of the scene completely? how I assigned to the json.encode to save the level number where the player stayed and when you press the "continue" button, take me to that scene.



[TOPIC: post.html]
#5

schizoid2k

[GLOBAL: userInfoPane.html]
schizoid2k
  • Contributor

  • 543 posts
  • Corona SDK

My free plugin, GBC Data Cabinet, will allow you to manage and (optionally) save your game data.  

 

Basically, you save your data at some point (after each level, for example).

Next time, when the player starts your game, you read the data.  GBC will store all data in a table for you.

You can then determine where to continue based on value(s) of your data.

 

I have some examples of using GBC Data Cabinet here.


  • agramonte likes this

[TOPIC: post.html]
#6

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 127 posts
  • Corona SDK

You could also look into this simple load save function made by Rob Miracle: https://github.com/robmiracle/Simple-Table-Load-Save-Functions-for-Corona-SDK.

I've used it, or something very similar to it, in almost every project of mine. 


  • dodi_games likes this

[TOPIC: post.html]
#7

agramonte

[GLOBAL: userInfoPane.html]
agramonte
  • Contributor

  • 757 posts
  • Corona SDK

I use something similar to what Rob posted in most of my games, but I have been thinking of switching to GDC Data Cabinet because it also has stacks and I use the GDC Language Cabinet in every game so far and I have never had a problem with it. 

 

So using the Cabinet you can make each level a cabinet and then stuff a table with all your variables into the cabinet. When I user clicks on level 5 you can just call the cabinet Level5 and pull down all the variables.


Edited by agramonte, 09 September 2018 - 05:37 AM.

  • schizoid2k likes this

[TOPIC: post.html]
#8

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

Thanks to everyone for your replies. I had thought about the GDC but as in this case the only thing I need is to return to the level where I stayed, which contains only two objects interacting with each other with physics, I do not think I should go too far in code to carry out that task. I am reading and evaluating, I would not like to add things to my code that I still do not understand.

 

One question: the whole "composer" scene is a variable?

--save and load last level
local json = require( "json" )

M.lastLevel = {}

local filePath = system.pathForFile( "levels.json",
  system.DocumentsDirectory )

function M.loadlevel()

  local file = io.open( filePath, "r" )

  if file then
    local contents = file:read( "*a" )
    io.close( file )
    M.lastLevel = json.decode( contents )
  end

  if ( M.lastLevel == nil ) then
    M.lastLevel = { 0 }
  end

end

function M.saveLevel()

--I'm stuck cause don't know how to store the level in the lastLevel table

end

It's all about newbie scope. I think I'm going to use the composer.setVariable() and composer.getVariable() with an if/then to check if level(whatever#) exist and then go to the level. 

 

thanks

DoDi



[TOPIC: post.html]
#9

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 6,969 posts
  • Corona SDK

You need to save a data state.  

 

Every non-static item in your level that can change state and that needs to be saved/restored must have state data.

 

The first time your game runs, you use default values for those states.  

 

As your game progresses, you extract that state from the identified objects and save it in your 'save table'.

 

Later when you restore you game, you re-build the level, but check the previously saved and not reloaded 'save table' for state data which applies to the current object you're building.

 

For a player, state data might be:

  • current <x,y>
  • current vx, vy
  • current rotation
  • current hit points
  • current score
  • ...


[TOPIC: post.html]
#10

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 6,969 posts
  • Corona SDK

I answered a similar question in July 2015 and made an example of 'persisting' info about two objects and restoring them on reload.

 

https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2015/07/persistingData.zip

 

This is the basic principle.

 

How you save, when you save, that depends on your needs.  

 

In my example I save as soon as the object moves.  That is overkill for most games.  Just save a set times or places.



[TOPIC: post.html]
#11

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 6,969 posts
  • Corona SDK

One more note.  My code is super basic and does not incorporate the concept of a id.  It depends on the creation order always being the same which is unreasonable for large data sets.

 

It is better to save the data for each object based on an ID.  

 

Always create your objects' data with IDs and restore by looking up those IDs.

 

Finally, I could make you a demo and helper module as a Level-1 hit if you're interested.

https://sellfy.com/p/sank/



[TOPIC: post.html]
#12

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

thanks @roaminggamer but I only need to save the level, when the user open the game appears a "play" button, when the user left the game and came back appears a "continue" button to take him to the last level he played. No scores, no lives, no position, no hit points, no sounds, nothing, just the level the player left.

 

this is what nI have:

---------------------------------------------------------
--save and load last level
local M = {}

local composer = require( "composer" )
local json = require( "json" )

local lastLevel = composer.getVariable( "level" )

local filePath = system.pathForFile( "levels.json",
  system.DocumentsDirectory )

function M.saveLevel()
  if filePath then
    local contents = json.encode(lastLevel)
    file:write( contents )
    io.close( filePath )
    return true
  else
    return false
end

function M.loadlevel()
  local file = io.open( filePath, "r" )
  if file then
    -- read all contents of file into a string
    local contents = file:read( "*a" )
    lastLevel = json.decode(contents);
    io.close( file )
    return lastLevel
  end
  return nil
end

return M

but know I can't test it cause I don't know how to call the loadLevel function in my play/continue listener to show the "play" or "continue" word and go to last level

 

in every level I use 

--level-1
composer.setVariable( "level", 1 )
--level-2
composer.setVariable( "level", 2 )
--etc, to level 10 for now

Am I doing things right for the task I want to carry out?



[TOPIC: post.html]
#13

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 6,969 posts
  • Corona SDK

That isn't how I would do it, so... I'll say it isn't ideal.

 

It sounds like you're not tracking state at all, which makes my answers a waste of time.

 

I thought you wanted to create a game where you loaded a prior level and restored the state of the level.  My bad.

 

1. I don't know why you're making those compose calls.  I do not see a purpose for them.

 

2. Just do this.

 

A. In main.lua load a table, if not found give it default values:

-- using SSK, not your code: 
-- https://roaminggamer.github.io/RGDocs/pages/SSK2/extensions/#saving-loading-tables
-- Load existing saved data or make new table.

_G.curLevelData = table.load("levelData.json") or { curLevel = 0 } -- 0 not started yet

-- save immediately
table.save( _G.curLevelData, "levelData.json" )

B. Whenever you need to load the last loaded level, just refer to the table:

if( _G.curLevelData.curLevel == 1 ) then
  ...
 
-- whatever

C. Whenver you load a new level change the 'curLevel' value and save

-- just got done loading level 2 or about to load level 2
-- whatever makes sense for you

_G.curLevelData.curLevel = 2
table.save( _G.curLevelData, "levelData.json" )

PS -

 

In your main menu, just check the 'curLevel' and if it is > 0, show a 'continue' button.



[TOPIC: post.html]
#14

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

It makes sense to me if my game had a common "game.lua" where I substitute values, but my game is so simple, just a few lines of code, I decided that each level is a game, in which, when losing, the composer scene is restored and if you win you go to the next "level # .lua" where the same thing happens but with a different scenario. Every level is a different mini-game.  What I need to understand is how I call composer.gotoScene() from menu to level-1 but when I pass to other levels there is no way to go back to the previous level, if the player closes the app I would like the menu, instead of show the word "play" show the word "continue", and when the player press that button takes me to the last level played, which is a different composer scene at any level, is another lua file.

 

In other words. First time, menu takes me to level-1, in any other time menu takes me to the last level played. But I need to save those values for when the player closes the game or decides to turn off or restart his phone and decides to play again can go to the last level where he stayed.

 

I thank everyone for their time and all the help they have given me

DoDi



[TOPIC: post.html]
#15

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

I try to do the best I can using what "Corona SDK" gives us, that's why I'm using composer. I understand that the only way to go to a next level is using composer.gotoScene() but I do not know how to tell composer.gotoScene() to go to the scene that contains the value that identifies the last level played which I must save in a .json file.

 

:wacko:  :wacko:  :wacko: 



[TOPIC: post.html]
#16

nick_sherman

[GLOBAL: userInfoPane.html]
nick_sherman
  • Corona Geek

  • 1,509 posts
  • Corona SDK

composer.gotoScene(mySceneNameVariableFromJson)

  • SGS likes this

[TOPIC: post.html]
#17

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,527 posts
  • Corona Staff

There are many approaches to communicating the level to play to a scene:

 

1. You can use a non-global data table that's required in each module and scene (which is a module): http://docs.coronalabs.com/tutorial/basics/globals/index.html (which I know you're familiar with). Simply set a table member to have the value the level to play:

myData.currentLevel = 3

for instance.

 

2. Composer has a set of API calls that performs a similar function to the non-global data table. After all when you require composer you are getting a Lua data table that's included in your scenes. We added a .setVariable() and .getVariable() API for this:

 

In your scene where you select the level:

composer.setVariable("currentLevel", 3)

in the scene where you need the information:

local currentLevel = composer.getVariable("currentLevel")

3. Composer allows you to pass parameters on the gotoScene() call:

composer.gotoScene("game", { params = { currentLevel = 3 }, effect = "crossFade", time = 500 } )

then in your scene, during the scene:create() and scene:show() events, you will have a populated table:

function scene:create( event )
    local params = event.params
    local currentLevel = 1

    if params and params.currentLevel then
        currentLevel = params.currentLevel
    end
    ...
end

I'm sure there are other techniques as well.

 

Rob


  • agramonte likes this

[TOPIC: post.html]
#18

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

thanks for your help.
I do not have a common game.lua module in wich I replace values in each level, NO.
I have 10 different modules and I'm getting errors in the composer.gotoScene()
I have a progress module:
 
---------------------progress.lua-------------------------
--save and load last level
local M = {}
local composer = require( "composer" )
local json = require( "json" )
local lastLevel = composer.getVariable( "level" )
local filePath = system.pathForFile( "levels.json",
  system.DocumentsDirectory )
function M.saveLevel()
  if filePath then
    local contents = json.encode(lastLevel)
    file:write( contents )
    io.close( filePath )
    return true
  else
    return false
end
function M.loadLevel()
  local file = io.open( filePath, "r" )
  if file then
    -- read all contents of file into a string
    local contents = file:read( "*a" )
    lastLevel = json.decode(contents);
    io.close( file )
    return lastLevel
  end
  return nil
end
return M

ok, now in every scene at top I require the progress module and
set the "level" variable to level # in this example 3, but it's
the same in level 1 and level 2.
 
---------------------level_3.lua--------------------------
local progress = require( "progress" )
local physics = require( "physics" )
physics.start()
composer.setVariable( "level", 3 )
 
I call the M.loadLevel() function in every levels#.lua scene "create"
and the M.saveLevel() function in every levels#.lua scene "show"
 
-- create()
function scene:create( event )
   progress.loadLevel()
end
-- show()
function scene:show( event )
   progress.saveLevel()
end
 
ok, and in menu.lua following @nick said
local function gotolastLevel()
 composer.gotoScene( progress.lastLevel )
end

but I', getting this error

 

 

Attached Files



[TOPIC: post.html]
#19

agramonte

[GLOBAL: userInfoPane.html]
agramonte
  • Contributor

  • 757 posts
  • Corona SDK

I am going to give it a hack since I think I understand what you want, but I am probably also wrong. 

 

1. In main.lua retrieve the level from json from a file (from what I am reading I think you have that down if not you can use what Rob originally posted).

2. From what I am also reading you have a bunch of levels called something like level1.lua, level2.lua, level3.lua. I am guessing you have a folder called levels. So from main I would call:

local level = 2 -- You would replace this with whatever code to retrieve the level from a file.
composer.gotoScene("levels.level"..level)


[TOPIC: post.html]
#20

agramonte

[GLOBAL: userInfoPane.html]
agramonte
  • Contributor

  • 757 posts
  • Corona SDK

Just read your last post you are almost there. Use Rob's library it won't get any easier and the code is there.

 

https://github.com/robmiracle/Simple-Table-Load-Save-Functions-for-Corona-SDK

 

In every level scene as the scene:show save the scene number. You already know it since you are creating 1 scene per level:

local loadsave = require("loadsave")

myTable = {}
myTable.sceneNumber = "1" -- This would be for level 1

loadsave.saveTable(myTable, "myTable.json")

Then when you launch the app do what I did above but retrieve the scene using Rob's library:

local loadsave = require("loadsave")
local myTable = loadsave.loadTable("myTable.json")

local level = (myTable.sceneNumber or 1)
composer.gotoScene("levels.level"..level)

Edited by agramonte, 09 September 2018 - 12:02 PM.


[TOPIC: post.html]
#21

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

@agramonte

why in the scene:show you use global myTable and in main you use local myTable?

 

I am experiencing many global and local problems with myTable, I do not understand anything …

If someone can explain to me how this work I will appreciate. It's new for me and hard to understand.

 

what is the roll of (t, filename, location) in function M.saveTable(t, filename, location)

do I need to chage those values?



[TOPIC: post.html]
#22

agramonte

[GLOBAL: userInfoPane.html]
agramonte
  • Contributor

  • 757 posts
  • Corona SDK

Sorry, both should be marked Local. I just copy and pasted from the sample in the link. The t is the table you want to save.

 

For example:

local t = {}
t.level = "1"


filename is the name of the file to save to the file system: "pref.json" or "level.json" or "whatever.txt"

 

And finally, location is where you want to save that file.


  • dodi_games likes this

[TOPIC: post.html]
#23

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,527 posts
  • Corona Staff

t = table to save

filename = name of file to save the data to

location = folder to save the file to (optional, defaults to system.DocumentsDirectory)

 

I suspect that @agramonte simply left the "local" off of the myTable variable.

 

If you're having trouble understanding global vs. local, getting a lot of nil variables that you think shouldn't be or things behaving weirdly there is a good chance you need to understand "scope" better. This tutorial explains it:  http://docs.coronalabs.com/tutorial/basics/scope/index.html

 

It's really a simple concept once you understand what defines a block of code. It really helps if you properly indent your code so you can easily see each block of code.

 

Consider:

local myFirstVariable = 1
local function myFunction( )
local mySecondVariable = 2
if mySecondVariable == 2 then
local myThirdVariable = 3 
print( mySecondVariable ) -- prints 2
end
print( myThirdVariable ) -- prints nil
end

vs

local myFirstVariable = 1
local function myFunction( )
    local mySecondVariable = 2
    if mySecondVariable == 2 then
        local myThirdVariable = 3 
        print( mySecondVariable ) -- prints 2
    end
    print( myThirdVariable ) -- prints nil
end

The blocks of code are identical other than some formatting, but the first block is really hard to understand where one block of code begins and ends. The indentions should show you that there actually three blocks of code. One is a "main chunk" (code lined up against the left edge, a function block named myFunction() and a "if" block.

 

local variables can only be seen in the block they are defined and any children blocks, for instance, the "if" block is a child of the myFunction() block. The myFunction() block is a child of the main chunk. 

 

This means that myThirdVariable only exists and can be seen inside the "if" block. The mySecondVariable only exists inside the function and can only be seen there. But since the "if" block is a child of the function, it can also see mySecondVariable. One the "if" block is done, myThirdVariable goes away and isn't available to the rest of myFunction().

 

The indenting of the code really helps you visualize what's going on. 

 

Rob


  • dodi_games likes this

[TOPIC: post.html]
#24

agramonte

[GLOBAL: userInfoPane.html]
agramonte
  • Contributor

  • 757 posts
  • Corona SDK

Here you go a simple example. I created this from the game template. I just copied and pasted 6 scenes. Imagine they are your 6 mini-games.  There is just one button in the middle and you click and go to the next scene. Then if using the simulator you refresh, it remembers where you left off.  I am using Rob's library.

Attached Files


  • dodi_games likes this

[TOPIC: post.html]
#25

dodi_games

[GLOBAL: userInfoPane.html]
dodi_games
  • Contributor

  • 265 posts
  • Corona SDK

thanks to everyone especially @agramonte. I am studying what you upload and I am understanding much better. I believe that I will soon be able to implement it.

 

:) 




[topic_controls]
Page 1 of 2 1 2
 
[/topic_controls]