Autor Beitrag
Kasko
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 90

Win 10
C# (VS 2017/19), (Java, PHP)
BeitragVerfasst: Mi 09.01.19 19:32 
In meine Spielebibliothek möchte ich eine ähnliche Hierarchie der Transform-Komponenten einbauen, wie sie in Unity zu finden ist. Dafür hab ich folgende properties:
  • position -> globale Position im Raum (Scale mit einberechnet)
  • localPosition -> relative Position zum Ursprung der Parent (Scale nicht mit einberechnet, also relative Position bei einer globalen Skalierung von 1x)
  • rotation -> globale Rotation im Raum
  • localRotation -> relative Rotation zum Parent
  • scale -> globale Skalierung der Größe und relativen Position aller Children
  • localScale -> lokale Skalierung gegenüber dem Parent
  • parent -> Transform-Komponente des Parent-Objektes, wenn null, dann kein parent -> root
  • children -> Liste aller Transform-Komponenten der direkt untergeordneten Objekte (keine grand children)
  • root -> Transform-Komponente des obersten Objectes der Hierarchie, zu dem diese Transform-Komponente zugehörig ist


Dies habe ich jetzt in einer Transform-Klasse umgesetzt (C#), allerdings bin ich mir nicht sicher ob meine Lösung so effizient ist, und da die Transform-Komponenten mit ihren Informationen als Basis für so ziemlich alles dienen, sollte diese Klasse bis ins kleinste Bisschen optimiert werden, um effizientes und schnelles rendering,etc. zu gewährleisten. Daher wollte ich frage ob ihr irgendwelche Optimierungsvorschläge habt.

Hier die wichtigsten Ausschnitte der Klasse:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
public sealed class Transform : Component, IEnumerable {
  public Vector2 position {
    get => INTERNAL_get_position();
    set { Translate(value - position); }
  }

  public Vector2 localPosition { get; set; }

  public float rotation {
    get => INTERNAL_get_rotation();
    set {
      float delta = value - rotation;
      localRotation += delta;
      Rotate(delta);
    }
  }

  public float localRotation { get; set; }
  public Vector2 globalScale => INTERNAL_get_globalScale();
  public Vector2 scale { get; set; }

  public Vector2 right => Vector2.right * Matrix3x3.GetRotationMatrix(rotation);
  public Vector2 up => Vector2.up * Matrix3x3.GetRotationMatrix(rotation);

  public Transform parent {
    get => parentInternal;
    set { SetParent(value); }
  }

  private Transform parentInternal { get; set; }
  public Transform root => INTERNAL_get_root();
  public State state { get; internal set; }
  internal List<Transform> children = new List<Transform>();

  public void SetParent(Transform parent) {
    this.SetParent(parent, true);
  }

  public void SetParent(Transform parent, bool keepWorldPosition) {
    Vector2 pos = Vector2.zero;

    if (keepWorldPosition)
      pos = position;

    this.parent?.children.Remove(this);

    parent?.children.Add(this);
    parentInternal = parent;

    if (keepWorldPosition) {
      Vector2 vecToNewPos = position - pos;
      Translate(vecToNewPos);
    }
  }

  #region INTERNAL_Get_Set
  private Vector2 INTERNAL_get_position() => parent != null ? localPosition * Matrix3x3.GetScaleMatrix(parent.globalScale) + parent.position : localPosition;
  private float INTERNAL_get_rotation() => parent != null ? localRotation + parent.rotation : localRotation;

  private Vector2 INTERNAL_get_globalScale() {
      Vector2 scale = this.scale;

      for (Transform parent = this.parent; parent != null; parent = parent.parent)
        scale *= Matrix3x3.GetScaleMatrix(parent.scale);

      return scale;
    }

  private Transform INTERNAL_get_root() {
      if (parentInternal == null)
        return this;
      else
        return parentInternal.INTERNAL_get_root();
    }
  #endregion

  public void Translate(Vector2 translation) {
    localPosition += translation;
  }

  public void Translate(float x, float y) {
    Translate(new Vector2(x, y));
  }

  public void Rotate(float angle) {
    localPosition *= Matrix3x3.GetRotationMatrix(angle);
    RotateChildren(angle);
  }

  private void RotateChildren(float angle) {
    foreach(Transform child in this)
      child.Rotate(angle);
  }

  public IEnumerator GetEnumerator() => new TransformEnumerator(this);
}
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Do 10.01.19 01:36 
- Nachträglich durch die Entwickler-Ecke gelöscht -
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 90

Win 10
C# (VS 2017/19), (Java, PHP)
BeitragVerfasst: Sa 12.01.19 18:08 
Das mag sein bzw. dem ist so, kein Frage, aber mir ging es jetzt mehr um Effizienzverbesserungen oder etwas der gleichen und nicht um Nichtbeachtung der standard C#-Namenskonventionen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4436
Erhaltene Danke: 910


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 12.01.19 18:47 
Effizienz kann man Teilausschnitten von Code eher kaum ansehen. Ich würde dir empfehlen dich mit den entsprechenden Performance Tools zu beschäftigen (z.B. dem Performance Profiler in Visual Studio) um die Stellen zu finden wo es sich lohnt Aufwand reinzustecken um sie effizienter zu gestalten.

Nachdem du solche Auswertungen gemacht hast kannst du dir diese Klasse nochmal ansehen (bei einer Transform klasse kann man ja vermuten das sie bezogen auf Performance in so einem System relevant ist, Wissen ist aber immer noch besser als vermuten). Du solltest z.B. Aufrufstatistiken aus diesen Tools bekommen und daran entscheiden können ob es sinnvoller ist bestimmte Berechnungen die du im Moment in gettern hast besser in setter zu verschieben oder ähnliche Optimierungen finden.

Der Hinweis von Frühlingsrolle solltest du nicht so leicht übergehen. Für dich mag es keinen Unterschied machen. Wenn du aber Hilfe möchtest, ob aktiv oder passiv, solltest du dich an die Standards halten. Ich könnte, wenn ich den Codeausschnitt analysieren wollte, um dir zu helfen bei vielen Dingen im Moment nicht sagen ob es jetzt ne Klasse eine Property, eine Variable, ein Feld etc. ist ich müsste es immer erst explizit nachschauen. Wenn es denn Teil des gezeigten Codes wäre. Vieles der Energie die man ins helfen stecken könnte geht so in Transferleistung verloren wobei einige der potenziellen Helfer sich dann möglicherweise denken "ach dann eben nicht".