osg坦克大战游戏开发过程

一、3D建模

(1)实现方法

3D建模是3D游戏开发中重要的一部分,若没有3D建模的过程而只靠osg内置的基本体,就不会有精致的模型。我们使用了Maya 2014来完成相应的建模工作,得到了我们的游戏中的主体——一辆虎式坦克。

(2)实现过程

在建模过程中,我们主要使用了立方体模型,通过大量的挤压命令,产生棱角分明的装甲。而最为复杂的履带则是通过建立运动路径并在路径上克隆单一履带单元来完成的。

(3)效果展示

二、纹理映射

(1)实现方法

在游戏中,我们需要将地图铺上贴图,以此来分辨角色是否在移动。为此,我们先建立了一个足够大的四边形来代表地面,并付给其一个纹理贴图。

(2)实现过程

指定一系列顶点来描述一个四边形:

osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;

vertices->push_back(osg::Vec3(-50.0f, -50.0f, -0.8f));

vertices->push_back(osg::Vec3(-50.0f, 50.0f, -0.8f));

vertices->push_back(osg::Vec3(50.0f, 50.0f, -0.8f));

vertices->push_back(osg::Vec3(50.0f, -50.0f, -0.8f));

并指定其法向量:

osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;

normals->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));

随后设置纹理的坐标:

osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array;

texcoords->push_back(osg::Vec2(0.0f, 0.0f));

texcoords->push_back(osg::Vec2(0.0f, 1.0f));

texcoords->push_back(osg::Vec2(1.0f, 1.0f));

texcoords->push_back(osg::Vec2(1.0f, 0.0f));

加载纹理图片:

osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;

osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/lz.rgb");

texture->setImage(image.get());

创建一个geometry节点并将图形和纹理添加到节点:

osg::ref_ptr<osg::Geometry> quad = new osg::Geometry;

quad->setVertexArray(vertices.get());

quad->setNormalArray(normals.get());

quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));

quad->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET);//BIND_OVERALL);

quad->setTexCoordArray(0, texcoords.get());

创建一个geode节点并将geometry节点添加到geode节点并加入场景:

osg::ref_ptr<osg::Geode> map = new osg::Geode;

map->addDrawable(quad.get());

map->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get());

root->addChild(map);

(3)效果展示

三、自动寻路

(1)实现方法

游戏中,对方坦克的目标是击毁我方坦克,对方坦克由电脑控制,所以需要一套人工智能来决策和执行它的动作行为。我们采用最简单的状态机的方法,实现了敌方坦克向我方坦克移动以及射击的功能。为了平衡游戏难度,当敌我坦克距离拉大时,敌方的命中率会线性下降,直到阈值。

(2)实现过程

if (tank->life > 0)

{

switch (state)

{

case 0://瞄准开炮状态

//获取炮塔旋转角度

if (current * 180.0f / osg::PI > accuracy + 2)//若在允许范围外

{

//旋转炮塔

}

else//若在允许范围内

{

now = clock();

if (now - last >= fireGap){//若已过开炮间隔

if (rand() % 100 < 50){//随机决定变量有1/2的几率决定开炮

//开炮

last = clock();

//转入下一个状态

}

}

}

break;

case 1://移动状态

switch (stage){

case 0://旋转车身

//获取炮塔旋转角度

if (current * 180.0f / osg::PI > accuracy + 1)//若在允许范围外

{

//旋转车身

}

else{//若在允许范围内

//转移到下一个阶段

}

break;

case 1://移动车身

//移动车身

if (count >= stageCount[1]){//如果到达阶段限定的时间

//转移到下个阶段

}

break;

case 2://旋转车身

//获取炮塔旋转角度

if (current * 180.0f / osg::PI > accuracy + 1)//若在允许范围外

{

//旋转车身

}

else{//若在允许范围内

//转移到下一个阶段

}

break;

case 3://移动车身

//移动车身

if (count >= stageCount[3]){ //如果到达阶段限定的时间

转移到瞄准开炮状态

}

break;

default:

break;

}

break;

default:

break;

}

}

(3)效果展示

由于此要素无法通过截图展示,请运行程序自行观察。

四、碰撞检测

(1)实现方法

游戏中规定,当任意一方的坦克被炮弹击中则生命值减一,当两方坦克之间发生碰撞之后双方的生命值也会减一。为了实现此项功能,需要引入碰撞检测。osg中提供了两种方法,一种是使用求交器,另一种是使用包围盒。

求交器可以被绑定到遍历器上,以实现整个场景的求交。常用的求交器有直线求交器和多边形求交器。由于直线求交器只检测直线与多边形的碰撞事件,故多用于判断鼠标点击事件,此处若想完成既定功能需使用多边形求交器。但是多边形求交器是一组较复杂的运算规则,所以在设定初始值时需同时设定需要被检测的多边形,如果这个多边形每帧发生变化,则需要每帧都重新指定多边形求交器的参数,具体来说就是polytope类型的变量,用以指定多边形的网格,而这一过程比较耗费时间,且其位置默认为坐标原点,需自己获取其实际世界坐标。

因此,我们使用了第二种方法,即包围盒的方法。osg中的包围盒有两种,一种是getBound()函数返回的球形包围盒,另一种是getBoundingBox()函数返回的立方体包围盒,但是只有Drawable节点才有getBoundingBox()的函数而且此包围盒,故不包含任何位置旋转等信息,相反所有Node节点都有getBound()函数,故MatrixTransform节点的getBound()函数包含位置旋转等信息。由于我们的实体节点是由MatrixTransform节点挂载的,所以想用包围盒只能选用球体包围盒。但是球体包围盒有一个缺点,就是范围不准确,可能比实际物体要大一点。

(2)实现过程

//两辆坦克的碰撞

if(tank1.transform.getMatrix()->getBound().intersects(tank2.transform.getMatrix()->getBound()))

{

tank1.life--;

tank2.life--;

if (tank1.life <= 0){

root->removeChild(tank1.transform.getMatrix());

last1 = clock();

}

if (tank2.life <= 0){

root->removeChild(tank2.transform.getMatrix());

last2 = clock();

}

}

(3)效果展示

由于此要素无法通过截图展示,请运行程序自行观察。

五、天空盒

(1)实现方法

天空盒可以理解为内部附有贴图的立方体,当镜头移动时,他们也会随着镜头移动,而当镜头旋转时,他们不会随着镜头旋转,从而产生图像在遥远地方的感觉且不同方向的图像是连续且不同的。另一个关键在于天空盒的深度必须是最大,这样才能不会遮挡所有其他物体。

(2)实现过程

//SkyBox类构造函数

SkyBox::SkyBox()

{

setReferenceFrame(osg::Transform::ABSOLUTE_RF);

setCullingActive(false);

osg::StateSet* ss = getOrCreateStateSet();

ss->setAttributeAndModes(new osg::Depth(         osg::Depth::LEQUAL, 1.0f, 1.0f)); //设置深度

ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

ss->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);

ss->setRenderBinDetails(5, "RenderBin"); //设置渲染顺序

}

//设置各方向贴图

void SkyBox::setEnvironmentMap(unsigned int unit,

osg::Image* posX, osg::Image* negX,

osg::Image* posY, osg::Image* negY,

osg::Image* posZ, osg::Image* negZ)

{

osg::ref_ptr<osg::TextureCubeMap> cubemap =

new osg::TextureCubeMap;

cubemap->setImage(osg::TextureCubeMap::POSITIVE_X, posX);

cubemap->setImage(osg::TextureCubeMap::NEGATIVE_X, negX);

cubemap->setImage(osg::TextureCubeMap::POSITIVE_Y, posY);

cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Y, negY);

cubemap->setImage(osg::TextureCubeMap::POSITIVE_Z, posZ);

cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Z, negZ);

cubemap->setResizeNonPowerOfTwoHint(false);

getOrCreateStateSet()->setTextureAttributeAndModes(unit, cubemap.get());

}

//加载天空盒的几何形状

osg::ref_ptr<osg::Geode> geode = new osg::Geode;

geode->addDrawable( new osg::ShapeDrawable(

new osg::Sphere(osg::Vec3(), scene->getBound().radius())) );

//创建天空盒类的指针并设置

osg::ref_ptr<SkyBox> skybox = new SkyBox;

skybox->getOrCreateStateSet()->setTextureAttributeAndModes(0, new osg::TexGen );

skybox->setEnvironmentMap( 0,

osgDB::readImageFile("Cubemap_snow/posx.jpg"),

osgDB::readImageFile("Cubemap_snow/negx.jpg"),

osgDB::readImageFile("Cubemap_snow/posy.jpg"),

osgDB::readImageFile("Cubemap_snow/negy.jpg"),

osgDB::readImageFile("Cubemap_snow/posz.jpg"),

osgDB::readImageFile("Cubemap_snow/negz.jpg") );

skybox->addChild( geode.get() );

(3)效果展示

六、Shader

(1)实现方法

由于使用Maya建模时模型是带有材质及贴图的,所以导出的obj文件也是有材质和贴图信息的。但是由于材质和贴图原路径地址以及文件大小的原因,我们没有将这些材质和贴图添加入游戏中。替代的方法是运用shader直接将模型的表面片元加上类似卡通渲染的风格,这样不但解决了材质的问题还不会使模型过于真实而感觉突兀。

具体实现时,我们创建了一个program变量,为其添加了一个顶点着色器,一个片元着色器,并在主体模型的节点的stateset中添加了该program,并指定了uniform变量。

随后,我们又添加了实时更换着色器颜色的功能。我们本想通过随时改变uniform变量来达到这点,但是发现对于uniform指针,我们无法直接改变指针的内容而不改变指针地址(无法直接声明变量必须使用new来创建),所以指定了新的uniform之后必须重新绑定新的uniform的指针。

(2)实现过程

//着色器的代码

static const char* vertSource = {//顶点着色器

"varying vec3 normal;\n"

"void main()\n"

"{\n"

" normal = normalize(gl_NormalMatrix * gl_Normal);\n"//指定法线

" gl_Position = ftransform();\n"

"}\n"

};

static const char* fragSource = {//片元着色器

"uniform vec4 color1;\n"

"uniform vec4 color2;\n"

"uniform vec4 color3;\n"

"uniform vec4 color4;\n"

"varying vec3 normal;\n"

"void main()\n"

"{\n"

//默认光源位置点成片元法线,计算夹角

" float intensity = dot(vec3(gl_LightSource[0].position),normal); \n"

" if (intensity > 0.95) gl_FragColor = color1;\n"//高亮区域赋值color1

" else if (intensity > 0.5) gl_FragColor = color2;\n"//中亮区域赋值color2

" else if (intensity > 0.25) gl_FragColor = color3;\n"//低亮区域赋值color3

" else gl_FragColor = color4;\n"//其余区域赋值color4

"}\n"

};

//着色器的绑定

osg::ref_ptr<osg::Shader> vertShader

= new osg::Shader(osg::Shader::VERTEX, vertSource);//获取GLSL代码

osg::ref_ptr<osg::Shader> fragShader

= new osg::Shader(osg::Shader::FRAGMENT, fragSource); //获取GLSL代码

osg::ref_ptr<osg::Program> program = new osg::Program;//创建stateset的设置参数

program->addShader(vertShader.get());//添加着色器

program->addShader(fragShader.get());//添加着色器

osg::StateSet * ss = body.getMatrix()->getOrCreateStateSet();//获取节点的stateset

ss->setAttributeAndModes(program.get());//绑定stateset的设置参数

ss->addUniform(new osg::Uniform//添加Uniform变量

("color1", osg::Vec4(color.r(), color.g(), color.b(), 1.0)));

ss->addUniform(new osg::Uniform//添加Uniform变量

("color2", osg::Vec4(color.r() / 2, color.g() / 2, color.b() / 2, 1.0f)));

ss->addUniform(new osg::Uniform//添加Uniform变量

("color3", osg::Vec4(color.r() / 4, color.g() / 4, color.b() / 4, 1.0f)));

ss->addUniform(new osg::Uniform//添加Uniform变量

("color4", osg::Vec4(color.r() / 8, color.g() / 8, color.b() / 8, 1.0f)));

(3)效果展示

时间: 2024-10-27 11:06:01

osg坦克大战游戏开发过程的相关文章

Java坦克大战游戏源代码

转载自: http://blog.csdn.net/java_cxrs/article/details/3860870 经过几天的练习和研究终于自己能写出坦克大战游戏了,写完这个程序后感觉收获了很多东西,对JAVA的知识又有了一定的增长,接下来还准备继续写几个小项目来练习J2SE 由于代码太长就不发在博客里了,我上传到了资源下载里,有需要的朋友大家可以去下载 下载地址:http://download.csdn.net/source/988654

【Java_项目篇&lt;1&gt;】--JAVA实现坦克大战游戏--赋予敌人行动和攻击(五)

前期相关文章 [Java_项目篇<1>]–JAVA实现坦克大战游戏–画出坦克(一) [Java_项目篇<1>]–JAVA实现坦克大战游戏–坦克移动+添加敌方坦克(二) [Java_项目篇<1>]–JAVA实现坦克大战游戏–坦克发射子弹(三) [Java_项目篇<1>]–JAVA实现坦克大战游戏–子弹连发+爆炸效果(四) 一.任务需求 赋予敌人行动和攻击. 二.思路 - 敌人行动 1.需要把EnemyTank做成线程类实现Runnable接口. run方法中,

基于HTML5坦克大战游戏简化版

之前我们有分享过不少经典的HTML5游戏,有些还是很有意思的,比如HTML5版切水果游戏和HTML5中国象棋游戏.今天要分享的是一款简化版的HTML5坦克大战游戏,方向键控制坦克的行进方向,空格键发射子弹,命中敌方坦克后也会发出声音,效果还算可以.效果图如下: 在线预览   源码下载 实现的代码. javascript代码: window.addEventListener("load", canvasApp, false); //是否支持canvas function canvasSu

HTML5移动开发之路(8)——坦克大战游戏2

本文为 兄弟连IT教育 机构官方 HTML5培训 教程,主要介绍:HTML5移动开发之路(8)--坦克大战游戏2 在上一篇文章中我们已经画出了自己的坦克,并且可以控制自己的坦克移动,我们继续接着上一篇来实现我们的坦克大战游戏吧. 一.将JS文件分离出来 使用OO的思想,我们已经对坦克进行了封装,对画坦克也进行了封装,下面我们将这两个对象提取到外部的js文件中,文件内容如下: [javascript] view plain copy print? //定义一个Hero类(后面还要改进) //x表示

HTML5移动开发之路(7)——坦克大战游戏1

本文为 兄弟连IT教育 机构官方 HTML5培训 教程,主要介绍:HTML5移动开发之路(7)--坦克大战游戏1 上一篇中我们介绍了关于Canvas的基础知识,用Canvas绘制各种图形和图片,在上一篇的基础上我们来做一个基于HTML5的坦克大战游戏,下面我们开始吧 一.用Canvas画出我们的坦克 我们设计的坦克结构如下图所示,如果有的朋友有更好的设计,希望分享和交流一下. 如上图所示,我们的坦克基本上是由三个矩形和一个圆形一个线段组成,每个部件的尺寸进行了标记,单位为px,下面我们用上一篇中

坦克大战游戏设计(C++)

设计:实现一款C++版的经典坦克大战游戏.   功能设计: 1)实现双人对战功能.获胜:但消灭对方所有坦克或者先炸毁对方总部. 2)战场由一个一个的格子构成,纵横为26*26格.坦克为2*2格.总部军旗2*2.子弹为2*1.石块与铁块:1*1 坦克与子弹每次移动一格. 3)坦克事件: 生成:在原始地点生成(即赋予原始坐标) 移动/不可移动:收到移动命令.判断移向方的2*1是否为空格,是则移动,否则放弃移动. 转向:收到左转或者右转命令时移动 被击中:当与子弹重合时,坦克被击毁,延时显示爆炸画面,

HTML5移动开发之路(9)——坦克大战游戏3

本文为 兄弟连IT教育 机构官方 HTML5培训 教程,主要介绍:HTML5移动开发之路(9)--坦克大战游戏3 上一篇我们创建了敌人的坦克和自己的坦克,接下来就应该让坦克发子弹了,我们下面来看一下如何让我们的坦克发出子弹. 前面我们用面向对象的思想对Tank进行了封装,又利用对象冒充实现了我们的坦克和敌人的坦克,仿照这种方式我们是不是也应该封装一个Bullet,答案是肯定的.好吧,那么我们再想想这个Bullet"类"都应该封装什么东西呢?位置应该有吧.子弹飞行的方向应该有吧.飞行的速

3D坦克大战游戏iOS源码

3D坦克大战游戏源码,该游戏是基于xcode 4.3,ios sdk 5.1开发.在xcode4.3.3上完美无报错.兼容ios4.3-ios6.0 ,一款ios平台上难得的3D坦克大战游戏源码,有20张不同的作战地图.通过左下角方向键和重力感应来控制坦克运行,点击右下角控制赶快开炮. 源码下载: http://code.662p.com/view/6309.html <ignore_js_op> <ignore_js_op> <ignore_js_op> <ig

坦克大战游戏1.0版

这是坦克大战游戏1.0版,以后会陆陆续续的完成... 准备素材(itank.jpg): 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>坦克游戏1.0版</title> </head> <body onkeydown="doSomething(event)"