Email:

动画指导: 问好鱼!

此指导包含一个用以学习Mama动画基本原理的示例程序。 程序包含4个阶段,是关于Mama语言和开发环境的简单介绍。 包含下列主题:
  • 场景编辑和程序编写
  • 使用拖放机制,在程序编辑器中编写方法
  • 介绍Mama中的标准方法
  • 3D空间的基本运动
  • 并行执行操作以实现连续运动
  • 介绍基本控制指令:条件和循环


内容

问好鱼 - 阶段1

在这个程序中,我们将制作动画显示鱼的移动。 当鱼到达屏幕边缘时转向回到起始点。

  • 运行Mama, 打开带有海底背景的新窗口。
点击3D屏幕底部的“+”按键,改变场景编辑模式
  • 从底部窗口的对象图库选择目录海洋,然后选择海洋_地面。 这是海洋背景,我们将在这个背景下制作动画。
现在添加鱼里尔_鱼
    • 添加程序方法-world的方法主要_方法-一个让鱼向前移动1米的指令。为了实现这个指令,从对象树中拖住鱼放到编辑区域,并从弹出菜单中选择移动和参数向前。点击更多...选择时长,从菜单中选择4秒。
    • 运行world程序,鱼移动到画面的右边然后停止。
  • 现在给鱼添加转向指令: 把鱼再次拖放到编辑区域,放在你刚添加的代码行后边。 从菜单中选择转动,然后选择,并从弹出菜单中选择转半圈。像以前一样,设置时长为4秒。
  • 右击第一行,从弹出菜单中选择复制-创建了此行的副本。
  • 拖住复制的代码行到程序末端(即转向指令的下边一行)。 这个指令让鱼货代左边的起始点。
  • 运行程序。 鱼移动到画面右侧,然后转向回来。
程序代码:

  my_first_method ( )
    
       lilfish .移动( 向前 , 1米 ); 时长 = 4
  lilfish .转动( , 0.5旋转' ); 时长 = 4
  lilfish .移动( 向前 , 1米 ); 时长 = 4


问好鱼 - 阶段2

我们想让鱼不停地移动,当它到达每一边是可以转向。我们该怎么办呢?
画面底部包含控制指令 - 这些指令指导程序执行,他们有条件指令,循环,并行执行指令等。我们将使用循环指令使鱼不停地从一边移动到另一边。 程序算法:
  • 当(真)
    • 向前移动1米
    • 向左转动半圈
正如所见,算法非常简单:它只包含了2条指令,可以让鱼不停移动,指导算法停止。
现在我们实现算法:
  • 从画面底部拖住指令并放到编辑区域的第一行。 从弹出菜单中选择
  • 拖住包含第一个移动指令的代码行放到循环中 (放在包含文本 什么都不做的代码行中)
  • 当代码行包含移动指令后,只要拖住包含转向指令的代码行放到循环中。
  • 通过右击和选择”删除“,删除循环外的(包含第二个移动指令的)代码行。
  • 运行world程序。现在鱼持续的从一边游到另一边!
程序代码:

  my_first_method ( )
    
    
  while( 真的 )
       lilfish .移动( 向前 , 1米 ); 时长 = 4
  lilfish .转动( , 0.5旋转' ); 时长 = 4


问好鱼 - 阶段3

现在我们想添加尾巴移动。 更新后的算法:
  • 向左转动尾巴1/8圈
  • 当(真)
    • 向右转动尾巴1/4圈
    • 向左转动尾巴1/4圈
    • 向前移动1米
    • 向左转动半圈
为实现算法,执行:
  • 从对象树中选择鱼,然后选择尾巴_主干,然后选择尾巴
  • 拖住尾巴并把它放到编辑区域的第一行(循环指令之前)。 从弹出菜单中选择 转动,然后选择向左转,并从显示选项中选择其他.., 输入0.125,即1/8圈。
  • 复制你刚添加的代码行,并拖放它到循环的开始部分。改变方向为,并改变数值为1/4圈。
  • 复制你刚添加的代码行,并拖放它到循环的开始部分。改变方向为,并改变数值为1/4圈。
  • 运行world程序是否像预期一样运行?在进入下一个部分之前,请尝试定义问题。
正如所见,循环按顺序执行,即首先尾巴转动然后鱼再移动。 但是我们需要尾巴转动和鱼移动/转动可以同时执行。 我们该如何实现呢? 如果我们回来看一下屏幕底部的控制指令,我们就会看到指令一起执行。 这个指令可以并行执行操作,而不是顺序执行操作。 我们将用这个指令解决我们的问题。
  • 定义计数器: 计数器=1
  • 向左转动尾巴1/8圈
  • 当(真)
    • 并行执行
      • 向右转动尾巴1/4圈
      • 向前移动0.1米
    • 并行执行
      • 向左转动尾巴1/4圈
      • 向前移动0.1米
    • 如果计数器==5
      • 向左转动半圈
      • 重置计数器:计数器=1
    • 否则
      • 给计数器增加1


实现算法:
  • 给程序添加计数器定义: 点击创建新的变量按键来创建新的变量,选择类型数字并命名为计数器。 使用默认值1。
  • 拖住指令一起执行放到循环开始处。
  • 拖放下列2条指令到一起执行 - 尾巴转动指令和移动指令。 改变移动指令值为0.1米,时长值为 0.4秒(为了保持原速度,我们已经把前进长度和时长分为10份)。
  • 复制一起执行模块。 在新创建的模块中,把转向值从右改为左。
  • 拖住控制指令如果并把它放在刚刚创建的模块后边。 从弹出菜单中选择。 从方法的变量区域中拖住刚刚创建的变量把它放在指令如果的括号里,即覆盖当前值。 从弹出菜单中选择 "==" 并从新菜单中选择其他创建值5。
  • 拖住鱼的转向指令并把它放在指令如果后边。
  • 再次从变量区域中拖住刚刚创建的变量,这一次把它放在否则中。 从弹出菜单中选择 "++" - 增量操作符。
  • 运行world程序。 它是否按预期运行了? 如果不是, 请在进入下一部分之前定义问题。
程序代码:

  my_first_method ( )
    数字 计数器 = 1 ;
       lilfish.尾巴_主干.尾巴 .转动( , 0.125旋转' );
  while( 真的 )
    
  doTogether
       lilfish.尾巴_主干.尾巴 .转动( , 0.25旋转' );
  lilfish .移动( 向前 , 0.1米' ); 时长 = 0.4
  doTogether
       lilfish.尾巴_主干.尾巴 .转动( , 0.25旋转' );
  lilfish .移动( 向前 , 0.1米' ); 时长 = 0.4
  如果( ( 计数器 == 5 ) )
       lilfish .转动( , 0.5旋转' ); 时长 = 4
  计数器 = 1
  其他的
    计数器 ++


问好鱼 - 阶段4

你可能注意到了,程序出现了一个问题(有时候又称作“漏洞”)。 当鱼完成移动指令时,它停了一会,然后才会执行下一个指令。 如果我们想要得到连续的移动,我们需要使用另一个标准方法: 我们将使用移动以速度而不是移动
  • 删除两个一起执行模块中的移动指令。
  • 插入移动以速度指令:在对象树中选择鱼,从细节区域(在对象树下边)中选择方法,从方法中选择移动以速度。 把它拖放到删除的那一行的位置(对两个删除行执行同样的操作)。 从菜单中选择1/4圈每秒。
  • 运行程序 - 现在鱼可以连续移动了!


练习: 在转向过程中,鱼的尾巴没有移动。 怎么让它在转向时移动呢? 提示:使用标准方法转动以速度取代转动来避免不连续移动。

程序代码:

  my_first_method ( )
    数字 计数器 = 1 ;
       lilfish.尾巴_主干.尾巴 .转动( , 0.125旋转' );
  while( 真的 )
    
  doTogether
       lilfish.尾巴_主干.尾巴 .转动( , 0.25旋转' );
  lilfish .移动以速度( 向前 , 速度 = 0.1 );
  doTogether
       lilfish.尾巴_主干.尾巴 .转动( , 0.25旋转' );
  lilfish .移动以速度( 向前 , 速度 = 0.1 );
  如果( ( 计数器 == 5 ) )
    
  doTogether
       lilfish .以速度 转动( , 速度 = 0.25 );
  lilfish.尾巴_主干.尾巴 .转动( , 0.25旋转' );
  doTogether
       lilfish .以速度 转动( , 速度 = 0.25 );
  lilfish.尾巴_主干.尾巴 .转动( , 0.25旋转' );
  计数器 = 1
  其他的
    计数器 ++


问好鱼 - 阶段5

如果我们检查一下缩写代码 - 主方法 - 我们会发现所有程序代码都和鱼有关,而和当前对象 -world无关。 在基于/面向对象的编程中我们尝试组织代码以便每一个对象都可以处理自己的属性和方法。 因此,我们喜欢把主方法代码移动到鱼的方法中。

首先, 我们复制world的主要_方法并把它粘贴在鱼的方法区域中,重命名为巡回
  • 选择对象树中的world对象,右击主要_方法并复制。
  • 选择对象树中的鱼对象,并选择方法_标签。 右击当前方法摇动并选择粘贴
  • 右击刚刚创建的新方法,并重命名为巡回
删除主方法代码:删除第一条指令以及“当循环”指令,然后删除变量计数器

现在我们从主方法中调用鱼的计数器方法 - 这个方法会在world程序启动时被调用:
  • 选择对象树中的鱼。
  • 拖住鱼的方法巡回并把它放到编辑区域中主方法的第一行。
  • 运行world程序 - 鱼应该像之前设计的那样移动!


你可能注意到了除了创建的方法,鱼的方法标签中还有另一个方法 - 摇动。 它是鱼的库方法,由鱼对象的创建者创建(创建者的名字会在我们把鱼从图库中添加时显示出来)。 读取方法代码并估计在进入下一部分之前该方法可以做什么。

为了看到方法摇动做什么,在主方法中添加对此方法的调用并使得对方法巡回的调用失效(你可以通过右击选择失效选项来实现)。 运行world程序。 它有什么动作?

在许多情况下,你可以通过检查对象的方法来了解库对象。在我们的例子中,通过检查方法摇动我们可以看到尾巴,头部和眼睛是如何移动的,进而最终动画非常精彩! 你可以通过方法巡回改进摇动方法。
注释: 注意你不能简单的从主方法中调用两个方法, 因为它们每一个都包含了无限循环。


问题: 我们该如何从主方法中调用鱼的方法巡回摇动使二者同时执行呢? 请在阅读下列代码之前试着回答这个问题。

程序代码:

  my_first_method ( )
    
    
  doTogether
       lilfish.观光 ( );
  lilfish.摇动 ( );