Auf das Thema antworten  [ 5 Beiträge ] 
Die Draw 2D Bibliothek 
Autor Nachricht
Administrator
Benutzeravatar

Registriert: Sa 15. Dez 2012, 19:15
Beiträge: 137
Wohnort: Karlsruhe
Mit Zitat antworten
Hallo Leute!

Im Laufe des Projektes "Tactical Dungon Master" ist unter anderem die Draw2D-Bibliothek entstanden.
Die dabei verwendeten Methoden möchte ich hier nun mit Code veröffentlichen.

Sie bietet primär für drei Anwendungsbereiche sehr hilfreiche Methoden:
  1. Kollisionsberechnung
  2. Drehen von Bildern und Polygonen
  3. (Dynamische) Bewegung

Zur effizienten Kollisionsberechnung wird es von mir später noch einen ausführlichen Beitrag in einem anderem Thema in den C# Bibliotheken geben.

Zum Download, Einbindung der Bibliothek und zur Dokumentation


Aber nun zur Draw 2D Bibliothek:

Folgende Namespaces werden inkludiert:
Code:
 
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
 


So 16. Dez 2012, 01:35
Diesen Beitrag melden
Profil Website besuchen
Administrator
Benutzeravatar

Registriert: Sa 15. Dez 2012, 19:15
Beiträge: 137
Wohnort: Karlsruhe
Mit Zitat antworten
Für die Kollisionsberechnung gibt es vier Methoden + Überladungen, zwei Hilfsmethoden und eine Sortiermethode (Comparer):

Distance
Berechnet die Distanz zwischen zwei Punkten
Code:
 
public static double Distance(double x1, double y1, double x2, double y2) { return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2)); }
public static double Distance(PointF point1, PointF point2) { return Distance(point1.X, point1.Y, point2.X, point2.Y); }
public static double Distance(Point point1, Point point2) { return Distance(point1.X, point1.Y, point2.X, point2.Y); }
 


Angle
Berechnet den Winkel von Punkt2 zu Punkt1. Dabei Ist das Ergebnis 0, wenn P2 direkt über P1 liegt, 90 wenn P2 direkt rechts von P1 liegt, 180 wenn P2 direkt unter P1 liegt und 270 wenn P2 direkt links von P1 liegt.
Vergleichbar mit der Navigation auf Schiffen: "Kurs 90°" ist direkt in Richtung Osten

Code:
 
public static double Angle(double x1, double y1, double x2, double y2)
{
   double angle = GetDEG(Math.Atan(Math.Abs(y1 - y2) / Math.Abs(x1 - x2)));
   if (double.IsNaN(angle)) { return 0.00; }
   if (x2 >= x1 &amp;&amp; y2 <= y1) { angle = 90 - angle; }       // 1. Quadrant (rechts, oben)
   else if (x2 < x1 &amp;&amp; y2 < y1) { angle = 270 + angle; }   // 2. Quadrant (links, oben)
   else if (x2 <= x1 &amp;&amp; y2 >= y1) { angle = 270 - angle; } // 3. Quadrant (links, unten)
   else if (x2 > x1 &amp;&amp; y2 > y1) { angle = 90 + angle; }    // 4. Quadrant (rechts, unten)
   return angle;
}
public static double Angle(Point point1, Point point2) { return Angle(point1.X, point1.Y, point2.X, point2.Y); }
public static double Angle(PointF point1, PointF point2) { return Angle(point1.X, point1.Y, point2.X, point2.Y); }
 


PointOfAngle
Gibt den Punkt zurück, der zum angegebenen Winkel und in der angegebenen Entfernung zu Pstart liegt.
Code:
 
public static PointF PointOfAngle(PointF start, double angle, double range)
{
  if (angle == 0 &amp;&amp; range == 0) { return start; }
  PointF dis = PointOfAngleDistance(angle, range);
  return new PointF(start.X + dis.X, start.Y + dis.Y);
}
 


PointOfAngleDistance
Berechnet die Distanz in X- und Y-Wert im angegeben Winkel und zur angegeben Entfernung. Soll heißen, dass ein Punkt2 immer gleich viele X- und Y-Werte weiter rechts/links bzw. runter/hoch zu einem Punkt ist, wenn der Winkel und die Entfernung gleich sind. Und wie viele X- und Y-Werte der gesuchte Punkt nun entfernt ist, gibt diese Methode zurück.
Code:
 
public static PointF PointOfAngleDistance(double angle, double range)
{
  double a = GetRAD(angle);
  float x = (float)(Math.Sin(a) * range);
  float y = (float)(Math.Cos(a) * range);
  return new PointF(x, -y);
}


Hilfsmethoden
Die zwei Hilfsmethoden wandeln DEG (Winkelmaß) in RAD (Bogenmaß) und umgekehrt.
Code:
 
private static double GetRAD(double deg) { return deg / 180.0 * Math.PI; }
private static double GetDEG(double rad) { return rad * 180.0 / Math.PI; }
 


Comparer
Der folgende Comparer wird für eine effiziente Kollisionsberechnung benötigt. Mit ihm kann eine Liste von Punkten sortiert werden. Dabei wird zunächst nach X-Wert, dann nach Y-Wert sortiert.
Code:
 
public static int SortPoints(PointF p1, PointF p2)
{
  if (p1.X < p2.X) { return -1; }
  else if (p1.X > p2.X) { return 1; }
  else
  {
    if (p1.Y < p2.Y) { return -1; }
    else if (p1.Y > p2.Y) { return 1; }
    else { return 0; }
  }
}
 


Ein Comparer gibt drei unterschiedliche Werte zurück:
  • Wert <0 bedeutet: p1 ist kleiner als p2
  • Wert 0 bedeutet: p1 ist genauso groß wie p2
  • Wert >0 bedeutet: p1 ist größer als p2


So 16. Dez 2012, 01:53
Diesen Beitrag melden
Profil Website besuchen
Administrator
Benutzeravatar

Registriert: Sa 15. Dez 2012, 19:15
Beiträge: 137
Wohnort: Karlsruhe
Mit Zitat antworten
Zur Darstellung von 2D-Zeichnungen und Bildern haben sich die folgenden zwei Methoden als sehr nützlich erwiesen:

rotateImage
Gibt das gedrehte Bild zurück.
Code:
 
public static Bitmap rotateImage(Image image, float angle, PointF center)
{
  if (angle == 0) { return new Bitmap(image); }
 
  Bitmap newBitmap = new Bitmap(image.Width, image.Height);
  Graphics g = Graphics.FromImage(newBitmap);
  Matrix m = new Matrix();
  m.RotateAt(angle, center);
  g.Transform = m;
  g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height));
  return newBitmap;
}
public static Bitmap rotateImage(Image image, float angle) { return rotateImage(image, angle, image.Width / 2, image.Height / 2); }
public static Bitmap rotateImage(Image image, float angle, float centerX, float centerY) { return rotateImage(image, angle, new PointF(centerX, centerY)); }
 


rotatePolygon
Dreht das über points beschriebene Polygon um angle am Ankerpunkt anchor und gibt es zurück. Bei mehrmaligen Durchführungen wird das Polygon zunächst mit den ersten, dann mit den nächsten Angaben bearbeitet.
Code:
 
public static List<Point> rotatePolygon(Point anchor, double angle, params Point[] points)
{
  List<Point> l = new List<Point>(points.Length);
  foreach (Point p in points)
  {
    double distanz = Distance(anchor, p);
    double a = Angle(anchor, p);
    a += angle; while (a < 0) { a += 360; } while (a >= 360) { a -= 360; }
    l.Add(Point.Truncate(PointOfAngle(anchor, a, distanz)));
  }
  return l;
}
 
// Für zwei Ankerpunkte:
public static List<Point> rotatePolygon(Point anchor1, double angle1, Point anchor2, double angle2, params Point[] points)
{
  List<Point> t1 = rotatePolygon(anchor1, angle1, points);
  return rotatePolygon(anchor2, angle2, t1.ToArray());
}
 
 
// Für drei Ankerpunkte
public static List<Point> rotatePolygon(Point anchor1, double angle1, Point anchor2, double angle2, Point anchor3, double angle3, params Point[] points)
{
  List<Point> t1 = rotatePolygon(anchor1, angle1, points);
  List<Point> t2 = rotatePolygon(anchor2, angle2, t1.ToArray());
  return rotatePolygon(anchor3, angle3, t2.ToArray());
}
 
 
// Das ganze nochmal für PointF
public static List<PointF> rotatePolygon(PointF anchor, double angle, params PointF[] points)
{
  List<PointF> l = new List<PointF>(points.Length);
  foreach (PointF p in points)
  {
    double distanz = Distance(anchor, p);
    double a = Angle(anchor, p);
    a += angle; while (a < 0) { a += 360; } while (a >= 360) { a -= 360; }
    l.Add(PointOfAngle(anchor, a, distanz));
  }
  return l;
}
 
// Für zwei Ankerpunkte:
public static List<PointF> rotatePolygon(PointF anchor1, double angle1, PointF anchor2, double angle2, params PointF[] points)
{
  List<PointF> t1 = rotatePolygon(anchor1, angle1, points);
  return rotatePolygon(anchor2, angle2, t1.ToArray());
}
 
// Für drei Ankerpunkte:
public static List<PointF> rotatePolygon(PointF anchor1, double angle1, PointF anchor2, double angle2, PointF anchor3, double angle3, params PointF[] points)
{
  List<PointF> t1 = rotatePolygon(anchor1, angle1, points);
  List<PointF> t2 = rotatePolygon(anchor2, angle2, t1.ToArray());
  return rotatePolygon(anchor3, angle3, t2.ToArray());
}
 

_________________
Bei Fragen, Lob, Kritik, Vorschläge, hilfreiche Hinweise oder Alternativvorschläge: Beitrag, neues Thema oder PN.
Für Dinge die diskutiert werden sollten, bitte neues Thema im jeweiligen Forum.
Wenn du nicht weißt wohin: Forum Unsortiert.


So 16. Dez 2012, 02:12
Diesen Beitrag melden
Profil Website besuchen
Administrator
Benutzeravatar

Registriert: Sa 15. Dez 2012, 19:15
Beiträge: 137
Wohnort: Karlsruhe
Mit Zitat antworten
Wenn Controls über das Spielfeld huschen - auch wenn es nur Figuren sind - dann ist es hilfreich dafür eine solide, funktionierende Sammlung an Methoden zu haben, mit der man die Informationen zur Durchführung dieser Bewegungen berechnen kann.

Dafür benutze ich wieder zwei Methoden mit ihren Überladungen:

MoveToDistance
Gibt die Distanz zum Bewegungsziel in X- und Y-Werten, unter Berücksichtigung seiner Geschwindigkeit, seinem Ziel und der Option einer dynamischen Bewegung, an.
Code:
 
public static PointF MoveToDistance(PointF start, float speedX, float speedY, PointF destination, bool dynamic)
{
  // Instanziierung der Ausgabe
  PointF move = new PointF(0, 0);
 
  // Berechnung des X- und Y-Abstandes
  float xAbstand = Math.Abs((start.X - destination.X));
  float yAbstand = Math.Abs((start.Y - destination.Y));
 
  // Ziel nicht verfehlen!
  if (destination.X > start.X) { if (start.X + speedX > destination.X) { move.X = xAbstand; } else { move.X += speedX; } }
  else if (destination.X < start.X) { if (start.X - speedX < destination.X) { move.X = xAbstand * -1; } else { move.X -= speedX; } }
 
  if (destination.Y > start.Y) { if (start.Y + speedY > destination.Y) { move.Y = yAbstand; } else { move.Y += speedY; } }
  else if (destination.Y < start.Y) { if (start.Y - speedY < destination.Y) { move.Y = yAbstand * -1; } else { move.Y -= speedY; } }
 
  // Dynamische Bewegung - nur wenn in X- und Y-Richtung eine Bewegung statt findet
  if (dynamic &amp;&amp; move.X != 0 &amp;&amp; move.Y != 0)
  {
    float xProzent = (Math.Abs(move.X) * 100) / xAbstand;
    float yProzent = (Math.Abs(move.Y) * 100) / yAbstand;
 
    if (xProzent > yProzent) { move.X = move.X * (yProzent / xProzent); move.Y = move.Y; }
    else { move.Y = move.Y * (xProzent / yProzent); move.X = move.X; }
    // Einfache Logik dahinter: Wenn x 30% schafft und y mehr, dann muss y so gedrosselt werden, dass auch y nur 30% schafft. So wird die Bewegung dynamisch.
  }
 
  return move;
}
 
public static PointF MoveToDistance(PointF start, float speed, PointF destination, bool dynamic)
{
  return MoveToDistance(start, speed, speed, destination, dynamic);
}
public static Point MoveToDistance(Point start, int speedX, int speedY, Point destination, bool dynamic)
{
  return Point.Truncate(MoveToDistance(start, (float)speedX, speedY, destination, dynamic));
}
public static Point MoveToDistance(Point start, int speed, Point destination, bool dynamic)
{
  return MoveToDistance(start, speed, speed, destination, dynamic);
}
 


MoveTo
Gibt den Zielpunkt, unter Berücksichtigung seiner Geschwindigkeit, seinem Ziel und der Option einer dynamischen Bewegung, an.
Verwendet immer die Methode MoveToDistance von oben.

Code:
 
public static PointF MoveTo(PointF start, float speedX, float speedY, PointF destination, bool dynamic)
{
  PointF dis = MoveToDistance(start, speedX, speedY, destination, dynamic);
  return new PointF(start.X + dis.X, start.Y + dis.Y);
}
public static PointF MoveTo(PointF start, float speed, PointF destination, bool dynamic)
{
  return MoveTo(start, speed, speed, destination, dynamic);
}
 
public static Point MoveTo(Point start, int speedX, int speedY, Point destination, bool dynamic)
{
  return Point.Truncate(MoveTo(start, (float)speedX, speedY, destination, dynamic));
}
public static Point MoveTo(Point start, int speed, Point destination, bool dynamic)
{
  return MoveTo(start, speed, speed, destination, dynamic);
}
 


Allerdings taugen diese Methoden nicht für eine Bewegung die eine Kollision berücksichtigen muss. Diese müsste man nämlich in die Methoden integrieren. Aber sie bieten schon einmal eine solide Basis.


So 16. Dez 2012, 02:33
Diesen Beitrag melden
Profil Website besuchen
Administrator
Benutzeravatar

Registriert: Sa 15. Dez 2012, 19:15
Beiträge: 137
Wohnort: Karlsruhe
Mit Zitat antworten
Wer nicht alle Methoden sich mühsam in seinen Code rüber kopieren will, der kann die komplette Bibliothek auch hier herunterladen.

Außerdem gibt es eine kleine Zusammenfassung der Methoden. Zwar ist die Bibliothek komplett dokumentiert, aber irgendwie wird mir das nach dem Importieren im IntelliSence nie angezeigt.

Einbilden einer Programmbibliothek in ein Projekt:
  1. Gehe in den Projektmappen-Explorer wo alle Projektdateien aufgelistet sind und
  2. rechtsklicke auf "Verweise",
  3. wähle "Verweis hinzufügen...",
  4. gehe auf das Tab "Durchsuchen",
  5. wähle die heruntergeladene und entpackte Programmbibliothek "Draw2DLibrary.dll" aus und
  6. klicke auf "OK".

Mit "using Draw2DLibrary" in deinen Code-Dateien kannst du den Namespace der Programmbibliothek importieren und direkt über "Draw2D" auf die statischen Methoden dieser statischen Klasse zugreifen.

Du kannst sie später einfacher hinzufügen, weil sie nach dem ersten Aufrufen im Tab "Aktuell" aufgelistet ist.

Für weitere Informationen zur Programmbibliothek rechtsklicke auf den Eintrag "Draw2dLibrary" in den Verweisen und klicke auf "Im Objektkatalog anzeigen". Es sollte nun die Dokumentation zur Bibliothek angezeigt werden.

Auch empfehle ich die PDF-Dokumentation die Du ebenfalls im Anhang herunterladen kannst.

Noch ein gut gemeinter Rat zum Schluss:
Schreibe in deinen Code als Kommentar woher du den Code hast. Damit du später mal nachschauen kannst, ob es Verbesserungen zu dem Code gibt oder Neues was dich auch interessieren könnte. Ein "//siehe magony.org" reicht vollkommen.

Schöne Grüße,
Magony



Anhang:
Die Draw2D-Bibliothek als Dll-Datei
Die Zusammenfassung zu den Methoden als pdf-Datei

_________________
Bei Fragen, Lob, Kritik, Vorschläge, hilfreiche Hinweise oder Alternativvorschläge: Beitrag, neues Thema oder PN.
Für Dinge die diskutiert werden sollten, bitte neues Thema im jeweiligen Forum.
Wenn du nicht weißt wohin: Forum Unsortiert.


So 16. Dez 2012, 02:58
Diesen Beitrag melden
Profil Website besuchen
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Auf das Thema antworten   [ 5 Beiträge ] 

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast


Du darfst neue Themen in diesem Forum erstellen.
Du darfst Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software
Deutsche Übersetzung durch phpBB.de

Impressum