Why is my transform null in Update?
Here's a snippet of code that passes transform data to a projectile:
private void OnTriggerEnter2D(Collider2D collider)
{
// Check that the collision is with the player and that it
// ignores any collider that is considered a trigger.
if(collider.gameObject.tag == "Player")
{
// The player character will multiple box colliders,
// but we only want to interact with the players hurt box here.
if(!collider.isTrigger)
{
moveSpeed = 0;
// If true, the player is further right on the x axis then the skunk. Else, it is to the left.
if(playerCharacter.transform.position.x > transform.position.x)
{
// If true, the skunk is facing the player. Else, it's facing away.
if(transform.right.x == transform.localScale.x)
{
Flip();
facingRight = false;
}
else
{
facingRight = false; // In case it was previously true
}
}
else
{
// If true the skunk is facing the player. Else, it's facing away.
if(transform.right.x != transform.localScale.x)
{
Flip();
facingRight = true;
}
else
{
facingRight = true;
}
}
Debug.Log(playerCharacter.gameObject.transform.position);
skunkProjectile.FireProjectile(projectileSpawnPoint, playerCharacter.gameObject.transform);
}
}
}
And here is the code that is suppose to receive that information:
public class SkunkProjectile : Projectile
{
private int xVelocity;
private bool fireLeft;
private Transform fireDirection;
/**
The fire projectile class creates an instance of a projectile and sets
the game object that fires the weapon.
@param spawnPosition (Transform) - The position the projectile spawns at in local space.
@param projectileLauncher (GameObject) - The game object that fires the projectile.
*/
virtual public void FireProjectile(Transform spawnPosition, Transform fireDirection)
{
Instantiate(this,
new Vector3(spawnPosition.position.x, spawnPosition.position.y, spawnPosition.position.z),
new Quaternion(0.0f,0.0f,0.0f,0.0f));
this.fireDirection = fireDirection;
}
// Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
private void Start()
{
xVelocity = 2;
rb = GetComponent<Rigidbody2D>();
Destroy(gameObject, 2.0f);
}
// Update is called every frame, if the MonoBehaviour is enabled.
private void Update()
{
rb.velocity = new Vector2(fireDirection.position.x, 0.0f);
}
}
So why is it that when the code isn't getting the new fireDirection on the next frame? When this code fires, I get a NullReferenceException no mater how many times i trigger the script.
Answer by vycanis1801 · Sep 30, 2020 at 08:36 AM
So, I found something that seems to work, but I feel like my code is starting to get a bit spaghetti is there a clearer solution for this. I'll post all relevant code.
PlayerCharacter:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
[RequireComponent(typeof(Rigidbody2D), typeof(PlayerController), typeof(Collider2D))]
public class PlayerCharacter : MonoBehaviour
{
// Required components
private Collider2D collider2d;
private Rigidbody2D rb;
private PlayerController playerController;
// Serialized components/variables
[Tooltip("Used to calculate the amount of push back happens when the player is damaged.")]
[SerializeField] private float hurtForce = 1f;
[Tooltip("Players default health")]
[SerializeField] private int health;
[Tooltip("Player health UI element")]
[SerializeField] private Text healthAmount;
/**
The rigid body is used in determining whether or not the player is infornt or behind an enemy.!--
GetRBPosition allows another script to access this information.!--
@return rb (RigidBody2D) - physics object used to get the players location
*/
public Vector2 GetRBPosition() { return rb.position; }
// Awake is called when the script instance is being loaded.
private void Awake()
{
collider2d = GetComponent<Collider2D>();
rb = GetComponent<Rigidbody2D>();
playerController = GetComponent<PlayerController>();
}
// Sent when an incoming collider makes contact with this object's collider (2D physics only)
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag == "Enemy")
{
Enemy enemy = collision.gameObject.GetComponent<Enemy>();
// Get the velocity state of the player controller. If the player is falling,
// "kill" the enemy. Otherwise take damage from the enemy.
if(playerController.getState() == VelocityState.falling)
{
enemy.SetDeathTrigger(); // This kills the enemy. It also assumes that all enemies have one health.
playerController.Jump();
}
else
{
// Determine which direction to push the player when they are in the hurt state
if(collision.gameObject.transform.position.x > transform.position.x)
{
rb.velocity = new Vector2(-hurtForce, rb.velocity.y);
playerController.updateState(VelocityState.hurt);
TakeDamage();
}
else
{
rb.velocity = new Vector2(hurtForce, rb.velocity.y);
playerController.updateState(VelocityState.hurt);
TakeDamage();
}
}
}
}
public void TakeDamage()
{
health--;
// Uncomment when UI is ready.
// healthAmount.text = health.ToString();
// Reset the level if player dies.
if(health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
}
Enemy:
using UnityEngine;
/// <summary>
/// This is the base class for enemy behavior that should be applicable to all enemies in the game.
/// This class handles basic enemy movement that can be refined in child classes if desired.
///</summary>
[RequireComponent(typeof(Rigidbody2D), typeof(Animator))]
public class Enemy : MonoBehaviour
{
public float moveSpeed;
// Required components
protected Rigidbody2D rigidBody2D;
protected Animator animator;
protected bool facingRight = true;
[Tooltip("A reference to the player's character. It should be some Joe prefab.")]
protected GameObject playerCharacter;
// Seralized fields
// Increases speed by the factor set in the editor.
[SerializeField] private float movementMultiplier = 1f;
protected Vector3 movement;
/**
Used to update the enemies animator component.
*/
public void SetDeathTrigger()
{
animator.SetTrigger("death");
}
/**
Flip is a function that "flips" an enemies sprite
by inverting it's x-scale.
*/
protected void Flip()
{
transform.localScale =
new Vector3(-transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
// Awake is called when the script instance is being loaded.
public void Awake()
{
rigidBody2D = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
playerCharacter = GameObject.FindGameObjectWithTag("Player");
}
private void Death()
{
Destroy(this.gameObject);
}
// Flip the sprite if it collides with platforms/walls, and invert its speed.
private void OnCollisionEnter2D(Collision2D collider)
{
if(collider.gameObject.tag == "Platforms" || collider.gameObject.tag == "Enemy")
{
Flip();
moveSpeed = -moveSpeed;
}
}
}
Skunk (inherits from enemy) :
using UnityEngine;
/// <summary>
/// The Skunk class is a child of the enemy class.
/// It has the added ability of movement and attacking the
/// player if they get too close.
/// </summary>
public class Skunk : Enemy
{
// Serialized fields. (Private memebers accessable by the editor.)
[Tooltip("This is where your projectile prefab should be inserted")]
[SerializeField] private SkunkProjectile skunkProjectile;
[Tooltip("A transform that should be a child component of the skunk")]
[SerializeField] private Transform projectileSpawnPoint;
// Private memebers
private float savedMoveSpeed;
/**
Get facing right is used to inform a caller if this sprite is facing in a x positive direction
@return facingRight (bool) - return true if facing right. Otherwise false.
*/
public bool GetFacingRight() { return facingRight; }
// Sent when another object enters a trigger collider attached to this object (2D physics only).
private void OnTriggerEnter2D(Collider2D collider)
{
// Check that the collision is with the player and that it
// ignores any collider that is considered a trigger.
if(collider.gameObject.tag == "Player")
{
// The player character will multiple box colliders,
// but we only want to interact with the players hurt box here.
if(!collider.isTrigger)
{
moveSpeed = 0;
// If true, the player is further right on the x axis then the skunk. Else, it is to the left.
if(playerCharacter.transform.position.x > transform.position.x)
{
// If true, the skunk is facing the player. Else, it's facing away.
if(transform.right.x == transform.localScale.x)
{
Flip();
facingRight = false;
}
else
{
facingRight = false; // In case it was previously true
}
}
else
{
// If true the skunk is facing the player. Else, it's facing away.
if(transform.right.x != transform.localScale.x)
{
Flip();
facingRight = true;
}
else
{
facingRight = true;
}
}
Instantiate(skunkProjectile, new Vector3(projectileSpawnPoint.position.x, projectileSpawnPoint.position.y, projectileSpawnPoint.position.z), new Quaternion(0.0f,0.0f,0.0f,0.0f));
}
}
}
// Sent when another object leaves a trigger collider attached to this object (2D physics only).
private void OnTriggerExit2D(Collider2D collider)
{
if (collider.gameObject.tag == "Player")
{
if(!collider.isTrigger)
{
moveSpeed = savedMoveSpeed;
// This makes sure that the skunk does move in the wrong direction
// after the player has left its attack range
if(moveSpeed > 0.0f && transform.localScale.x < 0.0f ||
moveSpeed < 0.0f && transform.localScale.x > 0.0f)
{
moveSpeed = -moveSpeed;
}
}
}
}
// Called once per frame
private void Update()
{
rigidBody2D.velocity = new Vector2(moveSpeed, rigidBody2D.velocity.y);
// It won't try and update the movement speed if the skunk is stopped.
if(moveSpeed != 0.0f)
{
savedMoveSpeed = moveSpeed;
}
}
}
Projectile:
using System.Collections;
using UnityEngine;
/// <summary>
/// The projectile class is the base class for all projectiles.
/// It is responsible for when the projectile spawns and when it is destroyed.
/// Child classes should always be attached to a spcific projectile prefab.
/// </summary>
public class Projectile : MonoBehaviour
{
// Serialized feilds. (protected memebers accessable by the editor.)
[Tooltip("This field comes from the projectile base class.")]
[SerializeField]protected Rigidbody2D rb;
private void OnTriggerEnter2D(Collider2D collision)
{
// When player collides with trigger, they take damage, and projectile disappears
if(collision.gameObject.tag == "Player")
{
collision.gameObject.GetComponent<PlayerCharacter>().TakeDamage();
Destroy(this.gameObject);
}
}
}
Skunk Projectile (inherits from projectile) :
using UnityEngine;
///<summary>
/// The skunk projectile is a child class of projectile.
/// This class is responsible for calculating the trajectory of
/// the projectile.
/// This script should only be used with the SkunkProjectile prefab.
/// Which should be at: Assets\Prefabs\Projectiles\SkunkProjectile.prefab
///</summary>
public class SkunkProjectile : Projectile
{
private int xVelocity = 2;
private bool fireLeft;
private Transform fireDirection;
// Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
private void Start()
{
xVelocity = 2;
rb = GetComponent<Rigidbody2D>();
PlayerCharacter pc = FindObjectOfType<PlayerCharacter>();
playerPosition = pc.transform.position;
Skunk skunk = FindObjectOfType<Skunk>();
skunkPosition = skunk.transform.position;
Destroy(gameObject, 2.0f);
}
// Update is called every frame, if the MonoBehaviour is enabled.
private void Update()
{
Debug.Log(skunkPosition);
if (playerPosition.x < skunkPosition.x)
{
rb.velocity = new Vector2(-2, 0);
}
else if(playerPosition.x > skunkPosition.x)
{
rb.velocity = new Vector2(2, 0);
}
}
All advice will be appreciated ^_^
Your answer
Follow this Question
Related Questions
PLEAASEE I NEED HELP!!! How to turn Mesh Renderer of cubes on and off by pushing buttons? 1 Answer
Problemas con Input.GetKeyDown() 0 Answers
I want my script to wait 2 seconds before continue in a condition, in update, using C# 2 Answers
OnCollisionEnter2D not working after update. 0 Answers
Help I've Lost all Hope, A script stopped working on my Player Character Prefab! 0 Answers