1

我正在使用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);
        }
    }
}

如下图所示,结果不是我所期望的:所有球加在一起和世界边界之间有一个偏移量

测试

  • 我尽力使用ConvertUnits助手正确处理 Farseer 世界单位和显示单位。
  • 我还读过一些关于包围所有身体以避免连续碰撞的“皮肤”,但我没想到它会那么大。
  • 我注意到 UIElement 的 X/Y 值是 UIElement 的上/左角这一事实。

有什么特别的我忘记了吗?

我知道我可以通过玩椭圆的显示半径来作弊,但我更愿意了解我在处理世界单位和显示单位的方式上做错了什么。

4

1 回答 1

1

好的,我找到了。问题很简单:

  • Ellipse WPF 元素使用WidthHeight来定义对象大小,即Diameter
  • CreateCircle 使用半径

所以球体的正确分配是:

var ball = BodyFactory.CreateCircle(world, ConvertUnits.ToSimUnits(w / 2), 1);
于 2013-03-29T10:05:20.950 回答