Contents:
Scriptable Objects are a brilliant way to store data for future reference, and we will be using this to have data for UI elements to derive from.
On the UI front, the plants have a Name
and Description
which we can utilise TextMeshPro
fields and [TextArea]
fields to show in the inspector. I’ve also moved the Prefab
, Sun Cost
and Spawn Recharge Time
fields from the original Plant
class into Plant Data
as it is required to be used by UI interactions rather than the plant itself.
[CreateAssetMenu(fileName = "PlantData", menuName = "PvZRemake/Plant/New Plant", order = 1)]
internal class PlantData : ScriptableObject
{
[Header("Identity")]
public string plantName;
[TextArea(5, 5)]
public string plantDescription;
[Header("Visuals")]
public GameObject plantPrefab;
[Header("Parameters")]
public int sunCost;
public enum PlantDesignation { Day, Night, Water_Day, Water_Night, Roof };
public PlantDesignation plantDesignation;
public enum SpawnRechargeTime { Fast, Slow, Very_Slow };
public SpawnRechargeTime spawnRechargeTime;
public bool isUpgrade;
}
Utilising the basic placing element we created in the Playable Area, we can create a system that needs to check the Sun Cost
, and if the plant has Recharged
to prevent spam clicking.
Checking the Sun Cost
is a simple comparison, but the Spawn Recharge Time
will require a bit more logic. We can set up a Coroutine to allow the Recharge Time to increment every second and do a check against the Recharge Time of the Plant UI selected; we can also apply this to the icon’s Image component to create a filling icon every second to show a visual Recharge timer. We also add an isInitial
state as Plants which are not defined as a Fast
recharge speed are not immediately able to be placed.
IEnumerator RechargePlantUI()
{
while (isRecharging)
{
//Wait for seconds.
yield return new WaitForSeconds(1f);
if (rechargeTime < maxRechargeTime)
{
rechargeTime++;
GetComponent<Image>().fillAmount = rechargeTime / maxRechargeTime;
}
else isRecharging = false;
}
while (!isRecharging)
{
GetComponent<Image>().fillAmount = 1f;
if (isInitial)
{
isInitial = false;
SetRechargeTime();
}
yield return null;
}
}
Now, we can add a new method call into the Clicking check inside of PlaceObjOnGrid
along the lines of uiController.currentlyHighlightedUIMember.PlaceAndBeginRecharging();
to store the currently highlighted icon for a future tooltip.
public void PlaceAndBeginRecharging()
{
uiController.DecreaseSunAmount(selectedPlant.sunCost);
uiController.currentlyHighlightedUIMember = null;
SetRechargeTime(); //Uses the spawnRechargeTime Enum to set the recharge time.
isRecharging = true;
GetComponent<Image>().fillAmount = 0;
rechargeTime = 0;
StartCoroutine("RechargePlantUI");
}
For the Tooltip
, we can utilise the OnPointerEnter
and OnPointerExit
methods to detect when we are hovering over the icons in the UI. Upon this, we can get the tooltip to populate its Text fields with the Name
and Description
fields we made in the Plant Data
Scriptable Object.
//Detect if the Cursor starts to pass over the GameObject.
public void OnPointerEnter(PointerEventData pointerEventData)
{
//Output to console the GameObject's name and the following message.
Debug.Log("Cursor Entering " + name + " GameObject");
uiController.currentlyHighlightedUIMember = this;
uiController.tooltip.SetActive(true);
tooltipController.plantName.text = selectedPlant.plantName;
tooltipController.plantDescription.text = selectedPlant.plantDescription;
tooltipController.sunCost.text = "Sun Cost: " + selectedPlant.sunCost.ToString();
}
//Detect when Cursor leaves the GameObject.
public void OnPointerExit(PointerEventData pointerEventData)
{
//Output the following message with the GameObject's name.
Debug.Log("Cursor Exiting " + name + " GameObject");
uiController.tooltip.SetActive(false);
}
Now we have a dynamic Tooltip
that updates based on the icon selected, which also allows a bit of optimisation due to only one Tooltip
existing instead of turning multiple on and off.
We’ve also changed the Raycast Target
element to be disabled on the ToolTip
and moved the ToolTip
layer above the buttons so the UI looks refined and isn’t blocking either element.