2013-01-10 14 views
5

Ich brauche Ihre Hilfe. Ich bin dabei, ein Skript zu arrangieren, das verschiedene Bedingungen überprüfen kann, bevor eine Fähigkeit in einem RPG-Spiel ausgeführt werden kann.C#, Unity - Einzelfunktion mit mehreren verschiedenen Objekten

Alle diese Fähigkeiten sind in einzelnen Klassen (Feuerball, Heilen, Gift) alle abgeleitet von einer anderen abstrakten Klasse (Fern Fähigkeit, Heil Fähigkeit, DOT Fähigkeit), die alle zu einer abstrakten Klasse (Fähigkeit) gehören.

Um das Anlegen mehrere Funktionen zu vermeiden, jede einzelne Fähigkeit zu handhaben:

void condition(Fireball f){//test}; 
void condition(Heal f){//test}; 
void condition(Poison f){//test}; 

ich einen einzigen Funktionsaufruf zu schaffen versuchen, die alle Arten von Fähigkeiten zu nehmen.

Bis jetzt ist es mir gelungen, ein Feuerball-Objekt zu erstellen und es an die Funktion zu übergeben.

Fireball _fire = new FireBall(); 
condition(_fire); 

void condition(Ability f){//test} 

Von hier aus kann ich alle öffentlichen Variablen in der Fähigkeit Klasse initialisiert zugreifen, aber ich kann nicht die öffentlichen Variablen in den abgeleiteten Klassen (Ranged Fähigkeit, Heilung Fähigkeit, DOT Fähigkeit) initialisiert zuzugreifen.

Bin ich es, der etwas vergisst, oder sehe ich das aus einer falschen Perspektive? (Ich bin nicht gut darin, Vererbung und abstrakte Klassen zu verwenden.)

+0

Klassisches OOP-Polymorphie/Vererbungs-Problem :) Es ist schwer und macht Spaß, an eine Lösung zu denken, die entweder die Kapselung bewahrt und Komfort nutzt. +1. – dreamzor

+0

Was hat das mit Objective C zu tun? –

+0

Entschuldigung, ich schätze, ich habe es falsch markiert. –

Antwort

8

Ohne weitere Details zu wissen, was die Condition-Funktion tut, haben Sie zwei Möglichkeiten.

One, können Sie so etwas wie

if (f.GetType() == typeof(FireBall)) 
{ 
    fireBall = (FireBall)f; 
    fireBall.FireTheFireBall(); 
} 
else if (f.GetType() == typeof(Heal)) 
... 

Oder Ihre Fähigkeit tun eine abstrakte Activate-Methode haben, die alle abgeleiteten Klassen zu überlasten sind erforderlich:

class Fireball 
{ 
    public override void Activate() 
    { 
     //do fireball specific things 
     this.FireTheFireBall(); 
    } 

    public void FireTheFireBall() {...}  
} 

class Heal 
{ 
    public override void Activate() 
    { 
     //do healing specific things 
     this.ApplyTheBandage(); 
    } 
    ... 
} 

abstract class Ability 
{ 
    public abstract void Activate(); 
} 

void condition(Ability f){ 
    f.Activate(); //runs the version of Activate of the derived class 
} 

Dann jede Sache, die funktioniert mit einer Fähigkeit kann someAbility.Activate() aufrufen und die von der abgeleiteten Klasse bereitgestellte Implementierung wird ausgeführt.

Sie sollten auch auf Schnittstellen studieren, die wie abstrakte Klassen sind. Der Vorteil von Schnittstellen besteht darin, dass Sie mehrere davon implementieren können, während Sie nur von einer abstrakten Basisklasse geerbt werden. Denken Sie an eine IKnob-Schnittstelle mit Turn- und Pull-Funktionen. Möglicherweise haben Sie eine Drawerklasse, die IKnob, eine Door-Klasse, eine TrappedDoor-Klasse implementiert, die Turn implementiert und einen Trap aktiviert. Ein Spieler geht zu einer Tür auf und trifft auf die Verwendung Taste darauf, und Sie passieren in die offene Funktion das Objekt, Open (IKnob Knopf)

void Open(IKnob knob) 
{ 
    knob.Turn(); 
    knob.Pull(); 
} 

class TrappedDoor:IKnob,IMaterial,ISomethingElse,IHaveTheseOtherCapabilitiesAsWell 
{ 
    private bool TrapAlreadySprung{get;set;} 
    //more complex properties would allow traps to be attached either to the knob, or the door, such that in one case turning the knob activates the trap, and in the other, Pull activates the trap 
    public Turn() { 
    if(! TrapAlreadySprung) 
    { 
     MessageBox("You hit your head, now you're dead"); 
    } 
    } 
} 

Es gibt Möglichkeiten, zu überprüfen, ob etwas eine Schnittstelle hat, also wenn Wenn ein Spieler auf ein Objekt zugeht und versucht, mit ihm zu sprechen, können Sie überprüfen, ob das Objekt die ICanTalk-Schnittstelle hat. Wenn dies der Fall ist, wird object.GetReply ("Hello") aufgerufen und das Objekt kann antworten. So können Sie sprechen Türen und Felsen, wenn Sie es wünschen. Sie erhalten all Ihren Code, der das Sprechen mit Dingen/das Anzeigen von Antworten usw. behandelt, die mit ICanTalk-Schnittstellenmethoden arbeiten, und dann können andere Klassen ICanTalk implementieren und sie entscheiden jeweils, wie sie reagieren, um mit ihnen gesprochen zu werden. Dieses Konzept wird als "Trennung von Problemen" bezeichnet und hilft Ihnen, mehr wiederverwendbaren Code zu erstellen.

Die wichtige Sache ist, dass Sie ein Stück Code, einen Algorithmus, eine Funktion usw. schreiben können, die nur mit dieser Schnittstelle funktionieren, und auf diese Weise können Sie diese Schnittstelle dann verwenden, sobald Sie den Code mit der Schnittstelle arbeiten jede Klasse, und diese Klasse kann den bestehenden Code nutzen.

I.e. Ihre condition -Funktion, wenn es in einer IAbility-Schnittstelle dauerte, sobald Sie diesen Code arbeiten, dann kann jede Klasse, die Sie erstellen, die IAlability implementiert, an die Bedingungsfunktion übergeben werden. Die Bedingungsfunktion ist dafür verantwortlich, das zu tun, was auch immer sie tun soll, und die Klasse, die die Flugfähigkeit implementiert, kümmert sich innerhalb der implementierten Methoden um alles, was für sie spezifisch ist.

Natürlich müssen die Klassen, die die abstrakte Klasse oder Schnittstelle implementieren, die erforderlichen Methoden implementieren, sodass Sie manchmal das Gefühl haben, dass Sie Code duplizieren. Wenn Sie beispielsweise ähnliche Klassen wie TrappedDoor und Door haben, verhält sich TrappedDoor möglicherweise genauso wie eine normale Tür, wenn die Falle nicht gesetzt ist. Sie können also in diesem Fall entweder von Door erben oder eine private Door-Eigenschaft haben (bekannt als "Komposition"). Wenn der Trap bereits gefedert ist, können Sie die Basis-Door-Klasse oder die private Door-Eigenschaft aufrufen und Call. Turn, so dass Sie einfach das Standardverhalten einer regulären Tür wiederverwenden, falls der Trap nicht aktiv ist.

Test if object implements interface

Ich persönlich verwende meist Schnittstellen und Zusammensetzung, anstelle der Vererbung. Nicht diese Vererbung ist schrecklich, aber Vererbungshierarchien können schnell sehr kompliziert werden.

+0

Danke für die Antwort. Ich bin mir nicht sicher, wie der zweite Vorschlag funktioniert, aber ich denke, der erste Vorschlag könnte funktionieren. Ich werde es so bald wie möglich untersuchen, und ich werde zurück melden :) –

+0

@Marc aktualisiert, um das zweite Beispiel zu erweitern. – AaronLS

+1

Das zweite Beispiel sieht aus wie eine viel vernünftige Idee, die ich vermute ... :) –

Verwandte Themen