Showing posts with label Director Tutorial. Show all posts
Showing posts with label Director Tutorial. Show all posts

Tuesday, 21 February 2012

Character Animation


Using keyframePlayer

This tutorial assumes you have completed my others in the series. You'll need to start with characteranim.dir, which has the shockwave 3D cast member with animation in the form of movement in the legs and arms. I've also included another model, a hat, which you can see in the pic on the left.
1. Open the characteranim.dir movie, and open the scene control behavior. For now, we'll run through some code already added. 

At the top, you'll see:
  global gWearHat
  property pTofuBB, pHatBB
  property pCamera, pFlip

I have a global variable, gWearHat, which will define when the character is wearing the hat. Properties pTofuBB and pHatBB are the bounding boxes for the tofu character and hat models respectively. The pFlip variable is one that will flip the movement depending on which camera angle you' re using. 

Scroll down the behavior. In the beginSprite handler, we have:
  hat = pMember.model("hat")
Here we set the hat local variable to the related model.

After we've defined the hat model, we create a hat bounding box, starting with a model resource as follows.
  -- create tofu bounding box  tofuBB_MR = pMember.newModelResource("tofuBox",#box)
  tofuBB_MR.height = 25
  tofuBB_MR.width = 25
  tofuBB_MR.length = 50

  pTofuBB = pMember.newModel("tofuBox",tofuBB_MR)
The above numbers relate to the proportions of the tofu. If you were doing this with your own model, you' d need to keep track of the units in your 3D application. In my case, it was 3DS Max. You could create the bounding box in your 3D application to avoid the need to do it via Lingo as I have.

Next we have:
  pTofuBB.worldPosition = tofu.worldPosition
  
pTofuBB.worldPosition.z = 23
This locates the Tofu character Bounding Box in the scene. I moved the z co-ordinate up 23 units. Otherwise my character would sink through the ground. Again, doing this in the 3D application would have been simpler.

We then have:
  invisShader = pMember.newShader("invisShader",#standard)  invisShader.transparent = TRUE  invisShader.blend = 50  ptofuBB.shaderList = pMember.shader("invisShader")
The above creates a new shader that is meant to be invisible. I have set the blend to 50 so that we can see the bounding box (as seen in the pic). We'll change it to 0 later. The pic shows the bounding box (sphere) for the hat, which we'll create later.
2. We' re now going to create some parent child relationships.  You can read more about parent-child relationship in 3D at my The make-up of shockwave 3D casts page. Here' s a quick reason why we' re using this technique:

The primary benefit of these parent-child relationships is that they make it easier to move complex models around in the 3D world and to have the component parts of those models move together in the proper way.

Hopefully that will make sense in the context of this tutorial. After the line:
  -- create parent child relationships
insert
  ptofuBB.addChild(tofu, #preserveworld)  ptofuBB.addChild(pMember.model("armR"), #preserveWorld)  ptofuBB.addChild(pMember.model("armL"), #preserveWorld)  ptofuBB.addChild(pMember.model("legR"), #preserveWorld)  ptofuBB.addChild(pMember.model("legL"), #preserveWorld)

In the above code, we made the character bounding box the parent of a number of child models. In a parent child relationship, you can link multiple child objects to a parent but each child can only have one parent. The child objects are the tofu character, and the arms and legs.

3. Let's inspect some other code I have. 
  -- create hat bounding box  hatMR = pMember.newModelResource("hatSphere",#sphere)  hatMR.radius = 18  pHatBB = pMember.newModel("hatSphere", hatMR)  pHatBB.worldPosition = hat.worldPosition  pHatBB.shaderList = pMember.shader("invisShader")  pHatBB.addChild(hat, #preserveWorld)

By
 now, I' d hope the above would all make sense to you. So, I won' t say anything more.

4. You can scroll through the more code which should all be clear. Finally, you'll get to:
  -- set starting motion speed
Follow this statement with:
  pMember.model("armR").keyframePlayer.playRate = 0  pMember.model("armL").keyframePlayer.playRate = 0  pMember.model("legR").keyframePlayer.playRate = 0  pMember.model("legL").keyframePlayer.playRate = 0  pMember.registerForEvent(#timeMS,#SetCollision,me,2500,50,0)

The keyframe playRate is a property which defines how fast or slow to play back the keyframe motion. A value of 2 would double the speed, 0.5 would halve it. A value of 0, in our case, sets it to a paused state.

The keyframePlayer registers 2 events - #animationStarted and#animationEnded. And these can be used by handlers declared inregisterforEvent()
5. Scroll down to the keyDown handler and look though the code, which should all make sense. Note the statement:
  if keypressed(8) then changeCamera

This means that if the character c is pressed, the changeCamera custom message will be executed.

6. Scroll down to the changeCamera handler.
See the code:
  pCamera.rotate(0,0,180,pTofuBB)
  -- when the camera rotates 180, flip the arrow direction as well  pFlip = pFlip*-1 
What' s happening here is that the camera rotates 180 degrees and the pFlipvariable is multiplied to -1. pFlip will become negative it if was positive or positive if it was originally negative.

7. Scroll up to the keyUp handler. After the pDownArrow statement, add:

  pMember.model("armR").keyframePlayer.playRate = 0
  pMember.model("armL").keyframePlayer.playRate = 0  pMember.model("legR").keyframePlayer.playRate = 0  pMember.model("legL").keyframePlayer.playRate = 0
This sets the leg and arm movement to a paused state when the any key is released.

8. Scroll back up to the exitFrame handler, and at the start you'll seeSetMoving. This is the custom message to define the movement of the character when a key is pressed. Now scroll down to the SetMoving handler. 

At the start, insert the following code: 
  if pRightArrow then pTofuBB.rotate(0,0,-5*pFlip)  if pLeftArrow then pTofuBB.rotate(0,0,5*pFlip)
  if pUpArrow then
    pTofuBB.translate(0,-5*pFlip,0,#self)
    pMember.model("armR").keyframePlayer.playRate = 1
    pMember.model("armL").keyframePlayer.playRate = 1
    pMember.model("legR").keyframePlayer.playRate = 1
    pMember.model("legL").keyframePlayer.playRate = 1
  end if
  if pDownArrow then
    pTofuBB.translate(0,5*pFlip,0,#self)
    pMember.model("armR").keyframePlayer.playRate = 1
    pMember.model("armL").keyframePlayer.playRate = 1
    pMember.model("legR").keyframePlayer.playRate = 1
    pMember.model("legL").keyframePlayer.playRate = 1
  end if
When the up or down arrows are pressed, the playRate is set to 1 (normal speed). We also have a variable, pFlip, in the equation, which switches between positive and negative, as we saw earlier.

9. The code that you'll see in the rest of the SetMoving handler just moves the character up and down the terrain using the modelUnderRay technique. It is explained in the Terrain following tutorial.

10. Scroll down to the SetCollision handler. In here, I have code that uses themodelUnderRay technique to test when the character collides with objects. But I have included 2 bits of collision scripting. One collision detect will activatecheckObjectFoundDistance, the other checkForCollision.checkForCollision is exactly as described in the collision detection tutorial.checkObjectFoundDistance is what' s used for finding the hat.

11. Scroll down to checkObjectFoundDistance handler. Here we check the distance from the starting point of the ray to the hat and if the distance represents a collision, gWearHat becomes TRUE.

12. Scroll backup to the exitFrame handler. After the SetMoving statement add:
  if gWearHat = TRUE then
    ptofuBB.addChild(pHatBB,#preserveParent)
    pHatBB.worldPosition = pTofuBB.worldPosition
    pHatBB.worldPosition.z = 87    gWearHat = FALSE
  end if
Following the result of step 11, we now have gWearHat being TRUE and so we make changes accordingly. The hat bounding box is made a child of the character bounding box so that when the character walks, the hat will not be left behind. We change the world position of the hat to correspond to the bounding box of the tofu character. Then we move the hat up 87 along the z axis so it sits on the character' s head. We must set gWearHat back to FALSE otherwise theif gWearHat = TRUE will be put into a infinite loop. The variable can' t be set back to TRUE since the ray that was created to detect the hat is downwards below the character' s head.

13. Now it's time to play the movie and see how it all works. Notice the bounding boxes. To make them completely invisible, change the blend to 0. This was referred to at the end of step 1 just before step 2. 

Another thing to note is that when you press c, you can toggle between a camera looking from behind and in front. But, no matter what camera view you're using, the up arrow will always move the character up the screen, down arrow will move it down. You can download the completed movie from here. 

If you're wondering why I used a global variable for gWearHat (it could have worked as a property variable like the others used), you can email me or check back this page in the near future as there is a planned addition. 

How the legs and arms were set up in 3DS Max
In Max, the arms and legs were animated as individual objects in a keyframed manner. Lingo made them a child to the body. Often, character animation is created using mesh deformation using a skelteton or bones hierachy. For more info on biped and bones animation setup in Max, look at the Preparing 3D content for Shockwave 3D technote (for general info) and the Character Animation for Shockwave 3D article (detailed info).

Collision Detection 1


Using the Collision Modifier

This tutorial builds on the last. So, you will need your completed Director file. If you want to download the completed one, it is available here (3Denv_cameras.dir).
1. Open the movie and open the scene control behavior.

2.  Add the following to the end of the beginSprite handler.
createWall 

3. Now we will define what the createWall does. As the name suggests, we will create a wall which will be used for collision detection with the character. Before we write the script, you will need to download the image brickmap.bmp, and import it into your movie. This will be used as a texture for the wall.

4. Add the following to the script.
on createWall
  -- create a box model resource for the wall  wallRes = p3Dmember.newModelresource("wallRes",#box)  wallRes.height = 10  wallRes.length = 50  wallRes.width = 150
  -- create a model from the model resource  wallMod = p3Dmember.newModel("wallMod",wallRes)

  -- locate the wall in our scene  wallMod.translate(-50,-20,25)

  -- create a texture for the wall, using the image brickMap  wallTexture = 
p3Dmember.newTexture \
("brick",#fromCastmember, member("brickMap"))

  -- assign the texture to the shader for the wall  wallMod.shader.texture = wallTexture
  -- scale down the texture and tile the bricks along   -- the wall  wallMod.shader.textureTransform.scale  = \
vector
(0.2, 0.4, 1)

end
We have just scaled the image to a width of 1 fifth (0.2) of the original and the height to 2 fifths (0.4). By default, the textureRepeat property is set to TRUEso the tiling will occur. If the following statement is included, the texture will not tile:
wallModel.shader.textureRepeat = FALSE

There are other ways you can control texture mapping in Director including:
textureMode specifies how the texture is mapped onto the surface of the model
textureModeList property specifies textures for layers other than the first layer
4. You can now play the movie and move the character around. As you will see, the character can move through the wall. We will now set up the collision detection using the collision modifier.
Add the following to the end of the beginSprite handler (before the endstatement).
createCollionDetect
5. Add the following to the script.
on createCollionDetect
  -- add collision modifier to the "wall"   p3Dmember.model("wallMod").addModifier(#collision)
  -- set bounding geometry for collision detection to   -- bounding box of model  p3Dmember.model("wallMod").collision.mode = #box
  -- resolve collision for wall  p3Dmember.model("wallMod").collision.resolve = TRUE
  -- add collision modifier to the character   pCharacter.addmodifier(#collision)
  -- set bounding geometry for collision detection to   -- bounding box of model  pCharacter.collision.mode = #box
   -- resolve collision for the character  pCharacter.collision.resolve = TRUE
end

By attaching the collision modifier to objects in a 3D world, we can detect collisions with between all the objects to which the modifier has been applied. Once the collision modifier has been attached to a model, the following collision modifier properties are accessible:
enabled (collision) allows you to turn off the collision detection (by setting the value to FALSE) while the modifier is applied. The default value is TRUE.
resolve indicates whether collisions models stop when they collide with each other. If the resolve is set to FALSE, then that model will continue to move. The default value is TRUE. In our example, we could have left out the resolve statements. Try change the character resolve to FALSE and see what happens.
immovable indicates whether a model can be moved as a result of collisions. This default value is FALSE.
mode (collision) indicates the geometry used for collision detection and could be:
#mesh uses the actual mesh geometry of the model’s resource.It is more precise but usually slower than #box or #sphere.
#box uses the bounding box of the model. It is useful for objects that can fit more tightly in a box than in a sphere, such as our wall.
#sphere is the fastest mode, because it uses the bounding sphere of the model. It is the default value for this property.

7.  Play the movie and test what you just created.
You can download the completed movie from here.

The use of the collision modifier is useful for relatively simple applications only. It can be very processor intensive, especially if there are complex models and a large number of modifiers attached to objects. The recommended technique for collision detection of more complex models is to use the modelsUnderRaycommand. You can see that in the next tutorial.

Monday, 20 February 2012

The Waving Woman - Animating Multiple Images & Alignment

In this tutorial, we will be using an existing file. You will need to downloadtut5a.dir (right click on the file and choose 'Save Target As'). To save time, cast members have been imported and placed on the Stage.

1. Open movie tut5a.dir. 

2. Double click cast member 4 to open it in the Paint window. You may notice a small cross at the elbow. This is what we will be using for the pivot point of the arm that will be animated. We will also use this for the Registration Point, which we will set later. 

3. Move the cursor over the selection rectangle  and hold down the mouse button to display the pull-down menu. Make sure Shrink is selected. 

4. Drag a box around the arm to select it. 

5. Click the Free Rotate button  at the top of the window. Click one of the anchor points of the rectangle (at the corners) and rotate the rectangle 90 degrees clockwise.

6. We are now going to use Director’s Auto Distort to generate the sequence of in-between cast members. Auto Distort works for any of the features in the Paint window's effect toolbar.

Choose Xtras > Auto Distort . A window pops up asking how many in-between members you would like Director to generate. Type in 5 and click the Beginbutton. 

7. Close the Paint window and open the cast window. Notice we now have 6 cast members of the arm rotating. 

Note: A quicker way to rotate the arm would seem to be to use the Rotate Right button. However, using this with Auto Distort will create a set of rotating arms moving in an anticlockwise direction, which is not what we want.

8. Select the 6 arm cast members - click on the first, then while holding downShift, click on the last. 

9. Click on frame 2 in channel 2, and choose Modify > Cast to Time. 

 10. In the Property Inspector, change the ink effect of this sprite to Background Transparent.

Notice how this makes the white part of the arm transparent. Ink effects describe transparencies within sprites. Ink effects allow a range of transparencies of graphic members. Most apply only to bitmap/paint members. The Background Transparent ink effect makes all white pixels in a bitmap, transparent.

Changing the colour depth of images in Director
11. Select all the arm cast members in the cast window.

12. Choose Modify > Transform Bitmap.

13. Change the colour depth to 8 bits and the palette to 3:arm palette.
The arm palette was created outside Director and imported with the original arm image. The image was changed to 16 bit because Director does not distort 8 bit images very well. In fact, whenever editing an image (even in PhotoShop), it's best to work with a high colour depth image and only optimise it once all work on the image is complete.

Setting registration points
When you import images, Director places a registration point at the centre of the image. This works fine when you are dealing with cast members that are the same proportions. This is not always the case as with our set of arms. The solution is to move the registration point to a position where we can easily identify where the images should be aligned.

14. Double click cast member 4 to display it in the paint window.

15. Click the Registration Point button .
Two intersecting lines appear in the paint window. The point where they intersect is the registration point. The registration point has been set to the bottom of the elbow, where a little cross has been drawn. Zoom into that area to get a closer look.

16. Move through the other arm cast members and click on the cross at the elbow to move the registration point. 

Aligning Sprites

When you place images directly into the Score, Director aligns their registration point to the centre of the stage. Now we will position sprites in relation to other sprites on the stage.

1. In the Score we have two sprites in channel 2. In frame 1, we have a sprite of the woman's arm and in frames 2 to 8, we have the animated arm. Select the animated arm sprite (channel 2, frames 2 to 8). Press Shift and click frame 1 in the same channel. This selects both sprites. 

 2. Choose Window > Align. 

3.
 The Align window appears.
Choose Align horizontal regPoint

 and then Align vertical regPoint. Director aligns all sprites in relation to the last sprite selected, which in our case was a sprite already positioned correctly.


Extending the animation and adding a trails effect

1. Delete frame 1 in channel 2, and move the arm sequence to frame 1. 

2. Copy and paste the arm sequence in channel 2 until the animation continues to frame 30 ( as seen below). 

3. Choose Modify > reverse sequence to reverse the every second sequence.

4. Select all the sprites in channel 2. You can do this by simply clicking the channel number as shown below.



5.
 Click on the Trails button  in the Property Inspector.
Rewind and play the movie. As you see, the trails button allows the last sprite position to remain on the stage, providing a ghost type effect. 
One last little cleanup task before you can notch up another Director tutorial to your belt.

6. Expand the Score so you can see the Effects Channels by clicking on theHide/Show Effects Channels button at the right end of your Score window.
 

You will notice that there a few sprites in the palette channel. When you drag an image cast member to the Stage/Score, Director automatically places its palette in the palette channel.This was a useful feature when dealing with 8 bit (256 colour) monitors. Today, it is not necessary, so select all the palette sprites and delete them.
Now would be a great time to save your movie.

You can see the completed Tutorial 5 here tut5a_fin.dir 

Animating the Arm without Multiple Cast Members

I have just shown you how to create multiple cast members within Director to create animation. However, the above process was not the most efficient way to do it from a disk space point of view. It is better to rotate the arm sprite using Director's vector animation capability rather than relying on multiple cast members.

1.
 Delete all the arm sprites in channel 2. 

2.
 Place the arm cast member (first arm of the sequence we created before) in channel 2, change the ink to Background Transparent. Move the sprite so it is placed correctly in relation to the woman.
The movie should now be back to its original state as movie tut5a.dir. 

3.
 In the Score, select sprite 1 (the woman) and sprite 2 (the arm), then in the Property Inspector, change the end frame from 30 to 9 . This changes contacts both sprite to end in frame 9.

4.
 Click on frame 5 in sprite 2 and choose Insert > Keyframe.

5. In the Sprite tab of the Property Inspector, change the Rotation of the keyframe at frame 5 to 90, and change the Rotation of the end frame of the sprite to 0.

Rewind and play the movie. We now have virtually the same effect without using all the extra cast members. Getting Director to rotate the sprite in the Score makes your computer's CPU to do some extra work. Having changing cast members is less work for the CPU but the added file space means the computer needs more RAM. With this example, the arm images are small and so would not make a significant difference from a RAM point of view. But if this was a web animation, any file saving means that it would be able to download quicker across the Internet.

You can see the completed tutorial 5 here tut5b_fin.dir 

Adding sound

To make this animation more multimidia-ish, we'll now add some sound and a touch of interactivity. For this section you will need g_day.wav. (Right click on the hyperlink and choose 'Save Target As') 

1. Open tut5b_fin.dir (your tutorial as completed to step 5 above). 

2.
 Import g_day.wav.

3. Drag the sound cast to one of the sound channels in the score and drag its end point to frame 9.

Rewind and play the movie.

Playing the sound file before continuing
4. Double click frame 1 in the tempo channel.

5. In the Frame Properties: Tempo dialog box (as shown below), select Wait for Cue Point, Sound 1: g_day should be selected in the Channel field (assuming your sound is in channel 1) . Select End under the Cue Points drop down, and click OK.


Rewind and play to see (and hear) how the tempo settings work. 

Adding simple interaction

1. Change the starting frame of the sound sprite to frame 2.

2. Double click frame 1 in the tempo channel .

3. Select the Wait for Mouse Click or Key Press option, and click OK.

4. Rewind and play the movie. Notice on frame 1, a flashing cursor appears. Click on the screen and see what happens. Play the movie again and this time press any key and see what happens.