MissingReferenceException: Error after destroying a moving game object - unity3d

In my game some objects move every few seconds and others don't. I have created a the method below to handle the movement of moving objects.
private IEnumerator moveObject(GameObject movingObject, float posX, float duration) {
if(movingObject != null) {
float elapsedTime = 0;
Vector3 startPos = movingObject.transform.position;
Vector3 endpos = new Vector3(posX, objectPositionY, camera.nearClipPlane);//hole.transform.position;
while(movingObject != null && elapsedTime < duration) {
movingObject.transform.position = Vector3.Lerp(startPos, endpos, elapsedTime / duration);
elapsedTime += Time.deltaTime;
yield return null;
}
}
}
Also I destroy the moving objects using the code below:
for (int i = 0; i < holeObjects.Count; i++) {
Debug.Log ("countingholeobjectindex"+ i );
if (col.gameObject.Equals (holeObjects [i].getHoleObject ())) {
Debug.Log ("condition satisfied!" );
foreach( HoleObjectSetup holeItem in holeObjects )
{
if (holeItem.getHoleObjectType () == HoleObjectSetup.HoleObjectType.moving) {
int holeItemIndex = holeObjects.IndexOf(holeItem);
holeItem.StopAllCoroutines();
holeItem.DestroyHoleObject ();
//holeObjects.RemoveAll(item=>item==null);
//holeObjects.RemoveAt(holeItemIndex);
}
}
}
Any time I destroy a movingObject (mostly, when it's moving), I get the error
MissingReferenceException: The object of type 'GameObject' 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.
pointing to the line
movingObject.transform.position = Vector3.Lerp(startPos, endpos, elapsedTime / duration);
in the movingObject method (provided above). I tried adding another nullity check to surround the contents of the while loop, but that made Unity3d unresponsive, leaving me with the only option of force quitting. How can I resolve this issue?
UPDATE
movingObject is called in the coroutine below:
private IEnumerator MovementLogic(GameObject movingObject)
{
// keep going until deactivated or disabled
float currentPos = movingObject.transform.position.x;
while(filledPositions.Contains(currentPos))
{
float newPos = getNextPos(currentPos);
if(freePositions.Contains(newPos))
{
freePositions.Add (currentPos);
filledPositions.Remove (currentPos);
freePositions.Remove (newPos);
filledPositions.Add (newPos);
yield return moveObject(movingObject, newPos, movementDuration);
//update positions
yield return new WaitForSeconds(movementDelay);
currentPos = newPos;
}
else
{
// if a valid hole wasn't found, try the other direction next frame
ReverseDirection();
}
}
yield return null;
//}
}
UPDATE 2
The code for DestroyHoleObject() which is in HoleObjectSetup is:
public void DestroyHoleObject () {
Destroy (currentHoleObject);
}
and
public void createHoleObjectType (GameObject holeObject, HoleObjectType holeObjectType, Vector3 holeObjectPosition, bool isCollider2DEnabled) {
currentHoleObject = (GameObject)Instantiate (holeObject, holeObjectPosition, Quaternion.identity);
setHoleObjectType (holeObjectType);
}

Solve the problem by adding
Debug.Log
statements everywhere. It will take a few seconds to find out the exact point in the cycle that is causing problems.
Secondly,
Sometimes a cheap solution to these problems is, use
DestroyImmediate
rather than
Destroy
This might give you a quick fix.
Thirdly. Be more sophisticated about coroutines:
Something like ..
void OnEnable()
{
Debug.Log("enable ..");
}
void OnDisable()
{
StopCoroutine("Whatever");
.. or perhaps ..
StopAllCoroutines();
}
Moreover, ideally you should "handle it manually".
Consider the object you are getting rid of.
That class, should have a function something like this:
void BeingDestroyed()
{
.. carefully write your own code here to shut down the object ..
}
... and actually call that explicitly when you are about to destroy it.
It's not that easy to add, destroy stuff in games. It's often very complex.
Could be the object destroys itself: perform any needed shutdown code, wait a frame or two if that is relevant to you, remove from any lists, and then "destroy self".
You can't just "leave coroutines alone": you have to manage them carefully by hand.

Related

Unity2D Disable player movement for X seconds

I am trying to disable a player movement for 1 second when it collides with a specific object. The object collides and it detects that just fine however currently the entire game freezes and does not return.
Ive implemented a 0 value (as found in other examples) to the X and Y movements with a timer. This freezes the game entirely and I have to kill process and reopen Unity.
public Boolean frozen = false;
float shipDisabledTimer = 1f;
// Start is called before the first frame update
void Start()
{
SetUpMoveBoundaries();
gameSession = FindObjectOfType<GameSession>();
}
// Update is called once per frame
void Update()
{
Move(frozen);
Fire();
if (SceneManager.GetActiveScene() == SceneManager.GetSceneByName("Game"))
{
if (Input.GetKey(KeyCode.Escape))
SceneManager.LoadScene("Game Over");
}
}
public void Move(Boolean Frozen)
{
UnityEngine.Debug.Log("Update1 - " + Frozen);
var deltaX = Input.GetAxis("Horizontal") * Time.deltaTime * moveSpeed;
var deltaY = Input.GetAxis("Vertical") * Time.deltaTime * moveSpeed;
var newXPos = Mathf.Clamp(transform.position.x + deltaX, xMin, xMax);
var newYPos = Mathf.Clamp(transform.position.y + deltaY, yMin, yMax);
transform.position = new Vector2(newXPos, newYPos);
if (frozen == true)
{
float timer = 0f;
timer += Time.deltaTime;
UnityEngine.Debug.Log(frozen);
while (timer < shipDisabledTimer)
{
newXPos = 0;
newYPos = 0;
}
disableship(frozen = false);
}
}
public Boolean disableship(Boolean frozen)
{
return frozen;
}
private void OnTriggerEnter2D(Collider2D other)
{
UnityEngine.Debug.Log(other.gameObject.name);
if (other.gameObject.name.Equals("Orb Shot(Clone)"))
{
UnityEngine.Debug.Log("Orb hit player");
//StartCoroutine(countdownTimer());
disableship(frozen = true);
}
}
The ship alone should freeze. No other game object should freeze. All scores, counters, enemies should continue onward. Only the player X,Y are disabled for 1 second.
This
while (timer < shipDisabledTimer)
{
newXPos = 0;
newYPos = 0;
}
entirely blocks your game's main thread since the timer nor shipDisabledTime are not changed within the loop so it will never end.
There are a few ways you could go here.
Invoke
Invoke allows you to call a method after a certain delay so you could easily do something like
public void Freeze()
{
frozen = true;
Invoke("Unfreeze"; shipDisabledTime);
}
private void Unfreeze()
{
frozen = false;
}
Coroutine
A Coroutine is like a temporary Update method. Simplest way in your case is using WaitForSeconds and do something like
public void Freeze()
{
StartCoroutine (FreezeRoutine());
}
private IEnumerator FreezeRoutine()
{
frozen = true;
yield return new WaitForSeconds(shipDisabledTime);
frozen = false;
}
simple timer
Or you could use a simple timer but in Update (not a while loop) like
private float timer = -1;
public void Freeze()
{
frozen = true;
timer = shipDisabledTime;
}
private void Update()
{
if(timer > 0)
{
timer -= Time.deltaTime;
if(timer <= 0)
{
timer = -1;
frozen = false;
}
}
...
}
And then either way you simply don't take any movement input in the meantime
public void Move()
{
if(frozen) return;
...
}
Except for the Invoke solution you can also extend them and decided whether you want to e.g. stack multiple freezes, ignore them while already frozen or simply start the timers over.
General note: In c# rather simply use bool it's basically the same but easier to read and write ;)
Also note that this call
disableship(frozen = false);
...
public Boolean disableship(Boolean frozen)
{
return frozen;
}
Is pretty strange ... first of all this method does absolutely nothing than just return the same value you pass in as parameter .. you are hiding the frozen field with a same named parameter so this does nothing!
Second your method returns a Boolean but you are not assigning it to anything.
If you want to change the value simply set it using frozen = XY but don't pass this in as parameter further.
Aaand avoid calling Debug.Log every frame .. it will slow down your app even in a build where you don't even see the log!

Instantiate is only working sometimes in unity

I'm having a bit of an odd issue in a 2d side-scroller game, where unity is only creating my projectile clone sometimes when pressing fire, it removes 1 grenade from the inventory correctly regardless of if the grenade is cloned or not.
here is my code
void ThrowGranade()
{
if (grenandeInventory > 0)
{
GameObject grenade = Instantiate(projectile, projectileSpawnPoint.transform.position, Quaternion.identity) as GameObject;
grenandeInventory =- 1;
//am.ThrowGrenade();
}
else if (grenandeInventory <= 0)
{
grenandeInventory = 0;
}
}
and the fire button script held in the update function
if (Input.GetButtonDown("Fire1"))
{
if (grenandeInventory>0)
{
grenandeInventory -= 1;
ThrowGranade();
}
}
i add the force in the start function of the projectile itself
void Start () {
#region REFERENCES
anim = GetComponent<Animator>();
am = FindObjectOfType<AudioManager>();
rb = GetComponent<Rigidbody2D>();
player = FindObjectOfType<PlayerScript>();
capsule = GetComponent<CapsuleCollider2D>();
#endregion
rb.AddForce(Vector2.right * player.projectileForce, ForceMode2D.Force);
}
You shouldn't be doing the check in both your throw grenade method and when you click the button before calling the method. The way you set it up right now, if you have 1 grenade, you subtract from the grenade count before calling your throwGrenade so that your throwGrenade sees you have 0 grenades. Just do this.
if (Input.GetButtonDown("Fire1"))
{
ThrowGranade();
}
Your ThrowGranade() Method already handles the logic.

Move rigidbody from point a to b [duplicate]

I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here

How to make unity objects "fly by wire" (move from point A to point B) [duplicate]

I am learning Unity from a Swift SpriteKit background where moving a sprite's x Position is as straight forward as an running an action as below:
let moveLeft = SKAction.moveToX(self.frame.width/5, duration: 1.0)
let delayAction = SKAction.waitForDuration(1.0)
let handSequence = SKAction.sequence([delayAction, moveLeft])
sprite.runAction(handSequence)
I would like to know an equivalent or similar way of moving a sprite to a specific position for a specific duration (say, a second) with a delay that doesn't have to be called in the update function.
gjttt1's answer is close but is missing important functions and the use of WaitForSeconds() for moving GameObject is unacceptable. You should use combination of Lerp, Coroutine and Time.deltaTime. You must understand these stuff to be able to do animation from Script in Unity.
public GameObject objectectA;
public GameObject objectectB;
void Start()
{
StartCoroutine(moveToX(objectectA.transform, objectectB.transform.position, 1.0f));
}
bool isMoving = false;
IEnumerator moveToX(Transform fromPosition, Vector3 toPosition, float duration)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
//Get the current position of the object to be moved
Vector3 startPos = fromPosition.position;
while (counter < duration)
{
counter += Time.deltaTime;
fromPosition.position = Vector3.Lerp(startPos, toPosition, counter / duration);
yield return null;
}
isMoving = false;
}
Similar Question: SKAction.scaleXTo
The answer of git1 is good but there is another solution if you do not want to use couritines.
You can use InvokeRepeating to repeatedly trigger a function.
float duration; //duration of movement
float durationTime; //this will be the value used to check if Time.time passed the current duration set
void Start()
{
StartMovement();
}
void StartMovement()
{
InvokeRepeating("MovementFunction", Time.deltaTime, Time.deltaTime); //Time.deltaTime is the time passed between two frames
durationTime = Time.time + duration; //This is how long the invoke will repeat
}
void MovementFunction()
{
if(durationTime > Time.time)
{
//Movement
}
else
{
CancelInvoke("MovementFunction"); //Stop the invoking of this function
return;
}
}
You can use co-routines to do this. To do this, create a function that returns type IEnumerator and include a loop to do what you want:
private IEnumerator foo()
{
while(yourCondition) //for example check if two seconds has passed
{
//move the player on a per frame basis.
yeild return null;
}
}
Then you can call it by using StartCoroutine(foo())
This calls the function every frame but it picks up where it left off last time. So in this example it stops at yield return null on one frame and then starts again on the next: thus it repeats the code in the while loop every frame.
If you want to pause for a certain amount of time then you can use yield return WaitForSeconds(3) to wait for 3 seconds. You can also yield return other co-routines! This means the current routine will pause and run a second coroutine and then pick up again once the second co-routine has finished.
I recommend checking the docs as they do a far superior job of explaining this than I could here

Unity3D scene not working properly and lagging after reloading

I have looked all over internet, but I cannot find a solution to my problem. I am trying to create a game and it has 3 scenes: the Game Start, Main and Game over.
The problem is that when trying to load the main level from other scenes it does not do what it has to do (e.g. jump) and the scene itself is lagging. When I try to load only the Main Scene it works ok, but after the character dies and the scene is reloaded, it starts to lag again and I cannot jump or do anything it is supposed to do.
Any ideas on what the problem might be?
using UnityEngine;
using System;
public class Player : MonoBehaviour
{
// The force which is added when the player jumps
// This can be changed in the Inspector window
public Vector2 jumpForce = new Vector2(0, 300);
private bool shouldJump = true;
// Update is called once per frame
private float jumpThreshold = 0.5f;
private float previousJumpTime;
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
// Die by collision
void OnCollisionEnter2D(Collision2D other)
{
Die();
}
void Die()
{
Application.LoadLevel ("main");
}
}
You said your level is called Main, but in the code you are loading "main", i'm not sure if that's the problem, but you seem to be loading the level correctly, so check if is main or Main the exact name of the level.
Also, when compiling, make sure you have all levels checked
With provided data its impossible to say what causes low performance, but I recommend you to use Profiler tool (can be found in personal version of Unity 5) and figure out what scripts and functions are problematic.
Also, try to avoid calling GetComponent<Rigidbody2D>() in Update/FixedUpdate/LateUpdate and cache this component instead.
I second what Utamaru said.
Rigidbody2D myRigidbody2d;
void Start ()
{
myRigidbody2d = GetComponent<Rigidbody2D>(); //Do this once
}
Inside your fixed update, you can do this:
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
myRigidbody2d.velocity = Vector2.zero;
myRigidbody2d.AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
I can't see the rest of your code so I am not really sure that is the problem but give this a try.