Hi! I made an alternative (and hopefully more fun) way to show each unit’s available and total action points!
In the guide, Hugo uses a number near the health bar to show the remaining/available action points for each unit. I find it quite bland and uninteractive (you can’t directly see the max amount of action points) so I took inspiration from another game in the genre (Mutant Year Zero: Road To Eden)
Here, the player can see each character’s action points the un upper left corner of the screen.
What I did, was to add a similar bar under each unit’s health bar and make sure the code is dynamic, so it changes based on the unit’s current max action points.
Here’s the code:
public class ActionPointsVisual : MonoBehaviour
{
[SerializeField] private Unit unit;
[SerializeField] private Transform ActionPointsVisualSinglePrefab;
[SerializeField] private GridLayoutGroup gridLayoutGroup;
[SerializeField] private Transform ObjTransform;
private List<ActionPointsVisualSingle> ChildrenList;
private ActionPointsVisualSingle[] ChildrenArray;
private int numberOfChildren = 0;
private bool hasSelectedVisuals = false;
private const float LENGHT = 1.04f;
private const float HEIGHT = 0.1f;
private const float SPACE_BETWEEN = 0.03f;
private void Start()
{
unit.OnSingleUnitActionPointsChanged += Unit_OnActionPointsChanged;
UnitActionSystem.Instance.OnSelectedActionChanged += unitActionSystem_OnSelectedActionChanged;
ChildrenList = new List<ActionPointsVisualSingle>();
BuildVisual();
UpdateVisual();
}
private void OnDestroy()
{
unit.OnSingleUnitActionPointsChanged -= Unit_OnActionPointsChanged;
UnitActionSystem.Instance.OnSelectedActionChanged -= unitActionSystem_OnSelectedActionChanged;
}
private void Unit_OnActionPointsChanged(object sender, System.EventArgs e)
{
if (numberOfChildren != unit.GetMaxActionPoints())
BuildVisual();
UpdateVisual();
}
private void unitActionSystem_OnSelectedActionChanged(object sender, System.EventArgs e)
{
if (unit == UnitActionSystem.Instance.GetSelectedUnit())
UpdateVisual();
else if (hasSelectedVisuals)
UpdateVisual();
}
private void BuildVisual()
{
foreach (Transform child in ObjTransform)
Destroy(child.gameObject);
ChildrenList.Clear();
numberOfChildren = unit.GetMaxActionPoints();
for (int i = 1; i <= numberOfChildren; i++)
{
Transform childTransform =
Instantiate(ActionPointsVisualSinglePrefab, ObjTransform);
ChildrenList.Add(childTransform.gameObject.GetComponent<ActionPointsVisualSingle>());
}
ChildrenArray = ChildrenList.ToArray();
gridLayoutGroup.cellSize = new Vector2((LENGHT - (numberOfChildren - 1) * SPACE_BETWEEN) / numberOfChildren, HEIGHT);
}
private void UpdateVisual()
{
bool isSelectedUnit = unit == UnitActionSystem.Instance.GetSelectedUnit();
hasSelectedVisuals = isSelectedUnit;
int availableActionPoints = unit.GetAvailableActionPoints();
int cost = unit.GetSelectedActionCost();
for (int i = 0; i < numberOfChildren; i++)
{
if (i < availableActionPoints)
{
if(isSelectedUnit && availableActionPoints >= cost && i >= availableActionPoints - cost)
ChildrenArray[i].SetSelected();
else
ChildrenArray[i].SetAvailable();
}
else
{
ChildrenArray[i].SetUsed();
}
}
}
}
public class ActionPointsVisualSingle : MonoBehaviour
{
[SerializeField] Image availableImage;
private bool isSelected = false;
private float sinAngle;
private void Update()
{
if ( !isSelected)
return;
if (sinAngle > 360f)
sinAngle -= 360f;
sinAngle += 4f * Time.deltaTime;
Color tempColor = Color.white;
tempColor.a = 0.6f + Mathf.Sin(sinAngle) * 0.4f;
availableImage.color = tempColor;
}
public void SetAvailable()
{
availableImage.enabled = true;
availableImage.color = Color.white;
isSelected = false;
}
public void SetUsed()
{
availableImage.enabled = false;
}
public void SetSelected()
{
availableImage.enabled = true;
isSelected = true;
sinAngle = 0; // sin(0) = 1
}
}
Some explanations:
- ActionPointsVisual is a child of UnitWorldUI in each Unit’s world canvas
- I had to add the OnSingleUnitActionPointsChanged event
- the ActionPointsVisualSingle contains a background image with a small outline, and an fill image (the white rectangle)
- You have start the OnSingleUnitActionPointsChanged event after every OnAnyActionPointsChanged
in the unit script
I also added a SelectedVisual effect to the ActionPointsVisualSingle (the fill image changes alpha) so you can see on the unit how many points the action costs. I used a sin function (since it’s periodical) but I’m not sure if there is a better way to do it? maybe with a shader… If someone knows a better way to do this, feel free to comment :))