How could we make the grenade have limited use like having limited ammo? So the player can only use one, two, three, etc. grenades. I am having trouble figuring out how to disable the action button seeing as it is instantiated.
Update BaseAction
to have another method called CanPerformAction
public abstract class BaseAction : MonoBehaviour
{
public virtual bool CanPerformAction()
{
return true;
}
}
Now in your GrenadeAction
you can have the action check how many grenades there are and return false if there aren’t any
public class GrenadeAction : BaseAction
{
public override bool CanPerformAction()
{
return grenadeCount > 0;
}
}
You will have to implement some way to hold the grenades, consume the grenades when the action is performed, and restock when a unit picks up more grenades.
Now, in ActionButtonUI
, change UpdateSelectedVisual
to check if the action can be performed and disable the button if it can’t
public void UpdateSelectedVisual()
{
BaseAction selectedBaseAction = UnitActionSystem.Instance.GetSelectedAction();
selectedGameObject.SetActive(selectedBaseAction == baseAction);
button.interactable = baseAction.CanPerformAction();
}
This is very quick-and-dirty. Ideally you would have some type of inventory for the grenades that can fire an event when the number of grenades change. You will then rather bind to that event in ActionButtonUI
and enable/disable the button based on whether or not there are still grenades left.
I used the above method in the GameDev.tv game jam this year because I didn’t have time to come up with a better way and was flying by the seat of my pants. But it works, it’s just not the ideal way.
This is the way
I did what you said about the grenade count and changing the UpdateSelectedVisual
but the button won’t change to being uninteractable. I put in Debug.Log("Button interactable? " + button.interactable)
to check if it was false and it is false, but it still lets me interact with the button. It’s very strange.
I don’t have this problem for
if (unit.CanSpendActionPointsToTakeAction(selectedBaseAction) == false)
{
button.interactable = false;
}
else
{
button.interactable = true;
}
So I am not sure what the problem is.
Does the button dim like a other buttons that are not interactable?
No, it doesn’t change at all. I’m trying to figure out if there’s something else reactivating or if the function itself has a problem.
But it works with
Here’s what I have:
public bool UpdateSelectedVisual()
{
if (UnitActionSystem.Instance.GetSelectedUnit() == null)
{
button.interactable = false;
selectedGameObject.SetActive(false);
return false;
}
BaseAction selectedBaseAction = UnitActionSystem.Instance.GetSelectedAction();
button.interactable = baseAction.CanPerformThisAction();
selectedGameObject.SetActive(selectedBaseAction == baseAction);
return baseAction.CanPerformThisAction();
}
And in this case, CanPerformThisAction is declared in BaseAction as
public virtual bool CanPerformThisAction()
{
return GetActionPointsCost() <- unit.GetActionPoints();
}
It appears that I used this CanPerformThisAction instead of calling unit.CanSpendActionPointsToTakeAction. It essentially does the same thing, checking for the AP cost, but because it’s virtual, in GrenadeAction (and I could do this in ShootAction as well), I can check for ammo, or perhaps if the unit has already thrown a grenade thsi turn
So in GrenadeAction
public override bool CanPerformThisAction()
{
if(grenadeCount<=0) return false;
return base.CanPerformThisAction();
}
When I try it this way, it makes all the buttons disabled when the game starts and also makes a dummy action button with the name “Action” for some reason. I also need to use return selectedBaseAction.CanPerformAction()
or else it throws a null error.
I figured out why the dummy action button was occurring, but I am stumped as to how to get the grenade to go inactive.
Let’s make sure we’re on the same page…
Paste in your UnitActionButtonUI.cs, BaseAction.cs and GrenadeAction.cs
Here is ActionButtonUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using System;
public class ActionButtonUI : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI textMeshPro;
[SerializeField] private Button button;
[SerializeField] private GameObject selectedGameObject;
private BaseAction baseAction;
public void SetBaseAction(BaseAction baseAction)
{
if(baseAction.AbilityAssigned() == true)
{
this.baseAction = baseAction;
textMeshPro.text = baseAction.GetActionName().ToUpper();
button.onClick.AddListener(() =>
{
UnitActionSystem.Instance.SetSelectedAction(baseAction);
});
}
}
public void UpdateSelectedVisual()
{
BaseAction selectedBaseAction = UnitActionSystem.Instance.GetSelectedAction();
selectedGameObject.SetActive(selectedBaseAction == baseAction);
button.interactable = selectedBaseAction.CanPerformAction();
Unit unit = UnitActionSystem.Instance.GetSelectedUnit();
if (unit.CanSpendActionPointsToTakeAction(selectedBaseAction) == false)
{
button.interactable = false;
}
else
{
button.interactable = true;
}
}
}
Here is BaseAction.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class BaseAction : MonoBehaviour
{
public static event EventHandler OnAnyActionStarted;
public static event EventHandler OnAnyActionCompleted;
protected Unit unit;
protected bool isActive;
protected Action onActionComplete;
protected virtual void Awake()
{
unit = GetComponent<Unit>();
//AbilityAssigned();
}
public abstract string GetActionName();
public abstract void TakeAction(GridPosition gridPosition, Action onActionComplete);
public virtual bool IsValidActionGridPosition(GridPosition gridPosition)
{
List<GridPosition> validGridPosition = GetValidActionGridPositionList();
return validGridPosition.Contains(gridPosition);
}
public abstract List<GridPosition> GetValidActionGridPositionList();
public virtual int GetActionPointsCost() //Set cost of actions
{
return 1;
}
public virtual bool CanPerformAction()
{
return true;
}
public virtual bool AbilityAssigned()
{
return true;
}
protected void ActionStart(Action onActionComplete)
{
isActive = true;
this.onActionComplete = onActionComplete;
OnAnyActionStarted?.Invoke(this, EventArgs.Empty);
}
protected void ActionComplete()
{
isActive = false;
onActionComplete();
OnAnyActionCompleted?.Invoke(this, EventArgs.Empty);
}
public Unit GetUnit()
{
return unit;
}
public EnemyAIAction GetBestEnemyAIAction()
{
List<EnemyAIAction> enemyAIActionList = new List<EnemyAIAction>();
List<GridPosition> validActionGridPositionList = GetValidActionGridPositionList();
foreach (GridPosition gridPosition in validActionGridPositionList)
{
EnemyAIAction enemyAIAction = GetEnemyAIAction(gridPosition);
enemyAIActionList.Add(enemyAIAction);
}
if (enemyAIActionList.Count > 0)
{
enemyAIActionList.Sort((EnemyAIAction a, EnemyAIAction b) => b.actionValue - a.actionValue);
return enemyAIActionList[0]; //Best action ends up on top
}
else
{
return null;
}
}
public abstract EnemyAIAction GetEnemyAIAction(GridPosition gridPosition);
}
And finally, here is GrenadeAction.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrenadeAction : BaseAction
{
private int grenadeCount;
[SerializeField] private Transform grenadeProjectilePrefab;
private int maxThrowDistance = 7;
private void Update()
{
if (!isActive)
{
return;
}
}
public override string GetActionName()
{
return "Grenade";
}
public override bool CanPerformAction()
{
if (grenadeCount == 0) return false;
return base.CanPerformAction();
}
public override EnemyAIAction GetEnemyAIAction(GridPosition gridPosition)
{
return new EnemyAIAction
{
gridPosition = gridPosition,
actionValue = 0,
};
}
public override List<GridPosition> GetValidActionGridPositionList()
{
List<GridPosition> validGridPositionList = new List<GridPosition>();
GridPosition unitGridPosition = unit.GetGridPosition();
for (int x = -maxThrowDistance; x <= maxThrowDistance; x++)
{
for (int z = -maxThrowDistance; z <= maxThrowDistance; z++)
{
GridPosition offsetGridPosition = new GridPosition(x, z);
GridPosition testGridPosition = unitGridPosition + offsetGridPosition;
if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
{
continue;
}
//Control shooting range and area
int testDistance = Mathf.Abs(x) + Mathf.Abs(z);
if (testDistance > maxThrowDistance)
{
continue;
}
validGridPositionList.Add(testGridPosition);
}
}
return validGridPositionList;
}
public override bool AbilityAssigned()
{
foreach (NoteInfo.NoteProperties.ID iD in unit.unitData.noteIDList)
{
if (iD.num == 100)
{
return base.AbilityAssigned();
}
}
return false;
}
public override void TakeAction(GridPosition gridPosition, Action onActionComplete)
{
Transform grenadeProjectileTransform = Instantiate(grenadeProjectilePrefab, unit.GetWorldPosition(), Quaternion.identity);
GrenadeProjectile grenadeProjectile = grenadeProjectileTransform.GetComponent<GrenadeProjectile>();
grenadeProjectile.Setup(gridPosition, OnGrenadeBehaviorComplete);
grenadeCount -= 1;
ActionStart(onActionComplete);
}
private void OnGrenadeBehaviorComplete()
{
ActionComplete();
}
}
You’re setting button.interactable based on CanPerformAction, but then immediately following that up by checking the spend and setting it again, overwriting the result of CanPerformAction…
Let’s refactor this
public void UpdateSelectedVisual()
{
BaseAction selectedBaseAction = UnitActionSystem.Instance.GetSelectedAction();
selectedGameObject.SetActive(selectedBaseAction == baseAction);
bool canPerform = button.interactable = selectedBaseAction.CanPerformAction();
if(canPerform)
{
Unit unit = UnitActionSystem.Instance.GetSelectedUnit();
button.interactable = unit.CanSpendActionPointsToTakeAction(selectedBaseAction);
}
}
After making those changes, the button does change to inactive but only when I choose a different action and then choose grenade action again. In addition, when it does go inactive, all the buttons go inactive.
I solved the first part by creating a new event in UnitActionSystem.cs called public event EventHandler OnActionEnded
and having it run UpdateSelectedVisual()
in UnitActionSystemUI.cs.
As for the second part, I am not sure why it deactivates all the actions instead of just grenade. As soon as I use the grenade action, all of the buttons stop working for that unit. It’s not because of Action Points because there is still one left.
That is an ooops on my part…
public void UpdateSelectedVisual()
{
BaseAction selectedBaseAction = UnitActionSystem.Instance.GetSelectedAction();
selectedGameObject.SetActive(selectedBaseAction == baseAction);
bool canPerform = button.interactable = baseAction.CanPerformAction();
if(canPerform)
{
Unit unit = UnitActionSystem.Instance.GetSelectedUnit();
button.interactable = unit.CanSpendActionPointsToTakeAction(baseAction);
}
}
Yes, this worked. I also tested it on a reload action and a few other actions and they all work fine. Many thanks to the both of you!
This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.