Jump to content
Unity Insider Forum

Inventar und Objekte


nordseekrabe

Recommended Posts

Moin, liebe Unityfreunde,

mit meinem Inventory kämpfe ich immer wieder und versuche dabei, die Zusammenhänge in Unity besser zu verstehen. Jetzt stellt sich eine neue Frage:

meine Scripte erlauben, ein Objekt in der Szene durch anklicken des Kontext-Buttons "Nimm" in das Display des Inventars zu platzieren, sichtbar sind Image und

Itemname, und das Objekt verschwindet aus der Szene. Allerdings sehe ich im Inspektor unter dem Objektaufnehmen-Script weiterhin eine 0 bei Size von ItemsInInventory ?

Bei dem Versuch, eine Aktion mit der Bedingung, ein bestimmtes Objekt im Inventar vorhanden zu haben, springt das Programm zur Else-Variante "Objekt nicht vorhanden",

obwohl dieses Objekt gleichzeitig im InventarDisplay dargestellt ist. Daraus schließe ich, dass mit dem Aufnehmen tatsächlich nur Image und Itemname ins Inventardisplay

geschoben werden, aber nicht in die List<Item> itemsInInventory gelangen. Dies sollte doch eigentlich die Methode PlaceItemInInventory(GameObject obj) übernehmen ? 

Was mache ich falsch ? Für jeden Hinweis, Tipp bin ich wie immer sehr dankbar. 

Grüße von der Ostseeküste bei herrlichem goldenen Oktober         Nordseekrabbe

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; 
using UnityEngine.SceneManagement;

public class Inventory : MonoBehaviour
{
    private static Inventory m_Instance;
    public static Inventory Instance { get { return m_Instance; } }

    public List<Item> itemsInInventory = new List<Item>();

    public Rect windowRect = new Rect(30, 30, 140, 300);
    public int windowScrollWidth = 140 - 20;
    public int windowScrollHeight = 300 - 30;
    public int windowScrollSize = 50;
    private Vector2 scrollPosition = Vector2.zero;

    private RaycastHit hit;
    private Ray ray;
    
    public bool showInventory = false;


    private void Awake()
    {
        if (m_Instance != null && m_Instance != this)
        {
            Destroy(this.gameObject);
        }
        else
        {
            m_Instance = this;
        }
        DontDestroyOnLoad(gameObject);
    }

    private void OnEnable()
    {
        //tell our "OnLevelFinishedLoading" function to start listening for  scene change as soon as this script is enabled
        SceneManager.sceneLoaded += OnLevelFinishedLoading;
    }

    private void OnDisable()
    {
        //tell our "OnLevelFinishedLoading" function to stop listening for a scene change as soon as this script is diabled.
        // Remember to always have an unsubscription for every delegate you subscribe to !
        SceneManager.sceneLoaded -= OnLevelFinishedLoading;
    }

    void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
    {
        //Debug.Log("Level Loaded: " + scene.name);
        RemoveDublicates();
    }

    private void RemoveDublicates()
    {
        Item[] allItems = FindObjectsOfType<Item>();

        foreach (Item itemInventory in itemsInInventory)
        {
            foreach (Item itemScene in allItems)
            {
                if (itemScene.itemName == itemInventory.itemName)
                {
                    print("Doppeltes Item '" + itemScene.gameObject + "' zerstört");
                    Destroy(itemScene.gameObject);
                }
            }
        }
    }

   

    private void Start()
    {
        for (int i=0; i< itemsInInventory.Count; i++)
        {
            itemsInInventory[i].gameObject.SetActive(false);   // Item deaktivieren, wenn bereits im Inventar
        }

        Item find = itemsInInventory.Find(x => x.itemName.Contains(""));
        if (find != null) print("gefunden");
    }
    

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.I))
           showInventory = !showInventory;

        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    }

    public void PlaceItemToInventar(GameObject obj)
    {
        Item collectedItem = obj.GetComponent<Item>();

        if (collectedItem != null)
        {
            itemsInInventory.Add(collectedItem);
            DontDestroyOnLoad(obj);
            obj.SetActive(false);
        }
    }

    public void RestoreItemFromInventar(int index)
    {
        if (index >= itemsInInventory.Count) return;       // falscher Index
        Item item = itemsInInventory[index];
        item.gameObject.SetActive(true);
        string name = item.gameObject.name;                // Namen zischenspeichern
        GameObject newItemGO = Instantiate(item.gameObject);    // Item duplizieren, damit DontDestroyOnLoad verschwindet
        DestroyImmediate(item.gameObject);
        newItemGO.name = name;                             // Namen zurücksetzen
        itemsInInventory.RemoveAt(index);                  // Item aus dem Inventar nehmen
    }

    public Item FindItemByName(string itemName)
    {
        for (int i = 0; i < itemsInInventory.Count; i++)
        {
            if (itemsInInventory[i] != null)
            {
                if (itemsInInventory[i].GetComponent<Item>().itemName == itemName)
                {
                    return itemsInInventory[i];
                }
            }
        }
        return null;
    }

    private void OnGUI()
    {
        if (showInventory)
            windowRect = GUI.Window(0, windowRect, DoMyWindow, "Inventar");
    }

    void DoMyWindow(int windowID)
    {
        int x = 10;
        int y = 20;
        
        scrollPosition = GUI.BeginScrollView(new Rect(10, 20, windowScrollWidth, windowScrollHeight), scrollPosition,
            new Rect(10, 10, windowScrollWidth + windowScrollSize, windowScrollHeight + windowScrollSize));

        for (int i=0; i<itemsInInventory.Count; i++)
        {
            GUI.Label(new Rect(x + 52, y + 10, 85, 50), itemsInInventory[i].itemName);
            if (GUI.Button(new Rect(x, y+1, 48, 48), itemsInInventory[i].itemImage))
            {
                // Button Listener
                print("Item '" + itemsInInventory[i].name + "' zurückgelegt");
                RestoreItemFromInventar(i);
            }

            y += 50;
        }
        GUI.EndScrollView();
        GUI.DragWindow();
    }
}
                                                
  using UnityEngine;
using System.Collections.Generic;


public class ObjektAufnehmen : MonoBehaviour
{
    [HideInInspector]
    public Aktionsmanager aktionsManager;
    InteraktivesElement interaktivitaet;
    public Room zimmer;
    public List<Item> itemsInInventory = new List<Item>();

    private Inventory myInventory;

    private RaycastHit hit;
    private Ray ray;

    private Item Schraubenzieher;
    
    
    public enum Room
    {
        Arbeitszimmer, Badezimmer, Bibliothek, Diele, Kueche,  Wohnzimmer
	}
    

    void Start()
    {
        aktionsManager = GameObject.Find("Aktionsmanager").GetComponent<Aktionsmanager>();
        interaktivitaet = GetComponent<InteraktivesElement>();

        myInventory = GameObject.Find("Inventar").GetComponent<Inventory>();
        Schraubenzieher = gameObject.GetComponent<Item>();
    }

    void Update()
    {
        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    }

    private void OnMouseDown()
    {
        if (aktionsManager.aktuelleAktion == "Nimm" && interaktivitaet.aufnehmbar &&
            interaktivitaet.ansehbar && interaktivitaet.zuuntersuchen)
        {
            if (zimmer == Room.Diele)           // Zettel , nur Test
            {
                aktionsManager.Infotext.text = "Kann ja nicht schaden, den Zettel zu lesen!";

                if (Physics.Raycast(ray, out hit, 20))
                {
                    myInventory.PlaceItemToInventar(hit.collider.gameObject);
                }
            }
            
            if (zimmer == Room.Kueche)
            {
                aktionsManager.Infotext.text = "Der Schraubenzieher ist unser!";

                if (Physics.Raycast(ray, out hit, 20))
                {
                    myInventory.PlaceItemToInventar(hit.collider.gameObject);
                }
            }
        }
    }
}
 
      
   using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ObjektOeffnen : MonoBehaviour 
{
    [HideInInspector]
    public Aktionsmanager aktionsManager;
    InteraktivesElement interaktivitaet;
    public Room zimmer;
    public List<Item> itemsInInventory = new List<Item>();

    private Inventory myInventory;
    private Item collectedItem;
    private Item Schraubenzieher;

    public enum Room
    {
        Arbeitszimmer, Badezimmer, Bibliothek, Diele, Kueche, Wohnzimmer
    }



    void Start()
    {
        aktionsManager = GameObject.Find("Aktionsmanager").GetComponent<Aktionsmanager>();
        interaktivitaet = GetComponent<InteraktivesElement>();

        Inventory m_Inventory = FindObjectOfType<Inventory>(); 
        itemsInInventory = m_Inventory.itemsInInventory;
        Schraubenzieher = gameObject.GetComponent<Item>();
        
    }

    private void OnMouseDown()
    {
        if (aktionsManager.aktuelleAktion == "Öffne" && interaktivitaet.zuoeffnen && interaktivitaet.einschaltbar)
        {
            if (zimmer == Room.Kueche)   // Dunstabzug
            {
                Debug.Log("wir sind in der Küche");
                
                if (itemsInInventory.Find(x => x.itemName.Contains("Schraubenzieher")))
                {
                    print("Item gefunden!");
                    
                    Vokabelheft.GetComponent<SpriteRenderer>().enabled = true;
                    Vokabelheft.GetComponent<InteraktivesElement>().enabled = true;
                    aktionsManager.Infotext.text = "Mit dem Schraubenzieher gelingt es dir, alle Schrauben zu lösen und" +
                        "in den Dunstabzug hineinzugreifen.";
                }
                else
                {
                    print("Item nicht gefunden!");
                    aktionsManager.Infotext.text = "Nun ja, von ganz alleine fällt der Dunstabzug nicht auseinander.";
                }
            }
        }
    }
}   

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi,

du hast bei ObjektAufnehmen eine unabhängige Liste erstellt, die nichts mit dem Inventar zu tun hat. Diese wird immer Size 0 anzeigen, weil dort nie etwas hinzugefügt wird. Bei ObjektOeffnen wurde es etwas anders gemacht. Dort zeigt das itemsInInventory tatsächlich auf die Liste im Inventar.

Ich würde empfehlen das itemsInInventory und myInventory bei ObjektAufnehmen und ObjektOeffnen zu entfernen. Das Inventory hat bereits ein static Instance-Property mit dem man es von überall aus ansprechen kann mit:

Inventory.Instance

Die Zeile if (itemsInInventory.Find...) lässt sich dann ersetzen zu:

if (Inventory.Instance.FindItemByName("Schraubenzieher") != null) {
  ...
} else {
  ...
}

Die Zeile myInventory.PlaceItemToInventar(hit.collider.gameObject) lässt sich ersetzen zu:

Inventory.Instance.PlaceItemToInventar(hit.collider.gameObject);

So bist du sicher, dass du immer mit diesem einen Inventar arbeitest.

Falls es dann immer noch nicht funktioniert, könnte es auch sein, dass hit.collider.gameObject das falsche Objekt ist. Denn es ist das Objekt auf welchem der Collider ist. Falls der Collider ein untergeordnetes Objekt vom Item ist, dann wäre es das falsche Objekt. Das Find findet dann kein Item mit dem Namen, da das hinzugefügte Objekt gar kein Item-Script hat. Um das zu verhindern, muss jeweils das höchste Objekt in der Hierarchie zum Inventar hinzugefügt werden:

Inventory.Instance.PlaceItemInInventar(hit.transform.root.gameObject);

Noch besser wäre es, wenn das PlaceItemToInventar gar kein GameObject erwartet, sondern direkt ein Item. Dann wäre der Fehler vermutlich schon früher zum Vorschein gekommen ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moin, Jolinah,

danke für die rasche Antwort. Habe die Anregungen übernommen. DerEinsatz von "Inventory.Instance" hat zumindest keine Fehlermeldung gebracht, die Objekte sind im Inventar-Display wie gehabt mit Image und Name vorhanden. Eine Kontrolle  im Inspektor habe ich derzeit nicht. Bei der Bedingungskonstellation wird die neue Syntax( Inventory.Instance.FindItemByName("Schraubenzieher") != null) zwar angenommen, aber das Programm springt weiterhin in den Else-Befehl. D.h. dann wohl, dass das Programm das Objekt nicht im Inventar findet. Könnte das daran liegen, dass die Methode nach einem Item(Itemname) sucht, der Schraubenzieher aber als GameObject deklariert ist ? 

Die Veränderung, wie von Dir empfohlen, von PlaceItemToInventar(Item myItem) klingt gut, aber wie kann Raycast ein Item finden ?

einen schönen Abend wünscht        Nordseekrabbe

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das mit PlaceItemToInventar(Item myItem) ist anscheinend doch nicht nötig. Ich habe gerade gesehen, dass die Methode bereits nach dem Item sucht und dann wirklich das Item zu der Liste hinzufügt und nicht das GameObject. Und da du sagst, das Item werde im Inventar auch angezeigt, kann eigentlich nur mit der Find-Methode etwas nicht stimmen.

Im Inspektor des Items sieht man nichts, aber wenn du das GameObject "Inventar" in der Szene auswählst, solltest du in dessen Inspektor das itemsInInventory sehen.

Du könntest innerhalb FindItemByName noch ein paar Debug.Log-Nachrichten platzieren oder einen Haltepunkt setzen und Debuggen, um zu schauen ob der Check auf null zutrifft und die Methode deshalb null zurückgibt oder ob itemName wirklich den richtigen Namen enthält. Vielleicht liegts ja nur an Gross-/Kleinschreibung oder ähnlichem ;)

 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, stimmt...Im Inspektor erscheint tatsächlich im Slot des Inventory-Scripts als Element 0 der Schraubenzieher im Moment des "Nimm" -Befehls. Jetzt heißt es herausfinden, woran die Methode FindItemByName("Schraubenzieher") scheitert. Morgen ist aber  auch noch ein  Tag, erst einmal vielen Dank!           Nordseekrabbe 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Es war tatsächlich, wie Du vermutest hattest, ein Buchstabenverdreher im ItemScript-Slot. Nachdem behoben, hat alles, wie gewünscht, geklappt. Der Hinweis mit dem "neuen" Inventory war Gold wert; besten Dank dafür. Mit der Aktivierung der Components (enabled = true) oder des gesamten Objektes (SetActive(true))  über eine public Deklaration und dann einer (erzwungenermaßen) Zuweisung im Inspektor bin ich noch nicht glücklich. Versuche einmal, andere Lösungen in den zahlreichen Unity-Tuts zu finden. Aber jetzt bin ich erst einmal wieder einen kleinen Schritt weiter. 

Jetzt hat uns wohl der Herbst gefunden, heute regnet es seit langem wieder einmal in Kiel. Dennoch ein schöner Tag an alle

wünscht    Nordseekrabbe

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da ich immer noch beim Inventory und den Objekten bin, bleibe ich mal in diesem Thread...ich hoffe, das ist okay ? Wenn ich die Szene mit 2 oder mehreren Objekten im Inventar verlasse, bleiben sie lediglich für einen Szenenwechsel vorhanden, beim nächsten Wechsel sind sie gelöscht, mit der Fehlermeldung:

                                              GUI Error: You are pushing more GUIClips than you are popping

Was ist das denn ? Das Inventory-Script ist oben noch vorhanden, habe allerdings die if-Bedingung(nur!! das if) mit GUI.Button und die folgenden Befehle gelöscht (print und RestoreFrom....); hinzugefügt ist das Szenenwechsel-Script, was jedem Objekt, das mit aus der Szene mitgenommen werden soll, zugewiesen bekommt.

using UnityEngine;
using UnityEngine.SceneManagement;

public class Szenenwechsel : MonoBehaviour
{
    [HideInInspector]
    public Aktionsmanager aktionsManager;
    InteraktivesElement interaktivitaet;
    public Room zimmer;
     
    public enum Room
    {
        Arbeitszimmer, Badezimmer, Bibliothek,  Diele, Kueche, Wohnzimmer
    }


    void Start()
    {
        aktionsManager = GameObject.Find("Aktionsmanager").GetComponent<Aktionsmanager>();
        interaktivitaet = GetComponent<InteraktivesElement>();
    }


    void OnMouseDown()
    {
        if (aktionsManager.aktuelleAktion == "Gehe zu" && interaktivitaet.begehbar)
        {
            // die Diele ist der Startraum
            if (zimmer == Room.Arbeitszimmer)
                SceneManager.LoadScene(7);
            if (zimmer == Room.Badezimmer)
                SceneManager.LoadScene(8);
            if (zimmer == Room.Bibliothek)
                SceneManager.LoadScene(9);
            if (zimmer == Room.Kueche)
                SceneManager.LoadScene(29);
            if (zimmer == Room.Wohnzimmer)
                SceneManager.LoadScene(38);
            
        }
    }
}

Inventar als empty GO mit zugewiesenem Inventory-Script und DontDestroyOnLoad(gameObject)-Script ist als Prefab in jeder Szene. Wie komme ich aus diesem Dilemma.

Wie immer, jeder Tipp hilft lernen und weiterkommen; besten Dank und schönes Wochenende         Gruß   Nordseekrabbe

Link zu diesem Kommentar
Auf anderen Seiten teilen

Solche Fehler können oft in OnGUI() passieren. Die Ursache ist, dass OnGUI() mehrmals pro Frame aufgerufen wird, z.B. einmal für eine Layout-Phase und dann um die GUI-Elemente effektiv zu zeichnen. Das heisst, zwischen mehreren OnGUI()-Aufrufen während dem gleichen Frame darf sich der Zustand auf keinen Fall verändern (z.B. die Anzahl Items).

Da es nur bei einem Szenenwechsel passiert, kann es eigentlich auch nur Code sein, der bei einem Szenenwechsel ausgeführt wird. Das einzige was ich da sehe ist RemoveDublicates(). Evtl. kannst du den Aufruf dieser Methode in OnLevelFinishedLoading() mal auskommentieren, um zu schauen ob das Problem dort liegt.

Je nach dem könntest du das GUI auch mit den neuen Canvas- und Image-Objekten etc. machen, statt mit OnGUI(), dort gibt es deutlich weniger Probleme dieser Art. Allerdings funktioniert es auch ganz anders als OnGUI() und da muss man sich natürlich zuerst wieder einarbeiten...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Guten Abend, Jolinah, besten Dank für Deine Antwort. Ich hatte es schon befürchtet, dass das mit dem "alten" OnGUI() zusammenhängen könnte. Das Auskommentieren von

RemoveDublicats() hat leider keinen Erfolg gebracht. Mit dem neuen GUI habe ich schon heftige Kämpfe ausgefochten, es gibt ja eine Vielzahl von (vermeintlichen) Tutorials hierzu.

Meine Unwissenheit und häufig die Kompliziertheit der Beispiele (eigentlich möchte ich doch nur einige Objekte in den Szenen sammeln und dann für irgendwelche Tasks einsetzen 

und nicht 'Stacken' und 'Typisieren' und und..) haben mich bisher (wieder einmal) scheitern lassen. Ein guter Tipp zu einem einfachen(!!!) Inventory wäre also eine sehr große Hilfe !

Euch allen wünsche ich eine gute neue Woche und grüße wie immer von der Ostseeküste                    Nordseekrabbe

Link zu diesem Kommentar
Auf anderen Seiten teilen

Inventar ist halt keine der ganz einfachen Aufgaben mehr. Du musst dich darauf einstellen, dich durch gewissen Dinge erstmal durchzufuchsen. Vielleicht sogar mit kleineren Projekten.

Und ich würde in jedem Fall empfehlen, IMGUI (OnGUI) liegen zu lassen, weil Fehler wie der, den du jetzt hast, eventuelle Probleme mit UGUI durchaus ausgleichen können. Lass dich also nicht abschrecken - Arbeit ist es so oder so.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 4 weeks later...

Schau dir mal die tutorials von Brackeys an.

Hier Rpg reihe

Dort kannst du dir sogar alle Videos anschauen und das wissen auf dein Spiel anwenden. 

Musst wahrscheinlich nur bissl anders denken da dies ein click rpg als beispiel ist.

Aber ich finde dort wird alles gut erklärt. 

Wenn ich mehr Zeit habe werdeich es mal ins deutsche übersetzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...