Player Removable Objects

How to add decoration objects like rocks, trees, or walls.

👍

Reminder - Keep Daily Backups!

When working on a strategy game with a kit as big as the Complete Kit - always keep a working daily backup! Save yourself the trouble of rolling-back changes and losing work.

What are player removable objects?

For free-to-play strategy games like Clash of Clans, each player base starts surrounded by terrain objects that prevent the player from building just about anywhere. As you build your city, you will have to remove some of them which costs resources. The player can pay in-game resources to remove these objects and expand their available base space.

These are called player removable objects, or "removables" for short in the City Building Kit.

782

Removable examples. Click to view larger.

How can I initialize my base with these objects?

For development, we've included a blank map and a separate button called "Trees" that you can push to initialize the removable objects (trees, rocks, and other terrain obstacles).

521

Keeping this process separate by default for developers makes testing easier, and it's easy to automate the removable initialization after save/load when you're finishing your game development. (See below for instructions)

So for development testing -- every scene starts with an empty map which you can then initialize the removables on by pressing the "Trees" button in Unity as seen above.

❗️

Initialize the removables before building or loading your base!

The removables should not be initialized after you have built a base as loading on top of your base objects creates unrecoverable collision problems. You will notice these collision errors by red squares

Where are the prefabs?

All of the game object prefabs are set in the Removables.asset (ScriptableObject) at Assets/CityBuildingKit/Resources/Assets/Removables.asset.

833

Game scene Game Manager > SaveLoadMap

What happens when the player taps on a removable object?

XML/Removables.xml is where you maintain your the full list of removable objects including costs to remove and awards received upon removable.

For players, if they want to expand their base space they can locate a removable object, like seen in the below image:

894

Click to view larger.

Tapping on the removable object shows the cost to remove (in the below example it's 150 gold) and two buttons. The left starts the removal and the right closes the menu.

574

Click to view larger.

Tapping the checkmark begins the removal which takes the amount of time specified in XML/Removables.xml for that object. Players can tap the gem button to instantly complete the removable for a cost of one crystal gem.

951

Click to view larger.

The amount of gems needed to remove the object can be edited in Scripts/Creators/Removables/RemovableTimerSelector.cs as seen in the below excerpt which charges a finishPrice of 1 gem if the total time remaining is less than 1 minute, or 3 gems if the total price remaining is greater than 30 minutes but less than 60 minutes, and so on.

private void UpdatePrice(int remainingTime)					//update the price label on the button, based on remaining time		
	{
		/*
		0		30		1
		30		60		3
		60		180		7
		180		600		15
		600		1440	30
		1440	2880	45
		2880	4320	70
		4320			150
		 */
		
		if (remainingTime >= 4320) { finishPrice = 150; }
		else if (remainingTime >= 2880) { finishPrice = 70; }
		else if (remainingTime >= 1440) { finishPrice = 45;	}
		else if (remainingTime >= 600)	{ finishPrice = 30;	}
		else if (remainingTime >= 180) { finishPrice = 15; }
		else if (remainingTime >= 60) { finishPrice = 7; }
		else if (remainingTime >= 30) {	finishPrice = 3; }
		else if(remainingTime >= 0) { finishPrice = 1; }
		
		Price.text = finishPrice.ToString();
	}

When the time has completed or the player accelerates with a gem, the player is awarded XP amount for that item from XML/Removables.xml and occasionally a rare crystal gem currency as incentive.

The developer console in the below screenshot shows the message upon completion. The player sees up to two icon notifications, one for XP and one for gems (if awarded) that appear in the place of the object and fade away shortly.

546

Developer message from Scripts/Creators/Removables/RemovableTimerSelector.cs

Where to edit the list of removables?

You want to open XML/Removables.xml to edit the default list of removables. Like other shop sections this follows a similar pattern you've seen explained before in other parts of the kit. Below is an example:

XML Example

The following is an XML example of the ClamA removable object seen in the above image.

<Removable>
		<Name>ClamA</Name>	
    <!--  IMPORTANT: prefab name displayed in the store -->	
    
		<RemoveTime>5</RemoveTime>
    <!-- the time (in minutes) needed to remove the object -->	
    
		<Currency>Gold</Currency>
    <!-- save as Gold, Mana, or Crystals to remove -->			
    
		<RemovePrice>50</RemovePrice>
    <!-- amount of resource necessary to pay for the removable -->
    
		<XpAward>2</XpAward>
    <!-- experience awarded for the completion of building this -->
    
	</Removable>

The only important thing to mention is that the name must match the prefab name seen in Assets/Prefabs/Creators/Removables which in this case is ClamA.prefab

The data you specify in the XML is loaded in Scripts/Creators/Removables/RemovableCreator.cs as seen in the below excerpt:

//reads removable XML
	private void GetRemovablesXML()
	{
		XmlDocument xmlDoc = new XmlDocument(); 
		xmlDoc.LoadXml(RemovablesXML.text); 
		XmlNodeList removablesList = xmlDoc.GetElementsByTagName("Removable");
		
		foreach (XmlNode removableInfo in removablesList)
		{
			XmlNodeList removablesContent = removableInfo.ChildNodes;	
			dictionary = new Dictionary<string, string>();
			
			foreach (XmlNode removableItems in removablesContent) 
			{
				
				if(removableItems.Name == "Name")
				{
					dictionary.Add("Name",removableItems.InnerText); 
          // put this in the dictionary.
				}	
				if(removableItems.Name == "RemoveTime")
				{
					dictionary.Add("RemoveTime",removableItems.InnerText); 
				}
				if(removableItems.Name == "Currency")
				{
					dictionary.Add("Currency",removableItems.InnerText); 
				}
				if(removableItems.Name == "RemovePrice")
				{
					dictionary.Add("RemovePrice",removableItems.InnerText); 
				}
				if(removableItems.Name == "XpAward")
				{
					dictionary.Add("XpAward",removableItems.InnerText); 
				}
			}
			removables.Add(dictionary);
		}
	}

The only major difference from the BaseCreator.cs script that uses the same formatting is that there's an additional function you need to add your items into that converts the name to a dictionary index value:

private void TypeToSelectionIndex(string type)
	{
		//"ClamA","ClamB","ClamC","TreeA","TreeB","TreeC","TreeD"
		switch (type) 					//converts the type to the dictionary index
		{
		case "ClamA":
			currentSelection = 0;
			break;
		case "ClamB":
			currentSelection = 1;
			break;
		case "ClamC":
			currentSelection = 2;
			break;
		case "TreeA":
			currentSelection = 3;
			break;
		case "TreeB":
			currentSelection = 4;
			break;
		case "TreeC":
			currentSelection = 5;
			break;
		case "TreeD":
			currentSelection = 6;
			break;
		}
	}

How are gems and XP awarded?

Randomized gem calculations are provided in the Scripts/Creators/Removables/RemovableCreator.cs as seen in the below excerpt:

// function called like: 
// ((RemovableTimerSelector)remTimer).crystalAward = GenerateReward ();
// text says xpAward however the function can be used for gem rewards too
	private int GenerateReward()
	{
		int winProbability =0, xpAward = 0;

		winProbability = Random.Range (0, 2);
		if (winProbability != 0)
			xpAward = Random.Range (1, 5);

		return xpAward;
	}

The variable above says xpAward however the function can be called to randomly generate any variable including crystalAwards which optimizes total line count. XP award values have been moved to the XML/Removables.xml as mentioned above.

The script that manages the removable timer is Scripts/Creators/Removables/RemovableTimerSelector.cs

The following is an excerpt from that script which is the process that runs when the timer or the crystal gem finish now button is clicked

if(ProgressBar.value == 1)				//building finished - the progress bar has reached 1												
			{
				((SoundFX)soundFX).BuildingFinished();
				
				if(!battleMap)															//if this building is not finished on a battle map
				{
					((Stats)stats).occupiedDobbits--;									//the dobbit previously assigned becomes available

					if (crystalAward > 0) 
					{
						((Stats)stats).AddResources (0, 0, crystalAward);
						MessageController.Instance.DisplayMessage("You found "+ crystalAward.ToString()+" crystals in " + removableName );
						GameObject gainCrystalIco = (GameObject)Instantiate(GainCrystals);
                        gainCrystalIco.transform.SetParent(GameObject.Find("GroupEffects").transform);
                        gainCrystalIco.transform.position = transform.position + new Vector3(0, 75, 0);


                        gainCrystalIco.GetComponent<FadeOutGain> ().SetValue (crystalAward);//pass the value to the label
					}		
					((Stats)stats).experience += xpAward;	

					GameObject gainExperienceIco = (GameObject)Instantiate(GainExperience);
                    gainExperienceIco.transform.SetParent(GameObject.Find("GroupEffects").transform);
                    gainExperienceIco.transform.position = transform.position;

                    gainExperienceIco.GetComponent<FadeOutGain> ().SetValue (xpAward);//pass the value to 
					((Stats)stats).UpdateUI();
				}
				inRemovalB = false;				
				Destroy(this.transform.parent.gameObject);				
			}

The awards the player sees in place of the removable (developer console also shows in the screenshot below, but the developer can disable this)

546

What the player sees. Click to view larger.

How to automatically initialize removables on first load?

The following is an excerpt from Scripts/Creators/Removables/RemovableCreator.cs which shows what occurs when you push the trees button.

521

For developer testing, removable "Trees" initialization button.

void OnGUI()
	{
		if (GUI.Button (new Rect (5, Screen.height*0.5f, 45, 25), "Trees"))
		{
			//I will leave this on manual control; When save/load are automated, include this as well

		if (!battleMap) 
		{
			if (!((Stats)stats).removablesCreated && !((Stats)stats).gameWasLoaded)
			{
				InitializeRemovables();
			} 
			else 
			{
					MessageController.Instance.DisplayMessage ("You already generated the removables, or loaded a game.");
					MessageController.Instance.DisplayMessage ("You wouldn't want a tree landing in the middle of a house, would you?");			
			}
		}
	}

}

As seen in the excerpt above, it checks if the removables were created yet or if the game was loaded. To automatically initialize the removables on your game load, you want to put this in the Start() function of your game source code making sure to reference the RemovableCreator.cs script.

InitializeRemovables ();

How are the terrain removables saved?

Removables are associated with the player base just like other objects the player builds. But, in this case removable objects cannot be moved like buildings but they can be removed permanently.

This design allows you to randomize the layout of terrain objects for each base creating a unique experience for each player.

So removable locations are stored in the Player ID save file, like the example below:

###StartofFile###
###PosStruct###
Weapon,ArcherTower,136,2,-128,-724
Weapon,ArcherTower,137,2,1024,90.5
Building,Vault,138,3,-1024,0
Building,Vault,139,3,0,724
Building,Summon,140,3,0,0
###GridStruct###
WoodFence,WoodFenceNE,93,22,16
WoodFence,WoodFenceNE,94,22,17
WoodFence,WoodFenceNE,95,22,18
WoodFence,WoodFenceNE,96,22,19
WoodFence,WoodFenceNE,97,22,20
WoodFence,WoodFenceNE,98,22,21
###Construction###
###Removables###
TreeC,0,0,2,10
TreeC,1,0,2,13
TreeD,2,0,2,26
TreeC,3,0,4,12
ClamC,4,0,4,13
TreeA,5,0,4,23
TreeB,6,0,4,26
TreeC,7,0,4,32
TreeC,8,0,5,8
###RemovableTimers###
###Numerics###
140,135,137
0.00,0
0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0
###Stats###
140,683,1,0,0,100,93700,96700,2,505000,500000,5,True,True,True,True,True
1/1/2017 9:47:10 PM
###EndofFile###

We can take a closer look at the example by highlighting the section of the save file that contains the location of the removable objects remaining on the map and their grid coordinates. As they are removed, they disappear from the player base's save file.

###Removables###
TreeC,0,0,2,10
TreeC,1,0,2,13
TreeD,2,0,2,26
TreeC,3,0,4,12
ClamC,4,0,4,13
TreeA,5,0,4,23
TreeB,6,0,4,26
TreeC,7,0,4,32
TreeC,8,0,5,8

When a removable is being removed, ###RemovableTimers### also lists the construction project just like a normal construction project is listed so that the save file keeps track of removables in progress that have not yet completed.

###RemovableTimers###

How to add removable prefabs?

Like the other parts of the complete kit, make sure your prefab name is the same as the object in your XML data above. For reference, look at how ClamA also has a prefab located in Prefabs/Creators/Removables/ClamA.prefab

Other prefabs are sorted in their appropriate folder and named the same as the object from the XML. The scripting expects this pattern to automatically insert your new removable objects. We recommend avoiding spaces or symbols in your prefab name and just sticking to alphanumeric names.

Don't forget to modify the Removables.asset file, at Assets/CityBuildingKit/Resources/Assets/Removables.asset. The scripts will load the rumbles from this prefabs list.

How to add non-removable prefabs?

The following tutorial demonstrates how to add non-removable terrain objects to the game scene which you can also provide a collider around. Take a removable prefab as example and make it some modifications.. Let's use ClamA prefab. You have to remove the selection button and scripts involved on it.

951 954

If you want to make the removable collidable, you can take a look at the structure of our Grass1x.prefab, after you reactivate the mesh renderers - even duplicate one of those cubes and parent it to your removable prefab. Observe the "Obstacle" tag on each red cube. These have to be instantiated before the map is scanned for obstacles.

907

Click on image to view larger

907