Jump to content

Sprites and Animation Part 3: Difference between revisions

From EDM2
mNo edit summary
Ak120 (talk | contribs)
mNo edit summary
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Written by [[Larry Salomon Jr.]]
{{SpriteAnim}}
 
''Written by [[Larry Salomon Jr.]]''
* [[Sprites and Animation Part 1|Part 1]]
* [[Sprites and Animation Part 2|Part 2]]
* [[Sprites and Animation Part 3|Part 3]]
* [[Sprites and Animation Part 4|Part 4]]
 
===Introduction===


==Introduction==
In the final part of this series, we will wrap things up by looking at how I495.EXE was built. This will hopefully get you on your way to using animation within your own applications, although it should be noted that no article or series of articles will ever be able to give an exhaustive treatment of this topic. And when there are applications like Autodesk and Renderman available (albeit expensive), hard-coded animations are rare.
In the final part of this series, we will wrap things up by looking at how I495.EXE was built. This will hopefully get you on your way to using animation within your own applications, although it should be noted that no article or series of articles will ever be able to give an exhaustive treatment of this topic. And when there are applications like Autodesk and Renderman available (albeit expensive), hard-coded animations are rare.


===Capabilities===
==Capabilities==
 
Before we can begin designing, we have to know what it is that we are designing - what will the application do, how much of the behavior can be controlled by the user, etc. Fortunately, I495.EXE allows for little control by the user, so the majority of the application is deterministic, i.e. we can predict what will happen with high accuracy (this statement is a bit inaccurate depending on how it is interpreted, since we use random number generators). Enumerated below are the intended capabilities of the application:
Before we can begin designing, we have to know what it is that we are designing - what will the application do, how much of the behavior can be controlled by the user, etc. Fortunately, I495.EXE allows for little control by the user, so the majority of the application is deterministic, i.e. we can predict what will happen with high accuracy (this statement is a bit inaccurate depending on how it is interpreted, since we use random number generators). Enumerated below are the intended capabilities of the application:
# It should have vehicles enter from the top right which proceed to the left and from the bottom left which proceed to the right.
# It should have vehicles enter from the top right which proceed to the left and from the bottom left which proceed to the right.
# It should not allow the vehicles to have individual velocities, i.e. all vehicles have the same speed.
# It should not allow the vehicles to have individual velocities, i.e. all vehicles have the same speed.
# It should allow the user to control the density of the traffic via the keyboard.
# It should allow the user to control the density of the traffic via the keyboard.
Now that we have a good idea of the capabilities of the application, we can begin the design with the goals that we have set in mind.
Now that we have a good idea of the capabilities of the application, we can begin the design with the goals that we have set in mind.


===Design===
==Design==
 
Because we have an easy to use "create sprite" function, it is easier for us to create two bitmaps for each vehicle type, one for each direction, and to create two sprites from these bitmaps. Although this is not a good design policy, by knowing which bitmap id's correspond to which vehicle type and direction, we can build a table of sprite information which contains the following information:
Because we have an easy to use "create sprite" function, it is easier for us to create two bitmaps for each vehicle type, one for each direction, and to create two sprites from these bitmaps. Although this is not a good design policy, by knowing which bitmap id's correspond to which vehicle type and direction, we can build a table of sprite information which contains the following information:
* the sprite handle
* the sprite handle
* the direction that the sprite should travel
* the direction that the sprite should travel
Line 30: Line 20:


The corresponding data structure for this is
The corresponding data structure for this is
 
<pre>
<nowiki>
  #define SIT_CAR                  0x0001L
  #define SIT_CAR                  0x0001L
  #define SIT_TRUCK                0x0002L
  #define SIT_TRUCK                0x0002L
Line 50: Line 39:
     ULONG ulState;
     ULONG ulState;
  } SPRITEINFO, *PSPRITEINFO;
  } SPRITEINFO, *PSPRITEINFO;
</nowiki>
</pre>


The explanation of the SPRITEINFO structure follows:
The explanation of the SPRITEINFO structure follows:
;hsSprite:handle of the sprite.
;szlSprite:size of the sprite. This is used to determine the value of ulState.
;ulType:type of the sprite and direction it travels (SIT_* constant).
;ulState:state of the sprite (SIS_* constant).


; hsSprite
==Animation==
: handle of the sprite.
; szlSprite
: size of the sprite. This is used to determine the value of ulState.
; ulType
: type of the sprite and direction it travels (SIT_* constant).
; ulState
: state of the sprite (SIS_* constant).
 
===Animation===
 
The animation itself is nothing more than good management of an array of SPRITEINFO structures from within a WM_TIMER message. The pseudocode for the logic is
The animation itself is nothing more than good management of an array of SPRITEINFO structures from within a WM_TIMER message. The pseudocode for the logic is


Line 71: Line 54:
# For all sprites that are not marked as "unused", increment or decrement their horizontal position as appropriate, and update their status to reflect if they are entering, on screen, exiting, or no longer visible ("unused").
# For all sprites that are not marked as "unused", increment or decrement their horizontal position as appropriate, and update their status to reflect if they are entering, on screen, exiting, or no longer visible ("unused").


Once you understand this, you should have no trouble interpreting the corresponding code below:
Once you understand this, you should have no trouble interpreting the corresponding code below:  
  case WM_TIMER:
  case WM_TIMER:
     switch (SHORT1FROMMP(mpParm1)) {
     switch (SHORT1FROMMP(mpParm1)) {
Line 172: Line 154:
You should notice the reference to pidData->lMod in the determination of whether a sprite is to begin entering the display area. This is a changing value which can be adjusted using the "+" and "-" keys; the code is trivial WM_COMMAND processing with the two keys mapped to accelerators.
You should notice the reference to pidData->lMod in the determination of whether a sprite is to begin entering the display area. This is a changing value which can be adjusted using the "+" and "-" keys; the code is trivial WM_COMMAND processing with the two keys mapped to accelerators.


The rest of the code is, honestly, initialization and termination processing (other than the "standard" PM code). Everything you need to compile I495.EXE is provided as I495.ZIP, which can be found in [../zips/0207s.zip sources 0207.zip]
The rest of the code is, honestly, initialization and termination processing (other than the "standard" PM code). Everything you need to compile I495.EXE is provided as I495.ZIP, which can be found in [http://www.edm2.com/zips/0207s.zip sources 0207.zip]
 
===Summary===


==Summary==
This month we said our farewells to this series by examining the animation in a simple application - I495.EXE. And while it was said earlier that hard-coded animations are rare, I would like to conclude that, although rare, a good animation sequence can spruce up any "About box" (or any other part of an application) and is well worth the effort of coding it.
This month we said our farewells to this series by examining the animation in a simple application - I495.EXE. And while it was said earlier that hard-coded animations are rare, I would like to conclude that, although rare, a good animation sequence can spruce up any "About box" (or any other part of an application) and is well worth the effort of coding it.



Latest revision as of 16:38, 12 September 2019

Sprites and Animation / Part
1 2 3 4

Written by Larry Salomon Jr.

Introduction

In the final part of this series, we will wrap things up by looking at how I495.EXE was built. This will hopefully get you on your way to using animation within your own applications, although it should be noted that no article or series of articles will ever be able to give an exhaustive treatment of this topic. And when there are applications like Autodesk and Renderman available (albeit expensive), hard-coded animations are rare.

Capabilities

Before we can begin designing, we have to know what it is that we are designing - what will the application do, how much of the behavior can be controlled by the user, etc. Fortunately, I495.EXE allows for little control by the user, so the majority of the application is deterministic, i.e. we can predict what will happen with high accuracy (this statement is a bit inaccurate depending on how it is interpreted, since we use random number generators). Enumerated below are the intended capabilities of the application:

  1. It should have vehicles enter from the top right which proceed to the left and from the bottom left which proceed to the right.
  2. It should not allow the vehicles to have individual velocities, i.e. all vehicles have the same speed.
  3. It should allow the user to control the density of the traffic via the keyboard.

Now that we have a good idea of the capabilities of the application, we can begin the design with the goals that we have set in mind.

Design

Because we have an easy to use "create sprite" function, it is easier for us to create two bitmaps for each vehicle type, one for each direction, and to create two sprites from these bitmaps. Although this is not a good design policy, by knowing which bitmap id's correspond to which vehicle type and direction, we can build a table of sprite information which contains the following information:

  • the sprite handle
  • the direction that the sprite should travel
  • the vehicle type
  • ...plus other information such as sprite size

The corresponding data structure for this is

 #define SIT_CAR                  0x0001L
 #define SIT_TRUCK                0x0002L
 #define SIT_GOESLEFT             0x0004L
 #define SIT_GOESRIGHT            0x0008L
 
 #define SIS_UNUSED               0L
 #define SIS_VISIBLE              1L
 #define SIS_ENTERTOP             2L
 #define SIS_EXITTOP              3L
 #define SIS_ENTERBOTTOM          4L
 #define SIS_EXITBOTTOM           5L
 
 typedef struct _SPRITEINFO {
    HSPRITE hsSprite;
    SIZEL szlSize;
    ULONG ulType;
    ULONG ulState;
 } SPRITEINFO, *PSPRITEINFO;

The explanation of the SPRITEINFO structure follows:

hsSprite
handle of the sprite.
szlSprite
size of the sprite. This is used to determine the value of ulState.
ulType
type of the sprite and direction it travels (SIT_* constant).
ulState
state of the sprite (SIS_* constant).

Animation

The animation itself is nothing more than good management of an array of SPRITEINFO structures from within a WM_TIMER message. The pseudocode for the logic is

  1. If nothing is entering on top and a random number is 0, find an unused sprite that goes from right to left and set its state to "enter on top".
  2. If nothing is entering on bottom and a random number is 0, find an unused sprite that goes from left to right and set its state to "enter on bottom".
  3. For all sprites that are not marked as "unused", increment or decrement their horizontal position as appropriate, and update their status to reflect if they are entering, on screen, exiting, or no longer visible ("unused").

Once you understand this, you should have no trouble interpreting the corresponding code below:

case WM_TIMER:
   switch (SHORT1FROMMP(mpParm1)) {
   case TID_TRAFFIC:
      {
         HPS hpsWnd;
         PSPRITEINFO psiSprite;
         POINTL ptlPos;
         ULONG ulIndex;

         hpsWnd=WinGetPS(hwndWnd);

         psiSprite=findSprite(pidData->asiSprites,SIS_ENTERTOP,0);
         if ((psiSprite==NULL) && (rand()%pidData->lMod==0)) {
            psiSprite=findSprite(pidData->asiSprites,
                                 SIS_UNUSED,
                                 SIT_GOESLEFT);
            if (psiSprite!=NULL) {
               psiSprite->ulState=SIS_ENTERTOP;

               ptlPos.x=pidData->szlPlay.cx;
               ptlPos.y=pidData->szlPlay.cy/2+40;

               SprSetSpritePosition(hpsWnd,psiSprite->hsSprite,&ptlPos);
               SprSetSpriteVisibility(hpsWnd,psiSprite->hsSprite,TRUE);
            } /* endif */
         } /* endif */

         psiSprite=findSprite(pidData->asiSprites,SIS_ENTERBOTTOM,0);
         if ((psiSprite==NULL) && (rand()%pidData->lMod==0)) {
            psiSprite=findSprite(pidData->asiSprites,
                                 SIS_UNUSED,
                                 SIT_GOESRIGHT);
            if (psiSprite!=NULL) {
               psiSprite->ulState=SIS_ENTERBOTTOM;

               ptlPos.x=(-psiSprite->szlSize.cx);
               ptlPos.y=pidData->szlPlay.cy/2-60;

               SprSetSpritePosition(hpsWnd,psiSprite->hsSprite,&ptlPos);
               SprSetSpriteVisibility(hpsWnd,psiSprite->hsSprite,TRUE);
            } /* endif */
         } /* endif */

         for (ulIndex=0; ulIndex<MAX_VEHICLES; ulIndex++) {
            psiSprite=&pidData->asiSprites[ulIndex];

            if (psiSprite->ulState==SIS_UNUSED) {
               continue;
            } /* endif */

            SprQuerySpritePosition(psiSprite->hsSprite,&ptlPos);

            if ((psiSprite->ulType & SIT_GOESLEFT)!=0) {
               ptlPos.x-=DELTA_X;

               if (ptlPos.x+psiSprite->szlSize.cx<0) {
                  psiSprite->ulState=SIS_UNUSED;
               } else
               if (ptlPos.x<0) {
                  psiSprite->ulState=SIS_EXITTOP;
               } else
               if (ptlPos.x+psiSprite->szlSize.cx>pidData->szlPlay.cx) {
                  psiSprite->ulState=SIS_ENTERTOP;
               } else {
                  psiSprite->ulState=SIS_VISIBLE;
               } /* endif */
            } else {
               ptlPos.x+=DELTA_X;

               if (ptlPos.x>pidData->szlPlay.cx) {
                  psiSprite->ulState=SIS_UNUSED;
               } else
               if (ptlPos.x+psiSprite->szlSize.cx>pidData->szlPlay.cx) {
                  psiSprite->ulState=SIS_EXITBOTTOM;
               } else
               if (ptlPos.x<0) {
                  psiSprite->ulState=SIS_ENTERBOTTOM;
               } else {
                  psiSprite->ulState=SIS_VISIBLE;
               } /* endif */
            } /* endif */

            if (psiSprite->ulState!=SIS_UNUSED) {
               SprSetSpritePosition(hpsWnd,psiSprite->hsSprite,&ptlPos);
            } else {
               SprSetSpriteVisibility(hpsWnd,psiSprite->hsSprite,FALSE);
            } /* endif */
         } /* endfor */

         WinReleasePS(hpsWnd);
      }
      break;
   default:
      return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
   } /* endswitch */
   break;

You should notice the reference to pidData->lMod in the determination of whether a sprite is to begin entering the display area. This is a changing value which can be adjusted using the "+" and "-" keys; the code is trivial WM_COMMAND processing with the two keys mapped to accelerators.

The rest of the code is, honestly, initialization and termination processing (other than the "standard" PM code). Everything you need to compile I495.EXE is provided as I495.ZIP, which can be found in sources 0207.zip

Summary

This month we said our farewells to this series by examining the animation in a simple application - I495.EXE. And while it was said earlier that hard-coded animations are rare, I would like to conclude that, although rare, a good animation sequence can spruce up any "About box" (or any other part of an application) and is well worth the effort of coding it.

Any questions can be sent directly to me via email at the address listed at the end of this issue.