Moving our sprite

Although the bitmap we have is never going to be much fun, it would be a lot better if we could move it around the screen. This requires three changes to the script:

We need to store an X position and a Y position for the sprite on the window

We need to detect the cursor keys being pressed so that we can change the X and Y values

Once we've done this, it will become apparent that a third change is required - we will get to that soon.

Storing an X and Y position is simple - we want to start the sprite in the top-left hand corner of the screen, so we just need to store X and Y values somewhere. Given that we already know we have to provide X and Y values as the fourth parameter to phpSDL_BlitSurface() to specify where the surface should be copied to, we can just store them in there. So, just after our call to phpSDL_LoadBMP(), add these two lines:

$dest['x'] =0
$dest['y'] =0;

Then, edit the call to phpSDL_BlitSurface() to this:

phpSDL_BlitSurface($bmp,NULL,$video,$dest);

Now we pass the new $dest array as the fourth parameter, which means all we have to do to make the square move is change the X and Y elements in $dest. How can this be done? Well, we need to implement a more complicated game loop - take a look at this new code:

Okay, first notice that this time we use a $done variable to decide whether to exit the main loop. This is because we no longer want our key press event to exit - we want to utilise it to control the position of our bitmap. Also, this time we define $event as an array and pass it into phpSDL_PollEvent() as its only parameter - this lets us query the contents of the event to find out more about it. Unsurprisingly, that is exactly what the code does - inside $event there is a "type" element that contains a special constant that defines what type of event just happened.

There is a big list of event codes you can check against, but right no we're only interested in two SDL_QUIT and SDL_KEYDOWN. SDL_QUIT will be the event type when we close the program by press Alt-F4 or some other way. SDL_KEYDOWN will be the event type when, unsurprisingly, we've hit a key. Note that no other events will come through as we still have the majority of them set on ignore mode from earlier.

The SDL_KEYDOWN match is not where we're controlling the movement of the square, though - notice how we have to poll the event list in a loop? This is because it is possible that there are hundreds of events waiting to be processed, so we have to keep whizzing through them like that to make sure we process them all. If we tried to process the cursor keys being pressed using this loop and our user just held one of the cursor keys down, the events would be added as fast as we could handle them, effectively never getting out of the phpSDL_PollEvent() loop and therefore never actually drawing the scene!

So, we only handle the user hitting Escape using SDL_KEYDOWN. In the code above, hitting Escape sets $done to 1 and therefore exits. Note that the whole ['key']['keysym']['sym'] thing is just fluff that allows us to access the all-important constant value - it is buried quite deep because pressing a key is actually quite complicated. If you really want to get deep into your code, you can try out some of the other values set inside $event for SDL_KEYDOWN - in place of 'sym', you can use 'mod' for example to get information on whether any modifier keys were held down such as KMOD_SHIFT for the shift key or KMOD_RALT for the right Alt key. Generally, though, just getting the keycode constant value is enough!

After the event loop, however, we get down to handling the key presses. phpSDL_GetKeyState() is a great function that returns an array with an element for pretty much every key on your keyboard - if the key is pressed, the value of the element is 1, otherwise it is 0. As with event types, there are many constants available for the keys on your keyboard, and we're using SDLK_UP, SDLK_DOWN, SDLK_LEFT, and SDLK_RIGHT to handle the cursor keys being pressed. You need to pass into phpSDL_GetKeyState() a reference to a variable, and this will be set to the number of keys returned - this is pretty much irrelevant.

We capture the return value, the list of which keys are pressed and which aren't, and store it in $keys, then we have four if statements to handle the four cursor keys. If SDLK_UP is set to 1, that is if the up cursor key is pressed, we decrement our Y value and thereby move the sprite up the screen. If SDLK_RIGHT is set to 1, we increment X, thereby moving the sprite to the right. As these four tests are individually done, users can press up and right at the same time to make the sprite move diagonally as we will see soon.

There is just one more tweak we have to make before we've completed parts 1 and 2 of our changes, and that is because there appears to be a minor bug in the PHP SDL extension. The $dest array we're using to pass in the X and Y values to phpSDL_BlitSurface() also has W and H values that specified Width and Height of the surface. Usually in SDL these default to 0 meaning "it does not matter", but in PHP SDL you need to set them yourself otherwise it will inexplicably crash. So, setting $dest to its initial values should look like this:

$dest['x'] =20;$dest['y'] =20;$dest['w'] =0;$dest['h'] =0;

It is only a small thing, but be sure to keep it in mind otherwise your programs may fail with little explanation. Note that an error like "Fatal signal: Segmentation Fault (SDL Parachute Deployed)" is the standard "SDL has crashed" error message and usually means you have screwed up in your script somewhere. You'll get this error message if you do not set the W and H in the $dest parameter for phpSDL_BlitSurface(), for example. You've been warned! Now, with all this in mind, go ahead and re-run the script and you should discover what our third change will be!

As you can see from the picture above, and hopefully also from your own script, we can now move our sprite around freely. However, and this is quite a big whoopsy on our behalf, the sprite leaves a big trail behind it as it moves around because we do not clear the screen before each frame is drawn.