Проблемы обработчика событий OnCollision в C # XNA с Farseer Physics - PullRequest
6 голосов
/ 22 июля 2010

В данный момент у меня в игре все нормально, но я не очень разбираюсь в математике.Когда два примитива сталкиваются, я хочу, чтобы они разбились на мелкие кусочки, если сила, приложенная к примитиву, превысила установленный порог.Мой обработчик событий столкновения в настоящее время выглядит следующим образом.

public bool Collision(Fixture fixtureA, Fixture fixtureB, Manifold manifold) 
{ 
   Vector2 position = manifold.LocalNormal; 
   float angle = (float)Math.Atan2(position.Y, position.X); 
   Vector2 force = Vector2.Zero; 
   if (angle < 0) 
     force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi + angle) * fixtureA.Body.LinearVelocity.Y); 
   else 
     force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi - angle) * fixtureA.Body.LinearVelocity.Y); 
   double XForce = Math.Sqrt(force.X * force.X); 
   double YForce = Math.Sqrt(force.Y * force.Y); 
   double totalForce = XForce + YForce; 
   if ((Breakable) && (totalForce > BreakForce)) 
   { 
      Breakable = false; 
      Active = false; 
      BreakUp(fixtureA, fixtureB); 
   } 
   return true; 
} 

Я поместил это в ДОЛГОЕ время назад, когда я просто играл.Это вызывает небольшую проблему в определенных ситуациях.Например, если примитив неподвижен на полу, а другой примитив падает на него с приличной высоты, почти всегда падающий ящик взрывается, а оставшийся ящик выживает.Также, если две коробки падают бок о бок и дают друг другу крошечные прикосновения, то обе коробки взрываются в воздухе.Хммммм, не совсем так.У кого-нибудь есть идеи, как улучшить мой обработчик коллизий?Заранее спасибо.

Ответы [ 3 ]

8 голосов
/ 22 июля 2010

Хорошо, поэтому мой другой ответ жизнеспособен.Но я более внимательно посмотрел на Farseer 3.0 (текущую версию SVN) и обнаружил, что уже реализует почти точно, что вы пытаетесь сделать.

Ищите " BreakableBody.cs ».Возможно, вы сможете использовать это напрямую, но в противном случае вы можете просто скопировать нужную вам функциональность.

В частности: вместо того, чтобы прикреплять функцию к OnCollision вашего прибора, вы хотите прикрепить ее к PostSolve.Требуется ContactConstraint, в который вы можете погрузиться и найти импульсы от столкновения.

Это реализация этой функции, используемая BreakableBody:

private void PostSolve(ContactConstraint contactConstraint)
{
    if (!Broken)
    {
        float maxImpulse = 0.0f;
        for (int i = 0; i < contactConstraint.manifold.PointCount; ++i)
        {
            maxImpulse = Math.Max(maxImpulse,
                         contactConstraint.manifold.Points[0].NormalImpulse);
            maxImpulse = Math.Max(maxImpulse,
                         contactConstraint.manifold.Points[1].NormalImpulse);
        }

        if (maxImpulse > Strength)
        {
            // Flag the body for breaking.
            _break = true;
        }
    }
}

Очевидно, импульсные данные в коллекторах правильно установлены только в PostSolve, а не в OnCollision.

6 голосов
/ 22 июля 2010

(Хотя это в настоящее время принятый ответ - я бы направил любого на мой другой ответ для потенциально более эффективного подхода.)

Ваш расчет силы удара полностью неверен. Вам нужно получить относительную скорость в точке (точках) контакта - вы получаете что-то довольно странное ...

Ваш код выглядит так, как будто он использует Farseer 3.0 (вам следует указать, потому что это скорее форк Box2DX, чем Farseer 2.1). То, что я сделал в Farseer 2.1 (где у вас есть ContactList contacts вместо Manifold), чтобы получить скорость удара, было:

foreach(Contact contact in contacts)
{
    Vector2 position = contact.Position;
    Vector2 v0;
    me.Body.GetVelocityAtWorldPoint(ref position, out v0);
    Vector2 v1 = new Vector2();
    if(!hit.Body.IsStatic)
        hit.Body.GetVelocityAtWorldPoint(ref position, out v1);
    v0 -= v1;

    float hitVelocity = v0.Length();
    // To then get the force, you need the mass of the two objects
}

Из краткого обзора источника Farseer 3.0 видно, что Manifold имеет члена:

public FixedArray2<ManifoldPoint> Points;

И оба Manifold и ManifoldPoint имеют членов:

public Vector2 LocalPoint;

Должно быть довольно просто изменить мой код Farseer 2.1, чтобы использовать их вместо этого.

Также: я рекомендую просто пометить два объекта как нуждающиеся в разрыве, а затем фактически разбить их после Ваше обновление физики завершится (а не в обработчике столкновений).

2 голосов
/ 22 июля 2010

Я не использовал XNA, но как физическую проблему, почему бы просто не вычесть линейную скорость A из линейной скорости B и получить «силу» как квадрат результирующего вектора (сложить квадраты компоненты)? Это должно согласовываться с кинетической энергией, соответствующей «E = (mv ^ 2) / 2», для очень простой физической модели, даже если мы игнорируем массы (или, в этом отношении, упругость или различие между энергией и импульс). Если объекты движутся в одном и том же общем направлении с одинаковой скоростью, вы получите небольшое значение; если кто-то приближается (или, конечно, уходит!) на высокой скорости, вы получаете большую ценность.

...