How Do I Store Scores? Help Needed!

Hi! I am currently using mydata.lua to store scores as demonstrated in this tutorial

http://www.youtube.com/watch?v=2IDzu6qWRCM

But this score always resets each time I reload the game. What I want to do is to keep a counter of scores to display in the level select screen. Like in the Cut the Rope game, if you get 3 stars on level 1, it will display 3 stars in the level select screen for level 1. And if I exit the game and start it back, the stars for level 1 on the level select screen is still 3. How do I accomplish this task? Are there any tutorials? Thank you!

The simplest way IMO is to save the data locally into a json file. You would overwrite the json file whenever is appropriate for your app (e.g. if you beat the score on a level), and I usually make a point of saving when the app exits or suspends too. 

Then you would load the data when your app starts.

Rob Miracle posted a helpful file to github: http://developer.coronalabs.com/code/super-simple-lua-table-save-and-load-file-functions

If you require this file, then you will be able to call it’s “loadTable” and “saveTable” functions like so:

local loadsave = require("loadsave.lua") --when you need to save the data if currentScore \> highScores[currentLevel] then highScores[currentLevel] = currentScore loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end --and when you need to load the data (e.g. at startup) highScores = loadsave.loadTable("highScores.json", system.DocumentsDirectory)

Obviously you’ll need to fiddle around with that so that it fits your needs, but that’s really all there is to it.

One thing that’s it’s worth keeping in mind is that your app cannot be built with files in the documents directory, so the very first time you try to load the table it will return nil. A simple solution is to use a quick if statement to create a new table if one doesn’t exist yet:

highScores = loadsave.loadTable("highScores.json", system.DocumentsDirectory) if highScores == nil then --no table was found so create one highScores = {} --and populate it with default data for i = 1, totalNumberOfLevels do highScores[i] = 0 end --might as well save the default table ready for next time loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end

Hi Alan! Thanks for the response. When I try to compare values in

if currentScore \> highScores[currentLevel] then highScores[currentLevel] = currentScore loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end  

It came up with an error message “attempt to compare nil with number”. In my test code, I’ve setup currentScore to 0 and then increases in value when I tap on a circle object. When I tap once, the previous error message pops up immediately. And then I tried to print highScores[1] to see if it can print out a value with the following code 

print ("highScores[1] is " .. highScores[1])  

an error message comes up as “attempt to concatenate field ‘?’ (a nil value)”. Can you assist me to whats causing the problem?

EDIT: Oh and I have included the create highScores table to check if its nil like in your second code.

Can you post in some more of your code please?

It could be that currentScore is defined AFTER wherever you have the if statement, or it could be something else entirely. Likewise, my guess would be that you have defined your function with print ("highScores[1] is " … highScores[1]) in it before you have created the highScores table.

Yes certainly, here’s my test code:

local loadsave=require("loadsave") local storyboard=require("storyboard") local scene=storyboard.newScene() cw, ch=display.contentWidth, display.contentHeight currentScore=0 function loadFoo() highScores=loadsave.loadTable("highScores.json", system.DocumentsDirectory) if highScores==nil then highScores={} for i=1, 25 do highScores[i]=0 end loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end end function scoreBtn(event) currentScore=currentScore+1 if(currentScore\>highScores[1])then loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end end function scene:createScene(event) local groups=self.view loadFoo() --Load highScores table if its nil print ("highScores[1] is " .. highScores[1]) --Should print 0 as initial created value circle=display.newCircle(300, 100, 30) circle:addEventListener("tap", scoreBtn) --Add 1 to currentScore end function scene:enterScene(event) local group=self.view end function scene:exitScene(event) local group=self.view end function scene:destroyScene(event) local group=self.view end scene:addEventListener("createScene", scene) scene:addEventListener("enterScene", scene) scene:addEventListener("exitScene", scene) scene:addEventListener("destroyScene", scene) return scene

I’ve just tried out your code and it works exactly as it is supposed to.

It prints “highScores[1] is 0” at the start, and then increases currentScore by 1 every time you tap the circle.

Wow thats weird. It keeps popping an error message on mine. I have the 2076 starter build, which should be the latest one.

The code above is named as game.lua. My main.lua code is 

local storyboard=require("storyboard") storyboard.gotoScene("game")

Does the error come from my main.lua? Thats all the files there is associated with the code. Including also the loadsave.lua I downloaded from your link above.

I also called my scene game.lua, and my main.lua had those same 2 lines of code and nothing else.  

Did you retype your code into the forum, or did you copy and paste it? It’s possible that you might have a typo in your code, perhaps:

--lower case S in "highscores" if(currentScore\>highscores[1])then

rather than:

--upper case S if(currentScore\>highScores[1])then

or something like that.

One thing I would mention now (while your code doesn’t have too much in it), is that I would not advise using global variables. Your currentScore variable is a global, which could cause problems further down the line if you have another scene with a variable also called “currentScore”. I assume these are probably variable which are used in multiple scenes, but there ways to avoid using global variables. Rob Miracle (again) posted this tutorial a few months back http://www.coronalabs.com/blog/2013/05/28/tutorial-goodbye-globals/

I copy pasted the codes to the forum, all of them.

Ya, thanks for the tip. Although this error is still bugging me. I did not change a thing…

Well as I say I copied it straight from your post and it worked, so something is going wrong somewhere. I’m using build 2099, but there is nothing between the 2 builds that should cause this to happen.

Try printing the value of currentScore in your tap function, both before and after you add 1. And the length of the highScores table.

function scoreBtn(event) print(currentScore) currentScore=currentScore+1 print(currentScore) -- this will tell you how many entries there are in the highScores table, it should print 25 (based on your code above) print(#highScores) if(currentScore\>highScores[1])then loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end end

If any values don’t look right, then hopefully that will point you in the right direction.

Oh, I’ve just had another thought. It’s possible that at some point you have saved an empty table to the json file.  

Then when you called the loadTable function, because it found a table (even though it was empty), the  

if highScores==nil then 

is false and the table doesn’t get populated.

In the simulator, try clicking File->Show Project Sandbox, open the Documents folder and then see if the highScores.json file looks like this when you open it in your text editor:

[] --or {}

That means it’s an empty table. If so then delete the file, and start again (and then you’ll need to set up some safety checks to prevent it saving empty tables by mistake).

Where do you get build 2099? The one in https://developer.coronalabs.com/downloads/coronasdk is build 2076.

Also I printed out the values of currentScore and it shows up properly. But the #highScores is 0

First click: 

0

1

0

Second click:

1

2

0

Third click:

2

3

0

the json file is: []

Then delete the file, and start again. That is an empty table.

Wow you’re a genius! After clearing the json file and then reloading the simulator back again, the #highScores value displays correctly as 25 each time I click. And no more error message!!! Thank you very much, Alan!!

One last question, how do I check if a table is empty rather than nil? 

if highScores=={}  

Is that correct?

I don’t think that would work (I could be wrong).

I would do this:

if #highScores == 0 then

The # operator gives you the length of the table. This only works where the indices in the table are numbers, e.g.

highScores = {} highScores[1] = 0 highScores[2] = 5 highScores[3] = 10 print(#highScores) -- prints 3 highScores[4] = 15 highScores[5] = 20 print(#highScores) -- prints 5 highScores[6] = nil highScores[7] = 25 print(#highScores) -- still prints 5, because highScores[6] was nil and so it stops counting there.

If they are strings then you would have to use the pairs() function and count them manually (there’s probably another way but this is how I would do it:

myTable = {} myTable["hello"] = "world" myTable["foo"] = "bar" myTable["test"] = 123 local tableLength = 0 --variable to store number of entries --this is a for loop which will iterate through the whole table for key, value in pairs(myTable) do tableLength = tableLength + 1 end print(tableLength) --prints 3

Thank you so much, Alan! I’ll try messing with it. You’ve been very very helpful! I really appreciate it! 

:slight_smile:

Hi @firdausm,

We actually just posted a tutorial and sample module for saving/keeping scores. It may be helpful for you.

http://www.coronalabs.com/blog/2013/12/10/tutorial-displayingsavingloading-scores/

Brent

Thanks Brent! I’ll take a look at it.

The simplest way IMO is to save the data locally into a json file. You would overwrite the json file whenever is appropriate for your app (e.g. if you beat the score on a level), and I usually make a point of saving when the app exits or suspends too. 

Then you would load the data when your app starts.

Rob Miracle posted a helpful file to github: http://developer.coronalabs.com/code/super-simple-lua-table-save-and-load-file-functions

If you require this file, then you will be able to call it’s “loadTable” and “saveTable” functions like so:

local loadsave = require("loadsave.lua") --when you need to save the data if currentScore \> highScores[currentLevel] then highScores[currentLevel] = currentScore loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end --and when you need to load the data (e.g. at startup) highScores = loadsave.loadTable("highScores.json", system.DocumentsDirectory)

Obviously you’ll need to fiddle around with that so that it fits your needs, but that’s really all there is to it.

One thing that’s it’s worth keeping in mind is that your app cannot be built with files in the documents directory, so the very first time you try to load the table it will return nil. A simple solution is to use a quick if statement to create a new table if one doesn’t exist yet:

highScores = loadsave.loadTable("highScores.json", system.DocumentsDirectory) if highScores == nil then --no table was found so create one highScores = {} --and populate it with default data for i = 1, totalNumberOfLevels do highScores[i] = 0 end --might as well save the default table ready for next time loadsave.saveTable(highScores, "highScores.json", system.DocumentsDirectory) end