Entwickler-Ecke

Alle Sprachen - Alle Plattformen - Unityähnliche Transform-Hierarchie


Kasko - Mi 09.01.19 20:32
Titel: Unityähnliche Transform-Hierarchie
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:


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:


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);
}


Delete - Do 10.01.19 02:36

- Nachträglich durch die Entwickler-Ecke gelöscht -


Kasko - Sa 12.01.19 19: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 - Sa 12.01.19 19: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".