Jump to content
Unity Insider Forum

Eine Klasse in einer andreren Klasse aufrufen


UpMerge

Recommended Posts

Hallo!

Ich möchte von der Klasse PlayerControllerCall auf die PlayerController Klasse zugreifen. Leider funktioniert das nicht. Ich bekomme den Fehler NullReferenceException.

Wenn ich die letzte Zeile im PlayerControllerCall Script in playerObject.GetComponent<Rigidbody>().AddForce(PC.movement * speed); ändere, bekomme ich die Fehlermeldung  "there is no GameObject on your game object".:huh:

Keine Ahnung was da los ist. Vielleicht könnte sich das ja jemand von euch anschauen? Unitypackage ist im Anhang.

thx!

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public Vector3 movement;

    public Vector3 PlayerMove (Vector3 go)
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        movement = new Vector3(moveHorizontal,0.0f,moveVertical);
        return movement;
    }
}
using UnityEngine;

public class PlayerControllerCall :  MonoBehaviour
{
    private PlayerController PC;
    private Rigidbody rig;
    
    public float speed = 1f;
    public GameObject playerObject;

	void Start ()
    {
        playerObject = GetComponent<GameObject>();
        PC = GetComponent<PlayerController>();
	}
    

    void Update ()
    {
        CallPlayerMove();
    }

    private void CallPlayerMove ()
    {
        rig.GetComponent<Rigidbody>().AddForce(PC.movement * speed);
    }
}

 

Export.unitypackage

Link to post
Share on other sites

Ich bin mir sehr sicher, dass die genaue Fehlermeldung nicht "there is no GameObject on your game object" ist :)

Jedenfalls scheint da ein Missverständnis mit GetComponent vorzuliegen. Oder sogar über Komponenten ansich. In Unity hast du lauter GameObjects mit jeweils irgendwelchen Komponenten drauf:

  • Ein GameObject
    • Ein Transform
    • Eine Komponente A
    • Eine Komponente B
  • Ein anderes GameObject
    • Ein Transform
    • Eine Komponente A
    • Eine Komponente X

GetComponent fragt ein GameObject nach einer Komponente, die dieses GameObject (vielleicht) hat. Zum Beispiel so:

dasErsteGameObject.GetComponent<KomponenteA>()

GetComponent ist auch Komponenten bekannt. Fragt man eine Komponente mit GetComponent nach einer Komponente, dann geht diese zum GameObject, auf dem sie selber sitzt, und fragt da nach.

eineKomponente.GetComponent<KomponenteB>()
// ist dasselbe wie
eineKomponente.gameObject.GetComponent<KomponenteB>()

Deshalb kann man auch einfach GetComponent ohne etwas davor schreiben - der Code, den wir schreiben, ist ja der einer Komponente. Daher:

dieseKomponente.GetComponent<KomponenteB>()
// ist dasselbe wie
this.GetComponent<KomponenteB>()
// ist dasselbe wie
GetComponent<KomponenteB>()

Eine Komponente zeichnet sich dadurch aus, dass sie auf einem GameObject liegen kann (da sie [indirekt] von Component erbt, falls das hilft).

Ein GameObject ist allerdings keine Komponente. Es kann daher kein GameObject als Komponente auf einem GameObjekt liegen. Daher geht

GetComponent<GameObject>()

nicht.

Wenn du das GameObject einer Komponente haben willst, brauchst du die Eigenschaft Component.gameObject.

Debug.Log("Ich gehöre zu " + gameObject.name);
Debug.Log(eineKomponente.name + " gehört zu " + eineKomponente.gameObject.name);

Wenn du nun eine Komponente auf demselben GameObject haben willst, könntest du schreiben:

gameObject.GetComponent<KomponenteB>()

Aber wie oben bereits erwähnt, gibt's dafür den Shortcut, dass man direkt die Komponente, also "sie sich selbst" fragen kann:

GetComponent<KomponenteB>()

Wenn du jetzt allerdings mit einer Komponente auf einem anderen GameObject arbeiten willst, dann musst du dir dieses erst einmal besorgen. Die Variante, dafür eine öffentliche Variable anzulegen, ist oft nicht schlecht. So wie du es gemacht hast:

public GameObject einGameObject;

Damit kannst du direkt arbeiten, denn der Wert dieser Variablen wird im Unity-Editor gesetzt, indem da was reingezogen wird.

Beispiel:

public GameObject einGameObject;

void Start()
{
  Debug.Log(einGameObject.name + " wurde reingezogen!");
}

Oder

public GameObject einGameObject;
private KomponenteA komponenteA;

void Awake()
{
  komponenteA = einGameObject.GetComponent<KomponenteA>();
}

void Update()
{
  komponenteA.Machwas();
}

Allerdings gibt's hier einen netten Trick: Man kann auch direkt die Komponente in der öffentlichen Variable referenzieren:

public KomponenteA komponenteA;

void Update()
{
  komponenteA.Machwas();
}

Dieser Code macht fast genau dasselbe wie der davor.

Im Editor kann man jetzt jedes GameObject in das Feld "Komponente A" ziehen, dass eine KomponenteA hat. Und im Code wird die KomponenteA des reingezogenen GameObjects direkt referenziert, sodass das GetComponent entfällt.

Link to post
Share on other sites

Vielen Dank für deine Antwort! Muss ich mir gleich nochmal durchlesen.

Die Fehlermeldung mit playerObject.GetComponent<Rigidbody>().AddForce(PC.movement * speed);

MissingComponentException: There is no 'GameObject' attached to the "Sphere" game object, but a script is trying to access it.
You probably need to add a GameObject to the game object "Sphere". Or your script needs to check if the component is attached before using it.

UnityEngine.GameObject.GetComponent[Rigidbody] () (at C:/buildslave/unity/build/artifacts/generated/common/runtime/GameObjectBindings.gen.cs:38)
PlayerControllerCall.CallPlayerMove () (at Assets/Scripts/PlayerControllerCall.cs:25)
PlayerControllerCall.Update () (at Assets/Scripts/PlayerControllerCall.cs:20)

Die Sphere ist doch schon ein GameObject!? :unsure:

 

Link to post
Share on other sites

Lies dir Sascha's Erklärungen einmal durch, dann solltest du dir die Antwort selbst geben können.

Kurzfassung:
Man fügt in Unity Komponenten einem GameObject hinzu. 

In deiner Klasse "PlayerControllerCall " versuchst du aber "GameObject" als Komponente an einem GameObject zu finden. Da es aber kein GameObject (als Komponente) an deinem GameObject gibt, kann das nur schiefgehen. C# kann diese Situation nicht auflösen, da es kein syntaktischer Fehler ist, sondern eine Systematik der Klassen von Unity.

playerObject = GetComponent<GameObject>(); // falsch
playerObject = this.gameObject; // richtig

 

Link to post
Share on other sites

 

Ich habe das Script so hinbekommen das ich keine Fehlermeldung mehr bekomme. Aber steuern läßt sich die Sphere damit auch nicht.

move und movement bekommen die richtigen Daten von meiner Eingabe aber sie werden mit rb.AddForce(movement * speed); nicht an die Sphere weiter gegeben.

EDIT: Die Daten der Steuerung kommen nicht bei movement an.

using UnityEngine;
using System.Collections;

//Kein Object verbunden

public class PlayerController
{
    public Vector3 MovePlayer (Vector3 move)
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        move = new Vector3(moveHorizontal,0.0f,moveVertical);
        return move;
    }
}
using UnityEngine;

// Liegt auf dem GameObject Sphere

public class PlayerControllerCall :  MonoBehaviour
{
    float speed = 2.0f;
    Vector3 movement = new Vector3();
    PlayerController PC = new PlayerController();
    Rigidbody rb = new Rigidbody();

    private void Start ()
    {
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate ()
    {
        NewMethod();
    }

    private void NewMethod ()
    {
            PC.MovePlayer(movement);
            rb.AddForce(movement * speed);
    }
}

 

Link to post
Share on other sites

Fast. Du versuchst da gerade die Variabel "move" zu benutzen.

Was jetzt passiert ist, dass eine Kopie von 

 Vector3 movement = new Vector3();

erstellt wird und dann an deine Methode "MovePlayer" übergeben wird.

 PC.MovePlayer(movement);

Jetzt machst du in der anderen Klasse etwas und schreibst das in die Kopie rein.

Was dir jetzt scheinbar noch fehlt ist das Konzept von Rückgabewerten einer Funktion.

Also nochmal: Du übergibst eine Kopie der Daten an die Funktion. Die schreibt in die Kopie etwas rein aber nicht in das Original. Deswegen "kommt das nicht an".

Man kann das so lösen wie du das versuchst und auf Rückgabewerte von Funktionen nicht achten aber das wäre dann Schritt 2 des Lernprozesses vor Schritt 1.

 

Was du jetzt machen musst ist ganz einfach:

movement = PC.MovePlayer(movement);

Dann wird das, was deine Funktion "MovePlayer" zurückgibt

return move;

auf das geschrieben, was in deiner "PlayerControlCall" Klasse in "movement" steht.

 

Ich würde dir wirklich ans Herz legen dir ein paar einfache C#/Unity Scripting Tutorials anzugucken. Es fehlt da noch ein wenig an Grundlagen:

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-know-the-difference-passing-a-struct-and-passing-a-class-to-a-method

BTW: Vector3 ist eine struct.

Link to post
Share on other sites

Sorry Leute aber ich verstehe es einfach nicht!!

Ich habe eine Klasse namens Weapon. Da hab ich ein public GameObject weaponObject; gemacht. Dann hab ich dieses Object in void Awake mit weaponObject = gameObject; initialisiert.

Jetzt möchte ich von der Klasse ControllerGrapObject auf dieses Objekt zugreifen. Und hab das so gemacht:

public class ControllerGrapObject : MonoBehaviour
{
    private Weapon WeaponRef;
    private Vector3 _griffPosition;
    private GameObject Holder;

    void Awake ()
    {
        WeaponRef = GetComponent<Weapon>();
        Holder = WeaponRef.weaponObject; //NullReferenceException: Object reference not set to an instance of an object
        _griffPosition = Holder.GetComponent<Vector3>();
    }

In void Awake bekomme ich dann die Fehlermeldung.

 

Link to post
Share on other sites

Mal ganz Grundlegend, warum überhaupt das ganze ?
In deinem Weapon Script legst du dir eine weitere Referenz auf dein GameObject an.
In deinem Controller holst du dir zuerst die Weapon Componente, was voraus setzt das sich diese auf dem selben Object befindet.
Wenn das der Fall ist, brauchst du dir gar nicht erst den Holder anzulegen.

Ich nehme aber an das deine beiden Scripts nicht auf dem selben Object liegen, GetComponent gibt also null zurück.
Leg dir also entweder Refernzen im Inspector an oder nutze FindObjectOfType welches dir ein Script zurückgibt was sich irgendwo in der Scene befindet.
Dein "Holder.GetComponent<Vector3>()" zeigt aber das du die Funktionsweise von GetComponent nicht so wirklich verstanden hast, da rate ich dir noch einmal Saschas Post durchzulesen.

Link to post
Share on other sites
vor 26 Minuten schrieb Mese96:

Mal ganz Grundlegend, warum überhaupt das ganze ?

Ich möchte von einem Objekt auf das andere zugreifen.

vor 26 Minuten schrieb Mese96:


In deinem Controller holst du dir zuerst die Weapon Componente, was voraus setzt das sich diese auf dem selben Object befindet.
Wenn das der Fall ist, brauchst du dir gar nicht erst den Holder anzulegen.

AddComponent funktioniert nicht auf WeaponRef. Darum neheme ich GetComponent.

vor 26 Minuten schrieb Mese96:

Leg dir also entweder Refernzen im Inspector an oder nutze FindObjectOfType welches dir ein Script zurückgibt was sich irgendwo in der Scene befindet.
 

Ich möchte eben wissen wie man sowas über das Script regeln kann.

vor 26 Minuten schrieb Mese96:

Dein "Holder.GetComponent<Vector3>()" zeigt aber das du die Funktionsweise von GetComponent nicht so wirklich verstanden hast, da rate ich dir noch einmal Saschas Post durchzulesen.

_girffPosition ist doch ein Vector3.  also _griffPosition = Holder.GetComponent<Vector3>();

Ok : Holder.tranform.position; ???

 

 

 

 

Link to post
Share on other sites

Get Component macht aber was anderes als AddComponent, auch würdest du letzteres nicht auf WeaponRef aufrufen sondern auf deinem GameObject. Z.B;

WeaponRef = gameObject.AddComponent<Weapon>();

Allerdings hättest du dann eine Refernz auf eine neue Komponente.

Wenn du wirklich nur eine Weapon hättest könntest du FindObjectOfType nutzen, ansonsten bist du mit dem zuweisen im Inspector besser aufgehoben.
Für alles weitere brauchst du erst einmal eine Sinnvolle Referenz auf das Gameobject auf welchem sich die Weapon Componente befindet. Z.B. durch eine Collision einen Mausklick o.ä.
 

Griff Position ist zwar ein Vector3, Vector3 ist aber keine Komponente, ergo geht das nicht. 
Was für einen Vector3 möchtest du denn da zurück bekommen ?

Link to post
Share on other sites

Dein Problem ist nicht so sehr das Aufrufen, sondern das Fehlen einer Referenz auf besagtes Object. Da wurde hier eigentlich schon so ziemlich alles was so allgemein wichtig wäre gesagt, da kann ich wieder nur auf Saschas Post verweisen. Ich rate dir dich noch einmal durch diverse Tutorials zu kämpfen.

Link to post
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...