I Part 1 lærte du at bruge Resources til at beskrive forskellige våben. Vi oprettede en klasse, WeaponData, og brugte den til at lave et sværd, en kniv og et spyd.
Men hvad sker der, når vores spil får flere typer genstande?
Forestil dig, at spilleren skal kunne finde og opbevare:
Alle disse genstande er forskellige, men de har også noget til fælles. De kan eksempelvis alle have et navn, et ikon og en beskrivelse.
Hvordan kan vi organisere vores kode, så vi ikke skal gentage de samme egenskaber i alle vores klasser?
Her skal vi bruge nedarvning.
Når du er færdig med denne guide, skal du kunne:
Item-klasse.Item.Forestil dig, at vi opretter en klasse til hver type item.
WeaponItem
WeaponName
Icon
Description
Damage
Range
PotionItem
PotionName
Icon
Description
HealAmount
ArmorItem
ArmorName
Icon
Description
Defense
Der er en del gentagelser. Alle tre klasser har eksempelvis et navn, et ikon og en beskrivelse.
Hvis vi senere vil tilføje en vægt eller en værdi i guld, skal vi ændre flere klasser.
Det er uhensigtsmæssigt, fordi vi gentager den samme struktur flere steder.
Vi vil i stedet samle de fælles egenskaber i én klasse og lade de specialiserede klasser arve dem.
Nedarvning er et princip inden for objektorienteret programmering (OOP).
En klasse kan arve egenskaber og metoder fra en anden klasse.
Vi kalder klassen, der nedarves fra, for basisklassen eller superklassen. Klassen, der arver, kaldes en subklasse.
I vores spil skal Item være basisklassen.
Item
|
+-------+-------+
| | |
Weapon Potion Armor
Alle tre subklasser arver de fælles egenskaber fra Item.
Derudover kan hver subklasse have sine egne egenskaber.
WeaponItem har eksempelvis skade og rækkevidde.PotionItem har eksempelvis en mængde healing.ArmorItem har eksempelvis en forsvarsværdi.Det betyder, at vi kun behøver at definere de fælles egenskaber ét sted.
Vi skal stadig bruge Godot Resources, ligesom vi gjorde i Part 1.
Opret et C#-script med navnet Item.cs.
using Godot; [GlobalClass] public partial class Item : Resource { [Export] public string ItemName { get; set; } = "New Item"; [Export(PropertyHint.MultilineText)] public string Description { get; set; } = ""; [Export] public Texture2D Icon { get; set; } }
Klassen indeholder tre egenskaber, som alle vores items har brug for:
| Egenskab | Formål |
|---|---|
ItemName | Genstandens navn. |
Description | En beskrivelse af genstanden. |
Icon | Et ikon, der kan vises i inventory. |
Bemærk, at Item arver fra Resource. Det betyder, at vi kan oprette og gemme konkrete items som .tres-filer.
Vi har nu defineret de fælles egenskaber for alle items i spillet.
Vigtigt: Item er vores fælles basisklasse. Den beskriver, hvad alle items har til fælles, men ikke hvad der gør et våben forskelligt fra en potion.
Nu skal vi oprette en specialiseret klasse til våben.
Opret et nyt C#-script med navnet WeaponItem.cs.
using Godot; [GlobalClass] public partial class WeaponItem : Item { [Export] public int Damage { get; set; } = 10; [Export] public int Range { get; set; } = 40; }
Læg mærke til denne linje:
public partial class WeaponItem : Item
Her fortæller vi, at WeaponItem nedarver fra Item.
Det betyder, at WeaponItem automatisk får adgang til de egenskaber, vi har defineret i basisklassen:
ItemNameDescriptionIconVi behøver altså ikke at skrive dem igen.
Samtidig tilføjer vi to nye egenskaber, der er særlige for våben:
DamageRangeVi har nu en klasse, der beskriver både de generelle egenskaber ved et item og de særlige egenskaber ved et våben.
Nu skal vi oprette en klasse til potions.
Opret et nyt C#-script med navnet PotionItem.cs.
using Godot; [GlobalClass] public partial class PotionItem : Item { [Export] public int HealAmount { get; set; } = 25; }
Denne klasse arver også fra Item.
En potion har derfor automatisk et navn, en beskrivelse og et ikon.
Derudover har den sin egen egenskab:
public int HealAmount { get; set; } = 25;
HealAmount beskriver, hvor mange HP potionen kan genoprette.
Vi behøver ikke at tilføje Damage eller Range, fordi de egenskaber hører til våben og ikke til potions.
Lad os også oprette en klasse til rustninger.
Opret ArmorItem.cs.
using Godot; [GlobalClass] public partial class ArmorItem : Item { [Export] public int Defense { get; set; } = 5; }
ArmorItem arver de fælles egenskaber fra Item og tilføjer sin egen egenskab:
public int Defense { get; set; } = 5;
Nu har vi tre specialiserede item-klasser, der alle bygger på den samme basisklasse.
Nu skal vi bruge klasserne til at oprette konkrete items.
Kompilér projektet, så Godot registrerer de nye klasser.
Opret derefter følgende Resources i Godot-editoren:
| Ressourcefil | Type | Navn | Særlig egenskab |
|---|---|---|---|
Sword.tres | WeaponItem | Sword | Damage: 20 |
Knife.tres | WeaponItem | Knife | Damage: 8 |
HealthPotion.tres | PotionItem | Health Potion | HealAmount: 25 |
IronArmor.tres | ArmorItem | Iron Armor | Defense: 10 |
Når du opretter en ressource af typen WeaponItem, kan du både se de fælles egenskaber fra Item og de særlige egenskaber fra WeaponItem i Inspector.
For PotionItem får du i stedet adgang til HealAmount.
Det er nedarvning i praksis: Subklassen genbruger basisklassens egenskaber og tilføjer sine egne.
Forestil dig, at du skal lave et inventory, der kan indeholde alle fire ressourcefiler.
Hvis inventory kun accepterede WeaponItem, ville du få problemer, når spilleren også skulle kunne opbevare potions og rustninger.
Men alle vores klasser nedarver fra Item.
Derfor kan vi arbejde med dem gennem den fælles type Item.
Eksempelvis kan vi have en liste med Items:
using Godot; using System.Collections.Generic; public partial class InventoryExample : Node { private readonly List<Item> _items = new(); public void AddItem(Item item) { _items.Add(item); GD.Print($"Tilføjet: {item.ItemName}"); } }
Denne klasse er kun et lille eksempel på, hvordan et inventory kan modtage items. Den er ikke et komplet inventorysystem.
Metoden AddItem modtager en parameter af typen Item.
Det betyder, at metoden kan modtage:
AddItem(sword); AddItem(healthPotion); AddItem(ironArmor);
Forudsat at variablerne refererer til de tilsvarende ressourceobjekter, accepteres alle tre typer, fordi de er subklasser af Item.
Vi behøver ikke en separat metode til våben, potions og rustninger.
Det er en af de store fordele ved nedarvning: Vi kan behandle forskellige objekter ens, når de har en fælles basisklasse.
Nu skal du selv udvide systemet.
Item.cs med navn, beskrivelse og ikon.WeaponItem.cs, der nedarver fra Item.PotionItem.cs, der nedarver fra Item.ArmorItem.cs, der nedarver fra Item.Opret mindst fire ressourcefiler:
Giv hvert item et navn, en beskrivelse og et ikon. Udfyld også de særlige egenskaber for den relevante type.
Tag udgangspunkt i InventoryExample fra afsnit 8.
Opret en metode, der kan modtage et Item og udskrive dets navn.
Undersøg, om du kan sende både et WeaponItem, et PotionItem og et ArmorItem til metoden.
Du skal ikke lave et grafisk inventory endnu. Fokus er på klassestrukturen og nedarvning.
Opret en ny klasse, QuestItem, der nedarver fra Item.
Giv den en særlig egenskab, der passer til quest items.
Opret derefter en ressourcefil til en nøgle, som spilleren kan finde i spillet.
Du skal ikke ændre basisklassen Item for at tilføje de særlige egenskaber til QuestItem.
Besvar spørgsmålene med dine egne ord.
WeaponItem fra Item?PotionItem en HealAmount, mens WeaponItem har Damage?Item, også modtage et PotionItem?I Part 1 lærte du at bruge Resources til at beskrive forskellige våben.
I Part 2 har du lært at bruge nedarvning til at organisere flere typer items.
Du har nu:
Item.WeaponItem, PotionItem og ArmorItem.Item.Det vigtigste er, at vi samler fælles funktionalitet i basisklassen og placerer specialiserede egenskaber i subklasserne.
Det giver os en struktur, som vi senere kan bygge videre på, når vi udvikler et rigtigt inventory med slots, stakke, opsamling og brug af items.