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

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

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

              战斗状态,

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

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

//分组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)
{
    , ).PadLeft(, '));
    Console.WriteLine(log);

}

查看一下

我们看到

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

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

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

输出结果

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

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

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

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

可惜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一旦竟然这组状态忽略一切状态
        , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);

        //移动组状态 4个状态值
         << , 0x00000f);//4位一组
         << , 0x00000f);//
         << , 0x00000f);//
         << , 0x00000f);// 无法移动的施法

        //战斗状态 这组只有一个状态值
         << , 0x000010);//

后面的分组值,的由来是

0x00000f  二进制 0000,1111,

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

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

        // 0x000000一旦竟然这组状态忽略一切状态
        , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);
         << , 0x000000);

        //移动组状态 4个状态值
         << , 0x0003c0);//4位一组
         << , 0x0003c0);//
         << , 0x0003c0);//
         << , 0x0003c0);// 无法移动的施法

        //战斗状态 这组只有一个状态值
         << , 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)
        {
            ;
        }

        public void Show()
        {
            , ).PadLeft(, '));
            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)
        {
            )//当分组为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_空值(, 0x000000),
    Status0_登陆( << , 0x000000),
    Status0_下线( << , 0x000000),
    Status0_正常( << , 0x000000),
    //移动组状态
    Status1_走动( << , 0x00000f),
    Status1_跑动( << , 0x00000f);

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

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

    public boolean hasFlag(EnumStatus status) {
        ;
    }

    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 = , , ");
        System.out.println(log);
    }

    String padLeft(String source, int length, String paddingChar) {
        String strB = Long.toBinaryString(this.value);
        int forCount = length - strB.length();
        ; 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_空值================
结果:          ->
===============Status.Status0_登陆================
结果:          ->
不存在状态 Status.Status0_登陆
===============Status.Status0_正常================
结果:        ->
存在状态 Status.Status0_登陆
===============Status.Status1_跑动================
结果:      ->
存在状态 Status.Status0_正常
===============Status.Status0_下线================
结果:         ->
存在状态 Status.Status1_跑动

到此为止。。ok,,,,

状态机,整理完成。。

Game中的状态机的更多相关文章

  1. PLC状态机编程-如何在STL中使用状态机

    搞PLC编程多年,一直不知道状态机,学习matlab后,发现状态机编程异常方便,过去很多编程时的疑惑豁然开朗起来.今天跟大家分享一下如何在STL中使用状态机. 下面是用状态机描述的控制任务. 这个状态 ...

  2. C#开发笔记之05-迭代器中的状态机(State Machine)到底是什么?

    C#开发笔记概述 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/961 访问. 状态机可以理解为实现了备忘录模式(仅作为 ...

  3. (文章也有问题,请自行跳过)react中的状态机每次setState都是重新创建新的对象,如需取值,应该在render中处理。

    demo如下 class Demo4StateLearn extends React.Component { constructor(props) { super(props); this.state ...

  4. Lua中使用状态机FSM简单例子

    FSM 有限状态机: 一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生.一个有 ...

  5. 6_State 游戏开发中使用状态机

    ### State 不好的代码 ``` //处理玩家输入的代码 void Heroine::handleInput(Input input) { if (input == PRESS_B) { if ...

  6. [整理]JS中的状态机

    /*StateMachine*/ var StateMachine = (function(){ function StateMachine(opts){ this.current = opts.in ...

  7. Maxim实时时钟芯片设计指南5413-二进制编码十进制(BCD)格式实时时钟中的状态机逻辑

    网上DS12C887的文章涉及到时间的存储格式使用的都是二进制代码,究竟使用BCD码该如何操作?正好美信官网上有一篇文章.美信官网不稳定,先贴到这里,有时间再翻译. 原文链接 State Machin ...

  8. 趣说游戏AI开发:对状态机的褒扬和批判

    0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...

  9. Unreal Engine4 学习笔记1 状态机 动画蓝图

    1.动画蓝图 包含 状态机 包含 混合空间BlendSpace,即状态机包含在动画蓝图的"动画图表中",而混合空间可用于在状态机中向某(没)一个状态输出最终POSE:    动画蓝 ...

随机推荐

  1. 4.Powershell交互界面

    Powershell提供两种接口:交互式和自动化脚本 先学下如何与Powershell Console和平共处,通过Powershell Console和机器学会对话. 通过以上一个简单测试,知道Po ...

  2. java异常处理

    try{}catch(){}中的代码与外部代码之间有一定的逻辑关系,需要考虑到如果抛出异常的情况下,外部代码是否可以执行. 在需要捕获异常前尽量不要代入非异常代码,捕获后相关的代码放在一起.

  3. Apache许可协议Open RIA Services

    Jeff Handley's进行了多年的项目--基于一份开源许可发布WCF RIA Services.遵循Apache 2许可,捐赠给Outercurve基金会的ASP.NET Open Source ...

  4. .Net 跨平台可移植类库正在进行

    [原文发表地址] Cross-Platform Portable Class Libraries with .NET are Happening [译文发表地址] .Net 跨平台可移植类库正在进行 ...

  5. 生活中的OO智慧——大话面向对象五大原则

    世间万物,以俗眼观纷纷各异,以道眼观种种是常.面向对象思想不仅是编程的智慧,同样也是人生的智慧.通过生活去领悟面向对象的智慧,以面向对象的智慧来指导生活. (部分图片取自How I explained ...

  6. java中文乱码解决之道(八)-----解决URL中文乱码问题

    我们主要通过两种形式提交向服务器发送请求:URL.表单.而表单形式一般都不会出现乱码问题,乱码问题主要是在URL上面.通过前面几篇博客的介绍我们知道URL向服务器发送请求编码过程实在是实在太混乱了.不 ...

  7. Unbroken(坚不可摧)——Mateusz M

    Unbroken(坚不可摧)——Mateusz M YouTube励志红人账号Mateusz M 的作品,短片由几位演讲家Les Brown.Eric Thomas.Steve Jobs.Louis ...

  8. 集群下session共享问题的解决方案.

    这一篇博客来讲解下babasport这个项目中使用的Login功能, 当然这里说的只是其中的一些简单的部分, 记录在此 方便以后查阅. 一: 去登录页面首先我们登录需要注意的事项是, 当用户点击登录按 ...

  9. iOS 离屏渲染的研究

    GPU渲染机制: CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示. G ...

  10. WaitType:SOS_SCHEDULER_YIELD

    今天遇到一个query,处于SOS_SCHEDULER_YIELD 状态,physical IO 不增加,CPU的使用一直在增长.当一个sql query长时间处于SOS_SCHEDULER_YIEL ...