中山大学数据科学与计算机学院本科生实验报告

(2019年春季学期)

课程名称 PAOGD 任课老师 郑贵锋
年级 16 专业(方向) 软件工程(计算机应用方向)
学号 16340132 姓名 梁颖霖
电话 13680473185 Email dic0k@qq.com
开始日期 2019/5/20 完成日期 2019/5/24

一、实验题目

HW4 利用OpenGL设计贪吃蛇游戏

二、实现内容

任务介绍

  • 贪吃蛇游戏:玩家控制贪吃蛇在游戏区域里驰骋,避免碰到自己或障碍物,尽可能地吃更多的食物以生长!
游戏玩法:
  • WASD控制蛇的移动
  • 游戏开始,每隔一定时间会在地图空闲位置刷新一个食物,蛇触碰到食物后食物消失,蛇会增加一个单位的长度
  • 当蛇触碰到自己或者障碍物,则游戏失败
  • 当蛇接触到地图边界,蛇会在地图另一端重新进入地图

开发环境

  • OpenGL3
  • GLFW
  • GLAD

要求:

  1. 完成贪吃蛇游戏的框架搭建(60%)
  2. 完成蛇以及食物的 3D 精灵加载和渲染,其中模型可以用简单的纯色几何模型实现如立方体、球体;或者网上下载合适的3D模型如Apple-Poly、Snake-Poly(20%)
  3. 实现蛇的控制(20%)
  4. Bonus:蛇的碰撞检测与响应

参考资料

  1. OpenGL游戏-框架设计
  2. OpenGL游戏-精灵渲染
  3. OpenGL游戏-碰撞检测
  4. OpenGL模型创建
  5. Google Poly-3D模型库

三、实验结果

1. 蛇的身体组成基类Block

这里主要是被Snake类所调用,本质是建立一个正方体,里面可以设置这个正方体的大小,颜色,并且最重要要定义draw函数,当snake的draw被调用的时候,实际上就是再调用block的draw。

这里这给出头文件的函数定义,其实现也比较简单

#ifndef BLOCK
#define BLOCK
#include <GL/glut.h>
#include "../sys.h"class Block {
public:Block(float pX, float pY, float pZ, float pSize = 1.0f);~Block();void setX(float pX);void setY(float pY);void setZ(float pZ);float getX();float getY();float getZ();void setColor(float pR, float pG, float pB); void draw();private:float x, y, z, size, r, g, b;
};#endif

其中的draw函数

//function to draw the block
void Block::draw() { glPushMatrix();glTranslatef(-x, y, z);if (r > 1 || g > 1 || b > 1) {glColor3ub(r, g, b);}else {glColor3f(r, g, b);}glCallList(cube);glPopMatrix();
}

2. 蛇的基类Snake

蛇所要做的操作,包括对位置的判断,是否更新,如何增加身体的长度。还需要一个draw函数,描绘蛇的形状。

class Snake { //contains the logic for the snake movement and collision
public:std::vector<Block*> blocks; //snake is just an array of blocksSnake(float startX, float startZ, int blockCount);~Snake();void draw();// 移动void move(Direction direction);// 添加蛇的身体长度void pushSnake();// 更新void update(Fruit* fruit, Stone* stones[]);// 碰撞检测,检查是否吃食物,或者碰到石头void collisionDetection(Fruit* fruit, Stone* stones[]);void addBlock();Direction getDirection();private:Direction currentDirection;bool isUpdated;int score;
};

先说draw函数,调用的是蛇的身体block的draw函数

void Snake::draw() { for (int i = 0; i <= blocks.size() - 1; i++) {Block* temp = blocks.at(i);temp->draw();}
}

蛇的移动,需要判断当前的方向,以及玩家操纵的方向。

判断完成后,改变蛇头的位置,并将蛇的身体附加到蛇头的后面

void Snake::move(Direction direction) { Block* snakeHead = blocks.at(0);if (direction == D_LEFT && currentDirection != D_RIGHT) {pushSnake();snakeHead->setX(snakeHead->getX() - 1.0f);currentDirection = direction;isUpdated = true;}else if (direction == D_RIGHT && currentDirection != D_LEFT) {pushSnake();snakeHead->setX(snakeHead->getX() + 1.0f);currentDirection = direction;isUpdated = true;}else if (direction == D_UP && currentDirection != D_DOWN) {pushSnake();snakeHead->setZ(snakeHead->getZ() + 1.0f);currentDirection = direction;isUpdated = true;}else if (direction == D_DOWN && currentDirection != D_UP) {pushSnake();snakeHead->setZ(snakeHead->getZ() - 1.0f);currentDirection = direction;isUpdated = true;}
};

完成上述函数就可以实现蛇的移动

下面要做的是碰撞检测,检测蛇头的位置是否为食物,或者建筑物。并且还要对蛇头是否到达边缘进行判断,实现穿越边界从另一边出来的功能需求。

void Snake::collisionDetection(Fruit* fruit, Stone* stones[]) {float x = blocks.at(0)->getX();float z = blocks.at(0)->getZ();// 检测食物if (fruit->getX() == x && fruit->getZ() == z) { score++;printf("Score = %i\n", score);bool repeat = false;// 避免食物生成是在蛇的身体上do { repeat = false;fruit->setX(rand() % 11 - 5);fruit->setZ(rand() % 11 - 5);for (int i = 0; i <= blocks.size() - 1;i++) {x = blocks.at(i)->getX();z = blocks.at(i)->getZ();if (fruit->getX() == x && fruit->getZ() == z)repeat = true;}} while (repeat);addBlock();}// 判断蛇头是否接触到边界Block* snakeHead = blocks.at(0);if (-6 >= x && currentDirection == D_LEFT) {pushSnake();snakeHead->setX(6.0f);currentDirection = D_LEFT;isUpdated = true;}else if (x >= 6 && currentDirection == D_RIGHT) {pushSnake();snakeHead->setX(-6.0f);currentDirection = D_RIGHT;isUpdated = true;}else if (z >= 6 && currentDirection == D_UP) {pushSnake();snakeHead->setZ(-6.0f);currentDirection = D_UP;isUpdated = true;}else if (z <= -6 && currentDirection == D_DOWN) {pushSnake();snakeHead->setZ(6.0f);currentDirection = D_DOWN;isUpdated = true;}// 检查蛇是否碰撞到了石头,如果是则结束游戏for (int i = 0; i < 1; i++) {if (stones[i]->getX() == x && stones[i]->getZ() == z) {printf("Stone collision. You loss!\n");// exit(0); glutHideWindow();}}// 检查蛇是否碰撞到了自己的身体,如果是则结束游戏for (int i = 1; i <= blocks.size() - 1; i++) { if (blocks.at(i)->getX() == blocks.at(0)->getX() && blocks.at(i)->getZ() == blocks.at(0)->getZ()) {printf("Body collision. You loss!\n"); glutHideWindow();blocks.at(i)->setColor(1, 0, 0); }}
};

3. 食物的基类Fruit与障碍物的基类Stone

这两个类也类似,其区别只是在于如何draw显示。我将石头显示成正方体,将食物显示成三棱锥。

class Stone {public:Stone(int pX, int pZ);~Stone();void draw();float getX();float getZ();void setX(float pX);void setZ(float pZ);float getAngle();
private:float x, z, angle;
};

Stone的draw函数

void Stone::draw() {angle += 2.0f;glPushMatrix();glTranslatef(-x, 0, z);glColor3f(0, 1, 1);glutSolidCube(0.9);
}

Fruit的draw函数

void Fruit::draw() {angle += 2.0f;glPushMatrix();glTranslatef(-x, 0, z);glRotatef(angle, 0, 1, 0);float gC = 0.5;glColor3f(128, 1, 1);glBegin(GL_TRIANGLES);glVertex3f(0 - gC, 0 - gC, 0 - gC);glVertex3f(1 - gC, 0 - gC, 0 - gC);glVertex3f(0.5 - gC, 0 - gC, 1 - gC);glTexCoord2f(0.5, 1);glVertex3f(0.5 - gC, 1 - gC, 0.5 - gC);glTexCoord2f(0, 0); glVertex3f(0 - gC, 0 - gC, 0 - gC);glTexCoord2f(1, 0); glVertex3f(1 - gC, 0 - gC, 0 - gC);glTexCoord2f(0.5, 1);glVertex3f(0.5 - gC, 1 - gC, 0.5 - gC);glTexCoord2f(1, 0); glVertex3f(0 - gC, 0 - gC, 0 - gC);glTexCoord2f(0, 0); glVertex3f(0.5 - gC, 0 - gC, 1 - gC);glTexCoord2f(0.5, 1);glVertex3f(0.5 - gC, 1 - gC, 0.5 - gC);glTexCoord2f(0, 0); glVertex3f(1 - gC, 0 - gC, 0 - gC);glTexCoord2f(1, 0); glVertex3f(0.5 - gC, 0 - gC, 1 - gC);glEnd();glBindTexture(GL_TEXTURE_2D, 0);glPopMatrix();
}

4.画图的基类Draw

该类用于画出贪吃蛇的运动区域,为一个11*11的矩形,并且画出边界的正方体线。

这里比较简单,只是循环布置正方体即可,就不放代码。

5. 主函数Main

键位回调函数

使用wsad来控制蛇的上下左右移动。

void keyEvents(unsigned char key, int x, int y) { switch (key) {case 27: exit(0);break;case 'a':snake->move(D_LEFT);break;case 'd':snake->move(D_RIGHT);break;case 'w':snake->move(D_UP);break;case 's':snake->move(D_DOWN);break;}
}
窗口大小改变

这里定义resize函数,当游戏窗口在改变大小的时候,保证游戏界面的横纵比保持一定,不会出现变形的情况,也可以用作全屏展示。

void resize(int w, int h) { //function called on resize of windowif (h == 0)h = 1;float ratio = w * 1.0f / h;glMatrixMode(GL_PROJECTION);glLoadIdentity();glViewport(0, 0, w, h);gluPerspective(50, ratio, 1, 200);glMatrixMode(GL_MODELVIEW);
}

绘制

首先要定义出食物,蛇,石头,然后分别调用这些基类的draw函数来画图

Fruit* fruit = new Fruit(1, 1);Snake* snake = new Snake(0, 0, 3);// 这里以后可以设置关卡,定义不同的石头位置
Stone* stone1 = new Stone(2, 2);
Stone* stones[1] = { stone1 };void draw() {snake->update(fruit, stones); //update snake positionglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();gluLookAt(0, yCamPos, -5, //eye0, 0, 0,  //centre0, 1, 0   //up);Draw d;d.drawGrid();snake->draw();fruit->draw();// build stonesstone1->draw();glutSwapBuffers();
}
主函数

定义窗口的大小,位置,标题等信息,绑定函数

int main(int argc, char **argv) {srand(time(NULL));glutInit(&argc, argv);glutInitWindowPosition(-1, -1);glutInitWindowSize(800, 600);glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);glutCreateWindow("Snake Game");initGL();Draw d;d.buildDLs();glutDisplayFunc(draw);glutIdleFunc(draw);glutReshapeFunc(resize);glutKeyboardFunc(keyEvents);glutSpecialFunc(specialKeys);glutMouseFunc(mouseEvents);glutMainLoop();return 1;
}

实验截图

实验截图如下:

粉红色三棱锥为果实,蓝色正方体为障碍物。绿色正方体为蛇的头部。蛇在接触果实后会自动增长一个格子,碰到障碍物的时候,游戏结束,命令行显示游戏结束。

第二个截图演示的是,蛇在碰到自己身体后,游戏结束的情形。

四、实验感想

​ 本周的贪吃蛇任务功能并不复杂,主要的难度在于如何使用opengl来绘制一个3d的蛇,以及食物。这里只是简单的使用正方体、三棱锥以及不同的颜色来代表不同的物品。关于碰撞检测方面,我是利用坐标来进行判断,获取蛇头的坐标与地图中食物、石头、身体的这些坐标进行比较,在每一次更新的时候进行检测。在更新动画方面,我也是以蛇头为主,身体跟随蛇头来进行绘制,绘制蛇的身体其实就是绘制多个正方体,只需要按顺序根据蛇头的坐标进行绘制即可。

PAOGD个人作业4——利用OpenGL设计贪吃蛇游戏相关推荐

  1. 利用OpenGL设计贪吃蛇游戏

    利用OpenGL设计贪吃蛇游戏 文章目录 利用OpenGL设计贪吃蛇游戏 任务介绍 游戏玩法 开发环境 游戏实现 贪吃蛇游戏的框架搭建 主程序 游戏类 游戏对象类 工具类 着色器类 摄像机类 精灵渲染 ...

  2. python贪吃蛇设计目标_基于 pygame 设计贪吃蛇游戏

    基于 pygame 设计贪吃蛇游戏 贪吃蛇游戏通过玩家控制蛇移动,不断吃到食物增长,直到碰到蛇身或边界游戏结束.其运行效果如下所示: 游戏开始时,先导入可能需要用到的包. import time im ...

  3. 贪吃蛇c语言课程设计报告下载,c语言课程设计-贪吃蛇游戏

    c语言课程设计-贪吃蛇游戏 C 语言课程设计 1 山东交通学院 C 语言课程设计 贪吃蛇游戏 院(系)别 交通与物流工程学院 班 级 物联 131 学 号 130516122 姓 名 王赛 指导教师 ...

  4. 如何用python开发一个贪吃蛇游戏_教你一步步利用python实现贪吃蛇游戏

    教你一步步利用python实现贪吃蛇游戏 来源:中文源码网    浏览: 次    日期:2019年11月5日 [下载文档:  教你一步步利用python实现贪吃蛇游戏.txt ] (友情提示:右键点 ...

  5. 计算机课程编程设计贪吃蛇游戏设计,c语言课程设计报告--贪吃蛇游戏系统

    c语言课程设计报告--贪吃蛇游戏系统 院 系 计算机科学技术学院 班 级 组 长 学 号 指导教师 2017 年 3 月 2 日 C 语言语言基础课程设计基础课程设计 贪贪贪贪吃吃吃吃蛇蛇蛇蛇游游游游 ...

  6. python设计贪吃蛇游戏论文_用Python写一个贪吃蛇AI,让程序自己玩游戏

    前言 这两天在网上看到一张让人涨姿势的图片,图片中展示的是贪吃蛇游戏, 估计大部分人都玩过.但如果仅仅是贪吃蛇游戏,那么它就没有什么让人涨姿势的地方了. 问题的关键在于,图片中的贪吃蛇真的很贪吃XD, ...

  7. 利用Python自制贪吃蛇游戏

         作者:丹枫无迹   来源:大龄码农的Python之路 在开始今天的正文前,首先为大家推荐一本关于数据产品经历不错的书籍: 一.游戏介绍 贪吃蛇是个非常简单的游戏,适合练手.先来看一下我的游戏 ...

  8. C语言贪吃蛇游戏代码,贪吃蛇C语言代码实现大全

    一.C语言贪吃蛇代码实现前言 设计贪吃蛇游戏的主要目的是让大家夯实C语言基础,训练编程思维,培养解决问题的思路,领略多姿多彩的C语言. 贪吃蛇是非常经典的一款游戏,本次我们模拟在控制台实现贪吃蛇游戏, ...

  9. 字符版本贪吃蛇游戏设计及算法

    字符版本贪吃蛇游戏设计及算法 贪吃蛇,是一款经典的益智游戏.我们可以通过算法捕捉和传递智能,设计出能自动跑着吃食物的智能蛇. 有不少人对此研究,设计出了很棒的算法,如图: 归纳一些比较高级的算法为三类 ...

最新文章

  1. 【朱-刘算法】【最小树形图】hdu6141 I am your Father!
  2. java int parse_java中Integer.parseInt和Integer.valueOf的区别
  3. 小球弹起次数及高度(python)
  4. redis 版的 hello world
  5. matlab与专业应用,MATLAB应用技术--在电气工程与自动化专业中的应用
  6. C语言递归算法(一)
  7. IDEA中的HTTP Client Editor测试API
  8. datetime 比较_Python 字典中key命中取值的两种方法性能比较!
  9. 逆波兰式数学表达式求解
  10. request获取各种路径记录
  11. 译Selenium Python Bindings 6 - WebDriver API
  12. C# string补位
  13. radio change事件
  14. 在FreeBSD 用户空间与内核空间使用内存共享
  15. 案例介绍ps去除网纹的方法
  16. 【python】使用python绘制地图时手动添加比例尺
  17. agc013e Placing Squares(模型转化+dp+矩阵优化)
  18. ios虚拟服务器降级,升级了iOS 12很后悔,没关系手把手教你如何降级!
  19. 基于Java毕业设计房屋租赁平台源码+系统+mysql+lw文档+部署软件
  20. 835616-60-9,4-Fluoro-thalidomide用于补充CRBN蛋白的沙利度胺基脑啡肽配体

热门文章

  1. 网页自动提交Form表单的方法
  2. 关于hi3516DV300的VDEC一些测试
  3. 解决基于SpringMVC+MYSQL注解@Transaction事务无效问题
  4. vue全局注册一个文件夹下所有的组件
  5. 总会计师是什么职务?
  6. 详解 C++ STL 六大组件,看完不懂打我...
  7. 在win7系统中用U盘安装Ubuntu16.04
  8. 正则表达式 去掉字符串后面所有的内容
  9. 闪聚支付 第2章-对接SaaS以及用户认证OAuth2.0概述
  10. 慕课网模抖音小程序 springboot笔记