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 fromStart(), 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 |