code of the Ninja();

// in search of swift, efficient, and invisible code

2009-12-08

Sinusoidal Motion

Welcome, Code Ninjas!

This time we'll be looking at a simple script that makes certain motions look more natural.

Imagine you have a platform that you'd like to move back and forth, a common element in platformer games. You might use code something like this:

Create Event:

r = 64;//maximum distance in pixels the platform may travel from its origin before reversing direction

s = 1/32;//speed of the platforms. The divisor is how many steps you wish the platform to take to reach its maximum distance from its origin.

Step Event:

if a //moving forward...
  {
  p += s;
  if p >= 1 { p = 1; a = 0; }
  }
else //...and moving back
  {
  p -= s;
  if p <= 0 { p = 0; a = 1; }
  }

x = xstart + r*p;//update platform's position
xspeed = x-xprevious;//get platform's speed in pixels

(Note: If this code seems a little overcomplicated, it is because it had been purposefully written to be able to make platforms of any speed and range.)

This effect that this code achieves is a platform that moves away from its starting point at the specified speed, reaches its maximum distance, and then immediately reverses direction and trots back to repeat the process indefinitely. This is the sort of platform you'll often see in early 8- and 16-bit games.

The trouble with this kind of motion is the immediate reversal of motion. In one step, the platform can be moving with a speed of +5 pixels, and the in the next, with a speed of -5 pixels. This doesn't look very realistic, because in reality, most often when something reverses direction, it has to slow down to a halt, and then begin to accelerate again.

This jerky motion isn't so bad if the platform's "patrol area" is bounded by walls at its extremes - then it just looks like the platform is bouncing off of the walls, and the motion doesn't look too bad. But if the platform is floating in mid-air, as they often are, there is nothing that appears to plausibly reverse it, and the motion looks unnatural.

And it's worse than just looking unpleasant. It actually makes the game less fair, and less fun. If the player can't tell by some visual cue when the platform is going to decide to turn around, they have a much harder time getting the proper timing on their jump. They will have to use more trial and error, watching the platform make its rounds more than once before they can confidently make their move.

This is almost game-breaking if you want to keep good flow, as in a Sonic game. In the Sonic the Hedgehog games for the Mega Drive (Genesis), almost all platforms not bounded by walls move with a natural motion - decelerating as they reach their extremes, and accelerating toward their point of origin as they turn back around. This allows players to intuit exactly where the platform will be any time when they first come across it, without patient study of its entire cycle. This is one of the subtler points about the Sonic the Hedgehog game, seldom recognised, but it contributes not insignificantly to the sense of speed that made them popular.

Well then, how can we achieve the same effect so that our platforms move with a natural motion, rather than an outdated, unrealistic, and unfair jerky one? Why, with the trusty cosine function, of course!

Imagine, now, a platform that - instead of moving simply up and down, or left and right - moves in a complete circle, as many do in Sonic and Mario games. The platform's speed should be uniform, but if we were to look at just one component of its velocity - say, just the xspeed, or just the yspeed - we would notice acceleration and retardation of its speed. Simply imagine looking at the platform's circular path edge on, instead of face on. It would appear to be moving in a straight line, but slowing down at the edges and speeding up in the middle, just like those Sonic platforms we want to emulate.

So, in effect, what we want to do is make the platform move in a circle - just a really flat circle that might as well be a line. We'd use code something like this:

Create Event:

r = 64;//maximum distance in pixels the platform may travel from its origin before reversing direction (radius of the circle)

s = 180/32;//speed of the platforms. The divisor is how many steps you wish the platform to take to reach its maximum distance from its origin. (This time we use 180, not 1, because we'll be using degrees.)

Step Event:

a += s;
if a >= 360 a -= 360;
//alternatively the preceding two lines could be 'a = (a+s) mod 360;'.

//also, here we don't need to use two states, forward and back, because the circular motion takes care of that for us automatically.

x = xstart + r*cos(degtorad(a));//update platform's position
xspeed = x-xprevious;//get platform's speed in pixels

To make the platform move vertically, we can just replace all the references to x and make them y. Or, to make it actually move in a perceptible circle, we can have both sets of lines. By using a different range value for both x and y, you can squash the circle into any sort of ellipse you want - for example, a circle that is twice as wide as it is tall:

Create Event:

xr = 64;
yr = 32;

Step Event:

//...

x = xstart + xr*cos(degtorad(a)); xspeed = x-xprevious;
y = ystart - yr*sin(degtorad(a)); yspeed = y-yprevious;

Remember to subtract the sine for y, and add the cosine for x, otherwise instead of moving circularly, it'll just move in a smooth diagonal - which is actually another useful effect you might want to achieve.

Well, that about does it for platforms, but the power of sinusoidal motion goes far beyond. There are other applications, and for just such an example, I'll use the purpose for which I first I needed it myself.

In games like Phantasy Star, when you talk to townsfolk or shopkeepers, their dialogue appears on the screen inside of bordered window, or a 'text box'. In most games, the text boxes appear on the screen gradually, either opening up, dropping down, or fading in.

I wanted this animation to appear smoother, so I thought perhaps I could apply sinusoidal motion as the solution. But there was a slight hitch.

Imagine you want to fade in a window, from an alpha of 0 (invisible) to an alpha of 1 (fully opaque). You could simply add 0.1 for ten steps, but that wouldn't look very smooth. How about, instead, we use a sine function.

Code:

step = 18;

for {a=0;a<=180;a+=step}
  {
  alpha = sin(degtorad(a));
  }

Well, clearly this won't work. The value of alpha will go from 0 (the sine of angle 0), accelerate toward 1 (the sine of angle 90) and decelerate back to 0 again (the sine of angle 180). The text box wouldn't fade in, it would fade in and back out again just as quickly! This obviously isn't what we want.

But why not just use 90, instead of 180, so that alpha will stop at 1, thereby fading the text box in how we want it? Well, in that case, the fade would start smoothly, but stop abruptly. I wanted it to both start and stop smoothly.

I needed some way to have the alpha value "move" like a half-circle (slow start and stop), but only "traverse" a quarter-circle (start at 0 and end at 1).

So I made a script, called it 'sinusoidal()', and this is the function I used:

sinusoidal()

//argument0: any value between 0 and 1

return (cos(degtorad(180-(180*argument0)))+1)/2;

Now, sinusoidal motion can be employed anywhere by calling the script. The text box fade code ends up looking something like this:

Code:

step = 0.1;

for {a=0;a<=1;a+=step}
  {
  alpha = sinusoidal(a);
  }

This little script can be very versatile. You can use it to slide logos or menus onto the screen. You could use for flashing lights for smoother look. You could use it to animate a pendulum. You could even use it to make your character push a block (as Link does in the Zelda games) with a less abrupt and better looking motion. And with clever modification, who knows to what ends a Code Ninja might put it to.

For an example GMK illustrating the difference between normal and sinusoidal motion in several types of movement and animation (flashing, shrinking, swinging, sliding), click here.

Next time we'll be looking deeper into text boxes - how to make the text type out, change the text speed, and more. Until then, happy coding, Code Ninjas, and happy holidays, too!

2009-11-13

Watchers

Welcome, Code Ninjas!

Today I have a simple fix for something that bugged me about Game Maker. First, let me tell you about the problem, and then I will move on to the solution.

Many times I would be running my game, and realise that there was some variable I needed to know the value of. Game Maker allows you to list the values of variables that you specify in a readout, but only when running in Debug Mode.

The trouble is, once you are already running your game, there is no way to dynamically switch to Debug Mode. You have to close the game, and run it again. This is kind of an annoyance, because compile time can get quite lengthy.

So, I thought, is there any way to have a list of "watchers" built into the game, that I can invoke and dismiss at will?

At first I used a simple method that worked much like any HUD (heads up display) in a video game. The values I needed to keep an eye on were just printed on the screen. This was fine and all, but there was no good way to add to it while the game was running. Game Maker's built in Debug Mode allows you to add watchers at any time.

Then I got the idea to - instead of printing out the values of a fixed set of variables - read the variables from a ds_list.

This ds_list would contain a series of strings, each of which would contain the name of the variable to watch (mouse_x, image_speed, etc). By right-clicking the list, I would bring up an input box that would let me add a new variable name to the ds_list.

How can you read the value of a variable by its name? Game Maker helpfully includes the functions variable_local_get(), and variable_global_get(), which take as their only argument the variable's name in string format, and return the value of the variable.

However, there are still issues with this method. A) You can only return the contents of variables, not expressions. This means you can print out the value of mouse_x and things like that, but never the value of instance_nearest(PlayerObj), and other such useful functions. B) You can't tell whether a variable is global or local, a constant, or part of an array, so you're pretty much screwed.

But then I had the brainwave. Instead of using variable_local_get() and its ilk, I'd use the execute_string() command!

Using this method, the ds_list could be filled with strings containing variable names, expressions, anything - using the identical syntax as the Game Maker Debug Mode watchers use. In fact, I could even save and load the strings to TXT documents in the same format, making them fully compatible with Game Maker's normal Debug Mode.

When drawing the watchers onto the screen, all I have to do is read the strings from the list. Then, I use the execute_string() command to perform the string as if it were code. By prepending "return" and a space before the string when I do this, execute_string() will return the value, which can be then drawn on the screen.

Code will demonstrate this better:

Code:

//a for loop that steps through the whole list
for (t=0;t<ds_list_size(watchlist);t+=1)
  {
  //the code string in the list to be executed
  r = ds_list_find_value(watchlist,t);
  //align the text to the left
  draw_set_halign(fa_left);
  //print the code - this is how you'll identify the watcher
  draw_text(8,8+16*t,r+":");
  //return the result of the watcher
  r = execute_string("return "+r);
  //align the text to the right
  draw_set_halign(fa_right);
  //print the result of the watcher
  draw_text(192,8+16*t,r);
  }

Now we have a watcher display that's just as nice as the Game Maker one. With a little bit of polish, you can add functions for adding, removing, replacing, and editing watchers from the list, as well as saving and loading lists to TXT documents.

For Code Ninjas who may need a working example to make sense of all this, here is a zip file which contains a GMK example and a sample TXT document of watchers.

Happy coding!

2009-09-25

The Nitpicker's Guide to Sonic Genesis - Part I

Hello again, Code Ninjas, and welcome to the first ever Code of the Ninja special, The Nitpicker's Guide to Sonic Genesis - Part I.

Some Code Ninjas are a disgrace to their title - they fail spectacularly at our subtle art. Perhaps they lack the necessary commitment or training. Or, perhaps they are not entirely to blame, and the reason for their failure is a lack of time, or budget.

Either way, the results of their efforts suffer terrible scars, belying the shoddy and haphazard code underneath. This is unacceptable, for the Code Ninja should be swift, efficient, and invisible.

The outstanding example of such an unsuccessful mission is Sonic the Hedgehog Genesis, for the Nintendo Game Boy Advance. It is supposed to be a port of the 1991 Sonic the Hedgehog for the Sega Genesis (Mega Drive), but you'd barely know it. Whereas the original Sonic the Hedgehog is an exemplar of good programming by a true Goemon of code, this embarrassing port is a shambles, infamous for being the worst Sonic game ever. In fact, it has a strong claim to be the worst programmed video game ever (a distinction a certain Bubsy Bobcat is used to enjoying).

In this special series of Code of the Ninja, I aim to draw attention to each of Sonic Genesis's plenitude of flaws, with special emphasis on their likely causes. It is one thing to notice that Sonic Genesis is bad - it is entirely another to find out why. It is a testament to the degree of the abject failure of the Sonic Genesis programmers that the likely causes of the many glitches in the game are not opaque.

To be sure, I cannot be 100 percent certain of any of the causes I will cite. I do not have access to the programmers' code, nor the inner workings of their brains (and I'm grateful, for they would assuredly be terrifying), but I can make educated guesses. As a Ninja whose current mission plants him squarely in the wilds of his own Sonic engine, I am in a better position than most to make such observations.

As in the infancy of the discipline of taxonomy, before the advent of the field of genetics, one simply looked at the external features of a lifeform when classifying it. The underlying coded information, the recipe for those external features, was invisible to taxonomists at the time, just as Sonic Genesis's code is unavailable to me.

They made mistakes, certainly, especially because of the wonderful yet maddening effects of convergent evolution, but plenty of good work was done, enough to cement the endeavor as respectable.

It is in this spirit that I undertake nitpicking Sonic Genesis. Whether all of my evaluations turn out to be true or false, I hope many of them will be incising insights, which will arm inchoate Code Ninjas and help them avoid the same traps and pitfalls (some of which the Sonic Genesis programmers' feet are still sticking out of, accompanied by contented digestive noises).

As a bonus, I will be pointing out some extra flaws each time which were not the result of programming.

Code Flaw #001: Sonic is not synched to moving platforms

Original:

GBA:

Programming moving platforms in a video game is actually relatively easy. When the character object detects a platform, it remembers the ID of the platform. From then on (until the character falls or jumps off the platform), the platform's motion is simply added to the character's.

Sounds easy enough. But a lot of beginners (including me, back in the day) are surprised to discover upon running their game, that the character's movement is not perfectly synchronised with that of the platform.

It turns out that it all relies on the order in which the code is performed. Every frame of the game (and there are usually 60 per second), the objects perform their code. But they can't do this at the same time - they queue up and do it one after another.

If the platform moves first, then Sonic follows suit. Then the screen is refreshed, and the player sees Sonic stuck fast to the platform. All is well.

But what if the platform comes later in the queue than Sonic? Then, Sonic moves based on the speed or position that the platform had in the last step. Then the platform moves to its new position. Then the screen is refreshed. The player sees Sonic juttering about the general vicinity of the platform, but not firmly atop it. Sonic is lagging behind, basing his position on variables that are one frame out of date!

Unless all moving solids complete their code before the character object's routine is run, this will be a problem. In Game Maker, this would amount to putting the platform routines in the "Begin Step" event.

Apparently the "programmers" of Sonic Genesis were too rushed or lazy to bother with this simple fact, and so they fail to achieve decent moving platform physics - something that early NES games can do in their sleep. It's pretty pathetic, when you think about it.

Bonus Flaw #001: The background in the title screen isn't animated

Not only is there no paralax, and the clouds don't blow by on the breeze, but the waterfalls and sparkles on the surface of the lake are totally frozen! The GBA can palette cycle, so there seems to be no explanation for this besides sheer sloppiness.

Bonus Flaw #002: There is no shrapnel when crushing through walls

Yes, folks - the segments of rock (or metal, in Starlight Zone) simply disappear, accompanied by a lame "poit" sound effect that is nothing like the original. I'm guessing that the 6 month delay still wasn't enough time to implement a few bits of shrapnel flying away.

Well, that's it for now. The normal Code of the Ninja will not be interrupted by the Nitpicker's Guide, so I'll see you next time.

Happy coding!

2009-09-05

Smart Triggers

Welcome to another lesson, Code Ninjas! This time I'll be demonstrating a game design concept, and not actual code. It's pretty simple, actually, but it requires a bit of backstory.

In The Legend of Zelda: The Windwaker for the Gamecube, you can target enemies with the L trigger. They call this L-Targeting. In the options menu of the game, you can change the behaviour of the L-Targeting. The two settings are Switch and Hold. The difference between them is thus: In Switch Mode, you begin targeting by pressing and releasing the L trigger once. You then stop targeting by pressing and releasing the L trigger again. In Hold Mode, you begin targeting by pressing and holding the L trigger down, and you stop targetting by releasing the L trigger.

Now, this option is an important one. I use Hold Mode, myself, and play miserably in Switch Mode. Some of my friends, however, use Switch Mode, and perform admirably. One mode isn't really better than the other. It all depends on the type of player.

However, when using either mode, sometimes things still don't work out so well. For instance, in Hold Mode, during long battles against one enemy, your finger can get tired out squeezing the trigger the whole time. In Switch Mode, in battles with many enemies, pressing the L trigger again sometimes cycles to the next enemy instead of ceasing to target altogether. This makes terminating a confrontation and retreating a confusing process. Continually switching between modes through the option menu would be tedious, though, so a player tends to pick one mode and stick with it, warts and all.

While thinking about these issues, I thought of a simple third setting, which I called Smart Mode. Perhaps it does not solve all of the problems, and I can't quite test it out in Windwaker, but here is how it would work.

Basically, when a press of the L trigger is detected, a timer begins. In Game Maker, you would use an alarm event, or increase a variable every step. Anyway, then when a release of the L trigger is detected, one of two things would happen:

1 - If the timer was below a certain time (say two-thirds of a second, about), you wouldn't stop targeting. It's rare that a player would want to target something for so short a time. At this point, it would require another press of L to cease targeting.

2 - If the timer was above that time, then you would cease targeting. In this way, a natural quick press and release of the trigger would enter Switch Mode, and pressing and holding down the trigger would enter Hold Mode. In a way, both modes would be available to you at any time, without having to change anything in the options menu. The computer could detect which you wanted it to be based on how you pressed the button. It is for this reason that I call it Smart Mode. It's like the computer knows what you're thinking.

This "smart toggling" system could be used for anything. It doesn't have to be targeting in a 3D adventure game. It could be used for opening and dismissing a Seiken Densetsu style menu ring, or activating a protective shield, or even for changing between two different weapon types in an action sidescroller.

And I'm sure that you Code Ninjas could think of many more applications that I couldn't. So, think about how you might add "smart" triggers or toggles to your game. It might make it a little more user-friendly.

Happy coding!

2009-08-07

At the Movies

Welcome back, Code Ninjas!

Last time, we learned how to implement a joypad system using binary values. Now we're going to be expanding on that a little by adding some scripts for recording joypad "movies".

Joypad movies can be useful in many ways. For one, you can easily record yourself playing a level of your game to make a demo, which might play if the player lingers on the title screen for too long. Or, you might allow players to record themselves, and share these movies as a way to show off their skills.

But they are also useful to you, as the game developer. If there is a bug or unexpected behaviour in your game, just start recording the joypad, and then trigger the bug. Now you can replay that sequence over and over until you determine the source of the bug. Then, once you think you've fixed the bug, you can play it once more to make sure that it's absolutely gone.

Also, if you want to record a true video movie of your game, for instance to make a YouTube trailer, many programmes that record video of the game window will slow down your game. If you record a joypad movie first, and then make a video of the joypad movie being played back, you won't have to actually be playing the game while it's slow.

Well, let's get to it!

Joypad movies will be saved to binary files. Our first script is save_joypad_file, which will set things up to start writing input to a file. It takes one argument, which is the name of the file to write to.

save_joypad_file()

close_joypad_file();
joy_mode = 2;
jfile = file_bin_open(argument0,1);
file_bin_rewrite(jfile);

You'll notice that the first thing it does is perform the script close_joypad_file. That's our next script. It takes no arguments.

close_joypad_file()

if joy_mode > 0
  {
  joy_mode = 0;
  file_bin_close(jfile);
  }

This script has to be called first so that if you call save_joypad_file while it is already recording or playing back a joypad movie, it will close the first one automatically.

It is also important to call close_joypad_file in the game end event of the Joypad object.

Next, let's write our next script, open_joypad_file. Like save_joypad_file, it takes one argument - the name of the file to read back.

open_joypad_file()

close_joypad_file();
joy_mode = 1;
jfile = file_bin_open(argument0,0);
jsize = file_bin_size(jfile);

You'll have noticed by now that the preceding three scripts all set the variable joy_mode to a value - 0 for normal, 1 for playback, and 2 for recording. Where joy_mode comes into play is in the next script - joy_step.

This script, joy_step, is not new. We wrote it in the last lesson, but it needs to be rewritten to accomodate the joypad movie system. Replace the whole script with this code:

joy_step()

JoyPast = JoyPrevious;
JoyPrevious = JoyCurrent;

if joy_mode = 1
  {
  JoyCurrent = file_bin_read_byte(jfile)+
  file_bin_read_byte(jfile)*256;
  jpos = file_bin_position(jfile);
  jprog = jpos/jsize;
  if jpos >= jsize
    {
    close_joypad_file();
    }
  }
else
  {
  JoyCurrent = 0;

  for (t=0;t<6;t+=1)
    {
    if joystick_check_button(1,Joy[t])
    or keyboard_check(Key[t])
    JoyCurrent |= 1<<t;
    }

  if AnalogCount
    {
    if joystick_xpos(1) < -AnalogDeadzone JoyCurrent |= LEFT;
    if joystick_xpos(1) > AnalogDeadzone JoyCurrent |= RIGHT;
    if joystick_ypos(1) < -AnalogDeadzone JoyCurrent |= UP;
    if joystick_ypos(1) > AnalogDeadzone JoyCurrent |= DOWN;
    }
  }

if joy_mode = 2
  {
  file_bin_write_byte(jfile,JoyCurrent mod 256);
  file_bin_write_byte(jfile,JoyCurrent div 256);
  }

First it checks to see if joy_mode is 1, for playback. If so, it reads from the file, updating a variable called jprog (you can take that bit out if you want, but it's useful for drawing a progess bar), and calling close_joypad_file automatically when it reaches the end. If joy_mode is something other than 1, input is received from the joypad and keyboard as normal, instead of from the file.

Next, it checks if joy_mode is 2, for recording. If so, it simply writes the value of JoyCurrent to the file as two bytes. If you choose to use more than 8 buttons (this tutorial only uses 6), the second byte will be necessary.

Okay! Now, all you have to do is include some interface for calling open-, save-, and close_joypad_file, and the rest will be taken care of. The simplest way is to call them in events for the press of function keys in the Joypad object.

Before we close this lesson, there is one more cool thing that recording joypad movies can do for you. Ghosts!

In Mario Kart, one of the coolest features is the ability to race against "ghosts". A ghost is just another driver, but with one important distinction. Instead of being controlled by computer AI, it is being controlled by a joypad movie. In this way, you can race against yourself, or go head to head with legendary runs by expert players, made years ago!

So, how do you include ghosts? If you simply play back the joypad file, JoyCurrent will receive its value from the file, and not the joypad, and your player object will follow the movie. We don't want that, though. We want the player object to be left alone, and have a new ghost object that follows the movie, co-existing with the player object. How do you get a ghost to play from the file, but leave the player object controlled by the joypad?

Well, you have to use a second set of variables besides JoyCurrent, JoyPrevious, and JoyPast. Let's call them gJoyCurrent, gJoyPrevious, and gJoyPast. These won't be global, but local to the ghost object, which should be a copy of the player object. Next, we'll need another set of scripts for checking them. Duplicate the joy, joy_pressed, and joy_released scripts and call them gjoy, gjoy_pressed, and gjoy_released. Then edit them so that they query gJoyCurrent, etc, instead of JoyCurrent.

Now, in the ghost object's code, replace anywhere the joy scripts were called with the gjoy scripts. Finally, you have to add code for opening, reading and closing the joypad movie in the ghost object, so that it does it all independently of the rest of your game.

I'd go into more detail about this last step, but it's easier to just look at it to see how it's done. You can use the link below.

Download the GML scripts and a GMK example of this lesson here.

Next time - Smart Triggers! Until then, happy coding, fellow Code Ninjas!

2009-08-03

Joy to the World

Welcome to your first lesson, my esteemed Code Ninjas in training! You have come here seeking knowledge of the Code in order to create your own video games. This is a worthy goal. It is my hope that I can teach you valuable lessons that will allow you to fulfill your dreams more quickly and capably. Armed with the secrets I shall reveal, you will be able to make your game engines more professional and - dare I say it - more fun. I do not mean to illude you - in no way can I make the path you have chosen easy. Game design is hard work. But I can make it easier.

Today's tutorial is about handling the player's input. Almost everyone playing your game will have a keyboard, but the keyboard is not the ideal input device for classic games of the type we want to make. Mario, Sonic, Metroid, Zelda, Final Fantasy, Klonoa - all these games are designed for joypads. So, ideally, for players who have access to PC compatible joypads, they should have the choice to use either their joypads or keyboards.

But it can be somewhat tricky to programme your game to either query both, or decide which one to query depending on the player's choice. Furthermore, Game Maker doesn't natively have very complete joypad functions.

These things are what I aim to teach you to overcome.

First, create a new object. We'll call it Joypad. It should be set to persistent, so that it is always present, even when the player moves between rooms. It shouldn't be visible, or have a sprite. Then you should place an instance of Joypad in your initial room, the one that your game starts in.

Make a script - we'll call it joy_init, and put it in the create event of Joypad.

Let's start writing joy_init:

joy_init()

globalvar JoyCurrent, JoyPrevious, JoyPast, Key, Joy, AnalogCount, AnalogDeadzone;

AnalogCount = joystick_axes(1) div 2;
AnalogDeadzone = 0.25;

What we're doing here is setting up a few global variables that can be referenced easily by every object in your game. The variable AnalogCount is set to the number of analog sticks the player's joypad has. We determine this by returning the number of axes and dividing by 2. Then we set up the variable AnalogDeadzone. A dead zone is essential when an analog stick is concerned. Neutral is 0, full on is 1. But due to the sensitivity of most controllers, the stick is never exactly at neutral, but fluctuates around .1 or even .2. A lot of driver software lets people set up dead zones for their joypads automatically, but we can't always bet on that. It's better to have your game take them into account. You can supply any value you think is reasonable (I've used .25 here), but it's even better if you include some option for the player to adjust the dead zone values manually - perhaps even independently for each stick.

Next, we add this to joy_init:

joy_init()

Joy[0]=3;
Key[0]=97;
Joy[1]=10;
Key[1]=13;
Joy[2]=13;
Key[2]=38;
Joy[3]=15;
Key[3]=40;
Joy[4]=16;
Key[4]=37;
Joy[5]=14;
Key[5]=39;

These are the joypad buttons numbers (Joy[]), and the keyboard keycodes (Key[]) we'll be using later to check for input. I've only done 6 buttons here, 0-5, because that's all a classic Sonic game really needs, but you can include as many as you'll be needing in your game. Any more than 16, though, is probably not a good idea, since most joypads won't have that many buttons. I've also entered the keycodes as raw numbers, but you can use the vk_... constants, or the ord() function as well.

Alternatively, you can read values into Joy[] and Key[] from an ini file, or even include an interface for the player to change them manually, which is best. Control configuration interfaces would be a tutorial in their own right, though.

Now that we know which buttons and keys we'll need to be checking for, we need to give them names. This is just a convenience for the programmer. I suggest using constants, and naming them after buttons on a console controller, such as A, START, LEFT, etc.

For example:

Code:

A = 0;
START = 1;
UP = 2;
//etc...

If you did the above, then you could type Joy[A] or Joy[START] instead of Joy[0] or Joy[1]. This is useful, especially for the direction buttons, since remembering which number corresponds to each of the four can be difficult.

But, actually, we're going to be doing something just a little more complicated than just making A = 0 and START = 1. We're going to be using some binary shifting, and you'll see why a little later.

Instead of setting A to 0 (or whichever number you want to call "A"), we'll be setting it to 1 left-shifted by 0. START will be set to 1 left-shifted by 1, and so on. This is what the code should look like:

Code:

A = 1<<0;
START = 1<<1;
UP = 1<<2;
//etc...

That means, in binary, A = 1, START = 10, and UP = 100.

Now that we've got our constants named, it's time to make a new script - let's call it joy_step - and put it in the begin step event of Joypad.

joy_step()

JoyPast = JoyPrevious;
JoyPrevious = JoyCurrent;
JoyCurrent = 0;

for (t=0;t<6;t+=1)
  {
  if joystick_check_button(1,Joy[t])
  or keyboard_check(Key[t])
  JoyCurrent |= 1<<t;
  }

What the for loop does is set up a binary variable, JoyCurrent, where each bit corresponds to one of the buttons being active. It checks both the joypad and the keyboard, so either one the player uses will work.

So, by default, both the joypad and keyboard are detected by the game and no setup or choice between the two is necessary. Although, it's very easy to rewrite the loop to not check for one or the other, if for instance you wanted to let the player choose which mode they'd rather use. Some people may not have a joypad at all and there's no reason to do extra checks.

So, if either the joypad button or the keyboard key (or both) is detected for button 0, JoyCurrent becomes a value of 1. If no other button is detected, it remains a value of 1. But if another button is detected, the new value is or-ed together. If both buttons 0 and 1 are detected, for instance, JoyCurrent becomes a (binary) value of 11. In this way, with only one variable, you can store which buttons are being detected during this step.

Of course, before the loop runs, we dump the value of JoyCurrent into a buffer value called JoyPrevious (and, one step further, dump JoyPrevious into JoyPast). This is going to be used to detect pressing and releasing the buttons, akin to the keyboard_check_pressed() and keyboard_check_released() functions. We could probably get by with only two values, JoyCurrent and JoyPrevious, but some joypads are subject to signal noise, and the addition of JoyPast will improve things in those cases. For instance, the device I use to convert my Nintendo Gamecube to be compatible with a PC stops detecting some buttons for an instant while others are rapidly pressed. This makes performing the spindash in Sonic 2 nearly impossible, because the Down button stops registering when the A button is tapped, causing Sonic to launch early.

Now we write another script, just called joy.

joy()

return (JoyCurrent&argument0);

Now, as long as the Joypad object is present in the room, any object can call the joy script to test for buttons. For example:

Code:

if joy(A)
  {
  //make the player jump
  }

if joy(B)
  {
  //make the player attack
  }

if joy(LEFT)
  {
  //move the player left
  }
else
if joy(RIGHT)
  {
  //move the player right
  }

Now, what about presses and releases? If you used something like the code above, the character would continually jump as you held down the A button. What we need is another script - joy_pressed.

joy_pressed()

return (JoyCurrent&argument0) and !(JoyPrevious&argument0) and !(JoyPast&argument0);

This will only return true when the button is active in this step, but not in the previous step, or the one before that.

And now for joy_released.

joy_released()

return !(JoyCurrent&argument0) and !(JoyPrevious&argument0) and (JoyPast&argument0);

This script only returns true if the button is not active during this step or the one previous, but was active in the step before that.

And there you have it. A very simple way of having robust joypad and keyboard support for your game, that's fully customisable to boot. If you don't feel like including an interface for mapping keys and buttons in your game, at least include an ini settings file. Nothing is more annoying than actually having joypad support in a game, but then finding that the buttons are all mapped wrong!

And finally, if you want to use an analog stick to emulate the directional buttons, you can add this code to the joy_step script we made.

joy_step()

if !AnalogCount exit;
if joystick_xpos(1) < -AnalogDeadzone JoyCurrent |= LEFT;
if joystick_xpos(1) > AnalogDeadzone JoyCurrent |= RIGHT;
if joystick_ypos(1) < -AnalogDeadzone JoyCurrent |= UP;
if joystick_ypos(1) > AnalogDeadzone JoyCurrent |= DOWN;

You can check as many axes as you want, of course. You can even use code like this to make the right-hand analog stick emulate the X, Y, and Z buttons like in The Legend of Zelda - Ocarina of Time (Nintendo Gamecube version). The great thing is, since the values are or-ed together, either the buttons or tilting the stick both work.

And that's it! You've got a complete joypad system that takes input from either the joypad or keyboard, and is incredibly easy to use.

Download the GML scripts and a GMK example of this lesson here.

Until next time, happy coding, fellow Code Ninjas!