Tutorial on how MKPTC was made.
I do not own Mortal Kombat or any of its characters or music. This is a tutorial on how to program in petit computer on a slightly more advanced level. The PTC Edition of MK is under development. The code in that project may vary from this tutorial as time passes. At no time do I clam to spell well.
- Loading graphics into arrays
- Selecting and unselecting animation
- Developing and loading sprite characters
- Game loop
- Not covered in this tutorial is music (MML). I suggest reading Randomous's tutorial on Nintendo Life Tutorials
Loading graphics into arraysEdit
An array is just a fancy name given to a type of table that can hold numbers or alphabetic characters similar to an Excel spreadsheet. An array with one number is like one row. An array with two numbers (double array) has rows and columns. Since the images of MK characters have width and height we will use double arrays.
The code begins by clearing the top and bottom screens. Next we load the faces of the characters that were drawn using GRPED. In a perfect world, these images would appear exactly where I want them. I load the images using LOAD "GRP0:MKPTC",0 . The MKPTC is the name I gave the image when it was created in GRPED. The zero at the end tells the computer not to show the load screen.
The command DIM JOHN(35,40) creates that array to store color numbers (0-255). The numbers 35 and 40 are the width and height of the array which is 35 pixels wide by 40 pixel high. I also created two single arrays to hold the x and y upper left positions of each image. DIM PLACEX(8) and DIM PLACEY(8). There are 7 characters and one MK dragon symbol.
Now to load the arrays. To get the faces off the computer screen we use GSPOIT(i,j). This command gets the color number (0-255) for an individual pixel. We use two FOR loops "nested" one inside another. The outside loop will move down one row at a time while the inner loop will read a row of pixels into the arrays. Off topic: I could have used DATA statements but it is easier to draw the faces in a graphics program.
Finally, I use another set of FOR loops to draw the faces to their final locations. GPSET I+29,J+50,JOHN(I,J) sets the pixel color of a specified point. +29 and +50 offset the image to a particular starting location. +29 in the x direction and +50 in the y direction. The I and J pick the offset from that starting location and the color stored in the JOHN array.
Selecting and Unselecting AnimationEdit
The select screen loops over and over checking for a button press. BUTTON(1) is used to check which button was pressed. For each button, checked by an IF statement, control is sent to "subroutine". Within the subroutine the current box around the character's face is removed by @UNSEL subroutine. This sub simply draws over the select box with a black box using GBOX command. Based on the current selection and because we are in a subroutine for a particular button, we can use IF statements to decide what character we need to select. IF SEL==4 THEN SEL=1 . Next, we go to @DOSEL to draw a box around our new selection.
Here is where we use the PLACEX() and PLACEY() arrays. A little more about arrays: SEL is a number that is called an "index" to the array. For whatever reason, arrays start at zero and end at one less that the number you put in when setting up the array. So when we say DIM PLACEX(8), we say there are 8 images total. But when we want to get an x position for the first character we use 0 instead of 1 (one less). So if SEL=1 we are looking for the second character (KANO). The program gets these (x,y) locations: PSX=PLACEX(SEL): PSY=PLACEY(SEL). I could have just used the arrays but PSX is shorter than PLACEX(SEL). Then we use GBOX with the psx and psy location to draw a box around the selected character.
Once a character is selected with the A button, the image is covered with a "mask" of checkerboad pixels. This was trial and error to find an equation that offset the pixels one space in the x and y direction. MASK=I%2+J%2. IF MASK==0 THEN GPSET I,J,4 all within a double FOR loop.
Developing and Loading Sprite CharactersEdit
Sprites are created using CHRED set at 4x4. I was able to get two characters per sprite file with the stuff they throw. Unfortunately, the stuff they throw is not 4x4 and had to be broken in half to look correct in the program. Warning: Do not think that by changing the sprite group in the lower left of this program lets you create more characters per file. It overwrote my characters. What you see at the top of the screen is what it saves. Each group of characters had to have a separate name like MKPEP1, MKPEP2.
Getting sprites into your game. Use LOAD "SPU0:MKPEP1",0 for the first group. LOAD "SPU1:MKPEP2",0 for the second group, and so on. Use SPSET command with 8 "parameters" to assign a sprite to a "sprite control number". This number will be used throughout the game to reference a particular sprite. The control number is the first parameter of the SPSET command. It can be 0-99. The second parameter is a number that relates to the location of the sprite in all of the groups. In MK, each character is 32 whatever from the previous character. I multiply the character SEL times 32 to get the correct character group. Note: a group includes all of the images of one character. In some cases, I add a number in increments of 4 to this product (math term) to get the individual images of one character like jumping, throwing, sweep kick. The last two optional parameters are important to use larger sprites. For the characters we use 32 wide by 32 high.
Each sprite is controlled by a particular point. Typically it is the upper left most point of the sprite. SPHOME is used to locate this point to the center-bottom of the sprite. SPHOME 0,16,32. This helps for keeping track of the characters feet and make sure they don't fall through the floor.
Even to this point we have not placed our character. X=30: Y=150 SPOFS 0,X,Y places our character in the correct location. With the thrown objects we can offset the x,y position from the characters home x,y position by adding to the x and subtracting from the y. But we start by placing these thrown objects off of the screen until they are called for.
This could be a long discussion about time. Unlike turn based games, action games requires giving a little slice of time to everything. Because things move so fast it may apear that everything is happening at once. In MK the jumps, thrown objects, character actions (good and bad), and score board all get their own time in what is called the "Game Loop". In MK PTC its called @Game. This main loop repeats constantly through the life of the game. What happens in the game loop is controlled by FLAGS and counters. If you learn about "states" in computers, you will see that each sprite may be in a particular state. For example, a character could be in a state of jumping (FLAG2) or a shot could be in a state of flying across the screen. States that are active have the flag set to 1. The program checks these flags and updates the postion or condition of a sprite until there state changes or ends.
Example: Johnny Cage throws a green blob at Kano. IF B==16 AND FLAG2==0 THEN GOSUB @JKTHR. I make sure that flag2 is not set and therefore there is not shot traveling across the screen. In JKTHR, flag2 is set to 1 (active). The game loop for @GJC picks up that the flag2 is active and initiates a routine to move the x postion of the shot one space. Flag2 remains active until it hits the opponent or the edge of the screen. The game loop is constantly checking the status of the flag and moving the shot sprite if the flag is active. IF FLAG2==1 THEN GOSUB @UPDATEPR.
There is also a flag for jumping. The jumping subroutine allows for more button pushes to move left or right in the air. The player does not have control over rising or falling. All of this is done through changing (x,y) and using SPOFS command but a little bit at a time.
I am working on freezing by SUB-ZERO. In this case, I check for the freeze flag and skip the action of the other character until the freeze flag counts down to zero. In this case a counter decreases by one. If statements keep it from going negative and release the opponent when it gets to zero.
It is important to note that the Game Loop controls the overall game. GOSUBs are used to break out the game loop into time slots for the good side, bad side, jumps, and throws. Each character in MK has his or her own subroutine that matches there button presses with their moves. I tried to recycle the good sides moves for the bad side. But this required changing the sprite control number, the direction they faced, the offset of the throws, the character, etc.
Some characters had to have their own flags because they used different sprites which had different width and height. Johnny Cages kick brought in a larger 32x32 sprite to follow behind him. These extra flags also have their own update routines as well. I used SPCHR to change the characters direction they are facing. I accomplished this by adding more parameter values that used the same sprite but changed its horizontal direction. I also tracked a DIR and EDIR variable to know which direction the character was facing. I hope this helps in your games. I know there is not much code here. Take a look at the MK code and see if you can find these topics in the code.