Canon PowerShot G12, Stabilizing the Situation

Thanks to the image stabilizer (IS), you can squeeze two stops of exposure out of your camera when shooting without a tripod (Figures 8.2 and 8.3). Typically, the average person can handhold their camera down to about 1/60 of a second before blurriness results due to hand shake. As the lens is zoomed, the ability to handhold at slow shutter speeds (1/60 and slower) and still get sharp images is further reduced. Keep in mind that the IS compensates for camera shake; it won’t magically clarify moving objects in your scene.

A moderate or high ISO level enables you to shoot in low-light situations without incurring motion blur.
Figure 8.1 A moderate or high ISO level enables you to shoot in low-light situations without incurring motion blur.
This image was shot handheld without the IS turned on.
Figure 8.2 This image was shot handheld without the IS turned on.
Here’s the same subject shot with the same settings, but this time with the IS enabled.
Figure 8.3 Here’s the same subject shot with the same settings, but this time with the IS enabled.

Ac tivating the IS Mode

  1. Press the Menu button.
  2. In the Shooting menu, scroll down to the IS Mode option.
  3. Press the Right or Left button to select Continuous, which applies image stabilization whenever you’re shooting. You can also choose Shoot Only, which activates the IS only when you press the shutter button halfway, or Panning, which corrects for vertical movement when you’re panning the camera horizontally (such as when you’re following a fast-moving object).
  4. Press the Menu button again to exit the menu.

Self-timer

Whether you are shooting with a tripod or even resting your camera on a wall, you can increase the sharpness of your pictures by taking your hands out of the equation. Whenever you use your finger to press the shutter release button, you’re increasing the chance that there will be a little bit of shake in your image. To eliminate this possibility, set your camera’s self-timer. Press the Self-Timer button and use the Control dial to choose an option. You can choose a delay between 1 second or 30 seconds using the Front dial. Pressing the left or right button lets you specify how many shots to fire. Yet another option is available: Face Self-Timer, which activates the shutter when the camera detects a face in the scene.

Transforming Frame Animations

Drawing Frames with Color Mods

We will first test the frame animation system with color modifications, then get into transform mods, and then create a new frame animation mod system as well.

Pivot Optimization

The sprite’s pivot point used for rotation, called origin, is a potentially serious source of difficulty and head scratching. When recurring events or calculations can be automated, they should be. Anytime the sprite frame size is changed, the origin should be modified along with it. This calls for either a method or a property. Presently, both size and origin are defined as public Vector2 variables in Sprite:

[code]
public Vector2 size;
public Vector2 origin;
[/code]

We want to make a change so that it doesn’t wreck any previous code that relies on the Sprite class, although those prior examples could just use an earlier version of the class if necessary. Perhaps the easiest way to do this is with a property, with size made private. This will require several changes in the Sprite class, as size is used quite a bit.

Sprite Class Modifications to Support, Well, Modifications

We will make changes to the Sprite class from the preceding chapter to support color and transform animation of a sprite with frame animation support.

  1. First, we’ll make a variable change in Sprite:
    [code]
    //public Vector2 size;
    private Vector2 p_size;
    [/code]
  2. Next, we’ll take care of initialization in the constructor:
    [code]
    //size.X = size.Y = 0;
    size = Vector2.Zero;
    [/code]
  3. Now we’ll work on the new property, with the changes made to the pivot/origin being done automatically:
    [code]
    public Vector2 size
    {
    get { return p_size; }
    set
    {
    p_size = value;
    origin = new Vector2(p_size.X / 2, p_size.Y / 2);
    }
    }
    [/code]
  4. One of the additional locations in the class where size is used is in Load(), which must be modified as follows. Note that with the property now being used, the new line of code causes origin to be changed as well! Now we’re already starting to see the benefit of this property right inside the Sprite class, and it will be equally helpful outside. Now, we can still manually change the origin whenever needed (for instance, recall the clock example from Hour 13, which needed to set the origin for the clock hands manually for it to rotate correctly). Just remember that changing size automatically changes the pivot point (origin).
    [code]
    public bool Load(string assetName)
    {
    try
    {
    image = p_content.Load<Texture2D>(assetName);
    origin = new Vector2(image.Width / 2, image.Height / 2);
    }
    catch (Exception) { return false; }
    //size.X = image.Width;
    //size.Y = image.Height;
    size = new Vector2(image.Width, image.Height);
    return true;
    }
    [/code]

Revisiting CycleColorBounce

The CycleColor class was introduced back during Hour 12 as a way to fade any color in a desired direction, going to fully red, or full whiteout, or fade to black, or any combination in between. A subclass was then developed, called CycleColorBounce, that could do the same color transforms, but would not automatically stop animating when the limits were reached. Instead, it would continue to cycle. We also considered a class called SolidColor that did no real color changes, but that would be helpful in some cases when just a solid color is needed without changing the base Sprite.color property manually. In other words, without creating an additional “old color” style property in the class, SolidColor allowed a sprite to change color temporarily and then return.

Despite the relative simplicity of color modification in principle, it turns out that— ironically—our color mod classes are the most complex. Here again in Listing 15.1 are the Animation subclasses for reference, since we are using them in the example.

LISTING 15.1 Review of CycleColor Class for Reference (Not New Code)

[code]
public class CycleColor : Animation
{
public int red, green, blue, alpha;
public CycleColor(int red, int green, int blue, int alpha)
: base()
{
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = alpha;
animating = true;
}
public override Color ModifyColor(Color original)
{
Color modified = original;
if (animating)
{
int R = original.R + red;
int G = original.G + green;
int B = original.B + blue;
int A = original.A + alpha;
if (R < 0 || R > 255 || G < 0 || G > 255 ||
B < 0 || B > 255 || A < 0 || A > 255)
{
animating = false;
}
modified = new Color(R, G, B, A);
}
return modified;
}
}
public class CycleColorBounce : CycleColor
{
public int rmin, rmax, gmin, gmax, bmin, bmax, amin, amax;
public CycleColorBounce(int red, int green, int blue, int alpha)
: base(red,green,blue,alpha)
{
rmin = gmin = bmin = amin = 0;
rmax = gmax = bmax = amax = 255;
}
public override Color ModifyColor(Color original)
{
Color modified = original;
if (animating)
{
int R = original.R + red;
int G = original.G + green;
int B = original.B + blue;
int A = original.A + alpha;
if (R < rmin)
{
R = rmin;
red *= -1;
}
else if (R > rmax)
{
R = rmax;
red *= -1;
}
if (G < gmin)
{
G = gmin;
green *= -1;
}
else if (G > gmax)
{
G = gmax;
green *= -1;
}
if (B < bmin)
{
B = bmin;
blue *= -1;
}
else if (B > bmax)
{
B = bmax;
blue *= -1;
}
if (A < amin)
{
A = amin;
alpha *= -1;
}
else if (A > amax)
{
A = amax;
alpha *= -1;
}
modified = new Color(R, G, B, A);
}
return modified;
}
}
[/code]

Listing 15.2 shows some source code showing how we will test color modification in the upcoming example; we will delay going into the example until the next section, which features a combined example.

LISTING 15.2 Sample Source Code Demonstrating the Latest Sprite Changes

[code]
//create skeleton sprite
skelly = new Sprite(Content, spriteBatch);
skelly.Load(“skeleton_attack”);
skelly.position = new Vector2(425, 240);
skelly.size = new Vector2(96,96);
skelly.scale = 2.0f;
skelly.columns = 10;
skelly.totalFrames = 80;
skelly.animations.Add(new CycleColorBounce(1, 2, 2, 0));
objects.Add(skelly);
[/code]

We haven’t tried to do anything this complex yet. The question is, will it work—will color and frame animations work correctly “out of the box” or will changes be needed?

The particular sprite sheet used for the color mod example is shown in Figure 15.1. As you can see, it is an animated skeleton with a sword (courtesy of Reiner Prokein via his website, http://www.reinerstilesets.de). We will see this awesome skeleton sprite in the upcoming example.

Animated skeleton warrior used as an example with color modification.
FIGURE 15.1 Animated skeleton warrior used as an example with color modification.

Drawing Frames with Transform Mods

Now we will test the frame animation system with transform modifications. Color modification is rather trivial compared to transform modifications, which involve gradually affecting movement, rotation, or scaling over time, very much in parallel with how frame animation changes the image over time. When we’re dealing with transforms, though, the image size and position are crucial for these mods to work correctly. If an image is loaded in from a bitmap file that has dimensions of 1024×1024, and then transforms are applied to the sprite based on this image size, most likely the sprite will not even show up on the WP7 screen, because frame animation is also being done, and the image is being thrown out of the range of the screen, in all likelihood. So, the initial setup of the sprite’s properties is essential to make transformed frame animation work correctly.

Figure 15.2 shows the sprite sheet for the animated swordsman also featured in the example (also courtesy of Reiner Prokein, http://www.reinerstilesets.de).

Animated swordsman used to demo two combined animations.
FIGURE 15.2 Animated swordsman used to demo two combined animations.

Custom Frame Animation

We have been using the simple version of the Sprite.Animate() method so far, passing just the time value to cause the animation to run at a consistent frame rate. But there is a second version of Sprite.Animate() that is called from this first one, giving us the ability to fine-tune or manually intervene in the way the animation runs. Here are the two methods again for reference (covered in the preceding hour):

[code]
public void Animate(double elapsedTime)
{
Animate(0, totalFrames-1, elapsedTime, 30);
}
public void Animate(int startFrame, int endFrame,
double elapsedTime, double speed)
{
if (totalFrames <= 1) return;
startTime += elapsedTime;
if (startTime > speed)
{
startTime = 0;
if (++frame > endFrame) frame = startFrame;
}
}
[/code]

The second method allows us to pass the start frame and end frame for a specific range of frames to be animated. By default, the first Animate() method just draws all frames (0 to totalFrames-1). This is a simple looped animation: When the last frame is reached, the animation system loops back around to the first frame again. But there are more advanced forms of frame animation that can be performed as well, such as a frame bounce rather than a loop: When the last frame is reached, the animation direction reverses downward toward the first frame again, and then reverses again in the positive direction. This can be actually quite useful if the artwork has only half of the total frames needed to create a complete loop, and instead the bounce technique must be used.

Now we come to the dilemma: The game loop does not have room for custom, manual method calls! Here is the code in Update() that updates the sprite list:

[code]
//update all objects
foreach (Sprite spr in objects)
{
spr.Rotate();
spr.Move();
}
And here is the code in Draw() that animates and draws all the sprites:
foreach (Sprite spr in objects)
{
spr.Animate(); //mods
spr.Animate(gameTime.ElapsedGameTime.Milliseconds); //frames
spr.Draw();
}
[/code]

It is true that we can jump into these foreach loops and make manual changes to a specific sprite in the list. Or we can just leave a sprite out of the list and handle it separately. That works and there’s nothing wrong with that approach. But it kind of defeats the benefits of having an automated game loop, in which sprites are updated and drawn automatically based on properties. This describes a data-driven game loop, and it is simply unmatched in versatility and power when it comes to building a complex game. In other words, we want this to work correctly; we really don’t want to bypass it. If a new feature is needed, it’s better to work that into the loop than to make exceptions.

What we need is a new modification method in the Animation class. Do you see where we’re going with this?

Updating the Animation Class

The Sprite class will retain the two new Animate() methods from the preceding hour, and that form of frame animation can be used when it will suffice. But we will now add a more versatile frame animation system that uses the Animation class, fully merging the functionality of color and transform animation with frame animation. This calls for some changes to the Animation class, but no changes to the Sprite class are needed because it already supports multiple animations.

Add the following new method called ModifyFrame() to the Animation class as shown:

[code]
public class Animation
{
. . . some portions skipped
public virtual int ModifyFrame(int current)
{
return current;
}
}
[/code]

New Modifications to the Sprite Class

To make the new frame animation work with Sprite, some things must be modified.

  1. A new variable, frameDir, must be added to the class:
    [code]
    public int columns, frame, totalFrames, frameDir;
    [/code]
  2. It is initialized in the constructor:
    [code]
    columns = 1;
    frame = 0;
    totalFrames = 1;
    frameDir = 1;
    [/code]
  3. The Animate() method can be updated like so (note the line in bold). Remember, this call to ModifyFrame() is the default or base method call, and the current frame is passed as the sole parameter. Any class that inherits from Animate and overrides ModifyFrame() will have the animation range set to specific values.
    [code]
    public void Animate()
    {
    if (animations.Count == 0) return;
    foreach (Animation anim in animations)
    {
    if (anim.animating)
    {
    color = anim.ModifyColor(color);
    position = anim.ModifyPosition(position);
    rotation = anim.ModifyRotation(rotation);
    scaleV = anim.ModifyScale(scaleV);
    frame = anim.ModifyFrame(frame);
    }
    else
    {
    animations.Remove(anim);
    return;
    }
    }
    }
    [/code]

New Animation Subclass Examples

Strangely enough, that’s it! That’s all we had to do to add frame animation support to the Animation class. Now we can create custom subclasses that inherit from Animation to create any kind of special frame animation we need. Listing 15.3 gives two sample animations involving frame modifications that you can study as a reference.

LISTING 15.3 New Animations

[code]

public class FrameLoop : Animation
{
public FrameLoop(int start, int end, int current, int direction)
: base()
{
animating = true;
}
public override int ModifyFrame(int start, int end, int current,
int direction)
{
current += direction;
if (current > end)
current = start;
else if (current < start)
current = end;
return current;
}
}
public class FrameBounce : Animation
{
public FrameBounce(int start, int end, int current, int direction)
: base()
{
animating = true;
}
public override int ModifyFrame(int start, int end, int current,
int direction)
{
current += direction;
if (current > end)
{
direction *= -1;
current = end;
}
else if (current < start)
{
direction *= -1;
current = start;
}
return current;
}
}
[/code]

If you want frame animation to continue to function as it did before, one option is to automatically add a FrameLoop object to the animation list when a sprite is created (from the Sprite constructor). But it’s probably better to just work with the more advanced system now in place.

The FrameLoop class duplicates the existing functionality of parameterized Sprite.Animate(), which updates frame animation. Those two methods could now be removed since FrameLoop replicates that functionality, but I will just leave them alone since they might be useful. Next, the FrameBounce class handles the situation in which an animation will go from start frame to end frame, and then reverse direction down toward start frame again, rather than looping.

Figure 15.3 shows an animated dragon that will also be used in the example.

 Animated dragon used to demo OrbitalMovement and FrameBounce.
FIGURE 15.3 Animated dragon used to demo OrbitalMovement and FrameBounce.

As of the preceding hour, we had an updated Draw() that handles sprite frame animation like so:

[code]
foreach (Sprite spr in objects)
{
spr.Animate(); //mods
spr.Animate(gameTime.ElapsedGameTime.Milliseconds); //frames
spr.Draw();
}
[/code]

The second line in the method, the second call to Animate(), can now be removed, after FrameLoop or FrameBounce has been added as an animation. But if you do remove that line, you must be sure to add FrameLoop or FrameBounce (or some variation thereof) to the animation list after a sprite has been created. For instance:

[code]
//create dragon sprite
dragon = new Sprite(Content, spriteBatch);
dragon.Load(“dragon”);
dragon.animations.Add(new FrameLoop(48, 56, 48, 1));
[/code]

Animation Mods Demo

We need a complete example to demonstrate how the new improvements to the animation system work. Figure 15.4 shows the final output of the demo for this hour. On the left is the swordsman character animated with FrameLoop and ThrobBounce.

The final Animation Mods Demo program animates three sprites with several animations at once.
FIGURE 15.4 The final Animation Mods Demo program animates three sprites with several animations at once.

The New ThrobBounce Class

The ThrobBounce class inherits from Throb and perpetuates the “throb” animation by overriding the animating property (making it continue when the animation normally ends). For reference, the original Throb class is shown here in Listing 15.4 as well since it is inherited.

LISTING 15.4 New ThrobBounce Class (Throb Repeated for Reference)

[code]
public class Throb : Animation
{
public float startScale, endScale, speed;
private bool p_started;
public Throb(float startScale, float endScale, float speed)
: base()
{
p_started = false;
animating = true;
this.startScale = startScale;
this.endScale = endScale;
this.speed = speed;
}
public override Vector2 ModifyScale(Vector2 original)
{
if (!animating) return original;
Vector2 modified = original;
if (!p_started)
{
modified.X = startScale;
modified.Y = startScale;
p_started = true;
}
modified.X += speed;
modified.Y += speed;
if (modified.X >= endScale)
speed *= -1;
else if (modified.X <= startScale)
animating = false;
return modified;
}
}
public class ThrobBounce : Throb
{
public ThrobBounce(float startScale, float endScale, float speed)
: base(startScale, endScale, speed)
{
}
public override Vector2 ModifyScale(Vector2 original)
{
//keep it going
Vector2 scale = base.ModifyScale(original);
if (!animating)
{
animating = true;
speed *= -1;
}
return scale;
}
}
[/code]

The Animation Mods Demo Source Code

Here in Listing 15.5 is the complete source code for the Animation Mods Demo that wraps up all the techniques of this hour in one short example.

LISTING 15.5 Source Code for the Animation Mods Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
Random rand;
SpriteFont font;
List<Sprite> objects;
Sprite swordsman, skelly, dragon;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
//create object list
objects = new List<Sprite>();
//create swordsman sprite
swordsman = new Sprite(Content, spriteBatch);
swordsman.Load(“swordsman_walking”);
swordsman.position = new Vector2(150, 240);
swordsman.size = new Vector2(96, 96);
swordsman.columns = 8;
swordsman.totalFrames = 64;
swordsman.animations.Add(new FrameLoop(0, 63, 1));
swordsman.animations.Add(new ThrobBounce(0.5f, 4.0f, 0.1f));
objects.Add(swordsman);
//create skeleton sprite
skelly = new Sprite(Content, spriteBatch);
skelly.Load(“skeleton_attack”);
skelly.position = new Vector2(425, 240);
skelly.size = new Vector2(96,96);
skelly.scale = 2.0f;
skelly.columns = 10;
skelly.totalFrames = 80;
skelly.animations.Add(new FrameLoop(0, 79, 1));
skelly.animations.Add(new CycleColorBounce(1, 2, 2, 0));
objects.Add(skelly);
//create dragon sprite
dragon = new Sprite(Content, spriteBatch);
dragon.Load(“dragon”);
dragon.position = new Vector2(700, 240);
dragon.size = new Vector2(128, 128);
dragon.scale = 1.5f;
dragon.columns = 8;
dragon.totalFrames = 64;
dragon.animations.Add(new FrameBounce(48, 55, 1));
dragon.animations.Add(new OrbitalMovement(
dragon.position, 50, 0, 0.1f));
objects.Add(dragon);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//update all objects
foreach (Sprite spr in objects)
{
spr.Rotate();
spr.Move();
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in objects)
{
spr.Animate();
spr.Draw();
}
spriteBatch.DrawString(font, ““, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

The sprite animation system is now completely functional, with lots of room for growth and with support for custom Animation subclasses. In a professional game, these subclasses would be programmed by designers with Lua script code, allowing designers to make their own custom animations, and the job of the programmers essentially being done at this point with regard to the Animation and Sprite classes. But we aren’t getting into scripting, so feel free to create your own awesome new animation classes to see what interesting effects you can come up with! In the following hour, we will study one last subject related to sprites: z-index ordering. This will allow our sprites to draw over each other with some having higher priority than others.

 

 

Sprite Frame Animation

Drawing Animation Frames

Let’s dig into the nitty-gritty of sprite animation. Most beginners create an animation sequence by loading each frame from a separate bitmap file and storing the frames in an array or a list. This approach has the distinct disadvantage of requiring many files for even a basic animation for a walking character or some other game object, which often involves 50 to 100 or more frames. Although it can be done, that is a slow and error-prone way to handle animation. Instead, it is preferable to use an animation sheet.

Preparing the Animation

A sprite animation sheet is a single bitmap containing many frames arranged in rows and columns, as shown in Figure 14.1. In this sprite sheet (of an animated asteroid), there are eight columns across and 64 frames overall. The animation here was rendered from a 3D model, which is why it looks so great when animated on the screen! One question that might come up is, where do you get animations? Most game art is created by a professional artist specifically for one game, and then it is never used again (usually because the game studio owns the assets). There are, however, several good sources of free artwork online, such as Reiner’s Tilesets (http:// www.reinerstilesets.de).

Sprite sheet of animation frames for an asteroid.
FIGURE 14.1 Sprite sheet of animation frames for an asteroid.

SpriteBatch doesn’t care whether your sprite’s source image uses a color key or an alpha channel for transparency; it just renders the image. If you have an image with an alpha channel, like a TGA or PNG, then it will be rendered with any existing alpha, with translucent blending of the background. This is the technique used to render a sprite with transparency in XNA. Looking at sprite functionality at a lower level, you can tell the sprite renderer (SpriteBatch) what color you want to use when drawing the image, which was the focus of the preceding two hours on performing color and transform animations.

The bitmap file should have an alpha channel if you want to use transparency (which is almost always the case). Most artists prefer to define their own translucent pixels for best results rather than leaving it to chance in the hands of a programmer. The main reason to use alpha rather than color-key transparency is better quality. An alpha channel can define pixels with shades of translucency. In contrast, a color key is an all-or-nothing, on/off setting with a solid edge, because such an image will have discrete pixels. You can do alpha blending at runtime to produce special effects (such as a particle emitter), but for maximum quality, it’s best to prepare artwork in advance. See Figure 14.2.

A sprite animation sheet of an explosion showing the alpha channel.
FIGURE 14.2 A sprite animation sheet of an explosion showing the alpha channel.

Rather than using a black border around a color-keyed sprite (the old-school way of highlighting a sprite), an artist will usually blend a border around a sprite’s edges using an alpha level for partial translucency. To do that, you must use a file format that supports 32-bit RGBA images. TGA and PNG files both support an alpha channel and XNA supports them. The PNG format is a good choice that you may consider using most of the time because it has wide support among all graphic editing tools.

Calculating Frame Position

Assuming that we have an animation sheet like the asteroid animation shown in Figure 14.2, we can begin exploring ways to draw a single frame. This will require some changes to the Sprite class, which currently just uses the dimensions of the loaded image. That will have to change to reflect the dimensions of a single frame, not the whole image. To calculate the position of a frame in the sheet, we have to know the width and height of a single frame. Then, beginning at the upper left, we can calculate how to move right and down the correct number of frames. Figure 14.3 shows a typical sheet with the columns and rows labeled for easy reference.

This animation sheet has eight columns and eight rows, for 64 total frames.
FIGURE 14.3 This animation sheet has eight columns and eight rows, for 64 total frames.

The more important of the two is the row calculation, so we’ll do that one first. To make this calculation, you need to know how many frames there are across from left to right. These are the columns. (See Figure 14.4.) Here is the formula for calculating the row or Y position of a frame number on the sprite sheet:

[code]
Y = ( Frame_Number / Columns ) * Frame_Height
[/code]

To calculate the column or X position of a frame number on the sprite sheet, a similar- looking calculation is done, but the result is quite different:

[code]
X = ( Frame_Number % Columns ) * Frame_Width
[/code]

Calculating the position of a frame in the animation sheet.
FIGURE 14.4 Calculating the position of a frame in the animation sheet.

Note that the math operator is not division. The percent symbol (%) is the modulus operator in C#. Modulus is similar to division, but instead of returning the quotient (or answer), it returns the remainder! Why do we care about the remainder? That represents the X position of the frame! Here’s the answer: because X is the extra or leftover amount after the division. Recall that the formula for calculating Y gave us a distinct integer quotient. We want to use the same variables, but modulus rather than division gives us the partial column in the row, which represents the X value.

Drawing One Frame

Equipping ourselves with these formulas, we can write the code to draw a frame from a sprite sheet onto the screen. First, we’ll create a Rectangle to represent the source frame:

[code]
Rectangle source = new Rectangle();
source.X = (frame % columns) * width;
source.Y = (frame / columns) * height;
source.Width = width;
source.Height = height;
[/code]

Next, we’ll use the Rectangle when calling SpriteBatch.Draw(), using one of the overloads of the method that allows use of a source rectangle. We can retain the existing rotation, origin, and scale parameters while still drawing just a single frame.

[code]
spriteBatch.Draw( image, //source Texture2D
position, //destination position
source, //source Rectangle
Color.White, //target color
rotation, //rotation value
origin, //pivot for rotation
scale, //scale factor
SpriteEffects.None, //flip or mirror effects
0.0f ); //z-index order
[/code]

Creating the Frame Animation Demo

The only way to really get experience with animation is to practice by writing code. One fairly common mistake that results in an animated sprite not showing up is to forget the frame size property. This must be set after a bitmap file is loaded or the image property is set to an outside Texture2D object. The Sprite.size property is a Vector2 that must be set to the width and height of a single frame. Forgetting to do this after loading the bitmap will result in the animation not showing up correctly.

Sprite Class Changes

Some rather dramatic changes must be made to the Sprite class to support frame animation. Now, the reason for most of the changes involves the sprite sheet image. Previously, the whole image was used, for drawing, for calculating scale, and so on. Now, that code has to be changed to account for the size of just one frame, not the whole image.

Modifying the Sprite Class

  1. First up, we have some new variables in the Sprite class. These can be added to the top of the class with the other variables:
    [code]
    private double startTime;
    public Vector2 size;
    public int columns, frame, totalFrames;
    [/code]
  2. In the Sprite constructor, the new variables are initialized after all the others. The columns and totalFrames variables are crucial to drawing simple sprites when no animation is being used. In other words, they’re needed to preserve compatibility with code that used the Sprite class before this point. By setting columns to 1, we tell the Draw() method to treat the image as if there is just one column. Likewise, setting totalFrames to 1 ensures that just that one frame is drawn, even if no animation is used. A flag will be used in the Draw() method just to make sure null errors don’t occur, but these initialized values should take care of that as well.
    [code]
    size.X = size.Y = 0;
    columns = 1;
    frame = 0;
    totalFrames = 1;
    startTime = 0;
    [/code]
  3. Next up are two helper properties that make using Sprite a bit easier, by exposing the X and Y properties of position. This is a convenience rather than a required change, but it is very helpful in the long term.
    [code]
    public float X
    {
    get
    {
    return position.X;
    }
    set
    {
    position.X = value;
    }
    }
    public float Y
    {
    get
    {
    return position.Y;
    }
    set
    {
    position.Y = value;
    }
    }
    [/code]
  4. Next, we need to review the Load() method again for reference, just to note what is being initialized at this point. Pay special attention to origin and size, because they are involved in a single frame being drawn correctly. Notice that origin is initialized with the full size of the image. When using a sprite sheet, origin must be reset after the image is loaded for drawing to work correctly!
    [code]
    public bool Load(string assetName)
    {
    try
    {
    image = p_content.Load<Texture2D>(assetName);
    origin = new Vector2(image.Width / 2, image.Height / 2);
    }
    catch (Exception) { return false; }
    size.X = image.Width;
    size.Y = image.Height;
    return true;
    }
    [/code]
  5. Next, make the required changes to the Sprite.Draw() method. Quite a dramatic change has come over Draw(), transforming it into a fully featured animation rendering routine with support for single images or sprite sheet animations. This is a frame animation workhorse—this is where all the “good stuff” is happening.
    [code]
    public void Draw()
    {
    if (!visible) return;
    if (totalFrames > 1)
    {
    Rectangle source = new Rectangle();
    source.X = (frame % columns) * (int)size.X;
    source.Y = (frame / columns) * (int)size.Y;
    source.Width = (int)size.X;
    source.Height = (int)size.Y;
    p_spriteBatch.Draw(image, position, source, color,
    rotation, origin, scale, SpriteEffects.None, 0.0f);
    }
    else
    {
    p_spriteBatch.Draw(image, position, null, color, rotation,
    origin, scaleV, SpriteEffects.None, 0.0f);
    }
    }
    [/code]
  6. Next, make a minor improvement to the Rotate() method to speed it up. If no rotation is happening, the calculations are skipped.
    [code]
    public void Rotate()
    {
    if (velocityAngular != 0.0f)
    {
    rotation += velocityAngular;
    if (rotation > Math.PI * 2)
    rotation -= (float)Math.PI * 2;
    else if (rotation < 0.0f)
    rotation = (float)Math.PI * 2 – rotation;
    }
    }
    [/code]
  7. Next, we must make minor modifications to the Boundary() method to account for the size of a single frame, rather than using the whole image. The old lines have been commented out; note the new calculations for halfw and halfh.
    [code]
    public Rectangle Boundary()
    {
    //int halfw = (int)((float)(image.Width / 2) * scaleV.X);
    //int halfh = (int)((float)(image.Height / 2) * scaleV.Y);
    int halfw = (int)((float)(size.X / 2) * scaleV.X);
    int halfh = (int)((float)(size.Y / 2) * scaleV.Y);
    return new Rectangle(
    (int)position.X – halfw,
    (int)position.Y – halfh,
    halfw * 2,
    halfh * 2);
    }
    [/code]
  8. Next, we’ll add two new overloads of Animate() to support frame animation. It might be less confusing to call these FrameAnimate() if you want, because they share the same name as the previous version of Animate() that did color and transform animations. The difference between those and frame animation is that the latter requires parameters, either the elapsed time or the actual frame range, time, and animation speed. First, let’s review the existing method (no changes required):
    [code]
    public void Animate()
    {
    if (animations.Count == 0) return;
    foreach (Animation anim in animations)
    {
    if (anim.animating)
    {
    color = anim.ModifyColor(color);
    position = anim.ModifyPosition(position);
    rotation = anim.ModifyRotation(rotation);
    scaleV = anim.ModifyScale(scaleV);
    }
    else
    {
    animations.Remove(anim);
    return;
    }
    }
    }
    [/code]

Okay, now here are the new methods for frame animation that you can add to the source code. The elapsedTime parameter helps the animation code to run at the correct speed. Note that the simple version calls the more complex version with default values for convenience. If you want to draw just a subset of an animation set, this second version of Animate() will do that.

[code]
public void Animate(double elapsedTime)
{
Animate(0, totalFrames-1, elapsedTime, 30);
}
public void Animate(int startframe, int endframe, double elapsedTime,
double speed)
{
if (totalFrames <= 1) return;
startTime += elapsedTime;
if (startTime > speed)
{
startTime = 0;
if (++frame > endframe) frame = startframe;
}
}
[/code]

That concludes the changes to the Sprite class, so now we can go into the example.

Sample Program

The example for this hour draws a bunch of animated asteroid sprites that move across the screen (in the usual landscape mode). But this is no simple demo—there is rudimentary gameplay. A small spaceship has been added. Tap above the ship to move it up, or below the ship to move it down, and avoid the asteroids! (See Figure 14.5.)

LISTING 14.1 Source Code for the Frame Animation Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
Random rand;
SpriteFont font;
List<Sprite> objects;
Sprite fighter;
int score = 0;
int hits = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
//create object list
objects = new List<Sprite>();
//create fighter sprite
fighter = new Sprite(Content, spriteBatch);
fighter.Load(“fighter”);
fighter.position = new Vector2(40, 240);
fighter.rotation = MathHelper.ToRadians(90);
//create asteroid sprites
for (int n = 0; n < 20; n++)
{
Sprite ast = new Sprite(Content, spriteBatch);
ast.Load(“asteroid”);
ast.size = new Vector2(60, 60);
ast.origin = new Vector2(30, 30);
float x = 800 + (float)rand.Next(800);
float y = (float)rand.Next(480);
ast.position = new Vector2(x, y);
ast.columns = 8;
ast.totalFrames = 64;
ast.frame = rand.Next(64);
x = (float)(rand.NextDouble() * rand.Next(1, 10));
y = 0;
ast.velocityLinear = new Vector2(-x, y);
objects.Add(ast);
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//get state of touch input
TouchCollection touchInput = TouchPanel.GetState();
if (touchInput.Count > 0)
{
TouchLocation touch = touchInput[0];
if (touch.State == TouchLocationState.Pressed)
{
if (touch.Position.Y < fighter.Y )
{
fighter.velocityLinear.Y -= 1.0f;
}
else if (touch.Position.Y >= fighter.Y)
{
fighter.velocityLinear.Y += 1.0f;
}
}
oldTouch = touch;
}
//gradually reduce velocity
if (fighter.velocityLinear.Y < 0)
fighter.velocityLinear.Y += 0.05f;
else if (fighter.velocityLinear.Y > 0)
fighter.velocityLinear.Y -= 0.05f;
//keep fighter in screen bounds
if (fighter.Y < -32)
{
fighter.Y = -32;
fighter.velocityLinear.Y = 0;
}
else if (fighter.Y > 480-32)
{
fighter.Y = 480-32;
fighter.velocityLinear.Y = 0;
}
fighter.Move();
//update all objects
foreach (Sprite spr in objects)
{
spr.Rotate();
spr.Move();
//wrap asteroids around screen
if (spr.X < -60)
{
spr.X = 800;
score++;
}
//look for collision with fighter
if (fighter.Boundary().Intersects(spr.Boundary()))
{
hits++;
spr.X = 800;
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in objects)
{
spr.Animate(gameTime.ElapsedGameTime.Milliseconds);
spr.Draw();
}
fighter.Draw();
spriteBatch.DrawString(font, “Score:” + score.ToString() +
“, Hits:” + hits.ToString(), Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

The Frame Animation Demo is a mini-game of dodging the asteroids.
FIGURE 14.5 The Frame Animation Demo is a mini-game of dodging the asteroids.

 

Canon PowerShot G12, Raising the ISO: The Simple Solution

Let’s begin with the obvious ways to keep shooting when the lights get low.

One option is to use the flash, but its limited range (15–20 feet) might not work for the situation. Also, the light from the built-in flash can too often be harsher than what you’re looking for. You could be in a setting where flash is prohibited, or at least frowned upon, like at a wedding or in a museum.

What about using a tripod in combination with a long shutter speed? That is also an option, and we’ll cover it a little further into the chapter. The problem, though, is that it performs best when subjects aren’t moving. And tripods aren’t exactly discreet: Just try to set up a tripod in a museum and see how quickly you grab the attention of the security guards.

That leaves us with raising the ISO (Figure 8.1). By now you know how to change the ISO: Turn the ISO dial on the top of the camera. In typical shooting situations, you should keep the ISO in the 100–400 range. This will keep your pictures nice and clean by keeping the digital noise to a minimum. But as the available light gets low, you might find yourself working in the higher ranges of the ISO scale, which could lead to more noise in your image.

The G12 performs decently at higher ISOs, so you can probably get away with shots made at ISO 800 or 1600. Turn the dial to ISO 3200 for the most light sensitivity, although the amount of noise may be unacceptable. Shooting with the Low Light mode can crank the ISO above 3200 (such as 12800!), but again, the camera will introduce a lot of noise.

 

Working with XML Data

Using the .NET XML Functionality

If you know how to work with XML in .NET languages such as C#, then you already know how to perform the same tasks in IronPython because you can import the System.Xml assembly to gain full access to this functionality. Because the XML capabilities of the .NET Framework are so well defined, using the System.Xml assembly may be all you need to perform tasks within your application. The main issue to consider is how you plan to use your application later. For example, if you plan to move your application to another platform, then using the .NET Framework solution won’t work. In addition, you need to consider data type translation in IronPython. The .NET data types that you use normally are translated into their IronPython counterparts, which could prove confusing for some developers. With these factors in mind, the following sections provide an overview of XML support in the .NET Framework from the IronPython perspective.

Considering the System.Xml Namespace

The System.Xml namespace provides access to the various classes used to interact with XML data. You use these classes to read, write, interpret, edit, build, and otherwise manage XML data. For example, you might use the XmlDeclaration class to begin building an XML data file from scratch when needed. All of these classes depend heavily on standards to ensure the file you create using IronPython is readable by other languages and applications. In fact, the System.Xml namespace supports these standards and specifications.

  • XML 1.0 (including Document Type Definition, DTD, support): http://www.w3.org/ TR/1998/REC-xml-19980210
  • XML Namespaces (both stream level and Document Object Model, DOM): http://www.w3.org/TR/REC-xml-names/
  • XSD Schemas: http://www.w3.org/2001/XMLSchema
  • XPath expressions: http://www.w3.org/TR/xpath
  • XSLT transformations: http://www.w3.org/TR/xslt
  • DOM Level 1 Core: http://www.w3.org/TR/REC-DOM-Level-1/
  • DOM Level 2 Core: http://www.w3.org/TR/DOM-Level-2/

Developing a Basic .NET XML Application

A .NET XML application will follow most of the same principles you use when working with a static language such as C# or Visual Basic.NET. In fact, you might not notice much difference at all except for the obvious structural requirements of a Python application. Consequently, you should find it easy to move your XML code over to IronPython because you really don’t have anything new to worry about. Listing 13-1 shows a simple XML application that creates an XML document, saves it to disk, reads it from disk, and then displays the content onscreen.

DOM-Only Support in the .NET Framework

It’s important to note that the .NET Framework supports DOM and not Simple API for XML (SAX). However, if you want SAX support, you can use the Python modules instead (see the “Working with xml.sax” section of this chapter). XML files include both data and context. In order to reconstruct the original dataset described by an XML file, you need a parser to read the text and then convert it to a usable object. DOM and SAX represent two different methods for interacting with XML documents without forcing the developer to create a parser. If you want more information about the DOM versus SAX approach to parsing XML parsers, check out the information at http://developerlife.com/tutorials/?p=28 and http://www.jamesh.id.au/ articles/libxml-sax/libxml-sax.html. Here’s a summary of the DOM features.

  • Object-based.
  • Object module is created automatically.
  • Element sequencing is preserved.
  • High memory usage.
  • Slow initial data retrieval.
  • Best for complex data structures.
  • In-memory document updates are supported.

SAX takes a completely different approach than DOM. Here’s a summary of the SAX features.

  • Event-based.
  • Object module is created by the application.
  • Element sequencing is ignored in favor of single events.
  • Low memory usage.
  • Fast initial data retrieval.
  • Best for simple data structures.
  • No document updates.

Listing 13-1: Reading and writing an XML document

[code]
# Import clr to add references.
import clr
# Add the required reference.
clr.AddReference(‘System.Xml’)
# Import the System.Xml classes.
from System.Xml import *
# This function creates the document and writes it to disk.
def CreateDocument():
# Create a document.
Doc = XmlDocument()
# Add the XML Declaration.
Declaration = Doc.CreateXmlDeclaration(‘1.0’, ‘utf-8’, ‘yes’)
Doc.AppendChild(Declaration)
# Create the root node.
Root = Doc.CreateNode(XmlNodeType.Element, ‘root’, None)
# Add child elements to the root.
MsgNode = Doc.CreateNode(XmlNodeType.Element, ‘Message’, None)
MsgNode.InnerXml = ‘Hello’
Root.AppendChild(MsgNode)
MsgNode = Doc.CreateNode(XmlNodeType.Element, ‘Message’, None)
MsgNode.InnerXml = ‘Goodbye’
Root.AppendChild(MsgNode)
# Add the root node to the document.
Doc.AppendChild(Root)
# Save the document to disk.
Doc.Save(‘Test.XML’)
def DisplayDocument():
# Create a document.
XMLDoc = XmlDocument()
# Load the XML data.
XMLDoc.Load(‘Test.XML’)
# Process the document.
for Nodes in XMLDoc:
if type(Nodes) == XmlElement:
for MsgNodes in Nodes:
print ‘Message:’, MsgNodes.InnerXml
# Interact with an XML document.
CreateDocument()
DisplayDocument()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing clr, which the application uses to add the required reference to System.Xml using the clr.AddReference() method. The code then imports the System.Xml classes.

The example relies on two functions to keep the code simple: CreateDocument(), which creates and saves the document to disk, and DisplayDocument(), which reads the document from disk and displays the content on screen. The example calls each of these functions in turn.

The CreateDocument() function begins by creating an XmlDocument object, Doc. As with any .NET application, Doc doesn’t contain anything when you create it. The first task is to add the XML declarations so that the result is a well-formed XML document using Doc.CreateXmlDeclaration(). Calling Doc.AppendChild() adds the declaration to the document.

Now it’s time to create some content. All XML documents have a root node, which is Root for this example. The code creates Root using Doc.CreateNode() with an XmlNodeType.Element type and ‘root‘ for a name. The example doesn’t work with XML namespaces, so the third argument is set to None.

The most efficient way to create an XML document from scratch is to add all the child nodes to Root before you add Root to the document. The code creates MsgNode using the same technique as for Root. It adds content to MsgNode using the MsgNode.InnerXml property and then adds the node to Root using Root.AppendChild(). The example provides two ‘Message‘ nodes.

At this point, the code adds Root to the document using Doc .AppendChild(). It then saves the document to disk using Doc.Save(). Figure 13-1 shows the typical output from this example when viewed in Notepad (you can use any text editor to view the output because the Doc .Save() method includes spaces and line feeds).

The XML document output looks much as you might expect.
Figure 13-1: The XML document output looks much as you might expect.

The DisplayDocument() function begins by creating a document, XMLDoc, using the XmlDocument class constructor. It then loads the previously created XML document using XMLDoc.Load(). At this point, XMLDoc contains everything the code created earlier and you can easily explore it using the IronPython console.

If you’ve worked with XML documents using C# or Visual Basic.NET, you know that these languages sometimes make it hard to get to the data you really want. IronPython makes things very easy. All you need is a for loop, as shown in the code. Simple if statements make it easy to locate nodes of a particular type, XmlElement in this case.

By the time the code reaches the second for loop, it’s working with the ‘Message‘ elements. The code simply prints the MsgNodes.InnerXml property value to the screen, as shown in Figure 13-2. By now you can see that IronPython makes it incredibly simple to work with XML documents using the .NET Framework approach.

The example outputs the message content in the XML document.
Figure 13-2: The example outputs the message content in the XML document.

Loading and Viewing the XMLUtil Module

The example in this section assumes that you’ve loaded the XMLUtil module from the IronPython Tutorial directory. The following steps show you how to load this module manually so you can see the content.

  1. Open the IronPython console.
  2. Type import sys and press Enter. This command imports the sys module so that you can add the required directory to it.
  3. Type sys.path.append(‘C:/Program Files/IronPython 2.6/Tutorial‘) and press Enter (make sure you change the path information to match the location of your IronPython installation). The XMLUtil.py module exists in the Tutorial directory. Using this module is fine for experimentation, but be sure you copy the XMLUtil.py module to another location for other uses.
  4. Type print sys.path and press Enter. You should see the new path added to the list.
  5. Type dir(XMLUtil) and press Enter. You see the list of methods available in XMLUtil (as shown in Figure 13-3), which includes the Walk() method used in the example.
The Walk() method makes viewing XML data easier.
Figure 13-3: The Walk() method makes viewing XML data easier.

Loading and Viewing the XMLUtil Module

As previously mentioned, the XMLUtil.py file isn’t anything so advanced that you couldn’t put it together yourself, but it’s an interesting module to work with and use. Listing 13-2 shows a short example of how you could use this module in an application.

Listin g 13-2: Walking an XML document using XMLUtil

[code]
# Add the path required to import xmlutil.
import sys
sys.path.append(‘C:/Program Files/IronPython 2.6/Tutorial’)
# Import xmlutil to access the Walk() function.
import xmlutil
# Import clr to add references.
import clr
# Add the required reference.
clr.AddReference(‘System.Xml’)
# Import the System.Xml classes.
from System.Xml import *
# Create a document.
XMLDoc = XmlDocument()
# Load the XML data.
XMLDoc.Load(‘Test.XML’)
# Walk the file contents.
print ‘Contents of Test.XML’
for Node in xmlutil.Walk(XMLDoc):
print ‘nName:’, Node.Name
print ‘Value:’, Node.Value
print ‘InnerXml’, Node.InnerXml
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The example begins by importing sys, appending the Tutorial folder path, and importing XMLUtil. The code then imports clr, adds a reference to System.Xml, and imports the System.Xml classes. There isn’t anything new about any of this code.

The example makes use of the Text.XML file created in the “Developing a Basic .NET XML Application” section of this chapter. It creates an XmlDocument object, XMLDoc, and loads Text.XML into it using the XMLDoc.Load() method. At this point, you have an XML document that you can walk (go from node-to-node and examine). The XMLUtil.Walk() method can walk any sort of XML document, so you should try it out with other files after you’ve worked with the example for a while.

The next step is to call on XMLUtil.Walk() to walk the XML document for you. The example shows output from the Name, Value, and InnerXml properties. However, you have access to all the properties provided for the various XML data types that the .NET Framework provides. Consequently, you can use XMLUtil.Walk() to display any information needed, or to manage that information. Just because the example displays properties doesn’t mean you have any limitation on how you interact with the output of XMLUtil.Walk(). Figure 13-4 shows the output of this example.

Screen shows the output of the Walk() method for Test.XML.
Figure 13-4: Screen shows the output of the Walk() method for Test.XML.

The XMLUtil.Walk() function is so important because it demonstrates a Python generator (described later in the section when you have the required background). Most languages don’t provide support for generators, so they require a little explanation. The issue at the center of this whole discussion is the variant list. You know that an application will need to process some number of items during run time, but you have no idea of how long this list is or whether the list will exist at all. A producer function is one that outputs values one at a time in response to a request. The producer keeps processing items until it runs out, so the length of the list is no longer a concern (even if the list contains no items at all). Most languages rely on a callback, an address to the requestor, to provide a place to send the producer output. The problem with using a callback is that the code must provide some means of retaining state information to remember previous values. In some cases, using callbacks leads to unnatural, convoluted coding techniques that are hard to write, harder to understand, and nearly impossible to update later.

Developers have a number of alternatives they can use. For example, the developer could simply use a very large list. However, lists require that the developer know what values should appear in the list during design time, and lists can consume large quantities of memory, making them a less than helpful solution in many cases. Another solution is to use an iterator to perform the task. Using an iterator makes it easier to get out of a loop when the processing is finished and eliminates the memory requirements. However, using an iterator shifts the burden of maintaining state information to the producer, complicating an already difficult programming task because the producer may not know anything about the caller. There are other solutions, as well, such as running the requestor and producer on separate threads so that each object can maintain state information without worrying about the potential corruption that occurs when running the code on a single thread. Unfortunately, multithreaded applications can run slowly and require a platform that fully supports multithreading, making your application less portable. In short, most languages don’t provide a good solution to the problem of working with data of variant length.

A generator creates a situation where the producer continuously outputs individual results as in a loop, maintaining its state locally. The requestor actually views the function as a type of iterator, even though the producer isn’t coded to provide an iterator. To accomplish this task, Python provides the yield statement shown in Figure 13-5. The yield statement returns an intermediate result from the producer to the requestor, while the producer continues to process a list of items.

The code in Figure 13-5 begins with the definition of a function named Walk(). This function accepts some kind of XML as input. The first yield statement sends the entire xml input back to the requestor (the example application shown in Listing 13-2). Consequently, you see #document as the Name and the entire XML document as the InnerXml.

The second call to Walk() moves past the first yield statement. Because the second item doesn’t meet the hasattr(xml, “Attributes“) requirement, the code moves onto the loop statement at the bottom of the code listing shown in Figure 13-5. The effect of this loop is to obtain the child elements of the entire document. So the second call to Walk() ends with yield c, which returns the XML declaration element. As a result, you see xml for the Name, version=“1.0“ encoding=“utf-8“ standalone=“yes“ for the Value, and nothing for the InnerXml. This second call ends processing of the XML declaration.

The XMLUtil.Walk() function is interesting because it provides a generator.
Figure 13-5: The XMLUtil.Walk() function is interesting because it provides a generator.

The third call to Walk() begins processing of the root node. It’s interesting to trace through the code in the debugger because you see the for loops in XMLUtil.Walk() used to trace through each element of the input xml as if it were using recursion or perhaps some type of iteration, but the fact is that the code merely combines the for loop with a yield statement to feed each partial result back to the requestor. Using the Python debugger is actually a bit more helpful in this case than using the Visual Studio debugger because the Visual Studio debugger won’t show you the value of xml, child, or c so that you can see the changing values. The example code for this book includes XMLUtilDemo2.py for the purpose of using the Python debugger. Follow these steps to load the debugger so you can trace through the example yourself.

  1. Open the IronPython console.
  2. Type import sys and press Enter. This command imports the sys module so that you can add the required directory to it.
  3. Type sys.path.append(‘C:/Program Files/IronPython 2.6/Tutorial‘) and press Enter (make sure you change the path information to match the location of your IronPython installation).
  4. Type import XMLUtil and press Enter to import the support file (important if you want to see how the generator works).
  5. Type import XMLUtilDemo2 and press Enter to import the source code file.
  6. Type import pdb and press Enter to import the debugger.
  7. Type pdb.run(‘XMLUtilDemo2.main()‘) to start the debugger. At this point, you can single step through the code to see how everything works.

Using the Python Modules

At one point, the Python modules were stable and straightforward to use, but later versions are less stable and, when it comes to IronPython, may be missing required elements completely. Consequently, you might see tutorials such as the one at http://www.boddie.org.uk/python/XML_intro.html and wonder why they don’t work. These tutorials are based on earlier versions of Python and don’t account for the missing CPython elements in IronPython. The following sections describe how to overcome these problems in your application when you use the Python approach to XML file management in IronPython.

Working with xml.dom.minidom

The xml.dom.minidom module is designed to help you work with XML using the DOM approach. However, this module is far from complete in IronPython, partly due to the CPython support required in standard Python. The actual document support is complete, so you won’t have a problem building, editing, and managing XML documents. It’s the write and read support that are lacking.

Fortunately, you can overcome write issues by using a different approach to outputting the document to disk (or other media). Standard Python development practice is to use the xml.dom.ext .PrettyPrint() method, which simply doesn’t exist in IronPython. You get around the problem by performing the task in two steps, rather than one, as shown in Listing 13-3.

The reading problem isn’t as easy to solve. Standard Python development practice is to use the xml .dom.minidom.parse() method. This method does exist in IronPython, but it outputs an error stating

[code]
ImportError: No module named pyexpat
[/code]

This module actually is missing. In order to fix this problem, you must download the pyexpat. py file from https://fepy.svn.sourceforge.net/svnroot/fepy/trunk/lib/. Place this file in your Program FilesIronPython 2.6Lib, not the Program FilesIronPython 2.6Lib xmldom folder as you might think. As shown in Listing 13-3, the standard Python techniques work just fine now.

Listin g 13-3: Managing XML documents using the Python approach

[code]
# Import the required XML support.
import xml.dom.minidom
def CreateDocument():
# Create an XML document.
Doc = xml.dom.minidom.Document()
# Create the root node.
Root = Doc.createElement(‘root’)
# Add the message nodes.
MsgNode = Doc.createElement(‘Message’)
Message = Doc.createTextNode(‘Hello’)
MsgNode.appendChild(Message)
Root.appendChild(MsgNode)
MsgNode = Doc.createElement(‘Message’)
Message = Doc.createTextNode(‘Goodbye’)
MsgNode.appendChild(Message)
Root.appendChild(MsgNode)
# Append the root node to the document.
Doc.appendChild(Root)
# Create the output document.
MyFile = open(‘Test2.XML’, ‘w’)
# Write the output.
MyFile.write(Doc.toprettyxml(encoding=’utf-8’))
# Close the document.
MyFile.close()
def DisplayDocument():
# Read the existing XML document.
XMLDoc = xml.dom.minidom.parse(‘Test2.XML’)
# Print the message node content.
for ThisChild in XMLDoc.getElementsByTagName(‘Message’):
print ‘Message:’, ThisChild.firstChild.toxml().strip(‘nt’)
CreateDocument()
DisplayDocument()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The first thing you should notice is that the code for this example is much shorter than its .NET counterpart, even though the result is essentially the same. Despite the problems with the Python libraries, you can write concise code for manipulating XML using Python.

The code begins by importing the only module it needs, xml.dom.minidom. It then calls CreateDocument() and DisplayDocument() in turn, just as the .NET example does. In fact, the output from this example is precisely the same. You see the same output shown in Figure 13-2 when you run this example.

The CreateDocument() function begins by creating an XML document, Doc, using xml.dom .minidom.Document(). The XML document automatically contains the XML declaration, so unlike the .NET version of the code, you don’t need to add it manually. So the first processing task is to create the root node using Doc.createElement(‘root‘).

As with the .NET example, this example creates two MsgNode elements that contain different messages. The technique used is different from the .NET example. Instead of setting an InnerXml property, the code creates an actual text node using Doc.createTextNode(). However, the result is the same, as shown in Figure 13-6. The last step is to add Root to Doc using Doc.appendChild().

A big difference between IronPython and Python is how you write the XML to a file. As previously mentioned, you can’t use the xml.dom.ext.PrettyPrint() method. In this case, the code creates a file, MyFile, using open(). The arguments define the filename and the mode, where ‘w‘ signifies write. In order to write the text to a file, you use a two-step process. First, the code creates formatting XML by calling Doc.toprettyxml(). The function accepts an optional encoding argument, but there isn’t any way to define the resulting XML document as stand-alone using the standalone=“yes“ attribute (see Figure 13-1). Second, the code writes the data to the file buffer using MyFile.write().

The Python output is similar, but not precisely the same as the .NET output.
Figure 13-6: The Python output is similar, but not precisely the same as the .NET output.

[code]
Calling MyFile.write() doesn’t write the data to disk. In order to clear the file buffer, you must call MyFile.close(). Theoretically, IronPython will call MyFile.close() when the application ends, but there isn’t any guarantee of this behavior, so you must specifically call MyFile.close() to ensure there isn’t any data loss.
[/code]

The DisplayDocument() function comes next. Reading an XML document from disk and placing it in a variable is almost too easy when using IronPython. All you need to do is make a single call to xml.dom.minidom.parse(). That’s it! The document is immediately ready for use.

The second step is to display the same output shown in Figure 13-2. Again, all you need in IronPython is a simple for loop, rather than the somewhat lengthy .NET code. In this case, you ask IronPython to retrieve the nodes you want using XMLDoc.getElementsByTagName(). The output is a list that you can process one element at a time. The print statement calls on a complex-looking call sequence.

[code]
ThisChild.firstChild.toxml().strip(‘nt’)
[/code]

However, if you take this call sequence apart, it really isn’t all that hard to understand. Every iteration of the loop places one of the MsgNode elements in ThisChild. The first (and only) child of MsgNode is the Message text node, so you can retrieve it using the firstChild property. The firstChild property contains a DOM Text node object, so you convert it to XML using the toxml() method. Unfortunately, the resulting string contains control characters, so you remove them using the strip(‘nt‘) method. The result is a simple value output.

Working with xml.sax

It’s important to remember that SAX is an event-driven method of working with XML. An application looks at a small number of bits out of an entire document. Consequently, SAX can be a good method for processing larger documents that you can’t read into memory at one time. A SAX application normally relies on three constructs:

  • One or more sources as input
  • A parser (normally, only one is used)
  • One or more handlers to respond to input events

There are many different Python SAX modules. Each of these modules provides different implementations of the three constructions. The default SAX implementation provides just four handlers. These handlers are implemented as classes that you use to interact with the events generated by the input file.

  • ContentHandler: Provides the main SAX interface for handling document events. Most applications use this interface as a minimum because it provides the basic support required for any document. The example shows how to use this handler, which is provided as part of the xml.sax module.
  • DTDHandler: Manages all of the Document Type Definition (DTD) events.
  • EntityResolver: Resolves external entities such as files referenced by processing instructions.
  • ErrorHandler: Reports any errors or warnings that the parser encounters when it processes the XML. Provided as part of the xml.sax module.

Now that you have a little better idea of what to expect, it’s time to look at an actual example. Listing 13-4 shows a simple SAX implementation that includes all of the constructs you normally need. Of course, you can easily add to this example to make it do considerably more than it does now.

Listin g 13-4: Parsing an XML document using SAX

[code]
# Import the required module.
import xml.sax
# Create a handler based on the default ContentHandler class.
class MessageHandler(xml.sax.ContentHandler):
# Contains the message text.
Message = ‘’
# Determines when the content is a message.
IsMessage = False
# Check for the kind of element before processing it.
def startElement(self, name, attrs):
if name == ‘Message’:
self.IsMessage = True
self.Message = ‘’
else:
self.IsMessage = False
# If this is the right kind of element, display the message for it.
def endElement(self, name):
if name == ‘Message’:
print ‘Message:’, self.Message.strip(‘nt’)
# Add each of the characters of the message to the Message variable.
def characters(self, ch):
if self.IsMessage:
self.Message += ch
# Create a parser.
Parser = xml.sax.make_parser()
# Create a handler for the parser and tell the parser to use it.
Handler = MessageHandler()
Parser.setContentHandler(Handler)
# Open a source and parse it using the parser with the custom handler.
Parser.parse(open(‘Test2.XML’))
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The code begins by importing the required xml.sax module. You don’t need anything fancy to create a basic SAX handler. Remember that SAX processes the file one character at a time and generates events based on the characters that the parser sees. Consequently, the code may seem a little odd for someone who is used to working with complete elements, but SAX gives you fine control over the processing cycle, including locating errors within the file.

The centerpiece of this example is the MessageHandler class. This class includes a variable to hold the message (Message), an indicator of whether an element is a message (IsMessage), and the three methods described in the following list.

  • startElement(): The parser calls this method at the beginning of an element.
  • endElement(): The parser calls this method at the end of an element.
  • characters(): Every character read from the source generates a call to characters().

For this example, the startElement() method checks the element name. If the element is a ‘Message‘ element, then the code sets IsMessage to True and clears Message of any existing content. This is a preparatory step.

When the characters() method sees that IsMessage is True, it appends every character it receives to Message. Remember that these are individual characters, so you can’t assume much about the content except that the flow is from the beginning of the file to the end of it. In other words, you won’t receive characters out of order.

The endElement() checks the element name again. When the element name is ‘Message‘, the code prints the content of Message. Because Message contains all of the characters from the source, you must use strip(‘nt‘) to remove any control characters. The output from this example is the same as shown in Figure 13-2.

Now that you understand the handler, it’s time to see how you put it to work. The main part of the code begins by creating a parser, Parser, using xml.sax.make_parser(). Remember that the parser simply generates events based on the input characters it sees. The handler performs the actual interpretation of those characters.

The next step is to create an instance of MessageHandler named Handler. The code uses Parser .setContentHandler() to assign the handler to Parser. Otherwise, Parser won’t know which handler to use to process the XML characters.

In order to process the XML file, the code still requires a source — the third construct. The open(‘Test2.XML‘) call opens Test2.XML as a source and passes this source to Parser through the Parser.parse() method. It’s the call to the Parser.parse() method that actually begins the process of generating events.

Sprite Transform Animation

The preceding hour saw the introduction of a powerful new Animation class that added color animation effect capabilities to Sprite objects. By subclassing Animation, we can create specific animation effects in reusable classes, such as the CycleColor class that demonstrated color cycling over time. There are many more possibilities with the Animation class that have not been explored yet, so we will spend this hour delving into new aspects of animation that are easy to implement and that produce great results for relatively little effort. Specifically, we’ll look at animating the transforms that can be applied with existing properties in the Sprite class: position, rotation, and scaling. These properties are important as they are currently implemented in Sprite, so we won’t mess up what already works. Instead, variations of the Animation class will set up modifiers for these Sprite properties, with changes applied to the Sprite.Animate() method to make it work.

Adding Transform Support to the Animation Class

To perform transforms on a sprite, we need to add some new code to the Sprite.Animate() method. Currently, the method supports only color animation, and we want transforms as well. So, here are the changes:

[code]
public void Animate()
{
if (p_animation != null)
{
if (p_animation.animating)
{
color = p_animation.ModifyColor(color);
position = p_animation.ModifyPosition(position);
rotation = p_animation.ModifyRotation(rotation);
scaleV = p_animation.ModifyScale(scaleV);
}
}
}
[/code]

This is all that is needed to give Animation access to Sprite properties, and we will take full advantage of it!

Position Transforms

When it comes to “animating” the position of a sprite with a custom animation class, we are really getting into custom behaviors, and the term “animation” may not be as appropriate—but it is what it is, so let’s just work with it. If you want to use the term “behavior” in the name for your own transform animation classes, that might seem more appropriate. Since we can do anything with the position, a really dumb but technically valid translation “animation” could be to simply position a sprite to one hard-coded location and never let it leave. That’s silly but it can be done, because the translation modifier added to the Sprite class makes it possible.

The important thing to remember when writing a translation class is to override the ModifyPosition() method (coming from the base Animation class). Any of these base methods that are not overridden will return the passed parameter back, so that no changes are made. Here is just one possible translation class called OrbitalMovement, shown in Listing 13.1. This class inherits from Animation, and exposes a number of properties (which will usually just be passed to the constructor to make initialization simple). ModifyPosition() is where all the work is done. Using some trig, the incoming position is completely ignored, while a calculated orbital position for the object is returned. In other words, it doesn’t matter where a Sprite is located; when this method runs, it calculates position based on sine and cosine calculations, using the properties provided.

LISTING 13.1 Source Code for the OrbitalMovement Class

[code]
public class OrbitalMovement : Animation
{
public int radius;
public Vector2 center;
public double angle;
public float velocity;
public OrbitalMovement(Vector2 center, int radius, double angle,
float velocity)
: base()
{
animating = true;
this.center = center;
this.radius = radius;
this.angle = angle;
this.velocity = velocity;
}
public override Vector2 ModifyPosition(Vector2 original)
{
Vector2 modified = original;
angle += velocity;
modified.X = center.X + (float)(Math.Cos(angle) * radius);
modified.Y = center.Y + (float)(Math.Sin(angle) * radius);
return modified;
}
}
[/code]

Figure 13.1 shows the output of the Transform Animation Demo program, which is found in Listing 13.2. There are a lot of asteroids orbiting a black hole in this small simulation! The really great thing about it is that no orbit code is found anywhere in the main program—it’s all stuffed in the OrbitalMovement class! Let this be just one example of what you can do on your own with similar results. I don’t know about you, but I enjoy producing interesting results with relatively small amounts of code. It is a challenge! Consider how to combine several simple rules to produce interesting results, rather than taking the brute force approach and coding a solution one step at a time. You may be surprised by how this synergy of code works. Following is the code for the program so that you can see how the black hole and asteroids are created and drawn.

The Transform Animation Demo uses a custom animation class to simulate an orbit.
FIGURE 13.1 The Transform Animation Demo uses a custom animation class to simulate an orbit.

LISTING 13.2 Source Code for the Transform Animation Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Random rand;
SpriteFont font;
Texture2D asteroidImg;
List<Sprite> objects;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
objects = new List<Sprite>();’
Sprite blackhole = new Sprite(Content, spriteBatch);
blackhole.Load(“blackhole”);
blackhole.position = new Vector2(400, 240);
blackhole.scale = 0.5f;
blackhole.origin = new Vector2(64, 64);
blackhole.velocityAngular = 1.0f;
objects.Add(blackhole);
asteroidImg = Content.Load<Texture2D>(“asteroid”);
for (int n = 0; n < 500; n++)
{
Sprite ast = new Sprite(Content, spriteBatch);
ast.image = asteroidImg;
ast.origin = new Vector2(32, 32);
ast.scale = 0.25f + (float)(rand.NextDouble() * 0.5);
ast.velocityAngular = (float)(rand.NextDouble() * 0.1);
Vector2 pos = new Vector2(380 + rand.Next(40),
220 + rand.Next(40));
double angle = rand.NextDouble() * 6.0;
int radius = rand.Next(60, 400);
float velocity = (float)(rand.NextDouble() * 0.01 *
((400-radius) * 0.1) );
ast.SetAnimation(new OrbitalMovement(pos, radius,
angle, velocity));
objects.Add(ast);
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
foreach (Sprite spr in objects)
{
spr.Move();
spr.Rotate();
}
base.Update(gameTime);
}’
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in objects)
{
spr.Animate();
spr.Draw();
}
spriteBatch.DrawString(font, “Transform Animation Demo”,
new Vector2(0, 0), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

Rotation and Scaling Transforms

We don’t need a custom Animation subclass just to rotate a Sprite object, so what is the purpose of a so-called rotation animation? Don’t think of rotation in terms of absolute rotation value being set and used to draw. Consider rotation instead in terms of rotation over time. We can transform an animation in small increments over time for interesting results. For example, a sprite could rotate back and forth between 180 degrees to look like it’s wobbling, or it could rotate around at one-second increments like the hand of a clock. It’s all based on the code in your own Animation subclass. To demonstrate, I’ve created an analog clock example. The clock will be based around a class called ClockHand, found in Listing 13.3.

LISTING 13.3 Source Code for the ClockHand Class

[code]
public class ClockHand : Animation
{
public int direction;
public ClockHand(int direction) //0 to 59
: base()
{’’
animating = true;
this.direction = direction;
}
public override float ModifyRotation(float original)
{
float angle = (float)(direction / 60.0f) *
(float)(2.0f * Math.PI);
return angle;
}
}
[/code]

This class is on the simple side so it can be used for hours, minutes, and seconds. Assuming you supply the project with an arrow for the “clock hands,” the ClockHand.direction property represents the time value. For minutes and seconds, this comes to a value from 0 to 59. For hours, we can use the same range by just multiplying hours by 5 (because 60 / 12 = 5). The time values are passed to ClockHand.direction at regular intervals in the program’s Update() method:

[code]
hours.direction = DateTime.Now.Hour * 5;
minutes.direction = DateTime.Now.Minute;
seconds.direction = DateTime.Now.Second;
[/code]

Figure 13.2 shows the output of the Rotate/Scale Animation Demo, but we can just call it the “Clock Demo” for short. This example does scale the clock-hand sprites during initialization, but the scale factor is not being actively used in this example. Given the ease with which rotation was modified in this example, I’m sure you will see how easy scaling would be as well. The source code is found in Listing 13.4.

LISTING 13.4 Source Code for the Clock Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game’’
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Random rand;
SpriteFont font;
List<Sprite> objects;
ClockHand hours, minutes, seconds;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
objects = new List<Sprite>();
Sprite clock = new Sprite(Content, spriteBatch);
clock.Load(“clock”);
clock.position = new Vector2(400, 240);
objects.Add(clock);
hours = new ClockHand(0);
minutes = new ClockHand(0);
seconds = new ClockHand(0); ’’
Sprite hourHand = new Sprite(Content, spriteBatch);
hourHand.Load(“arrow200”);
hourHand.position = new Vector2(400, 240);
hourHand.origin = new Vector2(30, 199);
hourHand.scaleV = new Vector2(1.5f, 0.7f);
hourHand.color = new Color(250, 50, 250);
hourHand.SetAnimation(hours);
objects.Add(hourHand);
Sprite minuteHand = new Sprite(Content, spriteBatch);
minuteHand.Load(“arrow200”);
minuteHand.position = new Vector2(400, 240);
minuteHand.origin = new Vector2(30, 199);
minuteHand.scaleV = new Vector2(1.0f, 0.9f);
minuteHand.color = new Color(250, 100, 150);
minuteHand.SetAnimation(minutes);
objects.Add(minuteHand);
Sprite secondHand = new Sprite(Content, spriteBatch);
secondHand.Load(“arrow200”);
secondHand.position = new Vector2(400, 240);
secondHand.origin = new Vector2(30, 199);
secondHand.scaleV = new Vector2(0.25f, 1.0f);
secondHand.color = new Color(120, 80, 150);
secondHand.SetAnimation(seconds);
objects.Add(secondHand);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
foreach (Sprite spr in objects)
{
spr.Rotate();
spr.Move();
}
hours.direction = DateTime.Now.Hour * 5;
minutes.direction = DateTime.Now.Minute;
seconds.direction = DateTime.Now.Second;
base.Update(gameTime);
}’’
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in objects)
{
spr.Animate();
spr.Draw();
}
spriteBatch.DrawString(font, “Rotate/Scale Animation Demo”,
new Vector2(0, 0), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}’’
[/code]

The Clock Demo actually shows how to use an animation class that modifies rotation.
The Clock Demo actually shows how to use an animation class that modifies rotation.

Code Consistency

An interesting thing has happened since we started using the Animation class and all of its children. Have you noticed that much of the source code in our examples changes very little from one project to the next? Oh, there are often different variables in use, but LoadContent() seems to be where most of the code is being written at this point. Have you really noticed much of a change to Update() or Draw() in quite some time? This is a good sign! When standard code begins to remain unchanged, that means we have replaced the core logic of the program with a statedriven, property-based game engine. Sure, it’s a very, very simple game engine, but that is the path we are now on. The end result is some very solid code, easy to debug, easy to modify, easy to understand. Strive for this type of code in your own projects!

Combining Multiple Animations

We have quite a bit of good animation code now with the ability to add new effects fairly easily using the techniques learned in the preceding two chapters. The next step is quite revolutionary: doing more than one animation at a time! The current animation system will apply a behavior to a game sprite using a single method, Sprite.SetAnimation(). This is going to be replaced with a more powerful mechanism that will support many animations stored in a list.

Although it is technically possible to remove an animation from the list, we’re not to be concerned with seldom-used features like that. Our sprite animation system currently supports just one animation, but it can be replaced at any time with the SetAnimation() method just mentioned. The same will be true when multiple animation support is added, because the list container will have public scope.

Remember the 80/20 rule! Focus most of your efforts on writing code that will be used 80% of the time, not on features rarely used (unless you have time to kill).

Sprite Class Modifications

Adding Multiple Animation Support

We need to make a few changes to the Sprite class to support multiple animations.

  1. Remove the old Animation variable, p_animation, and replace it with a List:
    [code]
    //private Animation p_animation;
    public List<Animation> animations;
    [/code]
  2. Completely remove the SetAnimation() method, which is no longer used:
    [code]
    //public void SetAnimation(Animation animation)
    //{
    // p_animation = animation;
    //}
    [/code]
  3. Make the following changes to the Animate() method. This is the most dramatic change to the class required at this time, adding support for multiple animations. Now, when an animation has completed (by setting the animating property to false), it is actually removed!
    [code]
    public void Animate()
    {
    if (animations.Count == 0) return;
    foreach (Animation anim in animations)
    {
    if (anim.animating)
    {
    color = anim.ModifyColor(color);
    position = anim.ModifyPosition(position);
    rotation = anim.ModifyRotation(rotation);
    scaleV = anim.ModifyScale(scaleV);
    }
    else
    {
    animations.Remove(anim);
    return;
    }
    }
    }
    [/code]

New Animations

To support the upcoming example, here are a couple of new animations to examine. They are called Spin and Throb, affecting the rotation and scaling, respectively. First up is the Spin class, which does a 360-degree spin and then stops. Note that this class overrides only ModifyRotation(), but none of the other modification methods. The source code is found in Listing 13.5.

LISTING 13.5 Source Code for the Spin Class

[code]
public class Spin : Animation
{
private float angleDist, velocity;
public Spin(float velocity)
: base()
{
animating = true;
this.velocity = velocity;
angleDist = 0.0f;
}
public override float ModifyRotation(float original)
{
if (animating)
{
float fullCircle = (float)(2.0 * Math.PI);
angleDist += velocity;
if (angleDist > fullCircle)
animating = false;
original += velocity;
}
return original;
}
}t
[/code]

Next up is the Throb class, which performs a scaling “throb” of a sprite by cycling between a start and an end scale value. This class implements only ModifyScale(), because it does not need to touch any other property. The source code is found in Listing 13.6.

LISTING 13.6 Source Code for the Throb Class

[code]
public class Throb : Animation
{
public float startScale, endScale, speed;
private bool p_started;
public Throb(float startScale, float endScale, float speed)
: base()
{t
p_started = false;
animating = true;
this.startScale = startScale;
this.endScale = endScale;
this.speed = speed;
}
public override Vector2 ModifyScale(Vector2 original)
{
if (!animating) return original;
Vector2 modified = original;
if (!p_started)
{
modified.X = startScale;
modified.Y = startScale;
p_started = true;
}
modified.X += speed;
modified.Y += speed;
if (modified.X >= endScale)
speed *= -1;
else if (modified.X <= startScale)
animating = false;
return modified;
}
}t
[/code]

Multiple Animation Demo

With all of these enhancements, we can now do multiple animations per sprite. Remember, the behavior of the animations is entirely up to you, so if you don’t like how an animation is automatically removed when it is done, then change it! Figure 13.3 shows the sample program. The Throb animation is automatically renewed anytime the animations have all completed. Also, you can tap the screen to add a Spin animation. The fun thing about this demo is that you can tap the screen repeatedly to give the sprite a superfast spin! That is, you can do so until each Spin has finished its 360-degree rotation and is removed. Listing 13.7 has the source code.

Testing multiple animations with the Spin and Throb animations simultaneously.
FIGURE 13.3 Testing multiple animations with the Spin and Throb animations simultaneously.

LISTING 13.7 Source Code for the Multiple Animation Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
Random rand;
SpriteFont font;
Sprite ship;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
ship = new Sprite(Content, spriteBatch);
ship.Load(“ship”);
ship.position = new Vector2(400, 240);
ship.rotation = (float)rand.NextDouble();
ship.animations.Add(new Spin(0.1f));
ship.animations.Add(new Throb(0.5f, 3.0f, 0.2f));
}f’
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//get state of touch input
TouchCollection touchInput = TouchPanel.GetState();
if (touchInput.Count > 0)
{
TouchLocation touch = touchInput[0];
if (touch.State == TouchLocationState.Pressed &&
oldTouch.State == TouchLocationState.Released)
{
ship.animations.Add(new Spin(0.1f));
}
oldTouch = touch;
}
ship.Rotate();
ship.Move();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
ship.Animate();
//reset throb
if (ship.animations.Count == 0)
ship.animations.Add(new Throb(0.5f, 3.0f, 0.2f));
ship.Draw();
int anims = ship.animations.Count;
spriteBatch.DrawString(font, “Tap screen to add animation…”,
new Vector2(0,0), Color.White);
spriteBatch.DrawString(font, “Animations:” + anims.ToString(),
new Vector2(600,0), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}f’
}
[/code]

Canon PowerShot G12, Advanced Techniques to Explore

For most of this book, I’ve focused on how to take a great shot—one exposure, one image. But shooting digital opens other options that combine several shots into one better photo. The following two sections, covering panoramas and high dynamic range (HDR) images, require you to use image-processing software to complete the photograph. They are, however, important enough that you should know how to correctly shoot for success, should you choose to explore these two popular techniques.

Shooting panoramas

If you have ever visited the Grand Canyon, you know just how large and wide open it truly is—so much so that it’s difficult to capture its splendor in just one frame. The same can be said for a mountain range, or a cityscape, or any extremely wide vista. Two methods can help you capture the feeling of this type of scene.

The “fake” panorama

The first method is to shoot as wide as you can and then crop out the top and bottom portion of the frame. Panoramic images are generally two or three times wider than a normal image.

Creating a fake panorama

  1. To create the look of the panorama, zoom out to the camera’s widest focal length, 6.1mm.
  2. Using the guidelines discussed earlier, compose and focus your scene, and select the smallest aperture possible.
  3. Shoot your image. That’s all there is to it, from a photography standpoint.
  4. Open the image in your favorite image-processing software and crop the extraneous foreground and sky from the image, leaving you with a wide panorama of the scene.

Figure 7.16 isn’t a terrible photo, but the amount of sky at the top of the image detracts from the dramatic clouds below. This isn’t a problem, though, because it was shot for the purpose of creating a “fake” panorama. Now look at the same image, cropped for panoramic view (Figure 7.17). As you can see, it makes a huge difference and gives much higher visual impact by drawing your eyes across the length of the image.

This is an okay image, but the sky occupying the top half detracts from the clouds.
Figure 7.16 This is an okay image, but the sky occupying the top half detracts from the clouds.
Cropping adds more visual impact and makes for a more appealing image.
Figure 7.17 Cropping adds more visual impact and makes for a more appealing image.

The multiple-image panorama

The reason the previous method is sometimes referred to as a “fake” panorama is because it is made with a standard-size frame and then cropped down to a narrow perspective. To shoot a true panorama, you need to combine several frames. Although the camera can’t stitch the photos together, it does contain a Stitch Assist mode that aids in lining up the images to be merged together later.

The multiple-image pano has grown in popularity in the past few years; this is principally due to advances in image-processing software. Many software options are available now that will take multiple images, align them, and then “stitch” them into a single panoramic image (Figures 7.18 and 7.19). The real key to shooting a multipleimage pano is to overlap your shots by about 30 percent from one frame to the next. I’ll cover the Stitch Assist mode, but I’ve also included instructions for doing the job manually. Also, it’s possible to handhold the camera while capturing your images, but you’ll get much better results if you use a tripod.

Using the Stitch Assist Mode

  1. Mount your camera on your tripod and make sure it is level.
  2. Choose a focal length for your lens that is somewhere in the middle of the zoom range (a wide angle can distort the edges, making it harder to stitch together).
  3. Turn the Mode dial to SCN and then turn the Control dial until you’ve selected the Stitch Assist scene. (There are actually two Stitch Assist scenes: One helps you shoot left to right, the other helps you shoot right to left.)
  4. Take the first photo.
  5. Carefully pan the camera, using the portion of the previous shot as a guide to align the next shot (see below). When the two images overlap, capture another photo.
  6. Repeat steps 4 and 5 until you’ve captured the entire panorama. Then switch to another mode to exit Stitch Assist.
the Stitch Assist Mode
the Stitch Assist Mode

Shooting properly for a multiple-image panorama

  1. Mount your camera on a tripod and make sure it is level.
  2. Choose a focal length for the lens.
  3. In Av mode, use a very small aperture for the greatest depth of field. Take a meter reading of a bright part of the scene, and make note of it.
  4. Now change your camera to Manual mode (M), and dial in the aperture and shutter speed that you obtained in the previous step.
  5. Switch to manual focus, and then focus your lens for the area of interest. (If you use autofocus, you risk getting different points of focus from image to image, which makes the stitching more difficult for the software.) Or, use the autofocus and remember to set the lens to MF before shooting your images.
  6. While carefully panning your camera, shoot your images to cover the entire area of the scene from one end to the other, leaving a 30 percent overlap from one frame to the next.
Here you see the makings of a panorama, with four shots overlapping by about 30 percent from frame to frame.
Figure 7.18 Here you see the makings of a panorama, with four shots overlapping by about 30 percent from frame to frame.
I used Adobe Photoshop Elements to combine the exposures into one large panoramic image. I also cropped and adjusted the color of the final image.
Figure 7.19 I used Adobe Photoshop Elements to combine the exposures into one large panoramic image. I also cropped and adjusted the color of the final image.

Now that you have your series of overlapping images, you can import them into your image-processing software to stitch them together and create a single image.

Shooting high dynamic range (HDR) images

One of the more recent trends in digital photography is the use of high dynamic range (HDR) to capture the full range of tonal values in your final image. Typically, when you photograph a scene that has a wide range of tones from shadows to highlights, you have to make a decision regarding which tonal values you are going to emphasize, and then adjust your exposure accordingly. This is because your camera has a limited dynamic range, at least as compared to the human eye. HDR photography allows you to capture multiple exposures for the highlights, shadows, and midtones, and then combine them into a single image (Figures 7.20–7.23).

There are two ways to get an HDR image with the G12. Switch to the SCN mode and choose the HDR option. Be sure to stabilize the camera on a tripod or other solid surface and press the shutter button to take the shot. The camera shoots and combines multiple exposures into one HDR shot with a complete range of exposures using a process called “tonemapping.”

For more control over the HDR photo’s appearance, capture multiple shots at different exposures and use third-party software to process them. I will not be covering the software applications, but I will explore the process of shooting a scene to help you render properly captured images for the HDR process. Note that using a tripod is absolutely necessary for this technique, since you need to have perfect alignment of each image when they are combined.

Sorting your shots for the multi-image panorama

If you shoot more than one series of shots for your panoramas, it can sometimes be difficult to know when one series of images ends and the other begins. Here is a quick tip for separating your images.

Set up your camera using the steps listed here. Now, before you take your first good exposure in the series, hold up one finger in front of the camera and take a shot. Now move your hand away and begin taking your overlapping images. When you have taken your last shot, hold two fingers in front of the camera and take another shot

Now, when you go to review your images, use the series of shots that falls between the frames with one and two fingers in them. Then just repeat the process for your next panorama series.

Underexposing one stop renders more detail in the highlight areas of the sky.
Figure 7.20 Underexposing one stop renders more detail in the highlight areas of the sky.
This is the normal exposure as dictated by the camera meter.
Figure 7.21 This is the normal exposure as dictated by the camera meter.
Overexposing by two stops ensures that the darker areas are exposed to get detail in the shadows.
Figure 7.22 Overexposing by two stops ensures that the darker areas are exposed to get detail in the shadows.
This is the final HDR image that was rendered from the three other exposures.
Figure 7.23 This is the final HDR image that was rendered from the three other exposures.

Setting up for shooting an HDR image

  1. Set your ISO to 80 to ensure clean, noise-free images.
  2. Set your program mode to Av. During the shooting process, you will be taking three shots of the same scene, creating an overexposed image, an underexposed image, and a normal exposure. Since the camera is going to be adjusting the exposure, you want it to make changes to the shutter speed, not the aperture, so that your depth of field is consistent.
  3. Set your camera file format to RAW. This is extremely important because the RAW format contains a much larger range of exposure values than a JPEG file, and the HDR software will need this information.
  4. Adjust the auto exposure bracket (AEB) mode to shoot three exposures in twostop increments. To do this, press the Function/Set button and highlight the Bracket setting (third from the top). Next, use the Control dial or press the Right button to select the AEB option (A).
  5. Press the Display button to access the exposure control setting.
  6. Turn the Control dial to the right until the AEB indicators move all the way out to –2 and +2 (B). Press the Set button to lock in your changes.
  7. Focus the camera using the manual focus method discussed earlier, compose your shot, secure the tripod, and press the shutter button once; the camera fires all three shots automatically.
shooting an HDR image
shooting an HDR image

A software program such as Adobe Photoshop or Photomatix Pro can now process your exposure-bracketed images into a single HDR file.

Bracketing your exposures

In HDR, bracketing is the process of capturing a series of exposures at different stop intervals. You can bracket your exposures even if you aren’t going to be using HDR. Sometimes this is helpful when you have a tricky lighting situation and you want to ensure that you have just the right exposure to capture the look you’re after. You can bracket in increments as small as a third of a stop. This means that you can capture several images with very subtle exposure variances and then decide later which one is best.

Sprite Color Animation

Getting Started with Color Animation

The first important point to consider when building an animation system that works with the Sprite class without infiltrating the Sprite class—that is, without mucking it up—is to design the animation system in such a way that transform or change values are used, not the internal properties of Sprite. Animation will be a passive, nonintrusive helper for Sprite, producing transform values that can be used or ignored, as the programmer sees fit. Animation can be integrated inside Sprite, or one or more animations can be applied to a sprite in the main program code instead.

The second significant aspect to this animation system is that animation applied to a sprite will not be permanent. A single animation is processed to a conclusion and then removed. What this allows us to do is write “fire and forget” animation code by setting up an animation event, turning it on, and then just watching it work. When completed, the animation object is deleted. Now, it’s perfectly fine if you want an animation to continue. You have control over when and where the animating property is set to false (which will cause it to be removed). To make this work in such a “fire and forget” manner, the Sprite class will need to be able to handle more than one animation at a time. This calls for a managed list.

The Main Animation Class

The primary or base Animation class, from which all “functional” animation subclasses will be derived, must have certain methods in place for modifying properties within Sprite. If we want to animate the color, a ModifyColor() method must be included. If we want to animate the scale, a ModifyScale() method is required. There really aren’t very many such methods needed in the base Animation class, because we need to animate only things that will affect a sprite’s appearance or behavior. Remember, our Sprite class already has the capability to perform transforms (changing the position, rotation, and scaling). Although it is possible to animate the basic transform properties in Sprite, that will be rare. Nevertheless, we still must give the programmer the ability to animate the transforms. Listing 12.1 contains our base Animation class.

LISTING 12.1 Animation Class Source Code

[code]
public class Animation
{
public bool animating;
public Animation()
{
animating = false;
}
public virtual void Update()
{
}
public virtual Color ModifyColor(Color original)
{
return original;
}
public virtual Vector2 ModifyPosition(Vector2 original)
{
return original;
}
public virtual float ModifyRotation(float original)
{
return original;
}
public virtual float ModifyScale(float original)
{
return original;
}
}
[/code]

Using Animation as a Base Class

Note how the methods in Animation return the passed parameter. This is a default behavior that will just perpetuate any properties that are not modified by a subclass. For instance, if we are doing just color animation, and not changing rotation or any other property, then rotation is completely ignored by the subclass and will have no effect on the animation being processed.

A subclass of Animation should override at least one of the ModifyXXX() methods in order to do something useful. Unless at least one of them is modified, no animation will occur and the class will essentially do nothing. On the flip side, more than one type of animation can be performed by the same class! Suppose you want a sprite to move back and forth on the screen while rotating and scaling—this animation system can handle that task. The sprite can then be used for collision testing, and all other aspects of sprite behavior remain intact! Perhaps the simplest functional subclass of Animation might look like this:

[code]

public class AnimationTemplate : Animation
{
public AnimationTemplate()
: base()
{
}
public void Update()
{
}
}
[/code]

You can use this as a template for your own custom animations. Just override any of the ModifyXXX() methods (such as ModifyColor()) to modify the properties you want to affect a sprite’s behavior. The Update() method can be used for generalpurpose processing. In the next two hours, we will use Update() as a general-purpose “pump” or “motor” for frame animation and transforms. Note that you can also add any variables you need to your Animation subclass. You can also override the parameter list of the modification methods to produce custom results (such as custom subsets of an existing animation).

Modifications to the Sprite Class

The Sprite class will need a few changes to support animation. First, there’s the addition of the Animation variable as a private:

[code]
private Animation p_animation;
[/code]

Next, there’s initialization of the variable in the Sprite constructor:

[code]
p_animation = null;
[/code]

And next, we need a method to set the current animation. We will eventually replace this with a List and support more than one animation at a time, but for now we’ll start with first things first!

[code]
public void SetAnimation(Animation animation)
{
p_animation = animation;
}
[/code]

Finally, we need a new method to get animation up and running! The following Animate() method will evolve quite a bit over the next two chapters. At this point, we need to work with only one type of animation (color cycling).

[code]
public void Animate()
{
if (p_animation != null)
{
if (p_animation.animating)
{
this.color = p_animation.ModifyColor(this.color);
}
}
}
[/code]

Color Animation

Color animation begins with a solid color that can then be transformed by manipulating the color components (red, green, blue, and alpha). We’ll see how to write a basic solid-color class to begin experimenting with color animation before getting into color cycling.

Solid Colors

A solid-color “animation” might seem contradictory, but there actually is a very good use for such a thing. Although it is true that a sprite can be drawn in any desired color, that often requires additional global variables because the Sprite class does not keep track of color changes, just the color property. To change the color on the fly while a game is running would require globals to keep track of the colors and a conditional variable used to trigger the color change. We can do this more easily with a subclass of Animation that works with solid colors.

Let’s begin with Listing 12.2, the source code to the new SolidColor class, which inherits from Animation. There’s very little to this class, which makes it a good starting point for our study of color animation coming up.

LISTING 12.2 Source Code for the SolidColor Class

[code]
public class SolidColor : Animation
{
public Color color;
public SolidColor(Color color)
: base()
{
this.color = color;
animating = true;
}
public override Color ModifyColor(Color original)
{
return color;
}
}
[/code]

The Solid Color project is part of the Color Animation Demo solution included with this hour (several projects in the solution share a common content project for convenience). To run this specific project in the solution, right-click the project name and select Debug, Start New Instance. In this manner, any project in a larger solution can be debugged, while F5 will try to run the currently “active” project. Figure 12.1 shows the program running. There are three boxes that move on the screen, and they change color anytime they hit the edge. Simple as that! The source code to the project is found in Listing 12.3.

Solid color transform as a form of “animation.”
FIGURE 12.1 Solid color transform as a form of “animation.”

LISTING 12.3 Source Code for the Solid Color Project

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Random rand;
SpriteFont font;
Texture2D shapeImage;
List<Sprite> shapes;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
shapeImage = Content.Load<Texture2D>(“box”);
Sprite box1 = new Sprite(Content, spriteBatch);
box1.image = shapeImage;
box1.position = new Vector2(rand.Next(0, 200), rand.Next(0, 380));
box1.velocityLinear = new Vector2(4.0f, 3.0f);
box1.origin = new Vector2(64, 64);
box1.SetAnimation(null);
Sprite box2 = new Sprite(Content, spriteBatch);
box2.image = shapeImage;
box2.position = new Vector2(rand.Next(200, 400), rand.Next(0, 380));
box2.velocityLinear = new Vector2(4.0f, 3.0f);
box2.origin = new Vector2(64, 64);
box2.SetAnimation(null);
Sprite box3 = new Sprite(Content, spriteBatch);
box3.image = shapeImage;
box3.position = new Vector2(rand.Next(400, 600), rand.Next(0, 380));
box3.velocityLinear = new Vector2(4.0f, 3.0f);
box3.origin = new Vector2(64, 64);
box3.SetAnimation(null);
shapes = new List<Sprite>();
shapes.Add(box1);
shapes.Add(box2);
shapes.Add(box3);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
foreach (Sprite spr in shapes)
{
spr.Move();
if (spr.position.X < 0 || spr.position.X > 800 ||
spr.position.Y < 0 || spr.position.Y > 480)
{
spr.SetAnimation(new SolidColor(
new Color(rand.Next(255), rand.Next(255),
rand.Next(255))));
if (spr.position.X < -64 || spr.position.X > 800+64)
spr.velocityLinear.X *= -1;
if (spr.position.Y < -64 || spr.position.Y > 480+64)
spr.velocityLinear.Y *= -1;
}
else
spr.SetAnimation(new SolidColor(Color.White));
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in shapes)
{
spr.Animate();
spr.Draw();
}
spriteBatch.DrawString(font, “Solid Color Demo”,
new Vector2(0, 0), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

Color Cycling

One common color animation involves cycling a color within a range and either going back and forth between two limits or wrapping around in either direction. A base CycleColor class will help us get started and then we can write subclasses from it to do specific color movements. The source code is found in Listing 12.4.

LISTING 12.4 Source Code for the CycleColor Class

[code]
public class CycleColor : Animation
{
public int red, green, blue, alpha;
public CycleColor(int red, int green, int blue, int alpha)
: base()
{
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = alpha;
animating = true;
}
public override Color ModifyColor(Color original)
{
Color modified = original;
if (animating)
{
int R = original.R + red;
int G = original.G + green;
int B = original.B + blue;
int A = original.A + alpha;
if (R < 0 || R > 255 || G < 0 || G > 255 || B < 0 || B > 255
|| A < 0 || A > 255)
{
animating = false;
}
modified = new Color(R, G, B, A);
}
return modified;
}
}
[/code]

This class will cycle a color from its original values upward or downward based on the color cycling modifiers passed to the CycleColor() constructor until the range is exceeded, at which point animation stops. The modifiers will usually be +1 or -1 for each color component, unless faster color cycling is desired. Let’s test it with a quick sample program called Cycle Color Animation (see Listing 12.5).

Three boxes are added: colored red, green, and blue. The boxes fade completely to black based on the CycleColor class’s properties, but if different colors were used for each one, they would not fade completely to black, but would just modify the color components to produce different color results. Setting the initial conditions (such as original color) does have an effect and should be considered ahead of time. In the combined Visual Studio solution for this hour, this project is called Cycle Color Animation, and is shown in Figure 12.2.

Demonstrating color cycling with three rotating boxes of different colors.
FIGURE 12.2 Demonstrating color cycling with three rotating boxes of different colors.

LISTING 12.5 Source Code to the Cycle Color Animation Project

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
SpriteFont font;
Texture2D shapeImage;
List<Sprite> shapes;
CycleColor cycleRed, cycleGreen, cycleBlue;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
shapeImage = Content.Load<Texture2D>(“box”);
Sprite redBox = new Sprite(Content, spriteBatch);
redBox.image = shapeImage;
redBox.position = new Vector2(200, 240);
redBox.origin = new Vector2(64, 64);
redBox.velocityAngular = 0.05f;
redBox.color = new Color(255, 0, 0);
Sprite greenBox = new Sprite(Content, spriteBatch);
greenBox.image = shapeImage;
greenBox.position = new Vector2(400, 240);
greenBox.origin = new Vector2(64, 64);
greenBox.velocityAngular = 0.05f;
greenBox.color = new Color(0, 255, 0);
Sprite blueBox = new Sprite(Content, spriteBatch);
blueBox.image = shapeImage;
blueBox.position = new Vector2(600, 240);
blueBox.origin = new Vector2(64, 64);
blueBox.velocityAngular = 0.05f;
blueBox.color = new Color(0, 0, 255);
cycleRed = new CycleColor(-1, 0, 0, 0);
redBox.SetAnimation(cycleRed);
cycleGreen = new CycleColor(0, -1, 0, 0);
greenBox.SetAnimation(cycleGreen);
cycleBlue = new CycleColor(0, 0, -1, 0);
blueBox.SetAnimation(cycleBlue);
shapes = new List<Sprite>();
shapes.Add(redBox);
shapes.Add(greenBox);
shapes.Add(blueBox);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
foreach (Sprite spr in shapes)
{
spr.Rotate();
spr.Move();
}
if (!cycleBlue.animating)
{
cycleBlue.blue *= -1;
cycleBlue.animating = true;
}
if (!cycleGreen.animating)
{
cycleGreen.green *= -1;
cycleGreen.animating = true;
}
if (!cycleRed.animating)
{
cycleRed.red *= -1;
cycleRed.animating = true;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in shapes)
{
spr.Animate();
spr.Draw();
}
spriteBatch.DrawString(font, “Color Animation Demo”,
new Vector2(0, 0), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

The examples in this hour are combined into a single Visual Studio solution so that they can all share the same content (a single white box image). To run a specific project in a combined solution, right-click the project and use Debug, Start New Instance. Optionally, you can highlight any one of the projects, set it as the Active Project, and then press F5 to run it.

Color Bouncing

The code in the previous example can be improved upon with a new subclass of CycleColor that automatically “bounces” the color components within a specified range. The new subclass is called CycleColorBounce, as found in Listing 12.6. Since this class does not assume that the color component range will bounce between 0 and 255, there is a min and max property for each color component, defined as integers. Although a more complex data type like Vector4 would clean up the code a bit, I was going for clarity. Now, this Animation subclass does not ever end by setting animating to false, so this animation will continue forever unless an intervention takes place. Another possibility is to have it perform a single bounce forward and backward before ending. Then, if you want to perform the animation again, it can be restarted.

LISTING 12.6 Source Code to the CycleColorBounce Class

[code]
public class CycleColorBounce : CycleColor
{
public int rmin, rmax, gmin, gmax, bmin, bmax, amin, amax;
public CycleColorBounce(int red, int green, int blue, int alpha)
: base(red,green,blue,alpha)
{
rmin = gmin = bmin = amin = 0;
rmax = gmax = bmax = amax = 255;
}
public override Color ModifyColor(Color original)
{
Color modified = original;
if (animating)
{
int R = original.R + red;
int G = original.G + green;
int B = original.B + blue;
int A = original.A + alpha;
if (R < rmin)
{
R = rmin;
red *= -1;
}
else if (R > rmax)
{
R = rmax;
red *= -1;
}
if (G < gmin)
{
G = gmin;
green *= -1;
}
else if (G > gmax)
{
G = gmax;
green *= -1;
}
if (B < bmin)
{
B = bmin;
blue *= -1;
}
else if (B > bmax)
{
B = bmax;
blue *= -1;
}
if (A < amin)
{
A = amin;
alpha *= -1;
}
else if (A > amax)
{
A = amax;
alpha *= -1;
}
modified = new Color(R, G, B, A);
}
return modified;
}
}
[/code]

The example for color bouncing is very similar to the previous example, with only minor changes to the variable declarations and initialization of the objects. I opted to turn off rotation of the sprites just to make this example stand apart from the previous one a bit. The source code to the Color Bouncing program that demonstrates this class is found in Listing 12.7.

LISTING 12.7 Source Code to the Color Bouncing Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont font;
Texture2D shapeImage;
List<Sprite> shapes;
CycleColorBounce cycleRed, cycleGreen, cycleBlue;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
shapeImage = Content.Load<Texture2D>(“box”);
Sprite redBox = new Sprite(Content, spriteBatch);
redBox.image = shapeImage;
redBox.position = new Vector2(200, 240);
redBox.origin = new Vector2(64, 64);
redBox.color = new Color(255, 0, 0);
Sprite greenBox = new Sprite(Content, spriteBatch);
greenBox.image = shapeImage;
greenBox.position = new Vector2(400, 240);
greenBox.origin = new Vector2(64, 64);
greenBox.color = new Color(0, 255, 0);
Sprite blueBox = new Sprite(Content, spriteBatch);
blueBox.image = shapeImage;
blueBox.position = new Vector2(600, 240);
blueBox.origin = new Vector2(64, 64);
blueBox.color = new Color(0, 0, 255);
cycleRed = new CycleColorBounce(-5, 0, 0, 0);
cycleRed.rmin = 100;
cycleRed.rmax = 200;
redBox.SetAnimation(cycleRed);
cycleGreen = new CycleColorBounce(0, -10, 0, 0);
cycleGreen.gmin = 150;
cycleGreen.gmax = 255;
greenBox.SetAnimation(cycleGreen);
cycleBlue = new CycleColorBounce(0, 0, -20, 0);
cycleBlue.bmin = 0;
cycleBlue.bmax = 255;
blueBox.SetAnimation(cycleBlue);
shapes = new List<Sprite>();
shapes.Add(redBox);
shapes.Add(greenBox);
shapes.Add(blueBox);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
foreach (Sprite spr in shapes)
{
spr.Rotate();
spr.Move();
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
foreach (Sprite spr in shapes)
{
spr.Animate();
spr.Draw();
}
spriteBatch.DrawString(font, “Color Bouncing Demo”,
new Vector2(0, 0), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
[/code]

This example is included in the Color Animation Demo solution, called Color Bouncing, and is shown in Figure 12.3. Each of the three boxes performs color cycle bouncing at a different rate to demonstrate the versatility of the animation system.

All the color animation being demonstrated in this hour can be applied to sprites with actual game artwork rather than a white shape. These special effects are demonstrated on a simple white box only for illustration, to show what is possible!

Animating color components bouncing between minimum and maximum values.
FIGURE 12.3 Animating color components bouncing between minimum and maximum values.

Canon PowerShot G12, Directing the Viewer: A Word about Composition

As a photographer, it’s your job to lead the viewer through your image. You accomplish this by utilizing the principles of composition, which is the arrangement of elements in the scene that draws the viewer’s eye through the image and holds their attention. As the director, you need to understand how people see, and then use that information to focus their attention on the most important elements in your image.

There is a general order at which we look at elements in a photograph. The first is brightness. The eye wants to travel to the brightest object within a scene. So if you have a bright sky, it’s probably the first place the eye will travel to. The second order of attention is sharpness. Sharp, detailed elements will get more attention than soft, blurry areas. Finally, the eye will move to vivid colors while leaving the dull, flat colors for last. It is important to know these essentials in order to grab—and keep— the viewer’s attention and then direct them through the frame.

In Figure 7.13, the eye is drawn to the bright white cloud in the middle of the frame. From there, it is pulled by the vibrant sky down to the reflection in the water, and finally along the sharp grasses of the river bank. The elements within the image all help to keep the eye moving but never leave the frame.

Rule of thirds

There are, in fact, quite a few philosophies concerning composition. The easiest one to begin with is known as the “rule of thirds.” Using this principle, you simply divide your LCD into thirds by imagining two horizontal and two vertical lines that divide the frame equally.

If you prefer something more concrete, press the Display button to make a grid overlay the screen. (You can turn the grid off permanently by going to the camera’s main menu, selecting Custom Display, and pressing the Function/Set button in the boxes to the right of Grid Lines to make sure neither includes a checkmark.)

The key to using this method of composition is to position your main subject at or near one of the intersecting points.

By placing your subject near these intersecting lines, you are giving the viewer space to move within the frame. The one thing you don’t want to do is place your subject smack-dab in the middle of the frame. This is sometimes referred to as “bull’s eye” composition, and it requires the right subject matter for it to work. It’s not always wrong, but it will usually be less appealing and may not hold the viewer’s focus.

Speaking of the middle of the frame, the other general rule of thirds deals with horizon lines. Generally speaking, you should position the horizon one-third of the way up or down in the frame (Figure 7.14). Splitting the frame in half by placing your horizon in the middle of the picture is akin to placing the subject in the middle of the frame; it doesn’t lend a sense of importance to either the sky or the ground.

The composition of the elements pulls the viewer’s eyes around the image, leading from one element to the next in a circular pattern.
Figure 7.13 The composition of the elements pulls the viewer’s eyes around the image, leading from one element to the next in a circular pattern.
Placing the horizon of this image at the bottom third of the frame places emphasis on the village above it.
Figure 7.14 Placing the horizon of this image at the bottom third of the frame places emphasis on the village above it.

Creating depth

Because a photograph is a flat, two-dimensional space, you need to create a sense of depth by using the elements in the scene to create a three-dimensional feel. This is accomplished by including different and distinct spaces for the eye to travel: a foreground, middle ground, and background. By using these three spaces, you draw the viewer in and render depth to your image.

Jeff Lynch’s photo of sunset at Palo Duro Canyon, shown in Figure 7.15, contains a lot of motion for a still landscape. The outcropping in the foreground at left draws your eye, but then the shadow casts your view to the opposite ridge in the middle ground, and the rest of the canyon extends toward the center of the frame.

The dominant outcropping and shadows against the receding background add to the feeling of depth in this image.
Figure 7.15 The dominant outcropping and shadows against the receding background add to the feeling of depth in this image.

 

Managing Lots of Sprites

Robot Trash Collectors

Our example in this hour is a simulation of robot trash collectors. The robots are represented as white circles, and the trash items as red squares. The robot cleaners will look for the closest trash item and move toward it, getting rid of the trash when it is touched. Figure 11.1 shows the simulation running with just one robot.

Simulation of robotic trash collectors with population control A.I.
FIGURE 11.1 Simulation of robotic trash collectors with population control A.I.

To make the simulation more interesting, it has the capability to automatically manage the amount of trash produced based on the number of cleaners present. A minus button at the bottom left removes cleaners, and its complementary plus button at the lower right adds new cleaners. As long as there is trash remaining, new trash is added rather slowly. But if the number of cleaners increases and the trash goes down, more trash is added more quickly to keep the cleaners busy. Figure 11.2 shows the simulation with five cleaners. Note the respawn time.

Five robot cleaners are present, which increases the trash rate.
FIGURE 11.2 Five robot cleaners are present, which increases the trash rate.

An algorithm of two trash items to one robot cleaner is used to adjust the speed. If there is fewer than twice the number of trash items compared to the cleaners, the speed is increased (by speeding up the respawn time). By watching the simulation run over time, you can see that a balance is maintained as long as the numbers are reasonable. Adding up to 100 or more robot cleaners causes the trash production to shift into high gear to keep up! See Figure 11.3 for an example.

The robot cleaner population is out of control!
FIGURE 11.3 The robot cleaner population is out of control!

Don’t let the primitive artwork dissuade you from studying the example in this hour. The code shared here will be extremely valuable in your own future game projects.

Building the Example

The simulation project this hour has a few asset requirements that you can source from the resource files for this hour, or just make yourself. The button image is just a 64×64 square. The robots are represented by a 32×32 white circle with alpha transparency around the edges. The trash is represented by a 32×32 red square. You are free to use different images if you want, perhaps even little robot and trash images! You can also change the theme of the art; how about insects and food?

Button Class

A helper class is needed to handle the buttons used to increase and decrease the number of robots. The Button class (see Listing 11.1) inherits from Sprite, so it will have all of Sprite’s capabilities and then some.

LISTING 11.1 Source Code for the Button Class

[code]
public class Button : Sprite
{
public string text;
private SpriteBatch p_spriteBatch;
private SpriteFont p_font;
public Button(ContentManager content, SpriteBatch spriteBatch,
SpriteFont font) :
base(content, spriteBatch)
{
p_spriteBatch = spriteBatch;
p_font = font;
Load(“button”);
text = ““;
color = Color.LightGreen;
}
public void Draw()
{
base.Draw();
Vector2 size = p_font.MeasureString(text);
Vector2 pos = position;
pos.X -= size.X / 2;
pos.Y -= size.Y / 2;
p_spriteBatch.DrawString(p_font, text, pos, color);
}
public bool Tapped(Vector2 pos)
{
Rectangle rect = new Rectangle((int)pos.X, (int)pos.Y, 1, 1);
return Boundary().Intersects(rect);
}
}
[/code]

As you can see from the code, a constructor requires ContentManager and SpriteBatch parameters (which are just passed directly to the Sprite constructor), as well as a third parameter, SpriteFont, so that the button can print text on its own. Draw() calculates the size of the text and tries to center it. The font being used in this example is Wasco Sans Bold 36-point, but you are welcome to use a different font if you want. The Tapped() method receives as a parameter the position of a click/tap and checks to see whether it (the button) was tapped, returning true or false.

Main Source Code

Listing 11.2 contains the main source code for the Entity Grouping Demo program. We’ll go over the new helper methods after this.

LISTING 11.2 Source Code for the Entity Grouping Demo Program

[code]
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TouchLocation oldTouch;
Random rand;
SpriteFont font, buttonFont;
Button plus, minus;
List<Sprite> cleaners;
Texture2D circleImage;
List<Sprite> trash;
Texture2D trashImage;
int lastTime = 0;
int target = -1;
int respawn = 500;
int score = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = “Content”;
TargetElapsedTime = TimeSpan.FromTicks(333333);
oldTouch = new TouchLocation();
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
rand = new Random();
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>(“WascoSans”);
buttonFont = Content.Load<SpriteFont>(“ButtonFont”);
//create minus button
minus = new Button(Content, spriteBatch, buttonFont);
minus.position = new Vector2(32, 480-32);
minus.text = “-”;
//create plus button
plus = new Button(Content, spriteBatch, buttonFont);
plus.position = new Vector2(800 – 32, 480 – 32);
plus.text = “+”;
//create cleaners group
cleaners = new List<Sprite>();
circleImage = Content.Load<Texture2D>(“circle”);
AddCleaner();
//create trash group
trash = new List<Sprite>();
trashImage = Content.Load<Texture2D>(“trash”);
AddTrash();
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
ButtonState.Pressed)
this.Exit();
//get state of touch input
TouchCollection touchInput = TouchPanel.GetState();
if (touchInput.Count > 0)
{
TouchLocation touch = touchInput[0];
if (touch.State == TouchLocationState.Pressed &&
oldTouch.State == TouchLocationState.Released)
{
if (minus.Tapped(touch.Position))
RemoveCleaner();
if (plus.Tapped(touch.Position))
AddCleaner();
}
oldTouch = touch;
}
//add new trash item periodically
if ((int)gameTime.TotalGameTime.TotalMilliseconds >
lastTime + respawn)
{
lastTime = (int)gameTime.TotalGameTime.TotalMilliseconds;
AddTrash();
}
if (trash.Count > cleaners.Count * 2)
{
respawn += 1;
if (respawn > 1000)
respawn = 1000;
}
else
{
respawn -= 1;
if (respawn < 10)
respawn = 10;
}
//move the cleaners
for (int n = 0; n < cleaners.Count; n++)
{
//find trash to pick up
target = FindNearestTrash(cleaners[n].position);
if (target > -1)
{
float angle = TargetAngle(cleaners[n].position,
trash[target].position);
cleaners[n].velocityLinear = Velocity(angle, 2.0f);
cleaners[n].Move();
}
//look for collision with trash
CollectTrash(cleaners[n].position);
//look for collision with other cleaners
for (int c = 0; c < cleaners.Count; c++)
{
if (n != c)
{
while (cleaners[n].Boundary().Intersects(
cleaners[c].Boundary()))
{
cleaners[c].velocityLinear.X += 0.001f;
cleaners[c].velocityLinear.Y += 0.001f;
cleaners[c].Move();
}
}
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.DarkBlue);
spriteBatch.Begin();
minus.Draw();
plus.Draw();
foreach (Sprite spr in trash)
spr.Draw();
foreach (Sprite spr in cleaners)
spr.Draw();
spriteBatch.DrawString(font,
“Cleaners:”+cleaners.Count.ToString(),
Vector2.Zero, Color.White);
spriteBatch.DrawString(font, “Trash:”+trash.Count.ToString(),
new Vector2(0,25), Color.White);
spriteBatch.DrawString(font, “Score:” + score.ToString(),
new Vector2(0, 50), Color.White);
spriteBatch.DrawString(font, “Respawn:” + respawn.ToString(),
new Vector2(0, 75), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
[/code]

Main Simulation Functionality Code

The “meat and potatoes” code that drives the simulation is coming up next, in Listing 11.3. Here we find helper methods that look for the nearest trash items, move the robots toward the trash, add new robots, remove robots, and calculate distance and velocity. Some of these methods, like AddCleaner() and RemoveCleaner(), could have been just coded directly where they are called in the program, but this approach is much cleaner and reusable.

LISTING 11.3 Entity Grouping Demo Program (Continued)

[code]
void RemoveCleaner()
{
if (cleaners.Count > 0)
cleaners.RemoveAt(0);
}
void AddCleaner()
{
Sprite obj = new Sprite(Content, spriteBatch);
obj.image = circleImage;
obj.position = new Vector2((float)rand.Next(0, 760),
(float)rand.Next(0, 450));
cleaners.Add(obj);
}
void AddTrash()
{
Sprite obj = new Sprite(Content, spriteBatch);
obj.image = trashImage;
obj.position = new Vector2((float)rand.Next(0, 760),
(float)rand.Next(0, 450));
trash.Add(obj);
}
int FindNearestTrash(Vector2 pos)
{
int target = -1;
float closest = 9999;
if (trash.Count == 0) return -1;
for (int n = 0; n < trash.Count; n++)
{
float dist = Distance(pos, trash[n].position);
if (dist < closest)
{
closest = dist;
target = n;
}
}
return target;
}
void CollectTrash(Vector2 pos)
{
if (trash.Count == 0) return;
for (int n = 0; n < trash.Count; n++)
{
float dist = Distance(pos, trash[n].position);
if (dist < 8)
{
score++;
trash.RemoveAt(n);
break;
}
}
}
float Distance(Vector2 A, Vector2 B)
{
double diffX = A.X – B.X;
double diffY = A.Y – B.Y;
double dist = Math.Sqrt(Math.Pow(diffX, 2) + Math.Pow(diffY, 2));
return (float)dist;
}
float TargetAngle(Vector2 p1, Vector2 p2)
{
return TargetAngle(p1.X, p1.Y, p2.X, p2.Y);
}
float TargetAngle(double x1, double y1, double x2, double y2)
{
double deltaX = (x2 – x1);
double deltaY = (y2 – y1);
return (float)Math.Atan2(deltaY, deltaX);
}
Vector2 Velocity(float angle, float acceleration)
{
double x = Math.Cos(angle) * acceleration;
double y = Math.Sin(angle) * acceleration;
return new Vector2((float)x, (float)y);
}
}
[/code]

Simulating Group Dynamics

The key to this simulation example is a pair of linked lists called cleaners and trash. A linked list is a managed collection for a single type of data, like our Sprite class. In C#, the list is defined with the data type (Sprite) as a template in brackets:

[code]
List<Sprite> cleaners;
The list is created in LoadContent():
cleaners = new List<Sprite>();
[/code]

At this point, the list is ready to be filled with Sprite objects. Each object is a separate, complete Sprite object with its own properties and methods. A new object is added to the list with the Add() method. This code creates a new Sprite object and adds it to the list:

[code]
Sprite sprite = new Sprite(Content, spriteBatch);
sprite.position = new Vector2(0, 0);
cleaners.Add(sprite);
[/code]

Testing revealed that the simulation locks up when there are more than about 250 robots, at which point it is difficult to click the minus button to reduce the population.

You can add as many objects to the list as you want, using a for loop, or by reading data in from a level file, or by any other means, and the list will grow as needed to contain all the objects being added. When objects have been added to the list, they can be forgotten to a certain degree. It’s up to us to make sure we can identify each object inside the list later if we want to use a specific object. For instance, if all the objects for a game are stored in a list like this, and you want to highlight the player’s character, then there must be a way to uniquely identify that sprite among all the others. This can be done by adding an identifier to the Sprite class, such as an ID number or name string. We aren’t doing that in this simulation because every object has equal treatment.

Using List Objects

There will be at least two different points where all the objects in the list must be accessed—from Update() and Draw(). That is, again, the very least number of times, while you might need to access the list elsewhere to test for collisions or for other purposes. There are two ways to iterate the list, using a for loop or using a foreach iterator.

A property of List provides the number of items it contains, called Count. For example:

[code]
for (int n = 0; n < cleaners.Count; n++)
{
//reference cleaners[n]
}
[/code]

Another way to access a list is with a foreach loop such as this:

[code]
foreach (Sprite sprite in cleaners)
{
//reference sprite
}
[/code]

Creating and Using a List

A List is like an array, but it is much easier to use. Let’s learn how to create a List container for other objects.

  1. Define the new List variable in the program’s global variables section. In the brackets, you will define the type of data that the List will contain— which can be anything, such as Sprite or int or string.
    [code]
    List<string> groceries;
    [/code]
  2. Create or initialize the List variable, again with the data type in brackets. Plus, don’t forget the parentheses at the end! The parentheses mean this is a function—or rather, method—call, the constructor method to be precise.
    [code]
    groceries = new List<string>();
    [/code]
  3. Add items to the list like so:
    [code]
    groceries.Add(“butter”);
    groceries.Add(“milk”);
    groceries.Add(“bread”);
    [/code]
  4. Access the items in the list using either a for loop with the groceries.count property, or a foreach loop with the groceries object.
    [code]
    foreach (string item in groceries)
    {
    // … do something with the item here
    }
    [/code]

Iteration Techniques Compared

There are distinct advantages to both iteration mechanisms, depending on programming needs. The numeric for loop is helpful when you want to find the index position of a certain object in the list and then reference it later with indexers ([]). For instance, you might keep track of a “clicked” object with a number variable, and then just index inside the list with that variable, like so:

[code]
cleaners[clicked].Move();
[/code]

The foreach loop is easier to use if you just want to do a quick update of every object, and is commonly used to update the position or draw the object. Of course, there are many other purposes for a foreach iteration, and this is just one example. In the simulation project, all the robot cleaners are drawn with just two lines of code:

[code]
foreach (Sprite spr in cleaners)
spr.Draw();
[/code]

Very complex-appearing A.I. behaviors can seem to derive from surprisingly simple algorithms. When it comes down to analysis, simple rules determine human behavior in social environments too!

But in another part of the project, an index for loop is used to find the nearest trash. This snippet of code from the FindNearestTrash() method looks for the closest trash item by calculating the distance to every item in the trash list, keeping track of the index of the closest one with an int variable called closest. That specific trash item can then be found by indexing into trash with the variable.

[code]
for (int n = 0; n < trash.Count; n++)
{
float dist = Distance(pos, trash[n].position);
if (dist < closest)
{
closest = dist;
target = n;
}
}
[/code]

That’s all there is to it! In the final analysis, we use the distance calculation for many, many things in an ordinary video game!