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)
{
, ).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中的状态机的更多相关文章
- PLC状态机编程-如何在STL中使用状态机
搞PLC编程多年,一直不知道状态机,学习matlab后,发现状态机编程异常方便,过去很多编程时的疑惑豁然开朗起来.今天跟大家分享一下如何在STL中使用状态机. 下面是用状态机描述的控制任务. 这个状态 ...
- C#开发笔记之05-迭代器中的状态机(State Machine)到底是什么?
C#开发笔记概述 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/961 访问. 状态机可以理解为实现了备忘录模式(仅作为 ...
- (文章也有问题,请自行跳过)react中的状态机每次setState都是重新创建新的对象,如需取值,应该在render中处理。
demo如下 class Demo4StateLearn extends React.Component { constructor(props) { super(props); this.state ...
- Lua中使用状态机FSM简单例子
FSM 有限状态机: 一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生.一个有 ...
- 6_State 游戏开发中使用状态机
### State 不好的代码 ``` //处理玩家输入的代码 void Heroine::handleInput(Input input) { if (input == PRESS_B) { if ...
- [整理]JS中的状态机
/*StateMachine*/ var StateMachine = (function(){ function StateMachine(opts){ this.current = opts.in ...
- Maxim实时时钟芯片设计指南5413-二进制编码十进制(BCD)格式实时时钟中的状态机逻辑
网上DS12C887的文章涉及到时间的存储格式使用的都是二进制代码,究竟使用BCD码该如何操作?正好美信官网上有一篇文章.美信官网不稳定,先贴到这里,有时间再翻译. 原文链接 State Machin ...
- 趣说游戏AI开发:对状态机的褒扬和批判
0x00 前言 因为临近年关工作繁忙,已经有一段时间没有更新博客了.到了元旦终于有时间来写点东西,既是积累也是分享.如题目所示,本文要来聊一聊在游戏开发中经常会涉及到的话题--游戏AI.设计游戏AI的 ...
- Unreal Engine4 学习笔记1 状态机 动画蓝图
1.动画蓝图 包含 状态机 包含 混合空间BlendSpace,即状态机包含在动画蓝图的"动画图表中",而混合空间可用于在状态机中向某(没)一个状态输出最终POSE: 动画蓝 ...
随机推荐
- NSIS 无边框移动问题总结笔记
无边框移动 插件 WinProc WinCore.nsh [一定要有这个] 代码 ;事件 ;处理无边框移动 Function onGUICallback ${If} $MSG = ${WM_LBUTT ...
- BigDecimal用法详解
一.简介Java在java.math包中提供的API类BigDecimal,用来对超过16位有效 位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数.在实际应用中,需要对更大或者更 ...
- C#3.0扩展方法学习篇
什么是类的扩展方法 扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型. MSDN Extension methods enable you to &q ...
- 游戏编程技巧 - Subclass Sandbox
Subclass Sandbox 使用场景 你正在开发一款类似LOL的游戏,里面有许多英雄角色,你决定把这些英雄类交给小弟们实现.因为在这些英雄中,释放放技能时,有的要使用粒子系统造成炫酷的效果,有的 ...
- .NET支持多平台后的一点拙见
我们目前对.NET的理解大部分可以归纳为:起初它是Java平台(注意是平台,不要跟Java语言搞混淆)的一个克隆品,后来慢慢演变,有了自己的特性.由于Java平台最显著的特点就是“平台独立性”(或者说 ...
- 多彩的Console打印新玩法
Chrome应该是每一个Web开发者必备的工具之一.它有而强大的Devtool,辅助我们的JavaScript调试,审视DOM元素,CSS即时修改等.以及它还有一个的庞大的插件系统,同时我们也可以很容 ...
- [.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上)
[.net 面向对象程序设计进阶] (16) 多线程(Multithreading)(一) 利用多线程提高程序性能(上) 本节导读: 随着硬件和网络的高速发展,为多线程(Multithreading) ...
- .NET组件程序设计之线程、并发管理(二)
.Net组件程序设计之线程.并发管理(二) 2.同步线程 手动同步 监视器 互斥 可等待事件 同步线程 所有的.NET组件都支持在多线程的环境中运行,可以被多个线程并发访问,如果没有线程同步,这样的后 ...
- Scrapy爬取自己的博客内容
python中常用的写爬虫的库有urllib2.requests,对于大多数比较简单的场景或者以学习为目的,可以用这两个库实现.这里有一篇我之前写过的用urllib2+BeautifulSoup做的一 ...
- DLL导出函数和类的定义区别 __declspec(dllexport)
DLL导出函数和类的定义区别 __declspec(dllexport) 是有区别的, 请看 : //定义头文件的使用方,是导出还是导入 #if defined(_DLL_API) #ifndef D ...