Game中的状态机

我相信大多数博友都会玩游戏。

玩游戏,牵涉到状态包含 登陆,正常,死亡,复活,下线,

在上面状态的基础上。同时包含 站立,走动,跑动,不可移动施法状态,

              战斗状态,

通常这是三个不同的分组。也就说可以同时存在的状态和不可同时存在的状态。

通常情况下也许你会这么定义,

//分组1
public bool 登陆 = false;
public bool 死亡 = false;
public bool 复活 = false;
public bool 下线 = false;
public bool 正常 = false;
public bool 打坐 = false;
//分组2
public bool 站立 = false;
public bool 走动 = false;
public bool 跑动 = false;
public bool 施法 = false;
//分组3
public bool 战斗 = false;

这种情况下,判断就变得很复杂,因为存在,同时成立和非同时成立的状态,

例如,战斗的同时可以走动,也可以跑动,或者技能施法

但是在打坐,下线,死亡等状态的时候既不能走动,也不能战斗也不能施法。

这样一来一去,判断的if else 会多的,让你头痛欲裂的吧。

或许你会想到,是要枚举,因为枚举提供位运算,提供Enum.HasFlag()方法来判断是否存在值

public enum Stauts
{
    //分组1
    空值,
    登陆,
    死亡,
    复活,
    下线,
    正常,
    打坐,
    //分组2
    站立,
    走动,
    跑动,
    施法,
    //分组3
    战斗,
}

查看一下使用方式

Stauts sValue = Stauts.登陆;

sValue = sValue | Stauts.站立;

if (sValue.HasFlag(Stauts.站立))
{
    Console.WriteLine("存在站立状态");
}

输出:

 

但是,继续测试一下

 

这样看出来了,我并没有设置复活状态,但是却存在复活状态~!

探究得明原因,是因为 位或(|) 运算,  按位或(|)表示相对应的每位至少有一个为1,则结果为1,只有两个都为0,结果才为0.

所以,我们应该设置枚举值的不同值;

为了更直观的查看运算方式,我们输出枚举值的二进制值,

public static void Show(Stauts value)
{
    string log = ("结果:" + ((long)value).ToString().PadLeft(10, ' ') + " -> " + Convert.ToString(((long)value), 2).PadLeft(64, '0'));
    Console.WriteLine(log);

}

 

查看一下

我们看到

Stauts.登陆 | Stauts.死亡 位或计算方式,结果值。

这些明白了,那就是枚举的值,定义不合法,

public enum Stauts
{
    //分组1
    空值 = 0,
    登陆 = 1,
    死亡 = 2,
    复活 = 4,
    下线 = 8,
    正常 = 10,
    打坐 = 20,
    //分组2
    站立 = 40,
    走动 = 80,
    跑动 = 100,
    施法 = 200,
    //分组3
    战斗 = 400,
}

 

 

输出结果

这下,没有了复活状态。这样判断,会减少一半,

可还是出现一个问题。那就是分组问题,也就说,死亡状态和复活状态不能同时存在,

死亡状态和跑动状态也不可能同时存在

那么我们在想,枚举值的能不能分组呢?

可惜C#下面枚举值,只能使用整型的基础类型。

没办法只能另辟蹊径:

    public class EnumStatus
    {

        public long Value { get; private set; }

        public long GroupValue { private set; get; }

        public EnumStatus(long value, long group)
        {
            this.Value = value;
        }
    }

接下来我们设置一组常量

        // 0x000000一旦竟然这组状态忽略一切状态
        public static EnumStatus Status0_空值 = new EnumStatus(0, 0x000000);
        public static EnumStatus Status0_登陆 = new EnumStatus(1 << 0, 0x000000);
        public static EnumStatus Status0_死亡 = new EnumStatus(1 << 1, 0x000000);
        public static EnumStatus Status0_复活 = new EnumStatus(1 << 2, 0x000000);
        public static EnumStatus Status0_下线 = new EnumStatus(1 << 3, 0x000000);
        public static EnumStatus Status0_正常 = new EnumStatus(1 << 4, 0x000000);
        public static EnumStatus Status1_打坐 = new EnumStatus(1 << 5, 0x000000);

        //移动组状态 4个状态值
        public static EnumStatus Status1_站立 = new EnumStatus(1 << 6, 0x00000f);//4位一组
        public static EnumStatus Status1_走动 = new EnumStatus(1 << 7, 0x00000f);//
        public static EnumStatus Status1_跑动 = new EnumStatus(1 << 8, 0x00000f);//
        public static EnumStatus Status1_施法 = new EnumStatus(1 << 9, 0x00000f);// 无法移动的施法

        //战斗状态 这组只有一个状态值
        public static EnumStatus Status2_战斗 = new EnumStatus(1 << 10, 0x000010);//

后面的分组值,的由来是

0x00000f  二进制 0000,1111,

0x000010 二进制 0001,0000,
这样就成功分组了

 

上面分组代码代码有错误:

        // 0x000000一旦竟然这组状态忽略一切状态
        public static EnumStatus Status0_空值 = new EnumStatus(0, 0x000000);
        public static EnumStatus Status0_登陆 = new EnumStatus(1 << 0, 0x000000);
        public static EnumStatus Status0_死亡 = new EnumStatus(1 << 1, 0x000000);
        public static EnumStatus Status0_复活 = new EnumStatus(1 << 2, 0x000000);
        public static EnumStatus Status0_下线 = new EnumStatus(1 << 3, 0x000000);
        public static EnumStatus Status0_正常 = new EnumStatus(1 << 4, 0x000000);
        public static EnumStatus Status1_打坐 = new EnumStatus(1 << 5, 0x000000);

        //移动组状态 4个状态值
        public static EnumStatus Status1_站立 = new EnumStatus(1 << 6, 0x0003c0);//4位一组
        public static EnumStatus Status1_走动 = new EnumStatus(1 << 7, 0x0003c0);//
        public static EnumStatus Status1_跑动 = new EnumStatus(1 << 8, 0x0003c0);//
        public static EnumStatus Status1_施法 = new EnumStatus(1 << 9, 0x0003c0);// 无法移动的施法

        //战斗状态 这组只有一个状态值
        public static EnumStatus Status2_战斗 = new EnumStatus(1 << 10, 0x000400);//

由于前面的分组0占用了6位,

所以分组应该改为,

 

也许看到这里你需要一定的位运算知识了;

接下来我在介绍一下

位运算的操作符

//// 按位异或(^)比较特殊,它比较的是如果两个不同则值为1(如:(1、0)(0、1)),相同则为0(如:(1、1)(0、0))。
//// 按位与(&)表示相对应的两位必须都为1,结果才为1,否则为0
//// 按位或(|)表示相对应的每位至少有一个为1,则结果为1,只有两个都为0,结果才为0.
//// 按位取反~ 运算符对操作数执行按位求补运算,其效果相当于反转每一位。

修改一下计算值

    public class EnumStatus
    {

        public long Value { get; private set; }

        public long GroupValue { private set; get; }

        public EnumStatus(long value, long group)
        {
            this.Value = value;
        }

        public bool HasFlag(EnumStatus status)
        {
            return (this.Value & status.Value) != 0;
        }

        public void Show()
        {
            string log = ("结果:" + this.Value.ToString().PadLeft(10, ' ') + " -> " + Convert.ToString(this.Value, 2).PadLeft(64, '0'));
            Console.WriteLine(log);

        }

        public static EnumStatus operator |(EnumStatus statusLeft, EnumStatus statusRight)
        {
            statusLeft.Value = statusLeft.Value & statusRight.GroupValue | statusRight.Value;
            return statusLeft;
        }

        public static EnumStatus operator &(EnumStatus statusLeft, EnumStatus statusRight)
        {
            statusLeft.Value = statusLeft.Value & (~statusRight.Value);
            return statusLeft;
        }

    }

 

 

上面重载的 位域算法也是有问题的。

public static EnumStatus operator |(EnumStatus statusLeft, EnumStatus statusRight)
        {
            if (statusRight.GroupValue==0)//当分组为0的时候清除所有状态
            {
                statusLeft.Value = statusLeft.Value & (statusRight.GroupValue) | statusRight.Value;
            }
            else
            {//当分组不为零
                statusLeft.Value = statusLeft.Value & (~statusRight.GroupValue) | statusRight.Value;
            }
            return statusLeft;
        }

这下才是正确的结果

 

这下是不是很轻松的解决了这一系列的状态问题呢?包括各种同时存在和不同时的存在的状态~!

但是这也有一个弊端,那就是值只有64个,也就是64个状态,,虽然存在分组。但是每一个分组的值都必须不同才行。

不知道,各位看官还有么没有更好的办法????

==============================================================================================

java版本的

 

java的枚举是可以自定义的这方面比较方便

但是java没有运算符重载,

 

enum EnumStatus {

    //0x000000一旦竟然这组状态忽略一切状态
    Status0_空值(0, 0x000000),
    Status0_登陆(1 << 0, 0x000000),
    Status0_下线(1 << 1, 0x000000),
    Status0_正常(1 << 2, 0x000000),
    //移动组状态
    Status1_走动(1 << 3, 0x00000f),
    Status1_跑动(1 << 4, 0x00000f);

    Long value = 0L;
    Long group = 0L;

    private EnumStatus(long value, long group) {
        this.value = value;
        this.group = group;
    }

    public boolean hasFlag(EnumStatus status) {
        return (value & (~status.value)) != 0;
    }

    public void addStatus(EnumStatus status) {
        value = value & status.group | status.value;
    }

    public void removeStatus(EnumStatus status) {
        value = value & (~status.value);
    }

    public void show() {

        String log = "结果:" + padLeft(this.value + "", 10, " ") + " -> " + padLeft(Long.toBinaryString(this.value), 64, "0");
        System.out.println(log);
    }

    String padLeft(String source, int length, String paddingChar) {
        String strB = Long.toBinaryString(this.value);
        int forCount = length - strB.length();
        for (int i = 0; i < forCount; i++) {
            strB = paddingChar + strB;
        }
        return strB;
    }
}

上面代码分组计算错误。修改为

public void addStatus(EnumStatus status) {
        if (status.group == 0) {//分组为零
            value = value & status.group | status.value;
        } else {//分组不为零
            value = value & (~status.group) | status.value;
        }
    }

 

 

测试代码

        EnumStatus status = EnumStatus.Status0_空值;
        System.out.println("===============Status.Status0_空值================");
        status.show();
        System.out.println("===============Status.Status0_登陆================");
        status.addStatus(EnumStatus.Status0_登陆);
        status.show();
        if (status.hasFlag(EnumStatus.Status0_登陆)) {
            System.out.println("存在状态 Status.Status0_登陆");
        } else {
            System.out.println("不存在状态 Status.Status0_登陆");
        }
        System.out.println("===============Status.Status0_正常================");
        status.addStatus(EnumStatus.Status0_正常);
        status.show();
        if (status.hasFlag(EnumStatus.Status0_登陆)) {
            System.out.println("存在状态 Status.Status0_登陆");
        } else {
            System.out.println("不存在状态 Status.Status0_登陆");
        }
        System.out.println("===============Status.Status1_跑动================");
        status.addStatus(EnumStatus.Status1_跑动);
        status.show();
        if (status.hasFlag(EnumStatus.Status0_正常)) {
            System.out.println("存在状态 Status.Status0_正常");
        } else {
            System.out.println("不存在状态 Status.Status0_正常");
        }
        System.out.println("===============Status.Status0_下线================");
        status.addStatus(EnumStatus.Status0_下线);
        status.show();
        if (status.hasFlag(EnumStatus.Status1_跑动)) {
            System.out.println("存在状态 Status.Status1_跑动");
        } else {
            System.out.println("不存在状态 Status.Status1_跑动");
        }

输出结果为

===============Status.Status0_空值================
结果:         0 -> 0000000000000000000000000000000000000000000000000000000000000000
===============Status.Status0_登陆================
结果:         1 -> 0000000000000000000000000000000000000000000000000000000000000001
不存在状态 Status.Status0_登陆
===============Status.Status0_正常================
结果:       100 -> 0000000000000000000000000000000000000000000000000000000000000100
存在状态 Status.Status0_登陆
===============Status.Status1_跑动================
结果:     10100 -> 0000000000000000000000000000000000000000000000000000000000010100
存在状态 Status.Status0_正常
===============Status.Status0_下线================
结果:        10 -> 0000000000000000000000000000000000000000000000000000000000000010
存在状态 Status.Status1_跑动

到此为止。。ok,,,,

状态机,整理完成。。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
      《地下守护神》是暗黑魔幻风格的ARPG手游,是真正按照商业级技术水准研发的Unity大型教学案例。这是“刘老师讲Unity”课程体系到目前为止最重量级教学课程,本课程将带领大家从手游前端各重要功能开发(基于MVC框架搭建、)到网络端的方方面面知识点,一一进行详细还原讲解,使得学员在短期内游戏研发能力有个质的突破,“主程”虚位以待,等待您的加入。 单机版主要技术点:    1: 前端分层的MVC架构设计,开发自定义的资源动态加载插件、音频插件、对话系统插件、事件监听插件、自定义强大的对象缓冲池脚本插件进行项目性能大幅提升等。    2: UI粒子特效、场景淡入淡出控制。    3: 单机与移动端的双输入控制系统开发。    4: 数值平衡处理与开发。    5: 多种设计模式在游戏研发过程的灵活运用。    6: XML技术的灵活运用,在系统解耦与资源国际化方面的技术突破。    7: 开发强大灵活的日志系统插件,应对服务器与移动端的各种复杂系统调试需求。温馨提示:        本课程使用Virtual Studio2012,以及Unity5.2 进行开发与讲解。(推荐学员使用Unity5.2 版本减少开发过程不必要的麻烦,VS使用更高版本则没有任何影响。) A:《MMOARPG地下守护神_单机版实战视频课程(上部)》https://edu.csdn.net/course/detail/24965B:《MMOARPG地下守护神_单机版实战视频课程(下部)》https://edu.csdn.net/course/detail/24979  
要创建一个有限状态机(Finite State Machine)的游戏框架,可以按照以下步骤进行: 1. 定义状态(States):首先,确定游戏的各个状态。例如,你的游戏可能有开始状态、进行状态、暂停状态和结束状态等。 2. 定义状态转换(State Transitions:确定状态之间的转换条件。例如,当游戏从开始状态转换到进行状态时,可能需要玩家点击开始按钮。 3. 实现状态类:为每个状态创建一个类,该类负责处理该状态下的逻辑和行为。每个状态类应该有进入状态、更新状态和退出状态的方法。 4. 创建状态管理器(State Manager):实现一个状态管理器来管理各个状态之间的转换。状态管理器应该能够根据当前状态和转换条件,切换到下一个合适的状态。 5. 在游戏主循环使用状态管理器:在游戏的主循环,使用状态管理器来更新当前状态,并执行该状态下的逻辑和行为。 下面是一个简单的示例代码,展示了如何使用 C# 创建一个简单的有限状态机游戏框架: ```csharp // 定义游戏状态枚举 enum GameState { Start, Playing, Paused, End } // 状态基类 abstract class State { public abstract void Enter(); public abstract void Update(); public abstract void Exit(); } // 开始状态 class StartState : State { public override void Enter() { Console.WriteLine("Game started"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.Space)) { GameStateManager.ChangeState(GameState.Playing); } } public override void Exit() { Console.WriteLine("Leaving start state"); } } // 进行状态 class PlayingState : State { public override void Enter() { Console.WriteLine("Game playing"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.P)) { GameStateManager.ChangeState(GameState.Paused); } else if (GameOverCondition) { GameStateManager.ChangeState(GameState.End); } } public override void Exit() { Console.WriteLine("Leaving playing state"); } } // 暂停状态 class PausedState : State { public override void Enter() { Console.WriteLine("Game paused"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.R)) { GameStateManager.ChangeState(GameState.Playing); } } public override void Exit() { Console.WriteLine("Leaving paused state"); } } // 结束状态 class EndState : State { public override void Enter() { Console.WriteLine("Game ended"); } public override void Update() { // 处理状态转换条件 if (Keyboard.IsKeyDown(Keys.Space)) { GameStateManager.ChangeState(GameState.Start); } } public override void Exit() { Console.WriteLine("Leaving end state"); } } // 状态管理器 class GameStateManager { private static State currentState; public static void ChangeState(GameState newState) { // 退出当前状态 currentState?.Exit(); // 创建新状态实例 switch (newState) { case GameState.Start: currentState = new StartState(); break; case GameState.Playing: currentState = new PlayingState(); break; case GameState.Paused: currentState = new PausedState(); break; case GameState.End: currentState = new EndState(); break; } // 进入新状态 currentState?.Enter(); } public static void UpdateCurrentState() { currentState?.Update(); } } // 游戏主循环 class GameLoop { static void Main(string[] args) { // 初始化游戏状态 GameStateManager.ChangeState(GameState.Start); // 游戏主循环 while (true) { // 更新当前状态 GameStateManager.UpdateCurrentState(); // 其他游戏逻辑 Thread.Sleep(16); // 控制帧率 } } } ``` 这只是一个简单的示例,你可以根据你的具体需求进行扩展和修改。希望这可以帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值