Skip to main content

3.1 - Conditional Statements

Conditional statements are the decision-makers in your code. They allow your program to execute different blocks of code based on whether certain conditions are true or false. This ability to make decisions is fundamental to creating dynamic, responsive programs and games.

Why Conditional Statements Matter

In games and applications, you constantly need to check conditions and respond accordingly:

  • Is the player's health below zero? If yes, trigger the game over sequence.
  • Did the player collect enough points? If yes, unlock the next level.
  • Is the enemy within attack range? If yes, initiate attack behavior.
  • Did the user enter valid input? If not, display an error message.

Without conditional statements, programs would execute the same instructions in the same order every time, making them static and unresponsive.

The if Statement

The most basic conditional statement is the if statement. It executes a block of code only if a specified condition evaluates to true.

Basic Syntax

if (condition)
{
// Code to execute if condition is true
}

Examples

// Check if player has enough health to survive an attack
int playerHealth = 30;
int incomingDamage = 25;

if (playerHealth > incomingDamage)
{
Console.WriteLine("You survived the attack!");
playerHealth -= incomingDamage;
}

// Check if player has collected enough coins
int coinsCollected = 100;
int coinsNeeded = 50;

if (coinsCollected >= coinsNeeded)
{
Console.WriteLine("Level complete! You collected enough coins.");
}

The if-else Statement

The if-else statement extends the if statement by providing an alternative block of code to execute when the condition is false.

Basic Syntax

if (condition)
{
// Code to execute if condition is true
}
else
{
// Code to execute if condition is false
}

Examples

// Check if player has enough mana to cast a spell
int playerMana = 15;
int spellManaCost = 20;

if (playerMana >= spellManaCost)
{
Console.WriteLine("Spell cast successfully!");
playerMana -= spellManaCost;
}
else
{
Console.WriteLine("Not enough mana to cast the spell!");
}

// Determine if a character is friendly or hostile
bool isCharacterFriendly = false;

if (isCharacterFriendly)
{
Console.WriteLine("Hello, friend! How can I help you?");
}
else
{
Console.WriteLine("Intruder detected! Guards, attack!");
}

The if-else if-else Statement

For situations where you need to check multiple conditions in sequence, you can use the if-else if-else structure.

Basic Syntax

if (condition1)
{
// Code to execute if condition1 is true
}
else if (condition2)
{
// Code to execute if condition1 is false and condition2 is true
}
else if (condition3)
{
// Code to execute if condition1 and condition2 are false and condition3 is true
}
else
{
// Code to execute if all conditions are false
}

Examples

// Determine player rank based on score
int playerScore = 7500;

if (playerScore >= 10000)
{
Console.WriteLine("Rank: Master");
}
else if (playerScore >= 5000)
{
Console.WriteLine("Rank: Expert");
}
else if (playerScore >= 1000)
{
Console.WriteLine("Rank: Intermediate");
}
else
{
Console.WriteLine("Rank: Beginner");
}

// Determine damage type based on weapon
string weaponType = "Fire Staff";

if (weaponType == "Sword" || weaponType == "Dagger")
{
Console.WriteLine("Physical damage");
}
else if (weaponType == "Fire Staff" || weaponType == "Flame Wand")
{
Console.WriteLine("Fire damage");
}
else if (weaponType == "Ice Scepter" || weaponType == "Frost Wand")
{
Console.WriteLine("Ice damage");
}
else
{
Console.WriteLine("Unknown damage type");
}

Nested if Statements

You can place if statements inside other if or else blocks to create more complex decision trees.

Basic Syntax

if (outerCondition)
{
// Code for outer condition

if (innerCondition)
{
// Code for both outer and inner conditions
}
}

Examples

// Check if player can enter a restricted area
bool hasKeyCard = true;
int securityClearance = 3;
bool isAlarm = false;

if (hasKeyCard)
{
if (securityClearance >= 2)
{
if (!isAlarm)
{
Console.WriteLine("Access granted to restricted area.");
}
else
{
Console.WriteLine("Access denied: Security alert in progress.");
}
}
else
{
Console.WriteLine("Access denied: Insufficient security clearance.");
}
}
else
{
Console.WriteLine("Access denied: Key card required.");
}

While nested if statements are powerful, they can quickly become difficult to read and maintain. Consider refactoring complex nested conditions using logical operators or separate methods.

Conditions and Boolean Expressions

The condition in an if statement must evaluate to a boolean value (true or false). You can create these conditions using:

Comparison Operators

OperatorDescriptionExample
==Equal toscore == 100
!=Not equal tohealth != 0
>Greater thanlevel > 5
<Less thanammo < 10
>=Greater than or equal toexperience >= 1000
<=Less than or equal totime <= 30

Logical Operators

OperatorDescriptionExample
&&Logical AND (both conditions must be true)health > 0 && ammo > 0
||Logical OR (at least one condition must be true)hasKey || hasLockpick
!Logical NOT (inverts a condition)!isGameOver

Examples of Complex Conditions

// Player can attack if they have a weapon and either ammo or mana
bool hasWeapon = true;
int ammo = 0;
int mana = 50;

if (hasWeapon && (ammo > 0 || mana >= 10))
{
Console.WriteLine("Player can attack!");
}
else
{
Console.WriteLine("Player cannot attack.");
}

// Check if player is in danger (low health or surrounded by enemies)
int health = 30;
int nearbyEnemies = 5;
bool hasShield = true;

if ((health < 20 || nearbyEnemies > 3) && !hasShield)
{
Console.WriteLine("Warning: Player in danger!");
}

Short-Circuit Evaluation

C# uses short-circuit evaluation for logical operators:

  • For &&, if the first condition is false, the second condition is not evaluated (since the result must be false).
  • For ||, if the first condition is true, the second condition is not evaluated (since the result must be true).

This behavior can be useful for efficiency and to avoid potential errors:

// The second condition is only checked if player is not null
if (player != null && player.IsAlive)
{
player.TakeDamage(10);
}

// The second condition is only checked if divisor is zero
if (divisor == 0 || result / divisor > 10)
{
Console.WriteLine("Cannot proceed with calculation.");
}

Single-Line if Statements

For simple if statements with only one line of code to execute, you can omit the curly braces:

if (playerHealth <= 0)
GameOver();

if (score > highScore)
UpdateHighScore(score);
else
DisplayMessage("Try again to beat the high score!");

While this syntax is more concise, it can lead to errors if you later add more lines to the conditional block. Many coding standards recommend always using curly braces for clarity and to prevent bugs.

The Conditional (Ternary) Operator

The conditional operator (?:) provides a concise way to write simple if-else statements:

// Syntax: condition ? valueIfTrue : valueIfFalse

// Instead of:
string status;
if (playerHealth > 0)
{
status = "Alive";
}
else
{
status = "Defeated";
}

// You can write:
string status = playerHealth > 0 ? "Alive" : "Defeated";

// Another example - calculating damage with critical hits
float damage = isCriticalHit ? baseDamage * 2.0f : baseDamage;

The ternary operator is best used for simple conditions. For complex logic, traditional if-else statements are more readable.

Null-Conditional Operator with Conditional Logic

The null-conditional operator (?.) can be combined with conditional logic to safely access members of potentially null objects:

// Check if player has a weapon and if that weapon has ammo
bool canShoot = player?.Weapon?.HasAmmo == true;

// Instead of:
bool canShoot = false;
if (player != null && player.Weapon != null)
{
canShoot = player.Weapon.HasAmmo;
}

Pattern Matching (C# 7.0+)

C# 7.0 introduced pattern matching, which enhances the is operator and adds the ability to declare variables as part of the condition:

// Type pattern
if (gameObject is Player player)
{
// 'player' is now a variable of type Player
player.GainExperience(100);
}

// Property pattern (C# 8.0+)
if (enemy is { Health: <= 0, HasDeathAnimation: true })
{
PlayDeathAnimation(enemy);
}

Common Pitfalls and Best Practices

1. Using == vs. = in Conditions

A common mistake is using the assignment operator (=) instead of the equality operator (==) in conditions:

// WRONG: This assigns true to isGameOver and always evaluates to true
if (isGameOver = true)
{
// This will always execute!
}

// CORRECT: This checks if isGameOver equals true
if (isGameOver == true)
{
// This executes only if isGameOver is true
}

// Even better: Since isGameOver is already a boolean
if (isGameOver)
{
// This executes only if isGameOver is true
}

2. Simplifying Boolean Conditions

When working with boolean variables, you can often simplify your conditions:

// Instead of:
if (isPlayerAlive == true)
{
// Do something
}

// Write:
if (isPlayerAlive)
{
// Do something
}

// Instead of:
if (isGameOver == false)
{
// Do something
}

// Write:
if (!isGameOver)
{
// Do something
}

3. Order of Conditions in if-else if Chains

In if-else if chains, conditions are evaluated in order. Place the most likely or computationally inexpensive conditions first for better performance:

// Check simple conditions before complex ones
if (!isGameActive)
{
return; // Exit early if game is not active
}
else if (player.Health <= 0)
{
GameOver();
}
else if (IsLevelComplete() && AreAllEnemiesDefeated())
{
// More complex checks that only happen if earlier conditions are false
NextLevel();
}

4. Avoiding Deep Nesting

Deeply nested if statements can make code hard to read and maintain. Consider:

  • Using early returns to reduce nesting
  • Extracting complex conditions into separate methods
  • Using logical operators to combine conditions
// Instead of:
if (hasKeyCard)
{
if (securityClearance >= 2)
{
if (!isAlarm)
{
Console.WriteLine("Access granted.");
}
}
}

// Consider:
if (!hasKeyCard || securityClearance < 2 || isAlarm)
{
Console.WriteLine("Access denied.");
return;
}

Console.WriteLine("Access granted.");

Practical Examples in Game Development

Example 1: Player Damage and Death System

public void TakeDamage(int damageAmount, bool isCriticalHit)
{
// Check for invulnerability
if (isInvulnerable)
{
PlayDamageBlockedEffect();
return;
}

// Calculate actual damage
int actualDamage = isCriticalHit ? damageAmount * 2 : damageAmount;

// Apply armor reduction if player has armor
if (hasArmor)
{
actualDamage = (int)(actualDamage * 0.7f);
}

// Ensure minimum damage of 1
actualDamage = Math.Max(1, actualDamage);

// Apply damage
currentHealth -= actualDamage;

// Display appropriate damage effect
if (isCriticalHit)
{
PlayCriticalHitEffect();
DisplayDamageText(actualDamage, true);
}
else
{
PlayNormalHitEffect();
DisplayDamageText(actualDamage, false);
}

// Check if player died
if (currentHealth <= 0)
{
currentHealth = 0;
if (hasExtraLife)
{
UseExtraLife();
}
else
{
Die();
}
}
}

Example 2: Item Pickup System

public void CollectItem(Item item)
{
if (item == null)
{
Debug.LogError("Attempted to collect a null item!");
return;
}

// Handle different item types
if (item is HealthPotion healthPotion)
{
if (currentHealth < maxHealth)
{
currentHealth = Math.Min(maxHealth, currentHealth + healthPotion.HealAmount);
PlayHealEffect();
DisplayMessage($"Healed for {healthPotion.HealAmount} points!");
}
else
{
// Player already at max health
if (inventory.HasSpace)
{
inventory.AddItem(item);
DisplayMessage("Health potion added to inventory.");
}
else
{
DisplayMessage("Inventory full! Cannot pick up health potion.");
return; // Don't remove the item from the world
}
}
}
else if (item is Weapon weapon)
{
if (currentWeapon != null && currentWeapon.Tier > weapon.Tier)
{
// Current weapon is better
DisplayMessage($"You already have a better weapon ({currentWeapon.Name}).");

// Ask player if they still want to pick it up
if (AskPlayerForConfirmation($"Pick up {weapon.Name} anyway?"))
{
EquipWeapon(weapon);
}
else
{
return; // Don't remove the item from the world
}
}
else
{
// New weapon is better or player has no weapon
EquipWeapon(weapon);
DisplayMessage($"Equipped {weapon.Name}!");
}
}
else if (item is Key key)
{
keyRing.AddKey(key);
DisplayMessage($"Picked up {key.Name}!");
}
else
{
// Generic item handling
if (inventory.HasSpace)
{
inventory.AddItem(item);
DisplayMessage($"Picked up {item.Name}!");
}
else
{
DisplayMessage("Inventory full!");
return; // Don't remove the item from the world
}
}

// Remove item from the world
item.RemoveFromWorld();
PlayPickupSound(item.SoundType);
}

Example 3: Game Difficulty Adjustment

public void AdjustDifficulty()
{
// Track player performance
float playerPerformanceRating = CalculatePlayerPerformance();

// Adjust difficulty based on performance
if (playerPerformanceRating > 0.8f)
{
// Player is doing very well - make it harder
if (currentDifficulty < maxDifficulty)
{
currentDifficulty += 0.1f;
IncreaseDifficulty();

if (playerNotifications)
{
DisplayMessage("The challenge increases...");
}
}
}
else if (playerPerformanceRating < 0.3f)
{
// Player is struggling - make it easier
if (currentDifficulty > minDifficulty)
{
currentDifficulty -= 0.15f;
DecreaseDifficulty();

if (playerNotifications)
{
DisplayMessage("You sense the challenge easing...");
}
}

// If player is really struggling, offer a hint
if (playerPerformanceRating < 0.1f && hintsEnabled)
{
DisplayHint();
}
}

// Apply difficulty changes
enemySpawnRate = baseDifficulty * currentDifficulty;
enemyHealth = (int)(baseEnemyHealth * (1 + (currentDifficulty - 1) * 0.5f));

// Log difficulty adjustment for debugging
Debug.Log($"Difficulty adjusted to {currentDifficulty:F2} based on performance rating of {playerPerformanceRating:F2}");
}

Conclusion

Conditional statements are the decision-making tools in your programming toolkit. They allow your programs to respond dynamically to different situations, making them essential for creating interactive and engaging games.

In this section, we've covered:

  • Basic if, if-else, and if-else if-else statements
  • Nested conditional statements
  • Boolean expressions and operators
  • Short-circuit evaluation
  • The conditional (ternary) operator
  • Common pitfalls and best practices
  • Practical examples in game development

In the next section, we'll explore the switch statement, which provides an alternative way to handle multiple conditions, especially when comparing a single variable against multiple possible values.

Unity Relevance

In Unity development, conditional statements are used extensively:

  • In Update() methods to check for player input or game states
  • In collision detection to determine what happens when objects collide
  • In AI systems to make decisions based on the environment
  • In UI systems to show or hide elements based on game state
  • In game mechanics to determine outcomes of player actions

Understanding conditional statements is crucial for implementing game logic that responds appropriately to the dynamic nature of games.