Jump to content
Unity Insider Forum
  • Announcements

    • Lars

      Allgemeine Forenregeln   03/13/2017

      Forenregeln Nimm dir bitte einen Moment um die nachfolgenden Regeln durchzulesen. Wenn du diese Regeln akzeptierst und die Registration fortsetzen willst, klick einfach auf den "Mit der Registrierung fortfahren"-Button. Um diese Registration abzubrechen, klick bitte einfach auf den "Zurück" Button deines Browsers. Wir garantieren nicht für die Richtigkeit, Vollständigkeit und Brauchbarkeit der Nachrichten und sind auch nicht dafür verantwortlich. Die Beiträge drücken die Meinung des Autors des Beitrags aus, nicht zwangsläufig das, wofür die Forensoftware steht. Jeder Nutzer, der denkt, dass ein veröffentlichter Beitrag unzulässig bzw. störend ist, ist aufgefordert uns unverzüglich per E-Mail zu kontaktieren. Wir haben das Recht störende Beiträge zu löschen und bemühen uns, das in einem realistischem Zeitraum zu erledigen (sofern wir beschlossen haben, dass die Löschung notwendig ist). Du akzeptierst, durchgehend während der Nutzung dieses Services, dass du dieses Forum nicht dazu missbrauchen wirst, Inhalte zu veröffentlichen, welche bewusst falsch und/oder verleumderisch, ungenau, beleidigend, vulgär, hasserfüllt, belästigend, obszön, sexuell belästigend, bedrohlich, die Privatsphäre einer Person verletzend oder in irgend einer Art und Weise das Gesetz verletzen. Des Weiteren akzeptierst du, dass du keine urheberrechtlich geschützte Inhalte ohne Erlaubnis des Besitzers in diesem Forum veröffentlichst. Mit dem Klick auf den "Mit der Registrierung fortfahren"-Button, akzeptierst du zudem unsere Datenschutzerklärung und stimmst der Speicherung deiner IP-Adresse und personenbezogenen Daten zu, die dafür benötigt werden, um dich im Falle einer rechtswidrigen Tat zurückverfolgen zu können bzw. permanent oder temporär aus dem Forum ausschließen zu können. Es besteht keine Pflicht zur Abgabe der Einwilligung, dies erfolgt alles auf freiwilliger Basis.   Zusatzinformationen Der Forenbetreiber hat das Recht, Nutzer ohne Angabe von Gründen permanent aus dem Forum auszuschließen. Des Weiteren hat er das Recht, Beiträge, Dateianhänge, Umfrage, Blogeinträge, Galleriebilder oder Signaturen ohne Angabe von Gründen zu entfernen. Mit der Registrierung verzichtest du auf alle Rechte an den von dir erstellten Inhalten, bzw. treten diese an das Unity-Insider.de und Unity-Community.de ab. Dies bedeutet im Klartext, dass das Unity-Insider.de und Unity-Community.de frei über deine Texte verfügen kann, sofern diese nicht wiederum die Rechte anderer verletzen. Es besteht weiterhin kein Anspruch von registrierten Nutzern bzw. ehemaligen registrierten Nutzern darauf, dass erstellte Inhalte und/oder die Mitgliedschaft (User) wieder gelöscht werden (Erhaltung der Konsistenz dieses Forums).   Einwilligungserklärung Wenn du mit der Speicherung deiner personenbezogenen Daten sowie den vorstehenden Regeln und Bestimmungen einverstanden bist, kannst du mit einem Klick auf den Mit der Registrierung fortfahren-Button unten fortfahren. Ansonsten drücke bitte Zurück. Stand: 07.03.2011
Sign in to follow this  
Lynxxx

Referenzierung nicht möglich/Wann ist Szene fertig geladen?

Recommended Posts

Hallo

 

Ich arbeite gerade an einem Projekt für meine Bachelor-Arbeit. Dabei bin ich jedoch auf ein Problem gestossen, welches ich auch nach mehreren Stunden Recherche und ausprobieren nicht lösen konnte. Entsprechend wende ich mich an euch, in der Hoffnung, dass ich einfach einen dummen Fehler gemacht habe, bzw. einer von euch mir weiterhelfen kann. Danke im Voraus!

 

Ich entwickle gerade ein Multiplayer-Spiel, bei welchem die Spieler sich in einer Lobby treffen und beim Start des Spiels dann in die eigentliche Spielszene wechseln. Die Spieler besitzen jeweils eigene Spieler-Objekte, welche dynamisch generiert werden (basierend auf einem Prefab). Ein solches Spieler-Objekt hat eine Script-Komponente, bei welcher ich gerne gewisse andere Spiel-Objekte referenzieren möchte. Das Problem dabei ist, dass ich bei eben jener Referenzierung in gewissen Fällen eine NullPointer-Exception erhalte. Im folgenden eine Liste aus Code-Snippets, Dingen, die ich schon versucht habe und diversen Fragen.

 

Hier einmal die Funktion, welche die Referenzierung erledigt:

private GameObject ball;
private GameObject game;
private GameController gameScript;
private GameObject fallToDeath;
private GameObject[] playerList;

[...]

void Reference() {
 ball = GameObject.FindGameObjectWithTag("Ball");
 game = GameObject.FindGameObjectWithTag("Game");
 gameScript = game.GetComponent<gamecontroller>();
 fallToDeath = GameObject.FindGameObjectWithTag("FallToDeath");
 playerList = GameObject.FindGameObjectsWithTag("Player");
 Debug.LogError("Ball: " + ball);
 Debug.LogError("Game: " + game);
 Debug.LogError("GameScript: " + gameScript);
 Debug.LogError("FallToDeath: " + fallToDeath);
 Debug.LogError("PlayerList: " + playerList);
 //Send ready command to server
 if (!isLocalPlayer) {
	  return;
 }
 Debug.LogError("ClientReady");
 CmdClientReady();
}

 

Normalerweise würde man die anderen Objekte ja in Awake() oder Start() referenzieren (welche der beiden Callbacks ist dabei vorzuziehen und warum?). Doch wenn ich Reference() in Awake() oder Start() aufrufe, erhalte ich eine NullPointer-Exception, wenn ich versuche die Script-Komponente auf dem Game-Objekt zu referenzieren.

 

void Awake() {
 Reference()
}

--> Wenn ich den Host (Client+Server) im Editor laufen lasse, erhalte ich KEINE NullPointer-Exception. Wenn der Host im Standalone läuft schmeisst er den NullPointer bei:

gameScript = game.GetComponent<gamecontroller>();

--> Egal ob der Client im Editor oder auf dem Standalone läuft erhalte ich die NullPointer-Exception.

 

Start():

void Start() {
 Reference()
 [...]
}

--> Hier ist es egal, ob Editor oder Standalone. Der Host erhält immer die Nullpointer-Exception (an der selben Stelle), während der Client keine Probleme damit hat.

 

 

Nachdem diese beiden Ansätze zu nichts geführt hatten, habe ich versucht das Problem mithilfe einer Coroutine zu umgehen. Diese sah wie folgt aus.

IEnumerator Reference() {
int count = 0;
playerList = GameObject.FindGameObjectsWithTag("Player");
while (ball == null || game == null || fallToDeath == null || playerList.Length !=  numberOfPlayers) {
 if (ball == null) {
  ball = GameObject.FindGameObjectWithTag("Ball");
 }
 if (game == null) {
  game = GameObject.FindGameObjectWithTag("Game");
  if (game != null) {
gameScript = game.GetComponent<gamecontroller>();
  }
 }
 if (fallToDeath == null) {
  fallToDeath = GameObject.FindGameObjectWithTag("FallToDeath");
 }
 if (playerList.Length != numberOfPlayers) {
  playerList = GameObject.FindGameObjectsWithTag("Player");
 }
 yield return null;
 count++
}
Debug.LogError("Count: " + count);
//Send ready command to server
if (!isLocalPlayer) {
  return;
}
Debug.LogError("ClientReady");
CmdClientReady();
}

Diese habe ich dann in Awake() aufgerufen:

Awake():
[code]
void Awake() {
StartCoroutine(Reference());
}

-->Hier werden alle Objekte richtig referenziert! Interessant ist, dass count beim Host am Ende 1 war (entsprechend hat hier die Referenzierung beim ersten Aufruf bereits funktioniert) und beim Client 2. Warum auch immer, scheint der Client 2 Frames zu benötigen, bis auf die Script-Komponente des Game-Objekts zugegriffen werden kann!

 

Da die Initialisierung mit einer Coroutine jedoch nicht gerade elegant ist, habe ich mir überlegt, ob es nicht ein Callback geben müsste, bei welchem alle Objekte fertig geladen sind (Ist das nicht bereits in Awake() und Start() so der Fall?). Also habe ich weiter recherchiert, bis ich auf den Event SceneManager.sceneLoaded gestossen. Also fix meinen Code wieder umgeschrieben und die Methode dem Event-Listener angehängt:

void Awake() {
SceneManager.sceneLoaded += Reference;
}

[...]

void Reference(Scene scene, LoadSceneMode loadMode) {
ball = GameObject.FindGameObjectWithTag("Ball");
Debug.LogError("Ball: " + ball);
game = GameObject.FindGameObjectWithTag("Game");
Debug.LogError("Game: " + game);
gameScript = game.GetComponent<gamecontroller>();
Debug.LogError("GameScript: " + gameScript);
fallToDeath = GameObject.FindGameObjectWithTag("FallToDeath");
Debug.LogError("FallToDeath: " + fallToDeath);
playerList = GameObject.FindGameObjectsWithTag("Player");
Debug.LogError("PlayerList: " + playerList);

//Send ready command to server
if (!isLocalPlayer) {
return;
}
Debug.LogError("ClientReady");
CmdClientReady();
}

--> Es passiert gar nichts. Der Event wird NUR dann gefeuert, wenn der Host im Standalone läuft. In allen anderen Fällen wird Reference() gar nicht aufgerufen. Und WENN Referencen dann aufgerufen wird (Host im Standalone), dann erhalte ich wieder die gleiche NullPointer-Exception <_< .

 

 

-Wann weiss man, dass die Szene fertig geladen ist?

-Kann man Szenen asynchron laden in UNET? (Das habe ich nämlich auch versucht, aber progress ging nie über 0.9, obwohl ich allowChangeScene auf true gesetzt habe. Wäre noch nützlich für einen Loading-Screen)

-

 

Ich bin froh um jede Rückmeldung.

 

Mit freundlichen Grüssen,

Lynxxx

 

PS: Die Foren-Suche hat nicht funktioniert. Falls es das Thema bereits gibt, tut es mir leid.

PPS: Ich war mir nicht ganz sicher, ob das jetzt in Networking oder Allgemeines gehört. Falls der Post am falschen Ort ist, bitte verschieben.

Share this post


Link to post
Share on other sites

Eine NullReferenceException in dieser Zeile kann nur auftreten, wenn game null ist.

Das heißt, das in der davorliegenden Zeile

GameObject.FindGameObjectWithTag("Game");

null zurück gibt.

Mit hoher Wahrscheinlichkeit hat das gesuchte GameObject in dem Moment nicht den richtigen Tag - oder es existiert gar nicht.

 

Dass man so etwas immer nur schwer diagnostizieren und reparieren kann, ist einer der Gründe, warum ich nahezu immer davon abrate, Tags zu benutzen.

Es gibt verschiedene, wesentlich vorteilhaftere Möglichkeiten, an seine Referenzen zu kommen.

In deinem Fall klingt der Name des gesuchten Objekts (game bzw. GameController) nach etwas einmaligem. Da würde sich static anbieten. Genauer das in Unity oft verwendete Pseudo-Singleton.

 

Dieser Code kommt in deine GameController-Klasse:

public GameController singleton { private set; get; }

void Awake()
{
 singleton = this;
}

Das ist die simpelste Form, das zu implementieren.

 

Statt jetzt die Referenz auf den GameController im Awake-Code von potentiell beliebig vielen Klassen herauszusuchen, wird eine statisch zugreifbare Referenz an einer zentralen Stelle gesetzt: In der Klasse selbst.

 

In jeder anderen Klasse kannst du an (fast) jeder Stelle im Code einfach sagen

GameController.singleton.Foo();

 

Und zu der Frage mit Awake oder Start: Referenzen suchen und Startwerte setzen (sowas wie GetComponent) kommt in Awake, Spiellogik (wie das Einfärben oder Bewegen eines Objekts) kommt in Start.

Share this post


Link to post
Share on other sites

Hallo Sascha

 

Danke für deine Antwort.

Der Tag stimmt und wird nie verändert, darum gehe ich davon aus, dass das Objekt noch gar nicht existiert zu diesem Zeitpunkt. ABER: Start() wird doch erst aufgerufen, NACHDEM alle Objekte Awake() abgeschlossen haben oder? Dann müssten ja alle Objekte existieren. Da das oben gezeigte Script auf dem Player ist, gehe ich davon aus, dass es etwas mit der Prefab-Initialisierung zu tun hat. Möglicherweise wird das Spieler-Prefab vor der Szene geladen, aber unabhängig von derselben.

 

Es kann doch nicht sein, dass die order of execution (alle Awake() vor Start()) plötzlich nicht mehr konsistent ist. Sobald man EIN Frame wartet (momentan meine Lösung mit der Coroutine und yield return null), funktioniert es!

 

Nun zu deinem Lösungsansatz:

Ich gebe zu, ich bin etwas skeptisch. Zwar hast du grundsätzlich recht, dass das Game-Objekt einmalig ist, aber es geht ja nicht nur um dieses Element. Da das Spieler-Objekt auf einem Prefab beruht, habe ich überhaupt keine Referenzierungen zu anderen Objekten in der Szene. Gibt es noch andere Alternativen?

 

Mit freundlichen Grüssen,

Lynxxx

Share this post


Link to post
Share on other sites

Alle Awakes sollten vor allen Starts ausgeführt werden, ja. Gilt natürlich nur für Objekte, die über die geladene Szene erstellt werden. Dinge, die durch Instantiate oder Netzwerkcode hinzukommen, führen Awake und Start nach dem Frame aus, in dem sie erstellt wurden.

 

Wenn es nicht daran liegt, dass das game-GameObject erst nach dem Laden der Szene erstellt wird, fällt mir auch nicht mehr wirklich etwas ein.

Share this post


Link to post
Share on other sites

Sorry, dass ich mich erst jetzt wieder melde. Vielleicht sollte ich das noch etwas genauer beschreiben:

Die Referenzierungs-Probleme treten beim Spieler-Objekt auf, welches vom LobbyManager verwaltet wird. Das Game-Objekt besteht bereits in der Szene.

Meine Vermutung ist nun, dass das Spieler-Objekt unabhängig von der Szene geladen wird und irgendwie schneller fertig ist, als die Szene. Entsprechend sind dann bei Start() die Szenen-Objekte noch nicht geladen. Ich versuche gerade über OnLobbyServerSceneChanged() oder OnLobbyServerSceneLoadedForPlayer() die Initialisierung zu machen. Da die Dokumentation darüber dazu zu Wünschen übrig lässt, bin ich mir nicht schlüssig, ob dieser Ansatz überhaupt verlässlich ist.

 

Falls dies nicht funktioniert, werde ich in einem weiteren Schritt versuchen den Grossteil der Szene über das Spieler-Objekt zu initialisieren (mit Prefabs). Was meinst du dazu? Ist das eine blöde Idee?

 

Und (das kam mir gerade JETZT in den Sinn^^) wäre es nicht möglich ein Referenzierungs-Objekt zu erstellen, das ein Singleton ist und Referenzen zu allen relevanten, sich in der Szene befindlichen Objekte, enthält? Dann kann ich die Referenzen direkt im Editor machen (mit public Variablen) und dann auch den Spieler da hinzufügen bzw. die anderen Referenzen davon auslesen. Oder ist das zu ineffizient?

 

Bin gespannt auf deine Meinung : )

 

Mfg, Lynxxx

 

PS: Ich habe auch mit Spawn() ein Problem und werde dazu einen weiteren Post erstellen. Aber ich zweifle schon ein wenig an mir momentan. Es kann ja fast nicht sein, dass es einfach nicht funktioniert......Jedenfalls habe ich mir überlegt auf Photon zu wechseln ODER aber das ganze Projekt von komplett neu zu erstellen und diesmal die Lobby selber zu schreiben anstatt das von Unity bereitgestellte Asset zu verwenden. (Oder aber auf UE4 zu wechseln, wobei ich nicht glaube, dass es das besser macht^^)

Share this post


Link to post
Share on other sites

Es würde mich schon wundern, wenn dein Netzwerk-Lade-Kram tatsächlich schneller Dinge spawnen kann als die Szene, in der es steckt. Es sei denn, die Szene wird, genau wie der Spieler, vom LobbyManager geladen.

Aber beschwören kann ich da nichts, ich arbeite nur selten mal mit Networking. Wann immer ich damit gearbeitet habe, bin ich aber nicht auf Probleme gestoßen, die sich nicht hätten lösen lassen.

 

Was ich an deiner Stelle erst einmal machen würde: Überall in alle relevanten Events (Start, Awake, OnConnect oder was auch immer) aller relevanten Objekte prints einfügen und sich damit die Reihenfolge, in der Dinge passieren, in der Konsole ausgeben lassen.

 

Ich merke auch gerade, dass ich deine letzte Frage gar nicht beantwortet habe.

Ja, es gibt verschiedene Methoden, Referenzen auf Szenenobjekte zu finden. Beim Einfügen eines Prefabs, das Objekte aus der Szene kennenlernen muss, sind statische Referenzen an zentralen Punkten meistens sehr praktisch. Bei einmaligen Objekten kann man das Pseudosingleton bauen wie oben beschrieben; bei mehrfach auftauchenden Objekten ist eine statische Liste möglich.

Die funktioniert ungefähr so:

private static List<Klassenname> instances = new List<Klassenname>();

void Awake()
{
 instances.Add(this);
}

void OnDestroy()
{
 instances.Remove(this);
}

Share this post


Link to post
Share on other sites

Ich habe nun versucht das Problem mit der OnLobbyServerSceneLoadedForPlayer() Funktion zu lösen. Aber, obwohl der Name es eigentlich impliziert, ist die Szene NICHT geladen, wenn der Funktionsaufruf geschieht. Im folgenden der Code:

 

public override bool OnLobbyServerSceneLoadedForPlayer(GameObject lobbyPlayer, GameObject gamePlayer)
    {
	    //This hook allows you to apply state data from the lobby-player to the game-player
	    //just subclass "LobbyHook" and add it to the lobby object.

	    //Simon: Hand over information from lobby to game
	    gamePlayer.GetComponent<PlayerController1>().playerColor = lobbyPlayer.GetComponent<LobbyPlayer>().playerColor;
	    gamePlayer.GetComponent<PlayerController1>().playerName = lobbyPlayer.GetComponent<LobbyPlayer>().playerName;
	    gamePlayer.GetComponent<PlayerController1>().playerNumber = lobbyPlayer.GetComponent<LobbyPlayer>().playerNumber;
	    Debug.LogError("OnLobbyServerSceneLoadedForPlayer");
	    Debug.LogError("gamePlayer: " + gamePlayer.GetComponent<PlayerController1>().playerName);
	    gamePlayer.GetComponent<PlayerController1>().sceneLoaded = true;
	    if (_lobbyHooks)
		    _lobbyHooks.OnLobbyServerSceneLoadedForPlayer(this, lobbyPlayer, gamePlayer);
	    return true;
    }

 

Die sceneLoaded Variable ist eine SyncVar auf dem Spieler-Objekt und hat einen hook auf die Reference() Funktion:

 

public void Reference(bool sceneLoaded) {
    Debug.LogError("Referencing for: " + playerName);
    Debug.LogError("IsLoaded :" + SceneManager.GetActiveScene().isLoaded);
    Debug.LogError("IsServer: " + isServer);
    Debug.LogError("IsLocalPlayer: " + isLocalPlayer);
    ball = GameObject.FindGameObjectWithTag("Ball");
    ballScript = ball.GetComponent<BallController>();
    game = GameObject.FindGameObjectWithTag("Game");
    gameScript = game.GetComponent<GameController>();
    fallToDeath = GameObject.FindGameObjectWithTag("FallToDeath");
    ability1UIScript = GameObject.FindGameObjectWithTag("CooldownAbility1").GetComponent<TextUI>();
    ability2UIScript = GameObject.FindGameObjectWithTag("CooldownAbility2").GetComponent<TextUI>();
    ability3UIScript = GameObject.FindGameObjectWithTag("CooldownAbility3").GetComponent<TextUI>();
    Debug.LogError("Donered");
    //Send ready command to server
    //TEST IF LOCALPLAYER CHECK IS ENOUGH
    if (isLocalPlayer) {
	    Debug.LogError("ClientReady: " + playerName);
	    CmdClientReady();
    }
   }

 

Reference wird aufgerufen, aber bei der Überprüfung, ob die Szene geladen ist (mit SceneManagement.GetActiveScene().isLoaded), wird angegeben, dass die Szene noch nicht geladen ist. Dies geschieht aber nur beim ERSTEN Aufruf der Funktion. Die Funktion wird einmal pro Spieler aufgerufen, aber erst beim zweiten Spieler-Objekt (bzw. beim zweiten Spieler, der geladen hat) ist isLoaded true. Weiss jemand warum?

Share this post


Link to post
Share on other sites

Danke für deine schnelle Antwort Sascha.

Ich habe, wie von dir vorgeschlagen, alle Awake() und Start() mit einem Log versehen. Dabei stellte ich fest, dass meine Vermutung richtig war. Die Awake() werden mindestens ab und zu (habe da nicht mehr gross weiter getestet) nach dem Initialisieren des Spieler-Objekts erst geladen. Dies erklärt auch, warum es zur NullPointer-Exception kommt. Jetzt frage ich mich, ob es nicht einen Callback gibt, bei welchem garantiert ist, dass die Szene geladen ist. Ich dachte, dass OnLobbyServerSceneLoadedForPlayer() genau das macht, aber wie bereits erwähnt, scheint dies nicht der Fall zu sein. Ich werde noch etwas weiter forschen. Vielleicht kann ich das Spawnen des Spielers verhindern, bis die Szene fertig geladen ist. Dies würde auch das Implementieren eines Ladebildschirms vereinfachen.

 

Und falls das alles nichts bringt, werde ich halt mein Game-Objekt als Singleton initialisieren und für die Referenzierung missbrauchen :P.

 

Mfg, Lynxxx

Share this post


Link to post
Share on other sites

Natürlich wäre es wünschenswert, genau das richtige Event zu finden und es damit zu lösen, aber ich hatte gerade so eine Idee...

Was hältst du von

private int componentsToLoad = 0;
private bool hasLoaded
{
 get { return componentsToLoad == 0; }
}

private void GetReferenceSometime<T>(ref T target, Func<T> func) where T : Object // vllt. auch nicht Object...
{
 StartCoroutine(GetReferenceSometime(target, func));
}

private IEnumerator GetReferenceSometime_Coroutine<T>(ref T target, Func<T> func) where T : Object // vllt. auch nicht Object...
{
 componentsToLoad++;
 target = func();
 while(target == null)
 {
   yield return null;
   target = func();
 }
 componentsToLoad--;
}

und dann in Awake

GetReferenceSometime(ref game, () => GameObject.FindGameObjectWithTag("Game"));

  • Like 1

Share this post


Link to post
Share on other sites

Okay, folgende Idee: Jeder Client lädt das Spieler-Objekt selber. Dazu registriere ich eine Funktion auf dem Event sceneLoaded (SceneManager.sceneLoaded += OnSceneLoaded;). Diese ruft dann ClientScene.AddPlayer() auf. Auf dem, im folgenden initialisierten, Spieler-Objekt referenziere ich alles nötige und sende dem Server die Nachricht, dass der Client bereit ist.

 

Der Vorteil hierbei ist, dass ich sowohl weiss, wann der Client/Alle fertig geladen haben (für einen allfälligen Ladebildschirm) als auch, dass ich auch gleich verschiedene Spieler-Prefabs benutzen kann (was sowieso noch auf der Liste steht).

 

Als ich nun jedoch die Option "Auto Create Player" auf false gesetzt habe, ist mir aufgefallen, dass der LobbyPlayer auch nicht mehr automatisch erstellt wurde. Weisst du per Zufall, wo/wann (bzw. mit welchem Callback) ich diesen erstellen sollte? (Ich verwenden das Unity Asset für den LobbyManager).

 

Momentan überlege ich mir gerade, ob ich nicht die ganze Lobby über den Haufen werfen soll und alles selber schreiben möchte. Spätestens, bei der Steamworks Integration muss ich das sowieso machen oder? (Soweit ich weiss hat Unity ja noch keine Integration davon oder?).

 

Mit freundlichen Grüssen,

Lynxxx

Share this post


Link to post
Share on other sites

Deine Idee klingt nicht schlecht, aber für eine vernünftige Bewertung fehlt mir die Erfahrung mit Networking.

 

Was Steamworks angeht - Unity bringt von sich aus nichts mit, aber es gibt gute, kostenlose Pakete. Im Asset Store und anderswo.

Share this post


Link to post
Share on other sites

Okay, ich habe nun versucht den Lobby- und den Game-Player manuell zu erstellen, jedoch erfolglos. Auch wenn man "Auto Create Player" auf false setzt, wird der Game-Player automatisch erstellt. Also funktioniert dieser Ansatz auch nicht.

 

Entsprechend habe ich versucht die Lösung mit dem Singleton zu machen. Dabei bin ich aber auf folgendes Problem gestossen:

Wenn ich das Singleton in Awake() instanziere, dann wird es zu spät erstellt und ich erhalte eine NullPointerException.

 

public static GameController instance = null;

void Awake() {
	if (instance == null) {
		instance = this;
	} else if (instance != this) {
		Destroy(gameObject);
	}
}

 

Die andere Variante, die ich versucht habe, war diese HIER. Dabei wird das Singleton bei Bedarf initialisiert. ABER dabei wird ein neues Objekt erstellt, welches dann das Script enthält. Und dieses neue Objekt hat die public Variablen nicht gesetzt, entsprechend erhalte ich auch dann eine NullPointerException :(.

 

Habe ich irgendwas falsch gemacht beim Singleton?

 

Mfg, Lynxxx

Share this post


Link to post
Share on other sites

Falsch ist das nicht, was du da hast.

 

Ich würde jetzt als erstes prüfen, warum GameController überhaupt eine Komponente ist.

Was genau macht es, das es sinnvoll macht, es auf einem GameObject in der Szene zu haben?

Warum ist es unsinnvoll, die Klasse statisch zu machen?

Oder ist es das vielleicht gar nicht?

 

Du schreibst, dass da Referenzen auf Objekte in der Szene im GameController referenziert sind.

Warum sind sie das? Was sind das für Objekte? Müssen die alle an einem zentralen Punkt sein?

Könnten sie selber Singletons sein?

Share this post


Link to post
Share on other sites

Ich habe mehrere Objekte, die fix in der Szene sind, welche vom Spieler benutzt werden. Entsprechend muss ich sie im Spieler-Script referenzieren. Die Idee war, dass ich die Referenzierung zu diesen Objekten statisch im Game-Singleton mache und das Spieler-Script diese Referenzierungen dann vom Singleton abholt. Aber dies scheint so nicht möglich zu sein.

 

EDIT: Um alle deine Fragen zu beantworten:

-Es geht nicht, die Klasse statisch zu machen, da ich dann (glaube ich zumindest) die Objekte der Szene nicht mehr darin referenzieren kann. Ähnlich wie es bei den Prefabs nicht möglich ist, da ja diese Objekte nur in der entsprechenden Szene vorhanden sind, das Prefab (bzw. hier das Singleton) aber überall existiert.

-Die Objekte sind beispielsweise ein Ball oder ein Monster.

-Nein, theoretisch müssten diese nicht zentral an einem Punkt referenziert sein, aber damit ich nicht jedes mal, wenn ich das Objekt benötige mit FindGameObjectWithTag() suchen muss, wollte ich es referenzieren und abspeichern im Spieler-Script.

-Nein, nicht alle Objekt (bzw. die Scripts darauf) können Singletons sein.

Share this post


Link to post
Share on other sites

-Die Objekte sind beispielsweise ein Ball oder ein Monster.

Das ist für mich ein interessanter Punkt, der sehr stark danach klingt, dass diese Aussage nicht stimmt:

Entsprechend muss ich sie im Spieler-Script referenzieren.

 

Nicht alle Objekte, die irgendwann einmal für ein anderes Objekt wichtig werden, müssen diesem Objekt schon von Anfang an bekannt sein.

Ein Netzwerk aus Referenzen, über das Objekte einander indirekt kennen, existiert bereits in Form der geladenen Szene. Sie lernen sich dann zum richtigen Moment über Unity-Events kennen. Klassische Vertreter sind OnTriggerEnter und OnCollisionEnter.

Wenn ein Objekt wirklich von Anfang an bekannt sein soll, z.B. weil es als Point of Interest auf einer Karte zu sehen sein soll, dann kann man immer noch mit variablen Sammlungen arbeiten. Aber ich glaube, das ist ein anderes Thema.

 

Aber zusammenfassend: Du hast anscheinend die Verbindung

Player - GameController - Ball

und dein Problem ist, dass das Aufbauen der Teilverbindungen schlechtes Timing hat.

Ich frage daher: Warum muss der Spieler indirekt von Anfang an den Ball kennen?

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

Sign in to follow this  

×