2
\$\begingroup\$

I am making a Unity game vaguely in the vein of Candy Crush Saga for a client and am moving in circles trying to decide how to manage the data for all of their levels.

In brief, they want to divide the game into multiple regions that each have 100 levels. For example, you might go to the world map, select Teddy Bear Islands, and then go to the winding-path style level select screen similar to King's "Saga" games. My goal is to have the levels be procedurally generated, so the data for each level will consist of dozen or so parameters instead of hundreds of variables. There will only be one scene, and the levels will be built by running the parameters through an algorithm.

The game isn't a match-3, but for comparison, imagine if your level data for a match-3 was just basic parameters like the number of each type of piece and the target score, and each time you started a level it would randomly arrange pieces based on the input parameters (instead of you manually declaring the position of each piece). The data for a level will look something like:

int targetScore
int levelType
float difficultyModifier
int numPieceA
int numPieceB
int numPieceC
int numPieceD

I haven't worked with such a large data set (in terms of the number of levels) in Unity before and am unsure how to structure it. In a less editor-driven engine, I would probably just store all of the levels in XML files. However, since this is a Unity project I feel like I should be making an editor-based solution that will be easier for the client to work with in the future.

With an XML-driven approach, I imagine that I would have an XML for each region containing all of the 100 levels in that region, and each location on the world map would be linked to one of the XML files. When the user selected a region, it would read the levels into an array, and link each of the 100 level buttons to an array index.

For a more editor-driven approach, I might create custom editor windows where the regions and levels can be defined, and then link the region and level buttons to the data from the editor window (I haven't done much with editor windows yet and am not entirely sure how to tackle this approach). Or I could create a serializable LevelData type and a simple MonoBehavior with a public array of LevelData so that the levels could be defined on GameObjects in the inspector.

Each solution has pros and cons, and I'm stuck on what to do. Is there a conventional approach for managing such a large number of levels in Unity? Should I try one of the above approaches, or is there a better solution?

\$\endgroup\$
  • \$\begingroup\$ We can really not help you with making this decision. There are games which lead themselves well to the one-scene-per-level approach, there are games where levels can be implemented as ScriptableObject-based custom assets and there are those where the tools Unity provides are simply inappropriate and you better use files which are edited externally. We can not tell you which case you have here. Only you can tell. \$\endgroup\$ – Philipp May 1 '16 at 8:13
  • \$\begingroup\$ @Philipp I am going to use one scene for all of the levels. Other than that, I am not sure what other information to provide. Data for the levels will consist of a few primitive parameters, e.g. target score, difficulty scale, and a few modifiers, that will get passed into the level generator. I'll update the description. \$\endgroup\$ – user45623 May 2 '16 at 22:51
3
\$\begingroup\$

(Sorry for poor formatting - I'm on my phone)

There are a lot of valid options. If I were tasked with this, I would probably do one of two things:

Save the level data in Google Sheets and use one of the google sheets assets available on the asset store to pull it into the game either at design time or run time.

Or:

Save the level data in ScriptableObjects, probably one per region. The simplest method would be to make each scriptableobject a list of LevelData objects and create a property drawer to improve handling and editing of the leveldata objects. To make things better from there you could:

Make the list a reorderable list (undocumented but if you search it's not hard to find)

Create custom level and region editor Windows

The second option is more "Unity" and the experience you get with editor scripting will make you more effective in future projects. The first option is probably quicker. Both are perfectly valid in my opinion.

\$\endgroup\$
  • \$\begingroup\$ Thanks, this is helpful. I updated the question a bit in case that helps you refine your answer. If I haven't gotten anything else soon, I'll mark yours as the accepted answer. \$\endgroup\$ – user45623 May 2 '16 at 22:58
  • 2
    \$\begingroup\$ ScriptableObject was definitely the way to go, thanks! \$\endgroup\$ – user45623 May 2 '16 at 23:31
  • \$\begingroup\$ Glad it was helpful! Sorry I didn't respond earlier - I think I need to adjust my notification settings. \$\endgroup\$ – Cameron Oltmann May 7 '16 at 2:01
1
\$\begingroup\$

Assuming you'd like your game's levels to be edited by either a non-technical game designer or QA team, I would actually recommend none of what you suggested for the actual level editing portion. Because the parameters of these levels are relatively simple, I recommend you make the editor a part of the compiled application, not part of the Unity editor.

In my experience, streamlining the editing tools in a way, where they don't require a recompile or the use of the Unity editor, makes designers, QA teams and non-technical clients very happy. They can then see what works and what doesn't with minimal effort. The same applies when balancing.

In terms of storing this data, I personally tend to use web services during the development / testing stage. These avoid juggling files around. The web service itself is just a dumb php script, which stores whatever is submitted to it into an on-server JSON file - in your case, it will listen for a unique level ID and a list of the various parameters that build a level. When it receives these, it stores them. Note, however, that this is only a solution to be used during development and testing, do not ever use something this simple in production code - it is very exploitable.

Here is a development/testing web service I use. This one only accepts a "data" POST parameter, which is pre-computed by the game, which saves the map. Because your levels use multiple parameters, it's possible that you might prefer to store each of these parameters individually, instead of just "data". In my case, not setting the POST parameters outputs the saved data - one per line, used elsewhere in game code to print saved maps.

<?php
//maps.php
$data = json_decode(file_get_contents("maps.json"), true);
if(isset($_POST["levelID"]) && isset($_POST["data"]) && isset($_POST["name"]) && isset($_POST["data"])){
    $data[$_POST["levelID"]] = Array("name" => $_POST["name"], "datetime" => date("Y-m-d H:i:s"), "ip" => $_SERVER['REMOTE_ADDR'], "data" => $_POST["data"]);
    file_put_contents("maps.json", json_encode($data));
}else{
    foreach($data as $i => $d){
        print $d["data"]."\n";
    }
}
?>

On the Unity side, saving looks like this:

int LevelID; //Unique identifier of edited map (for development purposes, this can just be sufficiently large random number)
string name = ""; //Name of developer using map (can be hardcoded into shared copy), meant for accountability
string SaveString = ""; //The level-generating data you want to save. In your case, this will likely need to be reworked to multiple parameters. 

string url = "http://...../maps.php";
WWWForm form = new WWWForm();
form.AddField("data", SaveString);
form.AddField("name", Settings.PlayerName);
form.AddField("levelID", LevelID.ToString());
WWW www = new WWW(url, form);

For the UI side of this, what I'd recommend is that you either use PlayerPrefs or a static class to transfer values between scenes. I'd create a simple "level editor" scene with the various parameters the tester/designer can manually adjust, with a "test" and "save" button. Clicking the "test" button will save the set parameters to PlayerPrefs or the static object and then load the level scene, which would generate a level from these parameters. If the designer is happy with a generated level, they can go back to the editor and hit "save". This is when the unity save script above is executed and the map saved to the server. You can, of course, add a "name" parameter to save, to make identifying levels easier down the line.

Once your maps are finished and your game is ready to ship, what I'd do is have an XML/Json file with the map data. It might be wise to create a script that takes this Json file and uses its data to generates a "hardcoded" C# script with the data, simply if you'd rather not have the end user being able to edit the file. Others have suggested alternative approaches, I'm simply listing what I'd use.

\$\endgroup\$
  • \$\begingroup\$ This is very clever, but I'm already struggling to keep the scope manageable and don't want to go throwing in web services. I'm already worried they're going to be broke before we finish all of the features they keep thinking up. \$\endgroup\$ – user45623 May 3 '16 at 3:39
-1
\$\begingroup\$

There is no conventional approach in designing a game really. You do whatever works for you. You can have full physics in your game, or you can fake it all.

Back to your question though, since the levels of a game like this are all random and also proceduraly generated, then just use one scene with a camera only. Switch from main menu to game type selection.

Then when you select a game type, you load the data for that level, sort of like a seed. You can have a single class for each game type and manage all the data in there. Then another class that handles power ups, bonuses etc.

\$\endgroup\$
  • \$\begingroup\$ I appreciate the thought, but you missed the question. I'm not asking how many scenes to use or how to design a data type. I'm trying to decide how to store and manage the data, whether that's in the editor or in external files. \$\endgroup\$ – user45623 May 1 '16 at 22:17
  • \$\begingroup\$ I just told you how didnt I? \$\endgroup\$ – zimspy May 2 '16 at 8:07
  • \$\begingroup\$ No you didn't... Please re-read my question, you seem to have misunderstood it. \$\endgroup\$ – user45623 May 2 '16 at 18:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.