软件工程作业:项目2--3D五子棋(结对编程)
结对成员:
刘宇奥 2011212007
肖光勤 2011211880
一、项目简介
• 项目内容:结对编程,将传统平面二维五子棋改写为三维空间五子棋
• 编程语言:C++、openGL
• 开发环境:Microsoft Visual Studio 2013集成环境
• 人员分工:本人负责2维转化3维图形界面程序编写,肖负责五子棋人机对战程序编写
二、功能展示
我们程序实现的3维五子棋场景是在一个房间里面的桌子上下棋,改变了传统只有2维棋盘的下棋方式,具有更好的趣味性、空间性和灵活性。
下面是最开始进入游戏的画面:
进入游戏后,可以通过键鼠交互改变视角,得到最佳的下棋角度。实现的交互功能:
1、鼠标左击,使“眼睛”看向鼠标点击的方向;
2、按下键盘按键‘a’、‘d’分别向左向右旋转视角;按下键盘‘w’、‘s’分别向当前视线方向向前向后移动“眼睛”位置;
键鼠共同操作得到多个视角的场景:
在调整好下棋视角后,按下'f'键(代表fix)固定视角,开始下棋,下面是多个不同视角下棋时和下棋结束时的图片:
玩家执黑棋,电脑白棋,在每下完一步后会有绿色边框显示最后一步。
上图为电脑赢,赢后会有红线连接5颗棋子,并锁定屏幕无法再下子。
在下棋时或结束后,可以按下'r'键,重置棋盘进行下一局。上图为第二局,黑棋胜利。可以看见与上上图 ,我们的视角有所改变,我们在结束后改变了视角:若当前处于固定视角状态,按下f键会修改为活动状态,即可调整视角。
三、设计思路与实现
左图为工程结构图。本次程序抽象出了4个类:Scene、Eye、ChessBoard、Game。其定义分别为:
Scene类主要负责绘制游戏场景,房间和桌子。
使用指定顶点图元画GL_QUADS和用glutSolidCube()函数画体。为了增加真实感,还用了纹理贴图。
class Scene { public: static void drawScene(); public: static const int house_length; static const int house_width; static const int house_height; static const int desk_length; static const int desk_width; static const int desk_thickness; static const int leg_height; static const int leg_margin; };
Eye类则负责根据键鼠交互,计算视角。
在鼠标点击时,调用对应的callback函数,得到的鼠标坐标为光标的屏幕位置。因此,需要逆变换得到三维空间坐标,其OpenGL函数为gluUnProject()。此即为视线上的点,可以直接作为gluLookAt()的第4、5、6个参数。在重绘时就可以看见视线方向的变化。
键、鼠交互函数的注册分别用glutKeyboardFunc(&myKeyBoard); glutMouseFunc(&myMouse);
class Eye { public: static void CalcPos(bool back); static void CalcMousePos(int x,int y); public: /*the position of eyes*/ static float pos_x; static float pos_z; static float pos_y; /*视线上一点的坐标(鼠标的世界坐标)*/ static float mouse_x; static float mouse_y; static float mouse_z; /*是否为固定状态*/ static bool fixed; };
ChessBoard类则绘制棋盘及与下棋相关的画面,比如棋子,最后一步的边框以及结束时的五子连珠红线。
在下棋的时候用到了openGL提供的鼠标挑选机制,进行下棋。
class ChessBoard { public: static const int board_len; static const int board_y; public: static void drawBoard(GLenum mode); };
Game类则为肖实现的人机对战程序。
电脑的实现机制AI,在人落子之后,电脑扫描整个棋盘,看看是否有三颗棋子相连的地方,看看有没有四颗棋子相连的地方,如果有的话,落下最后一字,即能取得最终的胜利。人是根据电脑落子后进行落子。实际上,是根据当前最后一个落子的情况来判断胜负的。需要从四个位置判断,以该子为出发点的水平,竖直和两条分别为 45度角和135度角的线,目的是看在这四个方向是否最后落子的一方构成连续五个的棋子,如果是的话,就表示该盘棋局已经分出胜负。
class Game { public: Game(); void initArray(); /* 返回-1平局 1玩家赢 2玩家输 */ int isOver(); /* 返回-1放置失败 1放置成功 */ int checkPosition(int row, int column); void AIsolve(); public: // static int chess[8][8]; /* 电脑棋子为o 人的为* */ char data[12][12]; bool istaked[12][12]; int lastStepX; int lastStepY; /*存储最终5子连珠的5个点及连珠模式*/ int line5pos[2]; int mode; //1-4 横 竖 撇 捺 };
四、项目总结
本次项目采取结对编程的方式进行。在项目初期,我们两人讨论了项目方向与内容确定做五子棋的2D到3D,然后根据自己的情况分配了各自的任务,每个人下去根据自己的任务找资料学习。在学习阶段,我们也互相交流进一步确定了程序要实现的细节,和整体框架。在感觉思考成熟之后,开始着手编程。编程的时候,大部分时间是两人一起编写,在责任人编写其负责的代码时,另一个人在旁边了解思路,帮助找出错误,并了解如何对接彼此的程序。在编程期间,我们也互相鼓励,战胜编程时的枯燥,最终完成了项目。通过此次项目的完成,我们增进了彼此的友谊,锻炼了编程合作能力,同时也从对方身上学习到了很多编程知识。
此外,此次项目给我最深的感受就是,计算机作为一门实用学科,特别是学习编程,动手实践才是学习最有效率的途径,而不是老是盯着书本。在最初编写代码前,我一直在看openGL红宝书,致力于关注所有的细节。但是我发现,在真正编程的时候,自己所看的内容大部分都忘记了。原因就是,书本上的东西太多了,光看不做既不知道重点,也不可能有很深的印象。而在动手实践的时候,才会真正懂得函数的用法以及需要注意的地方。并且在这种合作编程的项目中,若一个人拖慢了项目进度,会导致其他人也无法进行下去,项目也会因为个人而推迟。