How to add Undo Button Functionality?

96 views Asked by At

enter image description here

This is my Roulette Game Project and I am new learner in Unity. In this project, I am facing an issue with creating an undo button.

This is my code for the undo button:

public void Undo() {
    if (Bets.Count != 0 && BetSpaces.Count != 0) {
        var lastBet = Bets.Last();
        var lastSpace = BetSpaces.Last();
        //UserDetail.Balance += lastBet.chip;
        UserDetail.Balance += BetSpaces.Last().stack.value;
        UpdateBalance(UserDetail.Balance);
        lastSpace.SetAllMeshes(false);
        lastSpace.Clear();
        Bets.Remove(lastBet);
        BetSpaces.Remove(lastSpace);
        var totalBets = int.Parse(TotalBetCount.text);
        if (totalBets > 0) TotalBetCount.text = (totalBets--).ToString();
        TakeButton.interactable = false;
    }
    var indexesToRemove = new List<int>();
    BetSpaces.ForEach(X => {
        GlobalFunctions.PrintLog($"X: {X.stack.value}");
        if (X.stack.value == 0) {
            indexesToRemove.Add(BetSpaces.IndexOf(X));
        } else {
            X.SetAllMeshes(true);
        }
    });
    for (int i = 0; i < indexesToRemove.Count; i++) {
        Bets.RemoveAt(indexesToRemove[i]);
        BetSpaces.RemoveAt(indexesToRemove[i]);
    }
    UndoButton.interactable = Bets.Count != 0;
    SceneRoulette._Instance.clearButton.interactable = Bets.Count != 0;
    SceneRoulette._Instance.rollButton.interactable = Bets.Count != 0;
}

Here's a breakdown of how the function works:

Condition Checking:

--> It first checks if there are any bets made (Bets.Count != 0).

--> If there are no bets made, it essentially means there's nothing to undo, so the function returns without performing any further actions.

Undoing the Last Bet:

--> If there are bets made, it retrieves the details of the last bet placed (lastBet) and the corresponding betting space (lastSpace).

--> It then adds back the betting amount to the player's balance (UserDetail.Balance += BetSpaces.Last().stack.value), effectively refunding the player for the undone bet.

--> It clears the visuals associated with the last betting space (lastSpace.SetAllMeshes(false)), removing any visual indication of the bet on the game interface.

--> It removes the last bet (Bets.Remove(lastBet)) and the corresponding betting space (BetSpaces.Remove(lastSpace)) from their respective lists.

--> It updates the total bet count displayed on the UI, reducing it by the amount of the undone bet if the total bet count is greater than 0.

Handling Remaining Bets:

--> It then iterates over all the betting spaces to check if any of them have a stack value of 0, indicating that no bets have been placed on them.

--> If a betting space has a stack value of 0, it removes both the bet and the betting space from their respective lists.

--> This step ensures that any empty betting spaces (where the player has placed no bets) are removed from consideration, keeping the bet and betting space lists synchronized.

Updating Button Interactivity:

--> Finally, it updates the interactivity of various buttons on the game interface based on the presence of remaining bets.

--> If there are remaining bets (Bets.Count != 0), it enables the clear and roll buttons while disabling the undo button.

--> If there are no remaining bets, it disables all buttons since there's nothing left to do.

Overall, the Undo() function provides a mechanism for players to retract their last betting actions, returning their wagered amount and removing the visual indication of the bet from the game interface.

To test this code, I place 1Rs's 3 bet on same spot, then I press the undo button. It will remove whole 3Rs. Bet at a time, but I want to remove bet on a separate time. Can anyone help me?

1

There are 1 answers

0
Olivier Jacot-Descombes On

As the first comment already says, you can use commands that can undo themselves. The idea is to have a Command class having a Do and a Undo method. These methods have a ICommandContext context parameter (it can in fact be of any useful type, e.g. Game). This is basically your game application. It allows the commands to access and act on the game.

public abstract class Command
{
    public abstract void Do(ICommandContext context);
    public abstract void Undo(ICommandContext context);
}

From this abstract class, you derive different commands, e.g., a PlaceBetCommand. When Do is called, this concrete class must store all the information the Undo method needs, to be able to undo the command.

public class PlaceBetCommand : Command
{
    // Primary constructor
    public PlaceBetCommand(/* TODO: add parameters describing the bet */)
    {
        // TODO: store the parameters
    }

    public override void Do(ICommandContext context)
    {
        // TODO: place the bet and possibly store additional information that you will need in Undo.
    }

    public abstract void Undo(ICommandContext context)
    {
        // Undo the bet
    }
}

The reason bet related information is passed as constructor argument(s) is that different commands require different information but the Do method has the same parameter for all commands. Alternatively, you could also pass information through properties.

Then you need an undo-stack and possibly a redo-stack (but you didn't ask for a redo functionality).

private static readonly Stack<Command> _undoStack = new();

Placing a bet would go like this

var bet = new PlaceBetCommand(/* pass the required parameters */);
bet.Do(this);
_undoStack.Push(bet);

Undoing goes like this:

if (_undoStack.Count > 0) {
    var command = _undoStack.Pop();
    command.Undo(this);
}

I will let you figure out the details, as I do not know how you place bets and what other commands you might have.

If you want to be able to undo placing 3 Rs's one-by-one, then split it into three commands and execute and push them separately.


If you want a redo function, then you need a redo stack of the same type as the undo stack as well.

When you execute a command, you will have to clear the redo stack, because the commands on this stack become invalid (they are valid only immediately after having undone commands).

When you undo a command, place it on the redo stack.

Redoing a command consists of popping a command from the redo stack and then executing its Do method and pushing it on the undo stack.