我正在为一个游戏编写AI对象的代码。
我的目标是优化代码,以便能够同时处理尽可能多的AI进程。
游戏是2D物理游戏,所以我已经在处理碰撞处理中的实体数量瓶颈问题,因此AI需要高效运行。
我处理的命令包括简单的ASDF + QE移动,以及鼠标点击和位置信息。
我意识到将AI移动建模在实际的键盘/鼠标上可能不是一个好主意,但我希望从这里开始,然后再逐步改进。
我决定使用布尔数组来表示命令,如下所示:
//命令:继续、上、下、左、右、转Q、转E、鼠标0、鼠标1 boolean[] moveBools = {false, false, false, false, false, false, false, false, false}; //数组中的第一个布尔值'继续'如果为真,将终止循环。
我现在面临的挑战是如何将这些数据传递到实际移动物理体的函数中。我可以选择在每次游戏迭代中传递整个布尔数组,或者传递迭代之间的变化。
这种编程方式涉及到实际的布尔数学,这超出了我的知识范围。我推测我可以像在C++中那样创建运算符,让数组状态相互减去并输出结果。
问题是:在前面的上下文中,如何以最佳速度传递布尔数组?
回答:
直接转换你所写的内容,可以使用BitSet
。它允许你获取位的交集/或、与、非。你可以这样做:
BitSet move = new BitSet(9);move.set(1); // 向上移动move.set(2); // 也向下移动?
你可以通过流式传输toByteArray()
来相对紧凑地发送这些数据(例如通过网络),并在接收端使用new BitSet(inputByteArray)
重建它。
在有效的状态表达中,有多少个这些布尔值会被设置为真?
如果我们假设只有一个,我们可以做一些更简单的事情。例如,当“继续”为真时,所有其他值都被忽略,并且对我来说,同时向上和向下或同时向左和向右设置为真没有意义。尽管同时按下一个或两个鼠标按钮与其他状态一起,或者同时向上和向左以及转Q可能是有意义的,但如果我们只允许一种状态,问题可以简化。
也就是说,如果你是在表示状态,我会定义一个状态枚举,并认为它已经完成,有效地将布尔数组等同于一个数字,即枚举的基数。
public enum SingleAiMoves { NONE, STOP, LEFT, RIGHT, UP, DOWN, TURN_Q, TURN_E, MOUSE1, MOUSE2}
这允许一次只有一种状态。
如果你需要组合多个状态,但仅限于有效的状态,你可以定义一个逻辑状态组,如下所示:
public enum AiMove { // u/d l/r q/e m1 m2 stop STOP (null, null, null, false, false, true), NONE (null, null, null, false, false, false), UP (true, null, null, false, false, false), DOWN (false, null, null, false, false, false), LEFT (null, true, null, false, false, false), RIGHT (null, false, null, false, false, false), TURN_Q (null, null, true, false, false, false), TURN_E (null, null, false, false, false, false), MOUSE_1 (null, null, null, true, false, false), MOUSE_2 (null, null, null, false, true, false), MOUSE_1_2 (null, null, null, true, true, false), UP_LEFT (true, true, null, false, false, false), DOWN_LEFT (false, true, null, false, false, false), ... // 这里列出了一些108(?)种组合.... ; private final Boolean upDown; private final Boolean leftRight; private final Boolean turnQE; private final boolean mouse1; private final boolean mouse2; private final boolean stop; AiMove(Boolean upDown, Boolean leftRight, Boolean turnQE, boolean mouse1, boolean mouse2, boolean stop) { this.upDown = upDown; this.leftRight = leftRight; this.turnQE = turnQE; this.mouse1 = mouse1; this.mouse2 = mouse2; this.stop = stop; } public boolean isStopped() { return stop; } public boolean hasUp() { return Boolean.TRUE.equals(upDown); } public boolean hasDown() { return Boolean.FALSE.equals(upDown); } public boolean hasLeft() { return Boolean.TRUE.equals(leftRight); } public boolean hasRight() { return Boolean.FALSE.equals(leftRight); } public boolean hasTurnQ() { return Boolean.TRUE.equals(turnQE); } public boolean hasTurnE() { return Boolean.FALSE.equals(turnQE); } public boolean hasMouse1() { return mouse1; } public boolean hasMouse2() { return mouse2; }}
个人认为,如果你选择这条路线,你可能需要考虑表示比一些内部控制器输入更详细的状态。
此外,你可以使用位标志或掩码,或者使用看起来更清晰、非花哨的EnumSet<SingleAiMoves>
,但与上面的枚举不同,和你的数组以及BitSet
一样,这将允许同时向上和向下,以及/或向左和向右的状态存在。
最后,因为我发现编写所谓的有效组合的枚举既繁琐又相对难以阅读,如上面的例子所示,你可以在枚举内部使用BitSet
并澄清枚举的构造。这样可能会使用更多的内存。
public enum AiMove { NONE (), UP (0), DOWN (1), LEFT (2), RIGHT (3), TURN_Q (4), TURN_E (5), MOUSE_1 (6), MOUSE_2 (7), STOP (8), MOUSE_1_2 (MOUSE_1, MOUSE_2), UP_LEFT (UP, LEFT), DOWN_LEFT (DOWN, LEFT), ... // 这里列出了一些108(?)种组合.... ; private final BitSet bitsUDLRQE12S = new BitSet(9); AiMove(int index) { bitsUDLRQE12S.set(index); } AiMove(AiMove... moves) { for (AiMove move : moves) { bitsUDLRQE12S.or(move.getBitSet()); } } private BitSet getBitSet() { return bitsUDLRQE12S; } public boolean hasUp() { return bitsUDLRQE12S.get(0); } public boolean hasDown() { return bitsUDLRQE12S.get(1); } public boolean hasLeft() { return bitsUDLRQE12S.get(2); } public boolean hasRight() { return bitsUDLRQE12S.get(3); } public boolean hasTurnQ() { return bitsUDLRQE12S.get(4); } public boolean hasTurnE() { return bitsUDLRQE12S.get(5); } public boolean hasMouse1() { return bitsUDLRQE12S.get(6); } public boolean hasMouse2() { return bitsUDLRQE12S.get(7); } public boolean isStopped() { return bitsUDLRQE12S.get(8); }}