Sunday, 20 September 2015

Rendering sprites #3 (part 10)

So it is time for the next step and get some basic user interaction going. Before we get there however, lets look at some of the mechanics involved.

In our previous post we looked at the individual animations that make up the movements of our main character. One thing you may have noticed is that the sprite sheet I'm using has actually been scaled up to 200%. I'm not sure if that was done in the original game or by the person who extracted the graphics. I've left it as is as this is just to demonstrate the techniques and with todays high resolutions screens we would probably want to use higher quality images anyway.
There are however two key observations we can make from the sprites and the animations:
  1. Each animation should play in full and is then followed up by the next animation to create fluid motion. Some animations are split into multiple ones. Walking for instance has 3 animations, taking the first step, stopping at the end of the first step and continuing with the 2nd step. Play the first and 3rd in a repeated sequence and our character keeps on walking. If our character needs to stop moving after the first animation we need to play the second one to round the animation off, but we can stop on a dime if we're stopping after the 2nd step.
  2. If the character moves, the character moves a fixed amount. The movement is always a multiple of 32 (or likely 16 in the original game).So after taking one step (1st + 2nd animation) we'll have moved 32 pixels. If we take two steps (1st + 3rd animation) we'll have moved 64 pixels. I've not checked the animation for running yet but my guess is the movement will be either double or triple that.
    Similarly climbing or falling we're moving up (or down) 96 pixels (3 * 32).
There is a very important reason for these fixed movements, they align with the background used in the original flashback game and this makes it far easier to check what movements can be done. For instance for climbing we can simply check if we're on a background tile where climbing makes sense. It also improves game play as the player doesn't need to go through the frustration of being a pixel to far to the left or right. In this part of the write-up we won't worry about this yet but it will become clear in the next part.

The one animation that stands out is rolling but I think that is caused by either missing a part of the animation or that I've gotten something wrong (I am trying to reverse engineer the animations used here). I've left it in the source code for now but I will likely remove it once we go to the next part to ensure we only have animations that work with our background.

Going from one animation to the next 

So looking at our animations it is important to realize that only certain animations follow others and the animation that should follow is controlled by two other parameters:
  • What is allowed, i.e. we can't walk left if there is a wall
  • What input is given by the user
We'll leave the interaction with the environment for the next part and focus on user input first. We'll also limit ourselve to based keyboard input.

As far as making our engine aware of the keyboard input I've taken out my temporary keyboard interface from the previous part and exposed the glfwGetKey method through a call back. Remember I didn't want to put any GLFW specific code in the game engine itself. I'm not 100% sure I'm going to keep this working the way it does right now but it will suffice for the time being. This function simply checks if a specific key is currently being pressed. Unlike our previous example we're not reacting on the keypress itself but on the current state of the keyboard. GLFW should simply be keeping a keymap of the keys currently pressed so this check should be pretty quick.

Instead of programming all the individual keypresses and reactions to them we're going to use a lookup table with a state engine. A state engine is nothing more then a fancy word for defining in what state our character currently is in i.e. if he's standing still, walking, jumping, facing left or right, etc.

Our state engine has three variables that track it:
  • currentAnim, the current animation playing, which is equal to what our character is doing. This relates directly to our animation enumeration we already introduced in the last post.
  • currentSprite, the current sprite within the animation that is currently being displayed.
  • currentPos, the current position within our game world our character occupies.
After our animation finishes playing we'll adjust the position according to the movement recorded in our animation table and check our action lookup table for the next action/animation we want to invoke.

This lookup table is defined using the following struct:
// structure to control our characters with
typedef struct action_map {
  GLint     animationEnding;        // when this animation ends
  GLint     startAnimation;         // start this animation
  int       keys[5];                // if these keys are pressed (ignoring zeroes)
} action_map;

As we can see our action lookup table is defined by the animation that has just finished playing, the animation that should follow and up to 5 keys that the user should be pressing to make this happen.
We'll be checking our table top down and the first entry that matches our current state wins.

Here is a part of the table:
// and define our action map, the first action that matches defines our next animation
action_map conradActions[] = {
  // looking left
  CR_ANIM_LOOK_LEFT, CR_ANIM_TURN_LEFT_TO_RIGHT, { GLFW_KEY_RIGHT , 0, 0, 0, 0},                  // turn left to right if our right key is pressed
  CR_ANIM_LOOK_LEFT, CR_ANIM_WALK_LEFT_1, { GLFW_KEY_LEFT, 0, 0, 0, 0},                           // start walking right
  CR_ANIM_LOOK_LEFT, CR_ANIM_LOOK_LEFT, { 0, 0, 0, 0, 0},                                         // keep looking left if no key is pressed
  CR_ANIM_TURN_LEFT_TO_RIGHT, CR_ANIM_LOOK_RIGHT, { 0, 0, 0, 0, 0},                               // turn to looking right once finished

  // walking left
  CR_ANIM_WALK_LEFT_1, CR_ANIM_WALK_LEFT_2, { GLFW_KEY_LEFT, 0, 0, 0, 0},                         // keep walking left
  CR_ANIM_WALK_LEFT_1, CR_ANIM_STOP_WALK_LEFT, { 0, 0, 0, 0, 0},                                  // stop walking left if no key is pressed
  CR_ANIM_WALK_LEFT_2, CR_ANIM_WALK_LEFT_1, { GLFW_KEY_LEFT, 0, 0, 0, 0},                         // keep walking left
  CR_ANIM_WALK_LEFT_2, CR_ANIM_LOOK_LEFT, { 0, 0, 0, 0, 0},                                       // finished walking left if no key is pressed
  CR_ANIM_STOP_WALK_LEFT, CR_ANIM_LOOK_LEFT, { 0, 0, 0, 0, 0},                                    // finished walking left if no key is pressed

Now this is only a small section of our table but lets look at these entries more closely to try and explain how this works.

The first entry applies when our current animation is CR_ANIM_LOOK_LEFT which is a single frame animation of our character looking left. The animation that follows is CR_ANIM_TURN_LEFT_TO_RIGHT which is an animation that makes the character turn around. The key listed is GLFW_KEY_RIGHT which means this animation is triggered if the user is pressing the right arrow key. Note that we don't care about any other keys but also that we don't check any other keys. I.e. if they user is pressing the right and up key this is still the first entry that matches and this is what will happen. The order in which the animations are specified in this table are thus very important.

The second entry triggers our walking animation. If the character is currently standing still looking left (CR_ANIM_LOOK_LEFT) and the user has the left arrow key pressed, we start our walking animation.

The third entry is the action if the character is looking left and the user has no (matching) keys pressed. We'll just keep playing the same standing still animation over and over again until something else happens.

The next set of entries define the walk itself. These go through the combinations of cycling through the 1st (CR_ANIM_WALK_LEFT_1) and 3rd (CR_ANIM_WALK_LEFT_2) animations we talked about in our introduction above. If the user lets go of the left key depending on which animation is currently playing we either directly got back to standing still (CR_ANIM_LOOK_LEFT) or we first play our stop walking animation (CR_ANUM_STOP_WALK_LEFT).

A lookup table like this lets us easily add in more animations and how we should react to the users input.

Note that I have no way of knowing if the original game uses a similar approach or something very different. Also I've kept things simple, there are a few easy optimizations to speed things up, especially animations defined later in the table mean we have to do a lot of tests before we end up at the right one but that is for another day.

Our new update routine


Our final step is actually reacting to our keyboard commands and making it all work. For this we've enhanced our engineUpdate routine. The start of our routine is still pretty similar. I've changed the animation speed to roughly 12 frames a second but other then that we simply cycle through the sprites. Once our animation recycles we set animReset and do our keyboard check.

This part has changed:
    // if our animation was reset we have a chance to change the animation
    if (animReset) {
      int nextAnim = -1;
      
      // if our current animation was played forward, we add our movement
      if (conradAnim[currentAnim].firstSprite<=conradAnim[currentAnim].lastSprite) {
        currentPos.x += conradAnim[currentAnim].moveX * SPRITE_SCALE;
        currentPos.y += conradAnim[currentAnim].moveY * SPRITE_SCALE;        
      };

Once our animation has finished we need to check if we need to add the movement to our current position. We only do this if our animation was played in the normal direction.

      // check our action map
      for (int a = 0; a < MAX_ACTIONS && nextAnim == -1; a++) {
        if (conradActions[a].animationEnding == currentAnim) {
          // assume this will be our next animation until proven differently
          nextAnim = conradActions[a].startAnimation;
                    
          // check if we're missing any keys
          for (int k = 0; k < 5 && nextAnim != -1; k++) {
            int key = conradActions[a].keys[k];
            if (key == 0) {
              // no need to check further
              k = 4;
            } else if (!engineKeyPressedCallback(key)) {
              nextAnim = -1;
            };
          };
        };
      };

      // if -1  We're missing something
      currentAnim = nextAnim == -1 ? 0 : nextAnim;
      currentSprite = conradAnim[currentAnim].firstSprite;

This is our main logic scanning our table until we found the correct animation and keypresses. We just loop through our entire table until we find the right one. If we can't find anything our table is incomplete, oops, so we just default back to our first animation. 

      // if our new animation is going to be played in reverse, we subtract our movement
      if (conradAnim[currentAnim].firstSprite>conradAnim[currentAnim].lastSprite) {
        currentPos.x -= conradAnim[currentAnim].moveX * SPRITE_SCALE;
        currentPos.y -= conradAnim[currentAnim].moveY * SPRITE_SCALE;        
      };

Finally, if we're playing an animation in reverse we need to subtract our movement first. I've only used this for backwards rolls.

If you compile and run the sample application at this point in time you should be able to make Conrad walk left and right, roll, and making him climb. He still lacks a way to get back down :)


Download the source code here

So where from here?

Well the next step is putting a proper background in place and make Conrad interact with it so he can only climb where it makes sense and drop back down.

It may take awhile before I get this done. First off I need to spend some time on getting the graphics sorted out but I've also got a few other things on my plate so I may not be working on this any time soon.






Saturday, 5 September 2015

Rendering sprites #2 (part 9)

So we're taking baby steps going forward. First because I want to detail how I'm slowly progressing to something that allows the user to fully control the character, but also because time is too short to do it all at once:) I'm trying to find and use every spare few minutes I can find between family, work and playing games. Owh and I'm still hard at work building a HALO suit, that's turning out a much larger project then I initially thought.

First off, I made a few more changes on the work done so far just to complete it. I ended up enhancing spritesheet.h so that sprites are centered but you add an offset to position it properly. This also allows the animation to give flexibility on the movement.

Our sprite struct thus is now defined as:
// structure to keep data about our spritesheet
typedef struct sprite {
  GLfloat left;                   // left position of our sprite in our texture
  GLfloat top;                    // top position of our sprite in our texture
  GLfloat width;                  // width of our sprite
  GLfloat height;                 // height of our sprite
  GLfloat offsetx;                // horizontal offset
  GLfloat offsety;                // vertical offset
} sprite;

Our array of sprites now defines about the first 4 rows in our spritesheet. That is as far as I'll go for now. Eventually I'll add the climb in because I need that for this little demo but I'll leave most of the other animations alone.

Defining animations

The next step is to bring some structure to our animations. We're going to break up all the animations into small pieces that can be sequenced depending on the users input. For now we'll just switch between them so we can see if we've got them right but eventually after each animation ends we'll check for user input and determine what the next animation is.

I've define a struct in engine.h that allows me to define one animations sequence:
typedef struct animation {
  GLint     firstSprite;            // first sprite of animation
  GLint     lastSprite;             // last sprite of animation
  bool      flip;                   // flip sprite horizontally
  GLfloat   moveX;                  // add this to our X after anim is finished
  GLfloat   moveY;                  // add this to our Y after anim is finished  
} animation;

To make it easier to use my animation I've also added an enum to engine.h that lists all the different animations:
enum conrad_animations {
  CR_ANIM_LOOK_LEFT,
  CR_ANIM_TURN_LEFT_TO_RIGHT,
  CR_ANIM_LOOK_RIGHT,
  CR_ANIM_TURN_RIGHT_TO_LEFT,

...

  CR_ANIM_JUMP_UP_LEFT,
  CR_ANIM_COME_DOWN_LEFT,

  CR_ANIM_COUNT
};

The animations themselves are then defined in an array in engine.c:
// now define our animations
animation conradAnim[] = {
    0,    0,  false,   0.0,  0.0,    //  0 - look left
    0,    6,  false,   0.0,  0.0,    //  1 - turn from looking left to looking right
    6,    6,  false,   0.0,  0.0,    //  2 - look right
    6,    0,  false,   0.0,  0.0,    //  3 - turn from looking right to looking left

...

   50,   66,  false,   0.0,  0.0,    // 16 - jump up (facing right)
   67,   78,  false,   0.0,  0.0,    // 17 - coming back down (facing right)   
};

The code for displaying the animation hasn't changed much. We're just cycling through a single animation instead of all defined sprites.

I've turned off the back ground for now and I'm displaying all sprites in the current selected animation on the left and right slightly overlapped to help with changing the offset of each sprite. Until we actually start with controlling the character we won't know if we've gotten these right.



The final change at this point is changing the animation. I've removed the camera control (this will be changes anyway) and for the time being am sending key presses to the engine (again I'm trying to leave any GLFW specific code out of the engine). In the engine I'm simply keeping a stack of unhandled keypresses which I check at the end of each animation. This won't be the final solution for when we start to control our character but it does the job for cycling through the animations without loosing keypresses when the animation hasn't finished yet.

Download the source code here

So where from here?

The next step is simple, to react on keyboard input and actually control the character.

At first we won't worry about any character interaction with his surroundings and implement basic walk left, walk right, roll left, roll right and straight jump logic. I may through in a few more animations just for the heck of it.

After that we'll add a more appropriate background and start looking at properly interacting with it.

Thursday, 3 September 2015

Making timer and worker events in Omnis safe

Timer events and the new worker objects in Omnis Studio are two very powerful functions in Omnis. Both use the same internal logic to kick of logic at a given time, for timers this will happen at either fixed intervals or when a specific amount of time has passed, for worker objects the run logic once results have been received from the database in a background thread and the data is ready for the application to deal with.

This however is not without consequence. These events can interrupt whatever code is currently running in the foreground thread and as a result introduce very hard to find bugs if the interruption changes something the code being interrupted wasn't expecting.

TigerLogic have done a good job to ensure these interruptions don't happen willy nilly as code can only be interrupted at specific times. It is however unclear what interruptions are possible. After some experimentation I've found the following scenarios will be interrupted by timer and worker code:
- when the main thread is paused on an enter data command
- at the end of a loop
- when constructing a new window
I'm sure I've forgotten some others.

Generally speaking this works great unless you rely on globals.

The main exception to that however are worker object. They have one big drawback, a worker can not interrupt another worker. Omnis tends to crash when that happens. If you have more then one worker active at a given time and you're in the middle of processing the results of the first worker object (which you'll likely do within a loop), the 2nd worker can interrupt that loop as it has finished retrieving its result set. 9 out of 10 times this will crash your application.

It is therefor important to check if you are interrupting code that could potentially lead to issues and delay the action till later. At the start of my $completed function for my worker I cache the result set and check whether I can process it. If so I process it right away, if not I start a timer to check if I can process it a short time later.

In both my worker $completed code and timer $timerproc code I call the following functions that I've placed in a code class:
cCode/ERR
  ; Bypass error handler, we ignore any errors while this is active
  SEA continue execution

cCode/CanRunTimer(pvExpectedLineCount = 2, pvSendToTraceLog = kFalse)
  Begin reversible block
    ;  Temporarily replace our error handler with a dummy one, sys(192) tends to send bogus issues to our error handler..
    Load error handler cCode/ERR
  End reversible block

  Calculate lvList as sys(192)
  If lvList.$linecount<=pvExpectedLineCount
    ;  We can run it, just $event/$completed/$timerproc and CanRunTimer are on the stack
    Quit method kTrue
  Else If pos('Enter data',lvList.[pvExpectedLineCount+1].linetext)=1
    ;  We can run it, we've interupted an enter data!
    Quit method kTrue
  Else If pos('ALLOW TIMER',lvList.[pvExpectedLineCount+1].linetext)<>0
    ;  Our comment says we are allowed to interupt here!!
    Quit method kTrue
  End If

  ;  !BAS! Timer can interupt END FOR, END WHILE, and some other situation. We DON'T want this as we may be in the middle of processing stuff!
  If pvSendtoTraceLog
    Calculate lvList.$line as pvExpectedLineCount+1
    Send to trace log (Diagnostic message) {Can't run queued timer, stackcount = [lvList.$linecount], interupts [lvList.classitem().$name].[lvList.method]/[lvList.line] = [lvList.linetext]}
  End If
  Quit method kFalse
This method will return true if I can safely process my timer code, and false if I should delay the action.

There are only 3 situations I deem safe:
- if all that is on the stack is my $event, $completed or $timerproc method and my CanRunTimer
- if we are interrupting an "enter data" command
- if we are interrupting a command that contains the "ALLOW TIMER" text (which I can put in a comment)

I've also made it optional to sent any other situation as a diagnostic message to the trace log. If a timer seemingly never runs I can check if I'm being too strict in letting timers run their course.

A timer method would then look something like this:
Timer.$event
  On evTimer
    Do code method cCode/CanRunTimer Returns lvCanRun
    If lvCanRun
      ;  Do your stuff
      ...
    Else
      ;  Restart our timer (omit this if you have $autoreset turned on)
      Calculate $cinst.$objs.Timer.$timervalue as 200 
      Do $cinst.$objs.Timer.$starttimer()
    End If

One last note on the subject. My workers tent to start a timer that delays processing of the results for 1ms regardless of any other running code. First of it means that I only need to implement this check in one place but more importantly it allows $completed to finish and clean up. Workers will also crash Omnis if you start another worker or do any other form of communication with the database even on completely unrelated session objects. By allowing $completed to finish and handling the result set when the timer object fires you gain a lot of stability in your application.