Would it not make sense to create a new script for this?

I went about this a little differently. Trying to ‘think like a programmer’ as they say. I thought it would make sense to create a new dev/cheat key script so that it could also be used for things not relating to collisions.

First I made a public bool in the collision handler script so I could change if we ran the on collision code from a different script.

public class CollisionHandler : MonoBehaviour
{

    [SerializeField] float loadDelay = 2f;
    [SerializeField] AudioClip crashSFX;
    [SerializeField] AudioClip levelWinSFX;
    [SerializeField] ParticleSystem crashParticles;
    [SerializeField] ParticleSystem levelWinParticles;

    private Scene currentScene;
    AudioSource audioSource;
    

    bool isTransitioning = false;

    public bool canCollide;


    void Start()
    {
        currentScene = SceneManager.GetActiveScene();
        audioSource = GetComponent<AudioSource>();
        canCollide = true;
    }
    void OnCollisionEnter(Collision collision)
    {
        if (isTransitioning || !canCollide) { return; }
        switch (collision.gameObject.tag)
        {
            case "Friendly":
                Debug.Log("Collided with a afriendly object");
                break;
            case "Finish":
                FinishSequence();
                break;
            default:
                Debug.Log("That was life ending");
                CrashSequence();
                break;
        }
    }
type or paste code here

Then created another script and attached it to the rocket than can load and reload the level but also toggle canCollide in the collision handler script.

public class DevKeys : MonoBehaviour
{
    [SerializeField] bool devKeys;    

    //MeshCollider m_MeshCollider;

    private Scene currentScene;
    void Start()
    {
        //m_MeshCollider = GetComponent<MeshCollider>();
        currentScene = SceneManager.GetActiveScene();
    }

    void Update()
    {
        if (devKeys)
        {
            LoadNextLevel();
            ReloadLevel();
            ToggleCollisions();
        }      
    }

    void ToggleCollisions()
    {
        if (Input.GetKeyDown(KeyCode.C))
        {
            if (this.GetComponent<CollisionHandler>().canCollide)
            {
                this.GetComponent<CollisionHandler>().canCollide = false;
            }
            else if(!this.GetComponent<CollisionHandler>().canCollide)
            {
                this.GetComponent<CollisionHandler>().canCollide = true;
            }
        }       
    }
    void ReloadLevel()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            SceneManager.LoadScene(currentScene.buildIndex);
        }
    }
    void LoadNextLevel()
    {
        if (Input.GetKeyDown(KeyCode.L))
        {
            // if there is a scene in the build index after the current scene load that one
            if (currentScene.buildIndex < SceneManager.sceneCountInBuildSettings - 1)
            {
                SceneManager.LoadScene(currentScene.buildIndex + 1);
            }
            // if we are on the last scene in the build index load the first scene in the build index
            else if (currentScene.buildIndex == SceneManager.sceneCountInBuildSettings - 1)
            {
                SceneManager.LoadScene(0);
            }
        }
    }
}

As you can see at first I tried just disabling the mesh collider, which works in that we just float through everything but not quite what I wanted so commented it out for maybe later.
I also found out that you can’t just disable a script to stop the code from running which caused a lot of confusion, frustration and a fair bit of googling.

But I’m kind of proud of the outcome, it might not be pretty or performative code but it works as far as I can tell. If anyone can see any glaring problems I would appreciate any constructive.

1 Like

It’s really up to you. Making your program modular is always better. I would create a new script so you have more control over what’s going on and to help prevent crashes.

1 Like

I was thinking in the same way and did a very similar implementation :smiley: I guess there are pros and cons to each of them?

A big plus of having the script separate like you have here, you have all your debug/cheat code bundled together in one component. What I noticed personally is that a disadvantage of this could be that you may end up with having to expose some stuff …

For instance, as you mention yourself, you have to have a public bool canCollide; such that other components (in this case, DevKeys) can change the behavior of the CollisionHandler. In my case, I also made the CollisionHandler’s LoadNextLevel() a public method (in your case you didn’t do that, but with code duplication come other risks :sweat_smile: )

To imagine why this may not be great in real life: imagine if you and I were working on the same project and I’m working on another component; I could be accessing those public canCollide or LoadNextLevel() even though they weren’t intended to be used by others …

So, I guess it can also make sense to do it as Rick does; catching those debug/cheat keys right where they should have an effect. Note: nothing is stopping you from catching debug/cheat keys in multiple components (in the Update() of Movement, I could also call its own local RespondToDebugKeys() method; same name but in different component is still fine). Now, these local, private RespondToDebugKeys() can access everything of the component without having to make anything public just for debugging/cheating. But, the disadvantage is that the debug/cheat codes are spread all over your scripts (that is, if you would have more than just what we did in this lecture)

Nothing wrong with how you did it (as I said, I did it in the same way :slight_smile: ), but I can imagine there are also merits to not doing it in a separate component.

Also, this one also caught me by surprise :sweat_smile: I thought a disabled component was basically the same as “the component isn’t there”, but according to the docs (link), at enabled it mentions “Enabled Behaviours are Updated, disabled Behaviours are not.”. This is also explicitly mentioned with some of those methods; for instance Update() mentions “Update is called every frame, if the MonoBehaviour is enabled.”. TIL :slight_smile:

So the way I understand it now, is that a disabled component is like a “sleeping” component. It will not be doing anything of itself (i.e., no updates), but you can still “wake it up to do something for you” (i.e., callbacks, such as the physics system that can still call OnTriggerEnter(), or calling any public method you defined yourself). This is probably not entirely accurate (I guess, strictly speaking, Update() is also a callback method, the Unity system is calling it), but from the view of “how components interact/communicate with each other”, this way of looking at it sounds sensible to me.

I did something similar, but made the debug/cheat mode toggle based, and recycled the LoadNextLevel method (not the most elegant solution, I know :smiley: )

When the user presses ‘P’, it flips the cheat mode on, pressing the same key again, toggles it off.

I’ll probably move the boxCollider value up to the variable definition to clean it up a bit, but the script is attached below for those who might see other areas to improve on it!

using UnityEngine;
using UnityEngine.SceneManagement;

public class Cheats : MonoBehaviour
{
    [SerializeField] bool cheatsEnabled = false;
    BoxCollider boxCollider;

    void Start()
    {
        boxCollider = GetComponent<BoxCollider>();
    }

    void Update()
    {
        EnableCheats();
        CheatNextLevel();
        CheatDisableCollision();
    }   

    void EnableCheats()
    {
        if (Input.GetKeyDown(KeyCode.P))
        {
            if (!cheatsEnabled)
            {
                cheatsEnabled = true;
            }
            else
            {
                cheatsEnabled = false;
            }            
        }
    }
    
    void CheatNextLevel()
    {              
        if (Input.GetKeyDown(KeyCode.L))
        {
            if (cheatsEnabled)
            {
             Invoke("LoadNextLevel", 1f);
            }            
        }
    }
    void CheatDisableCollision()
    {
        if (Input.GetKeyDown(KeyCode.C))
        {
            if (cheatsEnabled)
            {
                if (boxCollider.enabled)
                {
                    boxCollider.enabled = false;
                }
                else
                {
                    boxCollider.enabled = true;
                }
            }            
        }
    }

    void LoadNextLevel()
    {
        int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
        int nextSceneIndex = currentSceneIndex + 1;

        if (nextSceneIndex == SceneManager.sceneCountInBuildSettings)
        {
            nextSceneIndex = 0;
        }
        SceneManager.LoadScene(nextSceneIndex);
    }
}

I thought of the same thing, it is much better to manage all of this in a separate class rather than assigning all the responsibilities to one single class (This is something even Rick talked about in the earlier lectures)

Privacy & Terms