如何自学游戏引擎开发培训的开发

自学游戏引擎的能去做游戏开发吗?_我要提问_牛客网
自学游戏引擎的能去做游戏开发吗?
非计算机学院,硕士,985排名靠后的学校,专业不好找工作,自学编程一年多,学了一个游戏引擎,但只能编写简单的小游戏,面试了几家公司,技术不牛逼,目前面试有只有两三家公司都挂了,有点担心自己的选择是不是对了的。
自学游戏引擎是想做游戏开发,应该很多喜欢玩游戏的同学应该都有自己的游戏开发梦吧。。。但是游戏开发是不是专业要求特别高,不知道像我这种没项目还专业不对口到底有没有戏呢?
目前状况是,我技术还不行,接下来该努力继续把基础知识打好,还是该考虑换换别的岗位了,突然有点迷茫了?
相信也有不少同学也是自学编程的,不知道你们怎么想的?只靠基础能不能找到工作?自学编程的同学怎么找项目做得?
扫描二维码,关注牛客网
下载牛客APP,随时随地刷题
浙ICP备号-2
扫一扫,把题目装进口袋Android游戏开发学习之引擎用法实例详解
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了Android游戏开发学习之引擎用法,较为详细的分析了Android游戏开发中所常用的JBox2D引擎功能及相关使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
本文实例讲述了Android游戏开发学习之引擎用法。分享给大家供大家参考。具体如下:
汽车引擎是汽车的心脏,其决定了汽车的性能和稳定性,是人们在购车时相当关注的。而游戏中的物理引擎就如汽车的引擎一样,占据了非常重要的位置。一款好的物理引擎可以非常真实地模拟现实世界,使得游戏更加逼真,提供更好的娱乐体验。
一、JBox2D简介
JBox2D是开源物理引擎Box2D的Java版本,可以直接用于Android。由于JBox2D的图形渲染使用的是Processing库,因此在Android平台上使用JBox2D时,图形渲染工作只能自行开发。该引擎能够根据开发人员设定的参数,如重力、密度、摩擦系数和弹性系数等,自动地进行2D刚体物理运动的全方位模拟。
1.小球弹跳进阶版
在第1节中小球的下落、碰撞、弹起都是用代码来维护的,下面使用物理引擎来实现,并且加入了刚体之间的碰撞。
(1)常量类Constant
package box2d.
public class Constant {
public static final float RATE=10; //屏幕与现实世界的比例
public static final boolean DRAW_THREAD_FLAG= //绘制线程工作标识位
public static final float TIME_STEP=2.0f/60.0f; //模拟的频率
public static final int ITERA=10; //迭代次数
public static int SCREEN_WIDTH; //屏幕宽度
public static int SCREEN_HEIGHT; //屏幕高度
(2)抽象类MyBody
该类为自定义的抽象类,是所有自定义刚体类的基类。由于JBox2D中的刚体类对象仅具有物理仿真计算的功能,并没有提供Android平台下的绘制功能,直接使用不是很方便。因此,这里定义了MyBody对自定义刚体的绘制及JBox2D物理仿真对象进行了封装。
package box2d.
import org.jbox2d.dynamics.B
import android.graphics.C
import android.graphics.P
public abstract class MyBody {
B //JBox2D物理引擎中的刚体
//刚体的颜色
public abstract void drawSelf(Canvas canvas,Paint paint); //绘制的方法
(3)圆形刚体类MyCircleColor
package box2d.
import org.jbox2d.dynamics.B
import android.graphics.C
import android.graphics.P
import android.graphics.Paint.S
import static box2d.bheap.Constant.*; //静态导入
public class MyCircleColor extends MyBody {
//圆形半径
public MyCircleColor(Body body,float radius,int color) {
this.body=
this.radius=
this.color=
public void drawSelf(Canvas canvas, Paint paint) {
paint.setColor(color&0xCFFFFFF); //设置颜色
float x=body.getPosition().x*RATE;
float y=body.getPosition().y*RATE;
canvas.drawCircle(x, y, radius, paint); //画圆
paint.setStyle(Style.STROKE); //设置空心无填充
paint.setStrokeWidth(1);
paint.setColor(color); //画边
canvas.drawCircle(x, y, radius, paint);
paint.reset(); //恢复画笔设置
(4)矩形刚体类MyRectColor
package box2d.
import static box2d.bheap.Constant.RATE;
import org.jbox2d.dynamics.B
import android.graphics.C
import android.graphics.M
import android.graphics.P
public class MyRectColor extends MyBody {
float halfW//半宽
float halfH//半高
public MyRectColor(Body body,float halfWidth,float halfHeight,int color)
this.body=
this.halfWidth=halfW
this.halfHeight=halfH
this.color=
public void drawSelf(Canvas canvas,Paint paint)
paint.setColor(color&0x8CFFFFFF);
float x=body.getPosition().x*RATE;
float y=body.getPosition().y*RATE;
float angle=body.getAngle();
canvas.save();
Matrix m1=new Matrix();
m1.setRotate((float)Math.toDegrees(angle),x, y);
canvas.setMatrix(m1);
canvas.drawRect(x-halfWidth, y-halfHeight, x+halfWidth, y+halfHeight, paint);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);//设置线条宽度
paint.setColor(color);
canvas.drawRect(x-halfWidth, y-halfHeight, x+halfWidth, y+halfHeight, paint);
paint.reset();
canvas.restore();
(5)生成刚体形状的工具类Box2DUtil
package box2d.
import static box2d.bheap.Constant.RATE;
import org.jbox2d.collision.CircleD
import org.jbox2d.collision.PolygonD
import org.jbox2d.dynamics.B
import org.jbox2d.dynamics.BodyD
import org.jbox2d.dynamics.W
public class Box2DUtil {
* 创建矩形物体(颜色)
public static MyRectColor createBox (
float halfWidth,
float halfHeight,
boolean isStatic, //是否为静止的
World world,
PolygonDef shape=new PolygonDef(); //创建多边形描述对象
if(isStatic) {
shape.density=0;
shape.density=1.0f;
shape.friction=0.0f; //设置摩擦系数
shape.restitution=0.6f; //设置能量损失率
shape.setAsBox(halfWidth/RATE, halfHeight/RATE);
BodyDef bodyDef=new BodyDef(); //创建刚体描述对象
bodyDef.position.set(x/RATE,y/RATE); //设置位置
Body bodyTemp=world.createBody(bodyDef); //在世界中创建刚体
bodyTemp.createShape(shape); //指定刚体形状
bodyTemp.setMassFromShapes(); //设置物体质量
return new MyRectColor(bodyTemp, halfWidth, halfHeight, color);
* 创建圆形物体(颜色)
public static MyCircleColor createCircle (
float radius,
World world,
CircleDef shape=new CircleDef(); //创建圆描述对象
shape.density=2; //设置密度
shape.friction=0.0f; //设置摩擦系数
shape.restitution=0.95f; //设置能量损失率
shape.radius=radius/RATE;//设置半径
BodyDef bodyDef=new BodyDef(); //创建刚体描述对象
bodyDef.position.set(x/RATE,y/RATE); //设置位置
Body bodyTemp=world.createBody(bodyDef); //在世界中创建刚体
bodyTemp.createShape(shape); //指定刚体形状
bodyTemp.setMassFromShapes(); //设置物体质量
return new MyCircleColor(bodyTemp, radius, color);
(6)颜色工具类ColorUtil
package box2d.
public class ColorUtil {
static int[][] result=
{56,225,254},
{41,246,239},
{34,244,197},
{44,241,161},
{65,239,106},
{45,238,59},
{73,244,51},
{99,233,58},
{129,243,34},
{142,245,44},
{187,243,32},
{232,250,28},
{242,230,46},
{248,196,51},
{244,125,31},
{247,88,46},
{249,70,40},
{249,70,40},
{248,48,48},
{250,30,30},
{252,15,15},
{255,0,0},
public static int getColor(int index)
int[] rgb=result[index%result.length];
int result=0xff000000;
result=result|(rgb[0]&&16);
result=result|(rgb[1]&&8);
result=result|(rgb[2]);
(7)主控制类MyBox2dActivity
package box2d.
import java.util.ArrayL
import java.util.R
import org.jbox2d.collision.AABB;
import mon.Vec2;
import org.jbox2d.dynamics.W
import android.app.A
import android.content.pm.ActivityI
import android.os.B
import android.util.DisplayM
import android.view.W
import android.view.WindowM
import static box2d.bheap.Constant.*;
public class MyBox2dActivity extends Activity
AABB worldAABB;//创建 一个管理碰撞的世界
Random random=new Random();
//物体列表
ArrayList&MyBody& bl=new ArrayList&MyBody&();
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
//设置为全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN ,
WindowManager.LayoutParams. FLAG_FULLSCREEN);
//设置为横屏模式
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//获取屏幕尺寸
DisplayMetrics dm=new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
if(dm.widthPixels&dm.heightPixels)
SCREEN_WIDTH=dm.widthP
SCREEN_HEIGHT=dm.heightP
SCREEN_WIDTH=dm.heightP
SCREEN_HEIGHT=dm.widthP
worldAABB = new AABB();
//上下界,以屏幕的左上方为 原点,如果创建的刚体到达屏幕的边缘的话,会停止模拟
worldAABB.lowerBound.set(-100.0f,-100.0f);
worldAABB.upperBound.set(100.0f, 100.0f);//注意这里使用的是现实世界的单位
Vec2 gravity = new Vec2(0.0f,10.0f);
boolean doSleep =
//创建世界
world = new World(worldAABB, gravity, doSleep);
final int kd=40;//宽度或高度
MyRectColor mrc=Box2DUtil.createBox(kd/4, SCREEN_HEIGHT/2, kd/4, SCREEN_HEIGHT/2, true,world,0xFFe6e4FF);
bl.add(mrc);
mrc=Box2DUtil.createBox(SCREEN_WIDTH-kd/4, SCREEN_HEIGHT/2, kd/4, SCREEN_HEIGHT/2, true,world,0xFFe6e4FF);
bl.add(mrc);
mrc=Box2DUtil.createBox(SCREEN_WIDTH/2, kd/4, SCREEN_WIDTH/2, kd/4, true,world,0xFFe6e4FF);
bl.add(mrc);
mrc=Box2DUtil.createBox(SCREEN_WIDTH/2, SCREEN_HEIGHT-kd/4, SCREEN_WIDTH/2, kd/4, true,world,0xFFe6e4FF);
bl.add(mrc);
//创建砖块
//砖块间距 行间距为20
模块宽度为10 最多一行为9块
final int bs=20;
final int bw=(int)((SCREEN_WIDTH-2*kd-11*bs)/18);
//============================================================
for(int i=2;i&10;i++)
if((i%2)==0)
//左侧蓝木块
for(int j=0;j&9-i;j++)
mrc=Box2DUtil.createBox
kd/2+bs+bw/2+i*(kd+5)/2+j*(kd+5)+3,
SCREEN_HEIGHT+bw-i*(bw+kd)/2,
ColorUtil.getColor(Math.abs(random.nextInt()))
bl.add(mrc);
//右侧蓝木块
for(int j=0;j&9-i;j++)
mrc=Box2DUtil.createBox
3*kd/2+bs-bw/2+i*(kd+5)/2+j*(kd+5)-3,
SCREEN_HEIGHT+bw-i*(bw+kd)/2,
ColorUtil.getColor(Math.abs(random.nextInt()))
bl.add(mrc);
if((i%2)!=0)
for(int j=0;j&10-i;j++)
mrc=Box2DUtil.createBox
kd/2+bs+kd/2+(i-1)*(kd+5)/2+j*(kd+5),
SCREEN_HEIGHT-(kd-bw)/2-(i-1)*(bw+kd)/2,
ColorUtil.getColor(Math.abs(random.nextInt()))
bl.add(mrc);
mrc=Box2DUtil.createBox
5*kd+bs+20,
SCREEN_HEIGHT-(kd+bw)*4-kd,
ColorUtil.getColor(Math.abs(random.nextInt()))
bl.add(mrc);
MyCircleColor ball=Box2DUtil.createCircle(SCREEN_WIDTH/2-24, kd, kd/2, world,ColorUtil.getColor(Math.abs(random.nextInt())));
bl.add(ball);
ball.body.setLinearVelocity(new Vec2(0,50));
GameView gv= new GameView(this);
setContentView(gv);
(8)显示界面类GameView
package box2d.
import android.graphics.C
import android.graphics.P
import android.view.SurfaceH
import android.view.SurfaceHolder.C
import android.view.SurfaceV
public class GameView extends SurfaceView implements Callback{
public GameView(MyBox2dActivity activity) {
super(activity);
this.activity=
this.getHolder().addCallback(this);
paint =new Paint();
paint.setAntiAlias(true);
dt=new DrawThread(this);
dt.start();
public void onDraw(Canvas canvas) {
if(canvas==null) {
canvas.drawARGB(255, 255, 255, 255); //设置背景颜色白色
for (MyBody mb : activity.bl) {
mb.drawSelf(canvas, paint);
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
public void surfaceCreated(SurfaceHolder holder) {
repaint();
public void surfaceDestroyed(SurfaceHolder holder) {
public void repaint() {
SurfaceHolder holder=this.getHolder();
Canvas canvas=holder.lockCanvas();
synchronized(holder){
onDraw(canvas);
} catch(Exception e){
e.printStackTrace();
} finally {
if(canvas!=null) {
holder.unlockCanvasAndPost(canvas);
(9)绘制线程类DrawThread
package box2d.
import static box2d.bheap.Constant.*;
//绘制线程
public class DrawThread extends Thread
public DrawThread(GameView gv)
public void run()
while(DRAW_THREAD_FLAG)
gv.activity.world.step(TIME_STEP, ITERA);//开始模拟
gv.repaint();
Thread.sleep(20);
} catch (InterruptedException e)
e.printStackTrace();
希望本文所述对大家的jsp程序设计有所帮助。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具&&&>自学游戏程序的步骤有哪些?
游戏程序编程专题
自学游戏程序的步骤有哪些?
对于一个员来说,通常需要经过以下几个学习阶段:
1、基础语言阶段:常用的游戏编程语言有,C,C++,JAVA,其中最广泛被使用的就是C++,C++语言是一切游戏程序的基础,换而言之,一个优秀的游戏程序员,必须非常熟练掌握并应用C++.
2、数据结构:在掌握C++之后,需要进行数据结构的学习,形象的讲,就是那么一大堆数据,如何去有序的排列其结构。通过对数据结构的学习,便能够开始阅读他人编写的代码,尝试 开发简单的程序,可以实现一些编程者自己的构想,是每个游戏程序员必须掌握的技巧。
3、“库”的学习及应用:最常用的有MFC(微软公司类库),GUI(图形界面库),当然“库”的知识及功能非常庞大,通常来说,熟练掌握一部分经常涉及的知识即可。通过以上三点的学习,c++ 基础语言,数据结构,库的使用。你基本已经可以自己独立制作一个小游戏了,比如贪吃蛇,俄罗斯方块等等。然后,将进入的高阶阶段,也将是直接涉及到游戏程序相关知识。
4、游戏程序对于硬件的操纵。比如,内存,CPU,显卡,这将会涉及到一些数学知识,比如立体几何,线性代数等,但是也不用惊慌,并不是非常困难。通过对硬件的熟练操纵,才能将游戏的画面,流畅度等等充分给予表达。
5、对于相关知识的学习。包括物理,脚本,美术等等。不需要深入了解,但相关知识的丰富对于你将来做游戏程序会有直接帮助。
6、对于游戏引擎的熟悉及使用。游戏引擎一般包括渲染器,内存管理器,组织管理几部分。对游戏的画面做出渲染,高效使用内存以及如何控制动画播放等功能。熟悉引擎的使用,也将对于游戏程序员有直观的帮助。课程名称读取中
支付宝支付
由百度开放云提供技术支持
&学院APP&&
扫描微信二维码精彩活动、课程更新抢先知
下载客户端,离线视频任您学
1.&基本概念介绍
2.&GLES2.0 开发环境建立
3.&gles2的开发流程
4.&基本图元绘制介绍(1)
5.&文理的使用(1)
6.&文理使用(2)
7.&坐标变换
8.&第一人称摄像机
9.&第三人称摄像机(1)
10.&第三人称摄像机(2)
11.&直接使用显存
12.&离屏幕渲染(画中画)
13.&混合技术,实现图片的透明
14.&模型基础(1)
15.&模型基础(2)
16.&动画-文理动画
17.&动画-关键帧动画
18.&动画-骨骼动画(1)
19.&动画-骨骼动画(2)
20.&动画-骨骼动画(3)
21.&场景管理-不可见剔除
22.&场景管理-节点管理(1)
23.&场景管理-节点管理(2)
24.&地形技术-地形绘制
25.&地形技术-多文理贴图
26.&地形技术-地形编辑(地形刷绘)
27.&地形技术-分页
28.&布告板技术(1)
29.&布告板动画技术(2)
加入购物车
【课程类型】技术教程
【难度级别】高级
【适合人群】初级研发工程师
【课程介绍】 介绍移动设备,介绍一款游戏引擎具备的要素,并依次进行开发实现,采用标准C++语言进行开发,需要有扎实的C++基础
【课程目标】 深入的了解游戏引擎的运行机制,掌握游戏引擎的开发过程,通过实践可以开发出简化的游戏引擎
【课程计划】 主要包含以下系统,渲染系统(gles2.0规范),GUI系统,粒子系统,特效系统,物理系统,AI系统,骨骼动画,关键帧动画,网络系统,音效系统,脚本系统,场景管理
更新了最新代码在:15模型基础(2)
介绍课程的目标,针对每一个课时都会编写一个示例程序,展示功能以及原理,辅助大家理解,同时将源代码以及所有的示例使用到的资源都共享给大家。
最新的课件代码-在第11课上(前20课的代码示例)包含可执行程序。
最新代码在地形技术-分页 [7954]上下载。
在Windows上建立OpenGL ES2.0的开发环境,如何组织工程,代码,建立基本的开发框架
介绍OpenGL ES2.0的开发流程,介绍可编程管线,开发步骤,编写基本的Shader片段,建立开发框架。
基于可编程管线技术,编写基本的shader显卡程序,绘制基本图元,完成基本的绘制功能,巩固可编程管线概念,以及渲染流程。
介绍文理的概念,使用FreeImage库加载文件数据,调用OpenGL Api完成文理的建立,编写显卡Shader完成文理的显示。
进一步巩固文理的概念,以及使用纹理。
介绍3D图形的基本原理,介绍矩阵变换,介绍投影矩阵,观察矩阵,模型矩阵,如何使用他们。
介绍三维场景中第一人称摄像机的原理,以及实现,实现一个基本的第一人称摄像机类
介绍第三人称摄像机的基本原理
在上一课的基础上,深入的理解第三人称摄像机的原理,并实现一个基本的第三人称摄像机类,并建立例程使用。
介绍如何直接使用显卡内存来加速程序,介绍顶点缓冲区的使用,介绍索引缓冲区的使用,实现基本的例程,介绍API的使用
介绍Frame buffer object 的用法,实现画中画效果,示例代码已经上传,共大家阅读,调试使用。
介绍模型的概念,介绍如何使用模型,游戏制作中采用的三维模型工具,并导出模型,在程序中使用。
介绍如何编写3DMax导出自定义格式的三维网络数据,并实现一个简单的导出插件
介绍关键帧动画的原理,介绍如何从3DMax建模工具中导出关键帧动画,并编写插件实现简单的导出动画功能,并编写例程实现关键帧动画。
介绍节本的骨骼动画原理,并实现一个简单的骨骼动画过程
介绍如何从3DMax中导出骨骼动画,编写相应的导出插件。
实现一个基本的骨骼动画系统,实现从3DMax中导出骨骼动画,并在程序中实现动画的播放。
介绍视锥提出算法,不可见物体的剔除,优化场景,提高绘制性能。
介绍场景管理的基本知识,以节点为中心,介绍其原理,重要性,并初步建立场景管理原型框架。
介绍包围盒子,包围球等技术在节点管理中的重要性,实现包围盒类,包围球类,节点类,初步完成节点设计。
介绍地形生成的基本原理,介绍如何刷绘地形,实现基本的地形编辑功能,例如草地刷绘,岩石,花卉,砂石等文理的刷绘功能,介绍刷绘的基本原理。
介绍地形生成的基本原理,介绍如何刷绘地形,实现基本的地形编辑功能,例如草地刷绘,岩石,花卉,砂石等文理的刷绘功能,介绍刷绘的基本原理。
介绍如何编辑地形,例如使用鼠标点下,拔出一座山,或者挖出一条河流,实现基本的地形编辑。
讲解大规模地形技术的分页技术。
实现一个一直面向摄像机的布告板效果类。
实现一个风吹草动的效果,摇摆的草地。
全部评价(5)
课程讲解清晰,难度适宜,老师有回复交流,推荐!
中间视频有模糊没声音的啊。。。CAO
好评,我买了全套学,虽然没有方方面面都涉及到。但是对于看完确实很大助益
老师,你这视频是讲全部的系统,还是就只有动画,和地图,
老师你的第9 10 摄像机的 视频 中间有听不见的声音的地方,你听听
10课程101856学员
参考知识库
为您推荐课程
讲师:张立铜 29课时
讲师:张立铜 85课时
讲师:张立铜 3课时
讲师:张立铜 15课时游戏引擎中的通用编程技术(一)
游戏引擎中的通用编程技术(一)
  你是否正在考虑构建一个游戏引擎呢?你对如何构建一个游戏引擎是否已经有了一个明确的计划呢?你是否已经对如何组织游戏引擎各个模块之间的关系有了一个通盘的考虑?如果没有,那么本文将对你建立一个良好的游戏架构提出一些有益的方案,如果你已经对上面的问题有了一个明确的答案,那么本文不是你需要阅读的内容。本文的目的是给那些没有任何建立完整游戏引擎经验的人提供一些入门性的知识,使他们初步了解一下如何来构建一个游戏引擎,构建游戏引擎应该注意哪些方面的问题,并提供了一些成熟的设计模版并指出这些设计模版使用的范围,我希望这些内容对那些中级编程人员也有一个良好的参考作用。本文的内容来源于一些流行的编程书籍,具体书目请见本文最后的部分,由于本文是介绍性质的文章,因此如果你对哪方面的内容非常感兴趣请参考相应的书籍,本文或许有很多错误的地方,如果你有什么看法的话可以通过Email和我进行讨论,我的地址为dreams_。   这里必须再次提醒你,本文介绍的是一些通用的游戏编程技巧,虽然是通用但是可能并不是非常全面,可能存在这样或那样的缺陷,因此如果你希望它发挥最大的效用必须恰当的使用它,而不是不分场合的滥用。切记切记,一个初学者最容易犯的错误就是任意使用一些设计模版而不顾它的使用范围。在开始构建一个游戏引擎时你需要先考虑哪些方面的问题呢?这是你必须认真考虑的问题,我的答案是首先必须考虑代码的可读性,尤其是在多人进行开发时更必须高度重视,如果你写的代码其他人需要花费非常大的精力进行阅读,那么根本谈不上提高工作效率,下面是提高代码可读性的一些良好建议:1、建立一份简单明了的命名规则。一份良好的命名规则可以大幅提高代码的可读性,规则必须简单明了,通常只需要两三分钟的阅读应该可以让其他人掌握,例如在代码中直接使用匈牙利命名法这种大家熟知的规则,使用字母I作为接口类的首字母,使用C开头作为实现类的首字母,使用g_开头的变量名作为全局变量,s_开头作为静态变量名,m_开头作为内部变量名,使用_开头作为类内部使用的函数名等等,通过名字就可以使你大概了解对象的使用范围和基本功能。2、不要讨厌写注释。一个编程者易犯的错误就是不写注释,认为它会增加自己的工作量,但是他没有考虑到相应的工作量已经转移到代码阅读者的身上,可能看代码的人会花费比写注释时间两倍或者三倍的时间来阅读代码,这是一种非常不负责任的行为,通过一段简短的注释可以使阅读者迅速的了解代码的功能,,从而把时间更多的用到功能的扩展上。下面是一些良好的建议:尽量对每一个变量标明它的功能。对每一个函数声明的地方标明它的功能,对于复杂的函数还应当写清参数和返回值的作用,注意是在声明函数的头文件中。在关键的代码处写清它的作用,尤其是在进行复杂的运算时更应如此。在每一个类声明的地方简要的介绍它的功能。3、减少类的继承层次。通常对于游戏编程来说每一个类的继承层次最好不要超过4层,因为过多的继承不仅会减少代码的可读性,同时使类表指针变长,代码体积增大,减低类的执行效率。还要注意要减少多重继承,因为不小心它会形成编程者非常讨厌的“钻石”形状。同时还要注意如果能使用类的组合的话那么就尽量减少使用类的继承,当然这是设计技巧的问题。4、减少每行代码的长度。尽量不要在一行代码中完成一个复杂的运算,这样做会增加阅读难度,同时不符合现代CPU的执行,由于CPU现在都使用了超长流水线的设计,它非常适合执行那些每行代码非常短而行数非常多的代码,例如对一个复杂的数学运算,写成一行不如每一步骤写一行。   以上建议是我的一些粗略看法,如果你还有什么好的看法可以给我指出来,同时上面的建议并不是绝对的,例如类的继承并不是绝对不能超过4层,如果你需要的话可以使用更多的继承,前提是这样带来的好处大于代码执行效率的损失。   接着看看要考虑什么,在Game Programming Gems3的《一个基于对象组合的游戏架构》一文指出了几个值得考虑的问题,首先是平台相关性与独立性和游戏相关性与独立性的问题,也就是说应当作到引擎的架构与平台和游戏都无关。为什么要做到与平台无关性呢?这是因为你必须在开始架构引擎考虑它的可移植性,如果在开始你没有注意到这个问题,那么一旦在游戏完成后需要移植到其他的游戏平台上,你会发现麻烦大了,你需要修改的地方实在是太多了,所有与平台相关的API调用都需要修改,所有使用了平台特定功能的模块也需要修改,这是一个非常耗费精力的事情,可能需要花费和开发一个游戏一样的时间,而如果你在开始的时候就考虑到这个问题,那么非常简单,只需要写一个相应平台的模块替换掉原来的模块即可,这样精力就可以放在如何充分的利用特定平台的能力来提高游戏的表现力上,而不是代码修改上。下面简单的谈一下如何使引擎作到与平台无关。1、注意操作系统的差异。现在主流的操作系统主要是Windows和Linux两种,当然还有Unix和Mac,在编程时你必须注意这一点,当你需要包含Windows的头文件时,你必须将它包含在宏_WIN32中,下面是一个简单的例子:#ifdef _WIN32#include "windows.h"#endif而你使用Windows平台特定的API时也应当如此,这样在其他平台上编译时可以保证Windows平台相应的代码不会被编译进去。对于其他平台也应当如此。2、注意编译器的差异。现在通用的编译器主要有VC,BC和gcc几种,在进行Windows平台编程时,你通常会使用VC或BC,而对Linux平台编程时通常使用gcc,使用VC编译器你不可能编译出用于Linux平台的代码,因此在编程时也需要注意,你可以使用上面的方法通过特定的宏来将不同的编译器分离开。举一个简单的例子:#ifdef _WIN32#ifdef _MSC_VERtypedef signed __int64 int64;#endif#elif defined _LINUXtypedef long long int64;#endif在不同的编译器中对64位变量的命名是不同的,因为它并不是C++标准的一部分,而是编译器的扩展部分。另外一个例子是编译器使用的内联汇编代码,在VC中你可以使用_asm来指明,而对于Linux平台的编译器你需要使用它的专用关键字了。3、注意CPU的差异。对于不同平台来说它通常会使用不同的CPU,不过幸好Windows和Linux都支持X86的CPU,这也是PC游戏的主流CPU平台,而XBOX使用的也是X86的CPU,除非你需要移植到PS2平台,否则这将大大减轻你的编程负担,在X86平台上提供了一个cpuid的指令可以非常方便的检查CPU的特性,如是否支持MMX,SSE,SSE2,3DNow!技术等,通过它你可以使用特定的CPU特性来加速你的代码执行速度。4、注意图形API的差异。现在图形API主要存在两种主流的平台DirectX和OpenGL,DirectX只能用于Windows平台,而OpenGL几乎被所有的平台所支持。因此你需要为不同的图形API进行封装,将它做成不同的模块,在需要的时候进行切换。完成这个工作最好的方法是使用后面介绍的类厂模式。5、注意显卡的差异。现在显卡有两大主流ATI和NV,虽然显卡可以被主流的操作系统所支持,但是必须注意在不同的游戏平台上还是使用不同的GPU,而在GPU之间也相应有自己的功能扩展,因此在使用特定的扩展功能时必须检查一下是否被显卡所支持。6、注意shader语言的差异。可编程图形语言的出现是最重要的一项发明,现在几乎每一个游戏都在使用这项技术,而正由于它的重要性现在出现了多个标准,HLSL只能用于DX中,而OpenGL由于标准的开放性更加混乱,每一个显卡厂商都根据自己的产品推出相应的扩展指令来实现shader,而NV更推出了GC可以同时适用于DirectX和OpenGL,这是一个非常好的想法,不过由于这不是一个开放的标准因此没有得到其他厂商的支持,在ATI显卡上运行GC代码你会发现比在NV显卡慢了几个数量级,由于上面的情况你需要根据不同的平台相应进行封装,方法和第4条一样。下面的建议值得你去考虑,当你使用DirectX平台时应当使用HLSL,而对于OpenGL可以封装为两个模块,根据显卡的不同进行切换,也可以使用GC特别为NV的显卡封装一个模块来对它进行优化。   这里需要补充一点,如果可以的话尽量和OGRE一样为不同的操作系统进行封装,这样方便在不同的系统之间进行切换。   接着看看如何实现游戏无关性,通常游戏引擎如果要实现游戏的无关性是非常困难的,这也就是说要求你的引擎适合所有的游戏类型,这太难了,考虑一下一个RPG游戏引擎如果用来做一个RTS游戏那简直是不可能,类似的你不可能拿Q3引擎来做RTS游戏,但是如果引擎设计的非常良好的话还是可以实现部分的游戏无关性。也就是说你可以将引擎的一部分模块设计成通用的模块,这样在开发其他类型的游戏时可以重用这部分的代码,这部分代码包括底层显示,声音,网络,输入等部分,在设计它们时你必须保证它们具有良好的通用性。   在这些问题之后你应当考虑程序的国际化问题。这也是非常重要的方面,因为你的游戏可能在其它国家发行,这主要是注意语言方面的问题,尤其是字符串的处理,在C++的标准库中提供了一个String容器,它提供了对国际化的良好支持,因此在引擎中你需要从头到尾的使用它。   接下来我们看看本文最重要的内容,如何组织一个引擎的架构。这是引擎最重要的部分,为什么重要呢?如果我们把引擎看作一间房子的话,那么架构可以看作是房子的框架,当你完成这个框架后就可以向框架内添砖加瓦盖房子了。下面让我们来看看如何构建这个框架,通常一个大型的软件工程是按照模块化的方式来构建的,编程之前要进行必要的需求分析,将软件工程根据不同的功能划分为几个较大的功能模块,对比较复杂的模块你可能还需要将它分为几个子模块,并需要给出各个模块之间的逻辑关系。当你编写一个引擎时也需要进行相应的功能分析,让我们看看如何来划分引擎的功能模块,如果按照上面的游戏无关性和相关性进行分析的话我们可以发现它可以分为游戏相关层和无关层两层,游戏相关层由于包含了游戏的逻辑性代码也被称为逻辑层。逻辑层应该位于引擎的最顶层,如果你在开发一个局域网或在线游戏的话,按照网络程序的C/S开发模式,这一层应该分为两个模块,服务器和客户端模块,它包含了和特定游戏相关的所有功能,如AI,游戏角色,游戏事件管理,网络管理等等。在它下面就是游戏无关层了,包括了引擎核心模块,GUI模块,文件系统管理模块等等,其中引擎的核心模块是最重要的部分,逻辑层主要通过它来和底层的模块打交道,它应该包含场景管理,特效管理,控制台管理,图形处理等等内容。在向下就是一些底层模块了,如图形渲染模块,输入设备模块,声音模块,网络模块,物理模块,角色模型模块等等,所有的这些底层模块必须通过核心模块来和逻辑层进行交互,因此核心模块是整个引擎的枢纽,所有的模块都通过它来进行交互。下面看看应该如何来进行模块的设计,这里有一些通用的规则是你应当遵守的:1、减少模块之间的关系复杂度。我们知道通常每一个模块内部都存在大量的对象需要在各个模块之间进行相互的调用,如果我们假设每一个模块内部对象的数量为N的话,那么每两个模块之间的关系复杂度为N*N,这样的复杂度是不可接受的,为什么呢?首先是它非常不利于管理,由于各个模块都存在大量的全局对象,并存在相互依存的关系,并且各自建立的时间各不相同,这就存在初始化顺序的矛盾,考虑这种情况,一个模块中存在一个对象需要另外一个模块中的对象才能进行初始化,当这个对象进行初始化时而另外的对象在之前并没有初始化就会引发程序的崩溃。其次,不利于多人进行同时的开发,由于各个模块存在相互依存的关系,当复杂度非常高时就会出现模块与模块的高度依存,也就是说一个模块没有完成下一个模块就无法完成,因此就需要一个模块一个模块按照它的依存关系进行编程,而无法同步进行。因此在设计模块时的第一件事情是减少模块之间的复杂度,为此你在设计模块时必须为模块设计一个交互接口,并约定所有模块之间的交互必须通过这个接口来进行,这样模块之间的关系复杂度就降低为1*1了,非常方便管理,同时这非常利于多人之间进行开发,假如每个人负责一个模块的开发的话,那么你只需要先完成这个接口类,其他人就可以利用这个接口进行其他模块的开发,而不必等到你完成所有的类再进行,这样所有的模块都是同步进行,可以节省大量宝贵的开发时间。2、对类的抽象接口而不是类的实现编程。这是《Design Patten》一书作者对所有软件编程者的建议,它也对游戏编程有很大的指导意义。对模块中所有被其它模块使用的类都要建立一个抽象接口,其它模块要使用这个抽象接口进行编程,这样其它模块就可以在不需要知道类是如何实现的情况下进行编程。这样做的好处是在接口不改变的情况下任意对类的实现进行改变而不必通知其它人,这对多人开发非常有用。3、根据调用对象的不同对类进行分层。实际上本条还是对第2条的补充,分层还是为了更好隐藏底层的实现。通常一个类不仅被其它模块使用还要被自身模块所调用,而且它们需要的功能也不同,因此我们可以让一个类对外部显现一个接口而对内部也显现一个接口,这样做的好处和上面一样,因为一个复杂的模块也是多人在进行编程的。4、通过让一个类对外显现多个接口来减少类的数量。减少关系复杂度的一个方法是减少类的数量,因此我们可以把完成不同功能的类合并成一个类,并让它对外表现为多个接口,也就是一个类的实现可以继承多个接口。上面的建议只是起到参考作用,具体实现时你应该根据情况灵活使用,而不是任意乱用。   下面的内容涉及到具体的编程技巧,对于引擎中的全局对象你可以使用Singleton,如果你不了解它是什么可以阅读《Design Patten》,里面有对它的详细介绍,具体的使用可以通过OGRE引擎获得。   调用模块内的对象可以通过类厂来实现。COM可以看作是一种典型的类厂,DX就是使用它来进行设计的,而著名的开源引擎Crystle Space也是通过建立一个类似的COM物体来实现的,但是我并不对它很认可,首先构建一个类似COM的类厂非常复杂,开销有点大,其次COM的一个优点是可以对程序实现向下兼容,这也是DX使用它的重要原因,而一个游戏引擎并不需要。OGRE中也实现了一个类厂结构,这是一个比较通用的类厂,但是使用起来还是需要写一段代码。我比较欣赏VALVE的做法,它通过使用一个宏就解决了这个问题,非常高效,使用起来也非常方便。这个做法很简单,它把每个模块中需要对外暴露的接口都连接到一个内部维护的链表上,每一个接口都和一个接口名相连,这样外部模块可以通过传入一个接口名给CreateInterface函数就可以获得这个接口的指针了,非常简单。下面看看它的具体实现。它内部保存的链表结构如下:class InterfaceReg{public:InterfaceReg( InstantiateInterfaceFn fn , const char *pName );public:InstantiateInterfaceFn m_CreateFn;const char *m_pNInterfaceReg *m_pNstatic InterfaceReg *s_pInterfaceR};并定义了两个函数指针和一个函数#define CREATEINTERFACE_PROCNAME "CreateInterface"typedef void *(CreateInterfaceFn)( const char *pName , int *pReturnCode );typedef void *(InstantiateInterfaceFn)( void );DLL_EXPORT void *CreateInterface( const char *pName , int *pReturnCode );下面看看它如何通过宏来建立链表#define EXPOSE_INTERFACE( className , interfaceName , versionName ) \static void *__Create##className##_Interface() { return (interfaceName*) new classN } \static InterfaceReg __g_Create##interfaceName##_Reg( __Create_##className##_Interface , versionName );如果你有一个类CPlayer它想对外暴露接口IPlayer,那么很简单,可以这么做#define PLAYER_VERSION_NAME "IPlayer001"EXPOSE_INTERFACE( CPlayer , IPlayer , PALYER_VERSION_NAME );如果在其他模块内你需要获得这个接口,可以这么做CreateInterfaceFn factory = reinterpret_cast&CreateInterfaceFn& (GetProcAddress( hDLL , CREATEINTERFACE_PROCNAME ));IPlayer player = factory( PLAYER_VERSION_NAME , 0 );其中hDLL为模块的句柄。这里函数指针factory实际指向模块内部的CreateInterface函数,这个函数通过比较传入的接口名从链表找到指定类指针。   解决了类厂问题,下面让我们看看如何建立模块对外的接口,在Game Programming Gems3的《一个基于对象组合的游戏架构》一文提出了一种架构,Half Life2引擎中对这种架构进行了有效的扩展,你可以让所有的对外暴露的接口都使用这个架构,前提是模块只有一个接口对外暴露。class IAppSystem{public:// Here's where the app systems get to learn about each other virtual bool Connect( CreateInterfaceFn factory ) = 0;virtual void Disconnect() = 0;// Here's where systems can access other interfaces implemented by this object// Returns NULL if it doesn't implement the requested interfacevirtual void *QueryInterface( const char *pInterfaceName ) = 0;// Init, shutdownvirtual InitReturnVal_t Init() = 0;virtual void Shutdown() = 0;};   通过Connect方法你可以将两个模块建立一个连接关系,通过QueryInterface方法你可以检索到其他需要暴露接口,这种方法很好的为所有的模块建立一个标准的对外接口,极大的减轻了编程的复杂性,遗憾的是在HL2引擎中只有部分模块使用了这个方法,可能是这个接口引入时间太晚的缘故。(待续)
(责任编辑:admin)
------分隔线----------------------------
游戏中的“学习”...
微型嵌入式GUI应用开发-Step2...
深入A*算法...
测试驱动开发与极限编程中人的关系...
测试驱动开发与极限编程中设计的关系...
测试驱动开发与极限编程核心价值的关系...

我要回帖

更多关于 游戏物理引擎开发 pdf 的文章

 

随机推荐