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

1

11.03.2016, 18:16

[C#] Vererbung und Methodenaufrufe

Hey Leute,

Ich bin heute auf folgendes Problem gestoßen:

Ich habe eine abstrakte Klasse geschrieben, welche alle paar Sekunden schießt. Wie genau Sie nun schießt soll dann die erweiternde Klasse implementieren.

Das ganze sieht ca so aus:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected void Update()
    {
        if (life <= 0)
            return;

        if (_shootingTimer <= 0)
        {
            _shootingTimer = firerate;
            Shoot();
        }

        if (_shootingTimer > 0)
            _shootingTimer -= Time.deltaTime;
    }

    public abstract void Shoot();


Die erweiternde Klasse soll nun also Shoot implementieren, und schießt dann alle paar Sekunden automatisch :D

C#-Quelltext

1
2
3
4
    public override void Shoot()
    {
        // SHOOT!! :D
    }


Das blöde an der Sache ist, dass nun die Update Methode in der erweiternden Klasse, die der Elternklasse überschreibt, daher ist folgender Code notwendig:

C#-Quelltext

1
2
3
new void Update () {
    base.Update();
}


Und genau das stört mich. Wie kann ich einem zukünftigen Benutzer meiner Elternklasse sagen, dass er die Eltern-Update-Methode aufrufen muss? Oder ist es in dem Fall vielleicht gar nicht so schlau mit abstrakten Klassen zu arbeiten? Lässt sich das mit Interfaces vllt. besser lösen?

Mein Ziel ist es halt, eine Art "Template-Gegner" zu erstellen, welchen ich dann verschiedene Angriffs- und Bewegungsmuster geben kann. (Momentan bin ich halt bei den Angriffsmuster^^)

Achso ich benutze übrigens Unity & C# für die Programmierung.

Würde mich über Vorschläge freuen,
Bigabig

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Bigabig« (11.03.2016, 18:21)


2

11.03.2016, 20:09

1)

[Meines Erachtens] ist das kein gutes Design, wenn du versuchst irgendwelche Grundlegen Renderfunktionen irgendwelchen Klassen überschreiben zu lassen. Also sowas wie Update() und Start(); Das sollte möglichst im Skript bleiben.

2)
Wenn du "scriptest" dann schreibe da direkt die benötigte Codelogik rein und verzichte, also zumin. in Unity3D auf Extra Kichtel Michtel Vererbungen, denn das m.M nach läuft anders als in gewöhnlicher Entwicklung.

3)
Wenn du es aber unbedingt machen willst, würde ich sagen, das du ein Interface definierst, das die benötigten Fähigkeiten BESCHREIBT:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Interface IShoot
{
   void Shoot(); // jede Klasse, kann diese Fähigkeit dann für sich selbst spezifisch definieren,
}
public Interface IMove
{
   Vector2D/3D Move(); // jede Klasse, kann diese Fähigkeit dann für sich selbst spezifisch definieren,
}

class Gegner : IShoot, IMove
{
   public void Shoot()
   {...}

   public Vector2D/3D Move()
   {
      //Bewege Gegner und gebe die neue Position zurück
      return new Vector2D/3D(...);
   }
}


Hoffe ich konnte etwas helfen und wenn ich mich dem Design in Unity3D irren sollte, korrigiert mich^^^

Mfg

JP

P.s Ich würde dir lieber sowas empfehlen:

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
class Move : Monobehaviour
{

   void Update()
   {
      transform.position = Move(); // oder irgendwie, das du das ganze auch am Objekt sehen kannst, war lange nicht mehr in Unity3D unterwegs^^
   }

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

   Vector3D Move()
   {
      //...z,B 
      transform.position.X += 120;
      transform.position.Y += 90;
      transform.position.Z += 10; 
   } //Vector3D als Beispiel

}

Und dann hängst du dieses Skript, an deinem Gegner-Gameobject (was du im unityeditor hast), einfach an.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »JungleProgger« (11.03.2016, 20:21)


3

12.03.2016, 13:08


P.s Ich würde dir lieber sowas empfehlen:

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
class Move : Monobehaviour
{

   void Update()
   {
      transform.position = Move(); // oder irgendwie, das du das ganze auch am Objekt sehen kannst, war lange nicht mehr in Unity3D unterwegs^^
   }

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

   Vector3D Move()
   {
      //...z,B 
      transform.position.X += 120;
      transform.position.Y += 90;
      transform.position.Z += 10; 
   } //Vector3D als Beispiel

}

Und dann hängst du dieses Skript, an deinem Gegner-Gameobject (was du im unityeditor hast), einfach an.


Aber bei sagen wir mal 30 verschiedenen Bewegungsabläufen von Gegnern, kann ich doch nicht jedes mal das immer gleich bleibende Update und Start kopieren. Das kann doch kein gutes Design sein.

Bei Interfaces habe ich das Problem, dass ich auf wichtige Informationen, wie transform.position, this.gameObject usw. usw., nicht zugreifen kann...

Ich weiß echt nicht wie ich das am geschicktesten lösen kann :/

Superwayne

Treue Seele

Beiträge: 242

Beruf: Student & App Entwickler (Xamarin)

  • Private Nachricht senden

4

12.03.2016, 13:33

Aber bei sagen wir mal 30 verschiedenen Bewegungsabläufen von Gegnern, kann ich doch nicht jedes mal das immer gleich bleibende Update und Start kopieren. Das kann doch kein gutes Design sein.

Wenn du 30 verschiedene Bewegungsabläufe hast, sollten das auch 30 verschiedene Komponenten (Skripte) sein. Wenn jedoch zwei gleich sind, verwendest du die gleiche Komponente erneut.
Ich bezweifle jedoch, dass so viele verschiedene Bewegungsabläufe nötig sind (bei kleineren Spielen). Meistens benutzen mehrere Gegner das gleiche Bewegungsskript, dass dann über Parameter wie Geschwindigkeit, Sprunghöhe, etc. individualisiert wird. Nur musst du natürlich z.B. die Bewegungskomponente von der Schusskomponente trennen. Sonst lässt sich das natürlich nur schwer mehrmals verwenden.

5

12.03.2016, 16:08

Es muss doch eine Möglichkeit geben zu verhindern 30 mal diesen Code in 30 verschiedenen Dateien zu haben:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void Update()
    {
        if (life <= 0)
            return;

        if (_shootingTimer <= 0)
        {
            _shootingTimer = firerate;
            Shoot();
        }

        if (_shootingTimer > 0)
            _shootingTimer -= Time.deltaTime;
    }


Wie gesagt, ich suche nach einer Möglichkeit das Verhalten austauschbar zu machen. Am besten noch zur Laufzeit, damit Gegner zwischen verschiedenen Bewegungsmustern / Schussmustern wechseln können.

6

12.03.2016, 16:59


Das blöde an der Sache ist, dass nun die Update Methode in der erweiternden Klasse, die der Elternklasse überschreibt, daher ist folgender Code notwendig:

C#-Quelltext

1
2
3
new void Update () {
    base.Update();
}


Und genau das stört mich. Wie kann ich einem zukünftigen Benutzer meiner Elternklasse sagen, dass er die Eltern-Update-Methode aufrufen muss? Oder ist es in dem Fall vielleicht gar nicht so schlau mit abstrakten Klassen zu arbeiten? Lässt sich das mit Interfaces vllt. besser lösen?

Mein Ziel ist es halt, eine Art "Template-Gegner" zu erstellen, welchen ich dann verschiedene Angriffs- und Bewegungsmuster geben kann. (Momentan bin ich halt bei den Angriffsmuster^^)


Ich verstehe nicht warum für dieses Ziel ist ein verbergen der geerbten Update Methode per 'new' nötig sein soll. Was spricht dagegen die für die erbende Klasse spezifischen Teile von Update in einer anderen überschreibbaren Methode unterzubringen?


Achso ich benutze übrigens Unity & C# für die Programmierung.


Kommt die Einschränkung das 'Update' überschrieben werden muss von Unity?

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

7

12.03.2016, 18:26

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.
„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.“

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

8

14.03.2016, 17:37

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.)


Das Problem mit der Update Methode könnte folgendes sein: Jedes Skript beinhaltet standardmäßig bereits eine Start- und Update-Methode mit Standard-Sichtbarkeit. Entfernt man diese nicht, überlagern sie die entsprechenden Methoden der Basisklassen.
Man müsste diese leeren Methoden also nur entfernen, damit es wie erwartet funktioniert.
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

9

14.03.2016, 20:32

Das Problem mit der Update Methode könnte folgendes sein: Jedes Skript beinhaltet standardmäßig bereits eine Start- und Update-Methode mit Standard-Sichtbarkeit. Entfernt man diese nicht, überlagern sie die entsprechenden Methoden der Basisklassen.
Man müsste diese leeren Methoden also nur entfernen, damit es wie erwartet funktioniert.

Das dürfte nicht der Fall sein. In http://blogs.unity3d.com/2015/12/23/1k-update-calls/ wird auf die Performanceinbußen eingegangen die man hat, wenn man unnötig die Update Methode definiert. Dort steht aber auch das von Außerhalb der Runtime die Update Methode gesucht und aufgerufen wird. Das kann durchaus den normalen C# Regeln widersprechen. Ich bin auf den Fall aber bisher noch nicht gestoßen, da ich relativ wenig mit den Message Methoden arbeite.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

10

15.03.2016, 12:27

In dem von mir zitierten Abschnitt sind mehrere Aussagen enthalten. Was genau davon dürfte nicht der Fall sein? Und was hat das mit Performance zu tun?
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Werbeanzeige