杜克大学爱丽丝编程与动画入门笔记-全-

杜克大学爱丽丝编程与动画入门笔记(全)

001:第二周概述

在本节课中,我们将开始爱丽丝的冒险之旅。我们将学习如何运用动画技术和编程来讲述并制作精彩的故事,这个过程将充满乐趣。

🎬 场景设置与对象管理

首先,我们需要从设置场景开始。这涉及到两个核心问题:我们希望在场景中拥有哪些对象?以及这些对象在初始场景中应该被放置在彼此的什么相对位置上?

我们将学习如何向爱丽丝场景中添加对象,并掌握如何将这些对象精确地放置到动画故事开始时应处的位置。爱丽丝提供了多种不同的方式来定位对象,我们将探索其中几种,希望能找到一两种你喜欢的方法。

📖 故事板与动画指令

对我而言,有趣的部分在于讲述一个精彩的故事。我们将花一些时间介绍故事板,这是本课程中我们将用来规划故事的技术。之后,我们将有机会构建程序来真正讲述这个故事。

为了帮助动画化我们的故事,我们需要引入不同的爱丽丝基础指令。我们将学习对象的移动以及对象可以移动的方向。我们还将学习对象的旋转,即让对象进行转向和翻滚。

此外,我们会学习“说话”指令,它能让一个对象通过对话气泡说出内容,类似于我们在漫画中看到的效果。我们也将开始了解爱丽丝许多更专业的指令。

🔧 专业指令与动画控制

本周,我们将学习许多专业的移动和转向指令。例如,爱丽丝有一个特殊的“转向”指令,可以让一个对象转向以面对第二个对象。我们还会花时间探索对象部件的转向和翻滚,例如如何转动一个人的肘部或膝关节。

本周我们还将学习动画中一个非常基础的选择:动画指令应该一个接一个地按顺序执行,还是同时一起执行?爱丽丝提供了两种代码块来帮助我们控制动画指令的运行顺序:“顺序执行”块和“同时执行”块。我们将能够组合不同的“顺序执行”和“同时执行”块,来构建完美的动画故事。

💡 动画技巧与示例学习

在本课程中,我们会定期介绍各种动画技巧和技术,这些是我们多年来与学生和课堂合作中学到的经验。希望你会发现其中许多都很有用。

本周的大部分时间将用于观看我们为你预先编写的爱丽丝程序示例。我们这样做是因为不同的爱丽丝指令之间存在许多细微差别,理解这些差别非常重要。我们希望这些示例程序能帮助你观察并理解这些差异。

在本周以及后续的课程中,你将开始设置场景并构建你自己的爱丽丝程序。

让我们开始吧。

002:动画与计算机科学 🎬💻

在本节课中,我们将介绍计算机科学、编程和动画的基本概念。你将了解这些领域是什么,以及它们如何相互关联,为后续学习奠定基础。


什么是计算机科学? 🤔

上一节我们提到了本课程的目标,本节中我们来看看计算机科学的定义。根据计算机科学教师协会(CSTA)在2003年制定的K-12计算机科学模型课程,计算机科学被定义为:

计算机科学是对计算机和算法过程的研究,包括其原理、硬件和软件设计、应用以及对社会的影响。

实际上,计算机科学如今被用于解决几乎所有领域的问题。以下是几个具体例子:

  • 生物学:计算机科学家帮助生物学家进行全基因组测序,绘制每个人的DNA图谱。
  • 医学:他们能帮助医生评估个人患特定疾病的风险。
  • 电影制作:计算机科学家帮助电影制作人创作动画,例如让《怪物公司》及其续集《怪物大学》中动物的毛发看起来逼真,或让《海底总动员》及其续集《多莉去哪儿》中的水花效果显得真实。
  • 数据分析:计算机科学帮助我们分析大量数据,例如用于天气预报或预测优秀的棒球运动员。
  • 日常生活:它让生活更便捷,例如通过机器人清洁地板,或通过自动驾驶汽车载你上下班或上学。

什么是编程? ⌨️

了解了计算机科学的广泛应用后,我们来看看与计算机沟通的具体工具——编程。编程是计算机的语言。

在本课程中,我们使用英语与你交流,英语是我们沟通关于Alice知识的语言。然而,英语作为与计算机沟通的语言存在许多问题。

首先,它非常模糊不清。例如,考虑这个句子:
The cat chased the mouse until it fell.
我们无法确定追逐结束是因为猫摔倒了(老鼠逃走了),还是因为老鼠摔倒了(猫可能抓住了它)。如今的计算机无法很好地处理这种歧义。

我们需要使用一种更精确的语言与计算机交流,确保我们想让计算机做的事情没有歧义。英语作为与计算机沟通的语言还存在许多其他问题。

因此,有必要开发新的语言来与计算机交流,我们称这些语言为编程语言Alice 就是这样一种编程语言。


什么是动画? 🎞️

既然我们已经了解了驱动动画背后的科学(计算机科学)和工具(编程),现在让我们直接聚焦于动画本身。Techopedia 将动画定义为:

一种在屏幕上模拟物体运动的技术。

我更喜欢通过例子来理解动画。例如,动画包括:

  • 过去在电视上观看、现在在线观看的周六早间卡通片。
  • 最新的皮克斯/迪士尼电影。
  • 我们的学生和孩子们总是在玩的酷炫视频游戏。


从消费者到创造者 🚀

美国作家兼媒体理论家道格拉斯·拉什科夫喜欢将世界分为两类人:技术的消费者技术的生产者。我们希望你能通过本课程从第一类人转变为第二类人。

图灵奖(计算机科学界的诺贝尔奖)得主、才华横溢的艾伦·凯说过:“预测未来最好的方式就是去发明它。

更谦虚地说,最好玩的电子游戏是你自己创造的,最好看的动画视频是你自己制作的。 欢迎来到激动人心的Alice世界!😊


总结 📝

本节课我们一起学习了计算机科学、编程和动画的核心概念。我们了解到计算机科学是研究计算机和算法的广泛领域,编程是与计算机沟通的精确语言,而动画则是通过技术让物体在屏幕上运动起来。本课程的目标是帮助你使用Alice编程语言,从一个技术的消费者转变为能够创造自己动画和游戏的生产者。

003:Alice软件概述 🎬

在本节课中,我们将学习如何创建一个Alice项目。创建项目主要分为两个部分:首先是设置初始场景,即决定动画中包含哪些3D模型以及它们的起始位置;其次是编写程序来讲述故事,让动画真正动起来。接下来的几讲将重点介绍第一部分——设置初始场景。

设置初始场景 🖼️

上一节我们介绍了创建Alice项目的两个主要部分。本节中,我们来看看如何具体设置初始场景。在Alice中设置场景,首先需要决定你的项目将包含哪些3D模型。

添加与定位模型

将模型添加到项目后,通常需要将它们放置在正确的位置。以下是两种主要的定位方法:

  • 你可以使用鼠标来移动和旋转对象,进行直观的调整。
  • 或者,你也可以使用“单步过程”功能来精确地定位对象。

总结 📝

本节课中我们一起学习了Alice项目创建的基础,特别是设置初始场景的步骤。我们了解到,这包括选择3D模型以及使用鼠标或程序指令来定位它们。掌握场景设置是制作动画故事的第一步。

004:向场景添加对象 🎬

在本节课中,我们将探索爱丽丝(Alice)编程环境,并学习如何向场景中添加对象。这是创建动画世界的第一步。

概述

当您首次启动爱丽丝时,会看到“选择项目”窗口。这个窗口包含五个选项卡,用于管理不同类型的项目。

项目窗口详解

以下是项目窗口中各个选项卡的说明:

  • 空白模板:此选项卡默认被选中,它提供空白的场景模板,是我们创建新项目的基础。
  • 入门项目:此选项卡包含卡内基梅隆大学爱丽丝开发团队预先构建的一些项目。这些项目在空白模板中已经添加了许多模型。
  • 我的项目:此选项卡允许您打开之前构建并保存在特定文件夹中的爱丽丝项目。在本课程中,我们将交替使用“爱丽丝世界”、“爱丽丝虚拟世界”和“爱丽丝项目”这些术语,它们都指代您将构建和编写的爱丽丝程序。虽然本课程中可能不会频繁使用此选项卡,但如果您选择将项目保存在默认位置,它会非常有用。
  • 最近项目:在课程后期,当您开始构建和修改爱丽丝项目时,您会经常使用此选项卡来快速打开最近的文件。
  • 打开项目:此选项卡允许您浏览计算机的文件系统来查找爱丽丝项目文件。如果您像我一样经常忘记项目保存的位置,在课程后期可能会更频繁地使用此选项卡。

创建新场景

现在,让我们确保左侧的“空白模板”选项卡已被选中。向下滚动并选择“雪景”模板,然后点击窗口右下角的“确定”按钮。

爱丽丝会创建一个包含雪景的新项目。需要了解的是,爱丽丝有两种主要模式:

  • 编辑代码模式:在此模式下,您将添加所有指令来创建动画。
  • 场景设置模式:在此模式下,您将向项目中添加对象。

由于本节的重点是向项目添加对象,请点击左上角窗口右下角的按钮,进入场景设置模式。爱丽丝默认从编辑代码模式启动,因此每次开始新项目时都需要点击此按钮。

添加3D模型

进入场景设置模式后,我们会看到一个更大的雪景窗口。现在,是时候向我们的爱丽丝项目添加一些3D模型了。我们将添加一个雪人和一个雪女。爱丽丝内置了数千个预建的3D模型,在本课程的大部分时间里,我们都会使用这些模型。

屏幕下方的窗口包含几个选项卡。我们鼓励您在探索爱丽丝时,尝试使用“类层次结构”、“主题”和“分组”选项卡。例如,如果您知道要添加一只猫,由于猫都有四条腿,您可以选择“类层次结构”选项卡,然后选择“四足动物”类。

现在,我不确定雪人模型是否有两条腿(这将使其属于“双足动物”类)还是没有腿(可能使其属于“道具”类)。因此,我们可以点击“搜索图库”选项卡。在筛选行中,输入“snow”。这将返回所有名称中包含“雪”字或爱丽丝认为与雪相关的模型。

添加对象的方法

向场景添加模型有两种主要方法。让我们看看第一种方法:点击雪人图标,会弹出一个窗口。我们可以为雪人命名,但这次我们不做任何输入,直接点击窗口右下角的“确定”按钮。瞧!一个雪人出现在屏幕上。我们将在后续课程中学习如何在场景中移动雪人。

现在,我们想添加一个雪女,并将其放置在雪人的右侧(或摄像机的左侧)。让我们使用第二种方法向场景添加对象:我们可以直接拖动雪女模型。在将雪女拖到雪地上时,我们会注意到雪地上出现一个方框,显示如果我们松开鼠标,雪女将被放置的位置。这个方框实际上称为“边界框”,它与雪女模型的高度、宽度和深度相同,因此您可以预览雪女如何融入场景。我们可以将她稍微靠近雪人一些,然后松开鼠标。

和雪人一样,我们也可以为雪女命名。让我们将雪女命名为“sue”(首字母小写)。在爱丽丝中,按照命名约定,对象名称以小写字母开头。点击“确定”,Sue 就出现在雪人的右侧了。

重命名与保存

假设我们想更改雪人的名字。您可以右键点击雪人,选择“重命名”选项。我们可以将雪人重命名为“Frosty”,然后点击“确定”。

最后一步是保存您的工作。在左上角的“文件”菜单中,向下滚动并点击“保存”按钮。我们将这个文件命名为 PracticeAddingObjectsToAScene。默认的“我的项目”文件夹是保存工作的合适位置。按照惯例,我们倾向于以大写字母开头命名爱丽丝项目,后续单词也以大写字母开头。点击“保存”按钮以保存我们的工作。

总结

本节课中,我们一起学习了爱丽丝项目窗口的构成,创建了一个雪景新项目,并掌握了向场景中添加和命名3D模型的两种方法。您现在已经成功构建并保存了您的第一个爱丽丝项目。

005:控制键与单次执行(Mac版)🎮

在本节课中,我们将学习如何在场景设置阶段移动场景中的对象。您将在整个课程中获得更多设置场景的经验。本节将演示几种不同的操作对象的方法。您无需精通所有方法,只需熟练掌握其中一种即可。

选择对象

在移动对象之前,必须先选中它。有三种方法可以选择对象:

  • 在屏幕左上角的对象列表中点击对象。
  • 在场景中用鼠标点击对象。
  • 在爱丽丝窗口右侧的下拉列表中选择对象。

让我们逐一查看。首先,点击对象列表中的 Sue。选中后,场景中的 Sue 周围会出现一个黄色圆环,同时右侧面板中也会显示 Sue 被选中。

接下来,练习其他方法。点击场景中央的雪人 Frosty。此时,黄色圆环会从 Sue 转移到 Frosty 周围,左上角的对象列表和右侧面板中 Frosty 均会高亮显示。

最后,尝试从右侧面板的下拉列表中选择 Sue。滚动列表并选择 Sue,您会看到黄色圆环重新出现在 Sue 周围,并且左侧对象列表中的 Sue 也被选中。

移动对象

现在,让我们尝试将 Sue 移动到 Frosty 身后。我们将使用两种主要方法:鼠标拖动和指令控制。

使用鼠标移动

首先,确保右侧面板的手柄样式设置为“默认”或“移动”。个人而言,我们更倾向于使用“默认”样式,以便看到对象周围的黄色圆环。

选中 Sue 后,您可以直接用鼠标将其拖拽到 Frosty 身后的位置。

在 Mac 上,有三种方法可以撤销操作:

  1. 点击菜单栏的 编辑 -> 撤销
  2. 按住 Command 键(空格键左右两侧均有),然后按 Z 键。
  3. 点击窗口右上角的 撤销 按钮。

使用指令移动

您也可以通过指令来移动 Sue。在右侧面板中 Sue 的下方,有一个名为 单次执行 的下拉按钮。点击此按钮,选择 过程,然后选择 移动到,并选择目标 Frosty。这将使 Sue 移动到与 Frosty 完全相同的位置。

接着,再次点击 单次执行 -> 过程 -> 移动,我们可以让 Sue 向后移动 2 个单位。现在,Sue 就站在 Frosty 正后方 2 个单位的位置。

通常,使用鼠标拖拽移动角色更快,而使用单次执行过程则更精确。

旋转与升降对象

上一节我们介绍了如何移动对象,本节中我们来看看如何改变对象的方向和高度。

旋转对象

接下来,我们将 FrostySue 转身,使他们的背部朝向摄像机。首先点击选中 Frosty。如果直接点击对象周围的黄色圆环并拖动,可以使 Frosty 旋转。如图所示,我们点击并拖动圆环,让 Frosty 转过来。

因为 SueFrosty 正后方,我们通过左侧对象树点击选中 Sue。然后使用单次执行指令让她转身:点击 单次执行 -> 过程 -> 转向 -> 左转 -> 0.5 圈。

升降对象

最后,让我们将 Frosty 升到空中。再次选中 Frosty。此时,按住 Shift 键并用鼠标拖动,就可以将 Frosty 向上提升,就像在飞行一样。

或者,我们也可以使用 单次执行 按钮,选择 过程 -> 移动 -> 向上 的指令来达到同样效果。

总结与提示

本节课中我们一起学习了在爱丽丝中移动、旋转和升降对象的几种核心方法。虽然还有其他操作方式,但我们介绍的这些是最常用的。

如果发现某种方法(尤其是在 Mac 上)无法移动对象,请尝试保存项目并重启爱丽丝软件。完成操作后请务必保存您的工作。如果您对某些移动效果不满意,可以使用之前提到的任意一种撤销方法,来撤销对 FrostySue 的所有操作。

006:控制键与单次执行(PC版)🎮

在本节课中,我们将学习如何在场景设置阶段移动场景中的对象。在整个课程中,你将获得大量设置场景的实践经验。本节将演示几种不同的方法来操作对象,作为场景设置的一部分。你不需要精通所有方法,只需熟练掌握其中一种即可。

我们将从雪人场景开始。点击“场景”。在场景中移动和旋转对象主要有两种方式:使用鼠标移动对象,或使用爱丽丝软件内置的一些指令。

在移动对象之前,需要先选中它。有三种方法可以选中对象:

  • 在屏幕左上角的对象列表中点击对象。
  • 用鼠标点击场景中的对象。
  • 在爱丽丝窗口右侧的下拉列表中选择对象。

让我们逐一查看这三种方法。首先,我们在屏幕左上角的对象树中点击“Sue”(技术上显示为 this.Sue)。这时会发生两件重要的事情:第一,在场景中,Sue周围会出现一个黄色圆环;第二,在屏幕右侧的框架中,this.Sue 被选中。

为了练习其他方法,我们先点击雪人。除了黄色圆环从Sue周围移动到Frosty周围,我们还会注意到左上角对象树中的 this.Frosty 被高亮显示,并且右侧框架中的 this.Frosty 也被选中。

现在,让我们从右侧框架的下拉列表中选择 this.Sue。注意黄色圆环如何重新出现在Sue周围,并且左侧对象树中的 this.Sue 现在也被选中了。

接下来,我们尝试将Sue移动到Frosty身后的位置。我们将使用的第一种方法是鼠标操作。首先,确保屏幕右侧的“手柄样式”设置为“默认”。“移动”手柄样式同样有效。就个人而言,我喜欢看到选中对象周围的黄色圆环,所以我们通常使用默认手柄样式。

选中Sue后,我们可以用鼠标将她拖拽到Frosty身后的位置。如果你不满意放置的位置,可以撤销这次移动。在Windows系统的PC上,有三种方法可以撤销移动:

  • 第一种,选择菜单栏的“编辑”->“撤销”。
  • 第二种,只需按住 Ctrl 键并点击 Z 键。注意,Ctrl 键位于空格键的左侧和右侧,你只需按住其中一个即可。
  • 第三种,点击窗口右上角的“撤销”按钮。

你也可以通过指令来移动Sue。实际上,在右侧框架中 this.Sue 的正下方,有一个名为“单次执行”的下拉按钮。左键点击此按钮,我们可以选择“过程”,然后选择任意指令。例如,我们选择 move to,然后选择 this.Frosty。这会使Sue移动到与Frosty完全相同的位置。

让我们尝试另一个指令:点击“单次执行”,选择“过程”,然后选择 move,接着指定方向为 backwards,最后选择距离为 2。现在,Sue就站在Frosty正后方两单位的位置了。一般来说,用鼠标拖拽移动角色更快,而使用“单次执行”过程则更精确。

现在,让我们把Frosty和Sue都转过来,使他们背对摄像机。首先点击Frosty选中他,然后按住 Ctrl 键,接着点击并拖动,Frosty就会转身。因为Sue直接在Frosty身后,所以我们在对象树中点击Sue选中她。现在,我们使用“单次执行”过程让Sue turn left 半圈。

最后,让我们把Frosty升到空中。我们再次点击Frosty选中他,使他周围出现黄色圆环。现在,我们按住 Shift 键,同时用鼠标左键点击并拖动,就可以将Frosty升到空中,就像他在飞一样,或者也可以将他降到地面。或者,我们也可以使用“单次执行”按钮,选择相应的过程让Frosty向 updown 方向移动。

虽然爱丽丝中还有其他移动对象的方法,但我们发现这些是最有用的。如果其中某种方法无法移动对象或停止工作,请保存你的项目并重启爱丽丝。完成操作后请务必保存你的工作。如果你不喜欢某些移动操作,只需使用 Ctrl + Z 即可撤销你对Frosty和Sue所做的任何或全部移动。

在本节课中,我们一起学习了在爱丽丝中移动对象的几种核心方法:通过鼠标拖拽、使用“单次执行”指令,以及如何利用 CtrlShift 键配合鼠标进行旋转和升降操作。同时,我们也掌握了撤销操作的多种方式。这些是进行场景初始布局的基础技能。

007:构建人物角色 👤

在本节课中,我们将学习如何在Alice 3中利用《模拟人生2》的角色构建器来创建和自定义属于你自己的动画角色。

Alice 3与电子艺界合作,引入了来自电子游戏《模拟人生2》的角色,其中包括一个角色构建器,让你可以创建自己的角色。你可以选择从幼儿到老人的各种人物类型,并为你的角色选择许多其他特征,例如服装、肤色和发型。


启动角色构建器 🚀

上一节我们介绍了角色构建器的来源,本节中我们来看看如何启动它。

首先,进入“设置场景”界面,选择“Biped”类。前五个类都代表《模拟人生2》角色构建器,点击其中任意一个即可。点击哪一个并不重要,因为它们都会启动相同的角色构建器。


选择年龄与性别 👶👴

启动构建器后,你可以从顶部的选项中选择人物的年龄范围:幼儿、儿童、青少年、成人或老人。点击不同选项时,你会看到服装风格随之改变。

以下是不同年龄段的服装特点:

  • 幼儿:提供适合幼儿的服装。
  • 儿童:提供有趣、活泼的儿童服装。
  • 青少年:既有有趣的服装,也有一些成熟的服装。
  • 成人老人:拥有精致成熟的服装和制服。

你还可以通过点击“随机生成”按钮来创建一个随机人物。每次点击都会得到一个不同的人物,这很有趣。

我将选择创建一个青少年,所以点击“Teen”。接下来,你可以选择性别:女性或男性。我选择女性。


自定义外观特征 🎨

确定了基本类型后,我们可以深入定制角色的外观。

选择肤色
你可以从深色到浅色之间选择肤色。也可以点击“自定义颜色”选项,通过选择RGB值来设定特定颜色,例如蓝色,从而得到一个偏蓝的肤色。我将选择一个中等肤色。

选择服装
服装部分提供了多种选择:

  • 套装:提供全身搭配好的服装。
  • 连衣裙:专为女性角色设计。
  • 分体式套装:已经协调搭配好的上衣和下装。
  • 特定主题服装:包含大量特定款式的服装。

点击任何服装可以预览角色穿上的效果。你还可以自由搭配自己的上衣和下装,例如选择一件蓝色衬衫和一条绿色裤子,创造出独特的组合。

在窗口底部查看服装时,你还可以调整角色的腰围。滑块最右端代表宽腰围,向左移动则腰围会变细。我将选择中间位置。

选择发型与发色
点击“Hair”选项卡,你可以选择想要的发型和发色。角色有多种发色选择,例如红色、蓝色、紫色等。我喜欢紫色头发。

调整面部与眼睛
点击“Face”选项卡,可以尝试不同的脸型。滚动到底部,你甚至会发现“外星人”脸型选项。我选择其中一个脸型。在底部,你还可以选择眼睛的颜色,例如绿色、棕色或蓝色。我喜欢蓝色眼睛。


保存并放置角色 💾

在保存角色之前,请确保你对其服装、发型和肤色等所有特征都满意。一旦保存并将其放入世界,你将无法再更改这些特征。

如果满意,点击“OK”保存。默认的角色名是“teen person”,这不太有趣。我将她的名字改为“Sandra”,然后再次点击“OK”将其放入世界。

角色需要几秒钟才会出现,因为Alice需要根据你选择的特征来构建这个人物。看,她出现了,名字是Sandra。


总结 📝

本节课中我们一起学习了如何在Alice 3中使用角色构建器。我们逐步操作了选择年龄与性别、自定义肤色、服装、发型、面部特征,并最终保存和放置自定义角色的全过程。现在,你可以尽情为你的Alice世界构建独特的人物了。

008:杰西卡·阿布罗姆斯访谈

在本节课中,我们将通过杰西卡·阿布罗姆斯的经历,了解计算机科学学习路径、职业发展以及她对编程初学者的建议。杰西卡是杜克大学校友,拥有计算机科学和文学双学位,并在卡内基梅隆大学获得人机交互硕士学位。她曾参与爱丽丝项目,并在皮克斯、迪士尼工作,如今创立了自己的公司。

教育与职业起点

上一节我们介绍了课程背景,本节中我们来看看杰西卡的教育和职业起点。她分享了选择计算机科学专业的原因以及早期学习经历。

我在杜克大学就读,获得了计算机科学和文学的文学学士学位。我实际上在申请大学时就说要主修计算机科学。这源于我高中时的一个小执念。我高中就读于一所女子学校,一直喜欢物理和数学,也热爱计算机。我当时很想上一门编程课。但不幸的是,学校当时没有提供任何编程课程。而附近的男子学校有编程课。学校建议我可以开车去那边上课。我对此感到非常生气。所以在申请大学时,我就想,我要主修计算机科学,这就是我想学的东西。当我开始学习计算机科学时,坐在实验室里学习调试技能、学习如何解决问题,这让我非常兴奋。你知道,你接到一个任务,它就像一个需要解决的复杂问题,你必须想出不同的方法和路径来完成它。当你最终完成任务时,真的非常有成就感。我觉得这些练习为你未来学习所有编程技能做好了准备。

编程中的挑战与解决之道

在编程学习中遇到困难是常见的情况。杰西卡回顾了她面对挑战时的经历和应对策略。

你在写程序时是否曾遇到过困难?当然,你会被卡住,想扯自己的头发,感觉非常沮丧。我觉得在大学上课的一个好处是,你有助教,实验室里也有可以求助的人,你们可以一起克服困难。我认为最好的资源之一就是休息一下,和别人讨论,想想“也许如果我试试这个方法,能找到不同的路径”。所以,当你沿着一条路走到死胡同时,你会想,好吧,我需要回溯,然后重新开始一条新路。我认为这是编程的一部分,任何编程的人在某些时候都肯定会遇到困难。

深造与人机交互

完成本科学业后,杰西卡选择了继续深造。她解释了选择卡内基梅隆大学人机交互专业的原因。

当我从杜克大学毕业时,我拥有计算机科学和文学双学位,我想规划未来。我喜欢读的所有关于未来的书。我也在寻找各种方法,来开发很棒的新程序或做这些事情。但我当时找到的工作大多是咨询类或商业类,非常偏向商业应用。我对此有些兴趣,但从未真正感到兴奋。我甚至不记得是怎么发现CMU的这个硕士项目的。我知道我想去娱乐技术中心,并给他们写了信。但那个硕士学位当时还没准备好。所以他们提供了人机交互的学位。我觉得,嗯,这听起来很有趣。我一直喜欢编程课中涉及界面的部分,随着项目规模扩大和团队合作增多,我总是想弄清楚如何让东西易于使用,如何构建界面,以及产品实际的外观和交互,而不仅仅是后端。所以我决定申请那个硕士项目继续深造,而不是接受我可能得到但并不真正吸引我的随机咨询工作。

与兰迪·波许和爱丽丝的相遇

在CMU,杰西卡有幸参与了兰迪·波许教授的课程,兰迪正是爱丽丝的发明者。她描述了这段激动人心的经历。

兰迪·波许在CMU教授“构建虚拟世界”课程,你上了那门课,他是爱丽丝的发明者,那是什么样的体验?真的非常激动人心。他的课是大家争相想上的课,它甚至不是我硕士课程的一部分,因为我的硕士专业是人机交互。但我们几个人成功进入了那个班级。当时班上大约有200或100名学生。每个人都想进去,名额非常有限。为了能进去,我必须学习爱丽丝。我以前从未用过它,但我还得想清楚自己想承担什么角色。你可以选择成为建模师、程序员或“无形者”(更像是项目经理)。但获得这些角色很难。所以我决定成为一名建模师,尽管我以前从未使用过3D应用程序包。因此我不得不学习很多教程,第一次打开3D Studio Max。你知道,你试图在电脑的二维世界里构建一些东西,这非常复杂。但我觉得我坚持了下来,通过在3D程序中获得一些实践经验学到了很多技能。

在皮克斯的黄金时代

从CMU毕业后,杰西卡进入了皮克斯动画工作室工作,她将其称为皮克斯的“黄金时代”。

你后来还去了皮克斯工作。你参与了哪些电影?我从《怪兽电力公司》开始,然后参与了《海底总动员》、《超人总动员》、《美食总动员》,最后是《飞屋环游记》。我称之为皮克斯的黄金时代,因为那都是原创电影,而且今天还在制作续集。甚至在迪士尼,我也有幸参与了配合电影《海底总动员2:多莉去哪儿》的应用程序开发,所以这就像一个完整的循环。

对初学者的建议

基于丰富的学习和职业经验,杰西卡为编程初学者提供了宝贵的建议。

我记得在电影《美食总动员》中,主厨古斯特的格言是“人人都能烹饪”。那么你认为人人都能编程吗?当然。我认为任何有动力、有内在驱动力的人都可以学会编程,因为我觉得这就像解决现实世界中的任何问题一样。即使像今天,我做了很多项目管理,作为一名项目经理,调试也是一项必备技能。比如,好吧,这边的食物不够,我们如何用有限的资源获取原料来制作这个蛋糕?你必须制定一个新计划,找出解决不同问题的新方法。

对于正在学习我们课程的人,你有什么建议吗?这些都是初学者。是的,我认为这是一项艰苦的工作,所以即使遇到困难也要坚持下去,就像多莉说的“一直游下去”,努力克服所有难点,这样你才能完成课程,度过困难的部分,最终收获回报。

总结

本节课中,我们一起学习了杰西卡·阿布罗姆斯从杜克大学到卡内基梅隆大学,再到皮克斯和自主创业的历程。她强调了内在驱动力、解决问题的技巧以及坚持的重要性。她的经历证明,编程是一项可以通过学习和实践掌握的技能,并且能够开启通往动画、游戏开发等多个创造性领域的大门。对于初学者,她的核心建议是:保持动力,勇于面对并克服困难,最终必将收获成功的喜悦。

009:简单指令 🎬

在本节课中,我们将学习如何在爱丽丝(Alice)编程环境中编写代码,让场景中的对象执行简单的动画指令。我们将从放置对象过渡到编写指令,并了解几种基本的数据类型。

上一节我们介绍了如何在初始空白场景中放置和移动对象来设置起始场景。本节中,我们来看看如何开始编写代码来创建动画。

你已经学会了如何进入场景设置界面,在爱丽丝项目中放置对象。这里我放置了一只金猴和一只松鼠。设置完成后,点击“编辑代码”即可返回代码编辑器。

在代码编辑器视图中,右侧有三个主要部分:代码编辑器,你将在这里放置指令来创建动画。“我的第一个方法”标签页显示在此处,放置在该方法中的代码将会执行。“执行”是一个专业术语,意思是“运行”。当你点击运行按钮时,代码就会运行。

左上角是摄像机视图,显示你设置的场景图片。在图片中,你可以看到我在场景中放置了一只金猴和一只松鼠。这里的图片比场景编辑器视图中的要小,因为此视图的重点是编写代码。

图片下方有一个水平栏,上面有松鼠的图片并写着“this.squirrel”。这也被称为对象下拉列表。它显示当前选中的对象。你还会注意到松鼠图片周围有一个粗粗的金色圆环,这也意味着松鼠是当前选中的对象。

左下角可以看到“过程”标签页,其中列出了当前选中对象(松鼠)知道如何执行的一系列指令。你可以点击对象下拉列表中“this.squirrel”右侧的小三角形,选择另一个动物,或选择名为“this”的场景,或选择摄像机。

我将当前对象更改为金猴。让我们更仔细地看看金猴的过程或指令。你可以看到金猴知道如何做很多事情。它知道如何“说”些什么,这看起来像一个卡通气泡。它知道如何“移动”,知道如何“转身”以及许多其他事情。我们将更详细地探讨其中一些指令。

我们希望金猴说些什么,比如“欢迎来到我的世界”。

要将代码放入代码编辑器窗口,你需要点击指令并将其拖拽到“我的第一个方法”代码编辑器中。如果我们点击“说”指令并将其拖入,它会询问我们想让金猴说什么?我们可以选择“你好”,或者我们可以选择“自定义文本字符串”,然后输入我们想要的短语。

以下是我们输入“欢迎来到我的世界”后,“说”指令的样子:

this.goldenMonkey.say("欢迎来到我的世界")

当世界运行时,金猴将在一个卡通气泡中准确显示我们告诉它说的话:“欢迎来到我的世界”。

你可以选择的另一个指令是“移动”。“移动”指令需要两条信息:你希望金猴朝哪个方向移动?角色可以朝六个方向移动:上、下、左、右、前、后。选择方向后,你必须确定希望角色移动多远。这个量应该是一个小数。

我们这里能用整数吗?数学家使用术语“整数”而不是“整数”。那样不会给我们太多灵活性。那样金猴就只能以整数增量移动。如果你需要它只移动一点点,比如一半或0.25呢?对于移动多远来说,小数更有意义。

以下是“移动”指令的两个例子:

this.goldenMonkey.move(forward, 1)
this.goldenMonkey.move(up, 2.5)

在第一个例子中,金猴向前移动1个单位。在第二个例子中,金猴向上移动2.5个单位。当我们有多个指令时,它们按顺序执行。这里金猴先向前移动,然后金猴向上移动。

现在让我们看看“转身”指令。当你拖入“转身”指令时,它会询问你转身的方向和转多远。对象可以朝四个不同的方向转身:左、右、前、后。它不能向上或向下转。你还需要被询问转多远或旋转多少。

转多远的量与“圈”相关,并且必须是一个小数。在这里,整数值没有意义。1意味着转一整圈。如果值是整数类型,对象将只能转一整圈,永远无法停在不到一圈的位置。这就是为什么该值的类型需要是小数。0.5意味着转半圈,0.25意味着转四分之一圈。

以下是“转身”指令的两个例子:

this.squirrel.turn(right, 0.25)
this.goldenMonkey.turn(left, 0.5)

我们让松鼠向右转四分之一圈,然后让金猴向左转0.5圈,也就是转半圈。

在学习过程的同时,你也在学习几种不同的数据类型,因为过程需要这些数据类型。

以下是涉及的主要数据类型:

  • 字符串数据类型:“说”指令使用字符串数据类型,也称为文本字符串。可以将其视为由双引号包围的字符序列。这些字符可以包括空格、数字和特殊字符,例如美元符号和英镑符号。例如,"USS Enterprise NCC-1701" 是一个字符串,代表星际迷航飞船的名称,这个字符串包括字母、数字和一个破折号。
  • 小数:我们在“转身”和“移动”命令中都看到了小数类型,我们必须告诉对象移动多远或转多远。
  • 整数:我们也讨论了整数,尽管我们还没有在任何指令中使用它们。我们稍后会用到。当你想在爱丽丝中使用数字时,你需要考虑使用整数还是小数是否有意义。如果使用一半、四分之一或小于一的值有用,那么你应该使用小数。
  • 方向数据类型:我们在“移动”和“转身”指令中都看到了它。对于“移动”指令,对象可以朝六个方向移动:上、下、左、右、前、后。对于“转身”指令,对象可以朝四个方向转:左、右、前、后。
  • 对象数据类型:另一个数据类型是对象,比如松鼠或金猴。每条指令都有一个与之关联的对象来执行该动作。你已经通过将对象放入爱丽丝世界来使用它们了。现在,你知道了如何通过将指令拖拽到代码编辑器中来让对象执行动作。

爱丽丝还有许多其他数据类型,你将在以后的学习中了解到。

本节课中我们一起学习了如何在爱丽丝中编写简单的动画指令,包括“说”、“移动”和“转身”。我们还介绍了执行这些指令所需的关键数据类型:字符串、小数、整数、方向和对象。通过拖拽指令并填写必要的信息,你已经可以开始创建基本的动画序列了。

010:单次执行与代码编辑器

在本节课程中,我们将学习爱丽丝编程环境中“单次执行”与“代码编辑器”中指令的区别。我们将通过一个具体的项目演示,了解它们各自的使用时机和效果。

概述

我们将创建一个沙漠场景,并添加两只鸟:一只孔雀和一只鹰。通过使用“单次执行”功能来精确设置场景,然后使用“代码编辑器”来编写动画指令,让鹰飞入场景并完成动作。

单次执行:精确设置场景

单次执行指令在场景设置时立即生效,用于调整角色的位置或属性。这有助于我们在动画开始前精确地布置场景。

以下是使用单次执行设置场景的步骤:

  1. 创建项目与添加角色:新建一个以沙漠为地面的爱丽丝项目。从场景库中找到“飞禽”类,选择并添加一只孔雀到场景中心。接着,在孔雀的正上方添加一只鹰。

  2. 调整初始位置:为了让两只鸟精确地相距两个单位,我们可以使用单次执行。首先选中鹰,使用单次执行中的“移动”指令,让它向左移动1个单位。然后选中孔雀,使用单次执行让它向右移动1个单位。这样,两只鸟的中心点就精确地相距两个单位了。

  3. 预设动画起始点:我们还可以用单次执行将物体移出屏幕,以便在动画中让它飞回来。首先让鹰展开翅膀(这是飞禽类的特殊指令),然后让它向右转四分之一圈,使其面向孔雀。接着,让它向上移动1个单位,再向后移动4个单位,使其完全移出屏幕。这样我们就知道,在动画中让它向前移动4个单位,它就能回到移出屏幕前的位置。

过渡到代码编辑器

上一节我们使用单次执行完成了场景的精确布置。现在,让我们切换到代码编辑器,编写动画指令,让鹰飞回场景并完成一系列动作。

代码编辑器:编写动画指令

代码编辑器中的指令会在按下“运行”按钮后,作为动画的一部分按顺序执行。

以下是编写动画代码的步骤:

  1. 添加“顺序执行”块:在开始拖入任何代码之前,最好先拖入一个“顺序执行”块。所有动画代码都应放在这个块内,它们将按照出现的顺序依次执行。

  1. 编写飞入动画:确保鹰被选中,在“过程”选项卡中找到“移动”指令,将其拖入“顺序执行”块中。选择方向为“向前”,距离设为4.0。点击运行按钮,鹰就会飞回屏幕,停在之前预设的位置上。

  2. 添加后续动作:关闭运行窗口后,我们可以继续添加代码。在鹰飞入的指令下方,再拖入一个“移动”指令,设置方向为“向下”,距离为1个单位,让鹰降落到地面。最后,从指令列表中找到“收起翅膀”的指令,将其拖入作为最后一个动作。

  3. 运行动画:再次点击运行按钮。你将看到代码编辑器中的所有指令按顺序执行:鹰飞入场景、降落到地面、然后收起翅膀。每次点击“重启”,动画都会重新播放。

核心区别总结

本节课中我们一起学习了单次执行与代码编辑器指令的核心区别:

  • 单次执行:在场景设置时立即生效,用于初始化世界。其效果是永久性的,除非被后续指令覆盖。
  • 代码编辑器指令:在按下运行按钮后,作为动画流程的一部分按顺序执行。用于定义角色的动态行为。

简单来说,单次执行用于“布置舞台”,而代码编辑器用于“编写剧本”。掌握这两者的区别,是开始用爱丽丝创作生动动画的第一步。

享受用爱丽丝编程的乐趣吧!

011:构建简单世界 🐰🐱

在本节课中,我们将学习如何使用Alice软件构建一个简单的动画世界。我们将通过一个关于兔子和猫互动的故事,逐步学习如何添加角色、设置场景、编写动画指令。

概述

我们将为以下简单故事构建动画:一只猫在兔子身后。猫走向兔子并告诉兔子让开。兔子向上跳起,猫从兔子身边经过。兔子引起猫的注意。猫转身面对兔子。兔子旋转并感到头晕,然后向后倒下。这就是整个故事。现在,让我们开始构建这个世界。

构建世界

首先,我们启动Alice软件,并选择任意地面。这里我们选择沙漠地面,它看起来像沙子。点击“确定”。

接着,我们进入场景设置。我们需要放入一只兔子和一只猫。兔子是两足站立的,因此我们在“两足动物”文件夹中寻找兔子。向右滚动,找到兔子。

将兔子放置在世界中心。旋转兔子,以便我们看到它的右侧。

在“两足动物”文件夹中还有一只黑猫。虽然猫有四条腿,但设计它的艺术家决定将其归为两足动物。

将黑猫放置在兔子身后的世界中。旋转它,使其面朝与兔子相同的方向。然后将黑猫紧贴在兔子身后,几乎要碰到它。

我们使用一个“单次移动”指令,将黑猫向后移动1.5个单位。移动方向选择“向后”,距离输入1.5。

至此,我们的场景设置完成。我们准备好编写动画代码了。

编写动画代码

点击“编辑代码”返回代码编辑视图。在放入任何指令之前,最好先拖入一个“顺序执行”模块来容纳所有代码。我们将在另一节课中更详细地讨论“顺序执行”。

对于我们的故事,首先发生的是猫走向兔子。

我们需要为黑猫拖入移动指令。如果黑猫未被选中,请先选中它。然后,你可以看到黑猫能执行的所有指令。我们知道黑猫在兔子身后1.5个单位处。

将黑猫的“移动”指令拖入“顺序执行”模块中。系统会询问移动方向,选择“向前”。然后询问移动距离,选择“1”。

现在你可以运行你的动画了。猫会向兔子靠近。运行后,关闭动画窗口。如果动画窗口未关闭,Alice将不允许你添加更多代码。

接下来,在我们的故事中,猫告诉兔子让开,然后兔子向上移动。

为猫拖入“说话”指令。选择“自定义文本字符串”,并输入“out of my way”。

我们希望兔子向上移动。我们需要将操作对象从黑猫切换到兔子。现在这些指令是兔子可以执行的。拖入“移动”指令,选择方向“向上”,距离选择“1”。

播放你的世界。猫向前移动,说“out of my way”,然后兔子向上移动。关闭窗口。

接下来,我们将让猫向前移动经过兔子,然后让兔子向下移动回到地面。

选中黑猫。然后将“移动”指令拖入“顺序执行”模块,作为最后一个指令。选择方向“向前”。然后选择一个能让黑猫经过兔子的距离,“2”应该可以。

然后选中兔子。拖入“移动”指令。让兔子向下移动。播放你的世界。

猫移动并说“out of my way”。兔子向上移动。猫向前移动。哎呀,兔子穿过了地面。我们把兔子向下移动得太远了。

我们需要让兔子向下移动的距离与它向上移动的距离相同,即1个单位。关闭运行窗口,将距离“2”改为“1”。

现在,播放你的世界。兔子向上移动,猫经过,兔子向下移动,正好回到原位,这样看起来好多了。关闭运行窗口。

现在,兔子将引起猫的注意。让兔子说“watch this”。拖入“说话”指令,选择“自定义文本字符串”,并输入“Watch this”。

然后选中猫,让它转身看向兔子。选中猫,拖入“转向”指令。转向多少?它应该转半圈,所以选择“向右”转半圈。

现在,让兔子旋转两圈。选中兔子,拖入“转向”指令,选择“向右”转两圈。

然后让兔子说“dizzy”。拖入“说话”指令,选择“自定义文本字符串”,输入“Dizzy”。

现在,我们希望兔子向后倒下。我们将使用“转向”指令,将其拖入。方向选择“向后”。兔子应该转多少?结果是四分之一圈,所以选择0.25。

这就是我们的故事。播放世界。

猫向前移动,说“out of my way”。兔子向上跳。猫向前移动经过。兔子落下并说“watch this”。它旋转两圈,感到头晕,然后向后倒下。

总结

在本节课中,我们一起学习了如何使用Alice构建一个简单的动画世界。我们从设置场景开始,添加了兔子和猫两个角色,并调整了它们的位置。然后,我们通过编写一系列顺序指令,实现了猫走向兔子、兔子跳跃、猫经过、兔子旋转并倒下等动画效果。整个过程涉及了选择对象、使用移动、转向、说话等基本指令,并通过调整参数来精确控制角色的动作。希望你能享受创作有趣故事的过程,并在此过程中学习编程。

013:更多控制与指令比较 🎬

在本节课中,我们将学习爱丽丝(Alice)动画环境中的更多控制指令,特别是关于物体运动与旋转的各种组合方式。我们将探讨不同指令的工作原理、适用场景以及它们之间的区别。

运动与旋转的基础

上一节我们介绍了动画的基本概念,本节中我们来看看爱丽丝中物体运动与旋转的具体类型。这些指令是构建动画的基础。

平移运动指令

平移运动是描述物体普通移动的术语。物体可以在三维空间中沿着三个轴,朝六个不同的方向移动。

以下是三个运动轴及其方向:

  • 前后轴:向前或向后移动。
  • 左右轴:向右或向左移动。
  • 上下轴:向上或向下移动。

旋转运动指令

旋转运动指令描述了物体所有可以转动或翻滚的方式。与平移运动类似,旋转也可以通过三种方式在六个方向上实现。

以下是三种旋转类型:

  • 前后翻转:这类似于滑雪时向前摔倒,身体面朝下向前翻转。
  • 左右转向:这可以通过门的开合来形象地说明,物体围绕垂直轴左右转动。
  • 左右翻滚:这类似于转动门把手,物体围绕其自身的轴线进行旋转。

对物体部件的操作

动画不仅可以施加于整个物体,也可以施加于物体的某个部件。然而,你应当只对部件使用turn(转向)或roll(翻滚)指令。

注意:尝试移动一个物体部件会导致奇怪的变形,因为该部件是附着在物体主体上的。

指令执行的顺序与组合

接下来,我们将探讨顺序执行运动/旋转指令与同时执行它们之间的区别。理解这一点对于创建流畅、符合预期的动画至关重要。

使物体移动的指令

最后,我们需要了解几种能使物体产生移动的指令。选择合适的指令取决于你希望物体如何移动以及移动的目标是什么。

面向与移动

第一种是常规的move(移动)指令。它前面通常会有turn to face(转向面对)或point at(指向)指令。

  • turn to face 指令使一个物体转向以面对另一个物体。随后,当第一个物体向前移动时,它会朝着第二个物体的方向在地面上移动。这个组合最常用于模拟人的行走,因为他们需要保持在地面上。
  • point at 指令使一个物体指向另一个物体。随后,当该物体向前移动时,它会朝着第二个物体移动,但可能不保持在地面上。这个指令序列非常适合鸟类或鱼类,而不太适合人类。

其他移动指令

除了常规的move指令,还有三个指令可以直接让物体向另一个物体移动。

以下是这三个指令:

  • place(放置)
  • move towards(移向)
  • move to(移动到)

根据你想要实现的具体运动效果,这些指令可能比常规的move指令更好或更差。


本节课总结:我们一起学习了爱丽丝中物体平移与旋转的各种指令,包括对整体和部件的操作。我们比较了指令顺序执行与组合执行的区别,并详细介绍了moveturn to facepoint at等关键移动指令及其适用场景。掌握这些指令的组合使用,是创建复杂动画的基础。

014:移动与转向 🛩️

在本节课中,我们将学习爱丽丝(Alice)编程环境中的两种基本运动类型:平移运动旋转运动。我们将通过代码示例和动画演示,详细讲解物体在三维空间中可以如何移动和转向。

平移运动 🚀

平移运动是指物体从一个位置直线移动到另一个位置。在爱丽丝中,物体主要可以沿三个方向进行平移运动。

以下是三种基本的平移运动方向:

  1. 前后方向:物体沿其面对的方向前进或后退。例如,一架面对摄像机的双翼飞机,其“向前”移动就是朝向摄像机移动。

    biplane.move(forward, 1)
    
  2. 上下方向:物体垂直向上或向下移动。这个方向最符合我们的直观感受。

    biplane.move(up, 1)
    
  3. 左右方向:物体向其自身的左侧或右侧移动。需要注意的是,当物体(如面对摄像机的飞机)向“自身右侧”移动时,在摄像机视角下它可能是向左移动的。

    biplane.move(right, 1) // 从飞机自身视角的右侧
    

旋转运动 🔄

上一节我们介绍了物体的直线移动,本节中我们来看看物体如何旋转。旋转运动让物体能够改变自身的朝向。

与平移运动类似,旋转运动也有三种基本方式:

  1. 俯仰:物体绕其左右轴旋转,表现为“点头”动作。例如,飞机机头向下或向上转动。

    biplane.turn(forward, 0.1) // 机头向下转
    
  2. 偏航:物体绕其上下轴旋转,表现为“摇头”动作。例如,飞机向左或向右转向。向右转向通常是顺时针旋转。

    biplane.turn(right, 0.1) // 向右转向(顺时针)
    
  3. 滚转:物体绕其前后轴旋转,表现为“侧滚”动作。例如,飞机向左或向右倾斜机身。由于视角关系,面向我们的飞机向右滚转时,看起来可能有些反直觉。

    biplane.roll(right, 0.1) // 向右滚转
    

总结与练习 📝

本节课中,我们一起学习了爱丽丝中的两种核心运动类型:平移运动(前后、上下、左右移动)和旋转运动(俯仰、偏航、滚转)。理解这些基本运动是创建复杂动画的基石。

我们建议你多次运行课程提供的示例项目。当我们最初学习在爱丽丝中创建动画时,也构建了类似的项目来熟悉物体移动和旋转的各种方式。通过亲手实践,你将能更直观地掌握这些概念,为后续的编程学习打下坚实基础。

015:转向与翻滚 🎬

在本节课中,我们将进一步探索“转向”与“翻滚”这两个动作过程。许多学生能很快熟悉爱丽丝3中的移动方向,但我们发现,要习惯旋转操作通常需要更多的练习。

概述 📋

我们将通过一个预先构建的爱丽丝项目来学习。该项目包含三个对象:一个宝箱、一个微波炉和一个落地钟。我们将观察这些对象如何通过“转向”和“翻滚”来实现不同的动画效果。

宝箱盖的转向 🧰

上一节我们提到了旋转操作,本节中我们来看看宝箱盖是如何工作的。宝箱盖的开关是通过“转向”动作实现的。

  • 打开宝箱:箱盖需要向后转向
  • 关闭宝箱:箱盖可以向前转向

微波炉门的转向 🚪

了解了宝箱的转向后,我们来看看微波炉门的运作方式。它与宝箱盖不同,其铰链位于微波炉的右侧(或从摄像机视角看是左侧)。

以下是微波炉门的开关方式:

  • 要打开门,门需要向右转向
  • 要关上门,门需要向左转向

落地钟指针的翻滚 ⏰

现在,我们转向最后一个对象——落地钟。钟的指针需要的是“翻滚”动作,而非“转向”。

以下是控制分钟指针的方法:

  • 要让时间前进,分钟指针需向其左侧翻滚
  • 要让时间倒退,分钟指针需向其右侧翻滚

总结与练习 🎯

本节课中,我们一起学习了“转向”与“翻滚”在爱丽丝中的区别与应用。我们通过宝箱盖、微波炉门和钟表指针三个例子,具体观察了这些旋转动作如何产生不同的动画效果。

为了更熟悉爱丽丝中转向和翻滚的工作方式,建议你多次运行这个示例项目进行练习。

016:部件转向与移动

在本节课中,我们将探讨当涉及到对象部件时,转向与移动的区别。简而言之,在使用涉及对象部件的指令时,你通常会希望使用转向(Turn/Roll)而非移动(Move)。

我们从一个包含两个对象(一只兔子和一个小孩)的爱丽丝项目开始。

兔子肩部的转向与移动

首先,我们来看看让兔子的肩部向左转再向右转会发生什么。正如预期的那样,兔子会来回摆动它的手臂。

接下来,我们看看如果让兔子的肩部向左移动再向右移动会发生什么。由于兔子的左肩是连接到兔子身体其他部分的,它无法脱离。因此,爱丽丝会尝试将兔子的肩部向左拉伸,从而产生一种非常奇怪且不自然的动作,这通常不是我们希望兔子做的动作。

小孩膝部的转向与移动

上一节我们介绍了兔子肩部的例子,本节中我们来看看小孩膝部的动作。

以下是让小孩膝部向前转再向后转的代码。膝部向前转是我们期望的膝关节正常旋转。

child.knee.turn(FORWARD, 0.25)
child.knee.turn(BACKWARD, 0.25)

接下来,我们尝试让小孩的膝部向前移动。由于膝盖是连接着的,爱丽丝无法移动膝盖,而是会将其向前(对膝盖而言是向下)拉伸,这确实会导致非常奇怪的动画效果。

child.knee.move(FORWARD, 0.5) // 会产生不自然的拉伸效果

小孩肘部的转向与移动

了解了膝部之后,我们再来看看肘部的情况。

让小孩的肘部转向是正确的旋转方式,小孩会因此来回摆动手臂。

然而,如果我们尝试移动小孩的肘部,爱丽丝无法将肘部从其身体的其他部分分离。试图移动肘部会导致肘部被奇怪地拉伸,这很可能不是我们想要或预期的效果。

总结

本节课中,我们一起学习了对象部件转向与移动的区别。

核心要点是:对于身体部件,或者更广义的对象部件,我们几乎从不希望使用移动指令。相反,我们应该根据要旋转部件的方向,使用转向滚动指令。

  • 转向/滚动:产生围绕连接点的自然旋转。
  • 移动:尝试平移部件,可能导致不自然的拉伸。

记住这个简单的规则,将帮助你创建更逼真、更符合预期的动画效果。

017:顺序执行与并行执行 🐒

在本节课中,我们将要学习爱丽丝编程中两种核心的指令执行方式:顺序执行并行执行。我们将通过一个包含两只猴子和两棵棕榈树的场景,直观地理解这两种方式如何影响动画的运行效果和最终结果。


场景介绍

我们有一个预先构建好的爱丽丝项目。场景中有两只猴子,各自面对一棵棕榈树。

  • 位于摄像机左侧的猴子被命名为 left monkey
  • 位于摄像机右侧的猴子被命名为 right monkey

接下来,我们将通过不同的指令执行方式,让这两只猴子完成相同的动作,并观察其差异。


顺序执行:一个接一个地转身

首先,我们来看看让猴子们按顺序向左转身半圈的效果。

以下是执行过程:

  1. 首先,left monkey 完成向左转身半圈的动作。
  2. 之后,right monkey 才开始并完成向左转身半圈的动作。

在这种方式下,动作是依次发生的,后一个动作必须等待前一个动作完全结束后才开始。


并行执行:同时一起转身

现在,让我们看看让猴子们一起向左转身半圈的效果。请注意其中的区别。

与顺序执行不同,在并行执行中:

  • 两只猴子同时开始转身。
  • 它们一起完成转身动作,而不是等待另一只完成。

这种方式使得多个对象的动作可以同步进行。


深入比较:移动与转身的组合

上一节我们介绍了简单的转身动作,本节中我们来看看更复杂的动作组合。我们将有机会看到,使用 do together(并行执行)实际上会导致动画运行后对象处于不同的位置。

首先,我们对右侧的猴子使用 do in order(顺序执行)来执行一组指令。

以下是具体指令序列:

  1. 它先向前移动2个单位。
  2. 移动完成后,再向右旋转四分之一圈。

这一系列按顺序运行的动画指令的最终结果是:猴子最终停在了棕榈树的正前方,并且由于第二条指令而面朝右侧。


现在,我们对左侧的猴子使用 do together(并行执行)来执行完全相同的指令。

对于左侧的猴子:

  • 它向前移动的同时,也在向右旋转四分之一圈。

请注意此时的区别。在这种情况下,左侧猴子的运动轨迹看起来像是绕其右侧画了一个四分之一圆。

它最终面朝的方向与右侧猴子相同(都是右侧),但所处的位置却不同:

  • 它位于其棕榈树的右侧。
  • 它没有右侧猴子那么靠前(离树更远一些)。

虽然它离树没有那么近这一点可能稍难察觉,但它明显更靠前(即Y轴方向位移更大)是很容易看出来的。


总结

本节课中我们一起学习了爱丽丝编程中 do in order(顺序执行)与 do together(并行执行)的核心区别。

  • do in order:指令依次执行,一个完成后才开始下一个。适用于需要严格先后顺序的步骤。
  • do together:指令同时开始执行,允许动作并发进行。适用于需要同步发生的动画效果。

通过观察猴子转身和移动旋转的组合动画,我们清晰地看到,执行方式的选择会直接影响动画的过程和对象的最终状态。建议多次运行此爱丽丝项目,以加深对这两种执行方式差异的理解。

018:移动与移动到指令详解 🎬

在本节课中,我们将学习爱丽丝(Alice)编程环境中几种不同的移动指令。通过一个预先构建好的项目演示,我们将直观地比较moveplacemove towardmove to等指令产生的不同动画效果,并理解它们各自的应用场景。

项目场景概述 🌳

演示项目中有五个孩子(Kid1到Kid5)围着一棵树,树上挂着一个风筝。我们将通过为每个孩子调用不同的移动指令,来观察他们行为的差异。

基础移动指令:转向与指向 🔄

首先,我们来看前两个孩子使用的常规move指令,并结合turn to facepoint at指令。

以下是Kid1的指令序列:

Kid1.turn to face, theKite
Kid1.move, forward, 1 meter

当Kid1执行turn to face风筝时,他会左右转动身体,使自己面朝风筝在地面上的投影方向。随后,当他向前移动时,他会始终在地面上行走。


对于Kid2,我们使用不同的指令:

Kid2.point at, theKite
Kid2.move, forward, 1 meter

Kid2首先执行point at风筝。当他向前移动时,他会离开地面,呈现出“飞行”的效果。

turn to facepoint at都是非常重要的指令,它们经常出现在move forward指令之前。但正如本例所示,两者有显著区别:turn to face让对象转身面向目标,而point at则让对象(的某个轴)指向目标,可能导致对象离开地面。

其他移动指令变体 🧭

除了基础的move指令,还有三个功能相似但效果不同的指令。

放置指令:Place 📍

place指令可以将对象瞬间放置在另一个对象的特定方位上。

例如,对于Kid3:

Kid3.place, theKite, inFrontOf, 0 meters

这条指令使Kid3立刻出现在风筝的正前方。我们也可以选择toTheRightOftoTheLeftOfabovebelowbehind等方位。这个指令非常实用,可以一键将对象移动到目标附近且不产生碰撞。但在本例中,由于风筝在树上,将Kid3“放置”在风筝前看起来就不太合理。

朝向移动指令:Move Toward ⚠️

move toward指令对学生来说可能较难掌握,通常不建议初学者使用。

对于Kid4:

Kid4.move toward, theKite, 1 meter

这条指令会让Kid4的中心点向风筝的中心点移动1米。问题在于,如果两个对象的中心点不在同一水平高度(例如风筝在空中),Kid4就会开始“飞行”;如果风筝的中心点更低,Kid4则可能“陷入”地面。此外,Kid4在移动前不会转身面向风筝,这有时看起来会不自然。

move toward的效果,与先执行point at再执行move forward的组合指令在最终位置上有些类似。

移动到指令:Move To 🎯

最后是move to指令,它也可能带来意想不到的效果。

对于Kid5:

Kid5.move to, theKite

这条指令会使Kid5的中心点与风筝的中心点完全重合。结果就是,风筝看起来像是穿过了Kid5的身体。虽然有时需要这种特效,但在当前场景下显得很奇怪。

总结与建议 📝

本节课我们一起学习了爱丽丝中几种核心的移动指令。总结如下:

  • 最常用的是基础的move指令,通常与turn to face配合使用,来制作角色在地面行走的动画。
  • place指令适用于需要对象瞬间出现在特定位置的场景。
  • move towardmove to指令由于会直接操作对象的中心点,容易产生飞行或重叠的怪异效果,使用时应格外小心,通常只在制作飞行生物(如鸟、鱼)或特定特效时考虑使用。

建议你亲自尝试这些指令的不同变体,以加深理解。在大多数人物动画中,使用turn to face配合move是最安全、最自然的选择。

019:故事板设计 📝

在本节课中,我们将要学习动画创作中的一个关键前期步骤:故事板设计。我们将了解什么是故事板,为什么它很重要,以及如何为一个简单的故事创建自己的故事板。

概述

创建动画主要包含两个步骤。首先,你需要设计动画。你需要规划动画的每一个场景,包括每个对象的位置。这可以通过故事板来完成。一旦你完成了动画设计,就可以准备将设计转化为代码。你在设计阶段投入的时间和深度越多,在调试代码上花费的时间就会越少。

什么是故事板?

故事板是一系列简单的草图,展示了动画每个场景中发生的事情。你不需要成为艺术家。使用简笔画人物完全可以,只要你能分辨出他们面对的方向。

故事板中重要的是列出动画每个场景中的所有对象,并了解每个对象的位置及其与其他对象的空间关系。同时,包含对每个场景中希望发生事情的描述也很重要。

为什么需要创建故事板?

在现实世界中,像皮克斯这样的电影公司会为他们的电影创建故事板。编剧写出故事,然后交给艺术家,艺术家会画出所有场景的草图,并按顺序贴在墙上。接着是关键的时刻:编剧和艺术家通过草图和描述,向导演生动地讲述故事,使其“活”起来。这个“故事提案”环节非常重要,因为导演将据此决定是否制作这部电影。

故事板设计实践

现在,让我们来看一个我们想要为其设计故事板的故事。

故事梗概:
宇航员艾米在月球上。突然,一个外星人从岩石后面出现并发出声音。艾米尖叫着跑向宇宙飞船,外星人注视着。舱门打开让她进去,然后关闭。飞船起飞时摇晃了一下,外星人继续看着。外星人问道:“你不想玩吗?”,然后外星人很伤心。

确定所需元素

为了创建这个动画,我们需要确定以下内容:

  • 我们需要使用月球作为地面,因为这个故事发生在月球上。
  • 我们需要决定世界中需要哪些对象。我们需要一个宇航员、一艘宇宙飞船、一个外星人和一块供外星人藏身的岩石。
  • 注意,我们的宇宙飞船需要有可以打开和关闭的舱门。

故事板分步解析

上一节我们确定了动画的基本元素,本节中我们来看看我为这个故事绘制的故事板草图。以下是每个场景的详细说明。

场景1:初始设置

场景1是动画的初始设置。我在场景中标注了对象。你也应该列出场景中的对象。请注意,我确实不是艺术家,用简笔画和形状团块完全可以。重要的是了解对象相对于其他对象的位置,这可以用简单的形状来完成。

场景2:外星人出现

场景2展示了动作的发生。当外星人从岩石后面出现时,艾米转身看向外星人。箭头可以用来表示运动。你还应该包含对每个场景中发生事情的描述。在这个场景中:有声音,外星人出现,艾米转向外星人。

场景3:艾米跑向飞船

场景3描述:艾米尖叫并移向宇宙飞船,外星人注视着。舱门打开让她进去。在这里你可以看到一个箭头表示艾米转向飞船,另一个箭头表示她移向飞船。还有一个箭头表示舱门正在打开,并且当艾米移向飞船时,外星人正转身看着她。

场景4:舱门关闭
(此场景在描述中提及,但未提供图片)
场景4显示宇宙飞船的舱门关闭。它还显示了正在看向飞船的外星人的背面。

场景5:飞船起飞
(此场景在描述中提及,但未提供图片)
场景5显示宇宙飞船摇晃并起飞,外星人注视着。有双箭头表示飞船摇晃,一个大箭头表示飞船起飞。你还可以看到外星人在飞船起飞时转身跟随飞船。

场景6:外星人发问
(此场景在描述中提及,但未提供图片)
场景6描述:外星人问道:“你不想玩吗?”,外星人很伤心。注意在这个场景中,只有两个对象:岩石和外星人,宇宙飞船和艾米已经不见了。

总结

本节课中我们一起学习了故事板设计。故事板是设计过程中的重要组成部分。草图展示了对象的位置、运动以及对象是否是场景的一部分。文字描述有助于解释每个场景中发生的事情。在绘制故事板上投入的额外努力将使实现故事变得更容易,也可能有助于减少调试故事代码的时间。

020:故事板实现 🚀

在本节课中,我们将学习如何将一个故事板转化为可执行的动画代码。我们将跟随宇航员艾米在月球上遇见外星人的故事,一步步在Alice中实现场景搭建、角色互动和动画编排。

概述

我们将实现一个简短的故事:宇航员艾米在月球上发现了一个外星人,她感到害怕并逃回飞船离开。外星人则显得很伤心。整个过程涉及场景设置、角色动作编排以及多个事件的同步执行。

场景搭建

首先,我们需要搭建故事发生的场景。以下是具体步骤:

  1. 启动一个新的Alice项目,选择“moon”作为地面。
  2. 点击“Setup Scene”按钮,开始添加对象。
  3. 点击“Biped Class”,使用角色生成器创建宇航员。选择“Female Adult”。
  4. 在服装列表中滚动到底部,选择太空服(只有成年男性和女性角色有此服装)。
  5. 点击“OK”,为你的宇航员命名(例如:Amy)。将她向后移动一些,并放置在场景左侧。
  6. 在对象库中找到绿色面孔的外星人,将其拖放到场景右侧,并稍向后移。
  7. 点击“All Classes”,进入“Transport”类文件夹,选择“Aircraft”类,添加一个UFO。将其重命名为“Spacecraft”并放置到远处。
  8. 在搜索栏中输入“rock”,添加一个小型岩石。如果它太大,可以使用“Resize”按钮适当缩小。将岩石放置在外星人前方。

初始动作设置

上一节我们搭建好了场景,本节中我们来看看如何设置故事的起始画面。我们需要让外星人转身面对艾米,并躲到岩石后面。

  1. 选中外星人,使用“One Shot”功能,让它“Turn to face”艾米。
  2. 将外星人移动到岩石后面,只露出一小部分绿色的脸。
  3. 使用左侧的紫色箭头稍微调整摄像机角度,使其俯瞰整个场景。

编写动画代码

现在场景和初始位置已就绪,我们进入代码编辑器来实现故事板。

首先,从窗口底部拖入一个“Do in order”指令块。将所有代码放入此顺序块中,便于整体管理和后续调整。

第一步:外星人出现

以下是外星人出现并吸引艾米注意的步骤:

  1. 添加注释:“An alien appears and gets Amy‘s attention.”
  2. 拖入一个“Do together”块,让外星人的移动和说话同时进行。
  3. 在外星人“Do together”块内,添加“move left”指令,移动2个单位,使其从岩石后移出。
  4. 添加“say”指令,输入自定义文本,例如:“Sliy tove.”
  5. 点击“Run”查看效果。运行后请关闭播放窗口。

第二步:艾米的反应

外星人出现后,接下来是艾米的反应。

  1. 在第一个“Do together”块下方,选中艾米,添加“turn to face”指令,让她面对外星人。
  2. 添加注释:“Amy is terrified and returns to the ship.”
  3. 让艾米说:“Ah!”
  4. 然后让艾米“turn to face”她的飞船。

第三步:逃向飞船

现在,我们需要让几个事件同时发生:艾米跑向飞船、飞船门打开、外星人转头观看。

  1. 插入一个新的“Do together”块。
  2. 让艾米“move to”飞船。
  3. 选中飞船,找到其左门,添加“turn left”指令,角度设为0.05。
  4. 选中飞船的右门,添加“turn right”指令,角度同样设为0.05。
  5. 让外星人“turn right”约四分之一圈,注视艾米。

第四步:关闭舱门与起飞准备

艾米进入飞船后,舱门关闭,飞船准备起飞。

  1. 插入一个“Do together”块来同步关闭两扇门。
  2. 让左门“turn right” 0.05度。
  3. 让右门“turn left” 0.05度。
  4. 关键修正:在这个“Do together”块中,同时让艾米“turn to face”摄像机,这样她在飞船内方向就正确了。
  5. 添加注释:“Spacecraft takes off.”
  6. 让飞船轻微摇晃模拟启动:先“roll left” 0.02度,再“roll right” 0.04度,最后“roll left” 0.02度回正。

第五步:飞船起飞

飞船起飞时,需要同时上升、前进,并且外星人会抬头目送。

  1. 插入一个“Do together”块。
  2. 让飞船“move up” 5个单位,并“move forward” 20个单位。
  3. 关键修正:在同一个“Do together”块中,让艾米执行完全相同的移动指令(“move up” 5 和 “move forward” 20),这样她才会和飞船一起移动。
  4. 让外星人“turn left”约四分之一圈,看着飞船离开。

第六步:外星人的结局

故事最后,外星人感到伤心。

  1. 在代码末尾添加注释:“The alien is sad.”
  2. 让外星人说:“Don‘t you want to play?”
  3. 为了让外星人看起来悲伤,选中它,通过层级箭头找到“head”,让它的头“turn forward”约0.125度,做出低头的动作。

总结

本节课中,我们一起学习了如何将故事板转化为Alice动画。我们完成了从场景搭建、角色定位,到编写复杂的顺序与并行代码的完整流程。关键点包括:使用“Do in order”组织整体流程,用“Do together”同步多个动作,以及注意角色相对方向对移动指令的影响。通过这个练习,你已经掌握了在Alice中实现叙事性动画的基本方法。

021:若干实用技巧 🎬

在本节课中,我们将学习一系列关于使用Alice软件进行编程和动画制作的实用技巧。这些技巧涵盖了软件运行、代码编辑、项目管理和成果分享等多个方面,旨在帮助你更高效、更顺畅地完成创作。


软件运行与项目管理 💻

你已经学习了很多知识,包括如何设置场景、如何移动和旋转角色、如何设计故事板,以及如何将故事板转化为代码来创建动画。你正在学习动画技术、编程和问题解决方法。本节中,我们将为你提供一些通用的Alice使用技巧。

运行Alice的注意事项

首先,关于运行Alice软件,有几点需要注意。

以下是运行Alice时的关键建议:

  • 仅运行一个Alice实例:不要同时启动多个Alice程序。因为Alice运行时需要占用大量计算机内存,其中的3D对象比2D图形更为复杂。同时运行多个实例可能导致计算机速度变慢,风扇高速运转。
  • 运行窗口与编辑窗口:当你点击“运行”后,动画会开始播放,但在关闭运行窗口之前,你将无法编辑代码。如果你点击Alice主窗口,它会被前置,而运行窗口仍在后台播放。如果发现无法编辑代码,这很可能是原因所在。
  • 处理“渲染已禁用”提示:如果你看到“出于性能考虑,渲染已禁用”的提示,请找到运行窗口。移动或最小化Alice代码编辑器窗口,运行窗口可能就在其后面。然后关闭运行窗口,即可恢复代码编辑。

项目保存与备份

因为Alice运行需要大量内存,在不编写项目时,不建议让其持续运行。最好在准备再次工作时再启动Alice。启动后,可以进入“最近项目”选项卡,你正在处理的项目通常会显示在最顶部。

你是否曾在电脑上丢失过工作?这种情况确实会发生。对于Alice项目,你应该经常保存。

以下是关于保存和备份的要点:

  • 保存项目:每次保存项目时,你会看到一个扩展名为 .a3p 的Alice 3项目文件,同时还会生成一个保存了多个备份文件的文件夹。
  • 备份文件:例如,我保存了一个名为“Cat and Bunny Simple”的世界,同时可以看到一个名为“cat and Bny Simple.BAK”的备份文件夹。在该文件夹内,有我每次保存项目时生成的所有备份文件,每个文件都有唯一的名称和保存时间/日期。
  • 从备份恢复:如果你的项目文件损坏,可以尝试从备份文件重新开始。前提是你经常保存。加载备份文件后,请务必立即将其重命名为一个有意义的名称。

代码编辑技巧 ✏️

上一节我们讨论了软件的运行与保存,本节中我们来看看如何更高效地编辑代码。

复制与粘贴代码

在Alice中,你可以使用剪贴板来复制和粘贴代码。你可以复制单行代码或整个代码块。

假设我有一个让鸡向前移动的指令,同时我也想添加一个让牛向前移动的指令。这两个指令非常相似,如果能复制鸡的指令就好了,而你可以做到。

操作方法是:点击指令的蓝色部分(最左侧的选项卡或“move”这个词),然后选择“复制到剪贴板”。接着,你可以点击剪贴板,将指令的副本拖拽到代码编辑器中。最后,点击单词“chicken”并将其改为“cow”。

如果你在一个“Do in order”块中有大量代码,你可以将整个“Do in order”块复制到剪贴板,然后在代码编辑器中获得它的副本。

禁用与启用代码块

Alice的另一个功能是禁用代码块,并在你想使用时再启用它。当代码被禁用时,它会被忽略,不会运行。你可以禁用一条指令或一大段代码。

这里有一个例子:我有一个“Do in order”代码块,其中包含鸡向前移动和牛向前移动的指令。同时,两个动物将头转向对方。

假设我想专注于研究头应该转多少度,并想尝试几个不同的数值。那么,在调试这个动作时,如果能禁用第一个“Do in order”块就好了,这样我就不必每次都先看鸡和牛移动。

如何禁用第一个“Do in order”块?点击“Do in order”最左侧的选项卡,选择旁边带有对勾的“is enabled”选项。选中后,对勾会消失,代码块即被禁用。当代码块被禁用时,Alice会用斜线划掉代码。当你点击运行时,被禁用的代码块会被忽略,两个动物只会转头。一旦我让转头动作达到预期效果,我可以再次点击“Do in order”选项卡,选择“is enabled”,该代码块就会重新激活,斜线也会消失。

慎用“Do Together”

使用“Do together”很有趣,但我发现学生有时会将过多代码放入一个“Do together”中,导致代码运行不符合预期。

以下是同时向上和向下移动相同距离的代码。首先,它在一个“Do in order”中展示,运行符合预期。然后,同样的向上和向下移动指令被放在一个“Do together”中。结果是什么也没发生。Alice足够智能,能判断出如果这两个指令同时发生,其结果是兔子停留在原地。因此,总体效果是它们相互抵消,没有产生可见动作。

这是另一个例子:这里是让Alice先转身再移动的代码。第一次用“Do in order”展示时,代码按预期运行,Alice先转身再移动。然后,同样的移动和转身指令被放入一个“Do together”中。效果截然不同,Alice甚至停在了不同的位置。这是用户想要的效果吗?


分享与打印 📤

打印代码

你可以打印你的Alice代码。操作方法是:选择“文件”,然后选择“打印当前代码”。有几个选项可供选择:选择打印机。我倾向于打印为Adobe PDF,这样我可以稍后检查并打印。我喜欢选择纵向模式,横向也是一种选择,但我认为纵向模式下代码在页面上布局更合适。

这是打印到PDF的部分代码,你可以看到打印出来的效果,它看起来和你在Alice项目中看到的一模一样。

制作并分享动画视频

你想与他人分享你制作的动画吗?你可以通过录制Alice项目运行的视频来分享你的动画,并上传到YouTube与朋友和家人分享。

操作步骤如下:

  1. 打开你想要制作视频的Alice项目。
  2. 点击“文件”,选择“上传到YouTube”。
  3. 你需要运行动画来录制它。你可以录制其中一部分,或整个动画。
  4. 点击“录制”。画面可能看起来有点抖动,但它正在正确录制动画。
  5. 点击“停止”,然后点击“下一步”,接着点击“导出视频到文件”。这会将视频以 .webm 格式保存到你的电脑上。Alice将视频保存在名为“My Videos”的文件夹内的“Alice3”文件夹中。
  6. 现在你需要浏览到YouTube网站,上传你的视频,然后将链接分享给你的朋友和家人。

这是一个我从本课程中制作的Alice视频链接。这是该视频在YouTube页面上的样子(刚上传时,还没有其他人观看过)。记得给它点个赞。


其他实用建议 💡

我们还有一些其他建议给你。

首先,无论何时搞砸了,请记住,“撤销”是你的好朋友。如果你在代码编辑器窗口中,需要点击“编辑”,然后选择“撤销”。如果你在场景设置窗口中,窗口右上角有一个撤销按钮。

如果你在使用Alice时遇到任何问题,最好的办法是重启Alice。就像每当我的笔记本电脑出问题时,我会重启它,问题通常就会消失。

这里有一个重启Alice会对我有帮助的例子:我可能编写了大量代码,并决定将我的一个角色从“Amy”重命名为“Sarah”。这意味着所有引用“Amy”的指令现在都应该改为“Sarah”。但有时Alice更新较慢,你可能仍然会看到一条写着“Amy”的指令。如果你看到这种情况,请关闭Alice并重新启动它。这将确保所有的“Amy”都已更新为“Sarah”。


总结 📝

本节课中,我们一起学习了使用Alice进行编程和动画制作的一系列实用技巧。我们涵盖了如何高效运行和管理软件、如何利用复制粘贴和禁用功能来编辑代码、如何打印代码以及如何将动画制作成视频进行分享。最后,我们还强调了使用“撤销”功能和重启软件来解决常见问题的重要性。希望这些技巧能帮助你在Alice编程中更加得心应手。

022:概述 🗺️

在本周课程中,我们将探索所有现代编程语言中一个绝对核心的构建模块。我们将学习如何创建自己的过程或指令,让编程变得更高效、更灵活。

创建自定义过程

上一节我们学习了使用爱丽丝内置的指令。本节中,我们来看看如何将多个指令组合成一个新的、可重复使用的指令。

我们将把几个爱丽丝指令(例如移动、转向、翻滚、转向面对等)组合在一起,并为这组指令命名,例如 hop。这样,我们就创建了一个名为“跳跃”的全新指令。之后,每当想让兔子跳跃时,我们无需记住所有构成跳跃的转向和移动指令,只需在程序中拖入一个 hop 指令块即可。

以下是创建自定义过程的好处:

  • 提高效率:节省大量拖放操作。
  • 简化记忆:记住一个单词 hop 比记住构成跳跃的大约7个指令要容易得多。

使用参数增强过程

在创建了几个自定义过程后,我们将学习如何通过使用参数,让我们的过程变得更强大、更通用。

我们已经在使用爱丽丝内置过程的参数了。例如,当我们使用 move 指令时,程序会要求提供更多信息:物体移动的方向和距离。参数的形式可以表示为:

move(direction, distance)

我们将学习如何为自己创建的过程添加参数。我们还将初步了解过程的“层级”。换句话说,如果我们为“兔子”类创建了一个 hop 过程,那么所有兔子对象都将知道如何跳跃。然而,如果我们为“双足动物”类创建 hop 过程,那么所有双足动物(如兔子、人类、白兔等)都将知道如何跳跃。

深入探索方向与运动

本周我们还将花更多时间探索物体朝向、方向与物体运动之间的关系。我们会研究物体的不同部分(例如人的肘部或膝盖),并了解物体部分的朝向通常与物体整体的朝向不同。

当尝试转动或翻滚物体的部分时,这种朝向差异将变得非常重要。理解这一点对于制作逼真的动画至关重要。

掌握摄像机的使用

本周要涵盖的最后一个主题是摄像机。在爱丽丝中,摄像机本身也是一个对象。和所有爱丽丝对象一样,摄像机可以在场景设置期间以及动画运行时被移动、转向、翻滚等。

您可能希望移动摄像机来获得对象的特写视图,或者从不同视角观看动画。例如,从房屋的顶部视图,以便看清哪些物体在房屋前面,哪些在后面。

鉴于我们几乎都是初露头角的“阿尔弗雷德·希区柯克”,我们都需要学习如何在动画运行时重新定位摄像机,以获取完美的镜头。

总结

本节课中,我们一起学习了第三周的核心内容:创建自定义过程使用参数深入理解方向与运动以及操控摄像机。这些是构建更复杂、更高效动画程序的基石。现在,让我们开始学习所有这些精彩的内容吧!

023:Hop功能概述 🐰

在本节课中,我们将要学习过程(Procedures)这一核心概念。过程是构建大型程序的基础,它能帮助我们简化代码、减少重复劳动,并让我们在更高的抽象层次上进行思考。

什么是过程? 🤔

上一节我们介绍了课程的整体目标,本节中我们来看看什么是过程。

在爱丽丝(Alice)中,一个过程本质上就是一条指令。我们已经见过许多内置的指令,例如 move(移动)、turn(转向)、roll(滚动)、turn to face(转向面对)等。这些指令是爱丽丝的原始指令,它们是软件的一部分,无法再分解为更小的指令。

然而,爱丽丝也允许我们创建自己的过程或指令。这些自定义指令将由爱丽丝的原始指令组合而成。

术语说明 📚

在深入探讨过程的用途之前,我们需要先了解一些术语。计算机科学充满了特定于编程语言或编程本身的词汇。

  • 过程:在爱丽丝中,描述创建自定义指令的术语是“过程”。
  • 方法:如果你与使用 Java、C++、C# 等语言的程序员交流,描述自定义指令的术语通常是“方法”。从技术上讲,过程是一种“无返回值的方法”,但大多数程序员直接使用“方法”一词。
  • 指令:指令、过程和方法这三个术语在本质上是相同的。

在本课程中,我们将交替使用这三个术语。

为什么需要使用过程? 💡

过程能为我们节省大量时间,因为它可以减少我们需要编写的代码量。你只需编写一次代码,然后在需要时调用它即可。

过程还能显著减少你拖放指令的操作次数。让我们通过一个例子来理解这一点。

示例:让兔子跳跃 🐇

在这个模块中,我们将编写让兔子跳跃的指令。根据我们期望跳跃的逼真程度,可能需要大约 7 条指令(移动、转向等)才能让兔子完成一次跳跃。

那么,如果我们想让兔子跳两次呢?如果不使用过程,我们就需要 14 条指令。跳三次则需要 21 条指令。这需要大量的拖放操作。

相反,爱丽丝允许我们创建一个名为 Hop 的自定义指令。创建这个指令本身仍然需要那 7 条指令。但一旦创建完成,要让兔子跳三次,我们只需要这样做:

do in order
    Bunny.hop
    Bunny.hop
    Bunny.hop

这比在我的第一个方法中拖入 21 条指令要简洁得多。

抽象的力量 🧠

计算机科学家认为,过程或方法的使用实现了抽象。这意味着,一旦我们满意于让兔子跳跃所需的这 7 条指令,我们就可以忘记创建跳跃所需的所有转向和移动细节,而只需将 hop 视为兔子现在可以执行的一个单一指令。

除了转向、滚动、移动等,我们现在是在一个更高的抽象层次上进行思考和推理——我们思考的是“兔子跳跃”,而不是“兔子转向,然后移动,然后再移动”。

总结 📝

本节课中我们一起学习了:

  1. 过程是自定义的指令,由原始指令组合而成。
  2. 理解术语过程方法指令之间的联系。
  3. 使用过程的主要好处是减少代码重复简化复杂操作
  4. 过程实现了抽象,允许我们在更高的概念层次上思考问题,而无需关注底层实现细节。

通过创建像 Hop 这样的过程,我们可以让编程变得更高效、代码更易读和维护。接下来,让我们开始动手,教一只兔子如何跳跃吧!

024:兔子跳跃过程 🐇

在本节课中,我们将学习如何创建自定义指令或过程。具体来说,我们将创建一个让兔子跳跃的指令。通过这个过程,你将理解如何将一系列复杂的动作封装成一个简单的、可重复使用的指令。

概述

我们将创建一个名为“跳跃”的过程,让兔子以三角形的轨迹跳跃。首先,兔子会转向芒果,然后同时向上和向前移动,最后同时向下和向前移动,完成一次跳跃。我们将使用“顺序执行”和“同时执行”指令块来组合这些动作。

创建跳跃过程

上一节我们介绍了本节课的目标,本节中我们来看看如何具体实现兔子的跳跃过程。

首先,我们进入编程模式,在“我的第一个方法”窗口中开始编写代码。我们需要让兔子完成转向和跳跃两个主要动作。

以下是实现跳跃的步骤:

  1. 转向芒果:首先,我们需要让兔子转向芒果。为此,我们拖入一个“顺序执行”指令块,然后将“转向”指令放入其中,并指定目标为芒果。

    this.bunny turn to face this.mango
    
  2. 实现跳跃的第一部分:接下来,我们需要让兔子同时向上和向前移动。我们在“转向”指令后,拖入一个“同时执行”指令块。

    • 在该指令块内,添加一个“移动”指令,方向设为“向上”,距离设为0.5米。
    • 同时,再添加一个“移动”指令,方向设为“向前”,距离设为0.5米。
    Do together {
        this.bunny move up 0.5 meters
        this.bunny move forward 0.5 meters
    }
    
  3. 实现跳跃的第二部分:然后,我们需要让兔子同时向下和向前移动,回到地面。我们在第一部分后面,再拖入一个“同时执行”指令块。

    • 在该指令块内,添加一个“移动”指令,方向设为“向下”,距离设为0.5米。
    • 同时,再添加一个“移动”指令,方向设为“向前”,距离设为0.5米。
    Do together {
        this.bunny move down 0.5 meters
        this.bunny move forward 0.5 meters
    }
    
  4. 添加注释:最后,为了代码清晰,我们可以在第一个“同时执行”指令块前添加一个注释,说明“兔子进行三角跳跃”。

运行程序,兔子会转向并朝芒果跳跃一次。

封装为自定义过程

虽然上述方法可行,但如果想让兔子跳跃多次,就需要重复拖放大量指令块,这非常低效。因此,我们将这个跳跃动作序列封装成一个自定义过程。

以下是创建自定义“跳跃”过程的步骤:

  1. 创建新过程:点击屏幕顶部中央的黄色六边形右侧的小三角,从下拉列表中选择“兔子”,然后选择“添加兔子过程”。
  2. 命名过程:将新过程命名为“hop”。
  3. 编写过程内容:在新的“hop”方法编辑窗口中,重复我们之前编写的跳跃指令序列(包含两个“同时执行”指令块和内部的移动指令)。
  4. 调用过程:完成编写后,点击返回“我的第一个方法”标签页。从左侧对象列表中选择“this.bunny”,然后找到并拖拽我们刚刚创建的“hop”过程到方法中。

现在,当点击运行时,兔子会执行一次跳跃。过程的威力在于其可重用性。如果我们想让兔子跳跃两次,只需将“hop”过程指令再拖入“我的第一个方法”中一次即可,无需重复编写所有细节代码。

// 在“我的第一个方法”中
this.bunny turn to face this.mango
this.bunny hop // 第一次跳跃
this.bunny hop // 第二次跳跃

总结

本节课中我们一起学习了如何创建和使用自定义过程。我们首先将兔子跳跃的复杂动作分解并组合实现,然后将其封装成一个名为“hop”的可重用指令。通过使用过程,我们避免了代码重复,让程序结构更清晰,也更容易修改和维护。这体现了编程中“抽象”和“模块化”的核心思想。

026:不同方向的对象移动

在本节课程中,我们将学习对象的方向如何影响其移动方式。我们将通过一个预先编写好的爱丽丝世界来演示,并解释为什么其行为初看之下有些奇怪,但实际上完全符合动画原理。

概述

在三维动画中,每个对象都有自己的局部坐标系,其移动方向(如前、后、左、右、上、下)是相对于其自身的朝向而言的,而非观众的视角。理解这一点是制作精确动画的关键。

场景设置

我们的演示世界中包含两个角色:一位女性和一位男性。初始状态下,他们都面朝观众(即摄像机方向)。

以下是程序执行的步骤序列:

首先,我们让男性角色向左移动半个单位,然后向左转四分之一圈(即90度)。

接着,我们通过让他向前旋转八分之一圈(即45度),使他的朝向与女性角色完全不同,从而让情况变得复杂。

观察移动行为

现在,我们让两个角色分别执行移动指令,并观察其效果。

当女性角色向左移动时,她移向自己的左侧,也就是观众的右侧,因为她正对着摄像机。然后她向右移动,回到了起始位置。

当男性角色向左移动时,由于他之前向左旋转了90度,他的“左”方向已经改变,因此他实际上是在远离观众的方向移动。

接下来,女性角色向上移动,然后向下移动。这个运动完全符合我们的预期。

当男性角色向上移动时,他看起来同时向前和向上移动。这是因为他的“上”方向,由于之前向前旋转了45度,实际上处于一个45度的倾斜角度。

当女性角色向前移动时,她向摄像机方向(即朝向观众)移动。这个运动也符合预期。

当男性角色向前移动时,他却向地面移动。这是因为他之前向前旋转了45度,所以他的“前”方向在观众看来是向前且向下的。

总结

本节课我们一起学习了对象方向对其移动的深刻影响。关键在于记住:move 指令中的方向(如前、左、上)总是相对于对象自身的局部坐标系,而非世界坐标系或观众的视角。我们鼓励你多次运行这个世界,以熟悉当对象并非直立时,这种看似奇怪实则正常的行为模式。掌握这一概念,将帮助你更精准地控制三维空间中的动画。

027:对象与部件转向翻滚演示 🧭

在本节课中,我们将学习如何在Alice中理解对象的旋转。旋转有时难以直观把握,因此我们将通过一个四手演示来清晰地展示对象整体及其部件的旋转原理。

概述 📋

我们将使用一个左手、一个坐标轴、一个方便的人体模型以及一只旋转模型及其关节的手来进行演示。在Alice中,坐标轴与人物模型的对应关系如下:绿色轴代表向上,红色轴代表向右,蓝色轴代表向后。模型前方通常没有白色箭头指示。

你可以在Alice中通过添加一个人物、添加一个坐标轴,并让坐标轴移动和对齐到该人物来验证这一点。

旋转规则:左手定则 ✋

Alice中的旋转遵循左手定则。我们将左手拇指指向要围绕旋转的轴的方向。

观察幻灯片:

  • 左右转向等同于围绕绿色轴旋转。
  • 前后转向等同于围绕红色轴旋转。
  • 左右翻滚等同于围绕蓝色轴旋转。

围绕各轴旋转演示 🔄

上一节我们介绍了旋转的基本规则,本节中我们来看看具体的演示。

以下是围绕绿色轴(上下方向)的旋转演示:

  • 使用左手演示。
  • 使用人体模型演示。

现在,让我们看看围绕红色轴(左右方向)的旋转:

  • 使用左手演示。
  • 使用人体模型演示。

接下来,观察围绕蓝色轴(前后方向)的旋转:

  • 使用左手演示。
  • 使用人体模型演示。

部件旋转示例 🦵

理解了整体的旋转后,我们来看看模型部件的旋转,其方向可能与整体不同。

首先观察人体模型的右膝,其局部方向与整体不同。现在,我们让膝盖弯曲。遵循左手定则,我们需要让膝盖向前旋转四分之一周。

接着,观察人体模型的左肘,其方向也与整体不同。现在,我们让左臂做一个弯举动作。遵循左手定则,我们需要让肘部向右旋转四分之一周。

最后一张幻灯片展示了人物整体、他的膝盖以及他的肘部所具有的不同方向。

总结 🎯

本节课中我们一起学习了Alice中的旋转机制。我们通过左手定则明确了围绕不同坐标轴(绿、红、蓝)旋转对应的动作(转向、翻滚)。同时,我们也了解到模型部件的局部旋转方向可能独立于整体。旋转可能比较复杂,一个有用的技巧是在Alice中添加一个坐标轴参考,或者用实物组装一个坐标轴模型来辅助理解。

028:带肘膝的Alice人物关节旋转演示

在本节课中,我们将学习Alice中人物模型不同关节的旋转方向。我们将重点探讨人物整体、膝盖和肘部关节在旋转、转向和滚动时的不同表现,并理解其背后的坐标系原理。

概述:关节的旋转方向

上一节我们介绍了基本的旋转概念,本节中我们来看看一个更具体的例子。在Alice中,人物模型的每个关节(如膝盖、肘部)都有其独立的旋转坐标系。理解这些坐标系的朝向,是控制角色动画的关键。

本次演示将探索关节的不同旋转朝向。具体来说,我们将探讨人物整体的转向和滚动,与人物膝盖的转向和滚动有何不同,以及膝盖的转向和滚动与人物肘部的转向和滚动有何不同。

场景中的三个坐标系

在这个Alice项目中,存在三个坐标系轴。

  • 最左侧的轴(从摄像机视角看)与人物整体的朝向一致。这意味着该轴的指向与人物面朝的方向相同。
  • 中间的轴与人物右膝的朝向一致。换句话说,该轴的指向与人物右膝的朝向相同。
  • 摄像机右侧的轴与人物左肘的朝向一致。也就是说,右侧轴的指向与人物左肘的朝向相同。

人物整体的旋转

当我们运行这个Alice项目时,人物和最左侧的轴会进行转向和滚动。请注意,绿色箭头指向上方,这代表人物的“向上”方向。

以下是人物执行的旋转动作序列:

  1. 首先,人物和左侧轴向右转,然后向左转。注意,向右和向左的转动遵循左手定则,围绕的是坐标轴的绿色方向。
  2. 接着,人物和左侧轴向前转,然后向后转。注意,向前转动同样遵循左手定则,但这次围绕的是坐标轴的红色方向。
  3. 最后,人物向右滚动,然后向左滚动。注意,向右和向左的滚动遵循左手定则,围绕的是坐标轴的蓝色方向。

膝盖关节的旋转

接下来,我们观察人物膝盖的旋转。如你所见,膝关节和膝盖坐标轴的朝向相对于人物整体向前旋转了四分之一圈

由于膝关节和膝盖坐标轴具有这种不同的朝向,其旋转行为看起来与人物整体不同。具体表现为:

  • 指示“向上”方向的绿色箭头现在指向前方,而不是像人物整体那样指向上方。
  • 指示“向后”方向的蓝色箭头现在指向上方,而不是像人物整体那样指向后方。

以下是膝盖执行的旋转动作:

  1. 当膝盖向右转时(这个动作对真人来说非常痛苦),旋转遵循左手定则,围绕的是膝盖坐标轴的绿色方向。幸运的是,模型的关节比真人灵活得多。
  2. 接着,膝盖向前转,然后向后转。这遵循左手定则,围绕的是膝盖坐标轴的红色方向。实际上,这是膝盖唯一能正常弯曲的方向。
  3. 最后,膝盖向右滚动。虽然这看起来极其痛苦,但它只是遵循左手定则,围绕膝盖坐标轴的蓝色方向。

肘部关节的旋转

现在,让我们看看人物的左肘及其相关的肘部坐标轴。

请注意,这个坐标轴相当于将原始人物的坐标轴先向前旋转四分之一圈,再向左旋转四分之一圈

这意味着:

  • 被称为“向上”方向的绿色箭头,现在指向人物的左侧,而不是像人物整体那样指向上方。
  • 代表人物“向右”方向的红色箭头,现在指向前方。
  • 代表人物“向后”方向的蓝色箭头,现在指向上方。

以下是肘部执行的旋转动作:

  1. 肘部向右转遵循左手定则,围绕的是肘部坐标轴的绿色方向。
  2. 肘部向前转遵循左手定则,围绕的是肘部坐标轴的红色方向。在这个例子中,肘部转向了人物的身体。幸运的是,人物在此过程中没有受伤。
  3. 最后,肘部向右滚动遵循左手定则,围绕的是肘部坐标轴的蓝色方向。

总结与练习建议

本节课中,我们一起学习了Alice中人物不同关节(整体、膝盖、肘部)的旋转坐标系及其差异。每个关节都有其独特的朝向,导致相同的旋转指令(如“向右转”)会产生不同的视觉动作。

反复观看本演示视频非常有帮助。它将使你更熟悉不同关节的朝向差异,以及人物不同部位如何沿着看似不同的方向进行转向和滚动。掌握这些概念,是制作流畅、符合解剖学规律的角色动画的基础。

029:多种兔子跳跃方式 🐇

在本节课中,我们将学习如何创建新的指令或过程。我们将基于上一节课创建的常规三角跳,制作两种变体:一种更简单的跳跃,供后续课程使用;以及一种更真实、但更复杂的跳跃指令。

创建矩形跳 📐

上一节我们介绍了三角跳,本节中我们来看看如何创建一个矩形跳。在这种跳跃中,兔子将先向上移动,然后向前移动,最后向下移动。

以下是创建矩形跳的步骤:

  1. 点击屏幕顶部中央的六边形图标。
  2. 点击兔子对象,选择“添加兔子过程”。
  3. 将新过程命名为 rectangularHop。请注意,我们使用“驼峰命名法”:第一个单词首字母小写,后续单词首字母大写,且单词间无空格。这是一种编程惯例。
  4. 点击“确定”创建过程。
  5. 在过程内部,按顺序添加以下指令:
    • 兔子向上移动 1 个单位。
    • 兔子向前移动 0.5 个单位。
    • 兔子向下移动 1 个单位。

创建完成后,我们可以在主方法中测试它。将旧的 hop 指令拖入垃圾桶,然后从“this”菜单中选择“this.bunny”,再将新的 rectangularHop 指令拖入。运行程序,可以看到兔子以矩形轨迹跳跃。

创建真实跳 🏃

现在,让我们创建一个更逼真的跳跃动作。这个动作模仿滑雪跳台运动员,包含四个步骤。

以下是创建真实跳的步骤:

  1. 再次为兔子添加一个新过程,命名为 realisticHop
  2. 首先,我们需要一个故事板来规划动作:
    • 场景0:初始姿势。
    • 场景1:兔子身体前倾,呈起跳姿势。
    • 场景2:兔子腾空(类似三角跳)。
    • 场景3:兔子落地。
    • 场景4:兔子恢复直立姿势。
  3. realisticHop 过程中,首先添加一个“顺序执行”块。
  4. 在“顺序执行”块内,第一步是让兔子身体前倾。这里需要一个动画技巧:我们让兔子整体向前转 1/8 圈,同时让它的两只脚向后转 1/8 圈,这样脚就保持在原地,只有身体前倾了。
    • 添加一个“同时执行”块。
    • 在“同时执行”块内,添加指令:this.bunny turn forward 0.125 revolutions
    • 要操作兔子的脚,需要分别选择左脚和右脚。从“this”菜单中选择 bunny’s right foot,然后添加指令:turn backward 0.125 revolutions。对左脚重复此操作。
  5. 接下来,让兔子向上移动 0.5 个单位。由于身体已经前倾,此时“向上”移动会同时包含向前和向上的分量。
  6. 然后,让兔子向前移动 0.5 个单位,使其回到地面。
  7. 最后,让兔子恢复直立姿势。我们可以复制第一步的“同时执行”块,然后将其粘贴到末尾,并将其中所有的 forward 改为 backward,所有的 backward 改为 forward
  8. 为了让动作更利落,我们可以调整动画时长。默认是1秒,我们可以将其改为0.25秒。分别修改起跳和恢复姿势两组指令中每个动作的“持续时间”属性。
  9. 最后,添加注释说明代码块的功能,例如“兔子进入起跳姿势”和“兔子恢复站立姿势”。

现在,在主方法中用 realisticHop 替换掉 rectangularHop 后面的指令,并运行程序。你将看到兔子先做一个矩形跳,然后做一个更逼真的起跳、腾空、落地、恢复姿势的完整跳跃。

总结 📝

本节课中我们一起学习了如何创建自定义过程来扩展角色的行为。我们创建了两种新的跳跃方式:简单的矩形跳和复杂的真实跳。在创建真实跳的过程中,我们运用了“同时执行”块来实现复杂的协同动作,并通过调整动画时长和添加注释来优化代码的可读性与效果。这些技能是构建更复杂、更生动动画的基础。

032:参数概述 🎯

在本节课中,我们将学习如何通过参数使指令变得更加灵活,从而避免编写大量重复的代码。

指令的局限性

上一节我们介绍了基本的矩形跳跃指令。现在,我们来看看这个指令的局限性。

矩形跳跃指令总是让兔子向上跳跃1个单位,同时向前跳跃1个单位。虽然这个指令本身没有问题,但它可能无法满足所有场景的需求。

想象一下,兔子需要跳过障碍物。由于它总是向上跳跃1个单位,如果障碍物只有0.5个单位高,兔子就会浪费能量。反之,如果障碍物有2个单位高,在现实中兔子会撞上障碍物,但在Alice动画中,兔子会直接穿过障碍物,这看起来会很奇怪。

解决方案:参数化

为了解决这个问题,一个方法是编写三个不同的矩形跳跃指令,分别对应0.5、1和2个单位的高度。但这很笨拙,会导致大量代码重复。

更好的方法是创建一个单一的矩形跳跃指令,并允许我们向它传递信息,告诉它我们希望兔子跳多高。这样,指令就能根据我们提供的值让兔子跳跃相应的高度。

参数与参数值

Alice已经为一些基本指令(如moveturn)提供了这种自定义功能。例如,在move指令中,我们需要指定方向距离。在turn指令中,我们需要指定旋转方向圈数

我们可以将这种灵活性添加到我们自己创建的任何指令中。方法是通过参数参数值

  • 参数 是你在指令中定义的一个占位符,用于接收输入。它必须有名称类型(例如,数字或对象)。
  • 参数值 是你在调用指令时传递给参数的具体值。每次调用指令时,你都可以传递不同的值。

实践:为矩形跳跃添加参数

让我们通过一个例子来理解。原始的rectangularHop指令如下:

bunny move up 1
bunny move forward 1
bunny move down 1

它的跳跃高度固定为1个单位。

现在,我们添加一个参数,使兔子能跳跃不同的高度。

  1. 点击指令顶部的 “添加参数” 按钮。
  2. 为参数命名,例如 howHigh,它代表兔子跳跃的高度。
  3. 选择参数类型。由于跳跃高度是一个数字,我们将其定义为 “十进制数”

添加参数后,其类型和名称会显示在指令名称旁边。但此时我们还没有使用它,兔子仍然固定移动1个单位。

我们需要修改指令体,将向上移动和向下移动的“1”替换为我们的参数 howHigh。修改后的指令如下:

bunny move up howHigh
bunny move forward 1
bunny move down howHigh

现在,我们可以为 howHigh 传递任何值,兔子将向上移动该值,然后向下移动相同的值。

调用带参数的指令

添加参数后,之前调用rectangularHop的地方会显示为红色,提醒我们需要提供参数值。

例如,要让兔子跳过一只高约0.5的猫,我们可以传递参数值 0.5。要让兔子跳过一只高约2.0的野兔,我们可以传递参数值 2.0。这样,兔子两次跳跃的高度都刚好能越过障碍物。

使用参数可以让你编写更灵活、更少的指令。在这个例子中,我们只需要一个rectangularHop指令,就能应对不同高度的障碍物,而无需编写多个指令。

总结

本节课中,我们一起学习了参数的概念和作用。我们了解到,通过为指令添加参数,我们可以向指令传递信息,使其行为根据我们的需求发生变化。这避免了代码重复,极大地提高了代码的灵活性和可重用性。现在,你可以尝试为你自己的指令添加参数,享受创造更智能动画的乐趣吧!

033:跨越世界 🐰

在本节课中,我们将学习如何使用参数来使你的指令更加通用和有用。

概述

我们将通过一个场景来学习参数的概念。场景中有一只兔子、一只乌龟、一只猫和一只狗。它们的身高和位置各不相同。我们的目标是让兔子能够根据不同的障碍物高度,精确地跳跃过去。为了实现这一点,我们将修改一个跳跃指令,为其添加参数,让调用者可以指定跳跃的高度。

世界设定与初始问题

这个世界里有我们的兔子,以及一只乌龟、一只猫和一只狗。乌龟的高度是0.25个单位,猫的高度是0.5个单位,狗的高度是1个单位。它们大致排成一条直线。狗距离猫1个单位,猫距离乌龟1个单位。兔子被放置在猫和乌龟的正中间,也就是说,它距离猫0.5个单位,距离乌龟0.5个单位。

屏幕上已经包含了之前创建的跳跃方法。如果你点击屏幕左侧“this”一词右边的三角形,然后选择“this.bunny”,你可以点击“edit”来查看兔子跳跃的代码。

这个指令与之前创建的有两个不同之处。第一个不同是,我们将这个指令命名为hopHigh。这样做没有特别的原因,主要是为了强调我们将修改这个指令,使用一个参数来允许兔子进行不同高度的跳跃。第二个不同是,我们将每个动画指令的持续时间改为了0.5秒,而不是默认的1秒,这样动画会运行得更快一些。

让我们运行这个世界。

我们看到兔子首先转向猫,然后从猫身上跳了过去。但问题是,兔子跳得太高了。让我们再看一次,观察兔子跳得过高的情况。

原因是,在hopHigh过程中,我们要求兔子向上移动1个单位,然后再向下移动1个单位。但实际上,兔子只需要向上移动0.5个单位就能安全地跳过猫。如果兔子想跳过乌龟,它只需要向上移动0.25个单位。

添加第一个参数

让我们添加一个参数,允许调用者指定兔子需要向上(以及随后向下)移动的高度。为此,我们需要修改hopHigh过程。

我们点击屏幕顶部的大“Add parameter”按钮。在名称框中,我们输入参数名howHigh。请注意,我们使用驼峰命名法来命名参数。

我们还需要告诉Alice另一个非常重要的信息:参数将包含什么类型的信息,例如方向或数字。在这个例子中,我们希望高度是一个十进制数字。

所以我们点击“unset”一词右边的红色小三角形,它位于“value type”的右边。点击三角形后,我们选择“decimal number”。

因为我们之前编写这个过程时没有参数,所以还需要勾选“I understand that I need to update the invocations to this procedure”这个复选框。这意味着Alice在提醒我们,之前调用这个过程时没有参数,现在添加了参数,我们也需要更改调用方式,我们稍后会做。

点击复选框,然后点击框右下角的“OK”。

现在,我们有了howHigh参数,可以在hopHigh指令中使用了。让我们把这个参数拖到两个“1.0”的上方,这两个“1.0”指的是兔子向上和向下移动的距离。我们只需拖动参数覆盖“up”中的1.0,再拖动覆盖“down”中的1.0。这就是我们修改过程需要做的全部。

让我们回到myFirstMethod

现在你可以看到hopHigh旁边出现了一个红色方框,因为我们需要向hopHigh过程传递一个值,告诉兔子应该跳多高。通过点击“unset”一词右边的三角形,我们可以选择值0.5。

让我们再次运行我们的世界。兔子现在刚好跳过猫,没有浪费任何能量。

使用参数进行多次调用

让我们添加对hopHigh指令的第二次调用,让兔子也跳过狗。同样,我们可以点击“this.bunny”,然后拖入第二次hopHigh调用。这次,我们为howHigh指定值1,这样兔子就能以正确的高度跳过狗。

让我们再次运行世界。很好,兔子以正确的高度跳过猫,然后以更高的高度跳过狗。

添加多个参数

在本节课的第二部分,我们将看看如何拥有多个参数。在这个具体案例中,我们将编写一个新的过程,我们称之为hopOverTarget。对于这个过程,我们希望有两个参数:第一个是兔子要向上和向下移动的高度,第二个是兔子要跳过哪个对象。

让我们开始做吧。首先,我们需要简单地关闭正在运行的世界。接下来,我们点击上方的六边形,添加一个新的兔子过程。我们将其命名为hopOverTarget,同样使用驼峰命名法。

接下来,让我们添加两个参数。点击第一个“Add parameter”按钮。第一个参数命名为heightOfTarget,这和上次做的完全一样。和之前一样,我们将这个参数设为十进制数字,点击“OK”。

对于第二个参数,我们需要再次点击“Add parameter”按钮。我们称它为whichAnimal,其类型为SModel,因为兔子要跳过的所有动物都被Alice视为模型。将whichAnimal的类型设为Quadruped也可以,因为兔子要跳过的所有动物恰好都有四条腿。要设置类型,在点击“unset”一词右边的小黄色三角形后,我们必须去点击“gallery class”,然后在“gallery class”中,点击“SModel”,然后指定“OK”。我们再次指定“OK”以添加第二个参数whichAnimal,其类型为SModel

这个hopOverTarget过程只需要做两件事。首先,它必须让兔子转向面对目标动物。所以让我们拖入一个“do in order”指令,然后拖入一个“bunny turn to face”指令。我们将指定这个“bunny turn to face”的目标是我们的参数whichAnimal

我们需要做的第二件事是跳过那个动物。我们可以直接调用我们方便好用的hopHigh过程。向上滚动,我们可以直接调用hopHigh,并指定我们的参数heightOfTarget。就这样。

让我们回到myFirstMethod。在将其改为“this.bunny”之后,我们现在可以调用我们的新方法。让我们调用hopOverTarget三次。

以下是三次调用的具体参数:

  • 第一次调用:我们指定高度为1,并指定我们要跳过斑点狗。
  • 第二次调用:我们指定高度为0.5,并指定猫。
  • 第三次调用:我们指定高度为0.25,并指定乌龟。

现在当我们运行我们的世界时,我们看到之前指定的前两次跳跃,现在我们调用第二个方法,这样兔子就会转向正确的对象并以精确的高度跳跃。

总结

在本节课中,我们一起学习了如何使用参数来增强指令的灵活性。我们首先为hopHigh过程添加了一个高度参数,使兔子能根据障碍物调整跳跃高度。接着,我们创建了hopOverTarget过程,它接受两个参数:跳跃高度和目标对象,从而将转向和跳跃两个动作封装成一个更通用、更强大的指令。通过使用参数,我们避免了为每个特定情况编写重复代码,使程序更加简洁和易于维护。

035:摄像机控制 📹

在本节课中,我们将学习如何在Alice动画中控制摄像机。你将了解如何设置不同的摄像机视点,如何移动和旋转摄像机,以及如何使用摄像机标记来保存和调用特定的摄像机位置。掌握这些技巧,你就能像电影导演一样,为你的动画故事创造动态的视角。

概述:摄像机视点

在电影中,你有时会看到这样的场景:两个人正在交谈,突然传来一声巨响。摄像机通常会转向声音的方向,甚至向前移动,以便你能看清声音的来源。接着,摄像机可能会切换到人物的特写镜头,让你看到他们对声音的反应。摄像机所观察的场景被称为摄像机视点

本节中,我们将学习如何设置这样的摄像机视点,以便在动画中将摄像机移动到不同的位置。

使用Alice内置的二维视点

在设置场景时,Alice提供了特殊的二维内置视点,帮助你精确地放置世界中的物体。

以下是Alice提供的五个内置视点,你可以点击箭头在不同视点间切换:

  • 起始摄像机视点:这是你开始场景设置时看到的视点,也是进入场景设置前的默认视点。
  • 布局场景视点:这是一个从摄像机上方俯瞰的视点,可以看到世界的大部分区域。但请注意,在这个视点和起始视点中,我们都无法看到某些被遮挡的物体(例如之前提到的兔子)。
  • 二维俯视图:这是一个二维视图,物体只能朝四个方向(前、后、左、右)移动,而不能上下移动。这个视图有助于你将物体在地面上移动到理想位置。
  • 二维侧视图:这也是一个二维视图,物体只能朝四个方向(上、下、前、后)移动,不能左右移动。这个视图可以确保物体正好站在地面上,而不是略高于或低于地面。
  • 二维前视图:这同样是一个二维视图,物体只能朝四个方向(左、右、上、下)移动,不能前后移动。

这些内置视点能帮助你在程序开始时,将物体精确地放置在你想要的位置。

移动摄像机的方法

现在,我们来看看如何移动摄像机以改变观察世界的角度。

方法一:使用摄像机控制箭头

从起始摄像机视点,你可以看到一组紫色的箭头控制工具。直线箭头用于移动摄像机,曲线箭头用于旋转摄像机。

  • 最左侧的箭头组:全部是直线箭头,用于上下、左右移动摄像机。
  • 中间的箭头组:包含两个直线箭头和两个曲线箭头。直线箭头用于前后移动摄像机,曲线箭头用于左右旋转摄像机。
  • 最右侧的箭头组:全部是曲线箭头,用于前后倾斜(俯仰)摄像机。

你可以使用这些控制箭头,将摄像机移动和旋转到你希望在动画中使用的视点。

方法二:使用“单次指令”

你之前已经使用过“单次指令”在设置过程中移动物体。同样,你也可以用它来移动或旋转摄像机。以下是一些可用于摄像机的指令示例:

camera.move(forward, 1)
camera.turn(left, 0.25)

一个小提示:摄像机的起始位置总是略微向前倾斜。在移动或旋转摄像机之前,最好先使用 orient to upright(定向为直立)指令让它完全直立。这样,当你让摄像机向前移动时,它就不会钻到地下去。

你可以结合使用“单次指令”和摄像机控制箭头,将摄像机移动到世界中的另一个视点。

保存视点:摄像机标记

既然你已经知道如何在世界中移动摄像机,你可能会希望保存某个特定位置,以便在动画播放时将摄像机切换过去。

摄像机标记是一种特殊的对象,可以记住你世界中的一个摄像机位置。你可以通过将摄像机放置在你想要保存的位置,然后在该位置放置一个摄像机标记来创建它。

在场景设置中选中摄像机后,你会看到“摄像机标记”选项和一个“添加摄像机标记”的按钮。点击该按钮会创建一个看起来像摄像机(但颜色不同)的标记。你应该为摄像机标记起一个有意义的名称。

添加标记后,它们会出现在场景设置视图中(方便你查看位置),但在运行世界时不会显示。

摄像机标记有两个控制按钮:

  • 左侧按钮(黑色摄像机指向彩色摄像机):用于将摄像机移动到该标记的位置。这在设置物体或在动画中将摄像机切换到预设位置时非常有用。
  • 右侧按钮(彩色摄像机指向黑色摄像机):用于将摄像机标记移动到当前摄像机的位置。如果你决定将标记重置到另一个位置,这会很有用。

要使用这些按钮,你必须先点击摄像机标记的名称,然后再点击按钮。

你也可以使用“单次指令”将摄像机移动到标记处,使用的指令是 move toorient to。这样摄像机会移动到标记位置,并转向与标记相同的方向。

camera.moveToAndOrientTo(cameraMarker)

使用摄像机标记的技巧

以下是一些有助于你使用摄像机标记的技巧:

  1. 始终先放置起始标记:在移动摄像机之前,应该先放置一个摄像机标记,并将其命名为类似“camera start view”的名称。Alice世界中起始摄像机的位置很特殊,它代表了世界的中心,也是你点击添加物体时默认放置的位置,不要丢失这个位置。
  2. 善用撤销:如果点击了错误的摄像机控制按钮,可以点击“编辑”->“撤销”。撤销功能是你的好帮手。
  3. 处理标记遮挡:有时摄像机标记会遮挡住视图中的物体,使你难以点击和移动该物体。如果发生这种情况,你可以始终使用“单次指令”来移动物体。
  4. 先布置物体,后设置标记:最好先设置好所有物体,然后再添加摄像机标记。

在动画中使用摄像机标记

一旦设置好摄像机标记,你就可以在动画过程中将摄像机移动到它们的位置。

你会使用 move to and orient to 指令,并选择你想要移动到的摄像机标记。通过更改持续时间,你可以控制摄像机移动到新位置的速度。例如,持续时间为5,意味着摄像机需要5秒钟移动到新位置。

// 示例:摄像机在2秒内移动到“侧视图”标记
camera.moveToAndOrientTo(sideViewMarker, duration=2)

总结

本节课中,我们一起学习了关于摄像机的许多知识:如何移动它,如何设置摄像机标记来保存视点,以及如何通过移动物体和选择Alice的二维视图来设置场景。现在,你可以享受使用不同的摄像机视点来创作更生动、更具电影感的动画了。

036:摄像机控制演示第一部分 🎥

在本节课中,我们将学习如何使用爱丽丝(Alice)软件内置的摄像机视图来设置场景,并创建摄像机标记。这些标记可以作为预设位置,用于在动画中移动摄像机视角。

我们将构建一个包含多个动物的场景,并通过切换不同的摄像机视图来讲述一个小故事。故事中,一只猎鹰会坠落到地面,导致其他动物四散奔逃。

场景设置与故事板 📝

首先,我们通过故事板来规划动画。故事板是动画的视觉草图,用于展示场景中对象的位置和摄像机视角的变化。你不需要成为艺术家也能绘制故事板,关键在于清晰地展示对象在世界中的位置。

以下是故事板的第一个场景:

场景1 展示了动物和摄像机标记的初始设置。你需要在爱丽丝世界中添加一只熊猫、一只黑曜石猫和一只猎鹰。你还需要添加一只兔子,并将其隐藏在熊猫身后,使其面朝与熊猫相同的方向。

你将设置三个摄像机标记:

  • 一个是起始摄像机位置。
  • 一个是从侧面观察动物右侧的视角。
  • 一个是猎鹰的特写镜头。

场景2 展示了摄像机已移动到侧面视角。你可以看到兔子正躲在熊猫身后,而黑曜石猫部分被遮挡。

场景3 展示了摄像机已移动到猎鹰的特写视角。在此视图中,你看不到其他动物。

场景4 中动物的位置与场景1相同,但摄像机已移回起始位置。

场景5 包含动作:猎鹰将坠落到地面,同时另外三只动物会向四周散开。

构建世界与添加对象 🌍

现在,让我们开始构建这个世界。

  1. 打开一个新的爱丽丝世界,选择带有草地的沙漠地面。你可能需要向下滚动才能找到它。点击“确定”。
  2. 点击“设置场景”。
  3. 首先,我们将动物添加到世界中。进入“双足类”文件夹。
  4. 拖入一只兔子。你可能需要滚动才能看到它。
  5. 然后,拖入一只熊猫。在爱丽丝中,熊猫被艺术家设计为用两条腿站立的双足模型。
  6. 接下来,进入“四足类”文件夹。你需要点击“所有类别”旁边的箭头并选择“四足类”。
  7. 添加一只黑曜石猫。四足类模型被设计为用四条腿站立。
  8. 最后,再次点击“所有类别”旁边的箭头,选择“鸟类”类别,添加一只猎鹰。

调整对象位置 ✨

由于猎鹰将位于动物上方,我们先对它进行一次性调整。

  1. 展开猎鹰的翅膀:选择“展开翅膀”。
  2. 将猎鹰移动到空中:选择“移动” -> “上” -> “自定义小数”,输入 1.3。这应该能将其精确地置于所有动物的正上方。

现在,按照故事板场景1的布局摆放动物。

  1. 将兔子放在中间偏左的位置。
  2. 将熊猫放在兔子正前方,完全挡住兔子。
  3. 将黑曜石猫放在熊猫旁边。
  4. 将猎鹰置于所有动物上方。

使用内置2D视图精确定位 🔍

我们如何确保猫确实站在熊猫旁边?兔子是否真的在熊猫正后方?猎鹰是否在所有动物正上方?为了精确定位,我们将使用爱丽丝内置的2D视图。

页面顶部的当前摄像机视图称为“起始摄像机视图”。点击旁边的按钮,你会看到其他四个选项。

以下是各个视图的功能:

  • 侧视图:从左侧观察所有动物。在此2D视图中,动物只能在两个维度上移动(上下、前后)。你可以拖动背景使动物居中显示。确保兔子在熊猫正后方且脚踩在地面上(即屏幕上看到的线上)。同时,将猎鹰向后移动,直到它位于熊猫和兔子的正上方。
  • 顶视图:从上方观察场景。在此2D视图中,动物只能在两个维度上移动(前后、左右)。你可以使用右下角的摄像机控制按钮进行缩放。确保黑曜石猫放回熊猫旁边。
  • 前视图:从正面观察场景。在此2D视图中,动物只能在两个维度上移动(上下、左右)。再次确保黑曜石猫放回地面上并位于熊猫旁边。
  • 布局场景视图:提供一个从正面但更高位置的视角。

检查完毕后,将视图切换回“起始摄像机视图”。现在,你的动物应该已按照预期精确排列,并且看不到兔子。

总结 📚

本节课中,我们一起学习了如何利用爱丽丝的多种内置摄像机视图来精确设置场景。我们了解了故事板的作用,并实际添加和摆放了多个动物对象。通过使用侧视图、顶视图和前视图这三种2D视图,我们能够将对象限制在特定维度上进行移动,从而确保它们被精确地放置在预期位置,为后续的动画制作打下了坚实的基础。

037:摄像机控制演示第二部分 🎥

在本节课中,我们将学习如何在Alice中设置和使用摄像机标记,以创建和切换不同的摄像机视角,并编写代码来控制摄像机移动,从而制作更生动的动画。

概述

摄像机标记类似于三脚架,它代表一个固定的摄像机位置。通过设置多个摄像机标记,我们可以在动画的不同阶段快速切换视角。初始的摄像机视图是特殊的,因为它正对着Alice世界的中心。在移动摄像机之前,保存这个初始位置至关重要。

保存初始摄像机视图

在移动摄像机之前,我们需要保存这个特殊的初始位置。具体操作如下:

  1. 在界面右侧,选择摄像机作为当前对象。
  2. 点击“摄像机标记”选项以展开菜单。
  3. 点击“添加摄像机标记”。
  4. 将标记重命名为一个有意义的名称,例如 cameraStart
  5. 点击“确定”。

完成上述步骤后,摄像机标记就被添加了。现在,即使移动了摄像机,我们也能轻松回到初始视图。

使用摄像机控制移动

现在,让我们使用窗口底部的摄像机控制箭头来实验移动摄像机。

以下是各个控制按钮的功能:

  • 最左侧的控制组:控制摄像机上下移动。
  • 中间的控制组:控制摄像机前后移动。
  • 右侧的两个控制组:控制摄像机旋转(转向)。

设置第二个摄像机视图

接下来,我们将设置一个从侧面观察动物的视角。我们将结合使用“一键操作”和摄像机控制来完成。

  1. 首先,使用“一键操作”将摄像机移动到猎鹰旁边:选择“移动到” -> falcon
  2. 接着,使用“一键操作”让摄像机“朝向直立”,这会使摄像机摆正。
  3. 然后,将摄像机向左移动10个单位,使其位于动物们的左侧。
  4. 最后,使用摄像机控制中的右转箭头,将摄像机转向右侧,直到获得满意的动物侧视图。
  5. 在此视角下,添加一个新的摄像机标记,命名为 cameraSideView

在摄像机标记间切换

创建了两个摄像机视图后,我们可以使用摄像机标记控件在它们之间移动。

  1. 点击 cameraStart 视图。
  2. 点击左侧的按钮(图标是一个黑色摄像机指向红色摄像机),摄像机将移动到 cameraStart 标记的位置。
  3. 点击 cameraSideView 视图。
  4. 再次点击左侧按钮,摄像机将移动到 cameraSideView 标记的位置。

如果对某个视角不满意,可以调整摄像机位置,然后使用右侧的按钮(图标是红色摄像机指向黑色摄像机)来更新该标记的位置。记得之后要测试一下更新是否成功。

设置第三个特写视图

现在,我们将设置第三个视图:猎鹰的特写。

  1. 使用摄像机控制或“一键操作”获得一个满意的猎鹰特写镜头。
  2. 在此视角下,添加一个新的摄像机标记,命名为 cameraFalconView

现在,我们可以通过点击标记和左侧按钮,在三个视图(cameraStartcameraSideViewcameraFalconView)之间流畅切换。

重要提示:在切换视图时,请确保点击的是左侧的“移动到标记”按钮,而不是右侧的“移动标记到摄像机”按钮。如果不小心点错,可以使用“编辑”菜单中的“撤销”功能来恢复。

在代码中使用摄像机移动

上一节我们设置了多个摄像机视角,本节中我们来看看如何在编写代码时使用它们。点击“编辑代码”进入编码窗口。

我们可以使用 move and orient to 命令让摄像机一次性移动并转向到某个标记。

例如,让熊猫说话后,将摄像机切换到侧视图:

panda.say(“Who is behind me”, duration=2)
camera.moveAndOrientTo(cameraSideView)

我们也可以将 move toorient to 两个命令分开执行,或者将它们放入一个 do together 块中同时执行,其效果与 move and orient to 相同。

让我们完成一个小故事:

  1. 熊猫说:“谁在我后面?”
  2. 摄像机切换到侧视图。
  3. 兔子说:“谁在我上面?”
  4. 摄像机先移动到猎鹰特写视图,再转向它(模拟 move and orient to 的分步效果)。
  5. 猎鹰说:“准备降落。”
  6. 摄像机移回初始视图。
  7. 在一个 do together 块中,让所有动物同时移动:猎鹰下降,熊猫右移,猫左移,兔子后移。

播放世界,查看完整的动画效果。

总结

本节课中,我们一起学习了Alice中摄像机控制的核心功能。我们掌握了如何设置和保存摄像机标记,如何在不同的预设视角间切换,以及如何在代码中通过 move and orient to 等命令控制摄像机的移动和旋转。这些技巧能帮助你为动画故事创建更专业、更多样化的镜头语言。

039:继承概述 🧬

在本节课中,我们将学习继承的概念,以及继承对于创建方法或过程的意义。

面向对象编程在当今非常流行。像Alice、C++、Java和Python这样的计算机编程语言都被称为面向对象语言。这意味着这些语言都支持一种特定的编程风格。

面向对象编程可能相当复杂。事实上,在面向对象编程被发现后的头二三十年里,世界上大多数程序员都不完全理解它能做什么。在本课程中,我们将逐步介绍面向对象编程的许多不同方面。今天,我们从面向对象编程对过程和过程创建的影响开始。

术语:类与对象

首先,我们需要了解一些基本术语。让我们从类和对象开始。

一个是一个模板,它描述了该类实例化对象的行为。而一个对象是一个类的实例。

我认为通过一个类比可以更好地理解这一点。几年前,我和妻子需要买一辆家用车。我们住在美国一个冬季天气相当糟糕的地区,所以我们想要一辆配备防抱死刹车和全轮驱动的汽车。

我们考虑了几种类型的汽车。我认为宝马X3会是一个很好的车型,但我妻子指出它们太贵了,而且随着孩子们长大,在他们学车时,这种车对他们来说可能难以驾驭。然后我建议了保时捷卡宴,我妻子说那也太贵了,对我们的孩子来说同样难以驾驭。最后我建议了斯巴鲁傲虎,这似乎是一个适合我们家庭的实用车型,我妻子同意了。

终于到了试驾傲虎的时候。我们有一本关于傲虎的漂亮宣传册,它描述了傲虎的特点。但我们必须去试驾一辆具体的斯巴鲁傲虎,也就是傲虎类的一个实例。当我们买下一辆时,我女儿给它起名叫“丑丑”,因为她不喜欢它的绿色,也不想让她的朋友们看到她坐在这么“丑”的车里。

Alice与此非常相似。让我们看一个兔子类和兔子对象的例子。

所有的3D模型都是类。兔子类是对兔子的描述,而不是一只真正的兔子。兔子类可以在Alice的图库中找到。当你想要向你的世界添加一只兔子时,你实际上是在向你的世界添加兔子类的一个实例。添加到世界中的这只兔子也被称为一个对象。幸运的是,在Alice中,你不需要为你添加的对象付费。而汽车经销商对我们购买的斯巴鲁傲虎可就没那么慷慨了。

类层次结构与继承

编程中对象和类的部分强大之处在于,类不是扁平的,而是存在于一个层次结构中。这意味着子类继承了超类的所有功能。同样,通过一个类比会更容易理解。

以汽车为例。斯巴鲁是一个超类的例子。所有的斯巴鲁汽车都有全轮驱动和防抱死刹车。傲虎类继承自斯巴鲁。这意味着所有的傲虎都有全轮驱动和防抱死刹车,但傲虎还有一些其他斯巴鲁车型没有的额外功能。例如,所有的傲虎都有车顶行李架和电动后尾门。

Alice是类似的。双足动物是一个超类,而兔子类是一个子类。Alice的创建者决定兔子应该用两条腿走路,属于双足动物,而不是像四足动物那样用四条腿奔跑。这意味着兔子天生就能做任何双足动物能做的事情。但我们还可以给兔子额外的能力,比如抽动它的鼻子或跳跃。

继承对方法创建的影响

那么,这一切意味着什么呢?如果你有一个像Biped这样的超类,并为它编写了一条指令,那么每一个双足动物都将能够使用那条指令。也就是说,Biped是一个超类。BunnyBiped的一个子类。如果你将hop写成一个Biped的指令,那么兔子就会知道如何跳跃,而且熊猫、金丝猴以及任何其他双足动物也都会知道如何跳跃。

另一方面,如果你决定将跳跃指令写成子类的指令,比如你将其写成一个Bunny的指令,那么只有兔子会知道如何跳跃。

到目前为止,我们创建过程时,一直在创建兔子过程。这意味着我们世界中的任何人都可以调用这些跳跃过程。但我们的世界里只有一只兔子。确实如此,如果我们添加了第二只兔子,那只兔子也能自动跳跃。但其他双足动物将不知道如何跳跃。也就是说,它们不能使用我们为兔子编写的跳跃过程。

如果我们查看Alice中的一些例子,这一切就会变得清晰。我们将首先看到一个兔子跳跃过程,然后看到一个双足动物跳跃过程。我们会看到所有双足动物都能跳跃,但只有兔子能进行特定的“hop”动作。


在本节课中,我们一起学习了面向对象编程中对象的基本概念,以及继承的核心机制。我们了解到,子类会继承超类的所有功能,这影响了我们编写方法的方式:为超类编写的方法,其所有子类都可以使用;而为特定子类编写的方法,则只有该子类的对象能够调用。理解这些概念是掌握面向对象编程的重要一步。

040:双足动物的继承跳跃 🐰🐱

在本节课中,我们将学习如何利用继承的概念,为双足动物(Biped)这一父类创建通用的跳跃方法,从而让不同子类(如兔子和柴郡猫)都能执行跳跃动作。

场景设置与观察

这个世界包含一只兔子和一只柴郡猫。柴郡猫站在兔子前方半个单位处,并且面朝同一个方向。我们将兔子的高度设置为半个单位,柴郡猫的高度设置为一个单位。

我们可以通过查看不同的摄像机视角来确认这些设置。点击“场景设置”,然后滚动到摄像机标记处。我们创建了三个摄像机标记。

以下是不同视角的观察结果:

  • 左视图:显示兔子直接站在柴郡猫后方,兔子的高度大约是柴郡猫的一半。
  • 右视图:显示柴郡猫站在兔子前方。由于柴郡猫更高,兔子被它完全挡住了。

让我们点击“编辑代码”以退出场景设置模式。

现有方法的局限性

在这个世界中,我们为兔子类创建了一个名为 rectangularHop 的方法。在 myFirstMethod 中,我们可以让兔子执行这个矩形跳跃。

然而,如果我们尝试让柴郡猫也执行 rectangularHop,会发现它无法做到,因为它不是兔子类的实例。这说明,在特定子类(如兔子)中创建的方法,无法被其他类(如柴郡猫)使用。

创建通用的双足动物方法

为了能让兔子和柴郡猫都能跳跃,我们需要在它们的父类——双足动物(Biped) 级别上创建方法。

以下是创建通用 leapfrog 方法的步骤:

  1. 点击屏幕顶部的六边形图标。
  2. 选择“Biped”,然后点击“添加Biped过程”。
  3. 将新过程命名为 leapfrog(注意驼峰命名法)。
  4. 为此方法添加一个参数,命名为 howHigh,类型设置为“十进制数”。

现在,我们来定义 leapfrog 方法的具体指令。它包含三个步骤:

  • 向上移动:让双足动物向上移动 howHigh 个单位。
  • 向前移动:让双足动物向前移动1个单位。
  • 向下移动:让双足动物向下移动 howHigh 个单位。

通过将 leapfrog 创建为双足动物的方法,兔子和柴郡猫就都能执行这个跳跃动作了。兔子需要向上跳1个单位,而柴郡猫只需要跳0.5个单位。

实现交替跳跃动画

现在,我们回到 myFirstMethod 中,让两个角色交替跳跃三次。

以下是实现交替跳跃的代码序列:

  1. 兔子执行 leapfrog,高度为1米。
  2. 柴郡猫执行 leapfrog,高度为0.5米。
  3. 重复上述两步,再执行两次。

运行世界,可以看到兔子和猫以各自合适的高度准确地轮流跳过对方。

添加多视角切换

为了让动画更生动,我们可以在跳跃过程中切换摄像机视角。

以下是添加摄像机切换的步骤:

  • 在第一对跳跃指令之后,让摄像机移动并定位到“左视图”。
  • 在第二对跳跃指令之后,让摄像机移动并定位到“右视图”。

这样,我们就能从三个不同的有趣视角来观看整个跳跃动画:起始的正面视角、从后方观察的左视图,以及从另一侧观察的右视图。

总结

本节课中,我们一起学习了方法创建的层级重要性。在子类(如兔子)级别创建方法,只允许该类的所有实例使用。而在父类(如双足动物)级别创建方法,则允许该父类下的所有子类(如兔子和柴郡猫)共享和使用此方法。这体现了面向对象编程中继承的核心优势,提高了代码的复用性和效率。

042:课程概述 🚀

在本节课中,我们将要学习第四周的核心内容。本周是课程中内容最丰富的一周,我们将显著扩展使用Alice所能讲述的故事类型。

本周内容概览 📚

我们将从学习Alice的一系列实用功能开始,这些功能不仅在本周,更会在后续课程中持续使用。

内置函数

首先,我们将介绍Alice的内置函数。函数用于计算数值,并可以替代硬编码的数字。例如,我们可以让一只鸟向上移动大象的高度。如果我们希望鸟能飞到象的顶部,这个功能就特别有用。

以下是几个关键的内置函数示例:

  • object.height:获取对象的高度。
  • object.distance to:计算两个对象之间的距离。

数学运算与动画功能

上一节我们介绍了函数,本节中我们来看看如何将简单的数学运算(如加法和减法)融入到Alice中。

接着,我们将探索几个有用的Alice动画特性:as seen byvehicle属性。

  • as seen by:允许你以某个对象的视角来执行命令,例如围绕另一个对象旋转。
  • vehicle属性:如果你将一个人放入一辆车,并将此人的vehicle属性设置为这辆车,那么当车移动时,人也会随之移动。

对象属性:颜色与透明度

然后,我们将学习对象的两个极其有用的属性:绘制颜色不透明度

  • 绘制颜色:你可以通过为对象绘制特定颜色来改变其色调。
  • 不透明度:指对象的透明程度。通过将不透明度设置为0,我们可以使一个对象变得完全不可见。

相机标记与随机数

最后,对于在使用常规相机标记时遇到困难的学员,我们将学习如何使用不可见对象作为替代的相机标记。

在掌握了这些基础知识后,我们将了解Alice如何生成随机数。随机数对于制作游戏至关重要,它们能让游戏每次玩起来都不同,从而增加趣味性。目前,我们将把随机数与常量一起使用。

核心编程结构 🔧

在本周的后半部分,我们将学习三个非常重要的编程结构:if语句、计数循环(通常称为for循环)以及while循环(或称不定循环)。

  • if语句:允许在某个条件(例如,白兔是否靠近疯帽子)为真时运行一组动画指令,而在条件为假时运行另一组不同的指令。
  • 两种循环for循环和while循环用于连续多次运行一组动画指令。

角色复用:面向对象编程的优势 💾

最后,我们将学习Alice中一个非常实用的能力:保存并复用角色。我们可以将一个Alice程序中正在使用的角色,连同为该角色编写的所有过程和函数一起保存,然后在另一个程序中重复使用。

例如,一旦我们在一个Alice程序中教会了一只兔子跳跃,我们就希望能在其他程序中使用这只会跳的兔子,因为跳跃是所有兔子都应该会做的事情。这个功能为我们节省了大量拖放操作的时间和精力,这也是使用面向对象编程的一个重要优势。

总结 ✨

本节课中我们一起学习了第四周的课程概述。本周内容非常丰富,涵盖了从Alice的实用功能(如内置函数、对象属性、随机数)到核心编程结构(如条件判断和循环),最后还介绍了角色复用的强大功能。这些知识将极大地增强我们使用Alice创作复杂、动态动画和交互式故事的能力。让我们立刻开始学习吧!

043:数学、环绕与属性 🎬

在本节课中,我们将介绍几个计算与动画主题,包括爱丽丝的内置函数、数学运算、环绕动画技术以及对象属性。这些概念是创建更复杂动画的基础。

内置函数介绍 🧮

上一节我们概述了本课内容,本节中我们来看看第一个核心概念:内置函数。函数会计算一个值,并将该值返回以供使用。

例如,假设你想让一只兔子跳过一头牛。兔子需要知道牛的确切高度,以便确定需要跳多高。爱丽丝可以计算牛的高度并返回该数值,这样你就能编写代码告诉兔子需要跳多高才能跳过牛。

爱丽丝还能计算对象的宽度和深度(或长度)。这些也都是数值。

在爱丽丝中,每个对象都关联着一个“函数”选项卡,其中包含许多爱丽丝可以计算的关于该对象的函数或值。有些函数返回布尔值(如两个对象是否碰撞),有些则返回对象的属性(如颜色)。许多函数计算的是数值,例如一个对象到另一个对象的距离,或对象的高度。

对于返回数值的函数,你可以在任何需要数字的指令中使用它们。

假设我们有一条指令:Bunny.move(up, 1)。我们可以将函数 cow.getHeight(这是一个代表牛高度的数值)拖拽过来,覆盖数字 1

现在指令变为:Bunny.move(up, cow.getHeight)。这样,兔子就会向上移动恰好等于牛高度的距离。

假设我们想让兔子移动到柴郡猫面前,即兔子和柴郡猫之间的精确距离。兔子正对着柴郡猫的右侧,所以它会向前移动,停在柴郡猫右臂的正前方。我们不知道兔子应该向前移动多远,所以先任意选一个数字,比如 1。我们需要一个函数来计算兔子和柴郡猫之间的精确距离。

以下是其他一些返回数值的函数。其中任何一个都可以在指令中替代数字使用。

我们可以调用兔子对象的函数 getDistanceTo(right of),来获取兔子到柴郡猫右侧的精确距离。

我们可以用这个函数替换 move(forward) 指令中的 1,让兔子移动恰好等于它到柴郡猫右侧的距离。

你将在其他视频中了解更多关于爱丽丝内置函数的内容。

数学运算 ➕

假设一只黑猫坐在餐桌上。为了让兔子跳过它们两个,兔子需要向上跳起的高度等于餐桌的高度加上黑猫的高度。我们如何实现这一点?我们需要用到数学。

爱丽丝中的数学运算允许我们将数字相加,然后在指令中使用这些数字的和。

让我们看看让兔子跳过餐桌和猫的代码。

首先,用一个 bunny.move(up, ...) 指令,暂时任意选一个数字,比如 1。由于兔子应该向上移动餐桌的高度,我们用函数 diningTable.getHeight 替换 1

然后,我们使用数学运算来加上另一个数字。我再次选择了 1

最后,我们可以用获取黑猫高度的函数替换第二个数字。

现在,兔子将向上移动的距离等于餐桌高度加上黑猫高度的总和。

你将在我们的其他视频中了解更多关于在爱丽丝中使用数学运算的内容。

环绕动画技术 🔄

现在,假设我们有一只鸵鸟和一只火烈鸟,鸵鸟想要绕着火烈鸟转一圈。我们该如何实现?

我们将在爱丽丝中讨论两种实现方法。仔细想想,环绕一个对象其实就是同时执行两个爱丽丝指令:向前移动和转向,这样向前移动的路径就会弯曲成弧形。

但是,如果鸵鸟想绕火烈鸟完整转一圈,它应该向前移动多远呢?

鸵鸟必须移动的距离称为圆的周长。计算圆周长的公式是:2 * π * 圆的半径。而圆的半径就是鸵鸟到火烈鸟的距离。

还有一种更简单的方法。turn 命令有一个名为 as seen by 的选项,它允许一个对象围绕另一个指定对象旋转。

我们将在其他视频中探索这两种实现环绕对象的方法。

对象属性 🎨

我们将介绍的最后一个主题是属性。在爱丽丝中,每个对象都有属性,这些属性使其区别于其他对象。以兔子对象为例,我在爱丽丝中放入了两只兔子,你可以看到它们截然不同。

白色的那只名叫 Bunny,红色的那只名叫 Bunny2。你可能还注意到 Bunny2Bunny 更高。

在场景设置视图中,你可以检查对象的属性。以下是 Bunny 的属性。

你可以看到,白色的 Bunny,其 paint(颜色)属性设置为 white。你还可以看到它的 height(高度)属性是 0.59

对于名为 Bunny2 的红色兔子对象,你可以看到它的 paint 属性设置为 red,并且它的高度更高,为 0.83

属性可以在场景设置期间设定,也可以在动画过程中通过代码更改。

这里你可以看到我们将要讨论的其他一些属性:opacity(不透明度)和 vehicle(载体)。

opacity 属性是对象的透明程度,即对象有多少是可见的。默认的不透明度值是 1,这意味着你无法看穿该对象,技术上来说,它是完全不透明的。

如果你将不透明度设置为 0.5,对象将部分透明,像幽灵一样。

如果你将不透明度设置为 0,那么对象将完全透明,即不可见。

这个属性可以在设置场景时使用,用来隐藏你世界中那些希望在动画过程中才变得可见的对象。

接下来,我们将讨论 vehicle 属性。vehicle 属性与其他属性略有不同,因为除非我们运行一些动画来检查,否则无法知道它的值是什么。换句话说,vehicle 属性不像颜色、不透明度和高度属性那样静态可见。

vehicle 属性允许你为对象指定一个特定的载体。

让我们看看如何使用对象的 vehicle 属性让对象“乘坐”在一个载体上。这意味着当载体移动时,乘坐其中的对象会自动随之移动,就像它正乘坐其中一样。

假设疯帽匠坐在皮卡车的驾驶座上。将疯帽匠的 vehicle 属性设置为这辆皮卡车是合理的。

然后,当皮卡车移动时,疯帽匠会随皮卡车一起移动。

在某些时候,你可能不再希望皮卡车是疯帽匠的载体,也许疯帽匠已经下车了。你可以将疯帽匠的 vehicle 属性重置为 this,这意味着它被设置为其自身。

然后,当皮卡车移动时,疯帽匠将不再随之移动。

你将在后续的几个视频中了解更多关于所有这些新主题的细节。

总结 📝

本节课中我们一起学习了爱丽丝编程的几个核心概念。我们介绍了内置函数,它们可以计算并返回值,例如对象的高度或距离。我们探讨了如何使用数学运算将数值相加,以计算复合距离。我们了解了实现对象环绕运动的两种思路。最后,我们学习了对象属性,如颜色、高度、不透明度和载体,这些属性定义了对象的特征并可以在动画中被操控。掌握这些基础将帮助你创建更动态和精确的动画场景。

044:内置函数与数学运算 🧮

在本节课中,我们将学习如何使用爱丽丝的内置函数以及数学运算,让角色精确地移动和跳跃。

我们从一个包含兔子、柴郡猫和奶牛的世界开始。兔子和柴郡猫都面向摄像机,奶牛则侧身面向柴郡猫。我们的目标是编写一个动画:让兔子转向柴郡猫,跳过它,然后跳过奶牛。

准备工作与初始场景

上一节我们介绍了动画的基本概念,本节中我们来看看如何利用函数进行精确计算。

在场景设置模式下,从侧视图可以看到所有动物排成一条直线。回到代码模式,我们已经为兔子编写了一个名为 hop 的矩形跳跃过程。这个过程有两个参数:howHigh(跳跃高度)和 howFar(向前移动距离)。其核心逻辑是:兔子向上跳起,向前移动,然后落回地面。

让兔子跳向柴郡猫

首先,我们为整个故事添加一个顺序执行结构。

以下是第一步的详细指令:

  • 添加注释:“兔子跳过柴郡猫”。
  • 让兔子转向面对柴郡猫:bunny.turnToFace(cheshireCat)
  • 让兔子移动到柴郡猫面前。我们不知道具体距离,但爱丽丝可以计算。我们先输入一个占位数字 1,然后将其替换为函数 bunny.getDistanceToRightOf(cheshireCat)。这个函数能精确计算出兔子到柴郡猫右侧的距离。

运行世界,兔子会精确地移动到柴郡猫面前。

精确跳过柴郡猫

接下来,我们调用 hop 过程让兔子跳过柴郡猫。对于跳跃高度和距离,我们再次使用占位数字 1

以下是替换占位符的步骤:

  • 跳跃高度应等于柴郡猫的高度,因此用函数 cheshireCat.getHeight 替换第一个 1
  • 跳跃距离应至少等于柴郡猫的宽度。但第一次尝试后发现兔子跳得不够远。这是因为兔子需要跳过的是“柴郡猫的宽度 + 兔子自身的深度”。

我们可以用数学运算来修正。点击 cheshireCat.getWidth 旁边的下拉箭头,选择 数学运算 -> 加法,会生成 cheshireCat.getWidth + 1 的结构。然后,将加号后面的 1 替换为函数 bunny.getDepth

现在,跳跃距离的公式是:cheshireCat.getWidth + bunny.getDepth。再次运行,兔子成功跳过了柴郡猫。

让兔子跳向并跳过奶牛

现在,我们添加第二部分:兔子跳过奶牛。

以下是实现步骤:

  • 添加注释:“兔子跳过奶牛”。注意,此时兔子已经面向奶牛。
  • 让兔子移动到奶牛面前。使用函数 bunny.getDistanceInFrontOf(cow) 来获取精确距离,替换移动指令中的占位数字。
  • 让兔子跳过奶牛。再次调用 hop 过程,并用占位数字 1 初始化参数。
  • 跳跃高度应等于奶牛的高度,用函数 cow.getHeight 替换第一个 1
  • 跳跃距离应等于“奶牛的深度 + 兔子的深度”。我们使用数学运算:先选择 cow.getDepth + 1,然后将加号后的 1 替换为 bunny.getDepth

现在,跳跃距离的公式是:cow.getDepth + bunny.getDepth。运行世界,兔子会先跳过柴郡猫,移动到奶牛面前,然后成功地跳过奶牛。

总结

本节课中我们一起学习了爱丽丝内置函数与数学运算的应用。函数帮助我们计算出精确的距离、高度和宽度,从而让角色(如兔子)能够准确地移动到目标位置并完成跳跃动作。数学运算(如加法)则允许我们将多个测量值组合起来,以满足更复杂的移动需求。

请记住:任何可以计算出一个数字的函数,都可以用来替换指令中的具体数字。爱丽丝提供了大量内置函数,你可以灵活运用它们来创建更精确、更生动的动画。

045:环绕对象运动

在本节课中,我们将探索如何让一个对象围绕另一个对象做圆周运动。

我们将从一个包含火烈鸟和其左侧鸵鸟的世界开始。两只鸟都面向摄像机。我们的目标是让鸵鸟围绕火烈鸟转圈。

我们将探索如何使用转向指令来实现这一效果。

初始场景与转向指令

世界场景中已有一些指令。火烈鸟说:“让我们探索转向。”接着,鸵鸟会执行向右转一整圈,然后向左转一整圈。

让我们运行这个世界。我们会看到,转向指令使鸵鸟在原地旋转,无论是向右还是向左。这是我们已知的。

那么,如何让鸵鸟围绕火烈鸟转圈呢?

实现圆周运动的思路

一种方法是让鸵鸟向前移动一点,然后转向一点,并重复多次。但这会形成一个非常不规则的圆形,并且实现起来很麻烦。

更好的方法是让鸵鸟围绕火烈鸟平滑地移动成一个圆形。

我们可以尝试让鸵鸟同时转向和向前移动。当鸵鸟向前移动时,同时进行的转向会使它持续地轻微改变方向,从而使其沿圆形路径移动。

以下是实现这一想法的步骤。

方法一:使用“同时执行”指令

首先,我们添加一个注释:“使用‘同时执行’指令实现转向”。

然后,我们添加一个 do together(同时执行)指令块。在其中放入鸵鸟的指令:选择鸵鸟,让它向右1圈。选择向右转的原因是,当鸵鸟围绕火烈鸟移动时,火烈鸟将始终位于鸵鸟的右侧。

接着,在 do together 指令块中添加鸵鸟向前移动5米的指令。这里的“5”是鸵鸟需要移动的总距离。

我们将这两个指令的持续时间都设为2秒,以便鸵鸟有更充足的时间完成圆周运动。

运行世界。在鸵鸟完成原地旋转后,它会完成一个完整的圆圈。因为我们让它转了一整圈(360度),并且恰好移动了5米,这意味着鸵鸟所画圆的周长是5米。周长就是沿圆形轨迹移动的总距离。

然而,这个圆非常小,鸵鸟并没有真正围绕火烈鸟。我们需要鸵鸟移动得更远一些。

调整圆周大小

我们复制刚才的 do together 指令块,并添加注释:“绘制一个更大的圆”。

在新复制的指令块中,我们只需将向前移动的距离从 5 改为 12

再次运行世界。鸵鸟会先原地旋转,然后画一个小圆,接着画一个更大的圆。现在鸵鸟移动的周长是12米,我们很幸运地让它刚好绕过了火烈鸟。

但这仍然不是我们想要的精确效果。我们希望鸵鸟以它和火烈鸟之间的距离半径来画圆。为此,我们需要使用 distance to 函数,并计算相应的周长。

方法二:结合数学公式计算周长

圆的周长公式是:
周长 = 2 × π × 半径

其中,π 是一个常数,约等于3.14。

这个公式计算出的周长,就是鸵鸟需要移动的确切距离。

我们添加注释:“使用数学计算实现转向”。

再次复制上一个 do together 指令块。这次,我们需要修改向前移动指令中的距离。

将其改为:2 × 3.14 × (鸵鸟到火烈鸟的距离)

在Alice中的操作步骤是:

  1. 将距离值改为 2
  2. 点击 2 旁边的下拉箭头,选择 数学运算 -> ×(乘号)。
  3. 接着输入 3.14
  4. 点击 3.14 旁边的下拉箭头,再次选择 数学运算 -> ×
  5. 此时需要一个占位数字(例如 1),然后我们用函数替换它。
  6. 点击 函数,找到并选择 鸵鸟 获取到 [其他对象] 的距离,将其拖拽覆盖掉占位数字 1
  7. 在弹出的对象选择框中,选择 火烈鸟

现在,向前移动的距离就变成了 2 * 3.14 * ostrich get distance to flamingo

运行世界。现在,鸵鸟会依次画出周长为5米、12米以及精确计算出的圆周长的圆。效果看起来很棒。

方法三:使用“以…为参照”选项

Alice提供了一个更简单的方法来实现这个效果,那就是在转向指令中使用 as seen by(以…为参照)选项。

我们添加一个新注释:“使用‘以…为参照’选项实现转向”。

然后,为鸵鸟添加一个转向指令:选择向右1圈,持续时间为2秒。

接着,点击 添加细节,这次选择 as seen by(以…为参照),并选择火烈鸟

运行世界。鸵鸟会依次执行前几种方法画出的圆,最后使用 as seen by 方法画出的圆,效果与使用数学公式计算出的完全一样,但指令却简单得多。

请注意,我们让鸵鸟以火烈鸟为参照向右转。这是正确的,因为当鸵鸟围绕火烈鸟转圈时,火烈鸟始终在鸵鸟的右侧。

让我们看看如果改为向左转会怎样。复制刚才的指令,将向右改为向左

再次运行世界。鸵鸟会先向右转画圆,然后向左转画圆。使用向左转时,鸵鸟会沿相反方向(逆时针)绕圈。

总结

本节课中,我们一起学习了在Alice中实现对象环绕运动的三种方法:

  1. 使用 do together 指令组合转向和移动。
  2. 结合数学公式 周长 = 2 × π × 半径 精确计算移动距离。
  3. 利用转向指令中的 as seen by 选项,这是最简单直接的方法。

通过探索这些方法,你掌握了如何让对象平滑地围绕另一个对象做圆周运动。

046:载具属性 🚗

在本节课程中,我们将学习如何使用“载具”属性。这个属性允许一个对象(如乘客或货物)自动跟随另一个对象(如车辆)移动,从而简化动画制作。

概述

我们将创建一个场景,其中包含一辆皮卡车、疯帽匠和一张桌子。通过设置“载具”属性,我们将让疯帽匠和桌子跟随皮卡车移动。随后,我们还将学习如何在动画中途取消这种跟随关系,并实现桌子从卡车后斗掉落的动画效果。

场景设置

首先,我们有一个初始世界,包含一辆皮卡车、疯帽匠和一张桌子。从侧视图可以看到,疯帽匠坐在驾驶座上。从顶视图可以看到,桌子被放置在皮卡车的后斗里。

使用载具属性

为了让疯帽匠和桌子跟随皮卡车移动,我们需要为它们设置“载具”属性。

以下是设置步骤:

  1. 为疯帽匠设置载具属性。

    • 选中疯帽匠对象。
    • 在“过程”选项卡中找到 set vehicle 指令。
    • 将该指令拖入代码区,并将其目标设置为皮卡车。
  2. 为桌子设置载具属性。

    • 选中桌子对象。
    • 同样找到并拖入 set vehicle 指令。
    • 将其目标也设置为皮卡车。

关键点set vehicle 指令的执行时间是瞬时的(0秒),而移动、转向等动画指令的默认持续时间是1秒。因此,我们需要在皮卡车开始移动之前就设置好载具属性。

设置完成后,当我们让皮卡车执行一系列移动指令(例如先后退、再前进、然后右转并前进一段距离)时,疯帽匠和桌子会自动跟随移动,无需为它们单独编写移动代码。

中途取消载具关系

有时,我们只希望对象在一段动画中跟随车辆,而不是全程。例如,我们可能希望皮卡车在转弯后,桌子不再跟随,并从后斗掉落。

以下是实现方法:

  1. 在皮卡车转弯后,立即将桌子的载具属性重置为其自身。

    • 使用 set vehicle 指令,将目标设置为 this(代表对象自身)。这是所有对象的默认载具属性。
  2. 为了让掉落效果更逼真,我们可以在桌子停止跟随后,让卡车的尾门打开。

    • 选中皮卡车,在“部件”中找到“尾门”。
    • 为尾门添加一个 turn backward 指令,例如转动0.25圈。

  1. 最后,让桌子落到地面。
    • 为桌子添加 move down 指令。
    • 下落距离可以通过“函数” get distance above ground 来动态获取到地面的距离。
    • 可以设置较短的持续时间(如0.25秒)让掉落更快。

调整细节:如果桌子初始位置悬浮,可以在场景编辑模式中微调其垂直位置,使其看起来是放在卡车后斗里的。

载具属性的单向性

载具关系是单向的。在上面的例子中,疯帽匠的载具是皮卡车,因此皮卡车的移动会带动疯帽匠。但是,如果疯帽匠自己移动(例如,我们让它在皮卡车第一次移动后快速上下跳动),皮卡车并不会因此被带动。这个特性让我们可以灵活控制不同对象的动画。

总结

本节课我们一起探索了“载具”属性的用法。我们学习了:

  • 如何通过 set vehicle 指令让一个对象自动跟随另一个对象移动,从而简化代码。
  • 如何通过将载具属性设回 this 来中途取消这种跟随关系。
  • 理解了载具关系的单向性:只有“载具”对象的移动会影响“乘客”对象,反之则不成立。

掌握载具属性,能在制作涉及多个对象协同运动的复杂动画时,大大提高效率和代码的简洁性。

047:颜色与透明度属性 🎨

在本节课中,我们将学习如何使用对象的颜色(paint)和透明度(opacity)属性,并结合内置函数来创建一个生动的动画故事。我们将跟随一只饥饿的狼寻找兔子的情节,逐步实现多个场景的动画效果。

概述

我们将创建一个包含三个对象(狼、兔子和汽车)的世界。通过设置对象的初始属性(如颜色和透明度),并编写一系列动作指令,来实现兔子出现、移动、变色、消失,最终跳上汽车逃离的完整故事动画。


场景设置与初始属性

首先,我们需要设置动画的初始场景。这包括放置对象并调整它们的初始状态,例如将汽车移出屏幕,以及设置兔子的颜色和透明度。

以下是初始设置的具体步骤:

  1. 点击 场景(Scene) 选项卡。
  2. 找到并点击名为 perform custom setup 的特殊过程。
  3. 在新打开的标签页中,添加以下指令:
    • 将汽车向后移动10个单位:hatchback.move(backward, 10)
    • 将兔子的颜色设置为黄色:bunny.setPaint(yellow)
    • 将兔子的透明度设置为0(完全透明):bunny.setOpacity(0)

完成这些设置后,运行世界,你将只看到狼,而兔子和汽车已处于预设的初始位置和状态。


编写主动画逻辑

上一节我们完成了场景的初始化,本节中我们来看看如何编写主动画的逻辑。请确保在 my first method 标签页中编写以下代码,而不是在 perform custom setup 中。

我们首先添加一个注释,说明初始化已在自定义设置中完成,然后开始构建故事。

场景一:狼的独白与兔子登场

首先,让狼说话,然后让兔子出现并跳跃。

  • 狼说话bigBadWolf.say(“Nothing much happening today.”, duration=2)
  • 兔子出现:将兔子的透明度设置为1(完全可见):bunny.setOpacity(1)
  • 兔子跳跃:调用我们预先写好的 jump 方法,例如:bunny.jump(howHigh=1.5)

场景二:狼的注视与兔子变色

接下来,让狼转身面对兔子,并低头看它,同时兔子改变颜色。

  • 狼转身面向兔子bigBadWolf.turnToFace(bunny)
  • 狼低头:选择狼的颈部对象,让其向前转动一点:bigBadWolf.neck.turn(forward, 0.1)
  • 兔子变红bunny.setPaint(red)

场景三:兔子消失与重现

现在,让兔子消失,移动到狼的另一侧,然后重新出现并面向摄像机。

  • 兔子消失bunny.setOpacity(0, duration=2)
  • 兔子移动:让兔子以狼为参照,向左转半圈(0.5圈),移动到另一侧:bunny.turn(left, 0.5, asSeenBy=bigBadWolf, duration=0)
  • 兔子转向摄像机bunny.turnToFace(camera, duration=0)
  • 兔子重现bunny.setOpacity(1)

场景四:兔子跳跃与狼转身

兔子再次跳跃,然后狼转身面对它。

  • 兔子跳跃bunny.jump(howHigh=1.2)
  • 狼转身面向兔子bigBadWolf.turnToFace(bunny)

场景五:兔子绕行与狼跟随

这是一个同步动作。我们使用 do together 块,让兔子绕狼半圈的同时,狼在原地转身跟随兔子。

  • 兔子绕行bunny.turn(right, 0.5, asSeenBy=bigBadWolf)
  • 狼跟随转身bigBadWolf.turn(right, 0.5)

场景六:汽车入场与兔子跳上车

汽车开回场景,然后兔子跳上车顶。

  1. 汽车入场hatchback.move(forward, 10) (这正好抵消了初始设置中向后移动的距离)。
  2. 同步动作一(兔子跳高,狼抬头)
    • 使用 do together
    • 兔子向上移动的高度等于汽车的高度:bunny.move(up, hatchback.height)
    • 狼的颈部向后转动一点以抬头:bigBadWolf.neck.turn(backward, 0.1)
  3. 同步动作二(兔子向前移动到车顶,狼转头)
    • 使用另一个 do together
    • 兔子向前移动的距离等于它到汽车的距离:bunny.move(forward, bunny.distanceTo(hatchback))
    • 狼向右转动一点以跟随兔子移动:bigBadWolf.turn(right, 0.25)
  4. 兔子调整方向:在跳上车后,让兔子面向汽车:bunny.turnToFace(hatchback)
  5. 兔子与汽车绑定:将兔子的 vehicle 属性设置为汽车,这样汽车移动时兔子会跟随:bunny.setVehicle(hatchback)
  6. 兔子与汽车方向一致:让兔子的方向与汽车对齐:bunny.orientTo(hatchback)

场景七:汽车载着兔子逃离

最后,汽车载着兔子开走,狼无奈地转身并说话。

  • 同步动作(汽车开走,狼转身)
    • 使用 do together
    • 汽车向前移动:hatchback.move(forward, 10)
    • 狼继续向右转身:bigBadWolf.turn(right, 0.25)
  • 狼的台词:在同步动作后,让狼说:bigBadWolf.say(“rats”)

总结

本节课中我们一起学习了如何综合运用对象属性(颜色、透明度、载体)和内置函数(距离、高度)来构建一个复杂的多场景动画故事。关键步骤包括:

  1. 使用 perform custom setup 进行初始状态设置。
  2. 通过 setPaintsetOpacity 控制外观。
  3. 利用 do together 实现同步动画。
  4. 使用函数如 object.heighta.distanceTo(b) 获取精确值以控制移动。
  5. 通过设置 vehicle 属性让一个对象随另一个对象移动。

通过这个练习,你不仅熟悉了属性的操作,也掌握了如何将多个简单指令组合成一个流畅的叙事性动画。享受使用属性和函数创造故事的乐趣吧!

100:点击九只企鹅 🐧

在本节课中,我们将学习如何创建一个名为“点击九只企鹅”的游戏。这是一个类似“打地鼠”游戏的变体,但规则有所不同:玩家需要在九只企鹅中,点击每一只企鹅一次,将它们全部变为红色。游戏开始时,所有企鹅会随机从地面下弹出,玩家需要抓住时机点击它们。当所有企鹅都被点击并变为红色后,游戏结束,所有企鹅会一起升上地面。

项目概述与场景设置

首先,我们来看看项目的初始设置。项目中有一个包含九只企鹅的数组,它们最初都站在同一个位置。

在“场景”选项卡中,向下滚动,可以看到一个名为 penguin list 的企鹅数组,其中包含了位置0到8的九只企鹅。

回到“我的第一个方法”,我们看到它只包含一个调用:set up scene。点击并编辑这个方法,可以看到具体的设置代码:摄像机被移动到一个俯视地面的位置,九只企鹅被移动到一个3x3的网格位置,然后全部被移动到地面以下。

实现企鹅随机弹出

上一节我们设置了场景,本节中我们来看看如何让企鹅从地面下随机弹出。

回到“我的第一个方法”,在 set up scene 调用下方,我们拖入一个 while 循环,条件暂时设为 true,这将创建一个无限循环。

接下来,我们需要创建一个让企鹅弹出的过程。

以下是创建 pop penguin 过程的步骤:

  1. 在“场景”中创建一个新的场景过程,命名为 pop penguin
  2. 为此过程添加一个类型为 Penguin 的参数,命名为 whichPenguin
  3. 在过程内部,拖入一个 If 语句。
  4. If 的条件设置为:whichPenguin.getPaint == white。这用于检查传入的企鹅当前是否为白色。
  5. If 语句的 Do 部分,按顺序添加以下动作:
    • whichPenguin.move(up, 1 meter, duration=0.25 seconds)
    • whichPenguin.delay(1 second) (这里我们将原教程的0.5秒改为1秒,以便更容易点击)
    • whichPenguin.move(down, 1 meter, duration=0.25 seconds)

这个过程的作用是:如果传入的企鹅是白色的,它会快速向上移动1个单位(弹出地面),等待1秒,然后再快速移回地面下。

现在,回到“我的第一个方法”的 while 循环中,拖入我们刚创建的 pop penguin 过程。在参数选择上,我们不直接选择某一只企鹅,而是选择 penguin list 数组,并将其索引设置为一个随机数。

具体操作是:将参数从 penguinList[0] 改为 penguinList[random(0, penguinList.length)]。这将从企鹅数组中随机选择一只企鹅并传递给弹出过程。

运行项目,现在可以看到随机的企鹅从地面下弹出。

添加点击事件

现在企鹅可以随机弹出了,但我们还需要让玩家能够通过点击来“击中”它们。

我们需要为鼠标点击对象添加一个事件监听器。

以下是设置点击事件的步骤:

  1. 点击“初始化事件监听器”选项卡。
  2. 点击“添加事件监听器”按钮,选择“鼠标”,然后添加“鼠标点击对象”监听器。
  3. 在“添加细节”中,将“视觉对象集”设置为我们的 penguin list 数组。这样点击任何一只数组中的企鹅都会触发事件。
  4. 在事件触发的动作中,我们只需要将点击的企鹅变为红色。拖入 penguin.setPaint 动作,将颜色设置为 red
  5. 关键的一步是:将执行动作的对象从固定的 penguin9 改为 getModelAtMouseLocation。这个函数能返回我们刚刚点击的那个具体对象。

再次运行项目。现在,当企鹅弹出时,尝试点击它。被点击的企鹅会慢慢变成红色(因为我们设置了1秒的延迟,有足够时间点击)。

设置游戏结束条件

目前,即使点击了所有企鹅,游戏也不会结束,因为 while 循环的条件始终为 true。我们需要修改循环条件,使其在所有企鹅都变红后停止。

我们需要创建一个场景函数来检查是否还有白色的企鹅。

以下是创建 atLeastOnePenguinIsWhite 函数的步骤:

  1. 创建一个新的场景函数,返回类型为 Boolean,命名为 atLeastOnePenguinIsWhite
  2. 在函数内部,拖入一个 For each in 循环,遍历 penguin list 数组中的每一只企鹅。我们将循环变量命名为 penguinIterator
  3. 在循环体内,拖入一个 If 语句。
  4. If 的条件设置为:penguinIterator.getPaint == white。这检查当前遍历的企鹅是否为白色。
  5. 如果条件为真(即找到一只白色企鹅),我们拖入一个 return true 语句。这意味着函数立即返回 true,游戏应该继续。
  6. 如果循环完整地执行完毕,都没有执行 return true(即没有找到任何白色企鹅),那么我们在循环结束后拖入一个 return false 语句。这意味着所有企鹅都不是白色,游戏应该结束。

现在,回到“我的第一个方法”,将 while 循环的条件从 true 改为我们刚创建的函数调用 atLeastOnePenguinIsWhite

添加游戏结束动画

最后,当游戏循环结束(即没有白色企鹅)后,我们让所有企鹅一起升上地面作为胜利的庆祝动画。

while 循环结束后,拖入一个 For each in together 循环。同样遍历 penguin list 数组,变量名例如 penguinIterator

在循环体内,让 penguinIterator 执行 move(up, 1 meter) 动作。使用 together 循环意味着所有企鹅会同时执行这个向上移动的动作。

总结与最终测试

本节课中,我们一起学习并完成了一个“点击九只企鹅”的游戏。

让我们总结一下实现的核心步骤:

  1. 初始化场景:将九只企鹅排列成网格并隐藏到地下。
  2. 随机弹出:使用无限循环和随机数,让白色企鹅随机弹出地面。
  3. 点击交互:添加事件监听器,使玩家点击企鹅时能将其变为红色。
  4. 游戏逻辑:创建函数检查游戏状态,当所有企鹅变红时结束弹出循环。
  5. 结束动画:游戏结束后,让所有企鹅一起升上地面。

现在运行最终的项目。游戏开始,白色企鹅随机弹出。你需要抓住时机点击每一只弹出的企鹅。每点击一只,它就会变成红色。当九只企鹅全部变红后,游戏循环停止,所有企鹅会一同升起,游戏胜利!

这个项目涵盖了数组遍历、随机数、条件判断、循环控制以及事件处理等多个编程核心概念,是一个有趣且综合性强的练习。

101:算法理论回顾 🧠

在本节课程中,我们将回顾之前构建的两个项目,通过对比分析来深入理解循环条件与数组在算法中的应用。我们将重点关注如何根据游戏状态动态调整程序逻辑。


概述

在本节中,我们将分析“点击两只企鹅”和“点击九只企鹅”两个游戏的代码。我们将探讨它们的相似之处与关键差异,特别是循环条件的构建方式以及如何利用数组和函数来简化多对象的管理。


项目回顾:从实践到理论

在之前的课程中,我们通常先介绍特定编程结构或技术的理论,再进行演示。本节我们将反其道而行之,通过对已完成的实践项目进行反思,来巩固对算法的理解。

点击两只企鹅的游戏 🐧🐧

首先,我们回顾“点击两只企鹅”游戏中的代码。游戏的核心逻辑是:只要至少有一只企鹅未被点击(颜色仍为白色),游戏就应继续。

因此,我们需要修改while循环的条件,以反映“至少一只企鹅为白色”这一状态。进入循环后,可能有两种情况:两只企鹅都仍是白色,或者其中一只已被点击并变为红色。

以下是处理逻辑的核心思路:

在循环内部,我们需要对每只企鹅进行单独检查。只有当某只企鹅仍为白色时,才让它随机移动并执行“弹出”动作。

点击九只企鹅的游戏 🐧🐧🐧...

“点击九只企鹅”的游戏则有所不同,主要体现在两个关键方面。

第一个显著差异是循环条件的构建方式。
我们意识到,为九只企鹅编写一个冗长的九重“或”条件是不现实的。例如,像 penguin1是白色 或 penguin2是白色 或 penguin3是白色... 这样的代码会非常臃肿。

取而代之,我们创建了一个函数。这个函数会检查数组中是否至少有一只企鹅是白色的,并返回truefalse。使用数组起初可能感觉陌生,但它使我们能够灵活处理任意数量的企鹅对象。

以下是该函数的逻辑流程:

  1. 遍历数组中的所有企鹅元素。
  2. 如果遇到颜色为白色的企鹅,则立即返回 true
  3. 如果遍历完所有企鹅后都没有发现白色企鹅,则返回 false

第二个重要差异在于企鹅的行为逻辑。
在九只企鹅的游戏中,并非所有白色企鹅都会随机移动。程序会从所有仍是白色的企鹅中随机选择一只,让这只被选中的企鹅执行“弹出并收回”的动作。

而在两只企鹅的游戏中,最初两只白色企鹅都会随机移动并弹出。


核心概念对比与总结

除了上述两点差异,两个游戏的基本框架非常相似。

当涉及的条件对象只有两个时(例如两只企鹅),直接使用逻辑“或”来构建循环条件通常是最简单直接的方法。

然而,一旦涉及三个或更多对象,使用函数配合数组遍历来检查条件就会变得清晰且高效。这种方法避免了代码重复,提升了可维护性和可扩展性。

建议重新运行这两个项目,仔细体会它们之间微妙的逻辑差异,这将帮助你更好地掌握条件判断和数组迭代的应用场景。


本节总结

本节课我们一起回顾并对比分析了两个点击企鹅游戏的算法实现。我们学习了:

  1. 如何为不同数量的对象设计循环条件。
  2. 如何利用函数数组迭代来简化多对象的状态检查,其核心代码逻辑可概括为:
    def check_any_white(penguin_array):
        for penguin in penguin_array:
            if penguin.color == "white":
                return True
        return False
    
  3. 理解了在管理多个对象时,集中处理(如随机选择一只行动)与分散处理(每只独立行动)的不同策略。

通过这种从实践反推理论的方式,希望你能够更深刻地理解如何根据具体需求,灵活运用循环、条件和数据结构来构建有效的程序逻辑。

103:计分器 🎮

在本节课中,我们将学习如何在Alice中为游戏创建一个计分器。计分器是游戏设计中的核心元素,它能追踪玩家的得分,增强游戏的互动性和挑战性。我们将通过修改一个“点击企鹅”游戏来实践这一概念,目标是让玩家点击10只企鹅。

计分器的基本原理

在Alice中,我们通过添加一个3D文本模型来实现计分功能。这个模型是一个可以显示文本的对象。当我们向项目中添加文本模型时,需要为其命名、指定颜色并设置初始值。例如,我们可以将计分器的颜色设为蓝色,初始值设为字符串“0”。

然而,仅仅显示文本是不够的。因为Alice屏幕上显示的是字符串,而我们需要对数字进行数学运算(如加分)。因此,我们需要一个额外的属性来存储实际的数字分数。

实现计分器的步骤

以下是创建和更新计分器的关键步骤。

1. 添加文本模型与属性

首先,我们添加一个3D文本模型,并将其重命名为scoreDisplay。我们设置其初始文本为“0”,颜色为蓝色。

接着,我们需要添加一个整数属性来存储实际的分数值。我们将这个属性命名为scoreValue,并设置其初始值为0。这样,我们就有了两个部分:一个用于显示的文本模型和一个用于计算的数字属性。

2. 编写更新分数的过程

更新分数需要两个步骤,这有点像“双重记账”。我们需要同时更新存储分数的属性和屏幕上显示的文本。

我们创建一个名为increaseScore的过程。这个过程包含两条指令:

  1. scoreValue属性的值增加1。
  2. scoreDisplay的文本设置为scoreValue的新值。

为了将数字转换为可以显示的字符串,我们使用字符串拼接的方法。具体做法是将一个空字符串与数字连接起来。在Alice中,这可以通过"" + scoreValue来实现。

代码示例:increaseScore 过程

// 第一步:增加分数值
set scoreValue = scoreValue + 1

// 第二步:更新显示文本
set scoreDisplay.text = "" + scoreValue

计分器的工作原理

让我们通过一个具体的例子,来看看Alice是如何执行分数更新过程的。

假设初始状态为:scoreValue = 0scoreDisplay显示为“0”。

  1. 当调用increaseScore过程时,第一条指令set scoreValue = scoreValue + 1开始执行。

    • Alice首先查找scoreValue的当前值,得到0。
    • 然后计算表达式0 + 1,得到结果1。
    • 最后,将新值1写回scoreValue属性所在的存储位置。
  2. 接着执行第二条指令set scoreDisplay.text = "" + scoreValue

    • Alice再次查找scoreValue的值,此时它已经是1。
    • 将数字1与空字符串""拼接,得到字符串“1”。
    • 将这个字符串“1”赋值给scoreDisplaytext属性,屏幕上的计分器显示随即更新为“1”。

通过这个过程,我们成功地将玩家的分数增加了1,并同步更新了屏幕显示。

总结

本节课中,我们一起学习了在Alice中创建计分器的方法。我们了解到,实现计分功能需要结合3D文本模型用于显示,以及一个数字属性用于存储和计算。通过编写一个更新过程,我们能够同步修改这两个部分:先对数字属性进行数学运算,再将其转换为字符串更新到文本模型上。掌握计分器的创建是设计交互式游戏的重要一步,在接下来的课程中,我们将把计分器应用到具体的游戏场景中。

104:带计分器的点击企鹅街机游戏 🐧🎮

概述

在本节课中,我们将学习如何为“点击企鹅”游戏添加一个计分系统。我们将修改现有游戏,使其目标变为在规定时间内点击10只企鹅,并实时显示玩家的得分。通过本教程,你将掌握在Alice中创建和更新分数显示、使用变量追踪状态以及修改游戏循环逻辑的方法。


第一步:创建分数显示 📊

上一节我们完成了基础的点击企鹅游戏。本节中,我们首先需要创建一个视觉上的分数显示器。

以下是创建分数显示的步骤:

  1. 点击 setup scene 按钮进入场景设置。
  2. 点击 Shapes 选项卡下的 text 标签。
  3. 将一个 text 模型拖拽到屏幕的右下角区域。
  4. 将这个文本模型命名为 score
  5. 将文本的 paint 颜色从白色改为蓝色,以便在雪地场景中清晰显示。
  6. 将文本的 value 设置为自定义字符串 0
  7. 由于游戏运行时摄像机会切换到俯视角度,我们需要让分数牌旋转。添加一个指令,让 score 向后旋转 1/4 圈。
  8. 最后,将分数牌的位置调整到合适的地方,确保它不会与企鹅重叠。

第二步:添加分数变量 🧮

创建了显示分数的文本后,我们需要一个变量来在后台追踪玩家的实际得分。

以下是添加分数变量的步骤:

  1. 点击 edit code 返回代码编辑界面。
  2. 点击 scene 选项卡。
  3. 选中我们刚刚创建的 score 文本模型。
  4. 为它添加一个属性(property)。
  5. 将属性类型设置为 whole number(整数)。
  6. 确保勾选 variable 选项,因为我们需要在游戏过程中改变这个值。
  7. 将变量命名为 playerScore
  8. 将其初始值设置为 0

第三步:编写增加分数的程序 📈

现在,我们需要创建一个程序(procedure),每当玩家点击一只企鹅时,这个程序就会被调用,用于增加分数并更新显示。

以下是编写 increase 程序的步骤:

  1. 点击黄色的六边形(创建程序按钮)。
  2. 选择 score 文本模型,然后为其添加一个新的程序。
  3. 将程序命名为 increase
  4. 在程序内,首先拖入一个 do in order 指令块。
  5. do in order 块内,首先设置 playerScore 的值。使用 set 指令,将其值设为 playerScore + 1。这可以通过调用 math 操作中的加法来实现。
  6. 接着,更新屏幕上的分数显示。拖入一个 set value 指令,将文本的值设置为一个空字符串。
  7. 然后,点击下拉菜单,选择 append 操作,并附加 playerScore 这个整数变量。这样就将后台的分数变量显示在了屏幕上。

第四步:修改鼠标点击事件 🖱️

我们已经有了增加分数的程序,现在需要修改游戏逻辑,让点击企鹅的动作触发分数增加,而不是将企鹅变红。

以下是修改鼠标点击事件的步骤:

  1. 点击 initialize event listeners 选项卡。
  2. 找到当前的 mouse clicked 事件。
  3. 将其中“将企鹅涂成红色”的指令拖到垃圾桶删除。
  4. 将事件对象从 penguin 改为 this score
  5. 将我们刚刚创建的 increase 程序拖入到鼠标点击事件中。

第五步:修改游戏循环条件 🔄

游戏的核心循环需要改变。原来的条件是“至少有一只企鹅是白色的”,现在我们需要将其改为“游戏尚未结束”,即分数未达到10分。

以下是修改游戏循环的步骤:

  1. 首先,我们需要创建一个新的场景函数来判断游戏是否结束。点击 scene,创建一个名为 gameIsNotOver 的新函数,返回类型为 Boolean
  2. 在这个函数中,使用一个 if/else 语句。
  3. if 条件中,判断 this scoreplayerScore 属性是否小于 10
  4. 如果小于10,则返回 true(游戏未结束)。
  5. 否则,返回 false(游戏已结束)。
  6. 完成函数后,回到 my first method 中的 while 循环。
  7. 将循环条件从原来的函数替换为我们新建的 gameIsNotOver 函数。

第六步:测试与调整 🧪

现在,让我们运行游戏进行测试。点击 Run 按钮,尝试点击企鹅。你会发现分数会随着点击而增加,当点击满10次后,游戏循环结束。

测试中可能会发现一些小问题,例如:

  • 分数牌的位置可能仍会被企鹅遮挡,可以返回 setup scene 微调其位置。
  • 玩家如果点击速度极快,可能对同一只企鹅进行双击,导致分数意外增加两次。
  • 在点击满10次后,玩家可能仍能继续点击企鹅。

你可以思考如何修复这些细微的漏洞,例如通过事件处理或增加状态检查来完善游戏逻辑。


总结

本节课中,我们一起学习了如何为Alice游戏添加一个完整的计分系统。我们创建了分数显示器,添加了用于追踪得分的变量,编写了更新分数的程序,并修改了事件和游戏循环条件,使游戏目标转变为积累分数。这些是构建交互式游戏的基础技能,你可以将这些概念应用到更复杂的游戏开发中去。

105:计时器 🕐

在本节课中,我们将学习如何在Alice中为游戏添加计时器功能。我们将修改“点击企鹅”游戏,挑战玩家在30秒内点击10只企鹅。实现这个目标需要我们理解并使用计时器,它与我们之前学过的分数系统既有相似之处,也有不同。

上一节我们介绍了如何使用分数来追踪玩家的成就。本节中,我们来看看它的“对立面”——计时器。分数值通常随着玩家的成功操作而增加,而计时器的值(剩余时间)则通常随着时间流逝而减少。

创建计时器显示

在Alice中实现计时器的方法与实现分数非常相似。首先,我们需要在场景中添加一个3D文本模型来显示时间。

以下是创建计时器显示文本的步骤:

  1. 从模型库中添加一个“3D文本”模型。
  2. 为其命名,例如 timer
  3. 设置其颜色(例如蓝色)和初始值(例如 30)。

添加时间属性

与分数一样,屏幕上显示的是字符串,但我们无法对字符串进行数学运算(如减1)。因此,我们需要一个单独的属性来存储实际的剩余时间数值。

我们创建一个名为 timeLeft整数属性,并将其初始值设为30。使用整数是因为时间通常以整秒为单位递减。

// 属性定义示例
property timeLeft: 30

编写更新计时器的过程

接下来,我们需要编写一个过程来更新计时器。这涉及“双重记账”:既要更新存储时间的属性,也要更新屏幕上显示的文本。

以下是 decreaseTimer 过程的内容:

  1. timeLeft 属性的值减1:set timeLeft to timeLeft - 1
  2. timer 3D文本的文本内容设置为 timeLeft 的新值。由于 timeLeft 是数字,我们需要将其转换为字符串才能显示。可以通过连接一个空字符串来实现:set timer.text to "" + timeLeft

设置定时事件

计时器与分数最大的不同在于其调用方式。分数通常在玩家完成某个动作(如点击企鹅)时通过事件触发增加。而计时器需要每隔一秒钟自动减少。

Alice提供了一个特殊的“时间监听器”事件来实现这个功能。

设置定时事件的步骤如下:

  1. 在事件编辑器中,点击“创建新事件”,选择“While the world is running” -> “Every [1] seconds”。
  2. 这会创建一个名为 timeElapsed 的过程,它每秒会自动被调用一次。

编写时间流逝逻辑

timeElapsed 过程中,我们需要判断游戏是否仍在进行。只要剩余时间大于0,我们就调用 decreaseTimer 过程来减少时间。

其逻辑代码如下:

// 在 timeElapsed 过程中
if timeLeft > 0
    decreaseTimer
end if

现在,让我们看看计时器在游戏中是如何运作的。当游戏开始时,计时器从30开始显示,并每秒减少1。这为玩家创造了一个时间压力,他们必须在时间归零前完成点击10只企鹅的目标。

本节课中,我们一起学习了如何在Alice中创建和使用计时器。我们了解了计时器与分数的异同,掌握了创建计时器显示、添加时间属性、编写更新过程以及设置定时事件监听的关键步骤。通过将这些概念结合起来,你就能为游戏添加时间限制,从而增加游戏的挑战性和趣味性。

106:带计分器和计时器的点击企鹅街机游戏 🐧⏱️🎮

在本节课中,我们将为点击企鹅游戏添加一个计时器,使游戏规则变为:玩家必须在30秒内点击10只企鹅。我们将从上一节课完成的项目开始,通过添加计时器来完善游戏。

概述

上一节我们为游戏添加了计分器。本节中,我们来看看如何添加一个30秒的倒计时计时器,并修改游戏逻辑,使玩家必须在限定时间内达成目标。我们还将添加游戏结束时的胜负判定。

添加计时器文本模型

首先,我们需要在场景中创建一个显示剩余时间的文本模型。这个过程与创建计分器类似。

以下是具体步骤:

  1. 点击 setup scene 按钮。
  2. 点击 shapes 下的 text 选项。
  3. 将一个新的文本模型拖入场景,可以放在计分器上方。
  4. 将这个文本模型命名为 timer
  5. 将其颜色设置为绿色,以形成良好对比。
  6. 将其初始值设置为字符串 "30"
  7. 点击 OK 确认。
  8. 与处理计分器数字“0”类似,使用 one shot 动画将文本旋转四分之一圈,使其正面朝外。

创建计时器变量与过程

接下来,我们需要为计时器创建变量和减少时间的过程。

  1. 点击 edit code 进入代码编辑界面。
  2. 在场景中点击 timer 文本模型。
  3. 为其添加一个属性(变量),命名为 timeLeft,类型为 whole number(整数),并初始化为 30
  4. 创建一个新的文本模型过程,命名为 decrease,用于将 timeLeft 的值减1。

decrease 过程的逻辑与计分器的 increase 过程相似:

  • 设置变量:使用 set timeLeft 指令,将其值设为 timeLeft - 1
  • 更新显示:使用 set value 指令,将文本模型的值设置为 timeLeft 变量的字符串形式。这确保了屏幕显示与内部变量同步。

设置计时器事件

现在,我们需要创建一个事件,让计时器每秒自动减少。

  1. 点击 initializeEventListeners 标签页。
  2. 点击 Add Event Listener 按钮。
  3. 选择 Scene activation or time 类别下的 time listener
  4. 设置时间间隔为 1 second
  5. 在事件触发时,调用 this.timer.decrease 过程。

这样,游戏运行时,decrease 过程会每秒被调用一次。

修改游戏结束判断条件

之前,游戏结束的条件仅仅是分数达到10分。现在,我们需要加入时间限制。

我们需要修改场景的 gameIsNotOver 函数:

  1. 找到并编辑 gameIsNotOver 函数。
  2. 将条件修改为:playerScore < 10 and timeLeft > 0
    • 这意味着游戏继续的条件是:玩家分数小于10 并且 剩余时间大于0
    • 只要其中一个条件不满足(分数达到10或时间归零),游戏就会结束。

添加胜负判定

游戏结束后,我们需要告诉玩家是赢了还是输了。这可以通过在游戏主循环结束后添加判断来实现。

在主 myFirstMethod 中,while 循环结束后:

  1. 添加一个 if 语句。
  2. 判断条件为:playerScore > 9(即分数达到10分)。
  3. 如果条件为真(玩家赢了),让一只企鹅说 "Player Won"
  4. 如果条件为假(玩家输了),让一只企鹅说 "Player Lost"

修复计时器在游戏结束后仍运行的Bug

目前,计时器事件会一直运行,即使游戏已经结束。我们需要修复这个问题。

修改 timer.decrease 过程:

  1. decrease 过程的一开始,添加一个 if 语句。
  2. 判断条件为:gameIsNotOver
  3. 只有当游戏尚未结束时,才执行减少时间的操作。
  4. 将原有的减少时间逻辑(设置变量和更新显示)放入这个 if 语句中。

核心逻辑用伪代码表示如下:

// 在 timer.decrease 过程中
if (gameIsNotOver 为真) {
    timeLeft = timeLeft - 1;
    更新屏幕显示为 timeLeft;
}

总结

本节课中,我们一起学习了如何为游戏添加计时器功能,从而创建了一个完整的街机游戏。我们完成了以下工作:

  1. 创建了计时器文本模型,用于在屏幕上显示剩余时间。
  2. 定义了 timeLeft 变量和 decrease 过程,来管理时间的逻辑。
  3. 设置了时间事件监听器,实现了每秒自动倒计时。
  4. 修改了 gameIsNotOver 函数,将时间限制纳入游戏结束条件。
  5. 添加了胜负判定逻辑,在游戏结束后告知玩家结果。
  6. 修复了关键Bug,确保计时器只在游戏进行时运行。

现在,你的点击企鹅游戏已经拥有了计分、限时和胜负判定等完整功能,成为一个相当精巧的游戏项目。

108:制作爱丽丝饼干 🍪

在本节课中,我们将学习如何将你喜爱的爱丽丝(Alice)编程环境中的角色对象,制作成美味的饼干。这是一个充满趣味的过程,我们将从选择角色开始,逐步介绍制作饼干模具、烘焙以及装饰的完整步骤。


选择角色与制作模具 🐷

上一节我们介绍了课程目标,本节中我们来看看如何选择角色并为其制作饼干模具。

首先,在爱丽丝中选择一个你想制作成饼干的角色。这里我们以猪(Pig)角色为例。制作与角色形状一致的饼干有多种方法,以下是三种常见方式:

  1. 使用3D打印机创建饼干模具
  2. 用刀具直接切割出形状
  3. 通过弯曲厚金属条自制饼干模具

本教程将主要聚焦于使用3D打印机制作饼干模具。即使你没有3D打印机,也可以将设计文件发送给专业的3D打印服务商进行制作。

我使用网站 cookiecaster.com 来设计猪形状的饼干模具。具体步骤如下:

  1. 在爱丽丝项目中放入猪角色。
  2. 进入场景设置(Scene Setup)。
  3. 将猪涂成黑色以获得良好对比度。
  4. 将地面设置为不可见(不透明度设为0)。
  5. 选择场景,将大气颜色(Atmosphere Color)设为白色。
  6. 最终得到一个白色背景上的黑色猪图像,对比鲜明。
  7. 截取这张黑色猪的图片,并上传至 cookiecaster.com 网站。

该网站会自动追踪高对比度图像的轮廓。点击猪的图像后,它会自动绘制出饼干模具的形状。接着,下载生成的STL格式文件。找到3D打印机,将STL文件转换为打印机可识别的格式。在打印前,你可以预览3D饼干模具的样式。

打印过程可能需要数小时。最终,我制作了两个不同尺寸的猪形状饼干模具。


准备与烘焙饼干 🧑‍🍳

上一节我们完成了模具的制作,本节中我们来看看如何准备面团并烘焙饼干。

现在开始烘焙饼干。你需要先制作面团。我最喜欢的配方是一种简单的沙塔尔饼干(Sand Tart),原料包括面粉、糖、黄油、鸡蛋、香草精等。该配方源自1996年12月的《好管家》(Good Housekeeping)杂志。

我通常一次性制作两份面团,使用两个碗。每份面团再分成三个面团球。你可以将面团冷冻数天,待需要时再使用。

如果没有3D打印机(我很多年前也没有),可以采用以下替代方法:

  • 打印出角色的图片,然后用刀具沿着图片轮廓切割面团。
  • 使用金属条工具包,将金属条弯曲成所需形状来自制饼干模具。

面团准备好后,开始制作饼干。清理桌面区域,拿出烤盘、擀面杖,并在桌上撒些面粉。同时,准备一个放置烤好饼干的地方,铺上蜡纸。如果先润湿桌面,蜡纸会粘得更牢。

将解冻(可用微波炉快速解冻,但注意不要过度)的面团擀平。将擀平的面团转移到烤盘上再切割形状,比先切割再转移更容易,能避免形状损坏。

在烤盘的面团上,你可以手动切割饼干(这是我多年来制作爱丽丝饼干的方式),即把图片放在面团上,用锋利的刀沿着形状切割。但如果使用3D饼干模具,效率会高得多——只需将模具按压进面团即可。如果你计划制作大量饼干,强烈建议自制饼干模具。

转移一大片擀平的面团可能很困难,容易撕裂。替代方法是,将面团切成合适大小的块,再铺到烤盘上。然后用饼干模具按压,即可得到饼干坯。

将一整盘饼干坯放入烤箱烘烤,大约需要16分钟。烤好的饼干放在桌上铺好的蜡纸上冷却,它们冷却得很快,香气扑鼻。


装饰饼干 🎨

上一节我们完成了饼干的烘焙,本节中我们来看看如何为饼干上色装饰。

接下来装饰饼干。你需要糖霜(Icing)。我主要使用白色糖霜,它必须是纯白的。香草糖霜略带黄色,如果加入红色等其他颜色,黄色会将其变成橙色。如果在杂货店找不到,手工艺品店通常有售。

如果需要黑色,我发现最容易的方法是使用巧克力糖霜并加入黑色食用色素。如果向白色糖霜中加入黑色,需要大量色素才能使其变黑。

以猪饼干为例,我们需要以下颜色:

  • 粉色用于身体。
  • 黑色用于蹄子。
  • 灰色用于鼻孔。
  • 白色和蓝色用于眼睛。

我喜欢使用食用色素凝胶(Food Gel Colors),它们更浓稠,不会稀释糖霜。

首先,将少量粉色凝胶与白色糖霜混合,制作一大份粉色糖霜。然后将所有猪饼干涂成粉色。接着,用黑色糖霜添加蹄子、眼睛和鼻孔。装饰完成后,饼干看起来非常不错。我总共制作了超过100块猪饼干。


总结 📝

本节课中,我们一起学习了制作爱丽丝饼干的完整流程。我们从在爱丽丝中选择角色开始,介绍了三种制作饼干模具的方法,并重点讲解了利用3D打印技术设计并制作模具的步骤。接着,我们学习了如何准备面团、使用模具切割形状并进行烘焙。最后,我们探讨了使用不同颜色的糖霜来装饰饼干,使其更加生动逼真。希望你能享受制作属于你自己的爱丽丝饼干的乐趣!

109:游戏开发概述 🎮

在本节课中,我们将学习如何使用爱丽丝(Alice)软件构建一个简单的游戏。我们将重点介绍两种新的事件处理机制:碰撞检测和键盘按键响应。通过这些新工具,你将能够创建一个玩家控制幽灵与兔子碰撞得分的游戏。


碰撞检测事件 🎯

上一节我们介绍了本周的学习目标,本节中我们来看看第一个核心概念:碰撞检测。

在爱丽丝中,我们可以设置一个事件,当程序检测到两个物体发生碰撞时,自动运行一段代码。这通过一个名为“碰撞检测”的新事件类型实现。

以下是其核心逻辑的伪代码描述:

当 物体A 碰撞 物体B 时:
    执行 [处理碰撞的代码]

在我们的具体游戏示例中,玩家需要操控一个幽灵去碰撞四处跳跃的兔子。当碰撞发生时,我们将编写代码来处理这个事件(例如增加分数)。


键盘按键事件 ⌨️

了解了如何检测碰撞后,我们需要让玩家能够控制幽灵。这就引出了第二个新事件:键盘按键事件。

爱丽丝可以检测键盘上的按键何时被按下。我们将利用这个功能,让玩家通过按下特定按键来控制幽灵移动。

以下是实现此功能的基本思路:

  • 我们将编写代码,响应“上箭头”键被按下,使幽灵向前移动。
  • 我们还将编写代码,响应“左箭头”和“右箭头”键,使幽灵向左或向右转向。

通过结合碰撞检测和键盘控制,游戏的基本框架就搭建完成了。


游戏功能增强 🚀

在构建了简单的碰撞检测游戏之后,我们将为其增加一些趣味性和挑战性。

以下是我们将要实现的三个增强功能:

  1. 添加计时器:我们将使用上周学到的计时器,挑战玩家在30秒内与所有兔子发生碰撞。
  2. 引入分数与颜色机制:我们将修改游戏,让兔子的颜色随机变为红、白或蓝。碰撞蓝色兔子加分,碰撞红色兔子则扣分。获胜条件将变为在30秒内获得至少5分。
  3. 创建多关卡系统:为了使游戏更真实,我们将添加多个关卡。后续关卡将比第一关更具挑战性。我们将通过使用一个场景变量来跟踪当前关卡,并解决在每个关卡开始前将所有物体重置到正确起始位置的挑战。

本节课中我们一起学习了如何在爱丽丝中利用碰撞检测和键盘事件来构建一个交互式游戏的基础框架,并探讨了通过添加计时器、分数系统和多关卡来增强游戏性的方法。这些概念是游戏开发的核心,为你创建更复杂的项目打下了坚实的基础。

110:碰撞与按键事件 🎮

在本节课中,我们将学习如何利用爱丽丝(Alice)中的碰撞检测和按键事件来构建一款新游戏。我们将设计一个游戏,玩家需要操控一个幽灵去碰撞多个兔子,当幽灵碰到兔子时,兔子会消失。游戏的目标是碰撞所有的兔子。

为了实现这个游戏,我们需要掌握两种新的事件类型:碰撞检测事件和按键事件。

碰撞检测事件详解 💥

上一节我们介绍了游戏的基本概念,本节中我们来看看碰撞检测事件的具体构成。碰撞事件包含两个重要部分:碰撞体被碰撞体。在爱丽丝中,这两者都需要被放入数组中,因为可能存在多个碰撞体和多个被碰撞体。

爱丽丝允许你构建两个数组。第一个数组被称为集合A,第二个数组被称为集合B

在我们的游戏示例中:

  • 我们有一个碰撞体(幽灵),所以我们将创建一个只包含幽灵的数组。爱丽丝将这个数组称为集合A。
  • 我们有多个被碰撞体(所有兔子)。所以所有兔子将被放入一个数组,即第二个列出的数组。这个数组最初被构建为一个场景属性数组,我们可以直接在此引用它。

在碰撞事件中:

  • 碰撞体是来自集合A的碰撞对象。爱丽丝将其引用为 event.getThingFromSetA()。在我们的例子中,它将是幽灵,因为幽灵是第一个数组中唯一的对象。
  • 被碰撞体是来自集合B的碰撞对象,即来自包含10个兔子的数组中的一只兔子。爱丽丝将其引用为 event.getThingFromSetB()

处理碰撞后的动作 🎯

假设我们想让幽灵碰撞到的兔子向下移动10个单位(即移动到地面以下)。爱丽丝在处理上有一个细微的问题。

碰撞体和被碰撞体在爱丽丝中被视为“事物”。虽然“事物”包括像兔子和幽灵这样的双足角色,但也包括像地面这样无法移动的物体。

因此,如果我们想让被碰撞的兔子向下移动,我们需要遍历所有兔子,一旦找到被碰撞的那只兔子,我们就可以让它向下移动10个单位。这通常通过一个循环和条件判断来实现。

按键事件处理 ⌨️

接下来,我们将学习如何使用按键事件来控制幽灵的移动。我们将为玩家可能按下的每个按键添加一个 if 语句。

在这个例子中,我们将处理三个按键:

  • 如果玩家按下左箭头键,我们将让幽灵向左移动一个单位。
  • 如果玩家按下右箭头键,我们将让幽灵向右移动一个单位。
  • 如果玩家按下上箭头键,我们将让幽灵向前移动。

以下是处理按键事件的逻辑结构示例:

// 当按下某个键时
if (keyPressed == leftArrow) {
    ghost.move(LEFT, 1);
} else if (keyPressed == rightArrow) {
    ghost.move(RIGHT, 1);
} else if (keyPressed == upArrow) {
    ghost.move(FORWARD, 1);
}

总结 📝

本节课中我们一起学习了如何利用爱丽丝的碰撞检测和按键事件来构建互动游戏。我们了解了碰撞事件中集合A(碰撞体)和集合B(被碰撞体)的概念,以及如何通过遍历数组来精确控制被碰撞对象的反应。同时,我们也掌握了如何使用 if 语句来处理键盘输入,从而控制游戏角色的移动。掌握了这些核心事件,你就能够开始创建更加动态和交互性强的动画与游戏项目了。

111:碰撞检测游戏 🎮

在本节课中,我们将学习如何使用Alice创建一个简单的碰撞检测游戏。玩家需要操控一个幽灵,使其与场景中的多个兔子发生碰撞。我们将详细解析游戏流程,包括主驱动循环和事件处理,并最终完成代码实现。

游戏流程解析

在开始构建项目之前,理解游戏流程至关重要。许多游戏的流程会变得相当复杂,因此我们将花些时间解释游戏应如何运作。

与几乎所有游戏一样,游戏玩法分为两个部分。

第一部分是我们所说的主驱动循环。这通常是“我的第一个方法”中的代码,即项目启动时运行的代码。

第二部分是事件。即处理玩家与游戏之间交互的部分。例如,当幽灵与兔子碰撞时应该发生什么,当玩家按下按键时应该发生什么。

主驱动循环

让我们从主驱动循环开始。它包含两个部分。

首先,是一个while循环,条件是“游戏尚未结束”。循环之后,会向玩家显示一条祝贺消息。

那么,“游戏尚未结束”是什么意思呢?只要至少有一只兔子尚未被碰撞到,游戏就没有结束。

只要游戏应该继续,我们就同时遍历兔子数组。对于数组中的每一只兔子,如果它尚未被碰撞到,我们可以让这只兔子上下跳跃。

事件处理

现在,让我们看看事件处理。将会有两个事件。

第一个事件是当幽灵与兔子碰撞时

第二个事件是当游戏玩家按下按键试图操控幽灵时

对于碰撞事件,Alice提供了幽灵碰撞到的对象。我们知道幽灵一定是与一只兔子发生了碰撞。我们需要遍历数组中的所有兔子。当我们找到匹配的兔子时,我们让那只兔子变得不可见,并将其移动到地面以下。

最后一个事件是按键事件。如果游戏玩家按下左方向键,我们应该让幽灵向左转一点。如果玩家按下右方向键,我们应该让幽灵向右转。最后,如果玩家按下上方向键,我们应该让幽灵向前移动。

总结与实现

这完成了游戏的三个方面:主驱动循环(让尚未被碰撞的兔子上下跳跃)和两个事件(一个处理幽灵与兔子的碰撞,另一个根据玩家按下的方向键移动幽灵)。

现在,让我们开始编写代码。

以下是实现游戏逻辑的核心步骤:

  1. 初始化游戏状态:设置兔子数组和游戏结束标志。
  2. 主驱动循环:使用 while 循环检查游戏是否结束,并在循环内让存活的兔子跳跃。
  3. 碰撞事件:为幽灵添加碰撞侦听器,当碰撞发生时,遍历兔子数组,找到被碰撞的兔子并将其“移除”(隐藏并移走)。
  4. 按键事件:为键盘添加按键侦听器,根据按下的方向键(左、右、上)控制幽灵的转向和移动。

本节课中,我们一起学习了如何规划一个碰撞检测游戏的流程,理解了主驱动循环和事件处理的核心概念,并概述了实现游戏功能所需的代码步骤。通过将这些部分组合起来,你将能够创建一个由玩家控制的互动游戏。

112:碰撞检测演示

在本节课中,我们将学习如何构建一个简单的游戏,其中幽灵角色需要与屏幕上的兔子发生碰撞。我们将重点实现游戏的主循环、碰撞检测逻辑以及键盘控制事件。

概述

我们将分步构建一个游戏。项目中已包含一个幽灵角色和一个兔子数组。幽灵的透明度被设置为0.7,以便玩家能部分看透它。摄像机被设置为幽灵的“载具”,这意味着玩家的视角将跟随幽灵移动。每个兔子都有一个名为 hop 的简单过程,使其在原地上下跳动。我们的目标是编写代码,让未被碰撞的兔子持续跳动,并处理幽灵与兔子之间的碰撞事件。

构建游戏主循环

上一节我们介绍了项目的基本设置,本节中我们来看看如何创建游戏的主驱动循环,让未被碰撞的兔子持续跳动。

首先,点击“我的第一个方法”,然后拖入一个 do in order 块。这是因为我们需要按顺序执行两件事:首先是一个 while 循环,只要还有兔子可见,就让它们跳动;其次是在游戏结束后,让幽灵发表一句祝贺玩家的声明。

让我们从 while 循环开始。将 while 块拖入 do in order 中,并暂时选择 true 作为占位符条件。实际上,我们希望进入循环的条件是:至少有一只兔子仍然可见

我们需要编写一个布尔函数来判断是否至少有一只兔子可见。

以下是创建此函数的步骤:

  1. 点击“场景”,然后添加一个场景函数。
  2. 将返回类型设置为 Boolean,因为此函数将返回 truefalse
  3. 将函数命名为“至少有一只兔子可见”。
  4. 在函数体内,拖入一个 for each in 块。我们将遍历兔子数组,寻找可见的兔子。
  5. 迭代器类型选择“图库类”中的 Bunny
  6. 将项目名称设为“bunnyIterator”,并将数组关联到我们的“bunnies”数组。
  7. 在循环体内,拖入一个 if 语句,暂时选择 true 作为条件。
  8. 将条件改为:检查迭代器兔子的不透明度是否大于0。这需要几个步骤:
    • 将条件改为关系判断(小数),选择“大于”。
    • 将左侧占位符改为 this.bunny 的“不透明度”属性。
    • this.bunny 改为 bunnyIterator
    • 将右侧值设为自定义小数 0
  9. 如果条件为真(即兔子可见),则立即 return true
  10. for each 循环结束后,拖入一个 return false 语句。这表示如果遍历了所有兔子都没有找到可见的,则返回假。

注意:最后的 return false 语句必须放在函数末尾,而不是 if 语句的 else 分支中。否则,只要第一只兔子不可见,游戏就会错误地判定为结束。

现在,回到“我的第一个方法”。我们可以将 while 循环的条件从 true 替换为我们刚写的函数“至少有一只兔子可见”。这样,只要还有兔子可见,循环就会继续。

while 循环内部,我们需要让所有可见的兔子跳动。以下是实现步骤:

  1. 拖入一个 for each in 块到 while 循环内。
  2. 类型再次选择“图库类”中的 Bunny,项目名称设为“bunnyIterator”,关联到“bunnies”数组。
  3. 在循环体内,拖入一个 if 语句,检查当前迭代的兔子是否可见(不透明度 > 0)。设置条件的方法与在函数中完全相同。
  4. 如果兔子可见,则调用它的 hop 过程。这同样需要两步:先拖入 this.bunnyhop 过程,然后将 this.bunny 改为 bunnyIterator

最后,在 while 循环结束后,让幽灵说一句话来祝贺玩家。将对象改为 this.ghost,然后拖入一个“说”指令,内容可以是“干得漂亮”,持续时间设为几秒。

现在运行项目,你会看到所有尚未被碰撞的兔子在同步地上下跳动。

实现碰撞检测事件

上一节我们完成了游戏的主循环,本节中我们来看看如何实现碰撞检测,让幽灵能够“抓住”兔子。

首先,点击“初始化事件监听器”,然后添加一个事件监听器。选择“位置与方向”类别,添加一个 碰撞开始监听器

我们需要指定哪些对象可能发生碰撞:

  • 集合A:设置为自定义数组,并添加 this.ghost(幽灵)。
  • 集合B:设置为 this.bunnies(兔子数组)。

这样,我们就创建了一个监听幽灵与任何兔子之间碰撞的事件。

当碰撞发生时,我们需要找出具体是哪只兔子与幽灵相撞。以下是处理碰撞的步骤:

  1. 在碰撞事件的处理块中,拖入一个 for each in 块,用于遍历兔子数组。
  2. 类型选择“图库类”中的 Bunny,项目名称设为“bunnyIterator”,关联到“bunnies”数组。
  3. 在循环体内,拖入一个 if 语句。我们需要检查当前迭代的兔子(bunnyIterator)是否就是碰撞事件中“集合B”的那个对象。
  4. 将条件设置为关系判断中的“是同一事物”。左侧放入 bunnyIterator,右侧放入从事件中获取的“集合B中的项目”。
  5. 如果匹配成功(即这只兔子就是被撞的),我们需要按顺序做两件事:将其不透明度设为0(使其不可见),然后将其向下移动10个单位(移出视野)。
  6. if 语句内拖入一个 do in order 块。
  7. do in order 内,首先设置兔子的不透明度为0:将对象改为 bunnyIterator,然后使用“设置不透明度为”指令,值设为 0
  8. 接着,让同一只兔子向下移动10个单位:使用“移动”指令,方向“下”,距离 10 米。
  9. 将这两个指令的持续时间都设为 0 秒,使变化立即发生。

添加键盘控制事件

上一节我们实现了碰撞检测,本节中我们来看看如何添加键盘控制,让玩家能够操作幽灵移动。

我们需要添加一个按键事件来处理玩家按下左、右和上箭头键。

  1. 再次点击“添加事件监听器”,选择“键盘”类别,添加一个 按键按下监听器
  2. 我们需要用 if 语句来检查具体按下了哪个键。首先拖入一个 if 语句。
  3. 将条件设置为检查事件中的“按键是”属性,并选择“左箭头”键。
  4. 如果左箭头键被按下,让幽灵向左转0.01圈。将对象改为 this.ghost,使用“转向”指令,方向“左”,量 0.01 圈,持续时间设为 0.1 秒使其反应迅速。
  5. 在第一个 if 语句的“否则”分支后,添加另一个 if 语句,检查按键是否是“右箭头”键。
  6. 如果是,让幽灵向右转0.01圈,持续时间同样为0.1秒。
  7. 在第二个 if 语句的“否则”分支后,添加最后一个 if 语句,检查按键是否是“上箭头”键。
  8. 如果是,让幽灵向前移动0.05米,持续时间设为0.1秒。

为了让按键可以持续响应(按住键时连续移动),我们需要修改事件策略。点击事件上的“添加细节”按钮,选择“多重事件策略”,然后选择“组合按键按下”。

总结

本节课中我们一起学习了如何构建一个完整的简单游戏。我们实现了游戏的主循环,让可见的兔子持续跳动;编写了碰撞检测逻辑,使幽灵与兔子碰撞后兔子会消失;并添加了键盘控制事件,让玩家能够操作幽灵移动。通过组合这些元素,我们创建了一个可交互的游戏体验。现在你可以运行项目,控制幽灵去碰撞兔子,看看效果如何。

114:30秒内捕捉兔子

在本节课中,我们将学习如何修改现有的“碰撞兔子”游戏,为其增加一个30秒的时间限制。我们将探讨实现此功能所需的具体修改步骤。

概述

上一节我们完成了基础的兔子碰撞游戏。本节中,我们将为游戏增加一个计时器,并修改游戏逻辑,使玩家必须在30秒内完成所有碰撞任务。这涉及到添加新对象、修改事件以及调整主驱动循环。

详细修改步骤

以下是实现30秒时间限制所需的五项核心修改。

1. 添加计时器

首先,我们需要在游戏场景中添加一个可视化的计时器。计时器本质上是一个3D文本对象,用于显示剩余时间。你可以复用上周创建的计时器,或者新建一个。

同时,我们需要创建一个变量来追踪剩余时间,并编写一个递减过程。这个过程需要完成两项工作:减少时间变量,并更新屏幕上显示的文本。

2. 解决计时器跟随问题

本游戏中,幽灵会移动,而摄像机以幽灵为载具,会随之移动。这会导致一个问题:一旦幽灵开始移动,固定在场景中的计时器将不再显示在屏幕上。

解决方案是:将计时器的载具也设置为幽灵。这样,计时器会始终跟随幽灵移动,稳定地停留在屏幕左下角。

3. 修改事件

我们需要对事件系统进行两处修改。

第一,添加一个每秒运行一次的事件。只要还有剩余时间,这个事件就会调用上述的递减过程,将剩余时间减少一秒。

第二,修改按键响应过程。我们只希望在还有时间时,左、右、上方向键的按下操作才有效。因此,我们可以在整个按键处理过程的外层包裹一个if语句,仅在时间大于零时才处理按键。

4. 修改主驱动循环

接下来,我们需要在my first method中的主游戏驱动循环里做两处调整。

目前,只要至少有一只兔子未被碰撞,它就会持续跳跃。我们需要修改这个条件:只有当还有剩余时间时,兔子才继续跳跃。

5. 处理游戏失败情况

最后,我们不能总是祝贺玩家。存在一种情况:玩家未能在30秒内成功碰撞全部10只兔子。

在这种情况下,我们需要让幽灵告诉玩家,他/她未能成功完成挑战。

总结

本节课我们一起学习了为“碰撞兔子”游戏增加30秒时间限制的完整方案。构建精彩游戏的主要难点在于理解游戏流程,即清晰地知道在项目的哪些位置需要进行修改。请确保你理解每一项修改的必要性,并对为何要调整特定事件和主循环形成良好的直觉。

在理解了这些修改方案后,让我们进入下一节课,动手实现这些变化。

115:限时碰撞兔子演示 🐰⏱️

在本节课中,我们将学习如何修改之前“幽灵碰撞兔子”的项目,为游戏玩家增加30秒的时间限制,以碰撞所有兔子。我们将从已完成的基础项目开始,通过添加计时器、创建和修改事件,以及调整主游戏循环来实现这一功能。

概述

我们将创建一个计时器,显示剩余时间,并确保它跟随幽灵移动。同时,我们将修改事件处理逻辑,使得只有在时间未耗尽时,玩家才能控制幽灵移动。最后,我们还会调整游戏循环和胜利条件,以检查玩家是否在规定时间内碰撞了所有兔子。

创建计时器

上一节我们完成了基础的碰撞游戏,本节中我们来看看如何添加时间限制。首先,我们需要在场景中创建一个可视化的计时器。

进入场景设置模式,点击“形状与文本”选项。像之前一样,拖拽一个新的文本模型到屏幕左下角。将其命名为 timer,并设置初始值为 30,代表30秒。文本颜色设为白色,不透明度设为1以确保可见。如果文本尺寸过大,可以按比例调整其高度,例如设置为 0.19

接下来,我们需要让计时器跟随幽灵移动,这样玩家在控制幽灵时也能看到剩余时间。将计时器的“载体”属性设置为幽灵。请注意,在场景设置中设置载体时,Alice可能会错误地将其标记为红色,但这并不影响功能。

添加时间变量与递减过程

创建好可视化的计时器后,我们需要一个变量来在后台记录剩余时间。

点击“编辑代码”,进入场景视图,选择文本模型。为其添加一个变量,类型为“整数”,命名为 timeLeft,并初始化为 30

现在,我们需要创建一个过程来减少剩余时间。为文本模型添加一个过程,命名为 decrease。这个过程将执行两个操作:

  1. timeLeft 变量减1。
  2. 更新文本模型的显示值,以反映新的剩余时间。

以下是该过程的实现逻辑:

// 减少剩余时间
this.timeLeft = this.timeLeft - 1
// 更新文本显示
this.text = this.timeLeft

创建与修改事件

有了计时变量和递减过程,接下来我们需要设置一个事件,让时间每秒自动减少。

点击“初始化事件监听器”选项卡,添加一个“时间”事件监听器。将其设置为每秒触发一次。在这个事件的处理逻辑中,我们需要加入一个判断:只有当剩余时间大于0时,才调用 decrease 过程来减少时间。

同时,我们还需要修改“按键按下”事件的处理逻辑。我们希望玩家只有在时间未耗尽时才能控制幽灵。因此,在这个事件处理器的开头,添加一个相同的判断条件:if (timeLeft > 0)。只有满足条件时,才执行后续处理按键移动幽灵的代码。

修改主游戏循环

最后,我们需要调整驱动游戏的主循环(myFirstMethod)和游戏结束的判断逻辑。

首先,将 while 循环的条件从 true 改为 timeLeft > 0。这样,当时间耗尽时,游戏循环会自动停止。

循环结束后,我们需要判断玩家是否获胜。在循环之后添加一个 if 语句:

  • 如果场景中仍有兔子可见(atLeastOneBunnyCanBeSeen),说明玩家未能在时间内碰撞所有兔子,则让幽灵说“下次好运”。
  • 否则,说明玩家成功碰撞了所有兔子,则让幽灵说“干得漂亮”。

总结

本节课中,我们一起学习了如何为“幽灵碰撞兔子”游戏添加时间限制。我们创建了一个跟随幽灵的计时器,通过变量和过程管理剩余时间,并修改了事件和主循环逻辑,使得游戏只在规定时间内进行。最终,游戏会根据玩家是否在30秒内碰撞所有兔子来给出相应的反馈。通过这个练习,你掌握了在Alice中实现基础游戏计时机制的方法。

116:随机给兔子着色 🎨

在本节课中,我们将学习如何修改兔子碰撞游戏,为不同颜色的兔子设定不同的分数值。我们将重点讲解如何编写一个过程,让兔子在跳跃时随机改变其颜色。


概述

在上一节中,我们实现了兔子与玩家的碰撞检测和计分功能。本节中,我们将引入一个重要的新机制:随机改变兔子的颜色。这将为游戏增加多样性,因为不同颜色的兔子将对应不同的分数(例如,蓝色兔子得2分,红色兔子扣1分,白色兔子得1分)。为了实现这一点,我们需要修改游戏代码,核心任务是创建一个过程,让兔子在跳跃时有一定概率改变其颜色。


随机改变颜色的逻辑

我们不希望兔子每次跳跃都改变颜色,那样会过于混乱。一个合理的设定是:兔子大约每跳跃四次,改变一次颜色。如何实现这种“有时发生”的效果呢?

最直接的方法是让爱丽丝生成一个介于0到3之间的随机整数。每次生成数字0时,我们就改变兔子的颜色;如果生成1、2或3,则保持颜色不变。

以下是实现此逻辑的代码框架:

if (random number from 0 to 3 == 0) then
    // 调用改变颜色的过程
end if

如何实际改变颜色

下一个挑战是:具体如何改变颜色?一个初步想法是生成一个0到2的随机数,分别对应白色、红色和蓝色。但这种方法有一个问题:如果生成的数字对应的颜色与兔子当前颜色相同,那么兔子实际上就没有改变颜色。

我们需要一个更可靠的算法:确保新颜色与旧颜色不同

以下是该算法的步骤:

  1. 将变量 oldPaint 设置为兔子当前的颜色。
  2. 将另一个变量 newPaint 初始化为 oldPaint
  3. 进入一个循环:oldPaint 等于 newPaint,重复以下操作:
    • 生成一个0到2的随机数。
    • 根据随机数更新 newPaint(0=白,1=红,2=蓝)。
  4. 当循环退出时,newPaint 必定与 oldPaint 不同。
  5. 将兔子的颜色设置为 newPaint

这个算法可以形式化地表示为:

oldPaint = bunny.getPaint()
newPaint = oldPaint
while (oldPaint == newPaint):
    randomNum = random integer from 0 to 2
    if (randomNum == 0): newPaint = white
    if (randomNum == 1): newPaint = red
    if (randomNum == 2): newPaint = blue
bunny.setPaint(newPaint)

虽然这需要一些步骤,但它能有效确保兔子每次颜色改变都是可见的。


整合到游戏中

现在,我们已经设计好了随机改变颜色的逻辑。接下来,我们需要:

  1. 将上述算法编写成一个名为 changePaintSometimes 的过程。
  2. 在兔子每次跳跃(即在 bunny.hop 方法中)时调用这个过程。

这样,兔子在游戏过程中就会以一定的概率改变颜色,为后续实现不同颜色对应不同分数的规则打下基础。


总结

本节课中,我们一起学习了如何为游戏中的兔子添加随机颜色变化的功能。我们探讨了如何利用随机数控制事件发生的概率,并设计了一个算法来确保兔子每次颜色改变都是有效的(即新旧颜色不同)。这是实现“不同颜色兔子有不同分值”这一游戏机制的关键第一步。

在接下来的课程中,我们将利用这里创建的彩色兔子,重新定义游戏得分规则和胜利条件。

117:碰撞红白蓝三色兔子演示 🐰🎨

在本节课中,我们将学习如何修改游戏,以处理具有不同分值的三色兔子。我们将让蓝色兔子值2分,白色兔子值1分,而红色兔子值-1分。游戏的新目标是让玩家获得至少5分。

正如之前提到的,我们需要完成四项任务。首先,我们需要让兔子定期改变颜色。其次,我们需要将获胜条件改为达到5分,而不是与所有兔子碰撞。第三,我们需要修改增加分数的过程,以适应不同兔子的不同分值。最后,我们需要修改碰撞检测算法,根据玩家碰撞到的兔子颜色来增加或减少游戏分数。

任务一:定期改变兔子颜色 🎨

上一节我们介绍了游戏的基本框架,本节中我们来看看如何让兔子随机变色。我们已经编写了这个过程。

如果我们点击这个点状兔子,然后点击“编辑”菜单下的“有时改变颜色”过程,可以看到这个过程与上一节描述的一致。当这个过程被调用时,大约有25%的概率,这只兔子会改变其颜色。

现在,让我们编辑兔子的跳跃过程。我们将把“调用‘有时改变颜色’”的指令拖拽到跳跃过程中,放在兔子开始上下移动之前。

以下是修改跳跃过程的步骤:

  1. 打开兔子的“跳跃”过程。
  2. 在兔子移动指令之前,插入“调用‘有时改变颜色’”的指令。

我们可以点击“运行”来测试,观察兔子是否会在红色、白色、蓝色等颜色之间随机变化。测试表明,这个功能运行正常。

任务二:修改获胜条件 🏆

接下来,我们需要将获胜条件修改为获得至少5分。让我们点击“我的第一个方法”。

我们需要修改if语句的布尔条件,我们希望条件是玩家的分数大于或等于5。

以下是修改步骤:

  1. 点击条件语句的下拉菜单。
  2. 由于分数是整数,我们选择“关系-整数”。
  3. 选择“大于或等于”运算符。
  4. 第一个值,我们先放一个占位符“1”。
  5. 第二个值,选择“自定义整数”并输入“5”。
  6. 为了访问玩家分数,我们需要将占位符“1”替换为“this.player.score”。我们可以从函数列表中选择“this.player.score”并将其拖拽覆盖“1”。
  7. 最后,我们需要交换两个条件分支的内容。因为逻辑反了:如果玩家分数至少为5,幽灵应该说“干得好”;否则,幽灵应该说“抱歉,下次好运”。

任务三:更新分数增加过程 ➕

现在,让我们更新增加分数的过程,允许调用者指定增加的数值。

我们点击“doScoresProc”,然后编辑“increase”过程。我们将添加一个名为“amount”的整数参数。由于事件处理程序中已经调用了这个过程的旧版本,我们需要勾选一个复选框,表示稍后会在事件处理程序中修改调用,以传递我们希望的数值。

最后,我们只需将原来增加“1”分的指令,改为增加由参数“amount”传入的数值。

任务四:修改碰撞检测处理程序 🔄

最后,我们需要根据被碰撞兔子的颜色,在碰撞检测处理程序中调用更新后的“increase”过程,并传入正确的分值。

我们点击“初始化事件监听器”,找到“碰撞开始”事件。在调用“increase”之前,我们需要检查兔子的颜色,以确定应该增加多少分数。

在“调整分数”的注释之后,我们创建一个变量来引用迭代器兔子的颜色。变量类型选择“其他类型”中的“Paint”,并命名为“iteratorPaint”。初始值可以设为黑色作为占位符。

然后,我们将其实际值设置为“bunnyIterator.getPaint”。

接下来,我们添加一个if语句来测试“iteratorPaint”的颜色,以决定增加多少分。

以下是判断逻辑的步骤:

  1. 如果“iteratorPaint”等于红色,则调用“increase”过程,传入值“-1”。
  2. 否则,在else分支中再添加一个if语句。
  3. 如果“iteratorPaint”等于白色,则调用“increase”过程,传入值“1”。
  4. 如果颜色既不是红色也不是白色(即蓝色),则在第二个else分支中调用“increase”过程,传入值“2”。

游戏测试与总结 🎮

现在,让我们运行游戏并尝试获胜。在游戏中,我们需要避开红色的兔子(扣1分),努力碰撞白色的兔子(得1分)和蓝色的兔子(得2分),目标是使总分达到或超过5分。

本节课中我们一起学习了如何通过四个步骤来增强游戏:让兔子随机变色、修改获胜条件、更新分数增加过程以及根据兔子颜色调整碰撞得分。这些修改使得游戏更具策略性和趣味性。

119:场景激活监听器事件

在本节课中,我们将学习如何使用场景激活监听器来让多个动画同时、独立地运行,从而解决不同动画执行时间不一致的问题。

概述

我们常常希望不同的动画能够同时发生。考虑以下例子:我们有一个乌龟数组和一个兔子数组。我们希望所有的乌龟同时爬行,所有的兔子同时跳跃。由于我们使用了 each in together 结构,这些迭代会同时发生。即使乌龟和兔子的数量不同,所有兔子也会同时跳跃,所有乌龟也会同时爬行。

然而,假设跳跃过程需要0.7秒完成,而爬行过程需要1.5秒完成。实际发生的情况是:所有兔子会先完成跳跃,然后它们会静止等待0.8秒,直到乌龟完成爬行。正如我们之前提到的,在 do together 块中的所有动画,理想情况下应该花费相同的时间来完成。

这说起来容易,但在实践中可能难以实现。乌龟爬行就是比兔子跳跃慢。如果一个项目中同时包含需要反复爬行的乌龟和跳跃的兔子,我们似乎就遇到了问题。

场景激活监听器

爱丽丝提供了一个“场景激活监听器”事件来帮助解决这个问题。当用户点击运行按钮时,爱丽丝会寻找场景激活监听器并立即开始运行它们。

我们可以利用场景激活监听器,而不是将乌龟爬行和兔子跳跃作为“我的第一个方法”的一部分。我们可以简单地创建两个场景激活监听器。这样做之后,我们就不再需要担心乌龟爬行和兔子跳跃的相对时间。乌龟以其速度爬行,兔子以其速度跳跃,两者独立进行。

当对象需要移动,且不应与其他对象的移动协调时,场景激活监听器会非常有用。

但也要注意,场景激活事件只在你点击“运行”按钮时运行一次。这不同于我们见过的其他事件(例如碰撞事件),后者在每次碰撞发生时都会触发。

接下来我们将看到一个使用场景激活监听器的精彩示例。

总结

本节课中,我们一起学习了场景激活监听器的概念和应用。我们了解到,通过创建独立的场景激活监听器,可以让执行时间不同的动画(如乌龟爬行和兔子跳跃)同时启动并独立运行,无需相互等待。这解决了在 do together 块中动画时长必须一致的限制,为制作更复杂、更自然的场景提供了便利。同时,我们也要记住,场景激活事件仅在场景启动时运行一次。

120:躲避障碍物碰撞兔子 🐰👻

在本节课中,我们将学习如何为游戏增加难度,通过引入会四处游荡的白色兔子作为障碍物。如果幽灵角色与任何一只白兔发生碰撞,它将被重置回起始位置。我们将通过创建两个新的事件监听器来实现这一功能。


上一节我们介绍了游戏的基本交互,本节中我们来看看如何添加障碍物来增加挑战性。我们将创建两个关键的事件:一个用于在场景激活时让兔子开始移动,另一个用于检测幽灵与兔子的碰撞。

场景激活事件:让兔子动起来 🏃‍♂️

首先,我们需要在游戏开始时让所有白色兔子开始四处游荡。这可以通过一个“场景激活”事件监听器来实现。当项目开始运行时,此事件将被触发。

以下是实现步骤:

  1. 创建一个名为 startRabbitsWandering 的事件。
  2. 将事件类型设置为 When the scene starts
  3. 在事件处理程序中,我们需要让数组中所有的白兔同时开始移动。为此,我们将使用一个 For all in order 循环(迭代器)。
  4. 在循环体内,为数组中的每一只兔子调用 wander 方法。

对应的伪代码逻辑如下:

When the scene starts
    For each rabbit in the whiteRabbits array
        Do rabbit.wander

碰撞检测事件:处理幽灵与兔子的碰撞 💥

接下来,我们需要检测幽灵何时与白兔发生碰撞,并在碰撞发生时将幽灵移回起点。这需要一个“碰撞开始”事件监听器。

以下是实现步骤:

  1. 创建一个新的 While something is true 事件,并选择 While a collision occurs 作为条件。
  2. 设置碰撞双方:第一对象为幽灵 (ghost),第二对象为白兔数组 (whiteRabbits)。
  3. 在事件处理程序中,我们只需要一条指令:将幽灵移动到其原始起始位置。

对应的伪代码逻辑如下:

While ghost collides with any object in whiteRabbits
    Do ghost.moveTo(originalStartPosition)

本节课中我们一起学习了如何通过事件驱动编程来增加游戏复杂度。我们引入了会移动的障碍物(白兔),并实现了碰撞检测与惩罚机制(将幽灵重置)。通过组合使用 场景激活监听器碰撞开始监听器,我们能够轻松地创建出更具互动性和挑战性的游戏场景。

121:躲避障碍物碰撞兔子演示 🎮

在本节课中,我们将学习如何修改项目,让幽灵在追逐兔子的同时,学会躲避白色的兔子障碍物。我们将通过添加障碍物、设置碰撞事件以及让障碍物随机移动,来创建一个更具挑战性的游戏。


上一节我们介绍了游戏的基本框架,本节中我们来看看如何为游戏添加障碍物和碰撞检测。

我们首先对场景设置进行了几处修改。点击“设置场景”按钮,可以看到场景中已经添加了五只白色兔子作为障碍物。

此外,场景中还添加了一个对象标记,名为“幽灵起始位置”。当幽灵与任何一只白色兔子发生碰撞时,它将被移回这个起始位置。

点击“编辑代码”按钮,可以看到另外两处主要改动。

第一处改动在“场景”选项卡中。滚动到页面底部,会发现新增了一个场景属性。这是一个包含五只白色兔子的数组,名为 obstacles

第二处改动在“白兔”对象中。为白兔新增了一个名为“漫游”的过程。这个过程会让白兔随机向前移动一小段距离,并随机向左或向右转动一小点角度。


为了让游戏一开始所有白兔就开始漫游,我们需要创建一个事件。

以下是创建该事件的步骤:

  1. 转到“初始化事件监听器”选项卡,添加一个“场景激活监听器”。
  2. 从列表底部添加名为“场景激活监听器”的事件监听器。
  3. 我们希望当游戏剩余时间大于零时,所有白兔持续漫游。
  4. 拖入一个 while 循环,将条件从 true 改为 getTimeLeft() > 0
  5. while 循环内部,拖入一个 each in together 循环,用于同时遍历所有白兔。
  6. 将循环类型设为 Gallery.WhiteRabbit,迭代器名称设为 whiteRabbitIterator,数组使用 obstacles
  7. 最后,在循环体内,调用 whiteRabbitIteratorwander 过程。

接下来,我们需要创建幽灵与白兔障碍物之间的碰撞事件。

以下是创建碰撞事件的步骤:

  1. 点击底部的“添加事件监听器”,选择“位置与方向”,然后添加“碰撞开始监听器”。
  2. 第一个碰撞集设为“自定义数组”,并只添加幽灵对象。
  3. 第二个碰撞集直接选择我们之前创建的 obstacles 数组。
  4. 在碰撞事件触发时,我们希望幽灵被移回起始位置。因此,在事件内部,拖入幽灵的 moveAndOrientTo 方法,并选择“幽灵起始位置”作为目标。

现在,游戏已经可以运行了。你的目标是点击白色和蓝色的兔子来得分,同时要小心躲避那些四处漫游的白色兔子障碍物。

点击运行,使用方向键控制幽灵。如果不小心撞到白兔,你会被弹回起点。努力在时间结束前获得尽可能高的分数吧!


本节课中我们一起学习了如何为游戏添加动态障碍物。我们创建了一个障碍物数组,为障碍物编写了随机移动的行为,并设置了碰撞检测事件来增加游戏难度。通过这些步骤,你的游戏变得更加丰富和具有挑战性。

122:为游戏添加多关卡 🎮

在本节课中,我们将学习如何为碰撞得分游戏添加多关卡功能。通过引入不同难度级别的关卡,可以显著提升游戏的可玩性和挑战性。我们将重点讲解如何管理关卡状态,以及在玩家完成一个关卡后如何重置游戏场景。


概述

本节教程将指导你为游戏创建多个关卡。我们将构建一个包含两个关卡的示例,但你可以轻松扩展至更多。核心思想是让后续关卡比初始关卡更具挑战性。例如,我们可以要求玩家获得更高的分数、添加更多障碍物,或者让游戏对象的行为更复杂。在本例中,我们将在更难的关卡中让兔子在跳跃时随机移动,这比在原地跳跃的兔子更难被碰撞到。


核心挑战与解决方案

在游戏中处理多关卡存在两个概念上的挑战。

第一:关卡状态管理

游戏的每个部分都需要知道玩家当前处于哪个关卡。最简单的方法是使用一个场景属性或变量。

我们可以创建一个名为 level 的场景属性,并初始设置为 1,表示玩家从第一关开始。

代码示例:

场景属性:level = 1

第二:关卡重置

一旦玩家成功完成一个关卡,所有游戏元素都需要被重置,以便开始下一关。这包括重置计时器,可能还需要重置玩家的分数。

更具挑战性的是将所有场景对象恢复到其初始位置和状态:

  • 幽灵需要回到初始位置。
  • 所有兔子需要回到初始位置,颜色变回白色,不透明度重置为 1
  • 所有障碍物需要回到初始位置。

实现兔子位置重置

为了将兔子移回起始位置,我们将使用一个巧妙的编程技巧。

在场景设置阶段,我们为每只兔子添加了一个对象标记。然后,我们创建了另一个数组,其中包含了所有这些对象标记。

关键步骤:

  • 如果 Bunny1 在兔子数组的 0 号位置,我们确保将 Bunny1 的标记放在“兔子起始位置”数组的 0 号位置。
  • 如果 Bunny2 在兔子数组的 1 号位置,我们确保将 Bunny2 的标记放在“兔子起始位置”数组的 1 号位置。

这样,我们就得到了一对并行数组


使用 While 循环遍历数组

为了同时处理这两个并行数组,我们需要一种新的遍历数组的方法:使用 while 循环(也称为数组索引循环),而不是 for each ineach in together 迭代器。

我们需要使用 while 循环的原因是,我们必须同时访问两个数组中相同索引位置的元素,而迭代器一次只能遍历一个数组。

实现方法:

  1. 我们使用一个名为 index 的整数变量,并将其初始值设为 0
  2. 在循环中,我们可以让兔子数组中索引为 index 的元素,移动并朝向“兔子起始位置”数组中索引为 index 的元素。
  3. 每次循环后,将 index 的值增加 1,直到遍历完所有兔子。

代码逻辑示例:

index = 0
while index < 兔子数组的长度
    兔子数组[index].moveTo(起始位置数组[index])
    兔子数组[index].setPaint(白色)
    兔子数组[index].setOpacity(1)
    index = index + 1

这种 while 循环方法虽然不如迭代器优雅,但它允许我们将所有10只兔子移回其原始位置,并将它们的颜色重置为白色,不透明度重置为 1


完成重置并开始新关卡

在将兔子重置到初始场景中的原始位置并更新关卡级别后,游戏就准备就绪,可以再次开始了。

通过这种方式,我们建立了一个清晰的关卡推进和重置机制,为游戏增添了结构和可重复游玩的乐趣。


总结

本节课中,我们一起学习了如何为游戏添加多关卡系统。我们介绍了管理关卡状态的核心变量,并详细讲解了在关卡切换时如何重置游戏对象,特别是利用并行数组和 while 循环来精确恢复兔子的初始状态。掌握这些概念后,你就可以为自己的游戏设计出丰富多样的关卡挑战了。

123:准备修改碰撞游戏 🎮

在本节课中,我们将学习如何修改现有的障碍物碰撞游戏,以支持多个关卡。我们将添加一个场景属性来追踪关卡,调整兔子的跳跃行为,并重构游戏的主循环逻辑。


上一节我们讨论了为游戏添加关卡功能的需求。本节中,我们来看看具体需要实施哪些修改。

我们需要对现有的障碍物碰撞游戏进行几项修改,以实现多个关卡。

我们已经讨论过需要添加一个场景属性来追踪当前关卡。

我们也描述了重置兔子、障碍物和幽灵的过程。

我们需要让兔子在玩家进行第1关(较低难度)时执行一种跳跃动作。

如果玩家成功通过第1关并进入第2关,兔子应该执行一种徘徊跳跃,而不是上下跳跃。这使得与兔子碰撞变得更加困难。

一个更复杂的修改是,我们需要在每个游戏流程外围包裹一个外层的while循环。条件是当关卡为1或2,并且玩家尚未输掉或赢得游戏时,执行一次游戏迭代。

这个循环需要放置在场景激活事件中障碍物随机移动的代码周围。

它也需要放置在我的第一个方法中的主while循环周围。

我们需要做的最后一项修改是,如果玩家成功完成一个关卡(得分至少为5分),则重新定位兔子、障碍物和幽灵,并重置分数和计时器。

如果玩家未能成功完成一个关卡,我们将关卡设置为0,表示玩家已失败。

我们需要将这段代码放入我的第一个方法中,位置是在玩家尝试与蓝白兔子碰撞30秒之后。

让我们开始修改游戏。


以下是实现多关卡功能的核心步骤列表:

  • 添加关卡追踪:创建一个场景属性(例如 level)来记录当前关卡。
  • 调整兔子行为:使用条件判断,在第1关让兔子执行 hop,在第2关执行 wanderHop
  • 重构游戏循环:在主游戏逻辑外添加一个 while 循环,条件为 level == 1 or level == 2
  • 重置游戏状态:在玩家通过关卡后,重置所有对象位置、分数和计时器,并将关卡数加1。
  • 处理游戏结束:若玩家失败,将 level 设置为0。

本节课中我们一起学习了如何为碰撞游戏添加多关卡支持。我们引入了关卡追踪属性,根据关卡改变了游戏角色的行为,并使用外层循环控制了游戏的整体流程。最后,我们设定了关卡成功或失败后的状态重置逻辑。这些修改将使游戏更具挑战性和可玩性。

124:多关卡碰撞兔子游戏实现教程 🎮

在本教程中,我们将学习如何修改一个已有的碰撞兔子游戏,为其添加多关卡功能。我们将创建关卡变量、重置游戏对象位置的复杂过程,并修改游戏的主驱动循环,使玩家能够连续挑战两个关卡。


游戏初始状态与修改概述

上一节我们完成了基础的单关卡游戏。本节中,我们来看看当前项目的两个主要变化。

首先,我们为每只兔子和每个障碍物都添加了对象标记。你可以在场景中看到所有这些标记。

其次,如果我们点击场景选项卡并滚动到底部,会发现新增了两个数组:

  • Bunny starting positions:按兔子数组的顺序,包含了每个兔子的起始位置标记。
  • White rabbit starting positions:按障碍物数组的顺序,包含了每个白色兔子(障碍物)的起始位置标记。

创建关卡变量

首先,我们需要添加一个场景属性来追踪当前关卡。

  1. 添加一个新的场景属性。
  2. 将其命名为 level
  3. 类型选择为整数
  4. 它需要是一个变量
  5. 将其初始值设置为 1

这个 level 变量代表游戏的当前关卡。我们将其设置为 1,表示玩家将从第一关开始游戏。


修改主驱动循环中的兔子行为

接下来,在 myFirstMethod 的主驱动循环中,我们需要根据关卡改变兔子的行为。

我们希望当关卡为 1 时,兔子执行普通的 hop 动作;当关卡为 2 时,兔子执行我们之前写好的 wanderHop 动作。

以下是修改步骤:

  1. 在检查 bunnyIterator 不透明度大于 0if 语句内部,再拖入一个 if 语句。
  2. 将条件从 true 改为整数比较,判断 level 是否等于 1
  3. 如果条件为真(即关卡为1),则让 bunnyIterator 执行 hop 动作。
  4. else 部分,让 bunnyIterator 执行 wanderHop 动作。

修改后的核心逻辑代码如下:

if (bunnyIterator.opacity > 0) {
    if (level == 1) {
        bunnyIterator.hop();
    } else {
        bunnyIterator.wanderHop();
    }
}

编写重置游戏对象的过程

这是本教程最复杂的部分。我们需要编写一个名为 resetObjects 的过程,将兔子、障碍物和幽灵重置到它们的起始位置。这在玩家成功完成第一关、准备进入第二关时必须执行。

以下是该过程的实现步骤:

重置兔子数组

首先,添加注释“重置兔子到原始位置”。

  1. 创建一个名为 index 的整数变量,初始值设为 0。它将作为数组的索引。
  2. 添加一个 while 循环,条件是 index < bunnies.length,用于遍历所有兔子。
  3. 在循环内,我们需要对每只兔子执行三个操作:
    • 移动并转向:使用 moveAndOrientTo 过程,将 bunnies[index] 移动到 bunnyStartingPositions[index] 的位置。将持续时间设为 0 以实现瞬间移动。
    • 重置颜色:使用 setPaint 过程,将 bunnies[index] 的颜色设置为白色。
    • 重置不透明度:使用 setOpacity 过程,将 bunnies[index] 的不透明度设置为 1
  4. 循环的最后,将 index 变量增加 1

重置障碍物数组

接下来,添加注释“重置障碍物到原始位置”。

  1. 创建一个名为 index2 的整数变量,初始值设为 0
  2. 添加一个 while 循环,条件是 index2 < obstacles.length
  3. 在循环内,使用 moveAndOrientTo 过程,将 obstacles[index2] 移动到 whiteRabbitStartingPositions[index2] 的位置。持续时间设为 0
  4. 循环的最后,将 index2 变量增加 1

重置幽灵

最后,添加注释“重置幽灵到起始位置”。

  1. 直接对 ghost 对象使用 moveAndOrientTo 过程,将其移动到 ghostStartingPosition。持续时间设为 0

修改主驱动循环以支持多关卡

当前的主驱动循环只运行一个关卡。我们需要将其包裹在一个更大的循环中,使其能在两个关卡间连续运行。

  1. 在现有的 while 循环之前,再添加一个 while 循环。
  2. 这个外层循环的条件是 level == 1 level == 2。这表示只要玩家在玩第1关或第2关,游戏就继续。
  3. 在外层循环的开头,让幽灵说出当前关卡:"Level is " + level
  4. 将原来的内层 while 循环和计分 if 语句拖入这个新的外层循环内部。

接下来,修改计分 if 语句内的逻辑:

  • 当玩家得分 >= 5(成功完成当前关卡)时
    1. 移除幽灵说“Great job”的语句。
    2. level 的值增加 1
    3. 调用我们刚写好的 resetObjects 过程。
    4. 调用 scoreBoardresetScore 过程(已提供),将分数归零。
    5. 调用 timerresetTime 过程(已提供),将时间重置为30秒。
  • 当玩家得分 < 5(失败)时
    1. else 部分,将 level 设置为 0,表示游戏结束。

在外层 while 循环之后,添加最终判断:

  1. 添加一个 if 语句,判断 level == 3
  2. 如果为真(意味着玩家通过了第2关),让幽灵说“Nice job! You won!”。
  3. 如果为假(意味着 level0,玩家失败了),让幽灵说“Better luck next time.”。

修改场景激活事件

最后,我们还需要修改场景激活事件,确保障碍物只在正确的关卡开始游走。

  1. 找到 sceneActivated 事件。
  2. 在现有的 while 循环之前,添加一个新的外层 while 循环。
  3. 这个循环的条件同样是 level == 1 level == 2
  4. 将原来的内层 while 循环(控制障碍物移动)拖入这个新的外层循环内部。
  5. 在外层循环内,在内层循环之后,添加一个 ghost.delay(1) 过程,延迟1秒。

添加这个延迟是为了给游戏对象留出时间,在我们调用 resetObjects 过程后能够重置到它们的起始位置。


测试游戏

至此,多关卡游戏已构建完成。点击运行按钮,尝试赢得两个关卡吧!

  • 第一关:兔子执行普通跳跃。
  • 第二关:兔子执行随机游走跳跃。
  • 成功通过两关后,幽灵会祝贺你获胜。

总结

本节课中,我们一起学习了如何将一个单关卡游戏扩展为多关卡游戏。核心步骤包括:

  1. 创建关卡状态变量
  2. 根据关卡变量改变游戏对象(兔子)的行为
  3. 编写复杂的过程来重置多个数组中的所有对象到其初始状态。
  4. 重构主驱动循环,使用外层循环控制关卡流程,并整合成功/失败的条件判断与状态重置。
  5. 同步修改初始化事件,确保游戏启动逻辑与关卡状态一致。

通过实现这些功能,你掌握了管理游戏状态、循环遍历数组以及构建更复杂游戏流程的关键技能。

126:构建你自己的冒险游戏概述 🎮

在本节课中,我们将学习如何整合之前学到的所有编程概念,构建一个“构建你自己的冒险”游戏。我们将创建三个独立的小游戏,并学习如何在它们之间切换场景,最终将它们组合成一个完整的冒险游戏。


上一节我们介绍了本周的目标,本节中我们来看看“构建你自己的冒险”游戏的基本概念。

这类游戏的核心思想是,玩家需要完成一系列不同的任务才能获胜。玩家可以自行选择完成这些任务的顺序。在我们的设计中,大部分任务本身就是玩家需要玩的小游戏。成功完成一个小游戏后,玩家将获得某种令牌。这些令牌将帮助玩家达成游戏的最终胜利目标。

为了在不同任务(即不同小游戏)之间导航,我们需要学习如何在Alice中切换场景。


在开始构建具体游戏之前,我们先来了解一下本周将要创建的三个小游戏。

以下是本周我们将要构建的三个独立游戏:

  1. 记忆游戏:我们将创建一组被打乱顺序的兔子,它们会快速按随机顺序变成紫色。玩家需要按照兔子变色的顺序点击它们。
  2. 逻辑解谜游戏:这是一个著名的计算机科学逻辑谜题。计算机会生成一个随机代码,然后玩家需要通过重新排列一组铃铛来猜测这个秘密代码。
  3. 配对游戏:几张不同颜色的卡片会面朝下放置在地面上。玩家需要找出颜色相同的卡片对。

这三个游戏将成为整个冒险游戏的一部分。对于冒险游戏,我们需要描述如何在Alice中切换场景来玩每一个独立的游戏。每个场景将代表玩家必须掌握的一个不同游戏。


上一节我们介绍了三个小游戏,本节中我们来看看实现冒险游戏的一个关键技术:场景切换。

我们将从一个包含四个不同场景的简单项目开始学习场景切换。例如,在一个场景中,我们的角色可能在沙漠里。然后,我们希望有一个过渡效果:世界变暗,然后再次变亮,此时我们的角色已经来到了一个被水环绕的岛屿上。这是我们构建这个游戏需要学习的动画技巧之一。

我们的计划是,首先设计和构建玩家需要赢得的各个小游戏。然后,我们将把这些游戏组合成一个整体的“构建你自己的冒险”游戏。


本节课中我们一起学习了“构建你自己的冒险”游戏的基本框架。我们概述了三个将要构建的小游戏(记忆游戏、逻辑解谜游戏和配对游戏),并介绍了将它们串联起来的关键技术——场景切换。现在,让我们从学习如何进行场景切换开始吧。

127:场景切换 🎬

在本节课中,我们将学习如何构建一个“设计你自己的冒险”游戏的第一步:场景切换。我们将探讨场景切换的含义,并通过一个具体的演示来展示如何在实践中实现它。

什么是场景切换? 🎥

上一节我们介绍了课程目标,本节中我们来看看场景切换的具体含义。当观看电视节目、电影或玩电子游戏时,场景切换意味着我们正在观看一些角色互动,然后场景变暗,我们什么也看不见。当场景再次亮起时,我们看到完全不同的内容。

一个经典的例子是宫崎骏的电影《哈尔的移动城堡》。在离开城堡时,有一个旋钮,可以打开门进入四个不同的地点。最初,可以选择绿色离开城堡前往市场奇平上方的山丘,或设置为蓝色进入港口天堂,红色进入金斯伯里,黑色进入威尔士。每一个选择都说明了从城堡内部到相应地点的场景变化。

在爱丽丝中实现场景切换 🏰

那么,我们如何在爱丽丝中实现这一点呢?爱丽丝中的场景非常大,可以创建多个场景。在这个例子中,我们创建了四个场景。

第一个是初始场景。有一只狗和三扇可能的大门,游戏的玩家可以引导狗进入其中一扇,以便让狗经历三种可能的冒险。红宝石、可乐瓶和骨头将是玩家成功赢得冒险后可以获得的奖励。返回按钮将用于让玩家从冒险场景返回到原始场景。老虎会告诉玩家初始挑战,而阿德莱德巴士将是玩家赢得游戏后的奖励。

让我们看看下一个场景。它实际上只是将摄像机移动到地面的不同部分,并将地面改为看起来像海洋的结果。我们添加了一个岛屿、一只猴子和一些棕榈树。我们还看到一个对象标记,这是当斑点狗需要完成岛屿冒险时,我们将移动它到的地方。我们仍然看到红宝石、可乐瓶、骨头和返回按钮。原因是,我们已经将红宝石、可乐瓶、骨头和返回按钮的载体设置为摄像机。因此,每当我们移动摄像机时,这些对象也会移动。

现在让我们看看第三个场景,一个沙漠场景。我们制作这个场景所做的只是移动摄像机,将地面颜色改为沙漠,并添加了几只乌龟。还有另一个对象标记,用于当斑点狗进入场景时,我们将移动它到的地方。

让我们看看第四个场景。我们所做的只是将摄像机移动到一个空位置,将地面改为森林地面,然后添加几个爱丽丝梦游仙境中的角色。实际上,如果我们从顶视图看场景,我们可以看到所有四个场景,尽管有些非常小。

在左边是带有斑点狗和老虎的城堡墙。在顶部中间是岛屿场景。在右上角是带有两只乌龟的沙漠场景。在右下角是带有女王的森林场景。

我们的挑战将是在场景之间移动。在场景之间移动将是一个多步骤的过程。

场景切换的步骤 📝

以下是实现场景切换的具体步骤:

  1. 场景设置:在场景设置期间,我们需要添加一个黑色矩形,并将其放置在摄像机上方,正好在摄像机的视野内。
  2. 开始切换:当需要改变场景时,我们只需将黑色矩形向下移动,使得通过摄像机唯一可见的东西是黑色矩形。
  3. 移动元素:接下来,我们将摄像机移动到指向其他场景之一。我们也将狗移动到新场景,并且需要更改地面绘制,例如为岛屿场景将其设置为水。
  4. 完成切换:最后,我们升起黑色矩形,瞧,狗就在一个新场景中了。

开始编码吧! 💻

本节课中我们一起学习了场景切换的概念及其在爱丽丝中的实现步骤。我们了解了场景切换在叙事中的意义,并通过一个多场景的例子,详细说明了如何通过添加黑色矩形、移动摄像机和对象来模拟平滑的场景过渡。接下来,我们就可以开始编写代码,将这些步骤付诸实践了。

128:场景切换演示

在本节课中,我们将学习如何在一个多场景项目中实现场景切换。我们将通过一个具体的示例,演示如何让角色通过不同的“门”进入不同的场景,并在每个场景中完成特定任务,最终赢得游戏。


项目概览

首先,我们来查看这个部分完成的项目。我们可以看到场景中的所有对象。

如果我们进入“设置场景”模式,可以让摄像机移动到摄像机标记点。

点击“摄像机标记”,然后选择“查看岛屿”。

在这里,我们可以看到除了地面颜色需要改变外,岛屿场景的一切都已设置好。

我们可以对沙漠中的乌龟场景和女王场景进行同样的设置。

我们也可以查看顶视图。从这里,我们可以拖动视图,或者向上移动。我们还可以拖动地面,看到老虎所在的场景、岛屿场景、乌龟场景,以及向下滚动一点,看到女王和其他爱丽丝角色的场景。

让我们回到摄像机的起始位置,并将摄像机移回起始位置。

现在,点击“编辑代码”,以便查看一些预先构建的代码。

如果我们点击“初始化事件监听器”,可以看到已经创建了几个事件。滚动到顶部,可以看到按键事件已经编码好,允许小狗移动:当玩家按下左箭头键时,小狗向左移动;按下右箭头键,小狗向右移动;按下上箭头键,小狗向前移动。

我们还有一个事件,当小狗与每个门或老虎发生碰撞时触发。然而,如果我们点击“处理左门碰撞”事件(让我们试着找到它),会发现还没有为这个程序编写任何代码。我们需要编写所有这些程序,以及“我的第一个方法”。

但在开始编码之前,我们仍然需要添加一个黑色矩形,以便在需要切换场景时使场景变黑。


添加场景切换遮罩

让我们回到“设置场景”模式。我们将添加一个广告牌。在“形状”文本选项卡中查找,然后拖入一个广告牌。

我们将它的背面涂成黑色,并将其命名为“CameraBlocker”,然后点击“确定”。

接下来,我们发出一个“一次性”指令,将摄像机遮罩移动并定向到摄像机的位置。使用“一次性”程序“移动并定向到”。摄像机遮罩飞过了我们。我们再发出额外的“一次性”指令,让摄像机遮罩向前移动半个单位。现在我们可以看到它了。

接着,我们让它向下移动半个单位。现在,我们只能看到黑色,这很完美。但我们还要发出最后一个“一次性”指令,将摄像机遮罩向上移开,所以让它向上移动两个单位。

最后,将摄像机遮罩的载体设置为摄像机,这样当摄像机移动时,摄像机遮罩也会随之移动。

现在,我们准备好开始编码了。点击“编辑代码”。


初始化游戏场景

让我们从“我的第一个方法”开始。我们需要在这里初始化初始场景。

首先,放入一个“顺序执行”块。我们需要同时执行几条指令,让一些物品在游戏开始时不可见。

我们需要让阿德莱德半身像不可见,因为它是在玩家获胜时出现的奖品。找到阿德莱德半身像(它是一个道具),然后找到“不透明度”属性,将其拖入并设置为0。

这将是多个需要变为不可见的命令之一,所以我们添加一个“同时执行”块,让所有这些操作同时发生。将阿德莱德半身像的不透明度设置为0,并将此操作的持续时间也设置为0,使其瞬间完成。

我们还需要让红宝石、可乐瓶和臂骨在游戏开始时不可见,因为玩家尚未成功赢得这些奖品。我们可以将此指令复制到剪贴板,然后拖入,并依次更改为红宝石、可乐瓶和臂骨。

最后,对返回按钮也进行同样的操作。这个按钮用于让玩家返回初始场景,而玩家是从初始场景开始的。返回按钮实际上是一个球体,其不透明度被设置为0.7,以便能看到球体内部,以及一个写着“返回”的3D文本。因此,我们需要将球体和名为“返回文本”的3D文本的不透明度都设置为0。

再次复制指令,拖入并更改为球体,然后再复制一次,更改为“返回文本”。

现在,我们已经将所有需要在游戏开始时隐藏的东西都设置好了。

在将所有物品设为不可见之后,让老虎说玩家需要给老虎带来一瓶可乐和一根骨头。找到老虎,让它说:“请给我一瓶可乐和一根骨头来赢得奖品。”这句话有点长,所以将其持续时间设置为2秒。

最后,让斑点狗告诉玩家如何移动以及与游戏中的物体互动。找到斑点狗,让它说:“使用箭头键移动我。如果你把我移到门那里,我会穿过去。让我与物体碰撞,其中一些会和你说话。”这句话也较长,将其持续时间也设置为2秒。

运行项目,查看一切是否设置正确。点击“运行”,许多东西都不可见了,老虎和斑点狗都说话了,看起来很好。


处理左门碰撞

我们要求玩家必须先获得红宝石,才能获得可乐瓶。左门是玩家获得可乐瓶的途径。

让我们开始编写处理与左门碰撞的程序。点击“处理左门碰撞”并编辑。我们需要在这里做几件事,以便将摄像机和狗移动到岛屿场景。

首先,放入一个“顺序执行”块。首先,让摄像机遮罩向下移动两个单位,这将使一切变黑。

接着,让摄像机移动并定向到“查看岛屿”标记点,以改变视图。

然后,还需要让狗移动并定向到“岛屿上的狗”标记点。

我们还需要让返回按钮可见,以便玩家在获得可乐瓶后可以点击它返回初始场景。添加代码将球体的不透明度设置为0.7,然后将“返回文本”的不透明度设置为1。

最后,将摄像机遮罩向上移回两个单位。

运行项目进行测试。我们看到指令,然后引导斑点狗进入左门。哎呀,场景没有切换到岛屿,摄像机没有移动。检查代码,发现第二行指令没有将移动对象改为摄像机。修正这个错误。

再次运行世界。引导斑点狗进入左门。这次切换到了岛屿,但地面是草地而不是水。我们可以修复这个问题。


优化场景切换

第一个问题是场景变黑的时间太长。我们可以通过将所有“移动并定向到”命令放入一个“同时执行”块中来修复,让它们同时发生。这样,摄像机遮罩降下,在变黑的同时我们改变一切,然后摄像机遮罩再升起。

更严重的问题是切换到岛屿时地面不是蓝色的。在“同时执行”块中,我们需要改变地面。找到地面,使用“设置颜料”操作,将其更改为“海洋”。

再次运行项目。这次场景切换更快了,并且有了海洋,看起来好多了。


添加岛屿场景事件

我们仍然需要为这部分冒险添加事件处理。我们将添加狗与猴子之间的碰撞事件。

如果红宝石可见(即狗已经完成了获取红宝石的任务),猴子将通过将其不透明度设置为1来给狗可乐瓶。否则,猴子会告诉狗需要先获得红宝石。

点击“初始化事件监听器”,添加一个新的“碰撞开始监听器”。在“位置方向”下选择“添加碰撞开始监听器”。需要为集合A和集合B选择自定义数组。

集合A只包含斑点狗。集合B只包含金丝猴。

对于代码,我们只需要一个“如果”语句。检查红宝石的不透明度是否等于1(即可见)。如果可见,让金丝猴说“这是一瓶可乐”,然后让可乐瓶可见(不透明度设为1)。

对于“否则”情况,让金丝猴告诉狗需要先获得红宝石。

运行项目测试。引导斑点狗进入左门,然后引导它撞向金丝猴,它会说需要先获得红宝石。


处理中门碰撞

接下来处理中门。由于代码与左门相似,我们首先复制“处理左门碰撞”中的所有代码。

然后打开“处理中门碰撞”程序,将复制的代码拖入。我们需要做一些更改:让摄像机移动并定向到“查看乌龟和沙漠”,让狗移动到“沙漠中的狗”标记点,并将地面颜料更改为“沙漠”。

我们还需要为狗与任意一只乌龟的碰撞添加一个事件。添加一个新的碰撞事件,集合A是斑点狗,集合B包含两只沙漠龟。

如果发生碰撞,我们首先需要一个“如果”语句来判断碰撞的是哪只乌龟。使用“事件”中的“从集合B获取物体”功能。如果碰撞的是第一只沙漠龟,让它说“这是一根骨头”。否则,让第二只沙漠龟说同样的话。

在“如果”语句之后,将臂骨的不透明度设置为1,使其可见。


处理右门碰撞

现在编写“处理右门碰撞”的代码。从“处理左门碰撞”复制代码,然后打开“处理右门碰撞”程序并粘贴。

进行更改:让摄像机移动并定向到“查看爱丽丝角色”(女王场景),让狗移动到“爱丽丝中的狗”标记点,并将地面颜料从“海洋”改为“森林地面”。

添加另一个碰撞事件,这次只处理斑点狗与南瓜头的碰撞。集合A是斑点狗,集合B是南瓜头。

当发生碰撞时,首先让南瓜头说“我赐予你一颗红宝石”,然后让红宝石出现(不透明度设为1)。


处理返回按钮

我们忘记了为返回按钮添加处理代码。让我们现在完成它。

再次从“处理左门碰撞”复制所有代码。然后打开“处理点击返回”程序,将代码拖入。

当点击返回按钮时,我们希望回到原始起始场景。因此需要更改几处:摄像机应移动并定向到“摄像机起始位置”,狗应移动并定向到“狗起始位置”。

还需要将球体和返回文本的不透明度都设置为0,因为它们在原始场景中应该是不可见的。

最后,将地面颜料改回“草地”。

此外,因为将狗移回原始场景可能会导致它立即与老虎碰撞,我们需要避免这种情况。在降低摄像机遮罩后,添加一个指令让老虎向下移动10个单位(持续时间设为0,瞬间完成)。在升起摄像机遮罩之前,立即让老虎再向上移动10个单位(同样瞬间完成)。这样可以有效避免斑点狗与老虎的意外碰撞。


处理老虎碰撞

最后,我们需要处理与老虎的碰撞。编辑“处理老虎碰撞”程序。

我们需要构建一个“如果”语句,检查可乐瓶和臂骨的不透明度是否都为1,这意味着你已经收集了所需的两件物品。

在“顺序执行”块中添加一个“如果”语句。条件是两个比较的“与”运算:检查可乐瓶的不透明度是否等于1,以及臂骨的不透明度是否等于1。

如果条件为真,我们同时做两件事:让老虎消失(不透明度设为0),让阿德莱德半身像出现(不透明度设为1)。然后,让阿德莱德半身像说“你赢了”。

对于“否则”情况,让老虎说“你必须给我带来一根骨头和一瓶可乐”。


游戏测试

让我们最后测试一次游戏。我们收到指令。首先尝试直接走向老虎,它会说需要骨头和可乐。

然后我们进入右门,撞向南瓜头,获得红宝石。返回后,进入左门,现在猴子会给我们可乐瓶。我们还需要骨头,所以进入中门,撞向任意一只乌龟获得骨头。

点击返回按钮,现在所有三件物品都齐了。最后走向老虎,老虎消失,半身像出现并说“你赢了”。太棒了!你现在可以玩你的第一个大型游戏了。


总结

在本节课中,我们一起学习了一个多场景游戏项目的核心构建流程。我们掌握了如何使用摄像机遮罩实现平滑的场景切换,如何通过碰撞事件触发不同的场景逻辑和物品获取,以及如何通过条件判断来管理游戏进度和胜利条件。关键步骤包括初始化场景状态、编写各个场景的切换逻辑、为每个场景添加特定的事件交互,并最终将所有部分连接成一个完整的、可玩的游戏。

130:记忆游戏 🧠

在本节课中,我们将学习如何设计一个记忆游戏。玩家需要记住一组兔子变色的顺序,并按正确顺序点击它们。我们将探讨如何随机排列数组元素、如何向玩家展示顺序,以及如何验证玩家的输入。


游戏设计概述

上一节我们介绍了如何在爱丽丝中切换场景,本节中我们来看看如何设计三个游戏或任务。第一个游戏是记忆游戏。

我们将从一个兔子数组开始。每只兔子在数组中的位置会显示在兔子下方。例如,Bunny 1 在数组的位置 0,Bunny 2 在位置 1,依此类推。

在这个游戏中,我们会随机重新排列数组中兔子的顺序。请注意,兔子本身并没有移动,只是它们在数组中的位置被重新排列了。


随机交换数组元素

以下是实现随机交换的核心挑战:如何交换数组中两个兔子的位置。

我们可以通过生成两个 0 到 6 之间的随机数来选取要交换的位置。假设我们生成了 3 和 5。我们希望位置 3 的数组元素变成原来位置 5 的兔子,同时位置 5 的数组元素变成原来位置 3 的兔子。

直接交换很困难,这类似于试图同时交换两个杯子中的果汁而不洒出来。一个更简单的方法是引入一个临时变量。

在爱丽丝中,代码实现遵循同样的思路:

// 假设 bunnies 是兔子数组,index1 和 index2 是要交换的两个索引
temp = bunnies[index1];
bunnies[index1] = bunnies[index2];
bunnies[index2] = temp;

首先,将位置 3 的兔子存入临时变量 temp。接着,将位置 5 的兔子放入位置 3。最后,将 temp 中的兔子放入位置 5。这样就完成了交换。


向玩家展示顺序

第二个挑战是在进行多次交换后,向玩家展示新的兔子顺序。

这很简单。我们只需要遍历兔子数组,对于遍历到的每只兔子,将其颜色变为紫色,然后再变回白色。这样玩家就能看到兔子依次变色的顺序。


验证玩家输入

第三个挑战有些技巧性:我们需要记住玩家点击兔子的顺序,并确保它与数组中兔子被重新排列后的顺序一致。

解决方案是创建一个场景变量 index,初始值设为 0。当用户点击一只兔子时,我们检查这只兔子是否与 bunnies 数组中位置 index 的兔子匹配。

如果匹配,我们将 index 增加 1。这样,下次用户点击时,我们会检查是否与数组中位置 1 的兔子匹配,依此类推。

然而,如果用户点击的兔子不匹配,我们立即将 index 设为 -1,表示玩家失败。因此,游戏在两种情况下结束:index 变为 -1(玩家失败),或 index 变为 7(玩家成功记住了从位置 0 到位置 6 的所有兔子顺序)。


总结

本节课中我们一起学习了如何构建一个记忆游戏。我们探讨了三个核心部分:使用临时变量随机交换数组元素、通过遍历和变色向玩家展示顺序,以及使用索引变量来跟踪和验证玩家的点击顺序。掌握这些概念后,你就可以在爱丽丝中创建自己的交互式记忆游戏了。

131:记忆游戏演示 🎮

在本节课中,我们将学习如何使用Alice创建一个简单的记忆游戏。游戏的核心玩法是:系统会打乱一组兔子模型的顺序并短暂展示,玩家需要按照它们展示的顺序依次点击兔子。我们将通过创建数组、编写随机交换算法、处理用户点击事件以及判断游戏胜负状态来完成这个项目。


场景与变量初始化 🐰

首先,我们来看看初始场景的设置。点击场景标签页,可以看到场景中唯一的对象是七只兔子。

一个名为 Bunnies 的数组已被创建,并按顺序填充了这七只兔子:Bunny, Bunny2, Bunny3, Bunny4, Bunny5, Bunny6, Bunny7

此外,还有一个名为 index 的整数变量,其初始值被设置为 0。除了这些基础设置,目前还没有编写任何游戏逻辑。


第一步:打乱兔子顺序 🔀

上一节我们介绍了游戏的基础设置,本节中我们来看看如何打乱兔子的顺序。游戏的第一部分需要将数组中的兔子随机打乱顺序。

我们可以从创建一个场景级别的 swap(交换)过程开始。

以下是创建 swap 过程的步骤:

  1. 添加过程:点击“添加场景过程”按钮,将其命名为 swap
  2. 组织步骤:首先拖入一个 do in order 图块,以确保步骤按顺序执行。
  3. 生成随机索引:我们需要生成两个介于 06 之间的不同随机数。因为将数组中同一位置的兔子与自己交换没有意义。
    • 创建一个名为 index1 的整数变量,将其初始值设为 0
    • 然后,将 index1 的值改为一个随机数。使用 random 函数,范围设为从 0this.bunnies.length(但不包括这个最大值)。公式index1 = random(0, this.bunnies.length)。使用 this.bunnies.length 而非固定数字 7 的好处是,如果后续增减数组中的兔子数量,代码无需修改。
    • 同理,创建第二个整数变量 index2,并用相同方法为其赋予一个随机值。
  4. 确保索引不同:在交换之前,必须确保 index1index2 不相等。我们使用一个 while 循环来实现。
    • 拖入一个 while 循环,将其条件设置为 index1 == index2
    • 在循环体内,为 index2 重新分配一个新的随机数。这样,只要两个索引相同,就会持续为 index2 生成新值,直到它们不同为止。
  5. 执行交换:现在可以交换数组中这两个位置的兔子了。这需要一个临时变量。
    • 创建一个类型为 Bunny 的变量,命名为 temp
    • 执行交换的三步操作:
      1. this.bunnies[index1] 赋值给 temp
      2. this.bunnies[index2] 赋值给 this.bunnies[index1]
      3. temp 赋值给 this.bunnies[index2]

至此,swap 过程就完成了。


第二步:展示打乱后的顺序 🎨

为了验证我们的交换是否有效,需要让玩家看到打乱后的顺序。接下来,我们创建一个过程来短暂地展示每只兔子的颜色。

我们将创建一个名为 showColors 的场景过程。

以下是 showColors 过程的实现步骤:

  1. 遍历数组:拖入一个 for each in 图块。将迭代器类型设为 Bunny,命名为 bunnyIterator,并指定要遍历的数组为 this.bunnies
  2. 改变颜色:在循环体内,我们分两步改变每只兔子的颜色:
    • 首先,将 bunnyIterator 的油漆颜色设置为紫色,持续 0.5 秒。
    • 然后,再将颜色设置回白色,同样持续 0.5 秒。

这样,运行程序时,兔子们就会依次快速闪一下紫色。

现在,回到 my first method 中,在调用 swap 之后,紧接着调用 showColors。运行程序,你就能看到交换后的兔子顺序了。

为了更彻底地打乱顺序,我们可以在一个循环中多次调用 swap。例如,循环 1000 次,这样每次运行游戏,兔子都会以完全随机的顺序出现。


第三步:处理玩家点击与游戏逻辑 🖱️

现在兔子顺序已经打乱,我们需要处理玩家的点击操作,并判断点击是否正确。

我们转到 initializeEventListeners 标签页,为“鼠标点击对象”添加一个事件监听器。

以下是实现点击逻辑的步骤:

  1. 检查点击对象:在事件处理中,首先使用一个 if 语句判断被点击的兔子是否是数组中当前 index 位置的那只。
    • 条件为:this.bunnies[index] == event.getMouseClickedObject()
  2. 处理正确点击:如果点击正确(匹配):
    • index 的值增加 1,这样玩家接下来就需要点击数组中的下一只兔子。
    • 然后,检查玩家是否已经获胜。获胜条件是 index 的值等于数组的长度(即 this.bunnies.length)。如果满足,可以让某只兔子说“You win!”。
  3. 处理错误点击:如果点击错误(不匹配):
    • index 设置为 -1,表示游戏结束,玩家失败。
    • 可以让兔子说“That is not the correct order.”。

第四步:游戏状态管理 🏁

我们还需要一个机制来控制游戏是否应该继续响应点击事件。

为此,我们创建一个场景级别的布尔函数,命名为 gameContinuing

以下是 gameContinuing 函数的逻辑:

  • 游戏继续的条件是:index >= 0 并且 index < this.bunnies.length
  • 如果 index 变为 -1(玩家失败)或等于数组长度(玩家获胜),则游戏结束,函数返回 false

然后,回到鼠标点击事件的处理中,在最外层包裹一个 if 语句,条件就是 gameContinuing()。这样,只有当游戏仍在进行时,才会处理玩家的点击。


最终完善与总结 📝

最后,我们可以为游戏添加一些初始提示。例如,在打乱顺序并展示颜色后,让一只兔子说:“Click on the bunnies in the order that they appear in purple.”,持续几秒钟。

运行游戏,现在你可以尝试按照紫色兔子出现的顺序点击它们了。如果顺序全部点击正确,你将赢得游戏!


本节课中我们一起学习了如何构建一个完整的记忆游戏。我们涵盖了以下核心概念:

  • 使用数组管理多个对象。
  • 编写随机交换算法来打乱数组顺序。
  • 利用循环条件判断实现游戏逻辑。
  • 通过事件监听处理用户交互。
  • 创建函数来管理复杂的游戏状态。

通过这个项目,你将掌握在Alice中创建交互式游戏的基本流程和关键编程技巧。

133:逻辑游戏 🧩

在本节课中,我们将学习如何使用爱丽丝(Alice)构建一个逻辑游戏。这个游戏的核心是让玩家通过点击铃铛来猜测一个由“上”和“下”位置组成的秘密代码。我们将分步讲解如何设置随机代码、处理玩家点击以及验证猜测是否正确。


游戏概述

这个逻辑游戏的目标是让玩家猜测一个由三个铃铛的“上”或“下”位置组成的秘密代码。游戏开始时,所有铃铛都处于“下”位置。玩家通过点击铃铛来切换其位置,直到铃铛的排列与秘密代码完全一致。


游戏的三部分

构建此游戏需要完成三个主要部分:设置随机代码、处理玩家点击以及检查猜测是否正确。接下来,我们将逐一详细探讨。

第一部分:设置随机代码

首先,我们需要生成一个由“上”和“下”组成的随机秘密代码。我们将使用一个文本字符串数组来存储这个代码。

以下是生成随机代码的步骤:

  1. 创建一个包含三个元素的数组,用于存储代码。
  2. 使用一个计数循环(因为爱丽丝不允许直接为迭代器赋值)遍历数组的每个索引。
  3. 对于每个索引,调用内置的 nextRandomBoolean 函数。该函数有50%的概率返回 true,50%的概率返回 false
  4. 如果函数返回 true,则将数组的该元素设置为字符串 "up"
  5. 如果函数返回 false,则将数组的该元素设置为字符串 "down"

通过以上步骤,我们就成功设置了一个随机的秘密代码。


第二部分:处理玩家点击

处理玩家点击铃铛的操作相对简单,这得益于一个动画技巧。我们在场景中间放置了一个不可见的横杆,用于判断铃铛的当前位置。

以下是处理点击的逻辑:

  1. 当游戏运行时,使横杆不可见。
  2. 当玩家点击一个铃铛时,使用 if 语句检查该铃铛是否在横杆上方。
  3. 如果铃铛在横杆上方(即处于“上”位置),则将其移动到下方(切换到“下”位置)。
  4. 如果铃铛不在横杆上方(即处于“下”位置),则将其移动到上方(切换到“上”位置)。

这样,每次点击都能正确地切换铃铛的状态。


第三部分:检查猜测是否正确

检查玩家的猜测是否与秘密代码匹配是游戏中最具挑战性的部分。我们不能直接比较铃铛的物理位置和文本字符串数组。

我们的解决方案分为两个子步骤:

子步骤一:将代码数组转换为单个字符串

  1. 从一个空字符串开始。
  2. 按顺序将代码数组中的每个“上”或“下”值追加到这个字符串的末尾。

子步骤二:将当前铃铛位置转换为单个字符串

  1. 遍历每个铃铛。
  2. 对于每个铃铛,检查它是否在横杆上方。
  3. 如果在横杆上方,则将字符串 "up" 追加到结果字符串的末尾。
  4. 如果不在横杆上方(即在下方),则将字符串 "down" 追加到结果字符串的末尾。

完成这两个子步骤后,我们只需比较这两个字符串是否完全相同。如果相同,则说明玩家的猜测正确,游戏获胜;否则,猜测错误,游戏继续。


总结

本节课中,我们一起学习了如何构建一个逻辑猜谜游戏。我们掌握了三个核心技能:使用随机函数生成秘密代码,利用不可见对象和条件判断处理用户交互,以及通过字符串转换和比较来验证游戏状态。现在,你可以尝试在爱丽丝中实现这个游戏,并进一步添加更多关卡或功能。

134:逻辑游戏演示

概述

在本节课中,我们将学习如何使用爱丽丝(Alice)编程环境创建一个简单的逻辑游戏。游戏的目标是让玩家通过点击铃铛来猜测一个由“上”和“下”组成的随机秘密代码。我们将分步完成三个核心任务:生成秘密代码、处理玩家点击事件以及检查玩家是否猜中了代码。


游戏初始设置

我们已经部分设置好了这个游戏。我们在三个古老的柱子前放置了三个铃铛。我们还添加了一只乌龟,它将用于提供游戏说明。

如果我们点击运行,现在来试一下。系统会询问我们是否需要说明。如果我们输入“是”,乌龟会告诉我们如何玩游戏。你需要猜测一个秘密代码,即铃铛的位置。从左到右,每个铃铛的状态是“上”或“下”。你将通过点击铃铛来上下移动它们,使其处于正确位置。

但是,如果我们点击一个铃铛,让我们试试看。什么也没有发生。让我们关闭运行窗口。


第一步:生成秘密代码

上一节我们看到了游戏的基本界面,本节中我们来看看如何生成随机的秘密代码。

我们点击场景选项卡,滚动到底部。我们看到底部有一个名为 bells 的铃铛数组和一个名为 code 的文本字符串数组。

首先,我们需要创建一个场景过程来生成秘密代码。

  1. 创建一个场景过程,命名为 generateSecretCode
  2. 我们需要遍历 code 数组的每个元素,并随机将其设置为“上”或“下”。由于爱丽丝不允许在迭代的数组中进行赋值,我们不能使用迭代器,而是使用计数循环。
  3. 创建一个名为 index 的整数变量,初始化为0。
  4. 在变量后添加一个计数循环,循环次数为 code.length
  5. 在循环内添加一个 if 语句,条件为 nextRandomBoolean,表示50%的概率。
  6. then 分支中,使用赋值语句将 code[index] 设置为“上”。
  7. else 分支中,将 code[index] 设置为“下”。
  8. 最后,在 else 语句后,将 index 增加1。

以下是生成秘密代码的核心代码结构:

变量 index = 0
循环 从 1 到 code.length 执行
   如果 nextRandomBoolean 为真 则
      赋值 code[index] = "上"
   否则
      赋值 code[index] = "下"
   结束如果
   赋值 index = index + 1
结束循环

为了验证我们生成了不同的随机代码,我们最好编写一个函数,将秘密代码从字符串数组转换为单个字符串。

现在创建一个场景函数,命名为 getSecretCode,返回类型为文本字符串。

  1. 创建一个名为 theCode 的文本字符串变量,初始化为空字符串。
  2. 添加一个 forEach 循环来遍历 code 数组。
  3. 在循环内,将 theCode 赋值为 theCode + oneCode(即拼接每个代码)。
  4. 循环结束后,返回 theCode

为了测试,我们可以在 myFirstMethod 中调用 generateSecretCode,然后让乌龟说出生成的秘密代码。运行几次项目,可以看到每次生成的代码都不同。测试成功后,记得注释掉让乌龟说出代码的那行代码,否则游戏就失去乐趣了。


第二步:处理玩家点击

上一节我们成功生成了秘密代码,本节中我们来实现游戏的核心交互:处理玩家点击铃铛。

我们需要初始化事件监听器,添加一个“鼠标点击对象”监听器,并指定监听对象为 bells 数组,这样我们只处理对铃铛的点击。

在监听器内部的过程中,我们需要按顺序做两件事:首先移动被点击的铃铛,然后(作为第三步的一部分)检查玩家是否赢得了游戏。

  1. 添加一个 doInOrder 块。
  2. 我们想要实现:如果被点击的铃铛在“下”位置,就向上移动;如果在“上”位置,就向下移动。
  3. 添加一个 if 语句,条件为 getModelAtMouseLocation(获取鼠标位置的模型)是否在横杆 bar 之上。
  4. 如果条件为真(铃铛在横杆之上,即“上”位置),则将其向下移动1.5个单位。
  5. 如果条件为假(铃铛在横杆之下,即“下”位置),则将其向上移动1.5个单位。

以下是处理点击移动的核心逻辑:

如果 getModelAtMouseLocation 在 bar 之上 则
   getModelAtMouseLocation 向下移动 1.5
否则
   getModelAtMouseLocation 向上移动 1.5
结束如果

现在运行程序并点击铃铛进行测试。点击铃铛,它会上下移动,功能正常。


第三步:检查是否匹配

上一节我们实现了铃铛的点击交互,本节中我们来完成最后一步:检查铃铛的当前位置是否与秘密代码匹配。

我们创建一个场景函数,命名为 checkForMatch,返回类型为布尔值(truefalse)。这个函数需要做两件事:首先,根据铃铛的当前位置构建一个猜测字符串;其次,检查这个猜测字符串是否与秘密代码相等。

以下是实现步骤:

  1. 创建一个名为 guess 的文本字符串变量,初始化为空字符串。
  2. 使用 forEach 循环遍历 bells 数组。
  3. 在循环内添加一个 if 语句,检查当前遍历的铃铛 bellIterator 是否在横杆 bar 之上。
  4. 如果为真(铃铛处于“上”位置),则将 guess 赋值为 guess + "上"
  5. 如果为假(铃铛处于“下”位置),则将 guess 赋值为 guess + "下"
  6. 循环结束后,guess 字符串就代表了铃铛的当前位置。
  7. 在循环后添加一个 if 语句,使用 contentsEqual 方法比较 guess 和调用 getSecretCode 函数得到的秘密代码是否相等。
  8. 如果相等,则返回 true;否则返回 false

检查匹配的函数逻辑如下:

变量 guess = ""
循环 遍历 bells 数组中的每个 bellIterator 执行
   如果 bellIterator 在 bar 之上 则
      赋值 guess = guess + "上"
   否则
      赋值 guess = guess + "下"
   结束如果
结束循环
如果 guess 的内容等于 getSecretCode() 则
   返回 true
否则
   返回 false
结束如果

现在,回到“初始化事件监听器”中,在移动铃铛的代码之后,添加一个 if 语句来调用 checkForMatch 函数。

  1. 如果返回 true,则让乌龟说“干得漂亮”,并说出秘密代码是什么。
  2. 使用字符串拼接,让乌龟说“秘密代码是”加上 getSecretCode() 的返回值。

总结

本节课中我们一起学习了如何在爱丽丝中创建一个完整的逻辑游戏。我们分三步实现了核心功能:使用数组和随机数生成秘密代码;通过事件监听器处理玩家的鼠标点击来移动铃铛;编写函数检查玩家摆放的铃铛位置是否与秘密代码匹配,并在猜中时给出反馈。你现在可以运行并试玩这个游戏了,祝你玩得开心!

136:配对游戏 🃏

在本节课中,我们将学习如何创建一个经典的“配对游戏”。玩家需要点击翻转的卡片,找出颜色相同的配对。我们将学习如何使用变量来追踪游戏状态,以及如何处理玩家的点击事件。


游戏概述

我们将创建第三个也是最后一个游戏。具体来说,玩家需要通过点击卡片来匹配相同颜色的卡片对。

如图所示,游戏开始时,有12张背面朝上的卡片,隐藏了它们的颜色。由于爱丽丝没有标准的扑克牌,我们使用公告牌来模拟卡片。玩家点击卡片以尝试找到匹配的对。

在这个例子中,玩家点击了右上角的卡片,它是蓝色的。然后玩家点击了左下角的卡片,它是灰色的。它们不匹配,所以它们被重新翻转回去。

现在,假设玩家点击了左上角的卡片,然后点击了右上角的卡片。结果这两张卡片都是蓝色的,所以它们匹配,并从游戏中移除。

游戏继续进行,直到玩家找到所有六对匹配的卡片。此时,玩家获胜。


与记忆游戏的相似之处

这个游戏的一部分与记忆游戏非常相似。在记忆游戏中,我们需要打乱兔子的位置;在这里,我们打乱的是卡片,或者更准确地说,是卡片的颜色。


关键差异:追踪玩家点击

然而,存在一个显著的差异。这个差异取决于玩家的点击。

当玩家第一次点击一张卡片时,我们只是将卡片翻转过来。但是,一旦一张卡片被翻转,并且玩家点击了另一张卡片,爱丽丝就需要记住第一张被翻转的卡片是什么。然后我们可以比较两张卡片的颜色,看它们是否匹配。

记住一张卡片最简单的方法是使用一个变量。


使用变量追踪状态

我们可以从一个初始化为非12张游戏卡片的变量开始。我们添加了第13张卡片,并将其命名为 null card。我们最初将 null card 放置在玩家点击寻找配对卡片区域的左侧。

这张卡片用于追踪游戏玩家是在点击寻找配对的第一张卡片,还是第二张卡片。

如果 first card 变量的值是 null card,我们就知道玩家还没有点击任何卡片。

当游戏玩家第一次点击一张卡片时,我们将 first card 赋值为被点击的卡片。

当游戏玩家点击第二张卡片时,我们首先检查 first card。如果 first card 不是 null card,我们就知道玩家之前点击过一张卡片,我们可以比较这两张卡片(first card 和刚被点击的卡片)是否匹配。

因为我们实际上不希望游戏玩家点击 null card,我们会将 null card 放置在地面以下,这样玩家就看不到也无法点击它。


处理卡片点击的详细步骤

让我们更详细地了解一下处理卡片鼠标点击所涉及的步骤。我们需要做三件事:

  1. 将点击到的 S thing 转换为一张卡片。
  2. 处理“还没有被翻开的卡片”的情况。我们称这种情况为“处理第一张卡片”。
  3. 处理“已经有一张被翻开的卡片”的情况。我们称这种情况为“处理第二张卡片”。

步骤一:转换点击对象

当我们处理鼠标点击事件时,爱丽丝会返回 get model at mouse location,这是一个 S thing 类型。因为爱丽丝不知道用户点击的是对象还是地面。

如果我们把 get model at mouse location 存储到一个 S thing 变量 What got clicked 中,我们需要遍历卡片列表来确定哪张卡片匹配。一旦找到匹配项,我们就可以将匹配的卡片存储到一个局部变量 clicked card 中。

代码示例:

// 遍历所有卡片,找到被点击的那一张
for each card in listOfCards
    if card equals What got clicked
        set clicked card = card
    end if
end for

步骤二:处理第一张卡片

这是比较简单的情况。我们知道需要处理第一张卡片,如果场景变量 first card 被设置为 null card

我们需要做两件事:

  1. clicked card 存储到 first card 中。
  2. 将这张卡片翻转过来。

步骤三:处理第二张卡片

处理第二张卡片需要五个子步骤。在我们开始子步骤之前,我们需要确保玩家点击的是不同的卡片。如果玩家两次点击同一张卡片,我们可以直接忽略第二次点击。

以下是五个子步骤:

  1. 翻转第二张卡片:这样玩家可以看到他们点击了什么。
  2. 检查颜色是否匹配:如果两张卡片的颜色匹配,将它们的透明度设置为0(即隐藏)。
  3. 将两张卡片重新翻回去:虽然从技术上讲,只有在卡片不匹配时才需要将它们翻回去,但我们每次都这样做。这样,如果我们想再玩一次游戏,所有卡片都会朝向同一个方向。
  4. 重置 first card 变量:将 first card 重置为 null card。这样做的原因是,当下次玩家点击卡片时,我们希望爱丽丝将其作为第一张卡片处理。
  5. 检查游戏是否结束:检查是否所有卡片都已匹配。如果玩家获胜,则祝贺他们。

代码逻辑示例:

if first card is not null card
    // 子步骤1:翻转第二张卡片
    flip over clicked card

    // 子步骤2:检查是否匹配
    if color of first card equals color of clicked card
        set opacity of first card to 0
        set opacity of clicked card to 0
    end if

    // 子步骤3:将两张卡片翻回去(背面朝上)
    flip back first card
    flip back clicked card

    // 子步骤4:重置第一张卡片变量
    set first card = null card

    // 子步骤5:检查胜利条件
    if all cards are matched (opacity is 0)
        say "恭喜你赢了!"
    end if
end if

总结

本节课中,我们一起学习了如何构建一个配对游戏。我们介绍了如何使用一个额外的 null cardfirst card 变量来追踪玩家的游戏状态。我们详细分解了处理鼠标点击事件的三个主要步骤:转换点击对象、处理第一张卡片点击以及处理第二张卡片点击(包括匹配检查、状态更新和胜利判定)。通过这个项目,你掌握了在爱丽丝中创建交互式记忆游戏的核心逻辑。

137:配对游戏演示 🃏

在本节课中,我们将学习如何为一个已搭建好场景的配对游戏编写核心交互逻辑。具体来说,我们将专注于创建处理卡牌点击的事件处理器。


概述

本节课的目标是为一个配对游戏编写鼠标点击事件的处理代码。游戏场景、卡牌阵列和洗牌逻辑已经预先设置完成。我们的核心任务是实现 processClickedCard 过程,它能响应用户点击,处理卡牌的翻转、匹配判断以及游戏胜利检测。


游戏初始化与现有代码

首先,我们运行项目以观察初始状态。可以看到12张卡牌出现,外星人给出了游戏说明。

关闭运行窗口后,我们查看已编写的代码。在 my first method 中,程序调用了两个过程:setUpScenesetCardColors,然后播放了外星人的指令。

点击进入 setUpScene 过程,可以看到其中包含了大量用于将12张卡牌定位成4x3网格的代码。卡牌实际上是方形卡片形状的公告板(billboard)。初始时,程序会调用 swap 过程1000次来随机打乱卡牌颜色顺序。

swap 过程的工作原理是生成两个0到11之间的随机数,然后交换数组中对应两张卡牌的颜色。这与我们之前制作的记忆游戏逻辑相似。

在场景(Scene)标签页底部,我们创建了两个场景变量(属性):

  • cards:这是一个包含12张卡牌的数组。
  • firstCard:这是一个公告板类型的变量,初始化为 nullCard

除了12张游戏卡牌,我们还创建了第13张名为 nullCard 的卡牌,并将其放置在地面之下。当需要表示尚未选中任何卡牌时,就将 firstCard 设置为 nullCard


创建事件处理器

由于游戏场景已搭建完毕,我们只需为卡牌点击创建事件处理器。

首先,点击“创建事件监听器”,添加一个“当鼠标点击对象时”的事件。在详细信息中,我们将其限制为仅处理对 cards 数组元素的点击。这样,Alice就只会处理对那12张游戏卡牌的点击。

考虑到事件处理器的代码会比较长且复杂,我们不直接在这里编写所有逻辑,而是创建一个场景过程来封装它。

我们创建一个名为 processClickedCard 的场景过程,并为其添加一个 SThing 类型的参数 whatGotClicked。创建完成后,我们立即返回事件监听器标签页,在鼠标点击事件下调用这个过程,并将 modelAtMouseLocation 拖拽覆盖 whatGotClicked 参数。

现在,我们可以回到 processClickedCard 过程内部编写具体逻辑。


编写 processClickedCard 过程逻辑

如前所述,我们需要完成三个主要步骤。让我们在一个 Do in order 结构块中组织它们。

第一步:确定被点击的卡牌

我们首先添加注释“确定被点击的卡牌”。为了便于后续操作,我们创建一个 Billboard 类型的局部变量 clickedCard,并将其初始化为 nullCard

接着,我们使用一个 For each in 循环遍历 cards 数组。在循环内部,使用一个 If 语句判断当前的 cardIterator 是否等于参数 whatGotClicked。如果相等,就将 clickedCard 变量设置为这个 cardIterator。这样就完成了对被点击卡牌的识别。

第二步:处理首次点击(翻开第一张卡牌)

我们添加注释“处理被点击的卡牌”。这里需要一个 If 语句来判断 firstCard 是否等于 nullCard。如果等于,说明玩家点击的是第一张卡牌。

在这种情况下,我们需要按顺序完成两件事:

  1. firstCard 赋值为 clickedCard
  2. 翻转这张卡牌。这通过一个两步动画实现:先让外星人向左旋转半圈(0.25 秒),然后将这个“外星人”角色替换为 firstCard,从而让卡牌执行翻转动作。

第三步:处理第二次点击(翻开并匹配第二张卡牌)

如果 firstCard 不是 nullCard,说明玩家正在点击第二张卡牌。我们首先需要确保玩家没有点击同一张卡牌两次。因此,使用一个 If 语句检查 clickedCard 是否不等于 firstCard

如果点击的是不同的卡牌,我们需要按顺序完成五件事:

  1. 翻开第二张卡牌:使用与第一步相同的方法,让外星人翻转并替换为 clickedCard
  2. 检查颜色是否匹配:使用一个 If 语句判断 firstCardpaint 属性是否等于 clickedCardpaint 属性。
    • 如果匹配,我们使用一个 Do together 块,同时将 firstCardclickedCardopacity(不透明度)设置为 0(持续 0.25 秒),使它们同时消失。
  3. 无论是否匹配,都将两张卡牌翻回背面:使用一个 Do together 块,让 firstCardclickedCard 同时快速(例如 0.1 秒)向左旋转半圈,翻回背面。
  4. 重置 firstCard:将 firstCard 重新设置为 nullCard,为下一次配对做准备。
  5. 检查游戏是否胜利:使用一个 If 语句调用我们预先写好的场景函数 allCardsHaveBeenMatched
    • 如果函数返回 true,则让外星人说“Nice job!!”来祝贺玩家。

测试游戏

代码编写完成后,我们运行游戏进行测试。点击卡牌寻找匹配对。当点击两张颜色相同的卡牌时,它们会短暂显示后消失。点击不同颜色的卡牌,它们会显示后翻回。当所有卡牌都被成功匹配并消失后,外星人会说出“Nice job!!”,标志着游戏胜利。


总结

本节课中,我们一起为一个预先搭建好场景的配对游戏编写了核心交互逻辑。我们创建了 processClickedCard 过程来处理鼠标点击事件,实现了识别被点击卡牌、处理首次和第二次点击、判断颜色匹配、控制卡牌翻转与消失动画,并在游戏胜利时给出反馈。通过这个过程,你将事件处理、条件判断、循环遍历和动画控制结合运用,完成了一个完整的交互式游戏功能。

138:综合整合 🎮

在本节课中,我们将学习如何将之前创建的三款小游戏与场景切换功能整合成一个完整的“自创冒险游戏”。我们将重点解决两个核心问题:如何在不同场景中控制玩家交互,以及如何确保鼠标点击只在对应的游戏中被处理。

概述

我们将创建一个名为 game 的文本字符串变量,用于追踪玩家当前所处的游戏场景。通过这个变量,我们可以精确控制何时允许玩家操控斑点狗,以及何时处理特定游戏的鼠标点击事件。这种方法能优雅地解决场景与游戏逻辑之间的冲突。

核心问题与解决方案

上一节我们学习了如何构建独立的游戏和场景切换。本节中,我们来看看如何将它们无缝整合。

整合过程中,我们面临两个主要问题:

  1. 玩家应仅在初始场景中操控斑点狗,进入游戏后则不能操控。
  2. 三款游戏都涉及鼠标点击,必须确保点击事件只在玩家进行对应游戏时才被处理。

幸运的是,一个巧妙的方案可以同时解决这两个问题。

核心机制:游戏状态变量

基本思路是,我们有四个场景(初始场景和三款游戏)。爱丽丝应该只为当前场景处理相应的事件,忽略其他场景的事件。

我们只需创建一个场景级别的文本字符串变量。我们可以将其命名为 game,并初始化为字符串 "initial"

以下是该变量的状态变化逻辑:

  • 如果用户操控斑点狗与右侧大门(逻辑游戏)碰撞,我们将 game 设为 "logic"
  • 如果用户操控斑点狗与中间大门(卡片匹配游戏)碰撞,我们将 game 设为 "match"
  • 如果用户操控斑点狗与左侧大门(记忆游戏)碰撞,我们将 game 设为 "memory"
  • 当玩家完成游戏并点击返回按钮时,我们将 game 重置为 "initial"

事件处理逻辑

然后,当游戏玩家点击鼠标时,爱丽丝只需检查当前正在玩哪个游戏,并处理该游戏的鼠标点击。使用同样的技术,我们也只在初始场景中才允许操控斑点狗。

这是一个非常强大的变量。基于此,我们邀请您试玩示例的“自创冒险游戏”。

游戏示例与总结

我们已预先将四个独立部分(场景切换、记忆游戏、逻辑游戏和匹配游戏)构建成此游戏,并添加了文本变量来正确处理玩家的鼠标点击和操控。

因为这里没有新的编程概念,我们不再从头开始演示构建过程。请尝试赢得全部三款游戏,让阿德莱德半身像出现。

本节课中,我们一起学习了如何通过一个游戏状态变量game)来整合多个游戏场景,从而控制玩家交互的权限。这是构建复杂、多模块项目的一个关键技巧,它能确保游戏逻辑清晰且互不干扰。

139:游戏玩法演示 🎮

在本节课中,我们将通过一个具体的游戏演示,来观察和分析游戏中的逻辑与交互过程。我们将跟随演示者的操作,理解如何解决游戏中的谜题并达成目标。


让我们开始游戏。演示者说:“给我一根骨头和一个香蕉,我们先去这边看看。”

第一个谜题需要我们返回并在某处找到一颗红宝石。好的,我们尝试去这边。不对,没有。试试这里。😔,也不是,这里。

这里。这里。😔,到底是什么?应该是这个。下,上,下。好的,我们返回。

现在我们可以玩那个游戏了。到这里。哦,这个很难,好吧。发送到。所以,是7。2,2,2,2,2。2,5。3。

一,我需要帮助,我想是六,太棒了四。不,我们做到了。那太难了。好的,我们拿到了一个香蕉。


中间还有一个游戏,好的。是的。好的,哦,我们匹配上了。好的,让我们看看。

我们从这里开始。红色。黄色。红色,哦,我们搞定了一个,那个很简单。蓝色。哦天哪,我们很幸运。粉色,蓝色。那里有个不同的蓝色。我想是蓝色。哦,我们拿到了蓝色。

我们拿到了蓝色。AGi。😔,很好,哦天哪,我们做得太好了。黄色一定是这个。哦,哦,哦,好的,黄色。😊,黄色,不对,好吧,我们会拿到的。好的,粉色,粉色。好的,现在我想这个是黄色。这个一定是黄色。好的,我们拿到了。我们有一根骨头和一个香蕉了。😊。

好的,现在让我们试试这个。让我们撞向那只老虎。哦耶,太棒了我们赢了,太棒了,好的,5,干得漂亮。😊。


本节课中我们一起学习了如何通过观察和尝试解决游戏中的一系列逻辑谜题。我们看到了从寻找物品到完成匹配游戏,再到最终挑战的完整流程。这个过程展示了在编程和游戏设计中,逻辑推理、问题分解和持续尝试的重要性。

141:后续学习方向 🚀

在本节课中,我们将回顾你在本课程中学到的核心知识,并探讨完成本课程后,你可以选择哪些方向进行深入学习。课程涵盖了编程和动画两个主要领域,我们将分别讨论每个领域的进阶路径。

你已经完成了本课程的学习。我们相信你不是直接跳到了最后一个视频。你已经在动画和编程方面学到了很多知识。😊

由于我和苏珊都是计算机科学教授,我们在课程中倾向于更多地关注爱丽丝的编程方面。你现在已经掌握了不少编程知识。你理解了基本的编程控制结构:if语句计数循环while循环。你获得了使用数组的经验。你可以创建自己的过程函数,甚至可以使用参数。你还初步了解了对象面向对象编程

你也学习了游戏设计。以及事件事件驱动编程。你理解了游戏玩家如何与游戏互动,以及如何处理这些互动。

此外,你还更广泛地学习了动画知识,了解了诸如移动、转向、旋转等动画原语的工作原理,以及如何将它们组合成更丰富的动画指令和故事。你甚至学习了如何使用故事板来设计你想要讲述的故事。

现在,让我们花点时间从学习的角度讨论一下你接下来可以探索的方向。😊

计算机科学与编程方向 🖥️

从计算机科学和计算机编程的角度来看,你已经完全准备好学习Java语言的入门课程了。

让我们看一小段爱丽丝代码。在这个例子中,一个名为Loki的角色在一个星球上,可能位于一艘宇宙飞船下方。如果Loki在飞船下方,那么Loki会说需要向上移动。然后,当Loki仍在飞船下方时,他会一次向上移动一个单位。最后,Loki说他到达了飞船,可以回家了。😊

// 爱丽丝中的逻辑对应的Java代码示例
if (loki.isBelow(spaceship)) {
    loki.say("I need to move up.");
    while (loki.isBelow(spaceship)) {
        loki.moveUp(1);
    }
    loki.say("I made it to the spaceship and can go home.");
}

让我们看看用Java编写的相同代码。Java代码包含一些奇怪的词,如publicvoid。它还有一些分号和花括号,看起来像是随意放置的。但如果你忽略这些,代码的其余部分看起来与我们刚才在爱丽丝中看到的代码非常相似。

事实上,我敢打赌,如果我先不给你看爱丽丝代码,只给你看这段Java代码,你很可能能准确地告诉我这段代码是做什么的,尽管你还没有学过Java。

Java确实包含许多有用的功能,这些功能在爱丽丝中实现起来会更困难,例如数据处理。Java是一种非常有用的语言,用于处理数据、构建更大的计算机程序以及执行许多其他类型的任务。

我们鼓励你考虑杜克大学在Coursera上的Java专项课程:“Java编程与软件工程基础”。如果你想学习更多编程知识,这是一个很好的选择。

作为该课程的额外收获,你将首先学习HTML、CSS和一些JavaScript,这样你就能够构建很酷的网页,然后再深入学习Java。就像爱丽丝是初学者的环境一样,这个专项课程使用BlueJ,一个帮助初学者学习Java的环境。该专项课程的最终项目是编写一个用于电影和书籍的推荐系统,类似于亚马逊和Netflix所做的。😊

亚马逊的推荐系统会检查你购买书籍的历史记录,将其与其他人的购书清单进行比较,然后推荐你未来可能喜欢阅读和购买的书籍。Netflix做的事情类似,不过是针对电影。如果你的推荐系统真的很好,也许你可以把你的解决方案卖给亚马逊或Netflix。😊

动画与设计方向 🎨

如果你对动画方面更感兴趣,你可以选择几个方向。

你可以学习3D建模课程,学习如何设计和构建你在爱丽丝中使用的那种模型。如果你喜欢动画的讲故事方面,专注于创意写作或直接讲述故事的英语课程将是一个很好的选择。

另一个可能的方向是专注于用户界面,更广泛地说是用户体验。这个被称为UI/UX的领域将涵盖用户玩游戏时的体验,以及更广泛的用户与计算机应用程序交互时的体验。

你也可以更多地关注游戏的设计方面,而不是游戏的构建。游戏的构建通常涉及更多编程,至少需要学习数据结构课程。而设计则有自己的一系列课程和专业方向可供你追求。

一个类似的兴趣领域是网页设计。你可以选择更侧重于网页的设计,这通常涉及HTML和CSS;或者更侧重于底层编程,这通常涉及JavaScript编程。

数据与高级应用 📊

如果你继续学习更多编程知识,相关的课程和专项课程很可能会更侧重于数据处理。这些数据可能涉及处理图像(例如将一张图片隐藏在另一张图片中)、在长链DNA中寻找模式,或者尝试分析数据以帮助预测天气模式。

我们感谢你学习了这门课程,并希望它能为你下一步的学习或职业生涯做好准备。


在本节课中,我们一起回顾了你在爱丽丝课程中学到的编程与动画核心技能,并探讨了未来的学习路径。在编程方向,你已具备学习Java等专业语言的基础;在动画与设计方向,你可以深入3D建模、故事创作或UI/UX设计。无论你选择哪条道路,本课程都为你打下了坚实的基础。祝你学习顺利,在未来的探索中取得成功!🚀

posted @ 2026-03-29 09:35  布客飞龙I  阅读(7)  评论(0)    收藏  举报