Laden...

MonoGame + Farseer Physics - Units zu Pixeln umrechnen

Erstellt von Djeurissen vor 8 Jahren Letzter Beitrag vor 8 Jahren 3.509 Views
D
Djeurissen Themenstarter:in
4 Beiträge seit 2015
vor 8 Jahren
MonoGame + Farseer Physics - Units zu Pixeln umrechnen

Tagchen,

ich versuche derzeit Farseer Physics in mein MonoGame Projekt einzubinden.
Das Problem ist das man immer wieder die Klasse ConvertUnits nutzen muss, wenn ich etwas zeichnen will oder der "World" hinzufügen möchte. Das zieht sich durch den gesammten Code.

Nun ist die Frage, muss man das wirklich immer machen oder gibt es da keine andere Lösung für?

742 Beiträge seit 2005
vor 8 Jahren

Du kannst es über die View oder Projektionsmatrix machen (vll. besser Projection)

D
Djeurissen Themenstarter:in
4 Beiträge seit 2015
vor 8 Jahren

Danke für die Antwort, aber könntest du mir das noch etwas genauer erklären?

Wenn ich zum Beispiel eine ProjectionMatrix nutze (Matrix.CreateOrthographic?),
was muss ich da dann übergeben? Ich dachte zuerst das ich einfach nur die derzeitige Width und Height auf meine Welt übertragen muss.
Sprich:

var res = new Vector2(GraphicsDevice.Viewport.Width / 100, ....Height / 100); //1 Pixel = 0.01 Units also durch 100 teilen
SpriteBatch.Begin(transformMatrix: Matrix.CreateOrthographic(res.X, res.Y, -1, 1);
SpriteBatch.Draw(Texture2D, new Vector2(res.X / 2, res.Y / 2));
SpriteBatch.End();

Aber das hat zur Folge das meine Sprites nur in der linken oberen Ecke gezeichnet werden. Anscheinend verstehe ich da was ziemlich falsch...

5.658 Beiträge seit 2006
vor 8 Jahren

Hi Djeurissen,

für das Zeichnen von Sprites brauchst du keine Projektionsmatrix. Sprites werden ja einfach an eine bestimmte Stelle auf den Bildschirm gezeichnet. Diese Position gibst du bereits als Parameter der SpriteBatch.Draw-Methode an.

Allerdings kann ich auch keinen Zusammenhang zwischen dem Codeausschnitt und deiner ursprünglichen Frage erkennen. Was macht die ConvertUnits-Methode, und wo wendest du sie an? Und was hat das ganze mit der Farseer Physics Engine zu tun?

Christian

Weeks of programming can save you hours of planning

D
Djeurissen Themenstarter:in
4 Beiträge seit 2015
vor 8 Jahren

Okay, ich muss zugeben warscheinlich gebe ich hier derzeit zuwenig Informationen preis^^.

Also FarseerPhysics arbeitet ja mit floats um die Position zu speichern.
Wenn ich nun einen Body an der Position (400Pixel/400Pixel) haben will, muss ich das ganze ja für FarseerPhysics umrechnen.
Dafür gibt es die ConvertUnits Klasse. Zb. ConvertUnits.ToSimUnits(new Vector2(...));
Ich habe mir diese Klasse mal angesehen und die macht nichts weiter als das hier:

(400 : 100 / 400 : 100) = (4/4)

Dasselbe gibt es dann eben noch umgekehrt ConvertUnits.ToDisplayUnits, da wird dann einfach mit 100 mal genommen.

So im Klartext, wenn ich nun zum Beispiel einen Body erstellen will


var position = new Vector2(4, 4); //Angabe in Units
var Body = new Body(position); //Ist natürlich nicht ganz so einfach, aber hier wird der Body einfach nur mit einer Position initalisiert.

//Zeichnen
SpriteBatch.Begin();
SpriteBatch.Draw(IrgendeineTexture, ConvertUnits.ToDisplayUnits(position));
SpriteBatch.End();

Was ich gerne hätte wäre eine Möglichkeit zu sagen das ich beim SpriteBatch.Draw einfach SimUnits mitgebe und diese in DisplayUnits umgerechnet werden, ohne das ich das die ganze Zeit von Hand machen muss. Das zieh sich nämlich durch den ganzen Code und je nachdem wie komplex der Body ist wirds wirklich lustig...

5.658 Beiträge seit 2006
vor 8 Jahren

Wenn du von Konvertierung redest, wäre es natürlich interessant zu wissen, zwischen welchen Einheiten du eigentlich konvertieren willst...

Die Dokumentation sagt dazu:

Farseer Physics works with the KMS (Kilo, Meter, Second) system and does not directly translate to pixels. It is your responsibility to convert between meters and pixels.

Das heißt, die Berechnungen der Physics-Engine erfolgen alle in Meter, und du mußt den Umrechnungsfaktor zwischen deinen Einheiten und Metern angeben, z.B.:

 ConvertUnits.SetDisplayUnitToSimUnitRatio(64f); // 1 meter = 64 pixels

Um das ganze zu vereinfachen, könnte man ein 3D-Koordinatensystem verwenden, bei dem 1 Unit = 1 Meter entspricht. Alternativ kann man den Skalierungsfaktor in der Transformations-Matrix der Szene (WorldMatrix) festlegen.

Das funktioniert aber nicht so einfach, wenn du Sprites zeichnen möchtest, denn da hängt der Umrechnungsfaktor von der Größe der Darstellung auf dem Bildschirm ab. Dann hast du entweder die Möglichkeit, beim Zeichnen der Sprite die richtigen Koordinaten zu berechnen, oder eine TransformationsMatrix (aber keine ProjektionsMatrix!) zu verwenden.

Wenn du allerdings die Konvertierung zwischen den Einheiten überall verstreut im Code hast, dann solltest du deine Software-Architektur überarbeiten (Stichwort: Separation of Concerns).

Christian

Weeks of programming can save you hours of planning

D
Djeurissen Themenstarter:in
4 Beiträge seit 2015
vor 8 Jahren

Naja durch den ganzen Code nun auch wieder nicht, ich habe ja für die Bodys einen Wrapper. Dementsprechend muss ich eben bei jedem Draw für das jeweilige Objekt umkonvertieren.
Aber anscheinend geht es wohl nicht anders, dann werde ich dabei belassen.

Trotzdem danke für die Hilfe^^

742 Beiträge seit 2005
vor 8 Jahren

Warum nicht Projektionsmatrix? Das wäre für mich die logische Wahl.

Man wählt ein Koordinatensystem mit 1 = 1Meter. Dann transformiert die World Matrix vom lokalen Objektkoordinatensystem in das globale (mit gleich bleibender Einheit).

Die View Matrix in den ViewSpace (immer noch gleiche Einheiten).

Die Projektionsmatrix transformiert dann in den Screen Space mit 1Meter = XXX Pixel.

Ich glaube so wäre es wirklich am einfachsten. Alle Berechnungen bis zum Bewegen der Kamera bleiben im gleichen Koordinatensystem.

5.658 Beiträge seit 2006
vor 8 Jahren

Hi malignate,

weil die Projektionsmatrix nur für die (perspektivische oder orthographische) Projektion auf die Kameraebene zuständig ist, und nicht für die Transformation der Szene oder gar einzelner Objekte.

Christian

Weeks of programming can save you hours of planning

742 Beiträge seit 2005
vor 8 Jahren

Im Prinzip gibt es nur eine Transformation, die halt auf 3 Matrizen aufgeteilt wird.

Über die Projektionsmatrix projiziert man vom 3D/2D Koordinaten System der Welt auf das 2D Screen Koordinatensystem und das muss ja nicht unbedingt 1:1 sein, wie man es in 2D oft macht 😉

Beim SpriteBatch ist das natürlich eine andere Sache, da hier die Projektionsmatrix verwaltet wird.

T
154 Beiträge seit 2009
vor 8 Jahren

Ich weiß, etwas spät, aber als weiterer Ansatz der Komfort schafft und etwas nach "Separation of Concerns" geht:

Verwende doch Extension Methods:


public static Vector2 ToDisplay(this Vector2 vector)
{
     return UnitConverter.ToDisplayUnits(vector);
}

Das macht die Verwendung wesentlich einfacher und kompakter und im Falle zusätzlicher Transformationen kannst du das zentral in der EM machen, ohne überall den Code zu ändern (bspw. wenn du den Vector noch normalisieren würdest zuvor - klar macht kein Sinn, aber als Beispiel).


SpriteBatch.Begin();
SpriteBatch.Draw(IrgendeineTexture, position.ToDisplay());
SpriteBatch.End();