2.3 运算符与表达式
在游戏开发中,我们经常需要进行各种计算:伤害计算、经验值增长、状态判断等。C#提供了丰富的运算符来帮助我们完成这些操作。
目录
算术运算符
想象你正在开发一个角色战斗系统:
public class CombatSystem
{
// 基础伤害计算
public float CalculateDamage(float baseDamage, float multiplier, float defense)
{
// 加法:基础伤害 + 附加伤害
float totalDamage = baseDamage + 25;
// 减法:伤害减去防御力
totalDamage = totalDamage - defense;
// 乘法:应用伤害倍率
totalDamage = totalDamage * multiplier;
// 除法:计算实际伤害(考虑护甲减免)
float finalDamage = totalDamage / (1 + defense * 0.1f);
// 取余:计算溢出伤害
int overkillDamage = (int)finalDamage % 100;
// 使用复合赋值运算符
totalDamage += 10; // 增加额外伤害
totalDamage *= 1.5f; // 暴击伤害
return finalDamage;
}
// 经验值计算
public void CalculateExperience(ref int currentExp, int gainedExp)
{
// 自增和自减
currentExp++; // 完成任务的基础经验
gainedExp--; // 经验衰减
// 前缀和后缀
int bonusExp = 100;
int totalExp = ++bonusExp; // 先增加再使用
int extraExp = bonusExp--; // 先使用再减少
}
}
赋值运算符
赋值运算符用于将值赋给变量。
运算符 | 描述 | 示例 | 等价于 |
---|---|---|---|
= | 简单赋值 | a = b | a = b |
+= | 加法赋值 | a += b | a = a + b |
-= | 减法赋值 | a -= b | a = a - b |
*= | 乘法赋值 | a *= b | a = a * b |
/= | 除法赋值 | a /= b | a = a / b |
%= | 取余赋值 | a %= b | a = a % b |
&= | 按位与赋值 | a &= b | a = a & b |
|= | 按位或赋值 | a |= b | a = a | b |
^= | 按位异或赋值 | a ^= b | a = a ^ b |
<<= | 左移赋值 | a <<= b | a = a << b |
>>= | 右移赋值 | a >>= b | a = a >> b |
示例代码
int a = 10;
a += 5; // 等价于 a = a + 5
Console.WriteLine($"a += 5: {a}"); // 输出: a += 5: 15
a -= 3; // 等价于 a = a - 3
Console.WriteLine($"a -= 3: {a}"); // 输出: a -= 3: 12
a *= 2; // 等价于 a = a * 2
Console.WriteLine($"a *= 2: {a}"); // 输出: a *= 2: 24
a /= 4; // 等价于 a = a / 4
Console.WriteLine($"a /= 4: {a}"); // 输出: a /= 4: 6
a %= 4; // 等价于 a = a % 4
Console.WriteLine($"a %= 4: {a}"); // 输出: a %= 4: 2
比较运算符
在游戏中,我们经常需要进行各种条件判断:
public class PlayerStatus
{
private float health;
private int level;
private bool isAlive;
public void CheckStatus()
{
// 等于和不等于
if (health == 0)
{
isAlive = false;
}
if (level != 99)
{
// 还可以升级
}
// 大于和小于
if (health > 0)
{
isAlive = true;
}
if (level < 10)
{
// 新手玩家
}
// 大于等于和小于等于
if (health >= 100)
{
// 满血状态
}
if (level <= 20)
{
// 低等级玩家
}
}
}
逻辑运算符
在处理游戏逻辑时,我们需要组合多个条件:
public class GameLogic
{
public bool CanAttack(Player player, Enemy enemy)
{
// 逻辑与:玩家存活且有足够的法力值
if (player.IsAlive && player.ManaPoints >= 10)
{
return true;
}
// 逻辑或:敌人被眩晕或被冰冻
if (enemy.IsStunned || enemy.IsFrozen)
{
return true;
}
// 逻辑非:敌人不是无敌状态
if (!enemy.IsInvincible)
{
return true;
}
return false;
}
public void ProcessCombat(Player player, Enemy enemy)
{
// 短路逻辑:如果玩家死亡,不会检查后面的条件
if (!player.IsAlive || !CanAttack(player, enemy))
{
return;
}
// 复杂条件组合
if ((player.Health > 50 || player.HasShield) && !enemy.IsInvincible)
{
// 可以进行攻击
}
}
}
位运算符
位运算符在处理游戏状态和权限时非常有用:
public class StatusSystem
{
[Flags]
public enum PlayerStatus
{
None = 0, // 0000 0000
Poisoned = 1 << 0, // 0000 0001
Burning = 1 << 1, // 0000 0010
Frozen = 1 << 2, // 0000 0100
Stunned = 1 << 3 // 0000 1000
}
private PlayerStatus currentStatus;
public void UpdateStatus()
{
// 位与:检查状态
bool isPoisoned = (currentStatus & PlayerStatus.Poisoned) != 0;
// 位或:添加状态
currentStatus |= PlayerStatus.Burning;
// 位异或:切换状态
currentStatus ^= PlayerStatus.Frozen;
// 位取反:清除所有状态
currentStatus = ~PlayerStatus.None;
// 左移:增加伤害等级
int damageLevel = 1;
int damageMask = 1 << damageLevel; // 1变成2,2变成4
// 右移:降低伤害等级
damageMask = damageMask >> 1; // 2变成1,4变成2
}
}
特殊运算符
C#还提供了一些特殊的运算符,用于特定的操作。
运算符 | 描述 | 示例 |
---|---|---|
?: | 条件运算符(三元运算符) | condition ? expr1 : expr2 |
?? | 空合并运算符 | a ?? b |
??= | 空合并赋值运算符 | a ??= b |
is | 类型检查 | obj is Type |
as | 类型转换 | obj as Type |
typeof | 获取类型 | typeof(Type) |
sizeof | 获取值类型的大小 | sizeof(Type) |
new | 创建对象 | new Type() |
. | 成员访问 | obj.member |
[] | 数组索引 | array[index] |
() | 方法调用 | method() |
条件运算符(三元运算符)
条件运算符?:
是C#中唯一的三元运算符,它根据条件表达式的结果返回两个表达式之一。
int a = 10;
int b = 20;
// 如果a大于b,返回a,否则返回b
int max = (a > b) ? a : b;
Console.WriteLine($"较大的数是: {max}"); // 输出: 较大的数是: 20
// 三元运算符可以嵌套使用
int c = 15;
int median = (a > b) ? ((a > c) ? ((b > c) ? b : c) : a) : ((b > c) ? ((a > c) ? a : c) : b);
Console.WriteLine($"中间的数是: {median}"); // 输出: 中间的数是: 15
空合并运算符
空合并运算符??
在左操作数为null
时返回右操作数,否则返回左操作数。
string name = null;
string displayName = name ?? "未知用户";
Console.WriteLine($"欢迎, {displayName}"); // 输出: 欢迎, 未知用户
name = "张三";
displayName = name ?? "未知用户";
Console.WriteLine($"欢迎, {displayName}"); // 输出: 欢迎, 张三
空合并赋值运算符
空合并赋值运算符??=
在左操作数为null
时将右操作数赋值给左操作数。
string username = null;
username ??= "游客";
Console.WriteLine($"用户名: {username}"); // 输出: 用户名: 游客
username = "admin";
username ??= "游客";
Console.WriteLine($"用户名: {username}"); // 输出: 用户名: admin
类型检查和转换运算符
is
和as
运算符用于类型检查和安全类型转换。
object obj = "Hello, World!";
// 使用is运算符检查类型
if (obj is string)
{
Console.WriteLine("obj是一个字符串");
// C# 7.0模式匹配
if (obj is string message)
{
Console.WriteLine($"字符串内容: {message}");
}
}
// 使用as运算符进行安全类型转换
string str = obj as string;
if (str != null)
{
Console.WriteLine($"转换后的字符串: {str}");
}
// 尝试将整数转换为字符串(会失败)
object number = 123;
string numStr = number as string; // numStr将为null
Console.WriteLine($"numStr是否为null: {numStr == null}"); // 输出: numStr是否为null: True
运算符优先级
C#中的运算符按照优先级从高到低的顺序执行。下表列出了主要运算符的优先级(从高到低):
类别 | 运算符 |
---|---|
主要 | x.y, f(x), a[i], x++, x–, new, typeof, checked, unchecked |
一元 | +, -, !, ~, ++x, --x, (T)x |
乘法 | *, /, % |
加法 | +, - |
移位 | <<, >> |
关系 | <, >, <=, >=, is, as |
相等 | ==, != |
按位与 | & |
按位异或 | ^ |
按位或 | | |
逻辑与 | && |
逻辑或 | || |
条件 | ?: |
赋值 | =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= |
当表达式中包含多个具有相同优先级的运算符时,大多数运算符从左到右计算(左结合性)。例外的是赋值运算符和条件运算符,它们从右到左计算(右结合性)。
示例代码
int a = 5 + 3 * 2; // 乘法优先级高于加法: 5 + (3 * 2) = 11
Console.WriteLine($"5 + 3 * 2 = {a}");
int b = (5 + 3) * 2; // 括号改变了优先级: (5 + 3) * 2 = 16
Console.WriteLine($"(5 + 3) * 2 = {b}");
int c = 10 - 2 - 3; // 左结合性: (10 - 2) - 3 = 5
Console.WriteLine($"10 - 2 - 3 = {c}");
int d = 1;
d = d + d++ + ++d; // 复杂表达式,需要理解运算符优先级和求值顺序
Console.WriteLine($"d = d + d++ + ++d 的结果: {d}");
表达式类型
在C#中,表达式可以分为多种类型:
- 算术表达式:使用算术运算符的表达式,如
a + b * c
- 逻辑表达式:使用逻辑运算符的表达式,如
a && b || c
- 关系表达式:使用比较运算符的表达式,如
a > b
- 位表达式:使用位运算符的表达式,如
a & b | c
- 赋值表达式:使用赋值运算符的表达式,如
a = b + c
- 条件表达式:使用条件运算符的表达式,如
a > b ? a : b
- 方法调用表达式:调用方法的表达式,如
Math.Max(a, b)
- 对象创建表达式:使用
new
关键字创建对象的表达式,如new Person()
- Lambda表达式:使用Lambda语法的表达式,如
x => x * x
复合表达式
复合表达式是由多个简单表达式组合而成的表达式。在评估复合表达式时,会根据运算符优先级和结合性确定操作的执行顺序。
// 复合表达式示例
int a = 5, b = 3, c = 2;
int result = a * b + c * (a + b) / (a - c);
Console.WriteLine($"表达式结果: {result}");
// 表达式求值过程:
// 1. (a + b) = 5 + 3 = 8
// 2. (a - c) = 5 - 2 = 3
// 3. a * b = 5 * 3 = 15
// 4. c * (a + b) = 2 * 8 = 16
// 5. c * (a + b) / (a - c) = 16 / 3 = 5 (整数除法)
// 6. a * b + c * (a + b) / (a - c) = 15 + 5 = 20
练习与实践
练习1:伤害计算系统
实现一个复杂的伤害计算系统:
public class AdvancedCombatSystem
{
public float CalculateDamage(
float baseDamage,
float criticalMultiplier,
float penetration,
float defense,
bool isCritical,
bool isWeakPoint,
PlayerStatus status)
{
// 基础伤害计算
float finalDamage = baseDamage;
// 应用暴击
if (isCritical)
{
finalDamage *= criticalMultiplier;
}
// 应用弱点攻击
finalDamage *= isWeakPoint ? 1.5f : 1.0f;
// 计算护甲穿透
float effectiveDefense = defense * (1 - penetration);
// 应用防御减免
finalDamage = finalDamage * (100 / (100 + effectiveDefense));
// 检查状态效果
if ((status & PlayerStatus.Burning) != 0)
{
finalDamage *= 1.2f; // 燃烧状态增加伤害
}
if ((status & PlayerStatus.Frozen) != 0)
{
finalDamage *= 0.8f; // 冰冻状态减少伤害
}
// 确保伤害不为负
return Math.Max(0, finalDamage);
}
}
练习2:技能系统
实现一个技能检查和释放系统:
public class SkillSystem
{
public bool CanCastSkill(
Player player,
Skill skill,
Target target)
{
// 检查玩家状态
bool playerReady = player.IsAlive
&& !player.IsStunned
&& !player.IsSilenced;
// 检查技能条件
bool skillReady = skill.Cooldown <= 0
&& player.ManaPoints >= skill.ManaCost
&& player.Level >= skill.RequiredLevel;
// 检查目标
bool targetValid = target != null
&& target.IsAlive
&& !target.IsInvulnerable
&& IsInRange(player, target, skill.Range);
// 组合所有条件
return playerReady && skillReady && targetValid;
}
public void CastSkill(
Player player,
Skill skill,
Target target)
{
// 前置检查
if (!CanCastSkill(player, skill, target))
{
return;
}
// 消耗资源
player.ManaPoints -= skill.ManaCost;
// 计算技能效果
float effectValue = skill.BaseValue;
// 应用增益效果
effectValue *= player.HasBuff("Empowered") ? 1.5f : 1.0f;
// 应用天赋加成
effectValue += player.TalentPoints * 0.1f;
// 处理技能效果
switch (skill.Type)
{
case SkillType.Damage:
ApplyDamage(target, effectValue);
break;
case SkillType.Heal:
ApplyHealing(target, effectValue);
break;
case SkillType.Buff:
ApplyBuff(target, skill.BuffType, effectValue);
break;
}
// 设置冷却
skill.Cooldown = skill.BaseCooldown;
}
}
常见问题与解决方案
-
整数除法问题
// 错误示例 int result = 5 / 2; // 结果为2,小数部分被截断 // 正确示例 double result = 5.0 / 2; // 结果为2.5 // 或者 int result = 5 / 2; double remainder = 5 % 2; // 获取余数
-
浮点数精度问题
// 错误示例
float a = 0.1f;
float b = 0.2f;
bool equal = (a + b) == 0.3f; // 可能为false
// 正确示例
float a = 0.1f;
float b = 0.2f;
bool equal = Math.Abs((a + b) - 0.3f) < float.Epsilon;
- 逻辑运算符短路
// 错误示例 if (player != null && player.Health > 0) // 可能抛出异常 // 正确示例 if (player?.Health > 0) // 使用空条件运算符
## 总结
在本节中,我们学习了:
1. 算术运算符在游戏数值计算中的应用
2. 比较运算符在游戏逻辑判断中的使用
3. 逻辑运算符在复杂条件判断中的作用
4. 位运算符在状态管理中的高效应用
5. 条件运算符让代码更简洁的方法
通过实战练习,我们实现了:
1. 一个复杂的伤害计算系统
2. 一个完整的技能系统
这些运算符和表达式的灵活运用,可以帮助我们实现更复杂的游戏功能。在下一节中,我们将学习数组与集合的使用。
## 下一步学习
- 数组与集合基础
- 流程控制语句
- 字符串处理