Skip to main content

2.2 - Operators

Operators are symbols that tell the compiler to perform specific mathematical or logical operations. They allow you to manipulate variables and values in your code, making your programs dynamic and responsive.

Arithmetic Operators

Arithmetic operators perform mathematical operations on numeric values:

OperatorNameExampleResult
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division5 / 31 (for integers), 1.6666... (for floating-point)
%Modulus (remainder)5 % 32

Integer vs. Floating-Point Division

Be careful with division, as the result depends on the operand types:

int a = 5;
int b = 2;
int result1 = a / b; // result1 is 2 (integer division truncates)

float c = 5f;
float d = 2f;
float result2 = c / d; // result2 is 2.5 (floating-point division)

// Mixed types result in floating-point division
float result3 = a / d; // result3 is 2.5

Modulus Operator in Game Development

The modulus operator (%) is particularly useful in game development for:

  • Creating cyclic behaviors
  • Wrapping values within a range
  • Determining if a number is even or odd
// Example: Cycling through an array of 4 weapons
int currentWeaponIndex = 0;

// When player presses "next weapon" button
currentWeaponIndex = (currentWeaponIndex + 1) % 4;
// After reaching 3, it wraps back to 0

// Checking if a number is even or odd
bool isEven = (number % 2 == 0);

Assignment Operators

Assignment operators assign values to variables:

OperatorDescriptionExampleEquivalent To
=Simple assignmentx = 5x = 5
+=Add and assignx += 3x = x + 3
-=Subtract and assignx -= 3x = x - 3
*=Multiply and assignx *= 3x = x * 3
/=Divide and assignx /= 3x = x / 3
%=Modulus and assignx %= 3x = x % 3

Compound assignment operators provide a concise way to update variables:

// Updating game stats
playerScore += 100; // Add points
playerHealth -= 10; // Take damage
enemyCount--; // Decrement enemy count
timeRemaining *= 0.9f; // Reduce remaining time by 10%

Comparison Operators

Comparison operators compare two values and return a boolean result (true or false):

OperatorDescriptionExample
==Equal tox == y
!=Not equal tox != y
>Greater thanx > y
<Less thanx < y
>=Greater than or equal tox >= y
<=Less than or equal tox <= y

These operators are essential for conditional logic:

// Game condition checks
if (playerHealth <= 0)
{
GameOver();
}

if (score >= highScore)
{
UpdateHighScore(score);
}

if (currentLevel != selectedLevel)
{
LoadLevel(selectedLevel);
}
caution

Be careful not to confuse the assignment operator (=) with the equality comparison operator (==). This is a common source of bugs!

Logical Operators

Logical operators work with boolean values and are used to combine or modify conditions:

OperatorDescriptionExample
&&Logical ANDx && y (true if both x and y are true)
||Logical ORx || y (true if either x or y is true)
!Logical NOT!x (true if x is false)

Short-Circuit Evaluation

The && and || operators use short-circuit evaluation:

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

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

// Check if an object exists before accessing its properties
if (player != null && player.IsAlive)
{
player.TakeDamage(10);
}

// Short-circuit to avoid division by zero
if (divisor != 0 && (number / divisor > 10))
{
// Safe to perform division
}

Increment and Decrement Operators

These operators increase or decrease a variable's value by 1:

OperatorDescriptionExample
++Incrementx++ or ++x
--Decrementx-- or --x

Prefix vs. Postfix

These operators can be used in prefix or postfix form, with different behaviors:

  • Prefix (++x, --x): The variable is incremented/decremented first, then its value is used
  • Postfix (x++, x--): The variable's current value is used first, then it's incremented/decremented
int a = 5;
int b = ++a; // a is incremented to 6, then b is assigned 6
// a is 6, b is 6

int c = 5;
int d = c++; // d is assigned 5, then c is incremented to 6
// c is 6, d is 5

Bitwise Operators

Bitwise operators manipulate individual bits in integer values:

OperatorDescriptionExample
&Bitwise ANDx & y
|Bitwise ORx | y
^Bitwise XORx ^ y
~Bitwise NOT~x
<<Left shiftx << y
>>Right shiftx >> y

While less common in everyday code, bitwise operators are useful for:

  • Flag operations (combining multiple boolean options)
  • Low-level optimizations
  • Working with hardware interfaces
// Using bitwise operators for flags (powers of 2)
[Flags]
enum PlayerAbilities
{
None = 0,
Jump = 1, // 2^0 = 1 (binary: 0001)
Sprint = 2, // 2^1 = 2 (binary: 0010)
DoubleJump = 4, // 2^2 = 4 (binary: 0100)
Dash = 8 // 2^3 = 8 (binary: 1000)
}

// Giving a player multiple abilities
PlayerAbilities abilities = PlayerAbilities.Jump | PlayerAbilities.Sprint;

// Checking if player has a specific ability
bool canJump = (abilities & PlayerAbilities.Jump) == PlayerAbilities.Jump;

// Adding an ability
abilities |= PlayerAbilities.Dash;

// Removing an ability
abilities &= ~PlayerAbilities.Sprint;

The Ternary Operator

The ternary 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.

C# provides special operators for working with nullable types:

OperatorDescriptionExample
??Null-coalescingx ?? y (returns x if not null, otherwise y)
?.Null-conditionalobj?.Method() (calls Method if obj is not null)
!Null-forgiving (C# 8.0+)obj!.Method() (tells compiler that obj is not null)

We'll explore these more in section 2.5 when we discuss nullable types.

Operator Precedence

When an expression contains multiple operators, operator precedence determines the order of evaluation:

CategoryOperatorsPrecedence
Primary(), [], ., ?., ?[]Highest
Unary+, -, !, ~, ++, --, (type)
Multiplicative*, /, %
Additive+, -
Shift<<, >>
Relational<, >, <=, >=, is, as
Equality==, !=
Logical AND&
Logical XOR^
Logical OR|
Conditional AND&&
Conditional OR||
Null-coalescing??
Ternary?:
Assignment=, +=, -=, etc.Lowest

When in doubt, use parentheses to explicitly control the order of operations:

// Without parentheses - multiplication happens before addition
int result1 = 2 + 3 * 4; // result1 = 2 + 12 = 14

// With parentheses - addition happens first
int result2 = (2 + 3) * 4; // result2 = 5 * 4 = 20

Practical Examples in Game Development

Example 1: Calculating Damage

// Base damage calculation
int baseDamage = weaponDamage + strengthModifier;

// Critical hit check
bool isCriticalHit = Random.value <= criticalHitChance;

// Final damage calculation
int finalDamage = isCriticalHit ? baseDamage * 2 : baseDamage;

// Apply damage reduction from armor
finalDamage -= targetArmor / 2;

// Ensure minimum damage of 1
finalDamage = finalDamage < 1 ? 1 : finalDamage;

// Apply damage to target
targetHealth -= finalDamage;

// Check if target is defeated
bool isTargetDefeated = targetHealth <= 0;

Example 2: Movement and Boundaries

// Update position based on input and speed
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");

// Calculate new position
float newX = transform.position.x + horizontalInput * moveSpeed * Time.deltaTime;
float newY = transform.position.y + verticalInput * moveSpeed * Time.deltaTime;

// Clamp position within boundaries
newX = Mathf.Clamp(newX, minX, maxX);
newY = Mathf.Clamp(newY, minY, maxY);

// Update position
transform.position = new Vector3(newX, newY, transform.position.z);

Example 3: Game State Management

// Check various game conditions
bool isLevelComplete = (collectedItems >= requiredItems) && (enemiesDefeated || timeRemaining <= 0);
bool isGameOver = playerHealth <= 0 || timeRemaining <= 0;

// Update game state
if (isGameOver)
{
gameState = GameState.GameOver;
ShowGameOverScreen(playerHealth <= 0 ? "You were defeated!" : "Time's up!");
}
else if (isLevelComplete)
{
gameState = GameState.LevelComplete;
int bonusPoints = (int)(timeRemaining * pointsPerSecond);
totalScore += levelScore + bonusPoints;
ShowLevelCompleteScreen();
}

Conclusion

Operators are essential tools in C# programming that allow you to manipulate data and control program flow. By understanding the different types of operators and how they work, you can write more efficient and expressive code.

In the next section, we'll explore type conversion (casting), which allows you to convert values from one data type to another.

Unity Relevance

In Unity development, you'll use operators extensively for:

  • Calculating physics and movement
  • Managing game state and conditions
  • Implementing game mechanics like damage, scoring, and timers
  • Optimizing performance with bitwise operations for flags and masks
  • Creating responsive UI and gameplay elements

Understanding operators is crucial for implementing game logic and creating dynamic, interactive experiences.