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
Operator | Description | Example |
---|---|---|
== | Equal to | score == 100 |
!= | Not equal to | health != 0 |
> | Greater than | level > 5 |
< | Less than | ammo < 10 |
>= | Greater than or equal to | experience >= 1000 |
<= | Less than or equal to | time <= 30 |
Logical Operators
Operator | Description | Example |
---|---|---|
&& | 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 isfalse
, the second condition is not evaluated (since the result must befalse
). - For
||
, if the first condition istrue
, the second condition is not evaluated (since the result must betrue
).
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
, andif-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.
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.