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:
Operator | Name | Example | Result |
---|---|---|---|
+ | Addition | 5 + 3 | 8 |
- | Subtraction | 5 - 3 | 2 |
* | Multiplication | 5 * 3 | 15 |
/ | Division | 5 / 3 | 1 (for integers), 1.6666... (for floating-point) |
% | Modulus (remainder) | 5 % 3 | 2 |
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:
Operator | Description | Example | Equivalent To |
---|---|---|---|
= | Simple assignment | x = 5 | x = 5 |
+= | Add and assign | x += 3 | x = x + 3 |
-= | Subtract and assign | x -= 3 | x = x - 3 |
*= | Multiply and assign | x *= 3 | x = x * 3 |
/= | Divide and assign | x /= 3 | x = x / 3 |
%= | Modulus and assign | x %= 3 | x = 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
):
Operator | Description | Example |
---|---|---|
== | Equal to | x == y |
!= | Not equal to | x != y |
> | Greater than | x > y |
< | Less than | x < y |
>= | Greater than or equal to | x >= y |
<= | Less than or equal to | x <= 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);
}
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:
Operator | Description | Example |
---|---|---|
&& | Logical AND | x && y (true if both x and y are true) |
|| | Logical OR | x || 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 isfalse
, the second operand is not evaluated (since the result must befalse
) - For
||
, if the first operand istrue
, the second operand is not evaluated (since the result must betrue
)
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:
Operator | Description | Example |
---|---|---|
++ | Increment | x++ or ++x |
-- | Decrement | x-- 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:
Operator | Description | Example |
---|---|---|
& | Bitwise AND | x & y |
| | Bitwise OR | x | y |
^ | Bitwise XOR | x ^ y |
~ | Bitwise NOT | ~x |
<< | Left shift | x << y |
>> | Right shift | x >> 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.
Null-Related Operators
C# provides special operators for working with nullable types:
Operator | Description | Example |
---|---|---|
?? | Null-coalescing | x ?? y (returns x if not null, otherwise y) |
?. | Null-conditional | obj?.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:
Category | Operators | Precedence |
---|---|---|
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.
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.