Why is this collision not working?

I am trying to do a simple Dr Who themed game where you pilot the TARDIS in the time vortex, but for some reason the collision isn’t working - the current thing is that when the Tardis hits a Dalek, it displays a game over text on the console for debug purposes, but either the objects aren’t colliding or the game over tag isn’t switching.

The game has a script to translate the enemy forwards to keep the movement speed consistent, and the arrow keys move the tardis relative to the camera. The tardis has a rigidbody and the dalek does not (it doesn’t work if the dalek has a rigidbody as well either, I tried that) and the tardis is constantly spinning on it’s vertical axis to make things look more dynamic which is why the object moves relative to the camera. However, the Dalek just phases through the tardis and doesn’t do anything - why is this?

Here is my code:

Dalek code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Dalek : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0,-1 * Time.deltaTime * 10);
    }
}

Tardis:

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public bool gameOver;
    public float rotateSpeed = 120;
    public float horizontalInput;
    public float verticalInput;
    public float xRange = 5f;
    public float yRange = 0.5f;
    public float moveSpeed = 10;
    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
        Transform camTransform = Camera.main.transform;
        horizontalInput = Input.GetAxis("Horizontal");
        verticalInput = Input.GetAxis("Vertical");
        transform.Rotate(new Vector3(0, 1, 0) * Time.deltaTime * rotateSpeed);
       
        if (transform.position.x < -xRange && !gameOver) {
            transform.position = new Vector3(-xRange, transform.position.y ,transform.position.z);
        }
        else if (transform.position.x > xRange && !gameOver)
        {
            transform.position = new Vector3(xRange, transform.position.y, transform.position.z);
        }
        else if (transform.position.y < -yRange && !gameOver)
        {
            transform.position = new Vector3(transform.position.x, -yRange, transform.position.z);
        }
        else if (transform.position.y > yRange && !gameOver)
        {
            transform.position = new Vector3(transform.position.x, yRange, transform.position.z);
        }
        transform.Translate(camTransform.right * Time.deltaTime * horizontalInput *moveSpeed, Space.World);
        transform.Translate(camTransform.up * Time.deltaTime * verticalInput * moveSpeed, Space.World);
    }

    private void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.CompareTag("dalek")){
            gameOver = true;
            Debug.Log("Game Over");
            Destroy(other.gameObject);
        }
    }

First of all, if you have a rigid body (non-kinematic), it’s undesirable to directly manipulate its transform because physics calculations will be incorrect. Instead, you should control it through the methods of the rigid body, specialized for physics, such as MoveRotation, MovePosition, and so on.

Secondly, clarify your specific goal: Do you want this TARDIS to behave like a rigid body, meaning it automatically repels from obstacles and other objects can collide with it, altering its position and rotation? Or do you not need physics for the objects at all, and you just want to know when one object intersects with another, without physical interaction between them?

The important thing will just be telling when the bodies intersect, once that happens it would play an explosion effect from the Tardis and end the game ideally. There would eventually be a second item to interact with that once it intersects it adds a powerup as I’m on that tutorial now. Will there be another way to check the collision without using a Rigidbody? I didn’t think it would cause issues as other tutorials that used collisions moved objects using the transform method.

You can compare the distance of their radius instead of using physics:

public bool CheckCollisionByRadius(Transform obj1, Transform obj2)
{
  float radius1 = GetComponent<Collider>().bounds.extents.x * 2;
  float radius2 = obj2.GetComponent<Collider>().bounds.extents.x * 2;

  float distance = Vector3.Distance(obj1.position, obj2.position);

  if(distance < radius1 + radius2)
  { 
    return true;
  }

  return false;
}

I’ve added a capsule collider to the dalek as I think I forgot to do that and the tardis already had a box. Hasn’t effected the existing code but i’ll try the one by radius and see if that works. Thanks. I’ll let you know if that works. The dalek didn’t have a native collider as it’s imported from blender, so I had to do it manually.

There are always several options, for example, some of them are:

  • You can do without rigid bodies altogether, checking collisions with each other by distance. For example, if objects are close to each other, they probably collided.
  • Alternatively, you can make Tardis kinematic (so you can control its transform directly, as you do now), but make the collider of Tardis or Dalek a trigger. Then the intersection will also trigger, but instead of OnCollisionEnter(Collision other), you need to implement OnTriggerEnter(Collider other).

There are more options, but perhaps these will suit you right now.

Small advice not related to the topic, but regarding the code. Right now, your Tardis probably jerks when it approaches the boundaries it shouldn’t cross. This happens because you correct its position BEFORE assigning a new one. Therefore, any adjustments should always be made at the very end. I slightly modified your code, adding position correction at the very end and shortened some parts. Perhaps you will find it useful to see an alternative version where the trigger implementation is included, as the trigger option is probably more convenient for you…

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public bool gameOver;
    public float rotateSpeed = 120;
    public float horizontalInput;
    public float verticalInput;
    public float xRange = 5f;
    public float yRange = 0.5f;
    public float moveSpeed = 10;

    private Transform _cameraTransform;

    private void Start()
    {
        _cameraTransform = Camera.main.transform;
    }

    void Update()
    {
        if (gameOver)
            return;

        horizontalInput = Input.GetAxis("Horizontal");
        verticalInput = Input.GetAxis("Vertical");

        Vector3 newPos = transform.position;
        newPos += _cameraTransform.right * horizontalInput * moveSpeed * Time.deltaTime;
        newPos += _cameraTransform.up * verticalInput * moveSpeed * Time.deltaTime;

        newPos.x = Mathf.Clamp(newPos.x, -xRange, xRange);
        newPos.y = Mathf.Clamp(newPos.y, -yRange, yRange);

        transform.position = newPos;
        transform.Rotate(new Vector3(0, 1, 0) * Time.deltaTime * rotateSpeed);
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("dalek"))
        {
            gameOver = true;
            Debug.Log("Game Over");
            Destroy(other.gameObject);
        }
    }

}

Thanks, I’ll try the trigger thing as it will require less of a rewrite. The Minimum Viable Product for this is the daleks and powerup, so it’ll be nice to get it working.

Cool, so the trigger works, thanks. I’ve also never heard of the clamp function so that will help to make the code more efficient, I was constraining the way I was because that’s how the tutorials taught me to do it.