English (United Kingdom) Hebrew

Animation guide: Hello Fish!

This guide contains a sample program to study animation principles in Mama. The program contains 4 phases and it is a quick introduction with the Mama language and the development environment. Topics covered by this guide:
  • scene editing and program editing
  • editing a method in the program editor, using the drag & drop mechanism
  • introducing standard methods in Mama
  • basic movement in the 3D space
  • parallel execution of operations to implement successive movement
  • introducing basic control instructions: condition and loop


Contents

Hello Fish - phase 1

In this program we will make an animation of a fish moving deep in the ocean to one side, when arriving the edge of the screen turn back and return to the starting point.

  • run Mama, open a new world with a sea background
  • click the '+' button in the bottom of the 3D screen, for changing to scene editing mode
  • from the object gallery opened in the bottom window, select the directory Ocean, and then select OceanFloor. This is an ocean background, in which we will make the fish animation.
  • now add the fish, LilFish
  • locate the fish in the left side of the 3D window, facing the other side (right side of the world)
  • now, we want to make a movement of the fish to the other side:
    • add to the program method - the world's method main_method - a forward movement instruction for the fish of 1 meter. To implement that, drag the fish from the object tree into the program editing area, and from the popup menu select move, and then the parameter FORWARD. Click on more... and select duration, and from the menu select 4 seconds.
    • run the world. The fish moves to the right side of the screen and then stops.
  • now add a turn instruction to the fish when arriving to the right side: drag again the fish to the editing area, into the line following the line you just added, and from the menu select turn, then select left, and from the popup menu select half a turn. As before, set the duration to 4 seconds.
  • right click on the first line, and from the popup menu select duplicate - a copy of the line is created.
  • drag the duplicated line into the end of the program (i.e. to the line following the turn instruction). This instruction causes the fish to return to the starting point on the left side.
  • run the world. The fish moves to the right side of the screen, then turns and goes back.
The program code:

  main_method ( )
    
       lilfish .move( FORWARD , 1 meter ); duration = 4 seconds
  lilfish .turn( LEFT , 0.5 revolutions ); duration = 4 seconds
  lilfish .move( FORWARD , 1 meter ); duration = 4 seconds


Hello Fish - phase 2

We want the fish to continue moving from side to side, turning when arriving to the edge of each side. How can we make it?
The bottom of the screen contains control instructions - these instructions guide the execution of the program, and they contain conditional instruction, loops, parallel execution of instructions, etc. We will use a loop instruction to make endless movement of the fish from side to side. The program algorithm:
  • while (true)
    • move forward 1 meter
    • turn left half a revolution
As we can see, the algorithm is quite simple: it contains only 2 instructions which the fish makes again and again, until we stop the program.
Now we will implement the algorithm:
  • drag the instruction while from the bottom of the screen and drop it as first line in the editing area. From the popup menu select true
  • drag the line containing the first move instruction into the loop (into the line containing the text Do Nothing)
  • similarly, drag the line containing the turn instruction into the loop, after the line containing the move instruction.
  • delete the line left outside the loop (containing the second move instruction) by right clicking on it and selecting 'delete'.
  • run the world. Now the fish moves from side to side continuously!
The program code:

  main_method ( )
    
    
  while( true )
       lilfish .move( FORWARD , 1 meter ); duration = 4 seconds
  lilfish .turn( LEFT , 0.5 revolutions ); duration = 4 seconds


Hello Fish - phase 3

Now we want to add the fish's tail movement. The updated algorithm:
  • turn the tail left a 1/8 revolution
  • while (true)
    • turn the tail right a 1/4 revolution
    • turn the tail left a 1/4 revolution
    • move forward 1 meter
    • turn left half a revolution
To implement the algorithm do:
  • from the object tree, select the fish, then tailStem, then tail
  • drag the tail and drop it as the first row in the editing area (before the loop instruction). From the popup menu select turn, then select left turn, and from the displayed options select other.., and type 0.125, i.e. 1/8 revolution.
  • duplicate the line you've just entered, and drag it into the beginning of the loop. Change the direction to right, and change the value to 1/4 revolution.
  • duplicate the line you've just edited, and change again the direction to left
  • run the world. Is it running as expected? try to define the problem before moving to the next section.
As you can see, the loop is executed sequentially, i.e. first the tail turns and then the fish movement. But what we would want is to have both the tail turns and the fish movement/turns to execute simultaneously. How can we do that? If we look back at the control instructions at the bottom of the screen, we will encounter the instruction doTogether. This instruction enables parallel execution of operations, contrary to sequential execution. We will use it to fix our problem.

The program algorithm:
  • define counter: counter=1
  • turn the tail left a 1/8 revolution
  • while (true)
    • do in parallel
      • turn the tail right a 1/4 revolution
      • move forward 0.1 meter
    • do in parallel
      • turn the tail left a 1/4 revolution
      • move forward 0.1 meter
    • if counter==5
      • turn left half a revolution
      • reset the counter: counter=1
    • else
      • increment the counter in 1


Implementing the algorithm:
  • Add to the program a counter definition: click the create new variable button to create new variable, select type Number and name it counter. Leave the default value 1 as it is.
  • drag the instruction doTogether and drop into the beginning of the loop
  • drag and drop into the doTogether body the following 2 instructions - tail turn instruction and move instruction. Change the move instruction value into 0.1 meter, and the duration to 0.4 seconds (to keep the original speed, we have divided both the advance length and its duration in 10).
  • duplicate the doTogether block. In the new block that you've just created change the turn value from right to left.
  • drag the control instruction if and drop it after the last block you've just created. From the popup menu select true. Drag the variable you've created from the variable area of the method and drop it into the parentheses of the instruction if, i.e. onto the existing value true. From the popup menu select "==" and from the new menu select other and create the value 5.
  • drag the fish's turn instruction and drop it after the if instruction
  • drag again the variable you've created from the variable area, and this time drop it into the else body. From the popup menu select "++" - the increment operator.
  • run the world. Is it running as expected? if not, try to define the exact problem before moving to the next section.
The program code:

  main_method ( )
    Number counter = 1 ;
       lilfish.tailStem.tail .turn( LEFT , 0.125 revolutions );
  while( true )
    
  doTogether
       lilfish.tailStem.tail .turn( RIGHT , 0.25 revolutions );
  lilfish .move( FORWARD , 0.1 meters ); duration = 0.4 seconds
  doTogether
       lilfish.tailStem.tail .turn( LEFT , 0.25 revolutions );
  lilfish .move( FORWARD , 0.1 meters ); duration = 0.4 seconds
  if( ( counter == 5 ) )
       lilfish .turn( LEFT , 0.5 revolutions ); duration = 4 seconds
  counter = 1
  else
    counter ++


Hello Fish - phase 4

As you probably noticed, the program contains a problem (which sometimes is called a 'bug'): when the fish completes a move instruction it stops for a short time, and only then continues with the next instruction. If we want a smooth movement we will have to use a different standard method: instead of move we will use moveAtSpeed.
  • delete the move instructions in both doTogether blocks
  • insert instead of each move instruction you deleted a moveAtSpeed instruction: in the object tree select the fish, and now from the details area (underneath the object tree) select methods, from the methods select moveAtSpeed, drag it and drop into the line in place of the lines you deleted (do it for both lines you deleted). From the menu select 1/4 revolution per second.
  • run the world - now the fish moves smoothly!


Exercise: during the turns, the fish's tail is not moving. How would you cause it to move while turning? Hint: use the standard method turnAtSpeed instead of the method turn to avoid the non continuous movement.

The program code:

  main_method ( )
    Number counter = 1 ;
       lilfish.tailStem.tail .turn( LEFT , 0.125 revolutions );
  while( true )
    
  doTogether
       lilfish.tailStem.tail .turn( RIGHT , 0.25 revolutions );
  lilfish .moveAtSpeed( FORWARD , speed = 0.1 meters per second );
  doTogether
       lilfish.tailStem.tail .turn( LEFT , 0.25 revolutions );
  lilfish .moveAtSpeed( FORWARD , speed = 0.1 meters per second );
  if( ( counter == 5 ) )
    
  doTogether
       lilfish .turnAtSpeed( LEFT , speed = 0.25 revolutions per second );
  lilfish.tailStem.tail .turn( RIGHT , 0.25 revolutions );
  doTogether
       lilfish .turnAtSpeed( LEFT , speed = 0.25 revolutions per second );
  lilfish.tailStem.tail .turn( LEFT , 0.25 revolutions );
  counter = 1
  else
    counter ++


Hello Fish - phase 5

If we review the method we wrote - the main method - we will find that all the program code is associated with the fish, and has nothing with the current object - the world. In object based/oriented programming we try to organized the code so that each object handles its associated properties and methods. Thus, we would like to move the main method code into a method within the fish object.

First, we will copy the world's main_method and paste it into the fish's methods area, renaming it to tour.
  • select the world object in the object tree, right click on the main_method and copy it.
  • select the fish object in the object tree, the select the methods tab. Right click on it's current single method, waggle and select paste.
  • right click on the new method you just pasted, and rename it to tour.
Delete of the main method code: delete the first instruction and the while loop instruction, then delete the variable counter.

Now we will call the fish's counter method from the main method - which is called when the world starts:
  • select the fish in the object tree
  • drag the fish method tour and drop it into the first line of the main method in the editing area
  • run the world - the fish is supposed to move exactly as before!


You probably noticed that the fish's method tab contains another method besides the one you created - the method waggle. This is a library method of the fish object, that is it was built by the builder of the fish object (which his name was displayed when we added the fish from the object gallery). Read this method's code and try to estimate what it does before moving to the next section.

To see what waggle does, add a call to this method in the main method and disable the call to the tour method (you can do that by right clicking on it and selecting the disable option). Run the world. What does it do?

In many cases you can learn about a library object by reviewing its methods, which the object builder wrote. In our example, from reviewing the method waggle we can see how the tail movement, the head movement and the eyes movement are done, so that the result animation is very convincing! You may improve the tour method according to the code of the waggle method.
Comment: note that you cannot simply call both methods from the main method, since each of them contains an endless loop.


Question: how can we call both fish's methods, tour and waggle, from the main method so that both will be executed simultaneously? Try answering this question before reading the following program code.

The program code:

  main_method ( )
    
    
  doTogether
       lilfish.tour ( );
  lilfish.waggle ( );