How to fix "The object of type 'Transform' has been destroyed but you are still trying to access it." error in Unity - unity3d

Alright, So I'm trying to create a 2.5D side scroll game, and in it I have an enemy shooting me and when the player character gets shot, it gets Destroyed and the scene restarts. Whenever the player gets destroyed, I get the error mentioned. I have no clue how to fix this, but my used code is below. Thank you for the help.
public class DestroyPlayer : MonoBehaviour
{
//Destroys Player Character On Contact
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.CompareTag("Player"))
{
Destroy(collision.gameObject);
}
}
}
This is the player spawn script as well:
public class SpawnScript : MonoBehaviour
{
public GameObject player;
public Transform spawnPoint;
//When Player Character is Destroyed, Respawn Player at Point
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Player"))
{
Scene currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
}
}
The Script in where the error is coming from:
public class TurretControl : MonoBehaviour
{
private Transform _Player;
private float dist;
public float maxRange;
public Transform barrel;
public GameObject _projectile;
public float fireRate, nextShot;
public float cannonBallForce = 40.0f;
void Start()
{
_Player = GameObject.FindGameObjectWithTag("Player").transform;
}
private void Update()
{
dist = Vector3.Distance(_Player.position, transform.position);
if(dist <= maxRange)
{
if(Time.time >= nextShot)
{
nextShot = Time.time + 1f / fireRate;
shoot();
}
}
}
void shoot()
{
GameObject clone = Instantiate(_projectile, barrel.position,transform.rotation);
//Forward force
clone.GetComponent<Rigidbody>().AddForce(-barrel.right * cannonBallForce);
Destroy(clone, 20);
}
}

Related

how to spawn a prefab with filled object fields from scene

The game is working correctly and there arent any other issues apart from the fact that the public fields from the players scripts that are supposed to be filled with game objects from the scene arent filled and im not sure how to do that.
heres an example from one of the scripts: image
and heres what it should look like: image
the joystick area from the second image is from the scene, not an asset: image
here is the code im using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovePlayer : MonoBehaviour
{
public MovementJoystick movementJoystick;
public int playerSpeed;
private Rigidbody2D rb;
bool facingRight = true;
public Animator animator;
public float interval;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
playerSpeed = 7;
interval = 10;
}
// Update is called once per frame
void FixedUpdate()
{
if (movementJoystick.joystickVec.y != 0)
{
rb.velocity = new Vector2(movementJoystick.joystickVec.x * playerSpeed, movementJoystick.joystickVec.y * playerSpeed);
animator.SetFloat("speed", Mathf.Abs(movementJoystick.joystickVec.x));
}
else
{
rb.velocity = Vector2.zero;
animator.SetFloat("speed", Mathf.Abs(0));
}
if (movementJoystick.joystickVec.x < 0 && !facingRight)
{
Flip();
}
if (movementJoystick.joystickVec.x > 0 && facingRight)
{
Flip();
}
}
void Update()
{
if (playerSpeed == 14 && interval > 0)
{
interval -= Time.deltaTime;
}
else
{
playerSpeed = 7;
interval = 10;
}
}
void Flip()
{
transform.Rotate(0f, 180f, 0f);
facingRight = !facingRight;
}
public void SpeedControl(int newplayerSpeed)
{
playerSpeed = newplayerSpeed;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class MovementJoystick : MonoBehaviour
{
public GameObject joystick;
public GameObject joystickBG;
public Vector2 joystickVec;
private Vector2 joystickTouchPos;
private Vector2 joystickOriginalPos;
private float joystickRadius;
// Start is called before the first frame update
void Start()
{
joystickOriginalPos = joystickBG.transform.position;
joystickRadius = joystickBG.GetComponent<RectTransform>().sizeDelta.y / 2;
}
public void PointerDown()
{
joystick.transform.position = Input.mousePosition;
joystickBG.transform.position = Input.mousePosition;
joystickTouchPos = Input.mousePosition;
}
public void Drag(BaseEventData baseEventData)
{
PointerEventData pointerEventData = baseEventData as PointerEventData;
Vector2 dragPos = pointerEventData.position;
joystickVec = (dragPos - joystickTouchPos).normalized;
float joystickDist = Vector2.Distance(dragPos, joystickTouchPos);
if (joystickDist < joystickRadius)
{
joystick.transform.position = joystickTouchPos + joystickVec * joystickDist;
}
else
{
joystick.transform.position = joystickTouchPos + joystickVec * joystickRadius;
}
}
public void PointerUp()
{
joystickVec = Vector2.zero;
joystick.transform.position = joystickOriginalPos;
joystickBG.transform.position = joystickOriginalPos;
}
}
this is how to instantiate the player using photon servers (what i am using)
public GameObject playerToSpawn;
PhotonNetwork.Instantiate(playerToSpawn.name, spawnPoint.position, Quaternion.identity);
There are also 2 buttons i need you to fix: a Shoot button and a Hit button (those are the names). Here is the code for them:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShootButton : MonoBehaviour
{
//i made this script for the button incase you may
have
needed it
}
Here is the shooting script attached to the player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Weapon : MonoBehaviour
{
public Transform firePoint;
public GameObject bulletPrefab;
public Button ShootButton;
void Start()
{
ShootButton.onClick.AddListener(ShootButtonTrue);
}
void ShootButtonTrue()
{
Shoot();
}
void Shoot()
{
Instantiate(bulletPrefab, firePoint.position,
firePoint.rotation);
}
}
Here is the hitting script attached to the Hit button
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HitButton : MonoBehaviour
{
}
And here is the Player Combat script using for hitting:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerCombat : MonoBehaviour
{
public Animator animator;
public Button AttackButton;
public Transform attackPoint;
public float attackRange = 0.5f;
public LayerMask enemyLayers;
void Start()
{
AttackButton.onClick.AddListener(AttackButtonTrue);
}
void AttackButtonTrue()
{
Attack();
}
void Attack()
{
animator.SetTrigger("Attack");
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(attackPoint.position, attackRange, enemyLayers);
foreach(Collider2D enemy in hitEnemies)
{
Debug.Log("u hit someone :O");
enemy.GetComponent<Health>().TakeDamage(15);
}
}
void OnDrawGizmosSelected()
{
if (attackPoint == null)
return;
Gizmos.DrawWireSphere(attackPoint.position, attackRange);
}
}
if you need any other pieces of code just ask
thanks in advance, i will mark the answer as an answer if its a good answer
There are multiple ways to achieve this. For example, you can make your joystick singleton and assign that to the player upon spawn. If you have multiple joysticks in the scene, you can group them under the same parent object and make that parent singleton.
Assuming you only have one joystick in the scene, add this into your joystick class:
public static MovementJoystick Instance { get; private set; }
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(this);
}
else
{
Instance = this;
}
}
In your player class, add this:
void OnEnable()
{
if( movemaneJoystick == null)
{
movementJoystick = MovementJoystick.Instance;
}
}

How to reset the position of object to their start position?

at some point, I want to reset all objects with the PhysicsObject script attached to their first position.
public class PhysicsObjects : MonoBehaviour
{
public float waitOnPickup = 0.1f;
public float breakForce = 35f;
[HideInInspector] public bool pickedUp = false;
[HideInInspector] public Interactions playerInteractions;
Vector3 originalPosition;
Quaternion originalRotation;
public Rigidbody rigidbody;
private void Start()
{
originalPosition = transform.position;
originalRotation = transform.rotation;
}
private void OnCollisionEnter(Collision collision)
{
if (pickedUp)
{
//collision.relativeVelocity.magnitude > breakForce
if (collision.relativeVelocity.magnitude > breakForce)
{
playerInteractions.BreakConnection();
}
}
}
public void ResetObjects()
{
transform.position = originalPosition;
transform.rotation = originalRotation;
if (rigidbody != null)
{
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
}
}
}
and in my other Script:
PhysicsObjects Physics;
private void Start()
{
GameObject inter = GameObject.Find("SeatPillow");
Physics = inter.GetComponent<PhysicsObjects>();
}
....
Physics.ResetObjects();
I tested this only for one object which is SeatPillow and it worked as I specified this object here: GameObject inter = GameObject.Find("SeatPillow");, How would I make this work for all the objects with PhyiscsObject attached without having to specify each one of them?
Create List of PhysicsObjects and populate it when Instantiating desired objects, then use foreach to operate on all of them - physicsObjectsList.ForEach(x => x.Reset());
note: if you are not instantiating those objects from scripts then create public (or [SerializeField] private) list and drug and drop all desired objects from scene to that "other Script" component

XRBaseInteractable does not contain a definition for OnSelectEntering

I am tring to follow this tutorial https://www.youtube.com/watch?v=gmaAK_BXC4c to make a vr gun but I am getting an the following error:
'XRBaseInteractable' does not contain a definition for 'onSelectEntering' and no accessible extension method 'onSelectEntering' accepting a first argument of type 'XRBaseInteractable' could be found (are you missing a using directive or an assembly reference?) [Assembly-CSharp]csharp(CS1061)
for the following lines :
socketInteractor.onSelectEntering.AddListener(AddMagazine);
socketInteractor.onSelectExiting.AddListener(RemoveMagazine);
Here is my code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
[AddComponentMenu("Nokobot/Modern Guns/Simple Shoot")]
public class SimpleShoot : MonoBehaviour
{
[Header("Prefab Refrences")]
public GameObject bulletPrefab;
public GameObject casingPrefab;
public GameObject muzzleFlashPrefab;
[Header("Location Refrences")]
[SerializeField] private Animator gunAnimator;
[SerializeField] private Transform barrelLocation;
[SerializeField] private Transform casingExitLocation;
[Header("Settings")]
[Tooltip("Specify time to destory the casing object")] [SerializeField] private float destroyTimer = 2f;
[Tooltip("Bullet Speed")] [SerializeField] private float shotPower = 500f;
[Tooltip("Casing Ejection Speed")] [SerializeField] private float ejectPower = 150f;
public AudioSource source;
public AudioClip fireSound;
public AudioClip reload;
public Magazine magazine;
public AudioClip noAmmo;
public XRBaseInteractable socketInteractor;
public void AddMagazine(XRBaseInteractable interactable)
{
magazine = interactable.GetComponent<Magazine>();
source.PlayOneShot(reload);
}
public void RemoveMagazine(XRBaseInteractable interactable)
{
magazine = null;
}
public void Slide()
{
}
void Start()
{
if (barrelLocation == null)
barrelLocation = transform;
if (gunAnimator == null)
gunAnimator = GetComponentInChildren<Animator>();
socketInteractor.onSelectEntering.AddListener(AddMagazine);
socketInteractor.onSelectExiting.AddListener(RemoveMagazine);
}
public void PullTheTrigger()
{
if(magazine && magazine.numberOfBullet > 0)
{
gunAnimator.SetTrigger("Fire");
}
else
{
source.PlayOneShot(noAmmo);
}
}
//This function creates the bullet behavior
void Shoot()
{
magazine.numberOfBullet--;
source.PlayOneShot(fireSound);
if (muzzleFlashPrefab)
{
//Create the muzzle flash
GameObject tempFlash;
tempFlash = Instantiate(muzzleFlashPrefab, barrelLocation.position, barrelLocation.rotation);
//Destroy the muzzle flash effect
Destroy(tempFlash, destroyTimer);
}
//cancels if there's no bullet prefeb
if (!bulletPrefab)
{ return; }
// Create a bullet and add force on it in direction of the barrel
Instantiate(bulletPrefab, barrelLocation.position, barrelLocation.rotation).GetComponent<Rigidbody>().AddForce(barrelLocation.forward * shotPower);
}
//This function creates a casing at the ejection slot
void CasingRelease()
{
//Cancels function if ejection slot hasn't been set or there's no casing
if (!casingExitLocation || !casingPrefab)
{ return; }
//Create the casing
GameObject tempCasing;
tempCasing = Instantiate(casingPrefab, casingExitLocation.position, casingExitLocation.rotation) as GameObject;
//Add force on casing to push it out
tempCasing.GetComponent<Rigidbody>().AddExplosionForce(Random.Range(ejectPower * 0.7f, ejectPower), (casingExitLocation.position - casingExitLocation.right * 0.3f - casingExitLocation.up * 0.6f), 1f);
//Add torque to make casing spin in random direction
tempCasing.GetComponent<Rigidbody>().AddTorque(new Vector3(0, Random.Range(100f, 500f), Random.Range(100f, 1000f)), ForceMode.Impulse);
//Destroy casing after X seconds
Destroy(tempCasing, destroyTimer);
}
}

Swapping Two GameObjects on Keypress

When I press Space, the player and the dummy are supposed to swap places. This doesn't just happen once, it needs to happen everytime I press the Space bar so they can swap back and forth. However, sometimes it works and sometimes it doesn't. When it doesn't work, the dummy teleports to the player and then the player teleports inside of the dummy (as that is where its last position is). This just kind of pushes the player about.
Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerSwap : MonoBehaviour
{
public GameObject player;
public GameObject dummy;
public GameObject cameraAnchor;
public Transform playerLastPos;
public Transform dummyLastPos;
public bool haveSwapped;
public bool canSwap;
public float swapRadius;
public LayerMask dummyInRadius;
// Update is called once per frame
void Update()
{
canSwap = Physics.CheckSphere(player.transform.position, swapRadius, dummyInRadius);
SwapTransforms();
if (canSwap)
{
if (Input.GetKeyDown(KeyCode.Space))
{
haveSwapped = !haveSwapped;
Swapping();
}
}
else
{
Debug.Log("Can't Swap");
}
cameraAnchor.transform.position = player.transform.position;
}
public void SwapTransforms()
{
if (haveSwapped)
{
dummyLastPos.position = player.transform.position;
playerLastPos.position = dummy.transform.position;
}
else
{
dummyLastPos.position = dummy.transform.position;
playerLastPos.position = player.transform.position;
}
}
public void Swapping()
{
if (haveSwapped)
{
player.transform.position = dummyLastPos.position;
dummy.transform.position = playerLastPos.position;
}
else
{
player.transform.position = playerLastPos.position;
dummy.transform.position = dummyLastPos.position;
}
}
public void OnDrawGizmos()
{
Gizmos.color = Color.white;
Gizmos.DrawWireSphere(player.transform.position, swapRadius);
}
}
I've solved it. After cleaning up most of the code and realsing I had to turn off movement and the character controller on the player (Link:Unity Answers), I came up with this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerSwap : MonoBehaviour
{
public ThirdPersonMovement playerMove;
public GameObject player;
public GameObject dummy;
public GameObject cameraAnchor;
public Transform playerLastPos;
public Transform dummyLastPos;
public bool canSwap;
public float swapRadius;
public LayerMask dummyInRadius;
// Update is called once per frame
void Update()
{
canSwap = Physics.CheckSphere(player.transform.position, swapRadius, dummyInRadius);
if (canSwap)
{
if (Input.GetKeyDown(KeyCode.Space))
{
playerMove.canMove = false;
player.GetComponent<CharacterController>().enabled = false;
SetTransforms();
}
}
else
{
Debug.Log("Can't Swap");
}
cameraAnchor.transform.position = player.transform.position;
}
public void SetTransforms()
{
dummyLastPos.position = dummy.transform.position;
playerLastPos.position = player.transform.position;
Debug.Log("Set Transforms");
Swapping();
}
public void Swapping()
{
player.transform.position = dummyLastPos.position;
dummy.transform.position = playerLastPos.position;
dummy.transform.rotation = player.transform.rotation;
Debug.Log("Swap");
playerMove.canMove = true;
player.GetComponent<CharacterController>().enabled = true;
}
public void OnDrawGizmos()
{
Gizmos.color = Color.white;
Gizmos.DrawWireSphere(player.transform.position, swapRadius);
}
}

Life bar doesn't change after players destroy collider

I am trying to do a 2d game and my object doesn't affect the player's lifebar after they collide. The player's healthbar will will get a bigger lifetime but I think something it's wrong with the scripts. (Also the collider of object that needs to be destroy is "is trigger" checked). I put this PowerUp on the object, and the character script and healthbar script on the player.
Character.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
public class Character : MonoBehaviour
{
Rigidbody2D rb;
float dirX;
[SerializeField]
float moveSpeed = 5f, jumpForce = 600f, bulletSpeed = 500f;
Vector3 localScale;
public Transform barrel;
public Rigidbody2D bullet;
// Use this for initialization
void Start()
{
localScale = transform.localScale;
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
dirX = CrossPlatformInputManager.GetAxis("Horizontal");
if (CrossPlatformInputManager.GetButtonDown("Jump"))
Jump();
if (CrossPlatformInputManager.GetButtonDown("Fire1"))
Fire();
}
void FixedUpdate()
{
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
}
void Jump()
{
if (rb.velocity.y == 0)
rb.AddForce(Vector2.up * jumpForce);
}
void Fire()
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
HealthBar.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HealthBar : MonoBehaviour
{
public Slider slider;
public Gradient gradient;
public Image fill;
public float health = 100;
public void SetMaxHealth(int health)
{
slider.maxValue = health;
slider.value = health;
fill.color = gradient.Evaluate(1f);
}
public void SetHealth(int health)
{
slider.value = health;
fill.color = gradient.Evaluate(slider.normalizedValue);
}
}
PowerUp.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PowerUp : MonoBehaviour
{
public float multiplayer = 1.4f;
public GameObject pickupEffect;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
Pickup(other);
}
}
void Pickup(Collider player)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
HealthBar stats =player.GetComponent<HealthBar>();
stats.health *= multiplayer;
// Remove Effect
Destroy(gameObject);
}
}
First of all as others already mentioned for a 2D game with Rigidbody2D and (hopefully) Collider2D components you want to use OnTriggerEnter2D instead!
The Physics and Physics2D are two completely separated engines and don't know each other. A 3D OnTriggerEnter will never get called for 2D physics events like collisions etc.
Then note that
Also the collider of object that needs to be destroy is "is trigger" checked
this is exactly the wrong way round. If your object shall track a OnTriggerEnter then the one that enters (the player) should have isTrigger enabled! In most cases you don't want to do this because the player shall e.g. actually collide with the floor and not fall through it etc.
So what you would need is rather putting an additional script on the player itself that waits for other trigger objects to enter!
Then to be sure either debug your code line by line or add additional logs in order to see what happens:
PowerUp
// put this on the pickup item
public class PowerUp : MonoBehaviour
{
// Make these private, nobody else needs access to those
// (Encapsulation)
[SerializeField] private float multiplayer = 1.4f;
[SerializeField] private GameObject pickupEffect;
public void Pickup(HealthBar stats)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
stats.health *= multiplayer;
// Remove Effect
Destroy(gameObject);
}
}
PowerUpDetector (on the player)
// put on player!
public class PowerUpDetector : MonoBehaviour
{
// reference this via the Inspector
[SerializeField] private HealthBar healthbar;
private void Awake()
{
if(!healthBar) healthBar = GetComponent<HealthBar>();
}
private void OnTriggerEnter2D(Collider2D other)
{
// or whatever tag your powerups have
if (!other.CompareTag("PowerUp"))
{
Debug.LogWarning($"Registered a collision but with wrong tag: {other.tag}", this);
return;
}
var powerup = other.GetComponent<PowerUp>();
if(!powerup)
{
Debug.LogError($"Object {other.name} is tagged PowerUp but has no PowerUp component attached", this);
return;
}
Debug.Log("Found powerup, pick it up!", this);
powerup.Pickup(healthbar);
}
}
Well, and then what you do is only changing the float value
stats.health *= multiplayer;
but you never update the GUI accordingly like you would do when instead using
stats.SetHealth(stats.health * multiplayer)
(Btw: I think you mean multiplier ;) )
I would suggest to rather implement a property like e.g.
public class HealthBar : MonoBehaviour
{
// Make these private, nobody else needs access to those
// (Encapsulation)
[SerializeField] private Slider slider;
[SerializeField] private Gradient gradient;
[SerializeField] private Image fill;
[SerializeField] private float health = 100;
public float Health
{
get { return health; }
set
{
health = value;
slider.value = health;
fill.color = gradient.Evaluate(slider.normalizedValue);
}
}
// be careful with variable names if you have this name already
// for a class field .. was ok this time but might get confusing
public void SetMaxHealth(int value)
{
slider.maxValue = value;
// The property handles the rest anyway
Health = value;
}
}
so now instead of calling SetHealth you simply assign a new value to Health and its setter is automatically execute as well so your GUI is updated.
public void Pickup(HealthBar stats)
{
//Spawn a cool effect
Instantiate(pickupEffect, transform.position, transform.rotation);
//Apply effect to the player
stats.Health *= multiplayer;
Destroy(gameObject);
}
Your qustions are below,
player will get a bigger lifetime
Also the collider of object that needs to be destroy
What situation possibly you could have
Check your Character is Triggered, so it calls 'OnTriggerEnter' and also check tag.
Check your powerUp item that checked true as trigger, 'isTrigger'
Check if collider or hit object scale is tiny, it won't work with collider.
If you have nothing with above check-list, then it should be working