我相信大多數博友都會玩游戲。
玩游戲,牽涉到狀態包含 登陸,正常,死亡,復活,下線,
在上面狀態的基礎上。同時包含 站立,走動,跑動,不可移動施法狀態,
戰斗狀態,
通常這是三個不同的分組。也就說可以同時存在的狀態和不可同時存在的狀態。
通常情況下也許你會這麼定義,
//分組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,,,,
狀態機,整理完成。。