Search code examples
c#unity-game-engine

Unity C# Directly Referencing a RigidBody2D Component to a Prefab after it is Instantiated


I have made a pong game, and created Power Ups that spawn around the screen randomly. One of the Power Ups will speed up the velocity of the ball. Since the Power Up is a prefab object and does not exist until it is spawned, I cannot directly reference the balls RigidBody2D component using [SerializeField] private RigidBody2D, as Unity does not allow you to reference in-game objects to prefabs which do not exist in the Scene at start up. I am wondering how I should get around this issue.

The velocity parameter should be edited when the Power Up spawns and is triggered (I have the collider working and spawner working..), but I am not able to directly point to the instance of the ball when this event triggers. Am I looking at this the wrong way all together?

First, before I knew the issue I directly referenced the RigidBody2D component: [SerializeField] private RigidBody2D _rb; in the prefab script which should then edit the velocity of the ball's RigidBody2D component. I then dragged the Ball GameObject into the input field created in the inspector view for the Prefab script to edit velocity (but this is not allowed). From here, within the Prefab script, I created a reference to the current velocity of the RigidBody2D velocity in a Vector2 and also edited it by multiplying it by 5f: Vector2 newSpeed = _rb.velocity * 5f;

Then I would define the RigidBody2D velocity to this new speed: _rb.velocity = newSpeed;

I got thrown an error that says: "Object reference not set to an instance of an object" which leads me to believe that the RigidBody2D component is not being pointed to...

using Unity.VisualScripting;
using UnityEngine;

public class SpeedUp : MonoBehaviour
{
    [SerializeField] private SpawnManager spawnManager;
    //[SerializeField] private BallSet _ballSet;
    private float _timer;
    private bool _hasPower;

    [SerializeField] private Rigidbody2D _rb;
    void Start()
    {
        // Find the SpawnManager in the scene
        spawnManager = FindObjectOfType<SpawnManager>();
        _timer = 5f;
        _hasPower = false;
    }

    void Update(){
        while(_hasPower){
            _timer += _timer - Time.deltaTime;
            if(_timer <= 0){
                PowerDown();
            }
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Ball"))
        {
            Debug.Log("Power-up hit by the ball!");

            // Notify the SpawnManager
            if (spawnManager != null)
            {
                spawnManager.OnPowerUpHit(gameObject);
                PoweredUp();
            }
        }
    }
    private void PoweredUp(){
        Debug.Log("Speed Up Initiated!");
        _hasPower = true;
        Vector2 newSpeed = _rb.velocity;
        newSpeed = newSpeed * 5f;
        _rb.velocity = newSpeed;
        //Debug.Log("The Ball Speed is now: " + _ballSet.rigidBody.velocity);
    }

    private void PowerDown(){
        Debug.Log("Speed Up Ended!");
        _hasPower = false;
        _timer = 5f;
        Vector2 newSpeed = _rb.velocity;
        _rb.velocity = newSpeed / 5f;
    }
}

Essentially, I'd like to know the best practice for locating an object in the scene for a script in a Prefab Object when it becomes instantiated because right now I am unable to do so via referencing, and I'd like to avoid any GetComponent or FindType etc declarations as I have heard they are inefficient for the CPU. I am expecting the velocity to be multiplied by 5, thus increasing the balls speed.


Solution

  • You can get the rigidbody from the ball, specifically its collider.
    You have a reference to the balls collider via the OnTriggerEnter2D function.
    Collider2D has a property attachedRigidbody. (the Rigidbody2D attached to the Collider2D)

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Ball"))
        {
            _rb = collision.attachedRigidbody;
    
            Debug.Log("Power-up hit by the ball!");
        }
    }
    

    If that is null for some reason, use GetComponent. GetComponent (or GetComponentInChildren or GetComponentInParent) are extremely useful and should not be avoided! It is advised not to use it in tight loops or every update, where it can be avoided, but in the case of getting a reference to cache it is fine. Setting up references prior is preferred, it is not always possible and avoiding GetComponent altogether is a useless optimization.