===== Inventory system ===== {{ :godot:inventory_in_use.png?nolink&450 |}} Her vil vi i fælleskab udvikle et inventory system i Godot C#. Krav til vores inventory er følgende: * Statisk antal pladser inventory * 'Stack-able' items. dvs vi skal kunne opsummere items af samme type. * Inventory GUI kan åbnet og lukkes med tasten 'i'. ==== Klasser & ansvar ==== * Item * Inventory * InventoryGui * InvSlotGui * CollectableGui === Item === * Navn * Beskrivelse ---- ==== Opsætning af InputHandler til visning af Inventory ==== * Project -> Project settings -> Input Map * Add new Action fx. 'toggle_inventory' * click plus og vælg tasten 'i'. * Og mens vi er der ..så evt. også et input til at interagere med omverdenen. åbne døre, kister, dialoger..etc. * 'Engage' kalder vi denne. Add action fx. 'player_engage', mappes til tasten 'e'. ---- ==== Item (Resource) ==== using Godot; using System; [GlobalClass] public partial class Item : Resource { [Export] public int ID { get; set; } [Export] public String Name { get; set; } [Export] public Texture2D Icon { get; set; } [Export] public int Quantity { get; set; } [Export] public int MaxStackSize { get; set; } [Export] public bool IsStackable { get; set; } } ==== Collectable ==== {{ :godot:collectablegui.png?nolink&400 |}} using Godot; using System; public partial class Collectable : Node { [Export] public Item item; // Called when the node enters the scene tree for the first time. public override void _Ready() { GetNode("Sprite2D").Texture = item.Icon; } public void OnBodyEntered(Node other) { if (other.Name == "Player") { GD.Print("Collectable was entered by Player."); if (GetNode("/root/World/CanvasLayer/Inventory").Add(this.item) == true) { this.QueueFree(); } } } } ---- ==== InvSlotGui ==== Ansvar: Viser indhold af Item i InventoryGui {{ :godot:inventorygui.png?nolink&400 |}} * Panel ( Visibility, set self-modulate 'A', til 0 ) * Background (Sprite2D) * Set ønskede Texture * Offset -> Centered = Off * Label ( til item coint ) * font: Disable Subpixel Positioning in the font’s import settings. * Juster Panel størrelse til at matche Bagground sprite. * På panelet sættes 'Custom minimum size', til størrelsen af Transform (som nu er samme størrelse som BG sprite). (Dette gøre for at undgå at Slot Panel bliver "klemt". * Gem scene som fx. '**InvSlotUI**' using Godot; using System; public partial class InvSlot : Panel { // Called when the node enters the scene tree for the first time. public override void _Ready() { GetNode("ItemDisplayIcon").Visible = false; // Som udgangspunkt er denne skjult } public void UpdateSlot(Item item) { if (item == null) GetNode("ItemDisplayIcon").Visible = false; else { GetNode("ItemDisplayIcon").Visible = true; GetNode("ItemDisplayIcon").Texture = item.Icon; } } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { } } ---- === Inventory === Indeholder alle vores opsamlede items. Håndtere funktionalitet ift. at tilføje og fjerne Items. ---- === InventoryGUI === {{ :godot:inventory.png?nolink&400 |}} * {{:images:inventory-background.png?linkonly|}} - Til NinePatchRect [[https://www.youtube.com/watch?v=OrTMWb1bN24|Using a NinePatchRect]] * {{ :images:inventory-slot.png?linkonly |}} NinePatchRect er en smart Component der deler en Sprite i 9 felter, og bibeholder aspektet på ydre deområder således vi kan bruge den i forskelle størrelse (fx. baggrund til Inventory, Dialoger eller andet) se nedenstående. Tilføj 8 stk InvSlotUI til denne GridContainer, og juster antal columns på GridContainer til 4. * Vi ønsker GridContainer skal være centeret ift. vores NinePatchRect. I GridContainer, Layout->LayoutMode sæt til 'Anchors' og vælg 'Center-Center'. Nu bør alle inventorySlots være i centrum af NinePatchRect. * Sæt afslutningsvis minimum size på inventory, til det samme som tranform size. ---- ==== Inventory ==== using Godot; using System; public partial class Inventory : Control { public bool IsOpen = true; [Export] public Item[] items; GridContainer gridContainer; override public void _Ready() { gridContainer = GetNode("NinePatchRect/GridContainer"); GD.Print("GridContainer check: " + gridContainer); } /* * Opening and Closing the inventory. * Controlled (input captured) from CanvasLayer Script */ public void OpenInventory() { this.Visible = true; IsOpen = true; } public void CloseInventory() { this.Visible = false; IsOpen = false; } public bool Add(Item item) { GD.Print("Adding item: " + item.Name + " to inventory"); for (int x = 0; x < items.Length; x++) { if (items[x] == null) { items[x] = item; UpdateAllInventorySlots(); return true; } else if ( items[x].ID == item.ID && item.IsStackable == true ) { if (items[x].Quantity < items[x].MaxStackSize) { // Der er noget plads !! if (item.Quantity + items[x].Quantity <= items[x].MaxStackSize) { // Der er plads nok i ét slot. items[x].Quantity += item.Quantity; UpdateAllInventorySlots(); return true; } else { // Fyld Slot op, Juster item quantity, og lad os om der plads andre steder item.Quantity -= (items[x].MaxStackSize - items[x].Quantity); items[x].Quantity = items[x].MaxStackSize; UpdateAllInventorySlots(); return true; } } } } GD.Print("Failed to ADD item: " + item.Name + ", to Inventory.. (maybe its full?)"); return false; } public bool HasItem(Item item) { for (int i = 0; i < this.items.Length; i++) { if (items[i].ID == item.ID) { return true; } } return false; // item was not found } public bool RemoveItem(Item item) { bool success = false; for (int i = 0; i < this.items.Length; i++) { if (items[i].ID == item.ID) { items[i] = null; success = true; } } return success; // item was not removed } /* * Updates all visible Slot GUIs so they are in sync with the items array */ public void UpdateAllInventorySlots() { for (int i = 0; i < items.Length; i++) { var slots = gridContainer.GetChildren(); ((InvSlot)slots[i]).UpdateSlot(items[i]); } } } ==== ItemSlot ==== using Godot; using System; public partial class InvSlot : Panel { // Called when the node enters the scene tree for the first time. public override void _Ready() { GetNode("ItemDisplayIcon").Visible = false; // Som udgangspunkt er denne skjult } public void UpdateSlot(Item item) { if (item == null) GetNode("ItemDisplayIcon").Visible = false; else { GetNode("ItemDisplayIcon").Visible = true; GetNode("ItemDisplayIcon").Texture = item.Icon; } } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { } } ==== Insert scenarier ==== * Der er et tomt inventory slot. * Der er et item af samme type * (og item kan stackes) * Der et et item af samme type (og item kan ikke stackes) * Der er ikke fundet et tomt item slot. (Der er ikke plads)