The Unity script needs the Editor and the setup listed below. The separate C# companion practice runs in SharpRune without UnityEngine.
A callback follows a configured physical interaction
A collision message is not produced merely because two visible objects seem to touch. The 3D colliders, body configuration, trigger state and filtering must support the intended interaction. This lesson uses a nonkinematic sphere Rigidbody colliding with a static floor, both with nontrigger 3D colliders. OnCollisionEnter and OnTriggerEnter describe different interactions, and 2D physics has separate components and messages. Establish one known setup first, then diagnose a missing callback through its individual configuration boundaries rather than treating every failure as a spelling error in the script.
The script’s mask reports interest after contact arrives
CollisionProbe receives a Collision and reads the other GameObject’s layer. It checks whether that layer’s bit appears in interestingLayers, then logs both contact identity and the selection result. The mask does not alter the global Physics layer matrix and does not prevent the contact itself. A false selection can therefore appear in a real contact log. This distinction is useful when a gameplay rule cares about only some physically interacting objects while a separate engine-level filter determines whether those objects interact at all.
A layer index is not already its bit mask
The portable lab combines bit eight and bit ten with bitwise OR, producing a set of allowed positions in an integer. Includes tests a layer by shifting one to that index and checking for a nonzero intersection. Layer eight therefore prints True and layer nine prints False. This is ordinary bit-set arithmetic, not a collision simulator. Supplying the number eight directly as the whole mask would describe a different bit pattern, so keep an index and a mask named distinctly even though both use integer storage.
Disabled behaviours can still receive physics messages
Unity documents collision and trigger message delivery to disabled MonoBehaviours, allowing a response that enables behavior. Disabling a component is therefore not a universal gameplay-contact guard. This logging demo may intentionally observe that behavior. A controller whose rules must ignore disabled actors should check its activation policy explicitly, as the capstone does before accepting a trigger collectible. Also distinguish disabled component, inactive GameObject and removed collider; they affect different parts of the engine lifecycle and should not be collapsed into one vague off state.
Practice: separate script interest from engine filtering
Assign the floor to a known layer and remove that layer from interestingLayers. Predict that the existing physical contact can still arrive while selected becomes false. Restore the script mask, then disable the corresponding layer pair in Project Settings > Physics in a separate copied experiment. Observe how engine filtering changes the interaction itself. Finally use a separate trigger setup with the correct callback signature, rather than expecting a trigger to continue producing the same nontrigger Collision message.
- Keep 3D and 2D component families separate during the experiment.
- Record body state, trigger flags, layer indices and matrix entries beside each observation.
Practice: validate a reusable bit-selection helper
For an independent C# exercise, include layer nine, then test layers eight, nine and ten. Add a public helper guard for the permitted layer-index range zero through thirty-one. Explain why arbitrary integer shift counts are not a safe substitute for validating a layer index: C# shift behavior can map out-of-range counts unexpectedly. Then include bit thirty-one and verify that a nonzero intersection still works even when the signed integer mask is negative. Avoid changing the test to greater-than-zero, which would weaken that contract.
- Predict masks and membership independently from any scene callback.
- A script-level mask is not the global collision matrix and does not establish which Rigidbody combination generates messages.
Make contact generation and gameplay acceptance separate
A reliable interaction design states what physics setup generates a message and what gameplay policy accepts it. The real CollisionProbe provides an attachable engine example; the companion validates only mask arithmetic. Neither local console output nor syntax checking proves that a Player’s colliders and matrix are configured correctly. Test the actual scene, including disabled-behaviour callback rules and the intended activation guard, before relying on collision-driven damage or scoring. This separation prevents both missing contacts and unwanted contacts from being hidden behind one misleading filter variable.
- Portable baseline is True for layer eight and False for nine.
- The logging mask chooses interest after a callback, not whether the engine simulates the pair.
- Disabled MonoBehaviours can receive documented physics messages; enforce gameplay activation policy explicitly.
Unity Editor example
Set up in Unity 6
- Create a floor Cube with a BoxCollider and a Sphere above it with a nonkinematic Rigidbody and SphereCollider.
- Attach CollisionProbe.cs to the sphere and leave both colliders’ Is Trigger boxes off.
- Assign named layers through Tags and Layers, then set interestingLayers in the component Inspector.
- Enter Play mode and observe the floor contact; inspect Project Settings > Physics > Layer Collision Matrix.
- Use a separate copy to test Is Trigger and an OnTriggerEnter(Collider) handler; do not expect OnCollisionEnter for that trigger configuration.
CollisionProbe.cs
Download Unity script ↗using UnityEngine;
public sealed class CollisionProbe : MonoBehaviour
{
[SerializeField] private LayerMask interestingLayers = ~0;
private void OnCollisionEnter(Collision collision)
{
bool selected = (interestingLayers.value & (1 << collision.gameObject.layer)) != 0;
Debug.Log("Contact: " + collision.gameObject.name + ", selected=" + selected, this);
}
}
What to observe in Unity
A configured nontrigger 3D contact produces a log identifying the other GameObject and mask selection. Collision filtering or trigger configuration can change which message is appropriate.
The console practice below isolates the C# logic. It does not run a Unity scene or emulate Unity engine callbacks.
C# companion practice
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
int allowed = (1 << 8) | (1 << 10);
bool Includes(int layer) => (allowed & (1 << layer)) != 0;
Console.WriteLine(Includes(8)); Console.WriteLine(Includes(9));
C# practice output
True False
A layer mask selects bits; it does not itself create a collision or override the Physics matrix.