2010-07-22 2 views
6

У меня в настоящее время это нормально (ish) в моей игре, но я не фантастичен в математике. Когда сталкиваются два примитива, я хочу, чтобы они разбивались на мелкие биты, если сила, приложенная к примату, была выше установленного порога. В данный момент мой обработчик событий столкновения выглядит так.Проблемы обработчика событий OnCollision в C# XNA с физикой Farseer

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; 
} 

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

ответ

6

(В то время как это в настоящее время принято решение - я бы направить кого-нибудь к моим other answer для потенциально превосходящего подхода.)

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

Ваш код выглядит так, как будто он использует Farseer 3.0 (вы должны указать, потому что это больше вилка Box2DX чем у Фарсера 2.1). То, что я сделал в предсказателе 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 
} 

Из краткого обзора 3.0 источника предсказателя, кажется, что Manifold имеет элемент:

public FixedArray2<ManifoldPoint> Points; 

и оба Manifold и ManifoldPoint есть члены:

public Vector2 LocalPoint; 

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

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

+0

Hi Andrew, Спасибо за ваш ответ. Моя функция BreakUp() просто добавляет загрузку новых примитивов в список, основанный на позиции преломляющего примитива (чипы, если хотите), они не добавляются до следующей физики «Step()». Аналогично, уничтожаемый ящик просто помечен для удаления в следующем столбце. Итак, в вашем примере ... Я бы правильно использовал LocalPoint, а не переменную положения в вашей функции? Также я вижу, что это создает переменную hitVelocity, но я не уверен, как это относится к силе, применяемой к объекту. Я знаю, что F = MA, но здесь нет никаких углов? – DrLazer

+1

Да, используйте LocalPoint. Моя переменная Position находится в мировом пространстве (так что найдите локальный эквивалент пространства GetVelocityAtWorldPoint). Чтобы найти силу, вам понадобится ускорение (F = MA). Вы можете аппроксимировать ускорение, определяя, насколько скорость изменится в этом кадре (разница в скорости, деленная на время). –

+0

См. Также: http://en.wikipedia.org/wiki/Impulse_(physics) –

2

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

+2

Это не сработает, например, для длинного вращающегося пучка, который «смазывает» другой объект. –

+0

@ Андрю: Правильно, это не так. Просто несколько более реалистично, чем опубликованный код ... –

8

Хорошо, поэтому мой other answer жизнеспособен. Но я больше смотрел на 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; 
     } 
    } 
} 

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

+0

Г-н Russell you являются истинным Гентом. Благодарим вас за вашу помощь. В этот момент я буду разорвать код и в значительной степени украсть эту функциональность. Im теперь заинтригован относительно того, что установит «_break» в true, будет достигнуто ... hmmmmmm – DrLazer

+0

Он помещает функцию «Update()» для выполнения фактического разрыва (я имел в виду ссылку на код: http: //farseerphysics.codeplex. ком/SourceControl/ревизия/просмотр/73518 # 1086713) –

Смежные вопросы