3 SpritesInDepth
Tim Young edited this page 2017-10-04 03:20:42 +02:00

Sprites

Drawing sprites

Sadly, it is hard to give a lot of advice on how to draw sprites. This is mainly because the act of drawing has such personal preferences associated with it. I tend to use GIMP. Most everyone I know of who has made games with SpriteController has drawn sprites using different tools. If you google for Sprite Creation Tools, you will find a lot of them.

The SpriteLibrary is going to look for multiple “frames”, which are individual images. These are usually easiest to create in one image, one after the other. For example:

Man jumping

This is a 4-frame sprite of a man jumping to the right. The sprite, when you define it, starts reading from the left side and moves to the right.

Here is another 4-frame sprite, this one of the same man, drawing a gun and shooting.

Man Shooting

In the actual sprite itself, you do not want to have the lines going around and in-between the spaces. I show them here, simply to help explain how a sprite is constructed. In Gimp, I have a layer for my “grid”, and I can hide that layer when I go to save my sprite file.

Because these sprites work best if they have transparency, you will most likely want to save the file as .png. PNG files handle transparency well (jpg and gif do not).

Because the sprite controller can rotate and flip sprites for you, you only need to make sprites facing one direction, so long as you do not mind them looking the same if they are flipped left vs right. If your adventurer has a sword belted on one side of his body, and not the other, then you may want him to look differently when he walks left than when he walks right. I am a lazy sprite maker. You see my adventurer above? He is a stick-man, and he was pretty easy to make. Half the work in a game is drawing animations. Really. Making animations is a lot of work, even if you have something like the SpriteLibrary to make animating things easier.

Instantiating sprites

Now that you have some sprite images, you need to turn them into sprites within your program. I will call this “instantiation”, though it can also be called “constructing.” There are a good number of sprite-instantiation functions. For greater information on “where” to instantiate your sprites, and how to design a game, see the section on Program Design.

Usually, however, you want to instantiate your sprites at the beginning of a program. If you have a lot of sprites and do not need them all immediately, you can try to instantiate them on demand. Either way, what you want to do is to make one “named sprite”, which is the master sprite for that image. Then, every time you want to use a sprite of that type, you duplicate the master sprite and put the copy of it onto the screen. This is because the initial instantiation of the sprite takes a long time, but duplicating the sprite happens very quickly. It is also much more memory efficient this way.

The SpriteController will remember your sprites for you. After you instantiate, and name, the sprite, you can then ask the SpriteController for the sprite named [whatever] and you get that sprite. Or, you ask for a duplicate of the sprite named [whatever], and you get a copy of that which is ready to be placed on your screen.

Here is an example. We are have this image in our resources:

Monsters

Each of the images is 100x100. We want to read in the second set of images, the jelly-monster.

Sprite JellyMonster = new Sprite(new Point(0, 100),
         MySpriteController,   
         Properties.Resources.monsters, 
         100, 100, 200, 4);
JellyMonster.SetName(jelly);

In this example, we are making a sprite named JellyMonster by pulling the animation out of the second row of the image “Properties.Resources.monsters”. (0,0 is the first row, 0,100 is the second row.) We pass it the sprite controller, and then the “monsters” image file from within the resources. We specify that the image we are pulling out is of the size 100 x 100. We use an animation speed of 200ms per frame, and we pull 4 frames out of the image.

When we print the sprite, we can grow, or shrink the sprite on the PictureBox. It does not need to remain at 100x100. That is just the size of the individual frame in the sprite sheet image.

The last step we do in the above code is to name the sprite. We name our master sprites, and then clone them when we want to have a bunch of them. Destroying the master means you need to start from scratch. It is very efficient to duplicate sprites, but it takes a lot more effort to generate new ones from scratch.

You duplicate a sprite by doing something like:

Sprite newsprite = SpriteController.DuplicateSprite(spritename);

Sprites, when they are duplicated, retain all the main settings of the original. BUT, they do not retain the position on the screen or any “changes” you made to the sprite (animation speed adjustments, what animation you are viewing, etc. Your named sprites should not be on the screen, remember? But even so, the location is not duplicated.) The sprite has the animations (the images), an animation speed, and can have functions for if it collides with another sprite. All of these things are duplicated from the master sprite.

The things that are not duplicated, are things which are usually unique to a sprite. The location, speed, direction, and which animation is currently displaying. Those things are all separate. That way we could have fifteen jelly-monsters running around the map, chasing after some poor adventurer. Those jelly-monsters need to have different locations, speeds, directions, etc. But, if one of them collides with our adventurer, we want the same “KillAdventurer” function to run.

Rotating and flipping sprites

When you instantiate a sprite, or add an animation to an existing sprite, you can have your sprite flip or rotate. You can give a sprite a custom rotation while it moves around, but that takes a bit more drawing power. If you are only going to have a few different rotations, you may want to have different animations, each one at a different rotation.

For example, if you have an adventurer walking right, we can create an animation with the image flipped horizontally. This will make him look like he is walking left instead of right. You can do this by using the AddAnimation (to your named sprite), giving it the number of an animation to duplicate, and either telling it the angle to rotate, or telling it to flip horizontally or vertically.

Here is the function you call to make a sprite that is rotated.

public void AddAnimation(
	int AnimationToCopy,
	int RotationDegrees
)

The jelly-monster does not really look good rotated, but we could try:

JellyMonster.AddAnimation(0,180);

This would have him basically jiggling on his head. What we really would want to do for the jelley-monster, would be to flip him horizontally using: Upsidedown

public void AddAnimation(
	int AnimationToCopy,
	bool MirrorHorizontal,
	bool MirrorVertical
)

The actual code to do that would be:

JellyMonster.AddAnimation(0, true, false);

One thing to be aware of with rotating and flipping. The sprites remain bound by the original size. If your original size was a rectangle and not a square, rotating the sprite will result in the object changing sizes.

If you do not want to create rotated or flipped sprites, you can rotate and flip them using their original shape. This does result in a little bit slower drawing and extra memory usage over time (C# does clean the memory usage up, so this works fine for many games). You can use the Sprite.Rotation(int) function to rotate a sprite. And you can use the Sprite.MirrorHorizontally and Sprite.MirrorVertically Booleans to flip a sprite. Moving Sprites Rotated

There are a few ways to have sprites move. Whenever possible, you want to use a movement function instead of simply placing a sprite at the new location. Even if the sprite location is moving a pixel at a time, you will find that manually moving the sprite will look jerky. The sprite movement functions that come with the sprite are set up on a timer such that they adjust to minor delays, so that movement always looks smooth. The two main ways to do movement are to use a MoveTo function, or set a direction and speed. The MoveTo functions can move to a specific point on the screen, or move to another sprite.

Here is an example of how to tell a JellyMonster to move to a specific point. The sprite will move at the specified speed until it reaches the given point. When it reaches that point, the SpriteArrivedAtEndPoint function will trigger. And, the Boolean, SpriteReachedEndPoint, will be true.

JellyMonster.AutomaticallyMoves = true;
JellyMonster.MoveTo(new Point(100,100));
JellyMonster.MovementSpeed = 30;
Here is an example for telling the JellyMonster to move in a direction:
JellyMonster.AutomaticallyMoves = true;
JellyMonster.CannotMoveOutsideBox = true;
JellyMonster.SpriteHitsPictureBox += SpriteBounces;
JellyMonster.SetSpriteDirectionDegrees(180);
JellyMonster.PutBaseImageLocation(new Point(startx, starty));
JellyMonster.MovementSpeed = 30;

Destroying sprites

Once you are done with a sprite, you should destroy it by calling the Sprite.Destroy() function, which is usually followed by setting the variable to null:

JellyMonster.Destroy();
JellyMonster = null;

Destroying a sprite will erase it from the screen, and then remove all traces of it from the SpriteLibrary. You should be careful about storing sprites in variables that persist after the sprite is destroyed. Most functions will realize that a sprite has been destroyed and work appropriately. But, if you use a sprite that has been “destroyed”, there can be some rather odd things that can happen.

What I mean by this is, if I have a Sprite variable named “JellySprite” and I do something like: JellySprite.Destroy(); Then, in a different part of the program I can still do something like:

JellySprite.ChangeAnimation(3);

JellySprite can still point to the sprite, even though the sprite has been destroyed. This is something C# does. The act of “Destroying” a sprite will go through the process of getting rid of it from the screen and SpriteLibrary, but you still need to set the variable to null. Once the variable is set to null, C# knows it can do “garbage collection” and finish getting rid of the variable.

Sprite Display Order

Some sprites will print on top of other sprites. When two sprites exist in the same space, one of them will invariably be on top. You ultimately have control over which one is on top.

Sprites have a Z value, which determines which sprite prints on top of which sprite. Usually, this is enough. But there will be some sorts of games where you will want to use an alternative approach to determining which sprite draws on top of another. For example, you might want to have a tower defense game where, as the sprites go farther down the Y axis, they print before those which are closer to the top of the screen.

The SpriteController has a function to sort the sprites. You can experiment with sorting them differently, but here is the default value, sorting strictly by the Zvalue:

SpriteComparisonDelegate = delegate (Sprite first, Sprite second) 
	{ return first.Zvalue.CompareTo(second.Zvalue); };