Instantiate is only working sometimes in unity - unity3d

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.

Related

Resetting Character Position in Unity is Not Working

I'm working on a sports game where if a player skates into the goalie crease, I want all the player positions to reset to the center of the ice and do a three second countdown before play resumes.
I have tried to hardcode the starting position for the main player in a variable called PlayerStart and I call Player.transform.position = PlayerStart. When I did this, the player didn't move so I tried to switch the object I was setting as the player. This did what I wanted, but the mouse functionality changes for some reason and when the countdown ends, the player just goes right back to the position they were in before the crease violation was called.
Other things I've tried:
transform.SetPositionAndRotation
PlayerStart = Player.transform.position (instead of hard coding the numbers in)
Here is my code:
public class Crease : MonoBehaviour
{
private float Delay = 0;
private bool CreaseViolated = false;
private GameObject Player;
public Vector3 PlayerStart;
// Start is called before the first frame update
void Start()
{
Ring = GameObject.Find("Ring");
Player = GameObject.Find("FPSController");
PlayerStart = new Vector3(29.75f, 6.03999996f, 4.42000008f);
}
// Update is called once per frame
void Update()
{
Delay -= Time.deltaTime;
if (CreaseViolated)
{
CreaseViolation();
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name != "Ring"
&& other.gameObject.name != "TeamGoalie"
&& other.gameObject.name != "OpponentGoalie")
{
CreaseViolated = true;
Delay = 3;
}
}
void CreaseViolation()
{
if (Delay > 0)
{
PlayerTip.GetComponent<PickupRing>().HasRing = false;
Opponent.GetComponent<AI>().HasRing = false;
Ring.transform.parent = null;
}
else
{
text.text = " ";
if (CreaseViolated)
{
Debug.Log("Player position before: " + Player.transform.position);
Player.transform.position = PlayerStart;
Debug.Log("Player position after: " + Player.transform.position);
//Player.transform.SetPositionAndRotation(PlayerStart + new Vector3(0f, 0.800000012f, 0f), new Quaternion(0f, -0.707106829f, 0f, 0.707106829f) + new Quaternion(0f, 0f, 0f, 1f));
GameObject.Find("Countdown").GetComponent<CountdownText>().timeRemaining = 4;
CreaseViolated = false;
}
}
}
}
Here is a short YouTube video showing my code and the demo: https://www.youtube.com/watch?v=mZt_4AppBh8
this problem is all solved now thanks to an awesome person at my university helping me out! The solution was disabling the CharacterController before repositioning the player and then enabling it again after.
So this:
Player.transform.position = PlayerStart;
in the CreaseViolation function becomes
cc.enabled = false;
PlayerController.transform.position = PlayerControllerStart;
cc.enabled = true;
with cc being declared earlier as
private CharacterController cc;
and in the start function I assigned it with the value
cc = Player.GetComponent<CharacterController>();
with PlayerController being set to the FPSController.
I renamed Player to PlayerController for more clarity.
Hopefully this helps anyone having the same problem I was having!
The Above answer works perfectly but might bring some issues when doing networked changes in player transform positions.
The reason the transform change doesn't work without disabling the Character Controller is because the character controller overrides the transforms.
To Disable this and enable an Auto Synchronization between transforms GoTo:
Edit > Project Settings > Physics > (Enable Auto Sync Transforms)

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!

Field of View for enemy only works with 1 enemy

I have some problems with a script for a "Field Of View" of the enemy. After watching a tutorial to create a field of view for the player I thought I can switch it to the enemy's so that they detect the player and do some other stuff. I created a boolean variable playerInRange to detect if the enemies can detect the player and set this variable to true or false.
It works fine with just one enemy. When I add another one, the new enemy will not detect the player. So maybe it is related to the coroutine, but I am not sure.
Here is a little bit of my code:
void Start() {
StartCoroutine("FindTargetsWithDelay", .2 f);
}
IEnumerator FindTargetsWithDelay(float delay) {
while (true) {
yield
return new WaitForSeconds(delay);
FindVisibleTargets();
}
}
public void FindVisibleTargets() {
visibleTargets.Clear();
Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
for (int i = 0; i < targetsInViewRadius.Length; i++) {
Transform target = targetsInViewRadius[i].transform;
Vector3 dirToTarget = (target.position - transform.position).normalized;
if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2) {
float dstToTarget = Vector3.Distance(transform.position, target.position);
if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask)) {
// Not so nice solution!
// The movement should be in a separate script!
visibleTargets.Add(target);
nav.SetDestination(player.position);
anim.SetBool("IsRunning", true);
if (dstToTarget < attackRange) {
playerInRange = true;
Debug.Log(playerInRange);
}
}
} else {
anim.SetBool("IsRunning", false);
playerInRange = false;
Debug.Log(playerInRange);
}
}
}
Thank you guys for your little hint. It was really a little hierarchy problem :( Sorry for that newbie/DAU question.
Cheers
Nico

MissingReferenceException: Error after destroying a moving game object

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.

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.