Action Buttons Disabled At First When Player Turn

I have a strange bug that I haven’t been able to solve where after it goes back to the player’s turn, the action buttons will all be disabled. They won’t work until a new unit is chosen and then it’s fine. I currently have it where when Action Points are zero then the buttons are disabled. It seems like it is some problem with how events are ordered but I can’t seem to figure it out. Here’s what I have right now in UnitActionSystemUI.cs

private void UpdateSelectedVisual()
    {
        foreach (ActionButtonUI actionButtonUI in actionButtonUIList)
        {
            actionButtonUI.UpdateSelectedVisual();
        }
    }

private void TurnSystem_OnTurnChanged(object sender,  EventArgs e)
    {
        UpdateActionPoints();
        UpdateSelectedVisual();
        //CreateUnitActionButtons(); //Fixes not working on change but makes things work that shouldn't
    }

private void CreateUnitActionButtons()
    {
        foreach (Transform buttonTransform in actionButtonContainerTransform) //Clean up by destroying all buttons
        {
            Destroy(buttonTransform.gameObject);
        }

        actionButtonUIList.Clear(); //Make sure nothing is highlighted initially

        Unit selectedUnit = UnitActionSystem.Instance.GetSelectedUnit();

        foreach (BaseAction baseAction in selectedUnit.GetBaseActionArray())
        {
            if (baseAction.AbilityAssigned() == true)
            {
                Transform actionButtonTransform = Instantiate(actionButtonPrefab, actionButtonContainerTransform); //Instantiate prefab in container
                ActionButtonUI actionButtonUI = actionButtonTransform.GetComponent<ActionButtonUI>(); //Grab transform for use
                actionButtonUI.SetBaseAction(baseAction); //Call function

                actionButtonUIList.Add(actionButtonUI);
            }
        }
    }

In ActionButtonUI.cs

public void UpdateSelectedVisual()
    {
        BaseAction selectedBaseAction = UnitActionSystem.Instance.GetSelectedAction();
        selectedGameObject.SetActive(selectedBaseAction == baseAction); //Set active if selected matches this one
        bool canPerform = button.interactable = baseAction.CanPerformAction();

        if (canPerform)
        {
            Unit unit = UnitActionSystem.Instance.GetSelectedUnit();
            button.interactable = unit.CanSpendActionPointsToTakeAction(baseAction);
        }
}

I also tried a method where private void UnitActionSystem_OnSelectedUnitChanged(object sender, EventArgs e) was triggered in UnitActionSystem.cs whenever it became the player’s turn again, but that still didn’t work.

I recall having this issue as well, I believe the has to do with the order in which things are being executed. Possibly UpdateSelectedVisual is being called before (or at the same moment that) the Unit’s ActionPoints are restored. In that case, this:

unit.CanSpednactionPointsToTakeAction(baseAction);

is false, so button.interactable gets set false as well.

If that’s the issue, you can try changing the script execution order, either in Unity or in your script. I have this at the top of my ActionButtonUI script so that it runs after most other scripts:

[DefaultExecutionOrder(+5)]
public class ActionButtonUI : MonoBehaviour
{

A way to test if this is the specific problem would be to have Actions with different ActionPoint costs, test having an Action which takes 2 ActionPoints and another Action which takes 1 ActionPoint. End the Player turn with one ActionPoint remaining, so that only one of the two Actions could have been used. When the Enemy turn finishes, check whether the Action which takes one ActionPoint is interactable or not.

Even without disabling buttons, this behavior exhibits in the game, as we’re not resetting the SelectedUnit when the turn changes, so if the selected unit dies during the enemy’s turn, then the SelectedUnit will be null (or otherwise invalid) when the turn heads back to the player.

What you want to do is in UnitActionSystem, subscribe to OnTurnChanged, if the turn is the Players, make sure that the selectedUnit is valid, and if it’s not, get a unit from the Player’s team that is valid and SetSelectedUnit with it. This should automatically force everything to refresh properly.

1 Like

I added the code below to try and verify if the unit is valid but it doesn’t seem to work. It still has the same problem.

private void TurnSystem_OnTurnChanged(object sender, EventArgs e)
    {
        if (TurnSystem.Instance.IsPlayerTurn())
        {
            if (selectedUnit.GetActionPoints() <= 0)
            {
                List<Unit> friendlyUnits = UnitManager.Instance.GetFriendlyUnitList();
                List<Unit> availableUnit = new List<Unit>();

                //See what units still have AP
                foreach (Unit unit in friendlyUnits)
                {
                    if (unit.GetActionPoints() > 0)
                    {
                        availableUnit.Add(unit);
                    }
                }
                if (availableUnit.Count > 0)
                {
                    SetSelectedUnit(availableUnit[0]);
                }
            }
        }
    }

Is there another way to check if a unit is valid?

If the SelectedUnit is dead, this will break and you’ll get a null reference error with this script…
We really don’t need to worry about Action points at this stage because ActionPoints are restored when the turn changes. What we want to know is if the character still exists. If it doesn’t, just find the first one that does exist. In both cases, it’s a good idea to call SetSelectedUnit on the selectedUnit to reset the UI

private void TurnSystem_OnTurnChanged(object sender, EventArgs e)
    {
        if (TurnSystem.Instance.IsPlayerTurn())
        {
            if (selectedUnit==null)
            {
                List<Unit> friendlyUnits = UnitManager.Instance.GetFriendlyUnitList();
                if(friendlyUnits.Count>0) SetSelectedUnit(friendlyUnits[0]);  
                //Perhaps here: else {DoSomethingToIndicateGameOverMan();}
            }
            else SetSelectedUnit(selectedUnit); //Forces redraw even if it's the same unit
        }
    }

So far I haven’t had any problems with a unit dying and the game breaking. Everything works as intended, even having a game over and victory screen. But I still run into the problem where as soon as the player’s turn starts, the unit that was selected has all the actions disabled unless a new character is selected. The unit has action points and can move; it’s just that no other actions can be selected.

Even when calling SetSelectedUnit(selectedUnit)?

Yes, I did the code you suggested and the same thing happened. It selects the unit but that unit can’t select actions.

Sounds like we have some sort of race condition going on… I’m just not sure where…
I’m not sure if I can get to it tonight (early work tomorrow). Zip up your project and upload it to https://gdev.tv/projectupload and I’ll take a look. Be sure to remove the Library folder to conserve space.

Unfortunately it seems like I can’t zip up the project because some files won’t let it and I can’t remove those files. The best I can do now is send you a build.

A build does me no good at all. It can demonstrate what is going on, but not why which I can only do by adding debug points to the scripts.

Do you have a repo you can link to? (git)

I downloaded and ran the build, so I can see that this is happening, but without the project, I have no way of finding what’s going on.
What is preventing you from zipping the project?

I tried to figure out how to do a repo so let me know if it works.

Unfortunately, the repo only has an empty directory and a .gitignore. You may need to push a commit with the project in it.

The push commit is over 2 gb so it’s having problems. Trying to fix it now.

I gave up on that and just deleted the offending files. Should be sent to you.

Great! I’ll take a look at it tomorrow after work.

Interesting, I actually wound up with a completely different bug when running the GameScene…

It was throwing a null-reference error in the RequestTPCost() method when the enemy went to shoot, as targetUnit didn’t appear to be set.

Once I cleared this (by passing no points if the targetUnit didn’t exist), at the end of the turn, the buttons correctly worked…

In short, I’m not sure at all what’s causing this. It’s likely my attempts to help earlier were based on incorrect assumptions about your code. I’m not entirely following the logic in the RequestTPCost(), as the only place I can find that the targetUnit is actually set is in HandleSelectedAction where it’s determined by the character under the mouse…

Sorry, I sent the wrong thing. It looks like GitHub wiped out my project and reverted it to an old version. I have to restore everything from a backup and see how far behind it was.

After I restored from my backup, I can’t send a zipped folder. It says something went wrong after I hit submit. Is there a data limit? If so, maybe I can just send the scripts.

Privacy & Terms