花了差不多一个星期写的试验性的碰撞物理引擎
还是试验性阶段,只有球和线段
首先为了统一接口,定义一个 物理实体类 CEntity,存些最简单关键的东西譬如坐标,碰撞系数,可动性等等,还有一个比较重要的参数,是实体类型(球,线段,或者以后加上的什么东西)
接下来就是球和线段的实现了。
当然,有了实体类,球和线段就要继承实体了。
在这个引擎里面,由于线段基本都被当障碍使用,所以线段暂时是不可动的,只存几个参数:两个端点,碰撞系数,没了。函数也是最简单的getter和setter就可以了。
接下来是球
球是这个引擎里面最主要的一个实体。可动的实体。其实就一个圆圈。刚才忘了说,这是一个2d的碰撞物理引擎。
模拟物理运动,力是最重要的,还有速度。我是用了向量来标记这些东西。为了运算简单方便,重载了向量的n个运算符:
1
class CVector
2![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
public:
4
double degree;
5
double x, y;
6
public:
7
CVector(double x1, double y1, double x2, double y2);
8
CVector(double x, double y, double degree);
9
CVector(double x, double y);
10
CVector();
11
CVector operator+(CVector);
12
CVector operator-(CVector);
13
//CVector operator=(CVector);
14
void operator=(CVector);
15
CVector operator*(double);
16
CVector operator&(CVector);
17
CVector operator/(double);
18
bool operator<<(CVector);
19
void standarlization();
20
void positivation();
21
void reset();
22
CVector reverse();
23
};
24![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
解析一下,向量是由一个二维坐标以及长度指定,有时候长度是没用的(标记方向的时候),所以degree不是一个必要的参数。
关于运算符,* , / 是简单的把向量长度改变,比较特别的是&(投影运算)和 <<(判断方向顺便判断长度。),这些是特殊要求。之后会说到。stan。。。是把方向坐标标准化,pos....是把degree正量化(通过改变方向),reset是用于除0的时候把向量至0,reverse返回相反的向量。
回到来说球,其实球需要的东西不多,存一个速度,然后写一个更新函数就差不多了。辅助参数是摩擦系数和半径什么之类的。当然,我的习惯是把一些用到的东西都封成接口对外。于是这里就有了几个接口:推力,牵引,或者直接重置一个速度。
顺便做名词解析:推力是施加外力;牵引力是他主动运动,来源于摩擦力。
为了解决重叠的问题,另加了一个重置坐标的接口。
这是更新函数:
1
void CBall::reflash()
2![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
if(force.degree > 0.01)
4![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
5
velocity = velocity + force/gravity;
6
}
7
if(velocity.degree < 0.01)
8![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
9
velocity.degree = 0;
10
}
11
else
12![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
13
velocity.standarlization();
14
double tx = x;
15
x += velocity.degree * velocity.x;
16
y += velocity.degree * velocity.y;
17
CVector griftForce(velocity.x * (-1), velocity.y * (-1), 1);
18
griftForce = griftForce * (gravity * Qgrift * Qgroundgrift);
19
griftForce.degree += velocity.degree * velocity.degree / 1000;
20
//force = force + griftForce;
21
velocity.degree -= griftForce.degree/gravity;
22
if(velocity.degree < 0)
23![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
24
velocity.degree = 0;
25
}
26
}
27
if(velocity.degree > maxVelocity)
28![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
29
velocity.degree = maxVelocity;
30
}
31
force.reset();
32
}
33![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
力的实现是每一帧清空一次,然后在下一帧内把受到的力全部叠加起来,下一次更新的时候用上。
再做一个备注,大家可能会发现有个Qgroundgrift的东西,这是场景的摩擦系数。
由于一些特殊结构的考虑,我把碰撞的检测移到场景类里边。
事实也证明,移到场景类里边是很好的一个决定。
一说到场景,首先反映是边界。其实场景的函数不多,不过个个都不是容易写的东西。。。
typedef map<int, CEntity*> ObjectMap;
1
class CScene
2![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
protected:
4
double width, height;
5
double grift;
6
public:
7
bool impulse(CEntity *, CEntity *);
8
bool impulseBallBall(CBall*, CBall*);
9
bool impulseBallLine(CBall*, CLine*);
10
//void scanCrash(CLinkList<CBall>);
11
void scanCrash(ObjectMap);
12
bool edgeCheck(CEntity *);
13
void init(double w, double h, double g);
14
double getGrift();
15
};
16![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
这里被水王打击了一下,
“18:24:54
举例子,如果你有10种本质上不同的几何体,你不得不写10×10=100个碰撞函数 ”
这是他的原话
幸好我现在只有两种东西。。其实两种东西可以实现很多样式了。
譬如线段可以组合成多边形。。。。当然没有角速度。。连球都还没实现角速度。
scanCrash是场景的主要逻辑,传入一堆实体对象,让他们在场景里面该碰撞的碰撞,该更新的更新,该撞墙的撞墙,当然,是要两两检测
void CScene::scanCrash(ObjectMap map)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
CEntity *oa,*ob;
ObjectMap::iterator pos;
if(map.size() > 0)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
int k = 0;
pos = map.begin();
oa = pos->second;
while(pos != map.end())
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
oa = pos->second;
k++;
pos++;
while(pos != map.end())
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
ob = pos->second;
impulse(oa, ob);
pos++;
}
pos = map.begin();
for(int i = 0; i < k; i++)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
pos++;
}
}
for(pos = map.begin(); pos != map.end(); ++pos)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
edgeCheck(pos->second);
}
}
for(pos = map.begin(); pos != map.end(); ++pos)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
if(pos->second->getType() == BALL)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
((CBall*)pos->second)->reflash();
}
}
}
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
也还是不完整的因素,线段都是unmovable的,所以只有ball有reflash函数。哇哈哈哈
edgeCheck就更简单了。生成几条线段(就是围成边界那几条,然后让他们跟实体碰撞就行)
下面把球球碰和球线碰的函数贴出来,
1
bool CScene::impulseBallBall(CBall *a, CBall *b)
2![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
CVector atob;
4
atob.x = b->getX() - a->getX();
5
atob.y = b->getY() - a->getY();
6
atob.degree = 1;
7
double distance;
8
distance = pow((atob.x * atob.x + atob.y * atob.y), 0.5);
9
atob.standarlization();
10
CVector pva = a->getV();
11
CVector pvb = b->getV();
12
//CVector rv = pva & atob - pvb & atob;
13
CVector tpva = pva & atob;
14
CVector tpvb = pvb & atob;
15
double rv = tpva.degree - tpvb.degree;
16
if(rv < 0)
17![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
18
return false;
19
}
20
if(distance - a->getEage(atob) - b->getEage(atob.reverse()) < rv/2)
21![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
22
double qa = a->getCollideQ();
23
double qb = b->getCollideQ();
24
double q = qa * qb;
25
double ga = a->getG();
26
double gb = b->getG();
27
double vad, vbd;
28
if(q < 0)
29![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
30
return true;
31
}
32
else if(q < 0.001)
33![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
34
//vad = ((ga - gb) * tpva.degree + 2 * gb * tpvb.degree) / (ga + gb);
35
//vbd = ((gb - ga) * tpvb.degree + 2 * ga * tpva.degree) / (ga + gb);
36
vad = vbd = (ga * tpva.degree + gb * tpvb.degree) / (ga + gb);
37
}
38
else
39![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
40
vad = tpva.degree - ((1+q) * gb * (tpva.degree - tpvb.degree) / (ga + gb));
41
vbd = tpvb.degree + ((1+q) * ga * (tpva.degree - tpvb.degree) / (ga + gb));
42
}
43
CVector tava = atob;
44
tava.degree = vad;
45
CVector tavb = atob;
46
tavb.degree = vbd;
47
CVector ava = pva - tpva;
48
ava = ava + tava;
49
CVector avb = pvb - tpvb + tavb;
50
a->move(ava);
51
b->move(avb);
52
return true;
53
}
54![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
55
else if(a->getEage(atob) + b->getEage(atob.reverse()) - distance > 0.1)
56![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
57
double d = (a->getEage(atob) + b->getEage(atob.reverse()) - distance + 0.2)/2;
58
CVector btoa = atob.reverse();
59
a->setXY(a->getX() + btoa.x * d, a->getY() + btoa.y * d);
60
b->setXY(b->getX() + atob.x * d, b->getY() + atob.y * d);
61
return true;
62
}
63![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
64
else
65![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
66
return false;
67
}
68
}
69![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
70
bool CScene::impulseBallLine(CBall* a, CLine* b)
71![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
72
//a,b:线段端点
73
//c:球心
74
CVector atob(b->getXa(), b->getYa(), b->getXb(), b->getYb());
75
CVector ctob(a->getX(), a->getY(), b->getXb(), b->getYb());
76
CVector shadow = ctob & atob;
77
double sx, sy;
78
if(shadow << atob)
79![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
80
shadow.standarlization();
81
sx = b->getXb() - shadow.x * shadow.degree;
82
sy = b->getYb() - shadow.y * shadow.degree;
83
double kx = b->getXa();
84
double ky = b->getYa();
85
kx = ky;
86
}
87
else
88![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
89
atob.degree = atob.degree + shadow.degree;
90
if(shadow << atob)
91![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
92
sx = b->getXa();
93
sy = b->getYa();
94
}
95
else
96![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
97
sx = b->getXb();
98
sy = b->getYb();
99
}
100
}
101
CVector cs(a->getX(),a->getY(),sx,sy);
102
//sc.standarlization();
103
CVector rv = a->getV() & cs;
104![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
105
if(xv.degree > 0)
106
{
107
//a->setXY(a->getX() + d * sc.x, a->getY() + d * sc.y);
108
return false;
109
}
110
*/
111
double distance = pow((a->getX() - sx) * (a->getX() - sx) + (a->getY() - sy) * (a->getY() - sy), 0.5);
112![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
113
if(distance < (a->getR() + xv.degree/2))
114
{
115
double d = a->getR() + xv.degree - distance;
116
a->setXY(a->getX() + d * cs.x, a->getY() + d * cs.y);
117
//CVector f = a->getF() & sc.reverse();
118
//a->push(f.reverse());
119
return true;
120
}
121
*/
122
if(distance < (a->getR() + rv.degree/2))
123![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
124
CVector v = a->getV();
125
//CVector rv = v & cs;
126
v = v - rv;
127
double q = a->getCollideQ() * b->getCollideQ();
128
rv.degree = pow(q * rv.degree * rv.degree, 0.5);
129
v = v - rv;
130
a->move(v);
131
CVector f = a->getF();
132
f = f & cs;
133
if(f.degree > 0)
134![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
135
f.degree = -f.degree;
136
a->push(f);
137
}
138
return true;
139
}
140
return false;
141
}
142![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
好长啊。。。慢慢看下算了。。不解析
还剩几个问题没解决,一堆球挤压的时候,会出现不规则的重叠问题。这个问题太难了。跟球球碰的顺序还有很大关系,所以就扔着不理了。