之前谈到模拟百战天虫的游戏类型,团队开始准备设计“百战天神”(Gods),需要一个统一的设计接口。
由于最核心的部分是2D Physics Engine ,所以先给出了一个公共实现框架性的代码(整个游戏代码的基础类库准备用vczh的vlpp库,vlpp_def是自己设定的统一vczh库调用头文件):
1
#ifndef _GODSPHY2D_
2
#define
_GODSPHY2D_
3
4
#include
"
vlpp_def.h
"
5
6
7
namespace
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
22
using
namespace
GType;
23
class
GVector2d
24
{
25
/**//* 待实现 */
26
}
;
27
28
class
GShape
29
{
30
public:
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
}
;
41
class
GBox:
public
GShape
42
{
43
44
}
;
45
class
GCircle:
public
GShape
46
{
47
48
}
;
49
class
GPoly:
public
GShape
50
{
51
52
}
;
53
54
55
56
57
class
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
85
public:
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
105
class
GForce
/**/
/* 主要封装作用力向量的计算方法, */
106
{
107
public:
108
}
;
109
110
class
GElasForce:
public
GForce
111
{
112
113
114
}
;
115
116
class
GElecForce:
public
GForce
117
{
118
}
;
119
class
GMagForce:
public
GForce
120
{
121
}
;
122
class
GFricForce:
public
GForce
123
{
124
125
}
;
126
127
128
129
class
GWorld
130
{
131
GULong time;//世界已运行时间
132
GUInt timeModule;//最小body状态变更周期
133
GULong doomsday;//末日期
134
135
136
GVector2d gField;//等效重力场(单一方向),gField=0时无重力场
137
Array<GBody*> bodies;//世界内所有body
138
public:
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
175
private:
176
GWorld::GDominator &god;
177
public:
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

2

3

4

5

6

7

8



9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24



25


26

27

28

29



30

31

32

33

34

35

36

37

38

39

40

41

42



43

44

45

46



47

48

49

50



51

52

53

54

55

56

57

58



59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89



90



91



92



93



94



95



96



97



98



99

100

101

102

103

104

105


106



107

108

109

110

111



112

113

114

115

116

117



118

119

120



121

122

123



124

125

126

127

128

129

130



131

132

133

134

135

136

137

138

139

140



141


142

143

144

145

146

147

148



149


150

151

152

153

154

155

156



157


158

159

160

161

162

163



164


165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

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影响物理世界的其他因素)。
对物理引擎简化的使用代码如下:
1
GPlayer player[
5
];
//
GPlayer and GSubstance inherit from GBody
2
GSubstance substance[
8
];
3
God god;
//
god inherits from GWorld::GDominator
4
5
6
GWorld World;
7
World.Appear(
0
,god,
5
);
8
9
for
(
int
i
=
0
;i
!=
5
;i
++
)
10
World.god.MakeBody(
&
player[i]);
11
12
for
(
int
i
=
0
;i
!=
8
;i
++
)
13
World.god.MakeBody(
&
substance[i]);
14
15
for
(;;)
16
{
17
World.Run();
18
}
19
20
World.End();

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16



17

18

19

20

这里省略的GPlayer、GSubstance定义分别继承自物理引擎中的GBody类
God类继承于GWorld::GDominator类。
这种继承的作用体现在是可配置的,增加策略AI是非常必要的环节。
以上接口的实现需要考虑更加细微的问题:
1 比如什么程度下万有引力需要去计算(这里考虑到多个星球加入世界)
2 碰撞检测是否需要多层形状,不同的碰撞用不同的形状去检测,以增加视觉精确性
3 对于转动惯量等内容的计算是否要完全符合物理规则
4 怎么更好的确定击打边角时该依赖的支点或者说转轴
5 什么程度的因素会导致发生碰撞后并不分离(硬度、密度、形状?)
6 风力系统的模拟是封装在God类中的,但是风力系统是用流体力学去计算还是直接用轨迹和制造转角力矩去实现树叶等物体的无规则飘散
....and so on.
目前为止应该算实现了Gods 2DPhyEngine公共接口部分的65%左右。
欢迎各位全图灵组员开喷~
另一方面,给出一个游戏整体框架:
