试验性物理引擎(未完成版)

花了差不多一个星期写的试验性的碰撞物理引擎
还是试验性阶段,只有球和线段
首先为了统一接口,定义一个 物理实体类 CEntity,存些最简单关键的东西譬如坐标,碰撞系数,可动性等等,还有一个比较重要的参数,是实体类型(球,线段,或者以后加上的什么东西)

接下来就是球和线段的实现了。
当然,有了实体类,球和线段就要继承实体了。
在这个引擎里面,由于线段基本都被当障碍使用,所以线段暂时是不可动的,只存几个参数:两个端点,碰撞系数,没了。函数也是最简单的getter和setter就可以了。

接下来是球
球是这个引擎里面最主要的一个实体。可动的实体。其实就一个圆圈。刚才忘了说,这是一个2d的碰撞物理引擎。
模拟物理运动,力是最重要的,还有速度。我是用了向量来标记这些东西。为了运算简单方便,重载了向量的n个运算符:
 1class CVector
 2{
 3public:
 4double degree;
 5double x, y;
 6public:
 7CVector(double x1, double y1, double x2, double y2);
 8CVector(double x, double y, double degree);
 9CVector(double x, double y);
10CVector();
11CVector operator+(CVector);
12CVector operator-(CVector);
13//CVector operator=(CVector);
14void operator=(CVector);
15CVector operator*(double);
16CVector operator&(CVector);
17CVector operator/(double);
18bool operator<<(CVector);
19void standarlization();
20void positivation();
21void reset();
22CVector reverse();
23}
;
24

解析一下,向量是由一个二维坐标以及长度指定,有时候长度是没用的(标记方向的时候),所以degree不是一个必要的参数。
关于运算符,* , / 是简单的把向量长度改变,比较特别的是&(投影运算)和 <<(判断方向顺便判断长度。),这些是特殊要求。之后会说到。stan。。。是把方向坐标标准化,pos....是把degree正量化(通过改变方向),reset是用于除0的时候把向量至0,reverse返回相反的向量。

回到来说球,其实球需要的东西不多,存一个速度,然后写一个更新函数就差不多了。辅助参数是摩擦系数和半径什么之类的。当然,我的习惯是把一些用到的东西都封成接口对外。于是这里就有了几个接口:推力,牵引,或者直接重置一个速度。
顺便做名词解析:推力是施加外力;牵引力是他主动运动,来源于摩擦力。
为了解决重叠的问题,另加了一个重置坐标的接口。
这是更新函数:

 1void CBall::reflash()
 2{
 3if(force.degree > 0.01)
 4{
 5  velocity = velocity + force/gravity;
 6}

 7if(velocity.degree < 0.01)
 8{
 9  velocity.degree = 0;
10}

11else
12{
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  {
24   velocity.degree = 0;
25  }

26}

27if(velocity.degree > maxVelocity)
28{
29  velocity.degree = maxVelocity;
30}

31force.reset();
32}

33

力的实现是每一帧清空一次,然后在下一帧内把受到的力全部叠加起来,下一次更新的时候用上。
再做一个备注,大家可能会发现有个Qgroundgrift的东西,这是场景的摩擦系数。

由于一些特殊结构的考虑,我把碰撞的检测移到场景类里边。
事实也证明,移到场景类里边是很好的一个决定。

一说到场景,首先反映是边界。其实场景的函数不多,不过个个都不是容易写的东西。。。
typedef map<int, CEntity*> ObjectMap;
 1class CScene
 2{
 3protected:
 4double width, height;
 5double grift;
 6public:
 7bool impulse(CEntity *, CEntity *);
 8bool impulseBallBall(CBall*, CBall*);
 9bool impulseBallLine(CBall*, CLine*);
10//void scanCrash(CLinkList<CBall>);
11void scanCrash(ObjectMap);
12bool edgeCheck(CEntity *);
13void init(double w, double h, double g);
14double getGrift();
15}
;
16
这里被水王打击了一下,
“18:24:54
举例子,如果你有10种本质上不同的几何体,你不得不写10×10=100个碰撞函数
这是他的原话

幸好我现在只有两种东西。。其实两种东西可以实现很多样式了。
譬如线段可以组合成多边形。。。。当然没有角速度。。连球都还没实现角速度。
scanCrash是场景的主要逻辑,传入一堆实体对象,让他们在场景里面该碰撞的碰撞,该更新的更新,该撞墙的撞墙,当然,是要两两检测
void CScene::scanCrash(ObjectMap map)
{
CEntity 
*oa,*ob;
ObjectMap::iterator pos;
if(map.size() > 0)
{
  
int k = 0;
  pos 
= map.begin();
  oa 
= pos->second;
  
while(pos != map.end())
  
{
   oa 
= pos->second;
   k
++;
   pos
++;
   
while(pos != map.end())
   
{
    ob 
= pos->second;
    impulse(oa, ob);
    pos
++;
   }

   pos 
= map.begin();
   
for(int i = 0; i < k; i++)
   
{
    pos
++;
   }

  }

  
for(pos = map.begin(); pos != map.end(); ++pos)
  
{
   edgeCheck(pos
->second);
  }

}

for(pos = map.begin(); pos != map.end(); ++pos)
{
  
if(pos->second->getType() == BALL)
  
{
   ((CBall
*)pos->second)->reflash();
  }

}

}

也还是不完整的因素,线段都是unmovable的,所以只有ball有reflash函数。哇哈哈哈
edgeCheck就更简单了。生成几条线段(就是围成边界那几条,然后让他们跟实体碰撞就行)
下面把球球碰和球线碰的函数贴出来,

  1bool CScene::impulseBallBall(CBall *a, CBall *b)
  2{
  3CVector atob;
  4atob.x = b->getX() - a->getX();
  5atob.y = b->getY() - a->getY();
  6atob.degree = 1;
  7double distance;
  8distance = pow((atob.x * atob.x + atob.y * atob.y), 0.5);
  9atob.standarlization();
 10CVector pva = a->getV();
 11CVector pvb = b->getV();
 12//CVector rv = pva & atob - pvb & atob;
 13CVector tpva = pva & atob;
 14CVector tpvb = pvb & atob;
 15double rv = tpva.degree - tpvb.degree;
 16if(rv < 0)
 17{
 18  return false;
 19}

 20if(distance - a->getEage(atob) - b->getEage(atob.reverse()) < rv/2)
 21{
 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  {
 30   return true;
 31  }

 32  else if(q < 0.001)
 33  {
 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  {
 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
 55else if(a->getEage(atob) + b->getEage(atob.reverse()) - distance > 0.1)
 56{
 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
 64else
 65{
 66  return false;
 67}

 68}

 69
 70bool CScene::impulseBallLine(CBall* a, CLine* b)
 71{
 72//a,b:线段端点
 73//c:球心
 74CVector atob(b->getXa(), b->getYa(), b->getXb(), b->getYb());
 75CVector ctob(a->getX(), a->getY(), b->getXb(), b->getYb());
 76CVector shadow = ctob & atob;
 77double sx, sy;
 78if(shadow << atob)
 79{
 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}

 87else
 88{
 89  atob.degree = atob.degree + shadow.degree;
 90  if(shadow << atob)
 91  {
 92   sx = b->getXa();
 93   sy = b->getYa();
 94  }

 95  else
 96  {
 97   sx = b->getXb();
 98   sy = b->getYb();
 99  }

100}

101CVector cs(a->getX(),a->getY(),sx,sy);
102//sc.standarlization();
103CVector rv = a->getV() & cs;
104/*
105if(xv.degree > 0)
106{
107  //a->setXY(a->getX() + d * sc.x, a->getY() + d * sc.y);
108  return false;
109
110*/

111double distance = pow((a->getX() - sx) * (a->getX() - sx) + (a->getY() - sy) * (a->getY() - sy), 0.5);
112/*
113if(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*/

122if(distance < (a->getR() + rv.degree/2))
123{
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  {
135   f.degree = -f.degree;
136   a->push(f);
137  }

138  return true;
139}

140return false;
141}

142

好长啊。。。慢慢看下算了。。不解析
还剩几个问题没解决,一堆球挤压的时候,会出现不规则的重叠问题。这个问题太难了。跟球球碰的顺序还有很大关系,所以就扔着不理了。

posted on 2009-03-08 22:18 whitech 阅读(188) 评论(1)  编辑 收藏 引用

评论

# re: 试验性物理引擎(未完成版) 2009-11-10 16:21 游客

旁边的注释太少了。。。。。  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2009年3月>
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿(1)

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜