目录
本文主要来自<
本章将专注以下主题:
最复杂的编程问题通常可以归结为对游戏或程序进行评估并执行的一系列简单选择。。因为 Visual Studio和Unity 无法自己做出这些选择,所以这些决定要由我们来做。通过使用ifelse和 switch 选择语句,我们可以基于一个或多个条件以及每种情况下将要执行的操作来指定分支路径。
传统上,这些条件包括:
使用if-else 语句是在代码中进行决策的最常见方式。抛开语法不讲,if-else的基本思想是:如果条件满足,就执行一段代码;如果不满足,就执行另一段代可以将这些语句视为以条件为钥匙的门。为了顺利通过,条件必须是有效的:否绝通过,代码将被发送到下一个可能的门。
1.基本语法
有效的if-else 语句需要具备以下条件:
语法如下:
- if(condition is true)
- {
- Execute this code block
- }
- else
- {
- Execute this code block
- }
因为这些都是对逻辑思维的很好介绍,至少在编程中是这样,所以我们将更详细地分析三种不同的ifelse 变体。
1.如果不关心条件没有满足时会发生什么,那么只使用 if语句即可。在图41中如果hasDungeonKey为tue,就输出调试日志;如果为 false,则不执行任何代码。
- public class LearningCurve : MonoBehaviour
- {
- public bool hasDungeonKey = true;
- // Use this for initialization
- void Start()
- {
- if(hasDungeonKey)
- {
- Debug.Log("You possess the sacred key - enter.");
- }
- }
- }
2.在无论条件满足与否都需要采取措施的情况下,可以添加 else 语句。如果hasDungeonKey为false,f语句将失败,并且控制流将跳至else 语句,如图42所示
- public class LearningCurve : MonoBehaviour
- {
- public bool hasDungeonKey = true;
- // Use this for initialization
- void Start()
- {
- if(hasDungeonKey)
- {
- Debug.Log("You possess the sacred key - enter.");
- }
- else
- {
- Debug.Log("You have not proved yourself worthy,warrion"):
- }
- }
- }
3. 对于需要两个以上可能结果的情况,可以添加带有括号、条件和花括号的else-if语句。这会是最好的展示而不是解释,我们将在后面介绍。
实践一一小偷的预期
下面编写一条 ifelse 语句,用于检查口袋中的金额,并针对三种不同的情况返回不同的调试日志。
(1)打开 LearningCurve 脚本并添加一个名为 currentGold 的int变量,将值设置为一个介于1和100之间的整数。
(2)添加f语句来检查 currentGold 是否大于50。如果大于,就向控制台打印一条消息。
(3)添加else-if语句来检查 currentGold 是否小于15。如果小于,就打印另一条不同的调试日志。
(4)添加不带任何条件的else 语句和想要打印的默认日志。
(5)如图4-3 所示保存文件并单击 Play 按钮。
- public class LearningCurve:MonoBehaviour
- {
- public Int currentGold = 32;
- // Use this for initialization
- void Start()
- {
- if(currentGold >50)
- {
- Debug.Log("You're rolling in it - beware of pickpockets.");
- }
- else if(currentGold < 15)
- {
- Debug.Log("Not much there to steal.");
- }
- else
- {
- Debug.Log("Looks like your purse is in the sweet spot.“);
- }
- }
- }
这里将currenGold 设置为 32,我们可以按以下方式分解代码。
2.使用逻辑非运算符
用例并不总是需要检查条件为 tue 的情况,而这正是逻辑非运算符出现的原因。逻辑非运算符允许我们检查迁或 else-if 语句满足条件为false 时的情况。如前所述,我们可以在 f条件下检查布尔值、字面值或表达式,所以很自然地,逻辑非运算符必须具有适应性。
- public class LearningCurve : MonoBehaviour
- {
- public bool hasDungeonKey = false;
- public string weaponType =“Arcane Staff";
- // Use this for initialization
- void Start()
- {
- if(!hasDungeonKey)
- {
- Debug.Log("You may not enter without the sacred keyl.");
- }
- if(weaponType !="Longsword")
- {
- Debug.Log("You don't appear to have the right type of weapon...");
- }
- }
- }
看到调试结果,如果仍然不理解,那么可以复制前面的代码到LearningCurve 脚本中,运行这些变量,直到它们奏效。
3.嵌套语句
ifelse 语句最有价值的功能之一是它们可以相互嵌套,从而创建复杂的逻辑路线在编程中,我们称之为决策树。就像真实的走廊一样,其他门的后面也可以有门,像是迷宫。
首先观察图下的示例:
- public elass LearningCurve : MonoBehaviour
- {
- public bool weaponEquipped = true;
- public string weaponType ="Longsword";
- void Start()
- {
- if(weaponEquipped)
- {
- if(weaponType==“Longsword")
- {
- Debug.Log("For the Queen!");
- }
- }
- else
- {
- Debug.Log("Fists aren't going to work against armor...");
- }
- }
- }
4.计算多个条件
除了套语句,还可以使用 AND 和 OR 逻辑运算符将多个条件检查组合到单个if或elseif语句中:
if语句已更新为检查 weaponEquipped和weaponType,只有当它们两者都为true时才执行代码块。
- if(weaponEquipped weapanType =="Longsword”)
- {
- Debug.Log("For the queen!“);
- }
实践一到达宝藏
下面进一步巩固所学知识。
(1)在 LearningCurve 脚本的顶部声明三个变量:pureOfHeart 是布尔值,应该true;hasSecretIncantation 还是布尔值,应该为false; rareltem 是字符串,值取决于你.
(2)创建一个名为OpenTreasureChamber 的没有返回值的 public 方法,并在 Start方法中调用。
(3)在OpenTreasureChamber 方法中声明 ifelse 语句来检查 pureOfHeart 是否为
true,并检查rareltem 是否和赋予的字符串匹配。
(4)在第一条f语句中创建套的 if-else 语句,检查hasSecretIncantation 是否为false。
(5)为每个if-else语句添加调试日志,保存后单击Play 按钮。
刚刚发生了什么
将会打印套的if 语句的调试日志,如图这意味着代码通过第一条 if 语句中的两个条件,但第二条if 语句中的条件没有通过
嵌套过多的代码最终将难以理解,且很难修改。switch 语句能让我们为每种可能的结果编写代码,但格式相比 if-else 语句更为简洁。
1.基本语法
switch语句需要具备以下条件。
以蓝图的形式看起来语法如下:
- switch (matohExpression)
- {
- case matchValvel:
- Executing code block
- break;
- case matchValue2 :
- Executing code block
- break;
- default:
- Executing code block
- break;
- }
在case子句中,冒号和break 关键字之间的任何内容都类似于ifelse语句中的代码块。break关键字用于告诉程序在选定的条件触发后完全退出switch语句。
2模式匹配
在switch语句中,模式匹配是 case 子句用来验证匹配表达式的方式。匹配表达式可以是不为null的任何类型。所有 case 子句的值都要对应匹配表达式的类型。例如,对于计算整型数的 switch 语句,每个 case 子句都需要指定一个整数来进行检查。与匹配表达式匹配的case 子句会被执行。如果没有匹配的case 子句,则执行 defaut 子句.
实践一选择动作
这里包含大量新的语法和信息,但 switch 语句有助于观察实际操作。下面让我们为游戏角色可以采取的不同操作创建一条简单的 switch 语句。
(1)创建一个名为 characterAction 的字符串类型的成员变量或局部变量,设置为"Attack"。
(2)声明一条 switch 语句,使用 characterAction 作为匹配表达式。
(3)创建两个 case 子句以打印不同的调试日志,并且不要忘记在每个 case 子句的末尾添加 break 关键字。
(4)添加带有调试日志的 default 子句和 break 关键字。
(5)保存脚本并在Unity 单击 Play 按钮,
- public class LearningCurve : MonoBehaviour
- {
- void Start()
- {
- string characterAction =“Attack";
- switch(characterAction)
- {
- case"Heal":
- Debug.Log("Potion sent,");
- break;
- case"Attack":
- Debug.Log("To arms!");
- break;
- default:
- Debug.Log("Shields up.");
- break;
- }
- }
- }
刚刚发生了什么
由于 characterAction 被设置为"Attack”,因此 switch 语句执行第二个 case 子句并打印相应的调试日志,更改 characterAction 为"Hea"或其他尚未定义的动作,以看到第一个 case 子句和 default 子句的执行情况。
3.fall-through
switch 语句能够在多种情况下执行相同的操作,类似于我们在一条f语句中指定多个条件。这种情况被称为fall-through。如果 case 子句没有代码块和 break 关键字那么控制流将直接进入下一个 case 子句。
实践一掷骰子
下面使用switch语和fall-through来模拟掷子游戏.
(1)创建一个名为diceRoll 的iint 变量,赋值为7
(2)声明switch语句并使用diceRoll作为匹配表达式
(3)为掷散添加3种可能的情况。
(4)当匹配表达式为15和20时,有相应的调试日志和break 关键字,为7时则直接进入掷散结果为15的case 子句。
(5)保存脚本并在Unity单击Play 按钮,
- void Start()
- {
- int diceRoll=7;
- switch(diceRoll)
- {
- case 7:
- case 15:
- Debug.Log("Mediocre damage,not bad");
- break;
- case 20:
- Debug.Log("Critical hit,the creature goes down!");
- break;
- default:
- Debug.Log("you comletely missed and fell on your face.");
- break;
- }
- }
刚刚发生了什么
当把diceRoll设置为7时,switch 语句会匹配第一个case子句,但由于其中没有代码块和 break 关键字,因而进入并执行下一个case子句中的代码块,如图如果将 diceRoll 更改为15或20,那么控制台将显示对应的调试日志,对于其他任何值,则直接进入switch 语句末尾的 default 子句。
到目前为止,我们只需要使用变量来存储单个值,但很多情况下我们需要存储一组值。此时,集合就派上用场了。C#中的集合类型包括数组(Array)、字典(Dictionary)和列表(List),它们各有优缺点。
数组是 C#提供的最基本的集合。可以将数组视为一组值的容器,这些值在编程术语中被称为元素,每个值都可以单独访问或修改。
数组是 C#中最不灵活的集合类型,主要是因为元素在创建后不能添加或删除。不过,当需要存储不太可能改变的信息时,数组特别有用
1.基本语法
声明数组类似于声明我们之前使用的其他变量类型,但也有一些差别。
以蓝图的形式看起来语法如下:
elementType[] name = new elementType[numberOfElementsl;
举个例子,假设我们需要在游戏中存储得分的前三名:
int[] topPlayerScores = new int[3];
topPlayerScores 是存储了3个整型元素的整型数组,由于我们没有添加任何初始值,因此topPlayerScores 数组中的3个元素默认都为0
C#为此提供了两种有效的语法:普通语法和速记语法
- // Longhand initializer
- int[] topPlayerScores = new int[] (713,549,984);
- // Shortcut initializer
- int[] topPlayerScores ={ 713,549,984 };
2.索引和下标
每个数组元素都按分配的顺序进行存储,这称为索引。数组元素从0开始索引,这意味着数组元素的顺序从0而不是1开始。可以将数组元素的索引看作引用或位置在 topPlayerScores 数组中,第1个整数452的索引为0,第2个整数713 的索引为1,第3个整数984的索引为2。
使用下标运算符还可以直接修改数组中的值,就像其他变量一样,甚至可以将自己作为表达式来传递:
- topPlayerScores[1] = 1001;
- Debug.Log(topPlayerScores[1]);
topPlayerScores 数组中的值现在是452、1001和984。
3. 范围异常
在创建数组时,元素的数量是固定的,这意味着我们不能访问不存在的元素。对于topPlayerScores数组来说,数组长度为3,因此有效索引的范围是0-2。任何大于或等于 3的索引都将超出数组的索引范围,并在控制台中生成名为IndexOutOfRangeException 的调试日志。
列表与数组密切相关,列表能将相同类型的多个值收集到单个变量中。但是,在添加、删除和更新元素时,列表要灵活得多,这使列表在大多数情况下成为首选。
1.基本语法
列表类型的变量需要具备以下条件:
以蓝图的形式看起来语法如下:
List name = new List();
注意:列表的长度总是可以修改的。因此,不需要在创建时就指定列表最终存储的元素数量
与数组一样,可以在变量的声明中通过在花括号内添加元素值来初始化列表:
List name = new List() {valuel, value2 };
元素从索引0开始按照添加的顺序进行存储,并且可以使用下标运算符进行访问。
实践一派对成员
下面通过在虚构的角色扮演游戏中创建派对成员列表来进行热身练习。
(1)创建一个名为 questPartyMembers 的字符串类型的列表,并使用三个角色名称进行初始化。
(2)添加一条调试日志,使用 Count 方法打印出列表中的派对成员数量
(3)保存脚本并在Unity 中单击 Play 按钮,
刚刚发生了什么
我们初始化了一个名为questPartyMembers 的列表,其中存储了三个字符串,然后使用Count方法打印了列表中的元素数量
2.常用方法
只要索引在列表范围内,就可以像数组那样使用下标运算符和索引访问并修改列表元素。但是,List 类提供了多个用来扩展列表功能的方法,从而添加、插入和删除元素.
继续使用questPartyMembers列表。下面将一个新的成员添加到这个列表中:
questPartyMembers.Add("I am 4");
Add 方法可以将新元素追加到列表的末尾,这将使 questPartyMembers 列表的长度变为4,并使元素的顺序变为如下形式:
"I am 1","I am 2","I am 3","I am 4"
要在列表中的特定位置添加元素,可以向 Insert 方法传递想要添加的索引和值:
questPartyMembers.Insert(1,"I am 5");
删除元素非常简单,只需要提供索引或字面值,List 类即可完成工作:
- questPartyMembers.RemoveAt(0);
- questPartyMembers.Remove("I am 1");
与数组和列表相比,字典在每个元素中存储值对而不是单个值。字典中的元素又称为键值对:键充当对应的值的索引。与数组和列表不同,字典是无序的。但是,典可以在创建后以各种配置进行排序。
1.基本语法
声明字典和声明列表几乎一样,但是许多细节(比如键和值的类型)需要在尖括号中进行指定:
Dictionary name = new Dictionary();
要使用键值对初始化字典,请执行以下操作:
- Dictionary
name = new Dictionary() - {
- {keyl,valuel},
- {key2,valve2}
- };
选择键值时,每个键必须唯一,并且不能更改。如果需要更新键,请在变量声明中更改对应的值,或者删除后重新添加。
提示:
就像使用数组和列表一样,可以在一行中初始化字典,这在 Visual Studio中没有问题。但是,与前面的示例一样,在每一行中写出完整的键值对是一种良好习惯,这有利于提高代码的可读性。
实践一建立库存
下面创建字典来存储角色可能携带的物品
(I)声明一个名为itemInventory 的字典,键的类型为string,值的类型为int
(2)将创建的字典初始化为 new Dictionary (3)添加调试日志以打印itemInventory.Count 属性,以便查看存储的物品的数量 (4)保存脚本并在Unity 单击 Play 按钮 刚刚发生了什么 我们创建了一个名为itemInventory 的字典,并使用三个键值对进行初始化。我们指定键为字符串类型、对应的值为整型,然后打印 itemInventory 字典中元素的数量 2使用字典对 可以使用下标和类方法在字典中添加、删除和访问键值对。要检索元素的值,请使用带元素键的下标运算符。如以下代码所示,numberOfPotions将被赋值为5: 元素值可以使用同样的方法进行更改。与"Potion"关联的值现在是 10: 元素可以通过两种方式添加到字典中:使用Add 方法或下标运算符。Add 方法接收键和值,并使用它们创建新的键值对元素,只要类型与字典声明中的一致即可: 如果使用下标运算符为字典中不存在的键赋值,编译器将自动把它们添加为新的键值对。 如果想添加新的元素”Bandages”,可以使用以下代码 这就引出如下关于引用键值对的关键问题:最好在尝试访问一个元素之前确定这个元素是否存在,以免错误地添加新的键值对。将ContainsKey 方法与i语句配对是种简单的解决方案,因为ContainsKey 方法会根据键是否存在返回一个布尔值。在以下代码中,我们在确保"Aspirin"键存在后才更改对应的值: 最后,可以使用 Remove 方法从字典中删除键值对,只需要传入键作为参数即可: 我们已经通过下标运算符和集合类型中的方法访问了各个集合元素,但是当需要逐个元素地遍历整个集合时,该怎么办呢?在编程中,这被称为迭代(iteration)。C#提供了几种语句类型,让我们可以遍历集合元素。迭代语句就像方法一样,它们可以存储将要执行的代码块;但与方法不同的是,只要满足条件,它们就可以重复执行代码块。 for 循环最常见的应用场景是:在程序继续之前需要执行某个代码块一定的次数for 循环语句本身包含三个表达式,每个表达式在执行循环之前都要执行特定的任务因为for 循环能够追踪正在进行的迭代,所以十分适用于数组和列表。 for 循环语句的蓝图如下: 下面稍作分析: 内容看起来好像很多,所以下面我们看看之前创建的questPartyMembers 列表示例: 下面看看其中的 for 循环是如何工作的 实践一查找元素 当遍历questPartyMembers 时,我们可以看看是否能迭代到某个确定的元素,并为这种情况添加特殊的调试日志。 (1)在for 循环的调试志的下面添加if语句。 (2)在if语句中检查当前的questPartyMembers 元素是否与”Merli the Wise"匹配 (3)如果匹配,就添加一条调试日志.\ 控制台输出应该看起来几乎相同,只不过现在有一条额外的调试日志,并且这条日志仅在遍历到"I am 1"时才打印一次。具体而言,当i等于1,进入第二次循环,因为满足 if 语句中的条件,所以会打印两条调试日志而不是一条 foreach 循环能够获取集合中的每个元素并将其存储到局部变量中,从而可以在语句中访问它们。局部变量的类型必须与集合元素的类型匹配才能正常工作。foreach循环可以与数组和列表一起使用,但是它们对于字典来说尤为有用,因为它们不是基于数字索引的。 以蓝图的形式看起来语法如下: 下面继续使用questPartyMembers列表示例,对其中的每个派对成员进行点名: 下面稍作分析: 这比 for 循环要简单得多,但是在处理字典时,我们需要指出如下重要的区别: 如何将键值对作为局部变量处理. 遍历键值对 为了在局部变量中捕获键值对,需要使用 KeyValuePair 类型,同时分配键和值的类型以匹配字典中相应的类型。KeyValuePair 由于能够作为自身的类型,因此可与其他任何元素类型一样充当局部变量。 我们已经指定了命名为kvp 的 KeyValuePair 局部变量,这个变量的作用类似于 for循环的初始化表达式中的 i,用于将键和值的类型设置为字符串和整数以匹配itemlnventory while 循环与 if 语句的相似之处在于,只要单个表达式或条件为 true,它们就可以运行。值的比较结果和布尔变量可以用作 while 条件,你也可以使用逻辑非运算符修改条件。 while 循环的语法如下:当条件为 true 时,就会一直运行代码块 在 while 循环中,通常需要声明一个初始化变量,就像在 for 循环中一样,然后在代码块的末尾手动对这个初始变量进行增减。根据自身的情况,初始化表达式通常是循环条件的一部分。 实践一追踪玩家是否还活着 在游戏中,假设我们需要在玩家活着时执行代码,并在玩家死亡时输出一些提示信息。 (1)创建一个int类型的名为 playerLives 的初始化变量,赋值为3。 (2)声明一个 while 循环,条件是检查 playerLives 是否大于0(判断玩家是否还活着) (3)在 while 循环中,输出一些信息,从而让我们知道玩家还活着,然后使用--运算符将 playerLives 减1。 (4)在while循环的后面添加调试日志,从而在玩家死亡时打印一些内容, 刚刚发生了什么 playerLives从3 开始,因而 while 循环执行了3次。循环期间会打印"Stillalive!"”并从 playerLives 中减去生命值。当第4 次运行 while 循环时,因为 playerLives为0所以循环条件不满足,于是代码块被跳过,打印 while 循环后面的调试日志 在结束本章之前,我们需要理解关于迭代语句的一个极其重要的概念:无限循环顾名思义,无限循环指的就是循环无法停止,因而将在程序中一直运行。 在 for和 while 循环中,当迭代变量不增加或减少时,通常会发生无限循环。例如.在之前的 while 循环示例中,如果去掉 playerLives 代码行,Unity 就会崩溃,因光playerLives 始终为3,循环会永远执行下去。 另外,在 for 循环中,永远不会通过或计算为 false 的设置条件,也可能导致无限循环。在遍历键值对时,如果将 for 循环条件设置为 i>0 而不是iquestPartyMembers.Count,那么i永远不会小于0,for 循环会一直运行下去直到Unity崩溃。 至此,我们应该反思自己已经完成了多少工作以及利用这些知识可以创建什么。我们已经知道如何使用简单的 felse 语句和复杂的switch 语句在代码中做出决策。我们可以使用数组、列表或带键值对的字典来创建存储各种值的集合变量,甚至可以为每种集合类型选择正确的循环语句,同时谨慎地避免无限循环。如果感到任务过重,没有关系一-逻辑和顺序思考是锻炼编程能力的有效方式。下面将通过研究类、结构体和面向对象编程(通常称为 OOP)来结束对 C#编程基础知识的讨论。我们将把到目前为止所学的一切都放到对这些主题的讨论之中,从而为第一次真正地深入了解和控制Unity引擎中的对象做好准备。int mumberofPotions = itemInventory["Potion"];
itemInventory["Potion"] = 10;
itemInventory.Add("Throwing Knife",3);
itemInventory{“WBandage"} = 5;
迭代
for循环
foreach循环
while循环
超越无限
总结