Articles Archive
Articles Search
Director Wiki
 

Director 7 Sprite Follies

February 16, 1999
by Zac Belado

Bitmap graphics are a double-edged sword in Director. They look good, they are compact, but your ability to transform and manipulate them is limited. Director 7 helps to solve this messy little problem with the addition of several new bitmap manipulation commands to Lingo (all of which are facilitated by the addition of a new graphics engine) and in this article I'll deal with 5; quad, rotate, skew, flipH and flipV.

Rotate

One of the most frequently asked questions on DIRECT-L (or almost anywhere two or more Director developers gathered) was the best way to rotate a sprite with Director. Prior to Director 7, your solutions were limited to either using Alphamania and Effector Set Xtras or pre-generating all the rotated graphics ahead of time and writing a behavior to swap bitmap cast memebrs out. With Director 7 it's now fairly simple using the new sprite property "rotation". Rotation was possible in Director 6 with the addition of the Flash Asset Xtra, but now the property has been extended to all of the "graphic" cast members; animated GIFs, bitmaps, Flash sprites and QuickTime movies.

Despite its simplicity, rotation can actually be accessed in one of two ways. This is common to most of the sprite properties with the addition of the new dot syntax in Director 7.


sprite(2).rotation = 123
set the rotation of sprite 2 to 123

Both examples will changes sprite 2's rotation property to 123 and there is almost no difference in the execution times of either method. Not that if you use dot notation to access a property you can not use the "set" keyword. It will generate an error.

If you assign the sprite to a variable first


thisSprite = sprite(2)
thisSprite.rotation = 123

the code will execute in approximately 95% of the time of the other two examples. This is almost entirely because Director no longer has to evaluate the sprite reference in the code.

Positive rotation values (and you can enter floating point as well as integer values) will rotate the sprite clockwise and negative values will rotate a sprite counter-clockwise. You can also enter rotation values from 21,474,836.48 degrees to - 21,474,836.48 degrees which is 59, 652 full rotations.

QuickTime movies add an extra wrinkle, as one would expect, by allowing you to not only set the rotation via the sprite's property but also through the QuickTime member's rotation property.

Both examples (below) will set the rotation of a QuickTime movie to -10 degrees (assuming that the sprite on screen is represented by cast member 5.


sprite(2).rotation = -10
member(5).rotation = -10

Click and drag on the center graphic to rotate it or simply click on the values to make it snap to a particular rotation.

Unlike bitmaps, which have their bounding box redrawn to accommodate the rotation of the sprite,

QuickTime movies will deform to ensure that they still display inside their original bounding box.

If you set the QuickTime member's "crop" property to 1 the movie will instead be cropped inside its original bounding box.

Flash sprites operate in a manner similar to QuickTime movies. The sprite and the member both have a rotation property so you can rotate a Flash sprite onscreen in either fashion. You can only use the member's rotation property to modify the sprite onscreen if the obeyScoreRotation property of the member is true. If it is false then the sprite onscreen will not change

Flash sprites also have the ability to rotate within their bounding box, although unlike QuickTime movies they do not "warp" to remain inside the bounding box but are always cropped by it. The D7 Lingo dictionary states that Flash sprites will do this by default but this is no longer true in D7. In order to enable this functionality you have to set the Flash member's obeyScoreRotation property to False.

skew

Skewing is the process of tilting (or shearing) an image along the vertical access.As with the rotation property, skew values can be either positive

or negative.

Both sprites have had their skew property value set to 30. The first positive, the second negative. Positive values skew an object in a clockwise direction and negative values, as you would expect, in a counter-clockwise direction.

You can mix skew and rotation values without one resetting the other.

Despite the fact that both skew and rotation will let you set values higher than 360, you should try and write your code to restrict values to 360 degrees. When you try to assign a value over 21,474,836.47 or below -21,474,836.47 the property will "roll over" (much like a car's odometer). Instead of resetting to zero though, the property will reset to either -116.47 or 116.48. This is because 21,474,836.47 degrees is equal to 116.47 degrees (when it is contrained to a maximum of 360) and -21,474,836.48 degress is equal to -116.48 degrees. This will cause your sprite to display in an irregular fashion. The simplest way to ensure that this doesn't happen would be to add a small prepareFrame handler to ensure that the values aren't higher than 360.


on prepareFrame me
 sprite(the spriteNum of me).rotation = sprite(the ¬
  spriteNum of me).rotation mod 360
end

If you assign a value larger than 360 this function will "snap" it back into range.

flipH and flipV

The two flip properties are really the poor second cousins of the new sprite properties. They really only do one thing (flip the sprite either horizontally or vertically). This is either a plus (if that's all you want to do) or a minus considering that you can duplicate flipV by simply setting a sprite's skew property to 180 degrees (or -180 degrees if you want to be contrary). flipH can be duplicated by setting the rotation and skew both to 180 degrees.

Both flip properties can be used in conjunction with skew and rotate and you can run into some interesting annoyances while working such as sprites that look "normal" but are still skewed.

flipH and flipV both flip the sprite in relation to its regPoint so you can easily use them in conjunction with skew and rotation without having to worry about the sprite jumping around the screen as you flip it.

Click on flipH or flipV to flip the sprite. Initially the sprite is having its skew property incremented but you can click on the rotation button to make the movie modify the sprite's rotation instead. I've deliberately written the scripts so that the skew and rotation values are not reset when you change what property the movie modifies so you can see the sometimes bizare and sometimes interesting results you can get.

quad

Of course all of these new capabilities are a result of the new graphic engine that Macromedia added to Director. The tip of the iceberg, so to speak, of the engine is the quad sprite property. A sprite's quad is a list of points (all containing float values) that describe the upper left, upper right, lower left and lower right corner points of the sprite.

A quad list would look something like


[point(77.0000, 69.0000), point(254.0000, 69.0000), ¬
  point(254.0000, 121.0000), point(77.0000, 121.0000)]

Each point is directly accessible using D7's new dot syntax so you can easily get the upper right corner of the quad by using


put sprite(2).quad[2]
-- point(254.0000, 69.0000)

or for you "old school" coders


put getAt(the quad of sprite 2, 2)
-- point(254.0000, 69.0000)

While you can directly access the individual corners of a quad you can't set each one individually. Director insists that you pass it a list of points in order to modify a sprite's quad property. The annoying thing about this is the Director won't actually give you an error if you do try to set an individual quad corner.


put sprite(3).quad
-- [point(77.0000, 69.0000), point(254.0000, 69.0000), ¬
  point(254.0000, 121.0000), point(77.0000, 121.0000)]
sprite(3).quad[2]=point(100,100)
put sprite(3).quad
-- [point(77.0000, 69.0000), point(254.0000, 69.0000), ¬
point(254.0000, 121.0000), point(77.0000, 121.0000)]

The code doesn't work...but it also doesn't generate an error. Instead you'll have to create a temporary list, modify that and then set the sprite's quad to the new list.


tempList = sprite(2).Quad
tempList[1] = aPoint
sprite(2).Quad = tempList

As with rotation and skew, you can't use the set keyword when referring to a sprite directly (i.e., using the sprite channel in brackets).

All of the other sprite properties mentioned in this article are actually internal modifications of the sprite's quad property. In fact you could write code to duplicate these effects yourself...which James Newton graciously did when I asked him to replicate the rotation property using quads.

As in the last example you can click and drag the sprite to rotate it but before you do that click on the rotation co-ordinates and notice that they still work (they use the sprite's rotation property). Now click and drag the sprite and rotate it a few times. Notice now that the numbers will no longer reset the rotation of the sprite.

Click on the "reset" button to reset the sprite's quad property. Notice that once the quad is reset Director will "remember" the rotation property value.

This is because using the quad property disables the ability to skew or rotate and (despite the fact that it isn't mentioned in the Lingo dictionary) the flipH and flipV properties. Curious Lingo thrill seekers can download James' uncommented code (Mac or PC) and explore the possibilities that this offers.

So what can you do with the quad property? To answer this question let's write a behavior that, when attached to a sprite, will allow you to drag the sprite and modify another sprite's quad.

Click and drag the red squares and you can deform the underlying sprite.

The behavior will have 5 properties

pmodSprite -- the sprite it will modify
pquadPoint -- the quad list element that the sprite represents (1 to 4)
pAttachPoint - a symbol used to give the user a list of corners in text format so it is easier to select a quad corner for the sprite
pMySprite - the sprite the behavior is attached to
pOrgQuadPoint - the original point that the corner resided at

So the first thing we need to do is get some values back from the user:


on getPropertyDescriptionList me
  
  if not the currentSpriteNum then
    -- behavior has been attached to script channel
    exit
  end if
  
  propList = [:]
  
  -- create a list with all the names of the points in a quad
  set pointList = [#upperLeft, #upperRight, #lowerRight, #lowerLeft]
  
  -- get the sprite we will modify
  setaProp propList, #pModSprite, [#comment: "Attach to ¬
    which bitmap sprite", #format: #integer, #default: 1]
  
  --get the corner point
  setaProp propList, #pAttachPoint, [#comment: "Select point ¬
    to attach to", #format: #symbol, #default: #upperLeft, ¬
    #range: pointList]
  return propList
  
end getPropertyDescriptionList

By using a list of symbols instead of just asking the user for a number from 1 to 4 you can not only ensure that the user enters a valid value but also present the information in a more understandable fashion.

Once we have the properties we need to do some setup before the sprite gets on stage.


on beginSprite me
  
  -- assign the property to a sprite reference 
  -- and not an integer 
  pModSprite = sprite(pModSprite)
  
  -- grab a reference to the sprite this is attached to
  pMySprite = sprite (me.spriteNum)
  
  -- convert the symbol to a list entry
  case (pAttachPoint) of
    #upperLeft: 
      pQuadPosition = 1
    #upperRight:
      pQuadPosition = 2
    #lowerRight:
      pQuadPosition = 3
    #lowerLeft:
      pQuadPosition = 4
  end case
  
  -- get the point from the sprite's quad
  quadPoint = pModSprite.quad[pQuadPosition]
  
  -- puppet the sprites
  pMySprite.puppet = 1
  pModSprite.puppet = 1
  
  -- set the sprites location to the corner 
  -- it is attached to
  pMySprite.loc = quadPoint
  
  -- store the original point
  pOrgQuadPoint = quadPoint
  
end

First, sprite references are stored to the sprite we are modifying (pModSprite) and the sprite the behavior is attached to (pMySprite). There isn't any particular reason for it other than the fact that it makes the code slightly more legible


pMySprite.loc as opposed to sprite(me.spriteNum).loc

Once we have that, we need to convert the symbol into a list value that corresponds to a corner in the quad. In this instance I use a simple case statement, but you could also use getOne


quadPoint = getOne ([#upperLeft, #upperRight, #lowerRight, ¬
  #lowerLeft], pAttachPoint)

but I like case statements...

After the corner symbol has been converted we puppet the sprites, store the original corner point in the pOrgQuadPoint property and then move the sprite so it sits on the corner we have assigned it to.

So the movie has started and the sprite is sitting on the correct corner...now what?

When the user clicks down on the sprite and drags it, we need to modify the sprites position and also modify the quad of the pModSprite sprite. One added feature I want to include is the ability to snap both sprites back to their original location when the user is finsihed moving the sprite. Since both of these features do the same thing, we can simply write a new function that handles both eventualities.

First the mouseDown handler


on mouseDown me
  repeat while the mouseDown
    updatePoint the mouseLoc
  end repeat
end

All this does is, while the user continues to hold the mouse down on the sprite, pass the current mouse location to a handler.

When the user is finished, as indicated by a mouseUp event, we will use the same handler to snap the sprite back and restore the original quad.


on mouseUp me
  updatePoint pOrgQuadPoint
end

The updatePoint function is very simple


on updatePoint thisPoint
  
  -- set the sprite's location to the location
  pMySprite.loc = thisPoint
  
  -- get the pModSprite's quad
  tempList = pModSprite.Quad
  
  -- assign the corner we are at to the location
  tempList[pQuadPosition] = thisPoint
  
  -- put this back into the sprite's quad value
  pModSprite.Quad = tempList
  updateStage
  
end

What it does is assign the pModSprite's quad to a temporary list, and then take the point it has been passed (either the current mouseLoc or the quad's original corner location) and put that into the temporary list. Then we reset the quad value by setting the pModSprite's quad to the temporary list.

It is a very simple piece of code that allows you to generate a very complicated effect. I can't think of any other authoring enviroment that allows you this kind of simple power. A demo movie with the code and some graphics to twist and distort is available for download in Mac or PC format.

Director 7 presents a lot of new lingo and capabilities to learn, but it's well worth the effort. Stay tuned to DOUG, for a continuing series of these articles which will focus on what's new in D7.

Special thanks to James Newton for his "most excellent" quad rotation code and David Jennings for his answers to my rather bizaree questions.

Zac Belado is a programmer, web developer and rehabilitated ex-designer based in Vancouver, British Columbia. He currently works as an Application Developer for a Vancouver software company. His primary focus is web applications built using ColdFusion. He has been involved in multimedia and web-based development, producing work for clients such as Levi Straus, Motorola and Adobe Systems. As well, he has written for the Macromedia Users Journal and been a featured speaker at the Macromedia Users Convention.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.