Indholdsfortegnelse

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:


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 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:

  1. Find FileSystem-panelet.
  2. Højreklik i den mappe, hvor du vil gemme våbnene.
  3. Vælg at oprette en ny Resource.
  4. Find og vælg typen WeaponData.
  5. Udfyld egenskaberne i Inspector.
  6. 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:

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>("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:

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

  1. Opret klassen WeaponData.cs.
  2. Kompilér projektet.
  3. Opret Knife.tres, Sword.tres og Spear.tres.
  4. Giv hvert våben de værdier, der fremgår af tabellen i afsnit 4.
  5. Tildel et ikon til hvert våben.

Opgave B: Brug ressourcerne

  1. Opret en WeaponPickup-scene med en Area2D og en Sprite2D.
  2. Tilknyt WeaponPickup.cs.
  3. Tildel Sword.tres til egenskaben Data.
  4. Kør spillet, og kontrollér outputtet i Godots Output-panel.
  5. Skift til Knife.tres og derefter Spear.tres.
  6. 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:

  1. Bruge den eksisterende WeaponData-klasse.
  2. Oprette en ny .tres-fil med egne værdier.
  3. Tilknytte ressourcen til en pickup.
  4. 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.

  1. Hvad er en Resource i Godot?
  2. Hvad er forskellen på WeaponData.cs og Sword.tres?
  3. Hvorfor kan vi oprette mange forskellige våben ud fra én klasse?
  4. Hvad er forskellen på en Resource og en Node?
  5. Hvordan får WeaponPickup adgang til våbnets skadeværdi?
  6. Hvad sker der, hvis flere Nodes deler den samme ressourceinstans?
  7. Hvilke fordele giver det at adskille data fra den kode, der anvender dataene?

9. Fejlfinding

WeaponData vises ikke i Godot-editoren

Data er null

Våbnets ikon vises ikke

Opsamlingen virker ikke

Opsummering

En Resource gør det muligt at adskille data fra den kode, der bruger dataene.

I denne guide har vi:

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.