137
社区成员
https://github.com/workhardhhh/GameWork
https://www.bilibili.com/video/BV1Fe4y1J7P4/
结对编号:__43__;队伍名称:__没有__;
学号 | 姓名 | 作业博客链接 | 具体分工 |
---|---|---|---|
102192113 | 欧阳欣泽 | https://bbs.csdn.net/topics/608611758 | 代码设计以及具体实现 |
102192108 | 朱志鹏 | https://bbs.csdn.net/topics/608612331 | 原型设计及代码测试 |
跟欧阳是同班且同宿舍,以往作业也有组队过。
原型选择用墨刀来进行涉及。选择着个工具一方面是支持国产设计工具,一方面是这款产品之前有用过。原本有考虑使用Figma,但搜索时出来的词条都被国内软件厂商投放走了,如即时设计、MasterGo等,也简单了解了下产品功能,考虑到骰子游戏的原型相对还比较简单,墨刀基本就能满足就顺手使用了。
在使用的过程中,也发现墨刀相对于另外两款原型设计工具,虽然更好上手一点,但整体功能和资源等细节方面也会稍显不足或有所限制。一些原型交互的功能在交互实现上并不能够十分地深度实现,后面一部分设计也是选择了曲线方式来伪实现。
原型设计中,首先要设计整体的框架,逻辑结构,其次根据结构需要准备好大部分相关的设计资源,由于之前并没有怎么做设计,在寻找资源方面也花了不少时间。
而在具体原型设计过程中,碰到了两次比较卡壳的地方,卡壳的原因是墨刀产品本身组件或功能的限制,如第一个卡壳的地方是在直接复制模块后,在模块上的触发事件的设置并不能够独立开,而是同步地更新设置,这使得我在设置骰子掷/置过程中,前后两页的事件触发老是前后出错,后面定位到可能存在的问题区域后,再针对性的进行测试,判断大概率是他们组件本身就是这样的一个逻辑,后面通过官方客服咨询佐证了我的一个判断。
第二个卡壳部分,是后面整体游戏逻辑已经梳理完毕后,想要再设计掷骰子、置骰子的一些简单动效,使得整体游戏原型的展示更加地真实,就花了比较多的事件在一局已设计的既定游戏过程中不断地调整不同页面切换的动效动画效果(约9*4=36页,每页有4个主要动效),在盘这些逻辑的时候,也遇到整个页面事件中,定时器设置无法设置两个以上组件状态变化,后面只能在游戏细节动效有一定舍弃,但整体上解决了问题。
比较麻烦的后面模拟的一次游戏过程,甲乙双方按顺序掷骰子、置骰子,穿插一次骰子消除的游戏模拟画面。因为希望整个过程尽可能地更像是真实的游戏,所以在动效细节上花了比较多的时间去掌握相关组件功能是使用,和在目标游戏动效的实现上需要如何使用,同时也需要不断梳理前后逻辑,这个比较耗费时间。
整体而言,原型设计下来,首先需要有一定的平面UI设计的能力,能快速地找到相关的设计素材实现目标UI的设计,由于本身在设计这方面技能一般,只能在现有资源基础上东拼西凑。其次,在原型设计上,整体框架在讨论后确立下来,即可按照既定框架去设计,但是在后续的原型设计协同上还比较缺乏,主要就由我这边找素材做设计,再跟欧阳那边沟通根据他的需求就细节做一些调整,在原型设计协同上还可以加强。最后,就是原型设计工具是否优秀,某种程度上也会限制原型开发的效率,如果原型没法直接实现功能动效需求,可能就得要在现有组件功能边界上通过别的也许是更低效更原始的方式方法来基本满足目标效果的实现。当然如有时间其实也可以多使用和比较几款不同的设计工具,最后选择一款适合自己的工具来使用。而且在使用的过程中,由于工具某些功能的限制产生的不便,就会下意识反推自己所需要的效果,有可能通过什么方式设计修改能够满足,这种可能优化修改的判断过程还比较令人感觉到兴奋。
https://modao.cc/app/FTVBULBrjcbny46ndPN45 #逍遥骰子-逍遥骰子-原型
游戏开始页面
游戏菜单
菜单内,可查看游戏规则介绍和得分排行,设置游戏模式;选择退出游戏则返回到首页;开始游戏时,跳出玩家姓名输入弹窗
开始游戏
玩家姓名输入弹窗,输入后点击确认进入游戏对战;点击取消则关闭游戏弹窗
模式选择
模拟了选择游戏模式后,该模式选项颜色变为白色,其他选项则变为红色,此后自动返回游戏菜单页面
游戏介绍
点击右下角返回按钮后回单菜单页
得分排行榜
展示游戏得分排行榜前十名,点击右下角返回按钮后回单菜单页
游戏页面
点击底部置骰子,开始置骰子
置骰子
掷完骰子,提示字变为白色“置骰子”,点击相应的九宫格棋盘,将骰子置入后,判断是否需要消去骰子,并计算得分显示出来;此后骰子区域变回置骰子状态
游戏胜利结束
游戏判断结束时,计算游戏得分并展示,同时显示胜利方画面;点击结束游戏可返回游戏菜单
由于程序是VS+QT开发项目,实现网络交互则需要QT的套接口函数,即还需建立一个服务器应用程序作为中转站,游戏程序为客户端,客户端与服务器通过套接口函数进行TCP连接,客户端在服务器注册房间号,服务器为同号房间的客户端进行转发,客户端实时发送自身的棋盘信息,玩家的顺序信号,另一方则根据发来的信息更新自己的棋盘并作出决策将改变后的棋盘以及玩家的顺序信号发回。由此实现网络通信对战。
如图所示主界面持有其他各个状态界面的引用,从而实现在主界面进行动态切换实现不同的监听事件。整体设计按照界面作为划分进行设计,即对每个页面单个设计后由主界面将其整合。
如图为AI算法流程图以及主界面程序流程图,ai算法采用递归树的方法进行计算,对每个坐标决策完之后的棋盘根据给定树高度进行递归,遍历所有可能情况,得到胜利场次以及失败场次从而计算胜利概率,取最大胜利概率的坐标为决策坐标。
Index Ai::get_next_step(int** myboard, int** enemyboard, int number)
{
int** mbd = NULL;
int** ebd = NULL;
Index decision = { 0 };
Index max_index = { 0 };
double max = 0;
//判断当前棋盘已有的骰子数目
int count = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (myboard[i][j] != 0)
{
count++;
}
if (enemyboard[i][j] != 0)
{
count++;
}
}
}
//根据骰子数目不同调整递归树的深度
if (count <= 4)
{
tree_depth = 1;
}
else if (count > 4 && count <= 9)
{
tree_depth = 2;
}
else if (count > 9 && count <= 15)
{
tree_depth = 3;
}
else
{
tree_depth = 4;
}
//尝试不同位置的放置,获取相应动作递归树返回的胜利概率,以胜利概率最大位置作为决策位置
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
//复制当前棋盘状况
mbd = copy_board(myboard);
ebd = copy_board(enemyboard);
//若当前位置无值则放置
if (ebd[i][j] == 0)
{
decision.i = i;
decision.flag = 1;
decision.j = j;
ebd[i][j] = number;
if (is_inline(number, mbd[i], 3))
{
clear_board_line(mbd[i], 3, number);
}
double win = 0;
double fail = 0;
double problity = 0;
Decision_tree* root = (Decision_tree*)calloc(1, sizeof(Decision_tree));
//填充根节点
fill_Decision(root, mbd, ebd, decision);
//建立递归树
build_tree(root, 0, &win, &fail);
//计算获胜概率
problity = win / (win + fail);
cout << "pro" << problity << endl;
//最大概率位置为决策位置
if (max < problity)
{
max = problity;
max_index.i = i;
max_index.j = j;
}
}
free_int(mbd, 3);
free_int(ebd, 3);
}
}
if (max == 0)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (enemyboard[i][j] == 0)
{
max_index.i = i;
max_index.j = j;
}
}
}
}
return max_index;
}
该函数为AI获取下一步函数,返回值为Index结构体存储的是其二维数组的下标,函数首先计算棋盘所剩总共的棋子数目,对于起初棋盘位置的放置的影响要小于后面已有较多棋子的局面,基本上棋子越多其决策的影响就越大,故根据当前棋子数目规定递归树的高度,棋子越多递归越多次。
获取棋子数后进入下一步对每一个空位置进行尝试,递归生成树计算获胜概率,后选择最大获胜概率作为决策位置,最后一步对max的判定则是考虑只有最后一个空格的情况则直接填充。返回坐标值。
由上图可知占用内存较大的是在QWidgets中继承的绘制图片函数的方法,以及QPainter的Paintevent方法,考虑优化方法只要有外部优化和内部优化,外部优化即对图片进行压缩,而内部优化则考虑存储图片的QPixmap结构体有许多是在函数中声明后被重复绘制而未能及时地释放,从而导致内存不断上涨,考虑将结构体存储为全局变量绘制方法中只需修改已经声明地全局变量结构体的值,这样在初始的时候可能会消耗多点的内存但有利于游戏的长时间的运行。
void test()
{
int mybord[3][3] = { {1, 0, 0,}, {6, 6, 6}, {2, 3, 4} };
int enembord[3][3] = { {2, 0, 2,}, {0, 0, 0}, {2, 3, 4} };
int number = 6;
Index index = get_next_step(mybord, enembord, number);
cout << "getindex i= " << index.i << "j = " << index.j << endl;
}
测试函数为Ai的下一步函数即get_next_step(),主要是考虑此ai函数是否能返回一个决策较好的位置,故在测试数据上将一行全置为最大,将决策的得分差设置得比较明显,来判断ai决策效果的好坏。同时也测试ai决策的速度从而调整递归树高度的设置,因为递归树过高的话极度消耗CPU资源,ai的决策速度也比较慢,因此需要综合决策效果以及决策速度考虑需要选择一个综合性能较好的树高度,也是通过该测试函数进行测试。
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 5 | 5 | 学习了原型工具的使用,主要参与了整体框架的设计,以及相关设计素材的整理 |
2 | 250 | 250 | 3 | 8 | 复习了之前Java和面向对象的知识,参与一些基础页面代码 |
朱志鹏:原型设计以设计原型的平面为主,需要辅以相关的文字进行细节和功能效果的说明。而随着设计工具的进步,一些简单的事件触发、动效功能也能之间展示在原型上,这一定程度上更利于开发者理解原型需求。当然也是这次的游戏设计相对功能比较简单,如果碰到功能复杂的项目,原型设计会更加的复杂,细节页面和细节动态会更繁复,也会占据着比较多的时间。原型设计能够在一定程度上把想象中的产品形态进行平面化显示,但并不能完全地展示想象产品中的复杂功能,需要通过画面或文字说明进行补充。而在原型设计的基础上进行开发,软件开发成果在形态上能够大部分地还原,本次的开发中是以开发便利为导向,所以在具体一些细节效果方面也是以开发能实现的边界为主,因为这些设计细节并不构成核心故而可以适当调整和舍弃。相反,一些游戏规则、效果的实现,基本上是要靠软件来开发实现,基于我们之前沟通过程中对游戏目标的理解进行具体游戏过程实现的开发(尤其是游戏AI算法部分,原型基本无法代替;我们有共识理解的部分直接进行了语言沟通,没有形成文字说明在原型文件中)。
欧阳欣泽:根据志鹏提供的原型设计基础上进行代码的开发,在大体上可以较好的还原原型设计的作品,但在游戏内存控制上则有些差强人意,由于游戏的规则是相对比较简单的最初考虑的内存也应该要比较小,但后面对于各种鼠标监听,动态切换等功能实现内存在不知不觉中超出了自己的规划,且在ai算法的设计中原打算利用递归树的方法创建出完整的棋盘各个情况,但当时也忽略了创建出递归树的内存需要导致无法实现,故只对每一步递归前进若干步来实现ai算法。以及由于本地项目编写的缘故游戏服务器需要自己开发来实现在线对战,由于其代码的复杂性还是需要一定的时间来实现,时间不足也只能暂时放弃。其余的由于在整个设计过程中志鹏的原型设计还是比较完善的,所以除了前面所述困难在代码开发时实现原型设计也没遇到太大的困难。可以说此次设计相对来说实现还是比较理想的。
朱志鹏:在项目过程中,欧阳还是比较给力的,对于项目的实现上能够立马有一个初步的实现想法,因此在项目的整体沟通上更加有效率。在开发方面,欧阳的编程能力是需要我进一步向他学习的。需要改进的方面,一些细节还可以继续优化,在电脑AI的实现上基本上欧阳原本的想法把所有可能情况去进行遍历(把计算机压榨到“极致”的想法虽然也很刺激好玩,但我会更考虑实现性和可玩性)。后面在AI实现按最早的想法算法很冗长,最后退而求其次选择了计算两步的方式简单实现AI功能,在算法的策略方面有机会的话可以进一步沟通研究。
**欧阳欣泽:志鹏在原型设计方面是值得我学习的,无论是在其美观还是完善性上,也由于志鹏的鼎力相助在原型基础上开发设计也比较顺利,整个项目的推进也能循序进行。需要改进的方面则是可能是整个项目的过程中一些细节方面需要改进,如原型设计中可能可以增加更多的动画来增加游戏的趣味性等。
朱志鹏:本次的作业从游戏整体功能的实现上来说,还比较好理解并进行设计。但是在具体开发过程中,涉及到游戏AI算法方面,想要设计的能够令游戏足够智能和有可玩性,那还需要花比较多的精力去研究玩法规则并涉及游戏算法。我在这次的作业中主要负责的还是原型这块相关的比较多,在开发过程中也需要跟队友多加交流,互相了解进度情况,以及同步遇到的问题或新的需求,如果可以则提供一定的帮助,尽可能满足需求或者给出相应的需求建议反馈,以便项目开发的继续推进进行。尤其是这一次的结对编程,引入了原型涉及和敏捷开发的概念,比较粗浅的体会到一些关于这种流程的感受(主要是这个项目在原型设计上没有太复杂,对于开发进度的影响没有很大,要是原型对开发影响较大时,那原型涉及反而也会变得棘手起来),感觉得到从开发的流程而言,一个相对成熟完善的开发流程,以及不同部门之间的分工协作,对于一个项目的完成也至关重要,尤其是今后走上工作岗位,许多开发工作都离不开与他人协作,那如何进行分工、划分开发模块,依照相对科学的工程流程进行项目开发,对于我们具体项目开发能力的成长会有所帮助。
欧阳欣泽:本次作业中在已有原型基础上进行代码的开发设计,整个过程还是比较享受的,在整个项目代码设计中也更会去考虑代码的可维护性来进行设计,将代码的耦合度尽量降低从方便以后的整改。同时也学习的QT的前端绘制,学习了其主要使用的API。在代码的锻炼上还是得到了不小的提升的。同时在开发过程中也有遇到难题,印象最深的还是ai算法的实现,本想采用递归整个棋盘树的情况然后ai直接进行遍历计算得到最大胜利概率来实现,无疑此种方法其决策是比较优良的,但决策速度以及硬件资源则很不理想。所以采用有限递归的方法来替代,同时也学习到了代码设计的过程中总是有舍有取的,难以两全,所以需要综合自己的需要选择一个最适合开发需求的区间来进行开发。同时经过此次团队协作也体会到了协作的重要性,团队往往能发挥出更强大的能量,每个人工作起来也事半功倍,总的来说经过此次的作业我受益良多,相信对于往后踏上工作岗位也会很有益处。