Spielfiguren-Komponente
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 69 von 70.
Aufgabe
EinfachLass uns ein Spielcharakter-System mit dem komponentenbasierten Architekturmuster erstellen! Anstatt starre Vererbungshierarchien zu schaffen, wirst du Charaktere zusammensetzen, indem du unabhängige Komponenten hinzufügst, die spezifische Verhaltensweisen steuern – ein flexibler Ansatz, der in der Spieleentwicklung weit verbreitet ist.
Du wirst deinen Code über fünf Dateien organisieren:
IComponent.cs: Definiere einIComponent-Interface im NamespaceGame.Components. Jede Komponente muss eineUpdate()-Methode haben, die ausgibt, was die Komponente gerade tut.HealthComponent.cs: Erstelle eineHealthComponent-Klasse, dieIComponentimplementiert. Sie sollte die aktuellen und maximalen Lebenspunkte verfolgen, mit Methoden fürTakeDamage(int amount)undHeal(int amount). Die Gesundheit sollte niemals unter 0 sinken oder das Maximum überschreiten. Füge einevent Action<int> HealthChangedhinzu, das ausgelöst wird, wann immer sich die Gesundheit ändert, und den neuen Gesundheitswert übergibt. DieUpdate()-Methode sollte den aktuellen Gesundheitsstatus ausgeben.InventoryComponent.cs: Erstelle eineInventoryComponent-Klasse, dieIComponentimplementiert. Sie verwaltet eine Liste von Item-Namen mit den MethodenAddItem(string item)undRemoveItem(string item)(gibt einenboolzurück, der den Erfolg anzeigt). DieUpdate()-Methode sollte 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>()wobei T : IComponent – gibt die erste Komponente vom Typ T zurück, oder null, falls keine gefunden wurdeUpdateAll()– ruftUpdate()für jede hinzugefügte Komponente auf
Program.cs: Führe alles zusammen, indem du einen Charakter erstellst, Komponenten hinzufügst und Befehle verarbeitest.
Du wirst die folgenden Eingaben erhalten:
- Name des Charakters
- Anfängliche 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 Event):
Health changed: {newHealth} - Beim Hinzufügen eines Items:
Added: {itemName} - Beim Entfernen eines Items:
Removed: {itemName}oderItem not found: {itemName} Update()der HealthComponent:Health: {current}/{max}Update()der InventoryComponent:Inventory: {item1}, {item2}, ...oderInventory: empty
Wenn die Eingaben zum Beispiel wie folgt lauten:
Hero
100
7
add_item
Sword
add_item
Shield
damage
30
heal
10
remove_item
Potion
remove_item
Sword
updateSollte die Ausgabe so 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 nicht von etwas Speziellem erbt – er hält einfach Komponenten, die das Verhalten bereitstellen. Die Methode GetComponent<T>() verwendet Generics für einen typsicheren Abruf, und Events benachrichtigen, wenn sich die Gesundheit ändert. Dieser Kompositionsansatz ermöglicht es dir, Komponenten frei zu 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. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten