Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Tobiking

1x Rätselkönig

  • Private Nachricht senden

11

15.03.2016, 14:54

Und was hat das mit Performance zu tun?

In dem Artikel geht es darum das die Performance sinkt, sobald Unity eine Update Methode findet und aufruft. Selbst wenn diese leer ist, da es sich um Aufrufe von C++ -> C# handelt, die einen gewissen Overhead mit sich bringen. Eine Änderung der Performance deutet also darauf hin das eine Update Methode gefunden wurde und aufgerufen wurde.

In dem von mir zitierten Abschnitt sind mehrere Aussagen enthalten. Was genau davon dürfte nicht der Fall sein?

Im Prinzip alle drei: Vorhandensein, Entfernen und Überlagern der initialen Methoden. Wenn es initiale Methoden gäbe, würde der Artikel nicht davor warnen das von einer Basisklasse geerbte leere Methoden durch Unity gefunden und aufgerufen werden. Das wäre bei den initialen Methoden ja schon der Fall. Wenn es keine initialen Methoden gibt, wird initial wohl auch nichts überlagert. Und ob es einen Weg gibt Methoden irgendwie zu markieren, so dass Unity sie nicht findet, halte ich in dem Kontext für eher unwahrscheinlich. Vor allem da sich Unity nicht um Sichtbarkeit der Methoden kümmert, sondern nur auf den Name schaut.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

12

15.03.2016, 18:23

Doch, beim Erzeugen von Scripts wird initial eine Start- und eine Update Methode angelegt. Ein Script würde dann wie folgt aussehen:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

    // Use this for initialization
    void Start () {
    
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}

Außerdem warnt der Artikel nicht nur vor dem Hinzufügen dieser beiden Methoden, sondern vor grundsätzlich allen, die von Unity zu bestimmten Ereignissen aufgerufen werden würden: Update, LateUpdate, FixedUpdate, OnCollisionStay, OnBecameVisible, OnBecameInvisible, OnGui, ...
Entfernt man die Methoden nicht aus der erbenden Klasse, werden die Methoden der erbenden Klasse aufgerufen, nicht aber der Basisklasse. Wenn die entsprechende Methode aber nicht entfernt werden soll, die der Basisklasse aber auf jeden Fall aufgerufen werden soll, muss ein manueller Aufruf (base.Update()) durchgeführt werden.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Tobiking

1x Rätselkönig

  • Private Nachricht senden

13

16.03.2016, 09:47

Das Default Scripttemplate hat die Methoden drin, die man entfernen muss. Da hast du Recht. Ich bin davon ausgegangen das das selbstverständlich ist und es um eine interne Unity Spezialität ging, weil die Antwort von Enguerrand in die Richtung ging.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

14

16.03.2016, 23:24

In einem anderen Thread wäre ich auch tendenziell davon ausgegangen, dass ein bestimmter Wert richtig abgerufen wurde, habe zur Sicherheit aber dennoch nochmal nachgefragt, da ansonsten nichts in Frage kam, und es war tatsächlich der Grund für das bestehende Problem.
Dass der Threadersteller wahrscheinlich nicht ganz Sattelfest in C# steckt, merkt bspw. an der Verwendung des Schlüsselworts new für die Update Methode. Das sorgt dafür, dass Objekte der Klasse nur teilweise Polymorph verwendet werden können. (Speichert man ein Objekt dieser Klasse in einer Variable vom Typ der Oberklasse, wird bei einem Aufruf die Methode der Oberklasse aufgerufen, da kein Überschreiben stattgefunden hat. Deshalb würde man in einem solchen Fall override verwenden.)

Es wäre gut zu wissen, ob Bigabig sich bereits für eine Vorgehensweise entschieden hat (und aus welchem Grund) oder ob noch irgendwo Unklarheiten bezüglich der Vorschläge bestehen.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

15

17.03.2016, 01:12

Erstmal Danke für die zahlreichen Antworten! :thumbsup:


Na um Verhalten austauschbar zu halten gibt es das Strategy Pattern.
Guck dir den Link mal an. Das sollte dein Problem an sich lösen.


Das Strategy Pattern ist tatsächlich genau wonach ich gesucht habe. Ich habe es mir grade durchgelesen und festgestellt, dass ich das Verhalten von meinen Enemys sehr ähnlich austauschbar gemacht habe. Der einzige Unterschied: ich habe abstrakte Klassen, statt Interfaces verwendet. Wahrscheinlich werde ich das demnächst noch abändern.

Wichtig ist vor allem: Composition over Inheritance
Das von Schorsch genannte Pattern ist dabei eine mögliche Lösung, bei Unity würde man am ehesten mit unterschiedlichen Komponenten vorgehen. (Dabei können die Komponenten eine gemeinsame, abstrakte "Basiskomponente" teilen, welche beim Strategy-Pattern der "Strategy" entsprechen würde. In dem Zusammenhang ist das Attribut RequireComponent sehr nützlich.)
Entweder man sorgt über SendMessage für den richtigen Methodenaufruf, oder man sorgt mit dem RequireComponent-Attribut dafür, dass auf jeden Fall eine geeignete Komponente vorhanden ist, ruft diese einmalig mit GetComponent ab (und speichert die Komponente in einem Member) und kann dann direkt auf die entsprechende Methode zugreifen. (Letzteres wäre zu bevorzugen.)


Danke Sacaldur. Genau, mit RequireComponent, GetComponent und entsprechenden Member lässt sich das Pattern gut in Unity umsetzen.


Dass der Threadersteller wahrscheinlich nicht ganz Sattelfest in C# steckt, merkt bspw. an der Verwendung des Schlüsselworts new für die Update Methode. Das sorgt dafür, dass Objekte der Klasse nur teilweise Polymorph verwendet werden können. (Speichert man ein Objekt dieser Klasse in einer Variable vom Typ der Oberklasse, wird bei einem Aufruf die Methode der Oberklasse aufgerufen, da kein Überschreiben stattgefunden hat. Deshalb würde man in einem solchen Fall override verwenden.)


Ebenfalls gut erkannt hast du, dass ich mich mit c# nicht so wirklich gut auskenne... Ich benutze es halt nur um in Unity zu programmieren. Das new vor der Update-Methode entstand übrigens nur weil Unity rumgemeckert hat, dass ich dort ein new hinsetzen solle^^

Vererbung bei MonoBehaviours mit Awake, Start, Update etc. interessiert mich jetzt aber doch nochmal:

Eine Oberklasse für alle Einheiten in meinem Spiel sieht sehr minimiert z.B. so aus:

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
public class NetworkUnit : NetworkBehaviour
{
    // Publics
    public int respawnTime;

    // Network Syncvars
    [SyncVar]
    public int life;

    // Privates
    bool _isAlive = true;
    int _maxLife;

    void Start()
    {
        _maxLife = life;
    }

    [Server]
    public void Hit(int damage)
    {
        life -= damage;
        RpcHit();

        if(life <= 0 && _isAlive)
        {
            _isAlive = false;
            RpcDestroy();
            StartCoroutine(Respawn());
        }
    }

    [Server]
    IEnumerator Respawn()
    {
        yield return new WaitForSeconds(respawnTime);

        _isAlive = true;
        life = _maxLife;
    }

    [ClientRpc]
    void RpcDestroy()
    {
        // Grafische Darstellung der Zerstörung nur auf dem Clienten
    }

    [ClientRpc]
    void RpcHit()
    {
        // Grafische Darstellung eines Treffers nur auf dem Clienten
    }


Alle Einheiten in meinem Spiel haben also eine gewisse Anzahl an Leben und wenn dieses auf bzw. unter 0 fällt wird die Einheit nach einer bestimmten Zeit respawned. Dazu muss in der Start() Methode das maximale Leben abgespeichert werden.

Jetzt möchte ich die Einheit etwas weiter konkretisieren und erstelle ein Enemy. Der Gegner benötigt jetzt weitere Komponenten in seinem Script, die also in der Start Methode via GetComponent geholt werden müssen.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
public class NetworkEnemy : NetworkUnit
{ 
    // Components
    ComponentType component1;
    ...    

    void Start()
    {
        // GetComponent ...
    }
}

So... Sowohl NetworkEnemy, also auch NetworkUnit benutzen die Start-Methode um Zeug zu initialisieren. Wie stelle ich jetzt also sicher, dass beide Start() aufgerufen werden?
Das Problem lässt sich auch auf Update, Awake etc. übertragen. Sagen wir das NetworkUnit bewegt sich jeden Frame und im NetworkEnemy möchte ich zusätzlich jeden Frame die Farbe wechseln (whatever).

Ich denke das Problem sollte klar sein.

Scheinbar ist "new" nicht richtig... eure vorherigen Posts bezüglich Update kann ich leider nicht nachvollziehen.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

16

17.03.2016, 08:42

NetworkUnit: protected void Start() {[...]}
NetworkEnemy: override protected void Start() { base.Start();[...]}

Gerade hier kann man aber schon überlegen, die Besonderheiten des Enemy in eine separate Komponente auszulagern (Stichwort "Composition over Inheritance").
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

17

17.03.2016, 11:26

sollte man bei dem Strategy - Pattern das Suffix: "Strategy" drinne lassen?

also zum Beispiel bei einem Charakter:

C#-Quelltext

1
2
3
4
5
6
IWeaponStrategy
{
   void Use();
}

class SwordStrategy : IWeaponStrategy {...}

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

18

17.03.2016, 11:55

Das kommt darauf an, ob eine solche Benennung Sinn macht. "SwordStrategy" macht nur dann Sinn, wenn man auch das Strategy-Pattern im Hinterkopf hat.
Und in gewissem Maße dürfte die Benennung auch Geschmackssache sein. Wenn man unterschiedliche Leute fragt, wird man meist auch unterschiedliche Antworten erhalten.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

19

17.03.2016, 12:54

Es ist irgendwo schon sinnvoll eine feste Benennung zu haben. Ob du jetzt Strategy hinten anhängst oder eine andere Methode wählst bleibt dir überlassen. An sich geht das aber. Du kannst das aber auch von Fall zu Fall unterschiedlich lösen. Möglicherweise macht auch SwordLogic oder vergleichbares Sinn. Eine andere Möglichkeit wäre mit Namespaces zu arbeiten. Du könntest einen Namespace "WeaponStrategies" anlegen und dort deine einzelnen Strategien hinterlegen. Die könntest du dann wieder einfach nach ihren Waffentypen benennen. Ich würde es einfach so lösen wie es dir am besten gefällt. Mit der Zeit wirst du dann schon merken womit du am besten fährst.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

20

17.03.2016, 20:22

Ok.

Ich habe auch eine "ähnliche" Situation:

ich habe eine ObjectLocation- Klasse (die Schreibt Objekte, an eine bestimmte Position an einem Ort => wie in Object Pascal (typisierte Dateien)) und zur Laufzeit soll es möglich sein,
in verschiedenen Locations die Daten zu schreiben

Locations sind z.B

File
Datenbank
Netzwerk
bestimmter Server

etc...

und meine 1.) Frage: ---> Könnte ich hier auch das Strategy Pattern anwenden

2) Frage-----> obwohl der Code sich nicht wirklich unterscheidet, aber ich kann argumentieren, das ich ja nicht endgültig weiß, ob sich die Codelogik des "WriteToLocation(Location bla)" sich nicht doch mal ändern würde und dann hätte ich dank des Patterns ja eigentlich Wiederverwendbarkeit und bessere Wartbarkeit

Was denkt ihr dazu, wie würdet ihr es machen und ich habe es jetzt als Antwort geschrieben, da es ganz Gut zum Topic past

Werbeanzeige