Back

Activity 1.14: Raylib Lab 3 — Decisions You Can See

divider

What You're Building

The stoplight with the top lamp bright red and the lower two a dull dark gray

state = 1

The same stoplight with the middle lamp bright yellow and the other two dark gray

state = 2

The same stoplight with the bottom lamp bright green and the upper two dark gray

state = 3

One program. Three pictures. Nothing about the drawing code changes between them — the lamps are in the same places, the housing is the same size, and every circle is drawn every single time.

What changes is one number, and an if. Lab 2 let one variable move every lamp. Lab 3 lets one variable decide which lamp is on, and by the end you will be changing it with the spacebar while the program runs.

divider

Activity 1.14

Raylib Lab 3 — Decisions You Can See

Key Concepts

A Color Can Be Stored

Decide First, Draw Second

State

Every Lamp Has Been On Since Lab 1

Three circles, three colors, drawn the same way every frame. A real stoplight has never once looked like that.

You have had if for three sessions. Today it finally changes something you can see.

The Color Is Just an Argument

Raylib.DrawCircle(lampX, firstLampY, lampRadius, Color.Red);

Color.Red sits in the last slot like any other value. Nothing says it has to be written there literally.

The Color Is Just an Argument

Color redLamp = Color.DarkGray;
Raylib.DrawCircle(lampX, firstLampY, lampRadius, redLamp);

Color is a type, the same way int and bool are. You can declare one, store a value in it, and hand it to DrawCircle.

And a variable you can store into is a variable an if can change.

Decide First, Draw Second

Color redLamp = Color.DarkGray;
if (state == 1)
{
redLamp = Color.Red;
}
Raylib.DrawCircle(lampX, firstLampY, lampRadius, redLamp);

Decide First, Draw Second

The lamp starts dark. The if might brighten it. Then it gets drawn — once, whichever way the decision went.

This is the shape to keep: the decision picks a value, and the drawing happens afterward.

Putting a DrawCircle inside each branch works too, and it means writing the same call twice with one word different.

Three Lamps, One Chain

Color redLamp = Color.DarkGray;
Color yellowLamp = Color.DarkGray;
Color greenLamp = Color.DarkGray;
if (state == 1)
{
redLamp = Color.Red;
}
else if (state == 2)
{
yellowLamp = Color.Yellow;
}
else
{
greenLamp = Color.Green;
}

Three Lamps, One Chain

All three start dark. The chain brightens exactly one of them, because that is what a chain does.

Activity 1.13 said a chain gives you one answer. Here that promise is the difference between a stoplight and a Christmas decoration.

What Separate Ifs Would Do

if (state == 1)
{
redLamp = Color.Red;
}
if (state == 2)
{
yellowLamp = Color.Yellow;
}
if (state == 3)
{
greenLamp = Color.Green;
}

Same three conditions. No else. Predict the picture.

What Separate Ifs Would Do

For any one state it behaves identically — only one condition is true, so only one lamp lights.

It is not identical. It is one typo away from being wrong. Set two of those conditions true and the chain still lights one lamp. Separate ifs light both.

You will see exactly that picture in Task 4.

A Number That Remembers

if (Raylib.IsKeyPressed(KeyboardKey.Space))
{
state = state + 1;
}
if (state > 3)
{
state = 1;
}

A Number That Remembers

state survives from one frame to the next. Press space, it goes up. Pass 3, it wraps to 1.

That is what state means — a value the program carries forward, which decides what it does next.

Two separate if statements here, and this time that is right: did they press space and have we run off the end are different questions.

Chain them and the light still looks perfect. Task 3 shows you where the damage hides.

Today's Objectives

  • Storing a Color in a variable and drawing with it
  • Letting a chain choose a value before anything is drawn
  • Seeing why a chain and separate ifs are not the same promise
  • Changing state while the program runs, and keeping it in range

Key Terms

State
A value the program carries from one frame to the next, which decides what it does now.
Frame
One pass through the drawing loop.
Argument
A value handed to something like DrawCircle.

'F' → Fullscreen

divider

Build

Start from your finished Lab 2 program, not an empty file. Save a copy named 1-14-stoplight so Lab 2 stays intact — you will want it again.

How to read the code blocks. Each task's code block shows your whole program, from line 1, as it should look when that task is finished.

The green lines marked with a + are the ones you add or change for that task — everything else is already in your file. Do not type the + sign.


Task 1: One Lamp That Can Be Off

  • Add the state variable, the three Color variables, and one if.
  • Then change all three DrawCircle calls so their last argument is the matching variable — redLamp, yellowLamp, greenLamp — instead of Color.Red and friends. Miss this and nothing you do to the variables will show up.
  • Run it. The red lamp is on, and the other two are dark because nothing ever brightens them.
  • Change state to 2 and run again. All three lamps go dark. That is correct for now — there is only one if, and its condition is false.
  • Set it back to 1 before you continue.
1-14-stoplight
#:package Raylib-cs@8.0.0
using Raylib_cs;
int screenWidth = 400;
int screenHeight = 600;
int lampRadius = 40;
int padding = 20;
int gap = 100;
int lampX = screenWidth / 2;
int firstLampY = 120;
int housingWidth = 2 * (lampRadius + padding);
int housingHeight = 2 * gap + 2 * lampRadius + 2 * padding;
int housingX = lampX - housingWidth / 2;
int housingY = firstLampY - lampRadius - padding;
int state = 1;
Raylib.InitWindow(screenWidth, screenHeight, "Stoplight");
Raylib.SetTargetFPS(60);
while (!Raylib.WindowShouldClose())
{
Color redLamp = Color.DarkGray;
Color yellowLamp = Color.DarkGray;
Color greenLamp = Color.DarkGray;
if (state == 1)
{
redLamp = Color.Red;
}
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.DarkGray);
Raylib.DrawRectangle(housingX, housingY, housingWidth, housingHeight, Color.Black);
Raylib.DrawCircle(lampX, firstLampY, lampRadius, redLamp);
Raylib.DrawCircle(lampX, firstLampY + gap, lampRadius, yellowLamp);
Raylib.DrawCircle(lampX, firstLampY + 2 * gap, lampRadius, greenLamp);
Raylib.EndDrawing();
}
Raylib.CloseWindow();

Task 2: The Chain

  • Turn the single if into a three-branch chain.
  • Test all three states by editing state: 1, then 2, then 3. Each should light exactly one lamp, and your screen should match the three pictures at the top of this page.
  • Now try state = 7. Write down which lamp lights, and why. It is not a bug, and it is not nothing.
  • Set it back to 1.
1-14-stoplight
#:package Raylib-cs@8.0.0
using Raylib_cs;
int screenWidth = 400;
int screenHeight = 600;
int lampRadius = 40;
int padding = 20;
int gap = 100;
int lampX = screenWidth / 2;
int firstLampY = 120;
int housingWidth = 2 * (lampRadius + padding);
int housingHeight = 2 * gap + 2 * lampRadius + 2 * padding;
int housingX = lampX - housingWidth / 2;
int housingY = firstLampY - lampRadius - padding;
int state = 1;
Raylib.InitWindow(screenWidth, screenHeight, "Stoplight");
Raylib.SetTargetFPS(60);
while (!Raylib.WindowShouldClose())
{
Color redLamp = Color.DarkGray;
Color yellowLamp = Color.DarkGray;
Color greenLamp = Color.DarkGray;
if (state == 1)
{
redLamp = Color.Red;
}
else if (state == 2)
{
yellowLamp = Color.Yellow;
}
else
{
greenLamp = Color.Green;
}
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.DarkGray);
Raylib.DrawRectangle(housingX, housingY, housingWidth, housingHeight, Color.Black);
Raylib.DrawCircle(lampX, firstLampY, lampRadius, redLamp);
Raylib.DrawCircle(lampX, firstLampY + gap, lampRadius, yellowLamp);
Raylib.DrawCircle(lampX, firstLampY + 2 * gap, lampRadius, greenLamp);
Raylib.EndDrawing();
}
Raylib.CloseWindow();

Task 3: Make It Change

  • Add the two if statements that advance state and wrap it back around.
  • Run it and press space. Red, yellow, green, red again.
  • Add Console.WriteLine($"state = {state}"); just below the wrap, so you can watch the number as well as the light.
  • These two ifs are deliberately not a chain. Make the second one an else if, run it, and press space past green a few times.
  • The light will look completely fine. Watch the console instead. The probe prints on every frame — sixty lines a second — so it scrolls past fast. Press space once past green, then close the window straight away, and the line you want will be near the bottom.
  • Write down the one number that appears there and never appeared before, then put the else if back to an if.
1-14-stoplight
#:package Raylib-cs@8.0.0
using Raylib_cs;
int screenWidth = 400;
int screenHeight = 600;
int lampRadius = 40;
int padding = 20;
int gap = 100;
int lampX = screenWidth / 2;
int firstLampY = 120;
int housingWidth = 2 * (lampRadius + padding);
int housingHeight = 2 * gap + 2 * lampRadius + 2 * padding;
int housingX = lampX - housingWidth / 2;
int housingY = firstLampY - lampRadius - padding;
int state = 1;
Raylib.InitWindow(screenWidth, screenHeight, "Stoplight");
Raylib.SetTargetFPS(60);
while (!Raylib.WindowShouldClose())
{
if (Raylib.IsKeyPressed(KeyboardKey.Space))
{
state = state + 1;
}
if (state > 3)
{
state = 1;
}
Color redLamp = Color.DarkGray;
Color yellowLamp = Color.DarkGray;
Color greenLamp = Color.DarkGray;
if (state == 1)
{
redLamp = Color.Red;
}
else if (state == 2)
{
yellowLamp = Color.Yellow;
}
else
{
greenLamp = Color.Green;
}
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.DarkGray);
Raylib.DrawRectangle(housingX, housingY, housingWidth, housingHeight, Color.Black);
Raylib.DrawCircle(lampX, firstLampY, lampRadius, redLamp);
Raylib.DrawCircle(lampX, firstLampY + gap, lampRadius, yellowLamp);
Raylib.DrawCircle(lampX, firstLampY + 2 * gap, lampRadius, greenLamp);
Raylib.EndDrawing();
}
Raylib.CloseWindow();

Task 4: Break It on Purpose

Two changes, one at a time. Screenshot or describe what you see each time, and put it back before the next.

  1. Delete every else from the color chain, so it becomes three separate if statements. Then change the second condition to state == 1 as well, and run it with state = 1.
  2. Put the chain back. Now delete the final else block only — keep the first two branches as if and else if — and run it with state = 3.

For each one, say what a driver at this intersection would do if a real light looked like that. One of the two is dangerous in a way the other is not.


Challenge (Optional): The Fourth State

  • Real lights have a flashing-red mode. Add state = 4 as a fourth case that lights the red lamp, and change the wrap so 4 is reachable.
  • Do not add a fourth Color variable. Two different states can brighten the same lamp.
  • Write a comment saying which line you had to change to make 4 reachable, and what would have happened if you had forgotten it.
divider

Checkpoint

Your two broken versions from Task 4 should look like this. If yours looked different, you changed something else as well.

The stoplight with all three lamps lit bright red, yellow, and green at the same time

Task 4, change 1 — separate ifs

The stoplight with all three lamps a dull dark gray and none of them lit

Task 4, change 2 — no final else

Look hard at the one on the left, because you have seen it before. It is not a redrawing of Lab 1's stoplight — it is the same image file. The picture you were proud of in Activity 1.7 is what a broken Lab 3 looks like, and the only thing that changed in between is that a stoplight is now supposed to decide.

  • Each of states 1, 2, and 3 lights exactly one lamp, and the other two are dark gray
  • Space advances the light and it wraps from green back to red
  • You have written down which lamp state = 7 lit, and why
  • You have written down the number that appeared in the console with the else if wrap, and can say why the light still looked right
  • Both Task 4 pictures are recorded, with what a driver would do at each
divider

Reflection

Answer these on your own before submitting your work.

  1. The three lamps are drawn on every frame no matter what. So what is the if actually changing?
  2. A chain and three separate if statements gave the same picture for every valid state. Explain why the chain is still the better choice here.
  3. state is an ordinary int, but the program behaves differently depending on what it holds. What does that let this program do that Lab 2's could not?
divider

Submit

Submit four things to the dropbox:

  1. Your 1-14-stoplight program file, with the spacebar working.
  2. Your state = 7 answer, and the number the console showed when the wrap became an else if.
  3. Your two Task 4 results, with what a driver would do at each.
  4. Your three reflection answers.

Activity Complete