Jump to content
Unity Insider Forum
ThE_MAiTiX

GameObjekt verliert Mesh nach Abspeichern als Prefab-Asset

Recommended Posts

Hallo

Ich habe die letzte Zeit damit verbracht eine Große Map zu gestallten auf dieser Zufällig Kisten Zufinden sind Diese Kisten tuh ich als Prafab in meinem Asset Ordner Abspeichern

jetzt zu meinem Problem wenn ich dies Tuhe verliert mein GameObjekt alles Mesh verlinkungen

hier ein vorher nachher bild zur Verdeutlichung.

 

Vorher: Als GameObjekt

24c045-1557614092.png

Nachher: Als Prefab.

5befcd-1557614291.png

Ich Währe sehr dankbar wenn mir Jemand sagen könnte wie ich Verlinkungen behalten könnte oder replacen könnte sobald ich mein Prefab Ausführe.

 

Code Falls benötigt:

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

public class PickupBridge : MonoBehaviour
{

    public Motivation Health;
    public Level XP;



    void OnMouseDown()
    {
        Debug.Log("Chest Eingesammelt");
        Destroy(gameObject);
        Health.GetHealth();
        XP.GetXP();



    }
}



 

Share this post


Link to post
Share on other sites

Du kannst in einem Prefab nichts referenzieren, das innerhalb einer Szene gebunden ist. Es kann ja sein, dass du das Prefab benutzt und diese Objekte gar nicht existieren, weil du in einer anderen Szene bist. Oder sie noch nicht gespawnt sind. Oder bereits zerstört.

Wenn deine Prefab-Instanz auf Objekte zugreifen sollen, deren Existenz ungewiss sind, gibt es verschiedene Möglichkeiten. Es gibt da ein Paar Techniken, die ich keinesfalls empfehlen würde. Die simpelste Möglichkeit, von der ich nicht mehr abraten würde, ist den Objekten beizubringen, sich irgendwo zu registrieren. Und zwar an einem Ort, dessen Vorhandensein eben nicht ungewiss ist. Hier ist die üblichste Variante das "Pseudo-Singleton", wie ich es nenne. Es wird halt oft "Singleton" genannt, ist aber nicht wirklich dasselbe. Hier gibt's nen Blogeintrag darüber: http://blog.13pixels.de/2019/unity-and-the-mysterious-singleton/

Das Pseudo-Singleton ist auf Dauer nicht die sauberste Variante, aber alles was darüber hinaus geht ist zweifelsohne kein Anfängermaterial mehr. Gelinde gesagt. Deshalb kann man es meiner Meinung nach durchaus benutzen.

Share this post


Link to post
Share on other sites

Gibt halt die ganz schlechte Varianten, aber mit denen will ich gar nicht erst anfangen.

Also, wenn du den Artikel in unter acht Minuten durchliest, brauchst du nich nicht zu wundern wenn's nicht ankommt. Aber fangen wir mal ganz am Anfang an. Was eine statische Variable ist, weißt du?

Share this post


Link to post
Share on other sites

Ich habe jetzt mal versuch es umzusetzen leider ohne erfolg 

 

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

public class PickupChest : MonoBehaviour
{

    public Motivation Health;
    public Level XP;

    public static Level singleton { get; private set; }

    private void Awake()
    {
        if (singleton == null)
        {
            singleton = this;
        }
        else
        {
            Destroy(gameObject);
        }
    }

    private void OnMouseDown()
    {
        if (singleton)
        {

            Debug.Log("Chest Pickup");
            Destroy(gameObject);
            Health.GetHealth();
            XP.GetXP();
    }
}
}

 

Share this post


Link to post
Share on other sites

Du zerstörst doch dieses GameObjekt bevor das Script komplett durchgelaufen ist. Eventuell liegt es daran.

 

Share this post


Link to post
Share on other sites

Ich Habs versucht nur leider ohne Erfolg, Ich denke das der Fehler irgendwo bei 

 

    private void Awake()
    {
        if (singleton == null)
        {
            singleton = this;
        }

Sein muss weil ab da nichts mehr ausgegeben wird. 😕

Share this post


Link to post
Share on other sites

Du darfst nicht einfach Code übernehmen, ohne zu verstehen, was er macht.

Ein Pseudo-Singleton besteht aus zwei Teilen: Einem statischen Feld und der Zuweisung in z.B. Awake. Ein statisches Feld ist eine Variable, die nicht an Objektinstanzen gebunden ist. Wenn du z.B. eine Lichtkomponente hast, kannst du das auf drei GameObjects packen und jedes deiner Lichter kriegt eine andere Farbe. Weil eben die Farbeigenschaft nicht statisch ist, sondern per Objekt existiert. Ein statisches Feld dagegen existiert nur einmal im gesamten Programm. Während du bei einem normalen Feld dem Programm sagen musst, welches Objekt du meinst (also, die Farbe welchen Lichts), kannst du bei einer statischen Variable einfach so darauf zugreifen, weil sie nur einmal existiert.

Das kannst du jetzt benutzen, damit Dinge, die es eben nur einmal gibt, sich selbst in einer statischen Variable anmelden. Und genau das macht

    public static Level singleton { get; private set; }

    private void Awake()
    {
        if (singleton == null)
        {
            singleton = this;
        }
        else
        {
            Destroy(gameObject);
        }
    }

Dein Fehler ist jetzt, dass du das auf die GameObjects geschmissen hast, auf die man klickt. Aber die Idee hinter diesem Pattern ist, dass du das für die Dinger benutzt, die nur einmal existieren (z.B. dem Spieler), sodass andere Objekte es finden können.

Nur so nebenbei... Es scheint fast so, dass deine beiden Komponenten "Motivation" und "Level", also die beiden, die Pseudo-Singletons sein sollten, nicht einmal Komponenten sein brauchen.

Benutze einfach dein neues Wissen über statische Felder und mache:

public static class PlayerStats
{
  public static int xp = 0;
}

und dann gibt's ganz einfach:

PlayerStats.xp += 10;

 

Share this post


Link to post
Share on other sites

Sobald ich in meinem Script auch nur etwas mit statics mache wird es sofort nicht mehr anerkannt.

ich kanns nicht mehr in meine Main Camera ziehen und sobald ich das Singleton verfahren einsetze bekomme ich Folgenden Fehler

In Zeile 18 von PickupBridge befindet sich 

        XP.GetXP();

 

Zitat

NullReferenceException: Object reference not set to an instance of an object
PickupBridge.OnMouseDown () (at Assets/Scrips/PickupBridge.cs:18)
UnityEngine.SendMouseEvents:DoSendMouseEvents(Int32)


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



public class Level : MonoBehaviour
{
    public  Slider LevelBar;
    public  Text TxtLvLHealth;

    public  Button BtnTakeDamage;
    public  Button BtnGetHealth;

    public  int LvLprogress = 100; 
    public  int SaveXP;

    public  int level = 1;
    public  Text levelDisplay;

    public static Level singleton { get; private set; }

    private void Awake()
    {
        if (singleton == null)
        {
            singleton = this;
        }
        else
        {
            Destroy(gameObject);
        }
    }

    void Start()
    {
        BtnTakeDamage.onClick.AddListener(delegate
        {
            TakeXP();
        });

        BtnGetHealth.onClick.AddListener(delegate
        {
            GetXP();
        });

        SaveXP = PlayerPrefs.GetInt("SliderWert2");
        LvLprogress = SaveXP;
        LevelBar.value = SaveXP;

        level = PlayerPrefs.GetInt("Level");

        UpdateHealthBar();
        SaveAndDisplayLevel();
    }

    public void TakeXP()
    {
        LvLprogress = LvLprogress - 100;
            if (LvLprogress < 0)
            {
            LvLprogress = 0;
            }
        

        UpdateHealthBar();
    }

    public void GetXP()
    {

        LvLprogress = LvLprogress + 4;
        if (LvLprogress > 100)
        {
            LvLprogress = 0;
            IncreaseLevel();
        }
        

        UpdateHealthBar();

    }

    public void UpdateHealthBar()
    {
        LevelBar.value = LvLprogress;

        SaveXP = Convert.ToInt32(LevelBar.value); //MakeSave = HealthBar.value;
        PlayerPrefs.SetInt("SliderWert2", SaveXP);
        PlayerPrefs.Save();
    }
    public void IncreaseLevel()
    {
        level++;
        SaveAndDisplayLevel();
    }

    public void ResetLevel()
    {
        level = 1;
        SaveAndDisplayLevel();
    }

    private void SaveAndDisplayLevel()
    {
        PlayerPrefs.SetInt("Level", level);
        PlayerPrefs.Save();

        levelDisplay.text = level + "";
    }
}

 

Share this post


Link to post
Share on other sites
vor 6 Stunden schrieb ThE_MAiTiX:

Sobald ich in meinem Script auch nur etwas mit statics mache wird es sofort nicht mehr anerkannt.

Wenn du damit meinst, dass du die Klasse "PlayerStats" aus meinem Post nicht mehr als Komponente benutzen kannst: Das ist gewollt und völlig richtig so. Vergiss mal alles, was du bisher gemacht hast, und kopiere diesen Code in dein Projekt:

public static class PlayerStats
{
  public static int xp = 0;
}
// Das hier kommt auf ein GameObject mit Collider, dein Button auf den du drückst
public class IncreaseXPButton : MonoBehaviour
{
  private void OnMouseDown()
  {
    PlayerStats.xp += 10;
  }
}
// Das hier kommt einen anderen Button (auch wieder einfach nur ein GameObject mit Collider
public class DisplayXPButton : MonoBehaviour
{
  private void OnMouseDown()
  {
    Debug.Log("Gerade haben wir " + PlayerStats.xp + " xp!");
  }
}

Den zweiten Button (drittes Script kannst du benutzen, um dir anzusehen, wieviel XP wir gerade haben. Der andere Button (zweites Script) fügt jedes Mal 10 XP hinzu. Das erste Script wird nirgendwo draufgezogen, weil es eben statisch ist und daher nicht Instanzen davon erzeugt werden (und genau das tut man, wenn man ein Script als Komponente auf ein GameObject zieht).

Share this post


Link to post
Share on other sites

Mein Ziel ist es eigendlich nur XP zu vergeben wenn der Spiel die auf der Map erscheinenden Kisten anklickt hab das Ganze mal versucht umzusetzen.

nur leider fehlen dann auch die Referenzen zum Text wo mein Level angezeigt wird und mein Slider der denn XP wert anzeigt.

 

Script für die Kiste:

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

public class PickupChest : MonoBehaviour
{


    private void OnMouseDown()
    {


        Debug.Log("Chest Pickup");
        Level.LvLprogress += 4;
        Destroy(gameObject);

    }
    }

 

PlayerStats Script:

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



public static class  Level
{
    static public Slider LevelBar;
    static public Text TxtLvLHealth;

    static public Button BtnTakeDamage;
    static public  Button BtnGetHealth;

    static public  int LvLprogress = 0; 
    static public  int SaveXP;

    static public  int level = 1;
    static public  Text levelDisplay;


    static void Start()
    {
        BtnTakeDamage.onClick.AddListener(delegate
        {
            TakeXP();
        });

        BtnGetHealth.onClick.AddListener(delegate
        {
            GetXP();
        });

        SaveXP = PlayerPrefs.GetInt("SliderWert2");
        LvLprogress = SaveXP;
        LevelBar.value = SaveXP;

        level = PlayerPrefs.GetInt("Level");

        UpdateHealthBar();
        SaveAndDisplayLevel();
    }

    static public void TakeXP()
    {
        LvLprogress = LvLprogress - 100;
            if (LvLprogress < 0)
            {
            LvLprogress = 0;
            }
        

        UpdateHealthBar();
    }

    static public void GetXP()
    {

        LvLprogress = LvLprogress + 4;
        if (LvLprogress > 100)
        {
            LvLprogress = 0;
            IncreaseLevel();
        }
        

        UpdateHealthBar();

    }

    static public void UpdateHealthBar()
    {
        LevelBar.value = LvLprogress;

        SaveXP = Convert.ToInt32(LevelBar.value); //MakeSave = HealthBar.value;
        PlayerPrefs.SetInt("SliderWert2", SaveXP);
        PlayerPrefs.Save();
    }
    static public void IncreaseLevel()
    {
        level++;
        SaveAndDisplayLevel();
    }

    static public void ResetLevel()
    {
        level = 1;
        SaveAndDisplayLevel();
    }

    static private void SaveAndDisplayLevel()
    {
        PlayerPrefs.SetInt("Level", level);
        PlayerPrefs.Save();

        levelDisplay.text = level + "";
    }
}

 

Share this post


Link to post
Share on other sites
Am 14.5.2019 um 01:17 schrieb Sascha:

Vergiss mal alles, was du bisher gemacht hast, und kopiere diesen Code in dein Projekt

 

Share this post


Link to post
Share on other sites

Also soll ich meine Level klasse auf der MainCamara ohne public statics lassen

 

mein Pickupchest script aufs Gameobjekt

 

und dein PlayerStats script niergends hin?

Share this post


Link to post
Share on other sites
vor 1 Minute schrieb ThE_MAiTiX:

meine Level klasse

 

vor 1 Minute schrieb ThE_MAiTiX:

mein Pickupchest script

Ne, nimm mal nur die drei Scripts von mir oben, sonst nichts. Mach dir auch gerne ein neues, leeres Projekt auf. Nur um das mal auszuprobieren und zu lernen.

Share this post


Link to post
Share on other sites

Ich weiß nur leider nicht wie genau ich das auf mein Script anwenden soll solte ich nur eine Methode dort auf Static setzten ist sofort der ganze code nichtmehr verwendbar. :(

Share this post


Link to post
Share on other sites

"Nicht verwendbar" ist halt nicht richtig. Du kannst es nicht mehr als Komponente auf ein GameObject ziehen, richtig. Aber wie du an dem kleinen Beispiel merkst, funktioniert's auch ohne.

Share this post


Link to post
Share on other sites

Ich hab versuche aus der GetXP(); eine static methode zu machen

nur leider wollen dann alle methoden drum rum auch zu einer static methode werden und spätestens bei der Update(); methode kann ich kein static mehr setzten weil ich denn Slider ja trotzdem weiterhin ausswählen muss was auch sein muss ist da das Script sich auf der MainCamera befindet.

Share this post


Link to post
Share on other sites

Hm, vielleicht ist das Konzept noch ein bisschen zu weit gegriffen. Nimm stattdessen sonst GameObject.Find. Ich kann nicht so ganz fassen, dass ich das sage, weil ich sehr viel Zeit damit verbracht habe, Leuten zu erklären warum man diese Methode niemals nutzen sollte :)

private void OnMouseDown()
{
  var playerStats = GameObject.Find("Name des GameObjects auf dem die Komponente ist").GetComponent<PlayerStats>();
  playerStats.GetXP();
}

 

  • Thanks 1

Share this post


Link to post
Share on other sites

Ahh super danke es Funkioniert :) 

Gibts dafür gründe das du diese Methode nicht entfehlen würdest oder ist das einfach nur unsauberes Programmieren?

Share this post


Link to post
Share on other sites
vor 25 Minuten schrieb ThE_MAiTiX:

Gibts dafür gründe

Ja, wegen Geschmackssachen rate ich nicht dringend con Dingen ab :)

vor 25 Minuten schrieb ThE_MAiTiX:

unsauberes Programmieren?

Genau deswegen. Unsauberes Programmieren ist halt keine Erfindung irgendwelcher Code-Gourmets, es gibt reale Nachteile, wenn man unsauberes Zeug schreibt. Beim Beispiel von GameObject.Find ist eine der möglichen Probleme, dass der Name des GameObjects irgendwann einmal geändert wird (z.B. weil du einen Rechtschreibfehler korrigierst oder merkst, dass ein anderer Name besser wäre). Oder du stellst fest, dass die Komponente auf einem anderen GameObject viel besser aufgehoben wäre und verschiebst sie. Und zack, funktioniert dein Script nicht mehr. Und noch viel besser: Dein Compiler sagt dir nichtmal, dass etwas nicht stimmt. Du hast da halt ne lächerlich mächtige Rechenmaschine, deren Programme in der Lage sind, dir ganz genau zu sagen, wo in deinem Code etwas nicht stimmt - aber bei GameObject.Find geht das nicht. Wenn's da kracht, dann krachts einfach während das Spiel läuft. Und zwar nichtmal unbedingt in der Zeile, in der der Fehler tatsächlich sitzt. Sowas zu bauen ist so das Programmier-Äquivalent zu Antibiotika-resistenten Bakterien.

  • Thanks 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×