作品名称:
《古堡危机》
小组团队名称: 拾荒三人组
日期:2018年12月
目录
第一章 简介
前言
在上大学的期间,我一直想自己制作一款枪战游戏,但一直苦于无从下手。在步入大三时,我有幸接触到了计算机图形学这门课程。这们课程的实验部分就是教会我们如何在计算机上使用代码绘制图形。如果加上一些代码逻辑,不断刷新窗口重画图形的话,就可以演变成简单动画。在学习完一些粗浅的图形学知识以后,我突然发现,以我们现在的知识储备,完全可以制作一款射击类游戏,于是我找到了志同道合的两名同学组成团队,计划在短短的一个月内制作一款游戏,并将其参加比赛。
最后,我们总归制作了一款像模像样的射击类游戏,虽然画面和模型十分粗糙,但我们制作完后还是收获了慢慢的成就感。
项目的创意设想、游戏类型、实现的功能、项目意义
游戏的创意设想:此游戏的风格有点借鉴我的世界这一个游戏,我的世界曾经是一个非常火爆的游戏,同时现在的丧尸题材电影和小说非常热门,如生化危机等电影。我们把游戏添加丧尸这一题材,让人们在丧尸中如何获得生存,而且我们通过游戏的制作把我的世界高度还原,使丧尸题材和我的世界这一生存游戏高度结合,也符合当前的潮流和热点。
实现的功能:
场景:树、流动湖、恐怖的古堡、阴深的雾、散发着微弱光的路灯、有坡度的地形、天空盒
对象:僵尸
模样:
灰色的长方形与红色的长方形堆积在一起
行为:
生命值、自动跟踪、靠近主角扑打、被攻击时显示流血状态-变红、被攻击时倒退、死亡后倒地
界面:登入选择界面、游戏页面、游戏结束页面
交互:
- 通过键盘控制角色上下左右移动,跳跃
- 鼠标控制灭点的位置,控制视角移动(wsad)
- 选择武器(手枪、AK、棒子),换弹夹
- 显示当前游戏信息(当前僵尸数、已经击杀数、游戏运行时间、主角血量)
游戏类型:
基于opengl开发的PC端第一人称FPS射击类游戏
项目意义:
通过本次项目的开发,我们高度还原了一个3d漫游游戏。我们在进行小组开发此项目时,认识了如何高度面向对象编程,如何合理小组分工。制作这个古堡危机之丧尸围城,我们认识了3d漫游游戏的开发,对以后去到实际工作起到很好的借鉴作用。
此项目以我的世界为蓝本,以古堡丧尸为背景,简述了我们探险进入古堡求生的故事。
此项目完成度不错:
- 场景把日常的该有的事物都添加进去了。
- 僵尸的外形模样和我的世界里的人物模型相像,栩栩如生。
- 僵尸的AI行为做的挺不错,把僵尸对人物的自动跟踪(自动跟踪算法)、靠近主角时抬臂扑打(边界检测算法)、走路、被击打时后退、被击打时变红,死亡后倒地都很好的实现。
- 游戏交互做的不错。通过键盘控制角色上下左右(wsad)移动、鼠标控制灭点位置(视点)、选择武器(手枪、AK、棒子)、换弹夹,显示游戏信息(当前僵尸数、击杀数、游戏运行时间、主角血量),把该有的交互都做了。
由此可见这是可玩度不错的游戏。
而且这个游戏是我们不使用游戏引擎开发,使用原生的opengl图形库进行开发,更加锻炼了我们游戏编程的基础能力。
Opengl
OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。而另一种程序接口系统是仅用于Microsoft Windows上的Direct3D。OpenGL常用于CAD、虚拟实境、科学可视化程序和电子游戏开发。
OpenGL的高效实现(利用了图形加速硬件)存在于Windows,部分UNIX平台和Mac OS。这些实现一般由显示设备厂商提供,而且非常依赖于该厂商提供的硬件。开放源代码库Mesa是一个纯基于软件的图形API,它的代码兼容于OpenGL。但是,由于许可证的原因,它只声称是一个“非常相似”的API。
OpenGL规范由1992年成立的OpenGL架构评审委员会(ARB)维护。ARB由一些对创建一个统一的、普遍可用的API特别感兴趣的公司组成。根据OpenGL官方网站,2002年6月的ARB投票成员包括3Dlabs、Apple Computer、ATI Technologies、Dell Computer、Evans & Sutherland、Hewlett-Packard、IBM、Intel、Matrox、NVIDIA、SGI和Sun Microsystems,Microsoft曾是创立成员之一,但已于2003年3月退出。
作品代表图
软硬件环境说明
电脑硬件:
操作系统:win10
Vs平台:2015
游戏操作说明
在主界面中:
F1 F2 选择关卡的模式
F3进入帮助界面
F9退出游戏
----------------------------------------
游戏中:
Wsad控制角色移动
鼠标控制视角
123键选择武器,r键换弹
F9退出游戏
项目制作过程进度,开发周期
|
时间节点 |
项目进度 |
|
4月9日 |
完成鼠标控制第一人称视角 |
|
4月10日 |
添加天空盒技术及坐标系的确定 |
|
4月14日 |
地形生成,山和湖制作 |
|
4月19日 |
添加地形上的移动实现以及各种移动状态 |
|
4月24日 |
添加僵尸,僵尸搜索及攻击倒地后退等动作,添加值至场景 |
|
4月28日 |
添加主角攻击判定,武器设定 |
|
4月30日 |
制作完善关卡流程 |
|
至今 |
基本制作完成,完善和检查bug |
程序代码量
3D_CastleEx.cpp 359
My_Camera.cpp 204
My_CastleEx.cpp 425
My_Item.cpp 139
My_Player.cpp 295
My_Scene.cpp 273
My_SkyBox.cpp 111
My_Terrain.cpp 264
My_Texture.cpp 397
Robot.cpp 474
以上的头文件.h 10*60=600
总计大概可能不确定不一定有3600行代码
本说明文档结构
第一章主要介绍了本作品的创意和实现的功能,介绍了我们使用到的opengl技术,然后简要介绍我们的作品截图,软硬件操作环境游戏操作说明等,记录游戏开发的周期,最后介绍文档的结构
第二章主要讲解我们是如何一步一步搭建起游戏内的主要环境,先从建立坐标系入手,然后添加天空盒,生成地形,添加树木,完成场景搭建。
第三章主要介绍如何实现第一人称角色漫游,在没有任何物理引擎的情况下实现在地形上行走跳跃,鼠标控制转动视角。
第四章开始制作游戏内使用到的模型,以及介绍如何制作僵尸攻击,倒地击退,武器的攻击等简要的动画效果。
第五章讲解战斗模块的实现,玩家的攻击力血量等各种主要参数的设置,及攻击判定以及触发各种战斗效果。
第六章说明本作品的关卡设计,包括整个游戏流程,类之间的包含关系,代码执行步骤,与游戏画面的 绘制等
第七章以生存模式为例,演示整个游戏流程
第二章 场景搭建
我将简单演示我们是如何一步一步使用代码将场景搭建起来的
建立坐标系
不像unity,在软件中自带参考系,可以很方便地搭建场景。
使用opengl,假如什么代码都不编写,运行出来将会是一片黑暗。我们想要搭建地形将十分困难,所以建立一个坐标系参考十分必要
如下图所示,我们使用简单的画线函数
以一定距离为步长,分别在z轴和x轴平行画线,最后换成红色分别画出xyz轴
我们将使用这个平面,来作为我们整个场景搭建的参考。
天空盒
在实时渲染中,如果要绘制非常远的物体,例如远处的山、天空等,随着观察者的距离的移动,这个物体的大小是几乎没有什么变化的,想象一下远处有一座山,即使人走进十米、百米、甚至千米,这座山的大小也是几乎不怎么改变的,这个时候可以考虑采用天空盒技术。
天空盒就是将一个立方体展开,然后在六个面上贴上相应的贴图,如下图所示。
为了迎合我们游戏的主题,我们寻找了一座古堡的天空盒贴图
天空盒添加完后的效果如下所示:
在随后的实现中,天空盒会以玩家为中心,一直随着玩家的移动而改变位置。
地形的生成
地形的生成我参考了某论文,它的原理是这样叙述的:
我将其中演示的代码封装成一个函数,使得传入一个二维数组,\他将打印二维数组里相同数值的地形,例如传入a[i][j],就会生成长i个单位,款j个单位,高a[i][j]个单位的地形
本我们预设计的地图是700*700大小,意味着二维数组长700个单位,宽700个单位,生成地形其实十分简单,只需要将数组里的数全都随机一个出来就行了。
构建地形的思路是将700*700的地图分成不同的块大小,保存在一个三维数组里,然后构建索引,调用glDrawElements函数,即可以以每个块为间隔画出不同高度的地形。
下图是高度随机在0~5范围生成的地形
但是随机生成出来的地形太过单调,而且假如随机数组的值过大,就会产生一些很尖锐的地形。
我们希望我们搭建的地形有平缓的山,以及一个湖面,所以我们必须拥有一个可以保存地形数据的文件,将700*700单位的地图高度数据保存在里面,在游戏加载的时候,读取出里面的数值,然后再绘制出来
建立地形是一个很艰辛的过程,我们先随机出一块数值高度为0~5的地面,然后划出两块圆形区域,从外到内高度递增,然后划出一块椭圆区域数值为-3作为我们的湖,然后不断人工修改文本内的数值,以达到我们预期的效果。
最终我们的地形效果如下
对应的地形数据如下
如上图所示,这就是记录我们3D程序的txt文件,其中数值比较高的两块区域就是山。而左下角一堆-3的区域就是湖。
湖的生成直接取一个高于地形的平面,然后贴纹理。
灯塔模型
这是一个由长方体,圆球和圆锥构成的一个路灯模型,我们的设想是玩家一出生就面对这个路灯。
此外,这个路灯模型标志着地图的中心,当后续的场景加入大雾的天气状况时,,改路灯可以起到指示的作用
树木
类似于湖面纹理,我们找了4种形状的树木,可以参考当今游戏里树的生成方法,建立一个交叉平面,在两个平面内贴同一颗树的纹理
树的位置如果要记录的话比较麻烦,所以是动态随机生成的,每进一次游戏都不一样。但是数的密度有讲究,比如不能生长在湖里,山上的树比较少,平地的树比较多等
最终的效果如下
小结
在本章,我们已经建立了一个较为粗糙的场景,虽然对比于unity建出来的场景烂得多,但完成上面的场景还是花去了我1/2的时间,而每次观察我的地形生成得怎么样的时候,我都要手动的写出视角位置的数值,因此下一步我需要一种高效的方式来移动和控制视角,并且,能够模拟在这个地形上行走
第三章 第一人称场景漫游
基础理论
在opengl中,整体视角控制使用Perspective来实现
而控制视线的方向则由LookAt来实现,如下图所示,XYZ表示玩家所处位置,LX,LY,LZ表示玩家看向的位置,每局改变XYZ可以模拟玩家行走,改变LX,LY,LZ可以模拟玩家转动视角
角色位置及视角控制
在opengl里,没有提供鼠标控制视角的函数,而且由于太过冷门,网上也没有解决方案。
因此我们自己想了一个办法。
首先将鼠标固定在屏幕中间,在每次调用重画函数时记录鼠标与屏幕中心的距离,然后强制将鼠标再次归到屏幕中间。
如此一来我们就知道了鼠标在屏幕坐标上移动的x,y分量。我们将基于这x,y分量来改变我们游戏中的视角
由于改变视角最终体现在代码中的是改变视点,也就是说我们要最终得出一个点的坐标,具体分析如下,假设鼠标在屏幕上移动了angleX和angleY个像素的距离
因此,最后视点与玩家坐标(x,y,z)的对应关系如下
lx = x + sin(angleX)*cos(angleY);
ly = y + sin(angleY);
lz = z + cos(angleX)*cos(angleY);
进入游戏前,先调用window库函数将鼠标光标隐藏
采用该方案后,我们已经可以完全使用鼠标来控制视角,不过需要将游戏窗口始终保持在屏幕中央
使用键盘来模拟控制角色移动,当玩家按下键盘中的一个键时,程序开始累加对应键盘方向上的一个值。随后在每盘重画时,按照下面的公式改变摄像机位置
地形高度判断,地图边界检测
生成了地形,还要判断玩家当前所处的地形高度,不然就会让玩家穿到地下去了。因此设置相机视点,要基于当前的x z值计算出地形的高度。,而地形高度数据则保存在我们读取了txt文本的二维数组中
返回地形当前高度的方法直接参考有关资料
模拟上述理论,我们可以根据伪代码写出c++代码
这时我们已经可以贴着地面行走的了
模拟行走震动
当前角色在行走时,根据地形会有高度变化,但都是沿着地形的梯度变化,非常平滑,没有动感。
因此当角色在行走状态时,给视点y值加上一个值在1 ~ -1之间变换的偏移,这样角色在行走时视角就会上下上下地变化。模拟真实走路的感觉
跳跃判断
想要实现跳跃,我们要自己给角色添加一个状态标签,分别为行走和跳跃,当按下空格键后,状态变为跳跃,始终给角色添加一个向上的速度v,只不过这个速度v每盘重画时都减去一个向下的加速度a,这样角色在上升到一定高度的时候,就会下降,最终落到与地形高度一致时,状态继续变为行走状态。
小结
完成这一模块的主要难点是所有的数值都要自己推导出来,难免有时候会有很多bug,在我们不断调试并修正以后,终于达到了想要的效果
我们已经添加了第一人称角色的行为,使得在场景内移动的自由度大大提高,接下来,我们是时候应该设计一些其他的东西,比如我们最终要打的小僵尸
第四章 模型和动画效果
图形构成原理
在开始介绍我们整个动画的设计之前。我想先介绍一下我们使用Opengl技术图形的构成的原理,方便后续介绍我们的工作。
如下图代码所示,该代码的作用就是在三位坐标零点画一个长宽高为1单位的长方体,并设定颜色为红色。
假如我们要将他移动一个位置,只需要加上
将他移动到(1,1,1)点
假如我们要将他旋转一个角度,加上
如果想让它旋转起来,就在外部添加一个循环,每盘重画时增加角度,就模拟它旋转了起来+
图形构成
小僵尸
我们设计的僵尸主要使用长方体构成,手脚头身体都是长宽高不同的,颜色不同的长方体。如下所示
大棒
大棒的图形构成是一个圆柱,由于opengl不能直接画出一个圆柱来,因此我们就将其近似成一个六棱柱
手枪步枪
手枪与步枪如果要使用立体图形来组成的话会比较复杂,因此我们想了个办法,直接在一个平面贴上手枪或步枪的图片,再配以一定角度的形变,就和立体的没什么区别,经过试验,效果还不错,效果如下。
子弹
子弹可以使用一个空间上一条直线来代替,颜色设定为黄色,效果如下
武器动画
大棒
我们做出大棒是想玩家使用它来攻击僵尸的,所我们要做出一个挥舞的动作。这样一个动作需要由两个步骤构成,向前摆动以及向后摆动归位。
当玩家按下攻击键后,大棒的状态变为攻击状态,因此在每次绘制时都旋转一定角度,该角度每次加一个固定值,到达最大值后再减去该值,完成一个攻击循环。
手枪步枪
手枪与步枪的攻击会有后坐力,因此在攻击时要模拟出抬起的效果。手枪的动画制作与大棒一样。不同的是步枪的攻击。
步枪攻击有可能是连发状态,鼠标按下时,步枪改为连发状态,鼠标抬起时连发状态改为普通状态。步枪画出一次攻击的过程后,检查是否为连发状态,如果还处在连发状态,则继续绘制一次攻击过程
僵尸动画
此游戏的名字叫做古堡危机之丧尸围城,所以丧尸是不可缺少的一部分。丧尸的制作采用沙盒游戏里的人物样式,身子和头都是四方的,肤色采用了灰色。因为在影视作品或一般的游戏中丧尸的身体是腐败的,所以灰色很好地呈现了丧尸的特征,而眼睛采用了红色,因为红色代表血腥,突出了丧尸的恐怖和残忍。还有獠牙,这是丧尸不可缺少的特征。
僵尸被击退
当机器人被攻击时,会判断机器人是否被攻击,如果被攻击则设置一个变红值,让机器人变红。
编写了机器人倒退算法,首先设定机器人在空中时间,在上一半时间是上升的行为,后一半时间是下降的时间,使用了重力加速度公式
上升:H = H + 1/2 * g * t
下降:H = H - 1/2 * g * t
然后再加入水平面上移动的公式,即倒退的距离
this->z = this->z + cos(this->angle/360*2*PI) * zs;
this->x = this->x + sin(this->angle/360*2*PI) * zs;
两者混合就可以实现机器人向后倒退的算法
僵尸被击杀倒地
将僵尸手脚的位置归位,然后让僵尸绕y轴旋转,待到与地面平行,即完成一次倒地动画
僵尸攻击动画
编写了攻击函数,设置僵尸攻击的手的角度,当僵尸靠近主角时(边界检测算法):通过计算僵尸与主角的距离,进入到一定的距离后,会调用攻击函数,手就会绕着z轴向上转到180度时,又会向下转到0度(以y方向的反方向为正方向,即0度方向)。
小结
至此,游戏中所有的图形构成已经介绍完毕,小僵尸的模型和动画以及武器已经制作完成,有了主要的敌人和武器,下一步我们将实现战斗模块
第五章 战斗模块
参数设定
战斗模块需要设定一些参数来调整游戏的平衡性,下面列举一些我们设定的游戏中一些主要的参数
小僵尸
生命 100
移动速度 3
攻击伤害 1
玩家
生命 100
移动速度 10
大棒伤害 50
手枪伤害 35
步枪伤害 30
角色武器命中判定
角色一共有三把武器,大棒,手枪和步枪。
在判定僵尸受到攻击时,调用改变僵尸状态的接口,使僵尸作出被击退一段距离的动画,假如被击退后检查到血量为零,则进入到死亡状态,播放死亡倒地动画
大棒
大棒的攻击范围实际上是一个圆球,当僵尸与这个圆球的距离接近到一个值时则判断僵尸被攻击,由于大棒的攻击属于范围攻击,所以每一个僵尸都要计算到这个圆球的距离,然后扣除相应的血量。
手枪与步枪
手枪与步枪的攻击主要是判断子弹是否有接触到僵尸,发射出去的每一个子弹都是一个对象,保存有伤害值和行进方向等信息,每个子弹对象在每盘重画时加上对应方向的距离。
子弹对象使用vector数组作为容器,对于vector中的每一个子弹都要对所有僵尸判断一遍。
为了简化计算,在每次判断一个子弹是否命中僵尸的时候首先判断他们的xyz值是否处在一定范围内,如果其中某一个值较大的话,可直接排除碰撞。
僵尸的命中判定直接就是一个椭球,每次判断的时候计算计算子弹是否在这个椭球范围内
子弹属于单体攻击,所以在判定子弹攻击到僵尸以后要移除子弹
当子弹没有碰到任何僵尸飞出地图的时候,应当移除子弹
僵尸自动寻路
丧尸的行为我设定了可以不停地追踪主人公(编写了自动跟踪算法)
原理,首先丧尸的视角面向Z方向,与Z方向平行,定义了一个角度,这个角度在z方向为0度。求出丧尸与主角的夹角(分为四个象限角讨论),这个主要使用了atan数学函数:
(角度 = atan ( xDifferenceValue / zDifference ) / PI ) * 180),
通过机器人的位置与丧尸的位置x,z的差值,来计算两者的夹角。求出夹角后,采用
this->z = this->z + cos(this->angle/360*2*PI) * zs;
this->x = this->x + sin(this->angle/360*2*PI) * zs;
设定步伐zs,然后朝那个方向走。
僵尸攻击范围判定与玩家流血状态
判断僵尸是否攻击到玩家则是在僵尸做完挥手的动作以后
僵尸做完攻击前摇后,判断其位置与玩家的距离,当小于某一个值以后就会扣除玩家的血,并触发流血状态。
玩家的流血状态在2D状态下显示,显示的范围是一个椭圆,在上方显示。每盘重画时减少透明度,以达到渐变效果,透明度到达0时,流血状态结束。
音效
我们在网上搜寻了许多游戏音效包括:行走音效,大棒攻击音效,步枪开枪音效,手枪开枪音效,僵尸被击中音效,僵尸击中玩家音效,玩家死亡音效。
音效主要使用palyviedio函数,当需要触发某个动画时,比如开枪动画,则在鼠标点击事件改变步枪状态的同时,将需要播放的音效添加到音频流中。
小结
各种战斗的判断已经完成,接下来我们需要添加一个掌控全局的类来监视整个战况,达到控制游戏流程的效果,随后我们就可以设计关卡流程
第六章 关卡设计
类之间的包含关系
图形绘制流程图
生存模式
在生存模式中,没有游戏时间的限制,待到玩家血量归零时,游戏结束。记录玩家击杀僵尸的数量,然后记录最高分。
随着时间的不断增加僵尸的数量会呈爆发式增长,而且僵尸的血量和速度都会线性提升。这时候玩家生存难度就会不断增加,最后血量归零,游戏结束
关卡模式
关卡模式下,以每一个阶段为单位,僵尸数量和基本状态都由人为设定,当玩家击杀完所有僵尸后,游戏进入到下一个阶段,关卡数加一,最后记录玩家完成关卡的数量。
第七章 游戏流程示例
以游戏中的生存模式为例,讲解游戏的主要流程和效果
开始界面
游戏编译运行或点击exe文件后
游戏整体呈现灰暗的风格,开始界面的背景是从上帝视角拍摄的图片,下面全部都是朝你走来的僵尸。
在开始界面,可以按f1,f2等选择对应的操作,可选的操作如下
开源一下
游戏内场景
游戏内的主要场景如下
界面说明
游戏中显示给玩家的信息
攻击僵尸
被攻击的僵尸会显示红色
手枪攻击
子弹的距离没有限制,直到飞出地图,如下图所示是攻击很远地方的僵尸
步枪攻击
步枪可以连发,有较大后座力
被攻击
玩家被僵尸攻击时会有音效,并在上方显示流血状态
死掉的僵尸
我们没有对死掉的僵尸做清除操作,我们觉得这样比较酷,僵尸不管有没有死亡,我们都将绘制出来
结束画面
第八章 总结与展望
通过此项目的学习,我们了解3d游戏开发的协同合作,而且交互功能,界面做的不错。3d的游戏最重要的是漫游,我们游戏也高度使用了漫游这一功能。再说到当前吃鸡类游戏非常火爆,我的世界这一个游戏曾经也是一个非常好的游戏,我们通过本次项目的学习,对吃鸡类游戏,还有我的世界诸如游戏开发有了一定的了解。我们高度仿真了我的世界这个游戏,完成度也非常之高。
应用价值:此游戏已经可以给一些小孩用来娱乐使用。
不足之处:此游戏没有制作多个地图,只在一个地图内,玩一段时间可能感觉没有什么疲倦之处,但是玩得久了,就会感到无聊了。
将来可以改进之处:可以设置多地图和多关卡,可以把自己的游戏与数据库连接起来,把自己的数据上传到数据库,这样就可以做到存档,而且还可以使用网络编程对游戏进行通信,到时就可以进行联机多角色对战,多角色闯关,增加游戏对用户的黏度。
在此3D项目中有很多技术都是自己想出来的,比如说使用鼠标控制摄像机的视角,在网上根本找不到参考资料。所以只能自己摸索,最后总算找了一个实现的方法。
以上就是大三上学期的图形作业了,上面所说的开发时间是为了应付比赛写的。说到项目用到什么高深的算法,也没有,只不过是灵活运用了基础算法。
最后想说一句java大法好!
代码已上传~
视频观看地址:https://www.bilibili.com/video/BV1ct41167ok