Wednesday, October 22, 2014

Creating Configurable Game Levels In Unity

This post is about creating game levels that are configurable using XML files in Unity3D.
However the concept can be easily applied anywhere.

When creating a game that contains levels, the last thing a developer would like to do is to code every single level. Instead what he wishes to do is to create independent game objects which he can put together in different combinations and levels using easily configurable XML files.

When developing LittleBirdy Android game I had to generate tens of levels because the game evolves from one level to another with different difficulties, different obstacles or surprises.

To make the task easier on myself, I conceptually designed a generic structure for all levels.
So each level will have a name and a description, a success message to be displayed when the player wins and failure message when he loses.
Furthermore the level must contain the scoring rule such as the target points to earn, the time limit, the targets to hit and the order in which they must be hit.

Then comes the board, which is the rectangular area where the gaming takes place which is different from the other displays such as the current score, the menu etc...
Inside the board we find the targets to be hit, their location, their condition of appearance, the points earned.
Then comes the obstacle settings inside the board. Each obstacle has a location and a dimension as well as a certain behavior: fixed, moving, rotating, growing/shrinking.
Outside the board element, you can find the BirdSettings that defines the number, location and velocity of the birds that will be in the level.
The BranchesSettings is used to tell how many Left and Right branches available in this level.
Finally the GiftSettings are the gift objects that appear from time to time to give the player extra bonus or eliminate certain threat.

The XML structure appears like the following:


Level
 |
 |------ Scoring
 |               |-------Target
 |               |-------Timer
 |               |-------Rules
 |               |-------RuleOrder
 |
 |
 |------ Board
 |               |------- Targets
 |               |              |----- Target (type, count, appearance times ...)
 |               |
 |               |------- Obstacles
 |               |              |----- Obstacle (type, count, dimensions, postion ...)
 |               |              |             |
 |               |              |             |--- ObstacleBehavior (rotation, translation)
 |
 |
 |------ BirdSettings
 |               |------- Birds
 |               |              |----- Bird(name, type, x, y, velocityX, velocityY)
 |
 |
 |------ BranchSettings
 |               |------- Branches
 |               |              |----- Branch(name, side, count, image ...)
 |
 |
 |------ GiftSettings
 |               |------- Gifts
 |               |              |----- Gift(type, count, startTime, endTime, duration ...)



For each node in the above structure corresponds a C# class with the same name.
Using System.Xml.Serialization of .Net it is very easy to serialize/deserialize object hierarchy into XML files.
The method DeserializeReader  shows how easy to read an XML file and create a C# object hierarchy from it.

         private Level DeserializeReader(TextReader reader){  
                XmlSerializer ser = new XmlSerializer(typeof(Level));  
                Level level = (Level)ser.Deserialize(reader);  
                return level;  
           }  


Once the Level C# object hierarchy is ready, we can proceed to create Unity GameObject from these objects and assign to their components the corresponding data reas from XML file.

 void Build(Level lvl, List<GameObject> gameObjects){  
      if(lvl.Board != null){  
           GameObject board = GameObject.FindGameObjectWithTag("Board");  
           // create Targets  
           if(lvl.Board.Targets != null){  
                foreach(Target t in lvl.Board.Targets){  
                     try{  
                          GameObject go = GameObjectFactory.CreateTarget(t);  
                     }catch(Exception ex){  
                          Debug.LogException(ex);  
                     }  
                }  
           }  
           // create obstacles  
           if(lvl.Board.Obstacles != null){  
                foreach(Obstacle o in lvl.Board.Obstacles){  
                     try{  
                          GameObject go = GameObjectFactory.CreateObstacle(o);  
                     }catch(Exception ex){  
                          Debug.LogException(ex);  
                     }  
                }  
           }  

           // build the Gift Settings  
           if(lvl.GiftSettings != null && lvl.GiftSettings.Gifts != null 
                                       && lvl.GiftSettings.Gifts.Length > 0){  
                BuildGiftSettings(lvl, lvl.GiftSettings);  
           }  
// Set the bird if(lvl.BirdSettings != null && lvl.BirdSettings.Birds != null && lvl.BirdSettings.Birds.Length > 0){ Bird birdInfo = lvl.BirdSettings.Birds[0]; CBird.Instance.BirdSettings = birdInfo; } } }



The GameObjectFactory contains method to create Unity GameObject objects and assigns to them the corresponding XML data.
For example CreateTarget checks whether it knows hos to create the given Target object, TargertPeanut in the code below, and instantiates the GameObject using Unity APIs then gets the Unity Component (CPeanut) attached to the GameObject and gives it the Target 't' in order to initialize the GameObject variables such as location, speed, movement etc...


      GameObject CreateTarget(Target t){  
           if( t is TargetPeanut){  
                go = GameObject.Instantiate(Resources.Load("prefab/peanut")) as GameObject;  
                CPeanut gPeanut = go.GetComponent();  
                gPeanut.PeanutInfo = (TargetPeanut)t;  
           }  
...
      }  


Using this approach hundreds of game levels could be created by simply duplicating XML files and apply some changes to each one.
In a team of two or more people, a developer can program each object or character of the game in an independent manner, while a level designer would easily write XML files to create combinations of scenarios and game play. With some extra efforts they can build level editor so level design will become a drag&drop gesture.



No comments:

Post a Comment