Смещение между сталкивающимися объектами в Farseer Physics

Я играю с движком Farseer Physics и WPF.

На данный момент мне удалось создать окно с падающими шарами, используя этот код:

XAML:

<Window x:Class="test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" MouseDown="Window_MouseDown_1">
    <Canvas
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            x:Name="root"
            RenderTransform="1 0 0 -1 0 0">
    </Canvas>
</Window>

Код позади:

using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace test
{
    public partial class MainWindow : Window
    {
        World world;

        public MainWindow()
        {
            InitializeComponent();

            world = new World(new Vector2(0, -9.81f));

            var polygon = new Polygon() { Fill = Brushes.Gray };
            foreach (var pt in border)
                polygon.Points.Add(pt);
            root.Children.Add(polygon);

            var vertices = new Vertices();
            foreach (var pt in border)
                vertices.Add(ConvertUnits.ToSimUnits(pt.X, pt.Y));
            var body = BodyFactory.CreateChainShape(world, vertices);
            body.BodyType = BodyType.Static;

            body.CollisionCategories = Category.All;
            body.CollidesWith = Category.All;

            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        Point[] border = new[] 
            { 
                new Point(200, -200), 
                new Point(200, 200),
                new Point(-200, 200),
                new Point(-200, -200),
                new Point(200, -200)
            };

        Dictionary<Body, UIElement> balls = new Dictionary<Body, UIElement>();

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            world.Step(1/60f);
            foreach (var ball in balls.Keys)
                display(ball);
        }

        void display(Body ball)
        {
            var element = balls[ball];
            Canvas.SetLeft(element, ConvertUnits.ToDisplayUnits(ball.Position.X));
            Canvas.SetTop(element, ConvertUnits.ToDisplayUnits(ball.Position.Y));
        }

        private void Window_MouseDown_1(object sender, MouseButtonEventArgs e)
        {
            var w = 20f;
            var point = e.GetPosition(root);

            var ball = BodyFactory.CreateCircle(world, ConvertUnits.ToSimUnits(w), 1);
            ball.BodyType = BodyType.Dynamic;
            ball.Position = ConvertUnits.ToSimUnits(new Vector2((float)point.X, (float)point.Y));
            ball.CollisionCategories = Category.All;
            ball.CollidesWith = Category.All;
            ball.Restitution = .5f;

            var c = new Canvas();
            var ellipse = new Ellipse() { Width = w, Height = w, Fill = Brushes.Yellow };
            c.Children.Add(ellipse);

            Canvas.SetLeft(ellipse, -w / 2);
            Canvas.SetTop(ellipse, -w / 2);
            Canvas.SetZIndex(c, 10);

            root.Children.Add(c);
            balls.Add(ball, c);

            display(ball);
        }
    }
}

Как показано на рисунке ниже, результат не тот, который я ожидаю: между всеми шарами и границами мира есть смещение.

Тестовое задание

  • Я изо всех сил старался правильно обрабатывать юниты мира Farseer и отображать юниты, используя ConvertUnits помощник.
  • Я также читал кое-что о "коже", которая охватывает все тела, чтобы избежать непрерывных столкновений, но я не ожидал, что она будет такой большой.
  • Я позаботился о том, чтобы значение X/Y элемента UIElement находилось в верхнем / левом углу элемента UIElement.

Есть что-то особенное, что я забыл?

Я знаю, что мог бы обмануть, играя с радиусом отображения Эллипса, но я бы предпочел понять, что я делаю неправильно, когда я обращаюсь с мировыми единицами и единицами отображения.

1 ответ

Решение

Хорошо, я нашел это. Проблема была проста:

  • Элемент Ellipse WPF использует ширину и высоту, чтобы определить размер объекта, то есть диаметр
  • CreateCircle использует Radius

Таким образом, правильное назначение для тела шара:

var ball = BodyFactory.CreateCircle(world, ConvertUnits.ToSimUnits(w / 2), 1);
Другие вопросы по тегам