3D Dino Run – Select Doors and Enums

Learn to classify door types using enums, expose private fields with SerializeField, and configure doors visually using SpriteRenderer

This tutorial covers the SelectDoors script, which controls the "select doors" that the dinosaur encounters while running. Each door displays one of four arithmetic operations (addition, subtraction, multiplication, division) along with a number. Depending on which door the player passes through, the number of dinosaurs changes. SelectDoors initializes each door's color and text, and provides the operation information when a collision occurs.


Game Structure

Script Role
SelectDoors Sets door types (enum), initializes color and text, provides left/right operation info
DinoController Dinosaur movement and door collision detection (Tutorial 52)
DinoPositionController Adjusts raptor count after passing through a door (Tutorial 52)

SelectDoors – Full Code

using UnityEngine;
using TMPro;

public enum DoorType { Plus, Minus, Times, Division }

public class SelectDoors : MonoBehaviour
{
    public SpriteRenderer rightDoorSpriteRD;
    public SpriteRenderer leftDoorSpriteRD;
    public TextMeshPro rightDoorText;
    public TextMeshPro leftDoorText;

    [SerializeField] private DoorType rightDoorType;
    public int rightDoorNumber;
    [SerializeField] private DoorType leftDoorType;
    public int leftDoorNumber;

    public Color goodColor;
    public Color badColor;

    void Start() { SettingDoors(); }

    public void SettingDoors()
    {
        if (rightDoorType.Equals(DoorType.Plus)) { rightDoorSpriteRD.color = goodColor; rightDoorText.text = "+" + rightDoorNumber; }
        else if (rightDoorType.Equals(DoorType.Minus)) { rightDoorSpriteRD.color = badColor; rightDoorText.text = "-" + rightDoorNumber; }
        else if (rightDoorType.Equals(DoorType.Times)) { rightDoorSpriteRD.color = goodColor; rightDoorText.text = "x" + rightDoorNumber; }
        else if (rightDoorType.Equals(DoorType.Division)) { rightDoorSpriteRD.color = badColor; rightDoorText.text = "÷" + rightDoorNumber; }

        if (leftDoorType.Equals(DoorType.Plus)) { leftDoorSpriteRD.color = goodColor; leftDoorText.text = "+" + leftDoorNumber; }
        else if (leftDoorType.Equals(DoorType.Minus)) { leftDoorSpriteRD.color = badColor; leftDoorText.text = "-" + leftDoorNumber; }
        else if (leftDoorType.Equals(DoorType.Times)) { leftDoorSpriteRD.color = goodColor; leftDoorText.text = "x" + leftDoorNumber; }
        else if (leftDoorType.Equals(DoorType.Division)) { leftDoorSpriteRD.color = badColor; leftDoorText.text = "÷" + leftDoorNumber; }
    }

    public DoorType GetDoorType(float xPos) { return xPos > 0 ? rightDoorType : leftDoorType; }
    public int GetDoorNumber(float xPos) { return xPos > 0 ? rightDoorNumber : leftDoorNumber; }
}

Key Concepts

1. enum – DoorType

public enum DoorType { Plus, Minus, Times, Division }

An enum (enumeration) is a type that groups a set of related named constants. Instead of using raw numbers, you give each option a meaningful name, which makes the code much easier to read and understand.

// Without enum – hard to understand
int doorType = 0;  // Is 0 Plus or Minus? Hard to tell from the code alone.

// With enum – immediately clear
DoorType doorType = DoorType.Plus;

Underlying integer values: By default, enum members are assigned integer values starting from 0.

Name Underlying Value
DoorType.Plus 0
DoorType.Minus 1
DoorType.Times 2
DoorType.Division 3

When to use an enum:

  • When a value can only be one of a fixed set of options
  • Directions (Up/Down/Left/Right), states (Idle/Moving/Attacking), elemental types (Fire/Water/Grass)
  • Whenever you want to make branching logic immediately understandable
// More enum examples
public enum GameState { Playing, Paused, GameOver }
public enum AttackType { Melee, Ranged, Magic }

.Equals vs ==

// Using .Equals (as used in this code)
if (rightDoorType.Equals(DoorType.Plus)) { ... }

// Using == operator
if (rightDoorType == DoorType.Plus) { ... }

Both approaches work identically for enum comparisons. Because enums are value types, both perform a value comparison rather than a reference comparison.

Comparison Notes
.Equals() Method call; not null-safe in general, but enums cannot be null so this is fine
== operator More concise; generally preferred for readability

2. SerializeField – Editing Private Fields in the Inspector

[SerializeField] private DoorType rightDoorType;
[SerializeField] private DoorType leftDoorType;

The [SerializeField] attribute allows a private field to be displayed and edited in the Unity Inspector, even though it is not publicly accessible from other scripts.

Why use SerializeField instead of public?

Declaring a field as public exposes it to every other script in the project, which breaks encapsulation. The door type should only be managed within this object. By keeping the field private and adding [SerializeField], designers can configure values in the Inspector without any risk of another script accidentally modifying the data.

Declaration Inspector Editable External Script Access When to Use
public Yes Yes When other scripts need to read or write the value
[SerializeField] private Yes No When only the Inspector should set it; block external access
private No No Fully internal data, never exposed
// Side-by-side comparison
public int score;                    // Inspector: YES, external: YES
[SerializeField] private int score;  // Inspector: YES, external: NO
private int score;                   // Inspector: NO,  external: NO

In the Inspector, an enum field decorated with [SerializeField] appears as a dropdown menu showing Plus / Minus / Times / Division.


3. SpriteRenderer.color – Setting Door Colors

public SpriteRenderer rightDoorSpriteRD;
public Color goodColor;
public Color badColor;

rightDoorSpriteRD.color = goodColor;  // Good door: tint with goodColor
rightDoorSpriteRD.color = badColor;   // Bad door: tint with badColor

SpriteRenderer is the component that renders a 2D sprite image. The .color property multiplies a color on top of the sprite texture. If the sprite is white, the resulting color is exactly the tint you assign.

// Color examples
spriteRenderer.color = Color.red;                   // Red
spriteRenderer.color = Color.blue;                  // Blue
spriteRenderer.color = new Color(1, 0.5f, 0, 1);   // Orange (RGBA)
spriteRenderer.color = Color.white;                 // Original color (identity for multiplication)

Why use goodColor / badColor? Instead of hardcoding which color represents a "good" door, these values are set in the Inspector. A designer can change the colors at any time without touching the code.

Door Type Color Reason
Plus (addition) goodColor Increases raptor count → favorable
Times (multiplication) goodColor Increases raptor count → favorable
Minus (subtraction) badColor Decreases raptor count → unfavorable
Division (division) badColor Decreases raptor count → unfavorable

4. SettingDoors() – Initializing Doors with Conditionals

public void SettingDoors()
{
    if (rightDoorType.Equals(DoorType.Plus))
    {
        rightDoorSpriteRD.color = goodColor;
        rightDoorText.text = "+" + rightDoorNumber;
    }
    else if (rightDoorType.Equals(DoorType.Minus))
    {
        rightDoorSpriteRD.color = badColor;
        rightDoorText.text = "-" + rightDoorNumber;
    }
    else if (rightDoorType.Equals(DoorType.Times))
    {
        rightDoorSpriteRD.color = goodColor;
        rightDoorText.text = "x" + rightDoorNumber;
    }
    else if (rightDoorType.Equals(DoorType.Division))
    {
        rightDoorSpriteRD.color = badColor;
        rightDoorText.text = "÷" + rightDoorNumber;
    }
    // leftDoorType follows the same structure
}
  • SettingDoors() is called from Start(), so each door's color and text are automatically initialized when the game starts.
  • Because it is declared public, an external script (for example, a map generation script) can call it again after changing the door configuration.
  • Text is built by string concatenation: "+" + 3 → "+3", "x" + 2 → "x2".

TextMeshPro.text: Assigns the string to display on a TextMeshPro component. The display updates immediately when you assign a new value to the .text property.


5. GetDoorType / GetDoorNumber – Left/Right Detection with the Ternary Operator

public DoorType GetDoorType(float xPos) { return xPos > 0 ? rightDoorType : leftDoorType; }
public int GetDoorNumber(float xPos) { return xPos > 0 ? rightDoorNumber : leftDoorNumber; }

These methods determine whether the dinosaur collided with the right or left door based on the dinosaur's X coordinate (xPos).

World X > 0  →  right door
World X < 0  →  left door

Ternary operator: condition ? value_if_true : value_if_false. It is a concise way to write an if-else that returns a value.

// if-else version
if (xPos > 0)
    return rightDoorType;
else
    return leftDoorType;

// Ternary version (identical behavior)
return xPos > 0 ? rightDoorType : leftDoorType;

These methods are called by DinoController's collision code (covered in Tutorial 52):

// Usage example (from Tutorial 52)
DoorType doorType = doors.GetComponent<SelectDoors>().GetDoorType(transform.position.x);
int doorNum = doors.GetComponent<SelectDoors>().GetDoorNumber(transform.position.x);

Unity Scene Setup Summary

Item Recommended Setup
Door prefab Attach SelectDoors to a parent GameObject; add left and right door objects as children
rightDoorSpriteRD / leftDoorSpriteRD Assign the SpriteRenderer component from each door child object
rightDoorText / leftDoorText Assign the TextMeshPro component from each door child object
rightDoorType / leftDoorType Select Plus / Minus / Times / Division from the dropdown in the Inspector
rightDoorNumber / leftDoorNumber Enter the number to use in the operation in the Inspector
goodColor / badColor Choose colors in the Inspector Color Picker (e.g., blue for good, red for bad)

Summary

Concept Location Key Point
enum DoorType Declared outside the class Manages door types with meaningful names
[SerializeField] private Field declaration Allows Inspector editing while blocking external access (encapsulation)
SpriteRenderer.color SettingDoors() Dynamically changes door color based on door type
TextMeshPro.text SettingDoors() Displays the operation symbol and number on each door
Ternary operator GetDoorType, GetDoorNumber Determines right vs. left door from X coordinate