===== 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)