CardConquest GameDev Blog #11: Unit Movement UI and “Turns”

Disclaimer: I am neither a professional game developer nor am I a professional programmer. I doubt I am following best practices. This is just what worked for me at the time. Hopefully it works for you if you’re playing along while reading!

Version of Unity used: 2019.4.13f1 Personal

The previous posts in this series can be found under the CardConquest category on this blog. The github for the project can be found here. The zip archive for this specific post can be found here.

Working on the Unit Placement phase is OVER! Now, it’s time to head back to working on the Unit Movement phase of the game. I hope to add two new things to the Unit Movement phase: 1.) Add a GUI and 2.) Make it so the player moves units in “turns.” The turns will make it so that a single unit can only move once per turn. To move a unit a second time, the player will have to advance to the next turn.

Starting with the GUI

The GUI for Unit Movement will be pretty simple. The following elements should be added:

  • A button to “end” the player’s turn in Unit Movement
  • Some text that tells the user if they have moved a unit yet or not
    • This text is just to inform the user if they have taken any action yet. In the final game, I don’t want to force the user to move any units if they don’t want to, but I also don’t want them to get confused and think they moved units when they are ending their turn when they haven’t

So, time to get started!

A Whole New Panel

Currently in the scene, there is a Canvas object called “GameplayUI” that has two child objects: the UnitPlacementUI panel and the GamePhase text object. For the Unit Movement GUI, a new panel is going to be created that is similar to the UnitPlacementUI panel.

To create the new panel, right click on “GameplayUI” in hierarchy, go to UI, then select “Panel”

Rename the panel to UnitMovementUI. You should now see something like this in your hierarchy:

The only configuration changes you need to make to UnitMovementUI is to make sure that the “Color” setting has the alpha value set to 0, and to make sure that the “Fill Center” box is unchecked. This will make the panel transparent since you don’t want to see the panel itself.

Creating the UI Elements

Now that the UnitMovementUI panel has been created, the actual GUI elements can be added.

The first element will be the “No Units Moved” text object. In the hierarchy, right click on UnitMovementUI, go to UI, then select “Text”

Rename the text to “NoUnitsMoved.” Select NoUnitsMoved in the hierarchy, and then in the Inspector window, click on the anchor setting. Set the anchor to Top Left.

For NoUnitsMoved’s position, set it to the following: Pos X: 175, Pos Y:-25, Width: 300, Height: 50

For the Text section’s settings, set the following:

  • Text to “no units moved”
  • Font to ThaleahFat
  • Font Size to 45
  • Alignment to Centered
  • Color to White

NoUnitsMoved should now look like this in the game scene:

Next, the button to end the player’s turn can be added. Right click on UnitMovementUI in the hierarchy. Go to UI, then select Button

Rename the button to endUnitMovementButton. It should should look like this in your hierarchy:

Next, select endUnitMovementButton in the hierarchy and then view it in the Inspector Window. Click on the anchor icon, and then set the anchor to Bottom Left.

Set the button’s position to the following: Pos X: 175, Pos Y: 25, Width: 300, Height: 50

Under the “Image” section, make sure that “Fill Center” is unchecked.

Back in the hierarchy, expand the endUnitMovementButton and select its child Text object.

In the Inspector window, under the “Text” section, configure the following:

  • Text to “End Unit Movement”
  • Font to ThaleahFat
  • Font Size to 35
  • Alignment to Centered
  • Color to White

After that, all the UI elements should be done. You should see this in your scene:

And now, GameplayManager.cs will be used to manipulate these UI elements

Giving the UI a Purpose

We all need a purpose, and so does the UI that you just made. I want the UI to do the following for the player:

  • “No Units Moved” signals that the player has not moved any units yet this turn
    • When a Unit is moved, the text disappears
  • The “End Unit Movement” button allows the player to end their current turn, and advance to the next
    • The player can end the turn even if they have NOT moved any units
  • I also want the button to help signal when a unit has been moved. When a unit is moved, the border of the “End Unit Movement” button will turn from white to yellow

So, lets get to it! But, wait, before moving on, lets make sure that all the UI elements are accessible to GameplayManager.cs, which will be the script that will be doing most of this UI manipulation.

Add the following to the global variables section of GameplayManager.cs:

[SerializeField]
private Text unitMovementNoUnitsMovedText;
[SerializeField]
private GameObject UnitMovementUI, endUnitMovementButton;
public bool haveUnitsMoved = false;

The Text variable unitMovementNoUnitsMovedText will be used to attach the NoUnitsMoved text object. The GameObject. UnitMovementUI will be used to attach the UnitMovementUI panel. The GameObject endUnitMovementButton will be used to attach endUnitMovementButton. And finally, the boolean “haveUnitsMoved” is simply there to track if units have moved or not.

Save GameplayManager.cs and go back to Unity. Select the GameplayManager object, and then in the Inspector window, attach the UI objects.

To make sure that the UnitMovement UI isn’t being displayed during the Unit Placement phase, it should be set to inactive when the UnitPlacement UI is activated. Back in GameplayManger.cs, go to the ActivateUnitPlacementUI function and add the following:

if (UnitMovementUI.activeInHierarchy)
	UnitMovementUI.SetActive(false);

Now, when the Unit Placement phase starts, GameplayManager.cs will check to see if the UnitMovementUI is active in the hierarchy/game. If it is, GameplayManager.cs will de-activate it.

Ok, NOW we can start manipulating the Unit Movement phase’s UI.

Detecting when a Unit Has Moved

A lot of the above relies on detecting if a player has moved a unit yet or not. This can be easily done from somewhere like MouseClickManager.cs when it moves units.

So, then, GameplayManager.cs needs a function that will take the actions on the UI when the unit is moved. Those actions are to hide the “No Units Moved” text, and to change the color of the “End Unit Movement” button. Below is the code for the “UnitsHaveMoved” function:

public void UnitsHaveMoved()
{
	if (unitMovementNoUnitsMovedText.gameObject.activeInHierarchy)
		unitMovementNoUnitsMovedText.gameObject.SetActive(false);
	if (endUnitMovementButton.activeInHierarchy)
		endUnitMovementButton.GetComponent<Image>().color = Color.yellow;
	haveUnitsMoved = true;
}

UnitsHaveMoved does the following:

  • If the unitMovementNoUnitsMoved text is active, de-activate it
  • if the endUnitMovementButton is active, change its color to “yellow”
  • Set haveUnitsMoved to true

Now, UnitsHaveMoved just needs to be called. This can be done in MouseClickManager.cs after it calls “MoveAllUnits” to move units. The following code should work:

MoveAllUnits(rayHitLand.collider.gameObject);
if(GameplayManager.instance.currentGamePhase == "Unit Movement")
	GameplayManager.instance.UnitsHaveMoved();

So, if it is the Unit Movement phase, and a unit is moved, GameplayManager.cs’s UnitsHaveMoved will be called to alter the UI as necessary.

So, now the UI will be altered when the player moves a unit.

Player “Turns” in Unit Movement

The purpose of the “End Unit Movement” button is to enforce “turns” for the player. in the Unit Movement phase, I only want the player to be able to move a unit 1 tile away from the tile it started the turn on. Right now, the player can move a unit as many times as they want, as far as they want, but just 1 tile at a time.

Limiting Unit Movement to 1 Tile Per Turn

To enforce the “turn” and make it so the player can only move 1 tile per turn, the starting location of the unit will need to be saved somewhere. To do this, I added a new GameObject variable to UnitScript.cs called “previouslyOccupiedLand”

public GameObject previouslyOccupiedLand;

I then created a new function called SaveUnitsStartingLocation in GameplayManager.cs that will save “currentLandOccupied” in previouslyLandOccupied for each unit.

void SaveUnitStartingLocation()
{
	Debug.Log("Saving unit's starting land location.");
	GameObject unitHolder = GameObject.FindGameObjectWithTag("PlayerUnitHolder");
	foreach (Transform unitChild in unitHolder.transform)
	{
		UnitScript unitScript = unitChild.transform.gameObject.GetComponent<UnitScript>();
		if (unitScript.currentLandOccupied != null)
		{
			unitScript.previouslyOccupiedLand = unitScript.currentLandOccupied;
		}
	}
}

SaveUnitsStartingLocation will need to be called at the beginning of each Unit Movement turn to save the units location.

Finally, to enforce the one tile per turn movement restriction, a check in UnitScript.cs’s CanAllSelectedUnitsMove function will need to be changed.

Currently, CanAllSelectedUnitsMove calculates the distance a unit has moved based on its currentLandOccupied location:

float disFromCurrentLocation = Vector3.Distance(landUserClicked.transform.position, unitScript.currentLandOccupied.transform.position);

This will just need to be changed from currentLandOccupied to previouslyOccupiedLand, like this:

float disFromCurrentLocation = Vector3.Distance(landUserClicked.transform.position, unitScript.previouslyOccupiedLand.transform.position);

So now, the player can move each unit 1 tile away from previouslyOccupiedLand, which was the tile the unit was on when Unit Movement started. The player can “move” a unit more than once by clicking on it again and selecting a new tile, but the unit will only move if that new tile is within 3.0 distance from the tile the unit started on.

Activating the UnitMovement UI

I still haven’t actually done anything to start and activate the Unit Movement phase and its UI.

So, in GameplayManager.cs, two new functions will need to be created: StartUnitMovementPhase and ActivateUnitMovementUI. StartUnitMovementPhase will start the ball rolling for the Unit Movement phase, and will be the function that endUnitMovementButton calls when it is clicked. ActivateUnitMovementUI will activate the UI elements for the Unit Movement phase.

First, StartUnitMovementPhase

public void StartUnitMovementPhase()
{
	if (!EscMenuManager.instance.IsMainMenuOpen)
	{
		Debug.Log("Starting the Unit Movement Phase.");
		haveUnitsMoved = false;
		ActivateUnitMovementUI();
		SaveUnitStartingLocation();
	}

}

StartUnitMovementPhase is pretty simple. It first makes sure that haveUnitsMoved is set to false. It then calls ActivateUnitMovementUI and SaveUnitsStartingLocation. Because it will be triggered by a button click, it will not execute if the EscMenu (remember that?) is open.

Now, ActivateUnitMovementUI:

void ActivateUnitMovementUI()
{
	Debug.Log("Activating the Unit Movement UI");
	if (!UnitMovementUI.activeInHierarchy && currentGamePhase == "Unit Movement")
		UnitMovementUI.SetActive(true);
	if (!unitMovementNoUnitsMovedText.gameObject.activeInHierarchy)
		unitMovementNoUnitsMovedText.gameObject.SetActive(true);
	if (endUnitMovementButton.activeInHierarchy)
		endUnitMovementButton.GetComponent<Image>().color = Color.white;
	// When the movement phase begins, save the land occupied by the unit to be used in movement resets
	SaveUnitStartingLocation();

}

ActivateUnitMovementUI just goes through and makes sure all the UnitMovementUI elements are activated. It also calls SaveUnitsStartingLocation. I forget why I call it in both functions. Something to do with how I was testing things. Anyway it works like this, so I am wary of changing it. Bad coding practices are common for me, sorry!

And now, StartUnitMovementPhase will need to be called when the phases change from Unit Placement to Unit Movement. This makes sense to do that in the EndUnitPlacementPhase function of GameplayManager.cs, which is where I called it from:

Ending the Turn

The player will end their turn in Unit Movement by clicking the “End Unit Movement” button. When this button is clicked, it should call the StartUnitMovementPhase function from GameplayManager.cs to restart the Unit Movement phase.

In Unity, select endUnitMovementButton in the hierarchy. Then, in the Inspector window, add a function to the button’s “On Click ()” by clicking on the “+” sign, attaching GameplayManager, and then selecting the StartUnitMovementPhase function.

Here’s a video of the Unit Movement UI and the turns working!

Next Steps

Next, I will hopefully add some functionality to “undo” unit movement during a turn. So, you move a bunch of units, but you don’t like it, so you hit a “reset” button, and all units move back to their original locations. Fun!