这是「C# 学习加固」系列的第 1 篇,共 5 篇;可通过文末的系列导航继续阅读。
1. 注释以及 log#
using UnityEngine;
// 注释是写给人看的
public class Hello : MonoBehaviour
{
void Start()
{
Debug.Log("这是一个普通日志。");
Debug.LogWarning("这是一个警告日志。");
Debug.LogError("这是一个错误日志。");
}
}
2. 变量以及常用类型#
变量是什么?
- 数据的抽象表示,类似数学中的变量
- 可以变化的,值可以变
- 除了整数、小数等以外还可以表示玩家的攻击力,生命值,等级,游戏昵称或者其他我们自定义的信息
常用的变量类型
start(字符串):用来存储用户名、密码、角色名称、技能名称等文本信息int(整数):存角色等级、纯数字ID等
float(浮点数):小数,用来存时间这类对精度要求更高的数值
整数、浮点数等数值类型都有取值范围,但是大多情况下以及目前情况下,可以忽视这个问题,感兴趣点链接了解:
变量的申明
类型 变量名 = 变量初始值;驼峰命名法
- 大驼峰:首字母大写,之后每个单次的首字母也大写,其余字母小写,如:GameOver、Home、MyGameID
- 小驼峰:首字母小写,之后每个单次的首字母也大写,其余字母小写,如:gameOver、home、myGameID
命名约束
- 首字母不能是数字,但是后续可以是数字
using UnityEngine;
public class Hello : MonoBehaviour
{
private void Start()
{
//变量的声明:类型 变量名 = 初始值;
string myName = "Lickba"; //字符串的使用,需要加双引号
int age = 18; //非字符串的使用,不需要加引号
float num = 15.654787354f; //浮点数的使用,后面加f表示这是一个浮点数
//使用或输出变量的时候,不需要加双引号,f等
Debug.Log(myName);
Debug.Log(age);
Debug.Log(num);
}
} 3. 转义字符#
转义字符
转义字符 \ 单引号 ' 双引号 " 退格符 (unity不支持) \b 换页符(unity不支持) \f 换行符 \n 回车 \r 表格 水平 \t 表格 垂直 \v 原义符
用来取消转义字符的特殊意义,要写在双引号外面
using UnityEngine; public class Hello : MonoBehaviour { private void Start() { // 转义字符是针对字符串的 string myName = "张三\t李四\t王五"; // 特殊需求:我们希望输出 双引号 // 程序认为 双引号是特殊符号,包括起来的就是字符串 // \:转义字符,它本身也是一个特殊符号 string str = "\""; Debug.Log(myName); // 输出转义字符的方式,就是用转义字符去修饰它 Debug.Log("\\\\"); // @:原义符,取消转义字符的作用 // 不能直接输出双引号,两个双引号会被认为是一个双引号 string names2 = @"张三\t李四\t王五"; string names3 = @"张三\t李四\t王五"""; Debug.Log(names2); Debug.Log(names3); } }
4. 字符串拼接#
+号拼接
$号格式化
using UnityEngine;
public class Hello : MonoBehaviour
{
private void Start()
{
// +号拼接
// 我是AA,我今年18岁
string name = "AA";
int age = 18;
// int类型和字符串相加时,int会自动转换为字符串
string info = "我是" + name + ",我今年" + age + "岁";
Debug.Log(info);
// $格式化输出,用{}去包裹变量
// $格式化输出时,@符号会对整体进行取消转义
string info2 = $"我是{name},我今年{age}岁";
Debug.Log(info2);
}
} 5. 变量的类型转换#
重新学习变量的定义方式
定义但是没有赋值,这种情况下无法使用变量
定义并且赋值
同类型变量批量定义
全部赋值
部分赋值
全部不赋值
// 批量定义,只需要写一次类型,变量名用逗号分隔 int num1 = 1, num2 = 2, num3 = 3;
=式的理解
- 等式右边的结果会赋值给左边,所以左边只能是变量,不能是数值
- 等式两边类型一直,不能int a = “123”
- 变量可以在等式两边任何一边
数值也有类型
C#内置的类型背后都有一个实际类型,鼠标放在类型关键字或数值上都能看到
- string: String
- int: Int32
- float: Single
int age = 25;
Int32 age2 = 30; // Int32 等于 int类型的转换方式
隐式类型转换:无需人为干涉的自动转换
类型上的兼容
精度小的转为精度大的
int num = 10; float fnum = num; // 把num的数值转为float类型
显式类型转换:人为干涉的强制转换
类型上的兼容
如果将精度大的转为精度小的,会丢失那些无法保存的精度
// 显式转换,丢失精度 float fnum3 = 10.01f; int num3 = (int)fnum3; // 将float转换为int,丢失小数部分
Convert强行转换(支持不兼容的类型)
本身对放进去的变量类型没什么限制,但是如果完全不具备转换的可能性,会报错
转换为Init: Convert.ToInt32
转换为float: Convert.ToSingle
转换为string: Convert.ToString
Convert.ToInt32("123"); Convert.ToSingle("1233.45"); Convert.ToString(123);
字符串转换
// ToString(),转为字符串 int age = 19; string ageStr1 = age + ""; // 不推荐的做法 string ageStr2 = age.ToString(); // 推荐的做法
6. 作用域和常量#
作用域#
C#中用大括号{}表示语句块、层次关系
而变量具备一个有效范围,这个范围就是他的作用域
变量只能在定义之后使用
变量只在它所在的“范围内有效”,这个范围可以简单的理解成大括号{}内
但是大括号{}有嵌套的情况时
“父级”的变量可以用在“子级”中
“子级”的变量不可以用在父级中
常量#
常量是固定的值,程序运行时不会修改的值,比如圆周率π就不会因任何原因而修改
非常类似变量,但是它在定义时必须赋值,其他时刻无法改变
常量同样有作用域
const float pi = 3.1415926f;
7. 算数运算符#
| 运算符 | 描述 |
|---|---|
| + | 加 |
| - | 减 |
| * | 乘 |
| / | 除 |
| % | 取余 |
| ++ | 自增1 |
| – | 自减1 |
前++、后++的区别(–同理)
int a = 1;
int b = a++; // b = 1
int a = 1;
int b = ++a; // b = 2前++是先自加再使用
而后++是先使用再自加
8. 赋值运算符#
| 运算符 | 描述 |
|---|---|
| = | 等号 |
| += | 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 = 3;
a += 4; // 7 等同于a = a + 4;
a -= 2; // 5 等同于a = a - 2;
a *= 2; // 10 等同于a = a * 2;
a /= 5; // 2 等同于a = a / 5;
a %= 3; // 2 等同于a = a % 3;
Debug.Log(a); // 输出结果为29. bool类型与关系运算符#
bool:布尔类型一般用来描述“是与否”、“真与假”、“能与不能”、“正确与错误”等情况
有两个值:
- True:代表是
- False:代表否
10. 关系运算符#
| 运算符 | 描述 |
|---|---|
| == | 检查两个值是否相等 |
| != | 检查两个值是否不相等 |
| > | 左边是否大于右边 |
| < | 左边是否小于右边 |
| >= | 左边是否大于或等于右边 |
| <= | 左边是否小于或等于右边 |
11. 逻辑运算符#
| 运算符 | 描述 |
|---|---|
| & | 同时,左右都是true返回true,一个为false就返回false |
| | | 或,左右都是false返回false,一个为true就返回true |
| && | 同时,左右都是true返回true,一个为false就返回false |
| || | 或,左右都是false返回false,一个为true就返回true |
| ! | 取反,true转为false,false转为true |
&:实际上是按位与,但是目前如果深究这块,会比较复杂,所以可以按照上面的理解
|:实际上是按位或,但是目前如果深究这块,会比较复杂,所以可以按照上面的理解
&&、||都会发生短路机制,也就是如果左边已经为false则不会执行右边,实际开发中使用较多
短路机制:一但确定了结论,后面就不执行,确定的顺序是从左往右(性能会更好)
string userName = "User2";
string password = "Password123";
bool checkUserName = userName == "User";
bool checkPassword = password == "Password123";
bool online = true; // 当前网络在线
bool canLogin = checkUserName & checkPassword & online; // 能登录 = 用户名正确 同时 密码正确
// bool canLogin = (checkUserName & checkPassword) & online;
Debug.Log($"玩家可以登录: {canLogin}");
bool admin = true; // 是否是管理员
bool adminCanLogin = checkUserName | admin; // 管理员可以登录 = 用户名正确 或者 是管理员
Debug.Log($"管理员可以登录: {adminCanLogin}");
Debug.Log(!false);
Debug.Log(!true);
// ! 是逻辑非运算符,用于取反布尔值。
// 它会将 true 变为 false,将 false 变为 true。
// & // 按位与运算符
// && // 逻辑与运算符
// | // 按位或运算符
// || // 逻辑或运算符
// & 跟 && 的区别:
// & 是按位与运算符,会对每一位进行比较,只有当两个操作数的对应位都为1时,结果才为1。
// && 是逻辑与运算符,只有当两个操作数都为true时,结果才为true,并且如果第一个操作数为false,则不会计算第二个操作数(短路求值)。
// | 跟 || 的区别:
// | 是按位或运算符,会对每一位进行比较,只要有一个操作数的对应位为1,结果就为1。
// || 是逻辑或运算符,只要有一个操作数为true,结果就为true,并且如果第一个操作数为true,则不会计算第二个操作数(短路求值)。12. 三目运算符#
也被称为条件运算符
三目运算符并不是一种必要的运算符,是为了方便
结果 = 如果条件为真 ?则为X :否则为Y

int value = -80;
int num = value < 0 ? 0 : value; // 如果num小于0,则num赋值为0,否则不变
Debug.Log(num);
// 最多是100
int num2 = value > 100 ? 100 : value; // 如果num大于100,则num2赋值为100,否则num2赋值为num
Debug.Log(num2);
// 控制在0到100之间
int num3 = value < 0 ? 0 : value;
num3 = num3 > 100 ? 100 : num3;
Debug.Log(num3);13. if判断语句#
if(如果):用来判断条件是否成立,即是否为true
分支语句:不能单独使用
else if (其他可能性):if不成立,但是满足我的条件,则执行。
else:以上条件都不满足则执行
注意事项#
- 短路机制,if如果执行成功则不会执行else if 或 else 的语句,短路机制不只是不执行{}中的代码,判断条件本身也不会运行(因为判断条件有时候本身是有计算过程的)
- if语句中大括号也存在变量的作用域问题
14. 枚举类型#
特别适合用来描述状态、阶段
当某个数据可以预期有确定性的几种结构时,我们可以使用枚举类型来保存,比如
AI:待机、攻击、死亡
时间:周一、周二、周三
游戏状态:选择英雄、加载界面、战斗中、游戏结束
枚举是一种由我们自己去创建的类型,也是我们首次去创建我们自己的类型
定义枚举类型,一般我们会将它定义在class同级中或单独新建一个脚本文件去保存,不要定义在函数中
定义这个枚举类型的变量
使用这个变量
/// <summary> /// AI状态 /// </summary> enum AIState { /// <summary> /// 待机 /// </summary> Idle, /// <summary> /// 攻击 /// </summary> Attack, /// <summary> /// 死亡 /// </summary> Die = 1000 // 自定义状态对应的值 }
15. Switch语句#
switch语句和if else if 非常接近,甚至理论上不存在也没有任何关系,不过是特定情况下会更加方便
语法:#

对比if语句#
if else if的特点是判断的因素可以完全不相关,只要条件都是bool类型即可,比如:
if(3==3)
{
}
else if (a2 > a3)
{
}但是Switch一般用在围绕一个因素去做判断,比如当前是星期几,AI处于什么状态,所以和枚举类型特别搭配
break#
并不是每一个case都需要break,如果case 语句后面没有要执行的代码,则可以不包含 break 关键字, 这时程序会继续执行后续的case语句,直至遇到break关键字为止;
这种情况的目的一般在于,如果我们希望当AI状态为巡逻、待机时候要做的事情是一样的

16. for循环#
一般用于循环固定次数的情况
语法#

运行步骤:#
初始化代码,只会执行一次,也可以不写
判断条件是否满足
满足则执行{}中的主代码片段
- 执行更新时间
- 判断条件,满足则继续执行…(后续循环)
不满足则退出循环
// 案例:1+2+3+4+5....+100
int res = 0;
for (int i = 1; i <= 100; i++)
{
res += i; // 将i的值累加到res中
Debug.Log($"i = {i}, res = {res}");
}
Debug.Log($"1+2+3+4+5....+100 = {res}");17. while循环语句#
基于条件的循环,也就是条件满足就一直循环
// 避免死循环
// 从1加到100
int num = 1;
int res = 0;
while (num<=100)
{
res += num;
num++;
Debug.Log($"当前num={num}, res={res}");
}
Debug.Log(res);还有一个变种 do while,但是用的少
// 至少执行一次
do
{
// 首先执行一次
// 循环体
Debug.Log("至少执行一次");
} while (false); // 如果条件满足才会执行下一次