====== Brug af Resources i Godot ======
===== Introduktion ======
Forestil dig, at du udvikler et RPG, hvor spilleren kan finde forskellige våben. Et sværd gør 10 i skade, en kniv gør 5, og et spyd har længere rækkevidde.
Hvordan kan vi beskrive alle disse våben uden at skrive en ny C#-klasse for hvert våben?
Her kan vi bruge en **Resource** i Godot.
En Resource er et objekt, der kan indeholde data og gemmes som en fil. Den kan eksempelvis beskrive et våbens navn, skade, rækkevidde og ikon.
Resources kan også bruges til fjender, rustninger, healing items, lyde, teksturer og meget andet.
I denne guide lærer du at:
* Oprette en Resource i C#.
* Oprette forskellige ressourcefiler ud fra den samme klasse.
* Bruge ressourcerne i dine spilscener.
* Forstå forskellen mellem en Resource og en Node.
----
===== 1. Resource kontra Node =====
I Godot bruger vi både Resources og Nodes, men de har forskellige roller.
^ Resource ^ Node ^
| Indeholder eller beskriver data. | Indgår i scenetræet. |
| Kan gemmes som en ''.tres''-fil. | Bruges til at opbygge spilobjekter og scener. |
| Kan deles mellem flere Nodes. | Kan håndtere eksempelvis input, bevægelse og kollisioner. |
| Eksempel: ''WeaponData''. | Eksempel: ''WeaponPickup'' eller ''CharacterBody2D''. |
Forestil dig et sværd i dit spil:
* En **Resource** beskriver sværdet: navn, skade, rækkevidde og ikon.
* En **Node** kan repræsentere sværdet i spillet og håndtere, hvad der sker, når spilleren samler det op.
En Resource behøver ikke at være en del af scenetræet. Den kan bruges af en eller flere Nodes, der har brug for dens data.
----
===== 2. Sådan fungerer en Resource =====
Vi skal bruge tre dele:
^ Del ^ Formål ^ Eksempel ^
| Klasse | Definerer, hvilke egenskaber dataene har. | ''WeaponData.cs'' |
| Ressourcefil | Indeholder værdierne for et bestemt våben. | ''Sword.tres'' |
| Bruger | Læser og anvender dataene. | ''WeaponPickup.cs'' |
Vi kan illustrere det sådan:
WeaponData.cs
|
+-- Sword.tres
|
+-- Knife.tres
|
+-- Spear.tres
|
v
WeaponPickup
|
v
Player / spillet
Vi skriver altså én klasse, men kan oprette mange forskellige ressourcefiler ud fra den.
**Vigtigt:** Det er klassens struktur, vi genbruger. Hver ressourcefil kan indeholde sine egne værdier.
----
===== 3. Opret klassen WeaponData =====
Først skal vi definere, hvilke data et våben skal indeholde.
Opret et nyt C#-script med navnet ''WeaponData.cs''.
using Godot;
[GlobalClass]
public partial class WeaponData : Resource
{
[Export]
public string Name { get; set; } = "Default Weapon";
[Export]
public int Damage { get; set; } = 10;
[Export]
public int Range { get; set; } = 40;
[Export]
public Texture2D Icon { get; set; }
}
Lad os gennemgå de vigtigste dele.
==== Klassen arver fra Resource ====
public partial class WeaponData : Resource
''WeaponData'' arver fra Godots ''Resource''-klasse. Derfor kan vi oprette og gemme objekter af denne type som ressourcer.
==== GlobalClass ====
[GlobalClass]
Attributten gør klassen tilgængelig som en global type i Godot-editoren. Det gør det nemmere at oprette nye ressourcer af typen ''WeaponData''.
==== Export ====
[Export]
public int Damage { get; set; } = 10;
Attributten ''[Export]'' gør egenskaben tilgængelig i Godot-editorens Inspector.
Du kan dermed ændre våbnets skadeværdi uden at ændre C#-koden.
==== Hvorfor bruger vi properties? ====
I C# bruger vi ofte properties med ''get'' og ''set'' til at repræsentere data, som andre dele af programmet skal kunne læse og ændre.
Godot kan eksportere disse properties til Inspector, så længe typen understøttes.
==== Kompilér projektet ====
Når du har oprettet klassen, skal du bygge/kompilere dit C#-projekt.
Godot skal registrere den nye klasse, før du kan oprette ressourcer af typen ''WeaponData''.
Hvis typen ikke vises i editoren, skal du kontrollere, at filnavnet og klassenavnet stemmer overens, og at projektet kompilerer uden fejl.
----
===== 4. Opret tre forskellige våben =====
Nu skal vi bruge klassen til at oprette tre våben.
I Godot-editoren:
- Find FileSystem-panelet.
- Højreklik i den mappe, hvor du vil gemme våbnene.
- Vælg at oprette en ny Resource.
- Find og vælg typen ''WeaponData''.
- Udfyld egenskaberne i Inspector.
- Gem ressourcen som en ''.tres''-fil.
Opret følgende tre ressourcer:
^ Ressourcefil ^ WeaponName ^ Damage ^ Range ^
| ''Knife.tres'' | Knife | 5 | 20 |
| ''Sword.tres'' | Sword | 10 | 40 |
| ''Spear.tres'' | Spear | 8 | 70 |
Tildel eventuelt et ikon til hvert våben.
Du har nu tre forskellige ressourcer, som alle er oprettet ud fra den samme klasse.
==== Hvad har vi opnået? ====
Vi har ikke skrevet tre forskellige klasser. Vi har skrevet én klasse, der beskriver strukturen for et våben.
De tre ressourcefiler indeholder forskellige værdier:
* Kniven gør 5 i skade og har en rækkevidde på 20.
* Sværdet gør 10 i skade og har en rækkevidde på 40.
* Spyddet gør 8 i skade og har en rækkevidde på 70.
Vi kan ændre disse værdier direkte i Inspector.
**Det er en af de store fordele ved Resources:** Vi kan adskille data fra den kode, der bruger dataene.
----
===== 5. Brug ressourcen i spillet =====
Nu skal vi bruge vores ressourcer i en Node.
Vi opretter en ''WeaponPickup'', der repræsenterer et våben, som spilleren kan samle op.
==== Opret scenen ====
Opret en scene med følgende struktur:
WeaponPickup (Area2D)
└── Sprite2D
Opret et C#-script med navnet ''WeaponPickup.cs'', og tilknyt det til ''Area2D''-noden.
==== WeaponPickup.cs ====
using Godot;
public partial class WeaponPickup : Area2D
{
[Export]
public WeaponData Data { get; set; }
private Sprite2D _sprite;
public override void _Ready()
{
_sprite = GetNode("Sprite2D");
BodyEntered += OnBodyEntered;
if (Data == null)
{
GD.PushWarning(
"WeaponPickup mangler en WeaponData-ressource."
);
return;
}
_sprite.Texture = Data.Icon;
GD.Print($"Våben: {Data.Name}");
GD.Print($"Skade: {Data.Damage}");
GD.Print($"Rækkevidde: {Data.Range}");
}
private void OnBodyEntered(Node2D other)
{
if (Data == null)
{
return;
}
if (other is Player player)
{
player.PickupWeapon(Data);
QueueFree();
}
}
}
==== Hvad sker der i koden? ====
**1. Vi eksporterer en reference til ressourcen**
[Export]
public WeaponData Data { get; set; }
Denne property gør det muligt at tilknytte en ''WeaponData''-ressource i Inspector.
Det betyder, at vi kan vælge mellem ''Knife.tres'', ''Sword.tres'' og ''Spear.tres'' uden at ændre scriptet.
**2. Vi bruger dataene**
_sprite.Texture = Data.Icon;
Vi bruger ikondataene fra ressourcen til at vise våbnet i spillet.
På samme måde kan vi læse:
Data.WeaponName
Data.Damage
Data.Range
**3. Vi håndterer opsamling**
if (other is Player player)
{
player.PickupWeapon(Data);
QueueFree();
}
Når spilleren kommer ind i området, sendes ressourcen videre til spillerens ''PickUpWeapon''-metode. Derefter fjernes pickup-noden.
Du skal selv have en passende ''PickUpWeapon(WeaponData data)''-metode i dit ''Player.cs''-script. Metoden skal håndtere, hvad spillet skal gøre med våbnets data. f.eks.
public void PickupWeapon(WeaponData data){
GD.Print("PLAYER PICKED UP WEAPON: "+ data );
}
Husk også, at spillerens collision skal kunne registreres af ''Area2D'', og at scenens collision layers og masks skal være sat korrekt.
==== Tilknyt ressourcen i Inspector ====
Vælg din ''WeaponPickup''-node.
I Inspector finder du egenskaben ''Data''. Her kan du tilknytte en af dine ressourcefiler.
Prøv at oprette tre pickup-scener eller tre instanser, hvor du vælger henholdsvis:
* ''Knife.tres''
* ''Sword.tres''
* ''Spear.tres''
Hver pickup kan nu bruge de data, der hører til det valgte våben.
Du genbruger den samme C#-kode, men ændrer ressourcefilen.
----
===== 6. En vigtig detalje: Delte ressourcer =====
En Resource kan bruges af flere Nodes. Men det er vigtigt at forstå, at flere Nodes som udgangspunkt kan referere til den samme ressourceinstans.
Forestil dig, at to våben-pickups bruger den samme ''Sword.tres''-ressource.
Hvis den ene Node ændrer ''Data.Damage'', kan den anden Node også opleve ændringen, fordi begge refererer til den samme ressourceinstans.
Det er normalt ikke et problem, hvis ressourcen bruges til at beskrive faste våbendata.
Men hvis du vil gemme individuelle værdier for hvert våben under spillet, skal du overveje, om du har brug for separate ressourceinstanser eller en anden datastruktur.
**Husk:** Resources er gode til data, som kan genbruges. De er ikke automatisk separate kopier, blot fordi flere Nodes bruger dem.
----
===== 7. Øvelse: Tre våben – én Resource =====
Nu skal du selv arbejde med Resources.
==== Opgave A: Opret ressourcerne ====
- Opret klassen ''WeaponData.cs''.
- Kompilér projektet.
- Opret ''Knife.tres'', ''Sword.tres'' og ''Spear.tres''.
- Giv hvert våben de værdier, der fremgår af tabellen i afsnit 4.
- Tildel et ikon til hvert våben.
==== Opgave B: Brug ressourcerne ====
- Opret en ''WeaponPickup''-scene med en ''Area2D'' og en ''Sprite2D''.
- Tilknyt ''WeaponPickup.cs''.
- Tildel ''Sword.tres'' til egenskaben ''Data''.
- Kør spillet, og kontrollér outputtet i Godots Output-panel.
- Skift til ''Knife.tres'' og derefter ''Spear.tres''.
- Kontrollér, at navn, skade og rækkevidde ændres uden ændringer i C#-koden.
==== Opgave C: Udvid dit spil ====
Opret et nyt våben, som passer til dit eget spil.
Du skal:
- Bruge den eksisterende ''WeaponData''-klasse.
- Oprette en ny ''.tres''-fil med egne værdier.
- Tilknytte ressourcen til en pickup.
- Undersøge, hvordan våbnets skade og rækkevidde kan bruges i spillets kampsystem.
Du må ikke oprette en ny C#-klasse alene for at repræsentere det nye våben.
===== 8. Kontrolspørgsmål =====
Besvar spørgsmålene med dine egne ord.
- Hvad er en Resource i Godot?
- Hvad er forskellen på ''WeaponData.cs'' og ''Sword.tres''?
- Hvorfor kan vi oprette mange forskellige våben ud fra én klasse?
- Hvad er forskellen på en Resource og en Node?
- Hvordan får ''WeaponPickup'' adgang til våbnets skadeværdi?
- Hvad sker der, hvis flere Nodes deler den samme ressourceinstans?
- Hvilke fordele giver det at adskille data fra den kode, der anvender dataene?
===== 9. Fejlfinding =====
++++ WeaponData vises ikke i Godot-editoren |
Kontrollér følgende:
* Filen hedder ''WeaponData.cs''.
* Klassen hedder ''WeaponData''.
* Klassen arver fra ''Resource''.
* Attributten ''[GlobalClass]'' er tilføjet.
* Projektet er kompileret uden fejl.
++++
++++ Data er null |
Hvis ''Data'' er null, har du sandsynligvis ikke tildelt en ressource i Inspector.
Vælg ''WeaponPickup''-noden, og kontrollér egenskaben ''Data''.
++++
++++ Våbnets ikon vises ikke |
Kontrollér følgende:
* At ressourcen har fået tildelt et ikon.
* At ''Sprite2D'' findes som barn af ''WeaponPickup''.
* At scriptet bruger ''Data.Icon'' til at sætte teksturen.
++++
++++ Opsamlingen virker ikke |
Kontrollér følgende:
* At din spillerklasse hedder ''Player'', eller tilpas typen i koden.
* At ''Player.cs'' indeholder metoden ''PickUpWeapon(WeaponData data)''.
* At ''Area2D'' har de korrekte collision layers og masks.
* At spilleren har en collision, som kan registreres af området.
++++
===== Opsummering =====
En Resource gør det muligt at adskille data fra den kode, der bruger dataene.
I denne guide har vi:
* Oprettet én klasse, ''WeaponData''.
* Oprettet flere ressourcefiler med forskellige våbendata.
* Brugt ressourcerne fra en ''WeaponPickup''-Node.
* Undersøgt forskellen mellem Resources og Nodes.
Den vigtigste pointe er, at **én klassedefinition kan bruges til mange forskellige datasæt**.
Det gør det lettere at tilføje nye våben, ændre værdier og vedligeholde dit spil uden at skulle skrive en ny klasse for hvert våben.