[译]A Verlet based approach for 2D game physics - Part Three

碰撞反应



        终于,我们在前面的文章中讲解完了碰撞检测的内容。剩下的最后一件事情就是解决碰撞反应,而这个内容能够很容易的在我们的系统中实现。正如我们在前面解释的,我们只需要沿着碰撞向量对碰撞边界和碰撞顶点进行移动就可以了。对于碰撞顶点来说,这个处理需要一定的技巧。由于我们在前面已经保证了碰撞法线指向了拥有碰撞顶点的那个刚体,所以我们只需要对这个顶点的位置加上半个碰撞向量即可:

void Physics::ProcessCollision() {
  Vec2 CollisionVector = CollisionInfo.Normal*CollisionInfo.Depth;

  CollisionInfo.V->Position += CollisionVector*0.5f;
}

        对于碰撞边界来说,它的处理就要复杂的多。构成这个碰撞边界的两个顶点需要不同的处理方式,而这依赖与碰撞顶点所在的位置。如果碰撞顶点靠近碰撞边界的某个端点,那么这个端点就移动的比较多,相反的另外一个就移动的比较少。也就是说,我们需要首先计算出我们的碰撞顶点在这条边界上的位置信息。这个可以使用下面的函数计算出来:



        上面公式中的V表示的是碰撞顶点的位置,E1和E2分别表示碰撞边界的两个端点的位置。t就是用来表示碰撞顶点在碰撞边界上位置信息的一个系数,它的范围在[0,1]之间。不管我们选择的是X轴还是Z轴进行计算,结果都是一样的。使用X轴来进行计算的公式如下所示:

Vertex* E1 = CollisionInfo.E->V1;
Vertex* E2 = CollisionInfo.E->V2;

float T = ( CollisionInfo.V->Position.X - CollisionVector.X - E1->Position.X )/(  E2->Position.X - E1->Position.X );

        但是要小心。如果E2顶点直接就在E1的上方,那么上面的代码就会发生除0异常。所以,我们需要添加一些额外的检测代码,如下所示:

float T;
if( abs( E1->Position.X - E2->Position.X ) > abs( E1->Position.Y - E2->Position.Y ) )
  T = ( CollisionInfo.V->Position.X - CollisionVector.X - E1->Position.X )/(  E2->Position.X - E1->Position.X );
else
  T = ( CollisionInfo.V->Position.Y - CollisionVector.Y - E1->Position.Y )/(  E2->Position.Y - E1->Position.Y );

        这段代码简单的判断一下碰撞边界在哪一个轴上的落差较大,并且使用较大的一方来进行反应系数的计算。

        然后,我们使用下面的一个公式,计算出一个缩放量,从而确保当我们解决完毕碰撞反应之后,碰撞顶点会坐落在碰撞边界之上。下面就是这个公式:



        我们将上面所有的内容都编写成代码,如下所示:

void Physics::ProcessCollision() {
  Vec2 CollisionVector = CollisionInfo.Normal*CollisionInfo.Depth;

  Vertex* E1 = CollisionInfo.E->V1;
  Vertex* E2 = CollisionInfo.E->V2;

  float T;
  if( abs( E1->Position.X - E2->Position.X ) > abs( E1->Position.Y - E2->Position.Y ) )
    T = ( CollisionInfo.V->Position.X - CollisionVector.X - E1->Position.X )/(  E2->Position.X - E1->Position.X);
  else
    T = ( CollisionInfo.V->Position.Y - CollisionVector.Y - E1->Position.Y )/(  E2->Position.Y - E1->Position.Y);

  float Lambda = 1.0f/( T*T + ( 1 - T )*( 1 - T ) );

  E1->Position -= CollisionVector*( 1 - T )*0.5f*Lambda;
  E2->Position -= CollisionVector*      T  *0.5f*Lambda;

  CollisionInfo.V->Position += CollisionVector*0.5f;
}

        看吧,我们已经成功的解决了碰撞反应,是不是非常简单啊?

        剩下的工作,就是将上面我们所有编写的代码,放置在一个更新函数中:

void Physics::Update() {
  UpdateForces();
  UpdateVerlet();
  IterateCollisions();
}

        IterateCollision函数中做了很多不同的事情。它会遍历所有的刚体,调用每一个刚体的UpdateEdges函数,重新计算刚体的中心坐标,然后进行碰撞检测,并且在检测到碰撞之后,进行碰撞反应。当然,这些工作并不是一次就结束,而是迭代的执行多次。这个过程调用的越多,游戏的物理看上去就越加的真实。原因已经在前面的文章中讲解了。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值