I am using Unity3D to create a demo.
The demo is like this: Use the EasyTouch to control the movement of the gameobject named "Player" —— the plane.
After creating the EasyTouch, I create a C# script like this:
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public float m_speed = 1;
protected Transform m_transform;
public MovePlayer m_movePlayer;
// Use this for initialization
void Start () {
m_transform = this.transform;
}
// Update is called once per frame
void Update () {
}
void OnEnable(){
Debug.Log ("OnEnable");
EasyJoystick.On_JoystickMoveStart += HandleOn_JoystickMoveStart;
EasyJoystick.On_JoystickMove += HandleOn_JoystickMove;
EasyJoystick.On_JoystickMoveEnd += HandleOn_JoystickMoveEnd;
}
void HandleOn_JoystickMoveStart (MovingJoystick move)
{
}
void HandleOn_JoystickMoveEnd (MovingJoystick move)
{
}
void HandleOn_JoystickMove (MovingJoystick move)
{
Debug.Log ("HandleOn_JoystickMove");
if (m_transform == null) {
m_transform = this.transform;
}
if (move.joystickName != "moveJoystick") {
Debug.Log ("return");
return;
}
float currentPositionX = this.gameObject.GetComponent<Transform> ().position.x;
float currentPositionZ = this.gameObject.GetComponent<Transform> ().position.z;
float joyPositionX = move.joystickAxis.x;
float joyPositionY = move.joystickAxis.y;
Debug.Log ("joyPositionX = " + joyPositionX + " joyPositionY = " + joyPositionY);
float moveh = joyPositionX / 10;
float movev = joyPositionY / 10;
this.m_transform.Translate (new Vector3 (-joyPositionX/10, 0, -joyPositionY/10));
}
}
When I start the game, the code worked fine, and the plane will be control.
Then I create the button by GUI.
When the button was clicked, the code Application.LoadLevel(0) will be called.And the level will restart.
But after I called Application.LoadLevel(0) to restart the game.
The plane will can not be control by the EasyTouch, and the Error message will appear:
MissingReferenceException: The object of type 'Player' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
I wonder at why the "Player" will be null.
The "Player" should be init after called Application.LoadLevel(0), doesn't it?
Preamble
I have never used this EasyTouch plugin (fortunately? :D). So I'll make an assumptions on what I see in your code. If I'm wrong — sorry :)
Answer
Take a look on this code:
void OnEnable(){
Debug.Log ("OnEnable");
EasyJoystick.On_JoystickMoveStart += HandleOn_JoystickMoveStart;
EasyJoystick.On_JoystickMove += HandleOn_JoystickMove;
EasyJoystick.On_JoystickMoveEnd += HandleOn_JoystickMoveEnd;
}
It looks like EasyJoystick is a static class. And you are subscribing to static events here. Then after calling Application.LoadLevel() the instance of your Player class marked as destroyed. But subscriptions don't go anywhere and once EasyJoystick fires one of the events (On_JoystickMoveStart, On_JoystickMove or On_JoystickMoveEnd) corresponding methods (HandleOn_JoystickMoveStart, HandleOn_JoystickMove or HandleOn_JoystickMoveEnd) are being invoked. This is why you get the exception. And it is probably thrown by this line (am I wrong? :)):
float currentPositionX = this.gameObject.GetComponent<Transform> ().position.x;
In order to fix the problem you need to unsubscribe Player instance(s) from the events mentioned before invoking Application.LoadLevel().
Side note
This:
float currentPositionX = this.gameObject.GetComponent<Transform> ().position.x;
can be rewritten as:
float currentPositionX = transform.position.x;
Though currentPositionX and currentPositionY are not used at all :D
Edit. How to unsubscribe
Something like this:
void OnDisable(){
Debug.Log ("OnDisable");
EasyJoystick.On_JoystickMoveStart –= HandleOn_JoystickMoveStart;
EasyJoystick.On_JoystickMove –= HandleOn_JoystickMove;
EasyJoystick.On_JoystickMoveEnd –= HandleOn_JoystickMoveEnd;
}
Related
So I am trying to create a new first-person movement system with the new input system in order to make gamepad support so much easier and I am experiencing a problem when I try to read the value of the Vector2 in a FixedUpdate loop, it only outputs (0,0) but if I read it in an InputAction.performed event it works. However, I cannot use the event as it doesn't repeat on keyboard input and it isn't smooth. I've seen a tutorial linked here and at the end it does demonstrate you can pull information from outside events. Now my question is did I miss something or is there a different way to do it, my code is found below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Assertions;
public class MovementEngine : MonoBehaviour
{
public InputMaster Input; // My input device here
public float currentSpeed = 2f;
public float walkingSpeed = 2f;
public float runningSpeed = 4f;
public Transform cameraTransform;
public CharacterController controller;
public InputAction Movement;
// Start is called before the first frame update
void Awake()
{
Input = new InputMaster(); // Creates new instance
}
void OnEnable()
{
Movement = Input.Player.Movement;
Movement.Enable(); // it is enabled
Input.Player.Interaction.performed += Interact;
Input.Player.Interaction.Enable();
}
private void Interact(InputAction.CallbackContext context)
{
Debug.Log("Interact");
}
void OnDisable()
{
Movement.Disable();
Input.Player.Interaction.Disable();
}
// Update is called once per frame
void FixedUpdate(){
Debug.Log("Movement: " + Input.Player.Movement.ReadValue<Vector2>()); // doesn't work
}
}
Store the value (retrieved in the performed event) in a variable, and use that variable in fixed update.
Make sure to reset the variable from the cancelled event (otherwise the variable will hold the last retrieved value from performed event).
You can read the input value directly into a class variable, as shown below.
// Value read from user input
//
private Vector2 movement;
private void Start()
{
SubscribeEvents();
}
private void SubscribeEvents()
{
// Read the value directly into our movement variable
//
Input.Player.Movement.performed += ctx => movement = ctx.ReadValue<Vector2>();
// Reset the movement variable in cancelled since we are no longer interacting
//
Input.Player.Movement.cancelled += ctx => movement = Vector2.zero;
}
private void FixedUpdate()
{
Debug.Log("Movement: " + movement);
}
I fixed this a while ago but StackOverflow never posted my answer
I fixed it by downgrading to Unity InputSystem v1.0.0 as v1.1.1 seemed to not like the InputAction.ReadValue<>() function.
im kind of newbie to unity and object oriented programming. Recently im trying to clone Cube Surfer mobile game. Basic idea from my view is this ;
-When we triggered to collactable cubes which consist script will be duplicated and it will be belong the main cube parent as child object then triggered cube will be destroyed.(After positioning)
-Later this duplicate child objects(cubes) will do the same when they enter trigger area of other collectable cubes(those will be the same prefab but did not yet create a prefab)
Im trying to collect(create a clone of it position and destroy the object) cubes. For first cube, I added some code to my movement script which is below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public GameObject addup;
Rigidbody rb;
float controlSpeed = 1.25f;
float forwardMovementSpeed = 10f;
private Vector3 axisGet;
float deathTime;
public int collected;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody>();
collected = 0;
}
// Update is called once per frame
void FixedUpdate()
{
axisGet = new Vector3(0, 0, Input.GetAxis("Horizontal"));
rb.MovePosition(transform.position + Vector3.left * forwardMovementSpeed * Time.deltaTime + axisGet * controlSpeed * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
if(other.tag=="add up")
{
gameObject.transform.position += Vector3.up;
var newObject = Instantiate(addup.gameObject, Vector3.zero, Quaternion.identity);
newObject.transform.parent = transform;
newObject.transform.position = gameObject.transform.position + Vector3.down;
Destroy(other.gameObject);
newObject.GetComponent<BoxCollider>().isTrigger = false;
collected++;
}
}
}
WORKED WITHOUT ERROR BUT THEN, I applied the same method to collectable cubes scripts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UpNdown : MonoBehaviour
{
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "add up")
{
GameObject parentTransform;//?????????
parentTransform = gameObject.GetComponentInParent<GameObject>(); //get first cube component
parentTransform.transform.position += Vector3.up; //first cube one unit up
GameObject newObject; // ?????????
newObject = Instantiate(other.gameObject, Vector3.zero, Quaternion.identity) as GameObject; //???????????
Debug.Log(newObject);
var collect = parentTransform.GetComponent<Movement>().collected;
if (other != null)
{
Destroy(other.gameObject); //destroy triggered collactable
}
newObject.transform.parent = parentTransform.transform; //setting parent to new cube
newObject.transform.position = parentTransform.transform.position + Vector3.down * (collect + 1); //setting position of new cube
newObject.GetComponent<BoxCollider>().isTrigger = false; //prepare the below cubes(new cubes) for trigger with other collactable cubes
collect++;
}
}
}
And, I had nullexception error in every line in ontriggerenter method then, I changed(added) the lines with question marks. So, I get
ArgumentException: GetComponent requires that the requested component 'GameObject' derives from MonoBehaviour or Component or is an interface.
UnityEngine.GameObject.GetComponentInParent[T] (System.Boolean includeInactive) (at :0)
UnityEngine.GameObject.GetComponentInParent[T] () (at :0)
UpNdown.OnTriggerEnter (UnityEngine.Collider other)
I thought, I understood the OOP instance idea which objects in the scenes are instances scripts has their own value... but i dont understand that while I was operating on a instance why it is null in the memory :((((((((((((( if PC can't access how instantiates the object ?
SORRY I WRITE THIS LONG BUT IM ABOUT THE EDGE AGAIN I DON'T WANT TO QUIT BECAUSE OF FACING THIS PROBLEM AGAIN
TY FOR YOUR ANSWERS, ALREADY APPRECIATED :)
GameObject is no component (it is rather a container of all components attached to it!)
=> you can't get it using GetComponent or GetComponentInParent at all. (Btw Note that GetComponentInParent starts the search on this object itself first before bubling up the hierarchy so either way this isn't what you want to use).
What you want is simply transform.parent to get the Transform component of the parent object of the object this script is attached to (assuming the rest of your code does what it should)
private void OnTriggerEnter(Collider other)
{
// Rather use CompareTag instead of string ==
// The latter silently fails in case of typos making your debugging life miserabel
// it is also slightly less efficient
if (!other.CompareTag("add up")) return;
// Get the parent of this object
var parentTransform = transform.parent;
// Cache this you will need it later see below
var parentMovement = parentTransform.GetComponent<Movement>();
var collect = parentMovement.collected;
parentTransform.position += Vector3.up;
// By using the correct type you want later you can skip GetComponent
var newObjectCollider = Instantiate(other, Vector3.zero, Quaternion.identity);
Debug.Log(newObjectCollider);
Destroy(other.gameObject);
newObjectCollider.transform.parent = parentTransform;
newObjectCollider.transform.position = parentTransform.position + Vector3.down * (collect + 1);
newObjectCollider.isTrigger = false;
// This does absolutely nothing. Numeric values are passed by value and there is no connection
// between your local variable and the component you got it from
//collect++;
// you probably rather want to increase
parentMovement.collected++;
}
Or alternatively since you anyway have a specific component on your parent object you could also do
// Instead directly get this component
var parentMovement = GetComponentInParent<Movement>();
// Then wherever needed access the transform through it
var parentTransform = parentMovement.transform;
...
I'm quite sure though that the other way round it is more efficient since you already know exactly which parent you are searching the component on.
Or - and this would probably be the best option - cache that parent information once right away:
// If possible already reference this vis the Inspector
[SerializeField] private Movement parentMovement;
private Transform parentTransform;
private void Awake ()
{
if(! parentMovement) parentMovement = GetComponentInParent<Movement>();
parentTransform = parentMovement.transform;
}
Ty sir my first code was nearly the same of your first answer but didn't work again at least for error.
private Transform parentTransform;
private void Awake ()
{
if(! parentMovement) parentMovement = GetComponentInParent<Movement>();
parentTransform = parentMovement.transform;
}
But this worked, I guess the problem I need to define instances to class so they don't disappear instantly on the trigger function or direct I need to define them to class.
Anyway, thank you derHugo now need to solve different problems :D
I was trying to get the camera follow my player (which is instantiated from a prefab) but I keep getting the error in my camera script.
My camera script (the error is in line offset = transform.position - Game.currentPlayer.transform.position;):
public class CameraControl : MonoBehaviour
{
private Vector3 offset;
private void Awake()
{
offset = transform.position - Game.currentPlayer.transform.position;
}
void LateUpdate()
{
transform.position = Game.currentPlayer.transform.position + offset;
}
}
I set my currentPlayer variable here:
void Start()
{
GameObject newPlayer = Instantiate(player,transform.position,transform.rotation);
newPlayer.name = "Player";
currentPlayer = newPlayer;
}
If you need anymore scripts to help, just ask :)
Awake is called before Start. Actually even all Awake methods are finished before the first Start is called (also see Order of Execution for Event Functions).
Therefore the reference is not set yet in Awake.
You will have to either move it to the Start method or move the instantiation part to Awake.
In both cases it is still no warranty that the Game script will have its Start executed before the GameControl. Therefore you will still have to adjust the Script Execution Order thus that Game is always excuted before GameControl. Simply
open the Edit > Project Settings > Script Execution Order
drag&drop in the Game script before the DefaultTime block
optionally also sortr already existing items
Alternatively you could use an event system:
public class Game : MonoBehaviour
{
public static event Action OnInitilalized;
public static GameObject currentPlayer;
privtae void Start()
{
GameObject newPlayer = Instantiate(player,transform.position,transform.rotation);
newPlayer.name = "Player";
currentPlayer = newPlayer;
OnInitilalized?.Invoke();
}
}
and then in the GameControl add a callback to the OnInitialized event like
private void Awake()
{
// This makes sure the callback is added only once
Game.OnInitialized -= OnGameInitialized;
Game.OnInitialized += OnGameInitialized;
}
private void OnDestroy()
{
// always make sure to remove callbacks if no longer needed
Game.OnInitialized -= OnGameInitialized;
}
privtae void OnGameInitialized()
{
offset = transform.position - Game.currentPlayer.transform.position;
}
I've searched around and I just can't get this to work. I think I just don't know the proper syntax, or just doesn't quite grasp the context.
I have a BombDrop script that holds a public int. I got this to work with public static, but Someone said that that is a really bad programming habit and that I should learn encapsulation. Here is what I wrote:
BombDrop script:
<!-- language: c# -->
public class BombDrop : MonoBehaviour {
public GameObject BombPrefab;
//Bombs that the player can drop
public int maxBombs = 1;
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.Space)){
if(maxBombs > 0){
DropBomb();
//telling in console current bombs
Debug.Log("maxBombs = " + maxBombs);
}
}
}
void DropBomb(){
// remove one bomb from the current maxBombs
maxBombs -= 1;
// spawn bomb prefab
Vector2 pos = transform.position;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
Instantiate(BombPrefab, pos, Quaternion.identity);
}
}
So I want the Bomb script that's attached to the prefabgameobject Bombprefab to access the maxBombs integer in BombDrop, so that when the bomb is destroyed it adds + one to maxBombs in BombDrop.
And this is the Bomb script that needs the reference.
public class Bomb : MonoBehaviour {
// Time after which the bomb explodes
float time = 3.0f;
// Explosion Prefab
public GameObject explosion;
BoxCollider2D collider;
private BombDrop BombDropScript;
void Awake (){
BombDropScript = GetComponent<BombDrop> ();
}
void Start () {
collider = gameObject.GetComponent<BoxCollider2D> ();
// Call the Explode function after a few seconds
Invoke("Explode", time);
}
void OnTriggerExit2D(Collider2D other){
collider.isTrigger = false;
}
void Explode() {
// Remove Bomb from game
Destroy(gameObject);
// When bomb is destroyed add 1 to the max
// number of bombs you can drop simultaneously .
BombDropScript.maxBombs += 1;
// Spawn Explosion
Instantiate(explosion,
transform.position,
Quaternion.identity);
In the documentation it says that it should be something like
BombDropScript = otherGameObject.GetComponent<BombDrop>();
But that doesn't work. Maybe I just don't understand the syntax here. Is it suppose to say otherGameObject? Cause that doesn't do anything. I still get the error : "Object reference not set do an instance of an object" on my BombDropScript.maxBombs down in the explode()
You need to find the GameObject that contains the script Component that you plan to get a reference to. Make sure the GameObject is already in the scene, or Find will return null.
GameObject g = GameObject.Find("GameObject Name");
Then you can grab the script:
BombDrop bScript = g.GetComponent<BombDrop>();
Then you can access the variables and functions of the Script.
bScript.foo();
I just realized that I answered a very similar question the other day, check here:
Don't know how to get enemy's health
I'll expand a bit on your question since I already answered it.
What your code is doing is saying "Look within my GameObject for a BombDropScript, most of the time the script won't be attached to the same GameObject.
Also use a setter and getter for maxBombs.
public class BombDrop : MonoBehaviour
{
public void setMaxBombs(int amount)
{
maxBombs += amount;
}
public int getMaxBombs()
{
return maxBombs;
}
}
use it in start instead of awake and dont use Destroy(gameObject); you are destroying your game Object then you want something from it
void Start () {
BombDropScript =gameObject.GetComponent<BombDrop> ();
collider = gameObject.GetComponent<BoxCollider2D> ();
// Call the Explode function after a few seconds
Invoke("Explode", time);
}
void Explode() {
//..
//..
//at last
Destroy(gameObject);
}
if you want to access a script in another gameObject you should assign the game object via inspector and access it like that
public gameObject another;
void Start () {
BombDropScript =another.GetComponent<BombDrop> ();
}
Can Use this :
entBombDropScript.maxBombs += 1;
Before :
Destroy(gameObject);
I just want to say that you can increase the maxBombs value before Destroying the game object. it is necessary because, if you destroy game object first and then increases the value so at that time the reference of your script BombDropScript will be gone and you can not modify the value's in it.
I'm making a spawn system for my game, that spawns enemies at random positions, but Unity is telling me that I'm not checking if the object is already destroyed. I've tried to solve it with some other topics here but I couldn't do it.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour {
public GameObject Enemy2Spawn;
public float maxWidth;
public float minWidth;
public float rateSpwan;
private float currentRSpawn = 2.0f;
public int maxInimigo;
public int Inimigos = 0;
void Start()
{
transform.position = new Vector3(0, 6.03f, 0);
}
// Update is called once per frame
void Update()
{
if (currentRSpawn > Time.time & Inimigos<=maxInimigo)
{
transform.position = new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z);
Instantiate(Enemy2Spawn);
currentRSpawn = currentRSpawn + rateSpwan;
Inimigos++;
}
if (Enemy2Spawn. == null)
{
Destroy(this.gameObject);
}
}
}
The error I'm getting is:
"The object of type 'GameObject' has been already destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object"
In the Update function, you are checking if the GameObject is null, which means that it does not exist, then you are using Destroy() to destroy that object that does not exist. Instead, you will want to check if the object exists in the if statement that spawns the enemies. Add that to the if statement, like this, and you should be good.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour {
public GameObject Enemy2Spawn;
public float maxWidth;
public float minWidth;
public float rateSpwan;
private float currentRSpawn = 2.0f;
public int maxInimigo;
public int Inimigos = 0;
void Start()
{
transform.position = new Vector3(0, 6.03f, 0);
}
// Update is called once per frame
void Update()
{
if (currentRSpawn > Time.time && Inimigos <= maxInimigo && Enemy2Spawn == null)
{
transform.position = new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z);
Instantiate(Enemy2Spawn);
currentRSpawn = currentRSpawn + rateSpwan;
Inimigos++;
}
}
}
Well there are a couple things that seem off on this script:
before that: For the error if you set Enemy2Spawn to an object in the scene and it gets destroyed then the value is null then when it tries to Instantiate again, which causes the error (could put the condition "Enemy2Spawn != null" in the first if statement as a fix)
"transform.position = new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z);"
This line changes the object attached to the script, it's position to be exact, which won't affect the position of the enemy you are going to spawn.
"Instantiate(Enemy2Spawn);" makes a clone of the variable's value. The clone inherits all values of the original, so position will be what the original's position is.
Unless you didn't set the value of "Enemy2Spawn" it would not become null, unless a different script changes the value and that it is a prefab. So, "if (Enemy2Spawn == null)" assuming the value is set, does not change else where and is set as a prefab in this case then the statement is never true. If the statement ever became true the line inside "Destroy(this.gameObject);" destroys the object that the script is attached to so it seems counter productive to me (to destroy the spawner) but if it's a measure to prevent error it should be in the Start or Awake function or if setting it with a different script just destroy the script instead of setting the variable to null (which i really doubt though).
Here is a changed script that should fix the issues i stated and fit your needs
using System.Collections;
using System.Collection.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour {
public GameObject Enemy2Spawn;
public float maxWidth;
public float minWidth;
public float rateSpwan;
private float currentRSpawn = 2.0f;
public int maxInimigo;
public int Inimigos = 0;
void Start()
{
transform.position = new Vector3(0, 6.03f, 0);
}
// Update is called once per frame
void Update()
{
if (currentRSpawn > Time.time & Inimigos<=maxInimigo)
{
Instantiate(Enemy2Spawn, new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z), Quaternion.identity);
currentRSpawn = currentRSpawn + rateSpwan;
Inimigos++;
}
}
}
Different reason for the error, the given script may spit out the error but i don't see how it'd cause it so it would have to be caused by a different script that causes the object with the script attached to be destroyed just before executing the update function.
Also, the scripting API suggests calling Destroy like the following if using JS/unityscript
UnityEngine.Object.Destroy(this.gameObject);
I hope this helps, if not then more information is needed