之前谈到模拟百战天虫的游戏类型,团队开始准备设计“百战天神”(Gods),需要一个统一的设计接口。
由于最核心的部分是2D Physics Engine ,所以先给出了一个公共实现框架性的代码(整个游戏代码的基础类库准备用vczh的vlpp库,vlpp_def是自己设定的统一vczh库调用头文件):
1#ifndef _GODSPHY2D_
2#define _GODSPHY2D_
3
4#include "vlpp_def.h"
5
6
7namespace GType
8{
9
10 typedef bool GBool;
11 typedef double GUReal;
12 typedef unsigned long GULong;
13 typedef float GReal;
14 typedef int GInt;
15 typedef unsigned int GUInt;
16 typedef __int64 GInt64;
17 typedef char GChar;
18 static const GUInt MAX_BODIES=2000;
19};
20
21
22using namespace GType;
23class GVector2d
24{
25 /**//* 待实现 */
26};
27
28class GShape
29{
30public:
31 virtual GUInt IsZero()=0;//是否无形状
32 virtual GUInt GetCG()=0;//几何中心
33 virtual GUInt GetArea()=0;
34 virtual GUInt GetGeoCentre()=0;
35 virtual GUInt Rotate(GUInt _rotAngle)=0;
36 virtual GUInt Scale(GUReal factor)=0;
37 virtual GUInt Minus(const GShape* _shape)=0;
38 virtual GUInt Add(const GShape* _shape)=0;
39
40};
41class GBox:public GShape
42{
43
44};
45class GCircle:public GShape
46{
47
48};
49class GPoly:public GShape
50{
51
52};
53
54
55
56
57class GBody
58{
59 GUInt id;
60
61 Vector<GShape*> shapes;
62 GReal fric;//Friction
63 GReal dens;//Density
64 GReal elas;//Elasticity
65 GReal area;//Area of bodies
66 GReal rigi;//Rigidity
67
68
69 GReal mag;//Magnetism
70 GReal elec;//Quantity of electric charge
71
72
73 GVector2d cg;//Center of gravity
74 GVector2d tv;//Translational velocity
75 GVector2d rv;//Rotational velocity
76 GVector2d ta;//Translational acceleration
77 GVector2d ra;//Rotational acceleration
78 GReal x;
79 GReal y;
80
81 GUInt IsDeformable;//(0,1,2)=(not,in scale,in minus)
82 GBool IsVisible;
83
84
85public:
86 GBody(const GBody& _copy);
87 GBody(const GShape* _shapes,int _n,GReal _friction,GReal _density,GReal _elasticity);
88 GReal AddShape(const GShape& _shape);
89 GReal GetArea(){return area;};
90 GReal GetDens(){return dens;};
91 GReal GetElas(){return elas;};
92 GReal GetMass(){return area*dens;};
93 GReal GetFric(){return fric;};
94 GVector2d GetCG(){return cg;}
95 GVector2d GetTV(){return tv;}
96 GVector2d GetRV(){return rv;}
97 GVector2d GetTA(){return ta;}
98 GVector2d GetRA(){return ra;}
99
100 virtual GUInt Consider();
101
102 virtual ~GBody();
103};
104
105class GForce/**//* 主要封装作用力向量的计算方法, */
106{
107public:
108};
109
110class GElasForce:public GForce
111{
112
113
114};
115
116class GElecForce:public GForce
117{
118};
119class GMagForce:public GForce
120{
121};
122class GFricForce:public GForce
123{
124
125};
126
127
128
129class GWorld
130{
131 GULong time;//世界已运行时间
132 GUInt timeModule;//最小body状态变更周期
133 GULong doomsday;//末日期
134
135
136 GVector2d gField;//等效重力场(单一方向),gField=0时无重力场
137 Array<GBody*> bodies;//世界内所有body
138public:
139 class GDominator:public GBody
140 {
141 /**//* 世界指针,控制范围参数等等 */
142 friend class GBody;
143 GWorld* world;
144 Array<Array<GVector2d*>> pairForces;
145
146 public:
147 class GCollision
148 {
149 /**//* Factors of Collision */
150 public:
151 static GUInt GetColliTime(GUInt _dTime,const GBody* _bodies,int _n);
152 static GBool DoColli(GUInt _dTime,GBody* _bodies,int _n);
153 };
154
155 class GBlast
156 {
157 /**//* 爆炸种类参数和爆炸效果、曲线范围方法的封装 */
158 public:
159
160
161 };
162 class GLog
163 {
164 /**//* 记录所有bodies改变以及改变原因,以及季节变化(如果存在变化的话) */
165 };
166 public:
167 GUInt KillBody(int _i);//按list序号零序第_i位毁灭一个body,但保持其为空:map相对稳定速度快
168 GUInt MakeWind(int _delayTime,GVector2d _wind);
169 GUInt MakeBody(GBody* _newBody);
170 GUInt BreakIntoPieces(GBlast& _blast);
171 GUInt MakeChanged(GBody* _body);
172 GBool ChangeGField(const GVector2d& _gField);
173 };
174
175private:
176 GWorld::GDominator &god;
177public:
178
179
180 GUInt AddGod(GWorld::GDominator& _god);
181 GULong GetTime();
182 GUInt GetTimeModule();
183 GULong GetDoomsday();
184
185 GVector2d GetGField();
186
187 GBool Appear(GULong _time,GULong _doomsday,GUInt _timeModule);//初始化世界参数,读档时读取存档参数传入此处开始游戏
188 GBool Run();//世界运行一个最小时间周期(timeModule)
189 GBool End();
190
191
192
193};
194
195
196
197#endif
198
可以这么简单描述:
要想让引擎工作,先要创造一个世界(GWorld),在世界里面添加一个神(GDominator),在世界里加上万物(GBody*),接着加入等效重力向量(GVector2d)。
到此不仅要问自己几个问题:
1 为什么要用神来控制随机事件和记录游戏分数、状态信息、活物的生死,而不是直接是GWorld类?
神是GWorld的内嵌类,在GWorld层次还不能加入神的意志(Consider方法),因为那是设计游戏逻辑阶段的可配置内容,所以所有随机事件和记录方式、影响审判的策略都没有形成,总不能用GWorld加入AI类的对象,因为AI不属于物理引擎范畴。于是GWorld::GDominator可以被游戏设计者自己创造的God类继承,God类玩家可以自由添加AI范畴,并且重载Consider方法来用AI驱动神的意志。
2 为什么世界类用“Appear”方法名开始世界对象的运行而不用“Start”名字?如果游戏进行到一半,存档后退出,如何重新加载进来呢?如果写成Start,那么就不符合加载中途游戏的意思,那么出现一个世界就变得合理了。
3 力应该属于个体(Body)吗?力是成对出现的,所以这里力不被单独标示,而用GVector2d直接表示,这里用一个二维表Array<Array<GVector2d*>> 表示i Body 对 j Body的力向量,每一个Body有唯一的序号,也就是说,Body被毁灭后仍然不会影响其它Body的id号和映射号、位置。因此力被设计成单独放在神的类中管理。
4 时间精度如何定义?这里的物理引擎完全逻辑设计,具体实现时间粒度与特定类型无关.最小时间粒度是1,并不定义是1ms还是1ns,那是游戏实现的事情,另外,时间最小粒度是1,但是不可能每1粒度刷新一次世界万物状态,所以设置了timeModule,这是刷新世界状态的最小时间周期,比如为timeModule=5时,每5个时间粒度刷新一次世界状态,世界状态的刷新也有具体实现方法的技巧,不可能有n个body就计算n*(n-1)次body状态,必定有些规则导致只有特定某些物体事件发生的状态会改变。
5 如何刷新运动状态?这里不可能大量使用积分拟合,对于圆周运动,需要用到精确运动位移量,对于抛体运动,可以直接从力刷新到加速度,再到速度,再到运动位移,相当于矩形近似拟合。
6 如何检测碰撞?碰撞必须是接触力或非接触力突变幅度大的双方物体。不可能每刷新一个时间粒度就检测所有body之间是否存在碰撞,有一点是确定的,只有突变了属性的(电荷量、质量、磁性、位置)的body之间才可能存在碰撞,也就是每一个时间粒度的逝去之前会专门对它们之间进行碰撞检测并计算出碰撞结果以及影响结果(这些结果也会记录进神的Log中,一边游戏结束前可以一次性分析Log得出得分,或者这些Log影响物理世界的其他因素)。
对物理引擎简化的使用代码如下:
1GPlayer player[5];//GPlayer and GSubstance inherit from GBody
2GSubstance substance[8];
3God god;//god inherits from GWorld::GDominator
4
5
6GWorld World;
7World.Appear(0,god,5);
8
9for(int i=0;i!=5;i++)
10 World.god.MakeBody(&player[i]);
11
12for(int i=0;i!=8;i++)
13 World.god.MakeBody(&substance[i]);
14
15 for(;;)
16 {
17 World.Run();
18 }
19
20World.End();
这里省略的GPlayer、GSubstance定义分别继承自物理引擎中的GBody类
God类继承于GWorld::GDominator类。
这种继承的作用体现在是可配置的,增加策略AI是非常必要的环节。
以上接口的实现需要考虑更加细微的问题:
1 比如什么程度下万有引力需要去计算(这里考虑到多个星球加入世界)
2 碰撞检测是否需要多层形状,不同的碰撞用不同的形状去检测,以增加视觉精确性
3 对于转动惯量等内容的计算是否要完全符合物理规则
4 怎么更好的确定击打边角时该依赖的支点或者说转轴
5 什么程度的因素会导致发生碰撞后并不分离(硬度、密度、形状?)
6 风力系统的模拟是封装在God类中的,但是风力系统是用流体力学去计算还是直接用轨迹和制造转角力矩去实现树叶等物体的无规则飘散
....and so on.
目前为止应该算实现了Gods 2DPhyEngine公共接口部分的65%左右。
欢迎各位全图灵组员开喷~
另一方面,给出一个游戏整体框架: