Jump to content
Unity Insider Forum

"Animator is not playing an AnimatorController"


Blissm

Recommended Posts

Heyho, ich hab's geschafft..!

...oder auch nicht. 😅

Aber der Reihe nach.. Bei meinem Projekt Mega Bliss Bros. habe ich schon seit längerer Zeit versucht eine Charakter-Auswahl im Titel-Menü zu integrieren, und nach vielen Versuchen und vielem Lernen hab ich es jetzt auch tatsächlich geschafft. Jedoch wartete dort natürlich gleich schon der nächste Fehler auf mich.. 😬

Nämlich kann ich zwar mit jedem der Charaktere spielen und auch von Level zu Level weiter springen, aber wenn der Charakter stirbt will er einfach nicht mehr respawnen. Es hängt dann in Dauerschleife in der Todes-Animation fest anstatt dass es den Animator triggert zu respawnen. 🤷‍♂️

Animator is not playing an AnimatorController
UnityEngine.Animator:SetTrigger(String)
CharacterController2D:Respawn(Vector3) (at Assets/Scripts/CharacterController2D.cs:306)
GameManager:ResetGame() (at Assets/Scripts/GameManager.cs:192)
<KillPlayer>c__Iterator0:MoveNext() (at Assets/Scripts/CharacterController2D.cs:267)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

Das ist was mit die Konsole dazu ausspuckt. Das Auswählen der Charaktere funktioniert ßber die PlayerPrefs mit SetInt und GetInt. Jeder Charakter hat den Character-Controller Skript mit individuellen Anpassungen, jeder ist als "Player" getaggt und ihr gemeinsames Parent hat den Character-Selection Skript. Die Spielfigur wird durch den "Player"-Tag erkannt.

Ausschnitt aus dem Character-Controller Skript:

// (...)
    
    // public function to apply damage to the player
	public void ApplyDamage (int damage) {
		if (playerCanMove) {
			playerHealth -= damage;

			if (playerHealth <= 0) { // player is now dead, so start dying
				PlaySound(deathSFX);
				StartCoroutine (KillPlayer ());
			}
		}
	}

	// public function to kill the player when they have a fall death
	public void FallDeath () {
		if (playerCanMove) {
			playerHealth = 0;
			PlaySound(fallSFX);
			StartCoroutine (KillPlayer ());
		}
	}

	// coroutine to kill the player
	IEnumerator KillPlayer()
	{
		if (playerCanMove)
		{
			// freeze the player
			FreezeMotion();

			// play the death animation
			_animator.SetTrigger("Death");
			
			// After waiting tell the GameManager to reset the game
			yield return new WaitForSeconds(2.0f);

			if (GameManager.gm) // if the gameManager is available, tell it to reset the game
				GameManager.gm.ResetGame();
			else // otherwise, just reload the current level
				SceneManager.LoadScene(SceneManager.GetActiveScene().name);
		}
	}
    
// (...)

	// public function to respawn the player at the appropriate location
	public void Respawn(Vector3 spawnloc) {
		UnFreezeMotion();
		playerHealth = 1;
		_transform.parent = null;
		_transform.position = spawnloc;
		_animator.SetTrigger("Respawn");
	}
    
// (...)

Ausschnitt aus dem Game-Manager Skript:

// (...)    

	Vector3 _spawnLocation;
    
// (...)


        // set things up here
    void Awake () {
		// setup reference to game manager
		if (gm == null)
			gm = this.GetComponent<GameManager>();

		// setup all the variables, the UI, and provide errors if things not setup properly.
		setupDefaults();
       
    }

// (...)

    	// setup all the variables, the UI, and provide errors if things not setup properly.
	void setupDefaults() {
		// setup reference to player
		if (_player == null)
			_player = GameObject.FindGameObjectWithTag("Player");
		
// (...)

        // get current scene
        _scene = SceneManager.GetActiveScene();

		// get initial _spawnLocation based on initial position of player
		_spawnLocation = _player.transform.position;

// (...)

		// get the UI ready for the game
		refreshGUI();
	}

// (...)

        // public function to remove player life and reset game accordingly
    public void ResetGame() {
		// remove life and update GUI
		lives--;
		refreshGUI();

		if (lives<=0) { // no more lives
			// save the current player prefs before going to GameOver
			PlayerPrefManager.SavePlayerState(score,highscore,lives);

			// load the gameOver screen
			SceneManager.LoadScene(levelAfterGameOver);
		} else { // tell the player to respawn
			_player.GetComponent<CharacterController2D>().Respawn(_spawnLocation);
		}
	}

Hoffe die Ausschnitte sind brauchbar. 😅 Aber da es mit einem einzelnen Charakter ja einwandfrei funktioniert hat, denke ich dass die anderen Sachen irrelevant sind. Jedenfalls komme ich hier nicht weiter, aber vielleicht habt ihr ja eine Idee..?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich denke wir brauchen noch mehr Informationen um zu helfen, bei den aktuell gezeigten Codeteilen fällt mir nichts auf.

Die Fehlermeldung besagt, daß am aktuellen GameObjekt zwar ein Animator hängt aber dieser keinen Controller hat und daher der Aufruf:

_animator.SetTrigger("Respawn");

ins Leere geht.

Dein Problem liegt aber vermutlich woanders. Ich denke das das GameObjekt (mit dem entsprechend ausgewählten Spieler) für den Respawn-Prozeß nicht richtig konfiguriert ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hmmm, also während diese Meldung kommt wird mir im Animator selbst angezeigt was er macht. Scheint alles normal, abgesehen von dem Respawn. Das seltsame ist dass es funktioniert wenn der Charakter der gespielt wird voher in der Szene schon aktiv ist, aber wiederum nicht wenn auch nur ein weiterer aktiv ist, oder garkeiner.

Und ich hab festgestellt dass wenn dieser eine Charakter aktiv ist, ich aber einen anderen im Menß anwähle zum spielen, ich diesen zwar auch spielen kann, aber bei einem Tod der zuvor aktivierte respawnt anstelle des Charakters den ich gerade gespielt habe. (Er ist jedoch inaktiv, weil durch den Int ja der andere aktiviert ist. Ich merk es nur daran, dass er den Parent verlässt.) 

Heißt das dass das Spiel einfach nach dem falschen Controller sucht..? Aber was kann ich da machen? 🤔 Sollte ich vielleicht den Parent respawnen, anstelle des Charakters selbst? (Andererseits hat der mit dem Animator ja nix zu tun..) 😅

Achja.. und _animator.SetTrigger("Victory"); funktioniert ganz normal. Das find ich auch äußerst kurios in Anbetracht dessen das behauptet wird da wäre kein Controller.

Ich schau mal ob ich noch was zum zeigen finden kann, aber eigentlich hab ich alles zum Thema Animator und Respawn oben schon drin.. 🤷‍♂️

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich vermute mal du hast nur einen Animator aber mehrere Spieler. Beim Respawn  hängt der Character-Controller dann vermutlich an einem Spieler der keinen gßltigen Animation-Controller hat (und daher kann er die Respawn-Animation nicht ausfßhren und wirft diese Fehlermeldung). Das sind aber alles nur Vermutungen, da ich die Objekt-Hierarchie in deinem Game nicht kenne.

Ändere deinen Quellcode mal wie folgt ab und klicke auf die geworfene Fehlermeldung, dann sollte er das entsprechende GO anspringen welches den Fehler verursacht (alle Ausführungen werden an dieser Stelle unterbrochen, damit du dir die Situation in Ruhe anschauen kannst):
 

	// public function to respawn the player at the appropriate location
	public void Respawn(Vector3 spawnloc) {
		UnFreezeMotion();
		playerHealth = 1;
		_transform.parent = null;
		_transform.position = spawnloc;

                if (_animator.runtimeAnimatorController==null) {
                   Debug.Log("Animation-Controller ist nicht korrekt gesetzt! (Klick mich)", gameObject);
                   Debug.Break();
                } else {
		   _animator.SetTrigger("Respawn");
                }
	}


 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn er in den Debug nicht reinläuft, dann setz den Debug mal so:

// public function to respawn the player at the appropriate location
	public void Respawn(Vector3 spawnloc) {
		UnFreezeMotion();
		playerHealth = 1;
		_transform.parent = null;
		_transform.position = spawnloc;

 	   _animator.SetTrigger("Respawn");
       Debug.Log("Animation-Controller ist nicht korrekt gesetzt! (Klick mich)", gameObject);
       Debug.Break();
	}

Kann es sein, daß der Charakter deaktviert wurde und im Skript dann versucht wird auf einen inaktiven Controller zuzugreifen?

Wenn die FM kommt und du in den Debug.Break() reinläufst schau dir mal das Objekt an indem du dann auf die Logausgabe klickst...

PS:
Warum nimmt er im Respawn den Parent zu "Player" weg, dass macht irgendwie keinen Sinn, da alle Charaktere unter "Player" liegen sollten?
Wenn ich die Technik so sehe, dann sollte immer 1 GO (= der Charakter) unter dem Player aktiv sein.

In der Charakterauswahl werden dann nur die GOs unter dem "Player"-GO aktiviert oder deaktivert, also so, dass immer ein GO unter "Player" aktiv ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Huuuch, auf einmal kommt jetzt doch ne andere Fehlermeldung (ohne dass ich jetzt nochmal was geändert hätte 🤨) anstatt der voherigen, nämlich:

NullReferenceException: Object reference not set to an instance of an object
GameManager.ResetGame () (at Assets/Scripts/GameManager.cs:192)
CharacterController2D+<KillPlayer>c__Iterator0.MoveNext () (at Assets/Scripts/CharacterController2D.cs:267)
UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Coroutines.cs:17)

Aach, welche der beiden Meldungen kommt hängt davon ab, ob ich die Charaktere alle an oder aus hab. Wenn sie alle aktiviert sind zum Start dann kommt das mit dem Animator.

Link zu diesem Kommentar
Auf anderen Seiten teilen

	_player.GetComponent<CharacterController2D>().Respawn(_spawnLocation);

Dein GameManager muss eine Referenz auf 1 aktives GO (=Character) unter "Player" haben ansonsten geht dieser Aufruf da schief.

Zu jeder Zeit sollte unter "Player" 1 aktives GO sein (= der aktive Charakter) wenn das nicht so ist läuft etwas schief.

Geh am besten mal auf deinen GameManager und mach den aktiven Spieler (= Variable "_player") dort "public" damit du siehst, welcher Charakter gerade aktiv sein sollte.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Er nimmt nich den parent von "Player" weg sondern den parent vom Charakter, also das "Player"-Objekt.

Das hier ist der komplette Selection-Script:

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

// based primary on a quick tutorial by "N3K EN"

public class CharacterSelection : MonoBehaviour {

    private GameObject[] characterList;
    private int indexx;

    private void Start()
    {
        indexx = PlayerPrefs.GetInt("CharacterSelected");

        characterList = new GameObject[transform.childCount];

 // TEST       characterList[1].SetActive(true);        TEST //

        // Fill the array with our characters
        for (int i = 0; i < transform.childCount; i++)
            characterList[i] = transform.GetChild(i).gameObject;
        
        // Toggle off their renderer
        foreach (GameObject go in characterList)
            go.SetActive(false);

        // Toggle on the selected character
        if (characterList[indexx])
            characterList[indexx].SetActive(true);
    }

        public void ChangeCharacter(int index)
        {
            indexx = index;
            PlayerPrefs.SetInt("CharacterSelected", index);
            int myPrefsValue = PlayerPrefs.GetInt("CharacterSelected");
        }

Beim laden der Scene wird der entsprechende Charakter je nach int aktiviert, was auch funktioniert.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 1 Minute schrieb Blissm:

Er nimmt nich den parent von "Player" weg sondern den parent vom Charakter, also das "Player"-Objekt.

Das hier ist der komplette Selection-Script:

Beim laden der Scene wird der entsprechende Charakter je nach int aktiviert, was auch funktioniert.

Mein ich ja macht keinen Sinn, wozu?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Was in der CharacterSelection fehlt ist das Aktualisieren des GameManagers, der halt eine Referenz auf den aktiven Charakter und die wird oben nicht neu gesetzt!

Poste mal den Quellcode vom GameManager vor allen wo er die Referenzen setzt.

Und geh mal auf deinen GameManager-Code und mach den aktiven Spieler (= Variable "_player") dort "public" damit du siehst, welcher Charakter gerade aktiv ist, dieser sollte dem Charakter entsprechen den du ausgewählt hast.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Der Inspector steht noch auf "June" so sieht man nix.
Macht fĂźr mich jedenfalls kein Sinn den C (Charakter) vom Parent zu lĂśsen.
Glaub aber das Problem liegt eher am GameManager.... siehe Kommentare oben

Sowas fehlt meiner Meinung nach (ist nur eine fiktive Methode da ich die GameManager-Klasse nicht kenne):

    // Toggle on the selected character
        if (characterList[indexx]) {
           characterList[indexx].SetActive(true);
           GameManager.gm.SetActivePlayer(characterList[indexx]);
           // oder GameManager.gm._player = characterList[indexx];
        }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hier eine Änderung für deinen GameManager. Dabei musst du danach alle deine Charaktere den GameManager in dem neuen Array zuweisen!

Sollte dein GameManager ßber die Szenen hinweg aber nicht persistent sein, dann ist es aber ggf. nicht die optimalste LÜsung, da du dann hier in jeder Szene diese Referenzen neu setzen mßsstest... (sollte der GameManager in jeder neuen Szene neu erzeugt werden, dann mßsstest du doch wieder ßber den Childs des GOs "Player" gehen)

 public GameObject[] characters; // Hier alle Charaktere zuweisen im Inspector

 void setupDefaults()
    {
        int selected = PlayerPrefs.GetInt("CharacterSelected", 0);
        // Alle Charaktere deaktiveren
        foreach (GameObject character in characters) {
           character.SetActive(false);
        }
        
        // Gewählten Charakter aktivieren (startet bei 0!)
        characters[selected].SetActive(true);

        // setup reference to player
        _player = characters[selected];

	//if (_player == null) _player = GameObject.FindGameObjectWithTag("Player");

 }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Soo, hab das jetzt mal ausprobiert. Funktioniert jetzt einwandfrei~ 😇 Danke schonmal für die groooße Hilfe!

Aber ja, ich muss es bei jeder Szene einzeln angeben. Beim GameManager trag ich nämlich unter anderem auch immer den Level ein der als nächstes geladen werden soll.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...