Collision based on layers not working as intended

I am doing a car racing project, a part of said project is a system for returning stuck AI cars into the road.
The script stores the nearest waypoint id(waypoints are in an array on another script) in a variable.
When the car is stuck for x amount of seconds we do the following:

  • Place the car at a position relative to the stored waypoint
  • Change the layer of the gameobject that contains the rigidbody to another layer called “ReSpawn”. This layer is configured to not collide with the other cars respawning or otherwise.
  • When the car has been in its new position for 3 seconds, it returns to the default layer, and is now able to collide with other cars.

I have 2 emerging bugs in this implementation:

  • The collision layers just don’t work. The car still collides with incoming cars and with other respawning cars.
  • The cars seem to share the currentTrackerWP variable, even though it is not static. Resulting in cars spawning not in their designated waypoints, but in the next waypoint of the car that updated it last(I am not even sure if this is true, just my guess from the debug logs the code has spewed in the past)

This is the script, the layer change happens on line 82, in line 88 there is a method being invoked that restores the layer, that method is in line 57.

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

public class AIController : MonoBehaviour
{
    public Circuit circuit;
    Drive ds;
    public float brakingSensitivity = 1.1f;
    public float steeringSensitivity = 0.01f;
    public float accelSensitivity = 0.3f;
    public float distanceToTargetThreshold = 2.0f; //threshold, aumentar si el coche empieza a dar vueltas entorno a un waypoint
    Vector3 target;
    Vector3 nextTarget;
    int currentWP = 0;
    float totalDistanceToTarget;

    GameObject tracker; //El objeto al que sigue el vehículo
    int currentTrackerWP = 0;
    public float lookAhead = 12;
    float lastTimeMoving = 0;   

    void Start()
    {
        ds = this.GetComponent<Drive>();
        target = circuit.waypoints[currentWP].transform.position;
        nextTarget = circuit.waypoints[currentWP + 1].transform.position;
        totalDistanceToTarget = Vector3.Distance(target, ds.rigidbody.gameObject.transform.position);

        tracker = GameObject.CreatePrimitive(PrimitiveType.Capsule);
        DestroyImmediate(tracker.GetComponent<Collider>());
        tracker.GetComponent<MeshRenderer>().enabled = false;
        tracker.transform.position = ds.rigidbody.gameObject.transform.position;
        tracker.transform.rotation = ds.rigidbody.gameObject.transform.rotation;

        //Ghost Shader Effect
        this.GetComponent<Ghost>().enabled = false;
    }

    void ProgressTracker()
    {
        Debug.DrawLine(ds.rigidbody.gameObject.transform.position, tracker.transform.position);
        if(Vector3.Distance(ds.rigidbody.gameObject.transform.position, tracker.transform.position) > lookAhead)
            return;

        tracker.transform.LookAt(circuit.waypoints[currentTrackerWP].transform.position);
        tracker.transform.Translate(0, 0, 1.0f);

        if(Vector3.Distance(tracker.transform.position, circuit.waypoints[currentTrackerWP].transform.position) < 1) //Tolerancia
        {
            currentTrackerWP++;
            if(currentTrackerWP >= circuit.waypoints.Length)
                currentTrackerWP = 0;
        }
    }

    void ResetLayer()
    {
        ds.rigidbody.gameObject.layer = 0;
        //Debug.Log("rigidbody layer: "+ ds.rigidbody.gameObject.layer + " lastTimeMoving: " + lastTimeMoving);
        //Ghost Shader Effect
        this.GetComponent<Ghost>().enabled = false;
    }

    void Update()
    {
        ProgressTracker();
        Vector3 localTarget;
        float targetAngle;

        //Car Stuck Fix
        if(ds.rigidbody.velocity.magnitude > 1)
        {
            lastTimeMoving = Time.time;
        }

        if(Time.time > lastTimeMoving + 4)
        {
            ds.rigidbody.gameObject.transform.position = circuit.waypoints[currentTrackerWP].transform.position + Vector3.up * 2 +
                new Vector3(Random.Range(-1,1), 1, Random.Range(-1,1));
            tracker.transform.position = ds.rigidbody.gameObject.transform.position;
            ds.rigidbody.gameObject.layer = 8;

            //Debug.Log("rigidbody layer: "+ ds.rigidbody.gameObject.layer + " lastTimeMoving: " + lastTimeMoving);
            //Ghost Shader Effect
            this.GetComponent<Ghost>().enabled = false;
           
            Invoke("ResetLayer", 3);
        }

        //Car Bump Fix
        if(Time.time < ds.rigidbody.GetComponent<AvoidDetector>().avoidTime)
        {
            localTarget = tracker.transform.right * ds.rigidbody.GetComponent<AvoidDetector>().avoidPath;
        }
        else
        {
            localTarget = ds.rigidbody.gameObject.transform.InverseTransformPoint(tracker.transform.position);
        }

        targetAngle = Mathf.Atan2(localTarget.x, localTarget.z) * Mathf.Rad2Deg;  

        float speedFactor = ds.currentSpeed / ds.maxSpeed;

        float corner = Mathf.Clamp(Mathf.Abs(targetAngle), 0, 90);
        float cornerFactor = corner/90.0f;
       
        //Steer
        float steer = Mathf.Clamp(targetAngle * steeringSensitivity, -1, 1) * Mathf.Sign(ds.currentSpeed);
        //Brake
        float brake = 0;
        if(corner > 10 && speedFactor > 0.1f) //Alterar el valor este si va lento en esquinas
        {
            brake = Mathf.Lerp(0, 1 + speedFactor * brakingSensitivity, cornerFactor);
        }
        //Acceleration
        float accel = 1f;
        if(corner > 20 && speedFactor > 0.2f)
        {
            accel = Mathf.Lerp(0, 1 * accelSensitivity, 1 - cornerFactor);
        }
       
        //Debug.Log("Brake: "+ brake + " Accel: " + accel + " Speed: " + ds.rigidbody.velocity.magnitude);

        ds.Go(accel,steer, brake);
        ds.CheckForSkid();
        ds.CalculateEngineSound();
    }
}

This is the layer setup

And this is the layer collision matrix

1 Like

Well, that doesn’t really help. The layer of the collider has to be changed, not the one of the rigidbody. The Collider is the part that is colliding. Only using the rigidbody layer would restrict a single object to only have colliders or triggers for one layer.

Let me be more clear, the gameObject that has the collider is the parent of the gameObjects that contain all the colliders for the car (the wheel colliders and the mesh colliders of the car meshes).

I thought that if you change the layer of a parent object all of the children change to the same layer. Is this not the case? if so I can get all of the children and change layer on all of them one by one if necessary, unless there is a better approach.

No that’s not the case :slight_smile: Every gameobject has its own layer. Otherwise only root objects could actually specify a layer and all the hierarchy down would be forced to use the same layer. That doesn’t make much sense. So you need to change the layer of the child gameobject(s) that holds the collider(s). Keep in mind that you can always outsource such tasks to seperate components. For example

public class LayerGroup : MonoBehaviour
{
    public List<GameObject> objs;
    public void SetLayer(int aLayer)
    {
        foreach(var go in objs)
            go.layer = aLayer;
    }
}

Just put that script on your parent, drag in the child objects you want to change the layer of and reference this script from your actual script where you want to decide to change the layer. So you define a “LayerGroup” reference and just use SetLayer. That way you abstracted away what objects are actually affected (that’s controlled through the objs list)

public LayerGroup colliders; // assign in the inspector

// [ ...]
// to change the layer of all those objects at once, just use
colliders.SetLayer(yourNewLayer);
1 Like

Thanks for the help, the collisions work perfectly now. I just need to fix the issue with the spawn points and the system will be functional.

Personally, I wouldn’t change layers as it makes it really hard to debug. Just Ignore the collisions during that time ( n seconds)

For the collision layers part, if you are using NavMesh Agents, put their Quality to None, it worked for me!