根据不同的游戏模式实现功能

Implement functions based on different game modes

我正在用 Unity 编写一款游戏,其中一个游戏具有不同的 游戏模式 但最终使用相同的关卡,只是表现不同而已。 例如,区别仅在于玩家必须如何瞄准,因此 s/he 必须快速找到目标的相同位置(目标按相同顺序出现)或随机出现。 这是同一个阶段,但规则不同,所以我认为 interface 会有所帮助 - GameManager class 根据游戏模式实现它。当然,我可以编写不同的 游戏管理器 或在其中使用 switch,但我希望保持井井有条以进行更改。 问题是,怎么做?

这是设置简单状态机的方法:

//state
public enum GameMode { Normal, Endless, Campaign, Idle, Count }
private GameMode gameMode;

//state machine array
private delegate void UpdateDelegate();
private UpdateDelegate[] UpdateDelegates;

void Awake()
{
    //setup all UpdateDelegates here to avoid runtime memory allocation
    UpdateDelegates = new UpdateDelegate[(int)GameMode.Count];

    //and then each UpdateDelegate
    UpdateDelegates[(int)GameMode.Normal] = UpdateNormalState;
    UpdateDelegates[(int)GameMode.Endless] = UpdateEndlessState;
    UpdateDelegates[(int)GameMode.Campaign] = UpdateCampaignState;
    UpdateDelegates[(int)GameMode.Idle] = UpdateIdleState

    gameMode = GameMode.Idle;
}

void Update()
{
     //call the update method of current state
     if(UpdateDelegates[(int)gameMode]!=null)
         UpdateDelegates[(int)gameMode]();
}

现在你可以把每个状态的逻辑分开了:

void UpdateNormalState() { 
    //...
    //write logic for normal state
}
//...
//same for other states

这样当你改变游戏模式时,新状态的更新方法将在当前帧结束后迭代调用。

有关更多信息,您可以 watch this video


状态机的好处是易于处理(与 switch-case 或许多 if 相比)。你有一系列方法,可以用它们做任何你想做的事情,但仍然要确保一次只能有一个方法 运行 。更改状态的最大延迟始终短至 Time.deltaTime(如果使用 Update 方法调用状态机方法)


您甚至可以将状态机设为 2D。但请确保分配所有 UpdateDelegates

public enum GameMode { Normal, Endless, Campaign, Idle, Count }
public enum GameState { Playing, Paused, GameOver, Idle, Count }
private UpdateDelegate[,] UpdateDelegates;
UpdateDelegates = new UpdateDelegate[(int)GameMode.Count, (int)GameState.Count];

如果您的游戏还不够,您可以使用高级状态机。这是我从某处复制但尚未测试的示例代码:

此方法使用状态之间的转换。例如使用给定的 Command 调用 MoveNext 并根据状态机的当前 ProcessState 和给定的命令将状态更改为下一个 ProcessState

using System;
using System.Collections.Generic;

namespace Juliet
{
    public enum ProcessState
    {
        Inactive,
        Active,
        Paused,
        Terminated
    }

    public enum Command
    {
        Begin,
        End,
        Pause,
        Resume,
        Exit
    }

    public class Process
    {
        class StateTransition
        {
            readonly ProcessState CurrentState;
            readonly Command Command;

            public StateTransition(ProcessState currentState, Command command)
            {
                CurrentState = currentState;
                Command = command;
            }

            public override int GetHashCode()
            {
                return 17 + 31 * CurrentState.GetHashCode() + 31 * Command.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                StateTransition other = obj as StateTransition;
                return other != null && this.CurrentState == other.CurrentState && this.Command == other.Command;
            }
        }

        Dictionary<StateTransition, ProcessState> transitions;
        public ProcessState CurrentState { get; private set; }

        public Process()
        {
            CurrentState = ProcessState.Inactive;
            transitions = new Dictionary<StateTransition, ProcessState>
            {
                { new StateTransition(ProcessState.Inactive, Command.Exit), ProcessState.Terminated },
                { new StateTransition(ProcessState.Inactive, Command.Begin), ProcessState.Active },
                { new StateTransition(ProcessState.Active, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Active, Command.Pause), ProcessState.Paused },
                { new StateTransition(ProcessState.Paused, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Paused, Command.Resume), ProcessState.Active }
            };
        }

        public ProcessState GetNext(Command command)
        {
            StateTransition transition = new StateTransition(CurrentState, command);
            ProcessState nextState;
            if (!transitions.TryGetValue(transition, out nextState))
                throw new Exception("Invalid transition: " + CurrentState + " -> " + command);
            return nextState;
        }

        public ProcessState MoveNext(Command command)
        {
            CurrentState = GetNext(command);
            return CurrentState;
        }
    }


    public class Program
    {
        static void Main(string[] args)
        {
            Process p = new Process();
            Console.WriteLine("Current State = " + p.CurrentState);
            Console.WriteLine("Command.Begin: Current State = " + p.MoveNext(Command.Begin));
            Console.WriteLine("Command.Pause: Current State = " + p.MoveNext(Command.Pause));
            Console.WriteLine("Command.End: Current State = " + p.MoveNext(Command.End));
            Console.WriteLine("Command.Exit: Current State = " + p.MoveNext(Command.Exit));
            Console.ReadLine();
        }
    }
}