<Start> <Intro> <Install> <Legal> <Screen> <Buffer> <Sprite> <Input> <Misc> <Debug> <Utes> <Author>
Sprite Functions

Overview

Sprites are like little pictures with transparent backgrounds. When you draw them only the 'solid' part of them is seen. They are used for things like enemies and bullets in games, because they move over a background without overwriting it. The JLib library provides routines to load and display sprites, as well as animate and move them automatically.

Utilities are provided in the jlib/utes directory that enable you to cut sprites from PCX files and edit the sprite files created. This means that you can design your sprites with any drawing tool, convert the output to a PCX file, and then cut the sprites out for use in your game/demo/whatever.

Sprites are positioned in co-ordinates that start from 0,0 in the upper left corner of a buffer. As sprites move into negative co-ordinates or off the sides of buffers, they are clipped, so they appear to slide smoothly out of the buffer. The number of sprites you can have in a JLib program is limited only by your machines memory.

Sprites are grouped together into a sprite_system structure, which holds information which sprites are active, their position and other information. You can have multiple sprite systems to differentiate between different uses or types of sprites. Sprite systems also hold information about what sprites look like. A frame in JLib is a small picture that a sprite is associated with. When a sprite is drawn, it looks like the frame. By changing the frame that a sprite is associated with, the sprite animates. Frames are loaded from .spr files into a sprite system. Many sprites can use the same frames at the same time. Frames can also be stamped or stencilled onto buffers without being involved with a sprite. A pointer to a sprite_system structure is passed to sprite functions to tell JLib which sprites the function should act on.

Sprites themselves are stored as internal records that hold information such as an x and y position, movement and animation details and which frame image a sprite is using. It is important to separate the sprite, which is the object that floats around the screen, with its frames, which are images that the sprite looks like at a given moment. Many sprites can share the same frame, thus they will look the same, but will not be the same sprite. You can also just draw sprite frames into buffers without involving a sprite at all. . Each sprite in JLib has its own movement and animation information, which can be updated for you by JLib.

Sprites have priority according to their number. Priority indicates whether a sprite will be drawn behind or in front of another sprite when the two sprites overlap. The lower a sprites number, the lower its priority, i.e. sprite 1 will always be drawn over sprite 0, and the highest priority sprite is always the last one in the sprite system.

Sprite Initialisation

sprite_system *sprite_init(int max_sprites,int max_frames);

This function creates a new sprite system with enough space to hold max_sprites sprites and max_frames frame images. The sprite system is initially empty, so you will need to load in some sprite frames before you can start to put some sprites on screen. Once a sprite system has been created, the following macros are available to get information about it: If your program needs to get some information on a given sprite system, you can use the following macros:

SPR_MAX_X Biggest possible sprite width.

SPR_MAX_Y Biggest possible sprite height.

SPR_MAX_SPRITES(spr_sys) Max. number of sprites you may use.

SPR_MAX_FRAMES(spr_sys) Max. number of frames you may use.

SPR_NUM_LOADED(spr_sys) Number of frames currently loaded.

SPR_NUM_ACTIVE(spr_sys) Number of sprites turned on.

The following macros are defined to get information about a particular sprite within a sprite system:

SPR_X_SIZE(spr) X size of the sprite pointed to by spr

SPR_Y_SIZE(spr) Y size of the sprite pointed to by spr

SPR_IS_ON(sys,num) Is sprite num in system sys turned on?

SPR_GET_XPOS(sys,num) X position of sprite number num

SPR_GET_YPOS(sys,num) Y position of sprite number num

The biggest sprite frame you can use defaults to 64 by 64 pixels, but you can change this if you want by changing the SPR_MAX_X and SPR_MAX_Y constants in <JLib.h>. You will need to rebuild the whole library if you change these numbers, however.

sprite_system *sprite_free(sprite_system *sys);

This function releases the resources used by a sprite system and returns NULL. Since sprite systems can use large amounts of memory, it is recommended that you use this function as soon as your application is finished with a sprite system.

int sprite_load(char *fname,sprite_system *sys);

This function opens the file called fname and loads the frames it holds into the sprite system sys. This function returns values as follows:

COULDNT_OPEN The file passed could not be opened.

TOO_MANY_IN_FILE Too many frames in the file.

SUCCESS The ffile loaded successfully.

You may call this function repeatedly to load more than one sprite file into a sprite system, provided that the total number of frames that are loaded will fit into the space reserved for frames in the sprite system as specified when sprite_init() was called.


Developers Note: Should you wish to write any sprite file manipulation code yourself, the format for sprites file is documented in the file sprite/sprite.c .


.

Sprite Manipulation

void sprite_turn_on(sprite_system *spr_sys,int snum);

void sprite_turn_off(sprite_system *spr_sys,int snum);

void sprite_set_xy(sprite_system *spr_sys,int snum,int newx,int newy);

void sprite_set_an_frame(sprite_system *spr_sys,int snum,int frame);

void sprite_set_mode(sprite_system *spr_sys,int snum,int mode);

These are the elementary sprite manipulation functions in JLib, which allow you to change the position, current frame and on/off status of a sprite snum. These functions are designed to be called while the sprite being manipulated is not currently drawn anywhere or is turned off. If these functions are called while a sprite is drawn into a buffer, there will probably be corruption of (at least) the sprites buffer when the library tries to restore the background behind the sprite that you are updating (as the sprite will then be at a different position, or have a different frame).

Turning a sprite off and on restores its mode to SPR_MODE_WHOLE. These functions do not cause the sprite to be re drawn automatically. You must redraw the sprites manually within your program, or if you are using one of the sprite_update_() functions then this will be done for you the next time it is called.

Sprite Drawing

The process of showing sprites on screen and having them move or animate follows a pattern in which order is important. Starting with a sprite turned on, but not drawn anywhere, the process is as follows:

  1. Save the buffer area that the sprite will overwrite.
  2. Draw the sprite into the buffer.
  3. Show the buffer containing the drawn sprite on screen.
  4. Restore the buffer area overwritten by the sprite.
  5. Update the sprite's movement or animation details.
  6. Go back to step 1.

Remember that if you are drawing and restoring individual sprites yourself in your program, you should save, draw and restore them in the correct order. The _all_ drawing functions perform these operations in the correct order, making them much easier to use.

void buff_save_sprite(sprite_system *sys,int num,buffer_rec *buff);

void buff_save_spriteNC(sprite_system *sys,int num,buffer_rec *buff);

void buff_save_all_sprites(sprite_system *sys,buffer_rec *buff);

These functions save the area in buffer buff that the sprite num (or all sprites in the case of buff_save_all_sprites()) will overwrite if drawn to the buffer. This function should be called after any sprite movement or animation has taken place but before the sprite(s) are drawn.

void buff_draw_sprite(sprite_system *sys,int num,buffer_rec *buff);

void buff_draw_spriteNC(sprite_system *sys,int num,buffer_rec *buff);

void buff_draw_all_sprites(sprite_system *sys,buffer_rec *buff);

These functions draw the sprite num (or all sprites in the system sys) to the given buffer. This function should be used after the sprite(s) are saved and before they are moved or animated further.

void buff_rest_sprite(sprite_system *sys,int num,buffer_rec *buff);

void buff_rest_spriteNC(sprite_system *sys,int num,buffer_rec *buff);

void buff_rest_all_sprites(sprite_system *sys,buffer_rec *buff);

These functions restore the area of buffer buff that the sprite num (or all sprites in the case of buff_save_all_sprites() had overwritten when drawn to the buffer. This function should be called after any sprites have been drawn and displayed, before any movement or animation takes place.

Sprite Movement Overview

There are two possible modes that a sprite may be in: SPR_MODE_WHOLE or SPR_MODE_FIXED. All sprites start out in SPR_MODE_WHOLE. The mode of a sprite affects how it moves and animates; in whole steps or in fixed point fractional steps. To understand this we need to look at the concept of time-slices.

A time-slice is exactly what its name suggests, a slice of time. When you want to have a sprite animated or moved automatically, you must specify how often movement or animation is to occur. These times are given in time-slices. A time-slice passes every time sprites are updated by the function sprite_update_all_anim_and_move() or manually updated by you. So if you wanted a sprite to change its animation frame every time it was updated, you would be setting it to update every time-slice. The actual elapsed time between updates is irrelevant - it is the number of times that an update is performed that determines when sprites will move or animate.

In the default mode, SPR_MODE_WHOLE, the speed parameter is treated as a counter that says how many time-slices to wait before moving or animating . A value of 1 means "do it every time-slice" while a value of 10 means "do it every 10 time-slices.

If the sprites mode is SPR_MODE_FIXED, the speed parameter is treated as a fixed point 4.4 digit number. This means that the top four bits of the byte are the number of times to move/animate each time-slice, while the bottom four bits hold a fractional number of times to move/animate each time-slice. Using this mode you can specify that a sprite is to move or animate from once in every 16 time-slices, to 16 times per time-slice, with many combinations.

For example, to move twice every time-slice you would use a speed value of (1<<5), to move 2.5 times per time-slice you would use a speed value of ((1<<5)&(1<<3)). Note that moving occurs when whole number quantities of time-slices are reached: moving 2.5 times per time-slice is approximated by moving 2 times, then 3 times, then 2 times etc.

Regardless of the mode of the sprite, a speed value of zero indicates that the sprite will stop updating movement or animation.

Sprite Movement Functions

void sprite_set_move_info (sprite_system *sys,int snum,UBYTE speed,int x_inc,int y_inc);

This function tells JLib to update the position of the sprite snum according to the speed and increment parameters. The parameters x_inc and y_inc refer to how many pixels to move in the x and y directions whenever the sprites speed dictates that it is to move. A negative value for either of these parameters means moving up and left respectively, while positive values indicate moving down and right respectively. The speed, x_inc and y_inc parameters are interpreted differently depending on the mode of the sprite (see Sprite Functions: Movement: Overview), but refer to how many pixels to move in the x and y directions when the sprite does move.

void sprite_set_anim_info(sprite_system *sys,int num,UBYTE speed,int frames,int *pat);

Within this function speed is interpreted in the same way as speed in sprite_set_move_info() above. The parameter frames gives the number of animation frames in the sequence to be followed by the sprite num. pat is an integer array of length frames, where each number from pat[0] to pat[frames-1] is a frame number in the sprite system. When the sprite reaches the end of the pattern it will begin again at the start of the animation sequence.

void sprite_update_anim_and_move(sprite_system *spr_sys,int snum);

void sprite_update_all_anim_and_move(sprite_system *spr_sys);

void sprite_do_all_anim_and_move_n_times(sprite_system *spr_sys,int n);

These functions automatically update the animation and movement information of sprites in the sprite system spr_sys that are turned on, according to the movement and animation information set by sprite_set_anim_info() and sprite_set_move_info(). These functions should be called when the sprite or sprites to be updated are not drawn anywhere.

Sprite Frame Drawing

void buff_stencil_sprite(int x,int y,sprite_system *sys,int frame,buffer_rec *buff)

void buff_stencil_spriteNC(int x,int y,sprite_system *sys,int frame,buffer_rec *buff)

This function draws the sprite frame frame into buff with a transparent background. This looks exactly the same as if a sprite was created with a frame frame and drawn to the buffer, but does not involve using or managing a sprite.

void buff_stamp_sprite(int x,int y,sprite_system *sys,int frame,buffer_rec *buff)

void buff_stamp_spriteNC(int x,int y,sprite_system *sys,int frame,buffer_rec *buff)

This function is exactly the same as buff_stencil_sprite() except that the sprites background is drawn in colour 0, overwriting anything underneath the transparent areas of the sprite with the background colour.

void sprite_build_from_buff(sprite_system *sys,int fr,buffer_rec *bf,int x1,int y1,int x2,int y2);

Sprites frames can be "built" out of a buffer's contents dynamically by using this function. If you have a buffer with data drawn in it that you would like to make a sprite frame out of, then you can copy that data into a frame and use it just like a regular sprite frame. The parameter fr is the frame you would like the data copied into. If the frame was already used then it will be overwritten. To find the next free frame available in the sprite system use the function sprite_find_first_frame(). You can't build a sprite bigger than SPR_MAX_X or SPR_MAX_Y. If the co-ordinates you pass cover an area bigger than this then the frame will be truncated to the maximum size. Built sprites are created without any bounding rectangles.

int sprite_find_first_free(sprite_system *spr_sys);

This function returns the number of the first unused sprite (that is, the first sprite turned off) in the sprite system.

int sprite_find_first_frame(sprite_system *spr_sys);

This function returns the frame number of the first unused frame in the sprite system.

Collision Testing

You can test any two sprites to see if they are colliding in a buffer. Each sprite frame has zero or more rectangles associated with it and stored in its sprite file. These rectangles are used by JLib to calculate whether sprites are intersecting. You can use the utility spr_edit.exe (see Utilities)to examine and change the bounding rectangles of frames within sprite files.

There are other, more precise methods of collision detection available than bounding rectangle checks. The reasons I chose bounding rectangles above any other methods are speed and accuracy. Bounding rectangles are fast compared to bit manipulation detection methods. If you want better accuracy of collisions with other sprites, you can increase the number of rectangles associated with a sprite frame to provide more accurate coverage. Each sprite can have up to 255 rectangles. There is a trade off in accuracy versus time (just like other methods), but this trade off can be made on a sprite to sprite basis. If a sprite doesn't need collision detection you don't even have to have a bounding rectangle at all.

int sprite_do_intersect(sprite_system *sys,int sprite1,int sprite2);

This function will return non zero if sprite1 is colliding with sprite2. Note that the sprites must both be turned on for a collision to be detected. Whether the sprites are currently drawn anywhere or not is not tested when deciding collisions. Thus sprites being drawn into two different buffers can still collide if they are turned on and their bounding rectangle co-ordinates overlap.


Developers Note: Collision functions haven't been optimised yet. A collision routine that checks every sprite against one another and then lets you query a table of who is colliding with who (which will be very fast) is planned. A function to automatically generate bounding rectangles for sprite frames is also planned.

Next Section: Input Functions

<Start> <Intro> <Install> <Legal> <Screen> <Buffer> <Sprite> <Input> <Misc> <Debug> <Utes> <Author>