Komponente für Spielfiguren
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 69 von 70.
Aufgabe
EinfachErstellen wir ein Spielcharaktersystem mit dem komponentenbasierten Architekturpattern! Anstatt starre Vererbungshierarchien zu erstellen, setzt du Charaktere zusammen, indem du unabhängige Komponenten anfügst, die bestimmte Verhaltensweisen übernehmen – ein flexibler Ansatz, der in der Spieleentwicklung weit verbreitet ist.
Du organisierst deinen Code über fünf Dateien:
IComponent.cs: Definiere einIComponent-Interface im NamespaceGame.Components. Jede Komponente muss über eineUpdate()-Methode verfügen, die ausgibt, was die Komponente gerade tut.HealthComponent.cs: Erstelle eineHealthComponent-Klasse, dieIComponentimplementiert. Sie soll die aktuellen und maximalen Lebenspunkte verfolgen, mit den MethodenTakeDamage(int amount)undHeal(int amount). Die Lebenspunkte dürfen niemals unter 0 oder über das Maximum steigen. Füge einevent Action<int> HealthChangedhinzu, das bei jeder Änderung der Lebenspunkte ausgelöst wird und den neuen Lebenspunktewert übergibt. DieUpdate()-Methode soll den aktuellen Lebenspunktestatus ausgeben.InventoryComponent.cs: Erstelle eineInventoryComponent-Klasse, dieIComponentimplementiert. Sie verwaltet eine Liste von Gegenstandsnamen mit den MethodenAddItem(string item)undRemoveItem(string item)(gibt einboolzurück, das den Erfolg angibt). DieUpdate()-Methode soll den Inhalt des Inventars ausgeben.Character.cs: Erstelle eineCharacter-Klasse im NamespaceGame, die eine Sammlung von Komponenten enthält. Dein Charakter benötigt eineName-Eigenschaft und diese Methoden:AddComponent(IComponent component)– fügt dem Charakter eine Komponente hinzuGetComponent<T>()where T : IComponent – gibt die erste Komponente vom Typ T zurück oder null, wenn keine gefunden wurdeUpdateAll()– ruftUpdate()für jede angefügte Komponente auf
Program.cs: Führe alles zusammen, indem du einen Charakter erstellst, Komponenten anfügst und Befehle verarbeitest.
Du erhältst die folgenden Eingaben:
- Name des Charakters
- Initiale maximale Gesundheit für die HealthComponent
- Anzahl der zu verarbeitenden Befehle
- Für jeden Befehl: die Aktion gefolgt von den relevanten Daten
Befehlsformate:
damage
{amount}
heal
{amount}
add_item
{itemName}
remove_item
{itemName}
updateAusgabeformate:
- Wenn sich die Gesundheit ändert (über das Ereignis):
Health changed: {newHealth} - Beim Hinzufügen eines Gegenstands:
Added: {itemName} - Beim Entfernen eines Gegenstands:
Removed: {itemName}oderItem not found: {itemName} Update()von HealthComponent:Health: {current}/{max}Update()von InventoryComponent:Inventory: {item1}, {item2}, ...oderInventory: empty
Wenn die Eingaben beispielsweise folgendermaßen aussehen:
Hero
100
7
add_item
Sword
add_item
Shield
damage
30
heal
10
remove_item
Potion
remove_item
Sword
updateDie Ausgabe sollte folgendermaßen aussehen:
Added: Sword
Added: Shield
Health changed: 70
Health changed: 80
Item not found: Potion
Removed: Sword
Health: 80/100
Inventory: ShieldBeachte, dass der Charakter nichts Besonderes erbt – er enthält einfach Komponenten, die Verhalten bereitstellen. Die Methode GetComponent<T>() verwendet Generics für eine typsichere Abfrage, und Ereignisse benachrichtigen dich, wenn sich die Gesundheit ändert. Mit diesem Kompositionsansatz kannst du Komponenten frei kombinieren!
Probier es selbst
using System;
using Game;
using Game.Components;
class Program
{
public static void Main(string[] args)
{
// Charakternamen lesen
string characterName = Console.ReadLine();
// Anfängliche maximale Gesundheit lesen
int maxHealth = Convert.ToInt32(Console.ReadLine());
// Anzahl der Befehle lesen
int numCommands = Convert.ToInt32(Console.ReadLine());
// TODO: Erstelle einen Character mit dem angegebenen Namen
// TODO: Erstelle HealthComponent mit maxHealth und InventoryComponent
// TODO: Abonniere das HealthChanged-Event, um Folgendes auszugeben: Health changed: {newHealth}
// TODO: Füge beide Komponenten zum Charakter hinzu
// TODO: Verarbeite jeden Befehl
for (int i = 0; i < numCommands; i++)
{
string command = Console.ReadLine();
// TODO: Behandle jeden Befehlstyp:
// - "damage": Menge lesen, TakeDamage auf der HealthComponent aufrufen
// - "heal": Menge lesen, Heal auf der HealthComponent aufrufen
// - "add_item": Item-Namen lesen, AddItem auf der InventoryComponent aufrufen
// - "remove_item": Item-Namen lesen, RemoveItem auf der InventoryComponent aufrufen
// - "update": UpdateAll auf dem Character aufrufen
}
}
}
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler