回合制游戏对战AI制作请重视这两个难点

游戏中的人机对战,“机”这一部分是由两个部分组成的:数值(比如bot数目、血量、命中率、技能cd、攻击力、防御力等等)和AI(人工智能,控制bot行为)。很多没入行的朋友经常把这两个概念混为一谈,然后铁口直断AI战胜玩家是非常简单的事情。

先说结论:对挑战电脑的难度而言,AI和数值是相乘的关系,通过改良AI设计可以达到让电脑更具挑战性的目的。当前游戏行业普遍使用的都是弱人工智能,即通过预设AI使bot呈现人工智能的效果,请注意这个大前提。在制作AI的过程中,思路是非常简单的,就是将设计师的思维(或者叫人的思维)植入AI代码中。
在一个具体的情势中,人会如何行为就设计AI如何行为,当然有一些具体的方法因游戏和关卡环境而异,比如战棋游戏的格子限制、剧情包装需要、关卡地形等。而设计的难点在于两个方面:

1.设计师的精力和智力有限

(1)先说精力有限

遵照弱人工智能的设计思路,设计师需要穷举所有的战斗情势,对不同情势下的bot行为作出决策然后形成AI,而实际上战斗情势是几乎不可能穷举的。以简单的2V2回合制站桩战斗为例,假设这个游戏有2种职业,每个职业战斗中可使用2个技能,那实际可能的战场行为是9(职业组合)×4(技能组合)=36,另外还有很多其他数据也会影响AI行为,如血量、技能冷却、buff状态,而这些数据往往是连续的,如血量可以是1~n任意一个值,这些数据导致战斗情势复杂度指数级提升。鉴于此,AI设计过程中往往会进行很多简化,如90%以上生命值为安全,10%~50%为受伤,10%以下为濒死,而实际上呢,不同职业承受伤害能力显然不同的,T和奶同为10%的生命值,生存能力一样吗?即使相同职业面对特定攻击力的敌人,15%可能再挨一下就死了,50%可能能挨2下,但AI认为10%~50%是同一个情势,这个合理吗?当情势判断由于简化导致出现不合理时,AI的智能程度自然也会大打折扣。

(2)再说智力有限

通常设计师是一类游戏中最精通的玩家,他们面对游戏中一个具体情势时能很快给出比较合理的决策,但这种合理是有局限性的,当战场形势足够复杂,A、B决策短时间内都无法直观判断效果时,设计师其实也很难抉择最优策略,因此设计逻辑中可能存在大量次优策略,自然也导致AI智能程度下降。相比精力有限,智力有限的影响小很多,毕竟设计师是足以战胜大部分玩家的。

2.硬件限制

假设真有不开眼的土豪游戏公司,雇了一大帮牛B闪闪的设计师来设计AI,相信我,那个AI脚本的复杂度一定会超过你的想象。网络游戏AI由服务器计算,同一时间战斗的玩家很多,服务器CPU会因此不堪重负;即使土豪公司使用了宇宙级的服务器,但是要同时传送足够多的战场情势给服务器分析,网络带宽消耗也是非常大的,所以你还要交大把钱给中国电信。你真的够壕吗?对于单机游戏本质是一样的,将大量脚本载入内存,将大量运算交给CPU,二手出售平台会让你感觉电脑很“卡”。想象一下,人家玩3d的LOL飞起,你玩个2d策略游戏卡成马,你一定会默默撕掉笔记本外星人的logo,然后换上hasee……

综上所述,目前回合制游戏AI智能程度是不及真实玩家的,阵容、装备、技能配置等完全相同的情况下,电脑干不过玩家。那么问题来了?为什么有时候你会打不过电脑呢?有时候会觉得“哇这一关很有挑战性”呢?其实就是设计师欺负人啦(打破规则赋予电脑更多数值),比如你100攻击力,电脑150攻击力啦;你7级,电脑9级啦;你的农民一次采10矿,电脑的农民一次采20矿啦;你50个兵,电脑80个兵啦……有些游戏所谓的简单模式、地狱模式基本上也是在数值上下功夫。

因为AI和数值是相乘的关系,在同等挑战难度下,AI做得越好,电脑需要的额外数值越少,这个时候游戏会显得越有趣。试想一下,你的张飞和吕布对砍,1分钟内张飞需要每次攻击都暴击才能杀死吕布,否则张飞死,面对这样屌炸天吕布,你真的会乐此不疲地默默给张飞堆暴击吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个比较复杂的项目,需要涉及到多个方面的知识和技能,包括游戏开发、网络编程等。以下是一个简单的示例代码,供参考: ``` #include <iostream> #include <winsock2.h> #include <windows.h> #pragma comment(lib, "ws2_32.lib") using namespace std; const int PORT = 8888; // 端口号 const int BUFFER_SIZE = 1024; // 缓冲区大小 void error(const char* msg) { cerr << msg << endl; exit(1); } int main() { // 初始化 Winsock 库 WSADATA wsaData; int ret = WSAStartup(MAKEWORD(2, 2), &wsaData); if (ret != 0) { error("WSAStartup failed"); } // 创建 socket SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (serverSocket == INVALID_SOCKET) { error("socket creation failed"); } // 绑定端口号 sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); serverAddr.sin_addr.s_addr = INADDR_ANY; ret = bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)); if (ret == SOCKET_ERROR) { error("bind failed"); } // 监听连接求 ret = listen(serverSocket, SOMAXCONN); if (ret == SOCKET_ERROR) { error("listen failed"); } // 等待客户端连接 SOCKET clientSocket = accept(serverSocket, NULL, NULL); if (clientSocket == INVALID_SOCKET) { error("accept failed"); } // 接收数据 char buffer[BUFFER_SIZE]; while (true) { memset(buffer, 0, sizeof(buffer)); ret = recv(clientSocket, buffer, BUFFER_SIZE, 0); if (ret == SOCKET_ERROR) { error("recv failed"); } if (ret == 0) { break; } cout << "Received message: " << buffer << endl; } // 关闭 socket closesocket(clientSocket); closesocket(serverSocket); // 清理 Winsock 库 WSACleanup(); return 0; } ``` 这段代码实现了一个简单的服务器端,通过监听端口号等待客户端的连接求,并能够接收客户端发送的消息。在实际开发中,还需要编写客户端程序,并在服务器端与客户端之间建立连接并传输游戏数据等信息。同时,还需要实现游戏逻辑、图形界面等功能,这些都需要比较多的代码和技能支持。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值