By now you should have the skills needed to start coding or modifying small games and utilities. In this Wii programming tutorial we will go through the Simon game and give a brief explanation of what things do. In this tutorial we focus on how to use the code we know to achieve certain the things that we are after. You can grab the PDF of this tutorial here: codemii-tutorial-9
Lets begin by firstly downloading the simon source code and opening up main.c. This version of Simon is intended to run on gcube. You can load the dol file with gcube and have a little play around.
Simon is a basic game; you have 4 colours and the computer generates a sequence of colours which you need to press correctly to win. The computer shows you one colour and when you press the correct colour the computer then shows you the next colour and so on.
A quick summary of this Simon game would be:
Show current sequence colour If sequence_counter is less than level_counter then increment sequence_counter If sequence_counter equals level_counter then wait for user input Wait for user input If colour correct, increment user_counter If user_count equals level_counter then increment level_counter and show new sequence If colour incorrect, return to main menu If user_counter equals sequence_length, game won
As usual we start off with our standard includes and also our graphical files. These graphic files were made with a program called bmp2gc which converts .bmp images to .h files which can be used with the gamecube/Wii. I would recommend you stick with using JPEGs instead of using bmp2gc.For the purpose of understanding the Simon game as it was coded, I’ve left most things as it was.
#include <gccore.h> … #include "main.h" // Graphics #include "simon.h" …
Next up we define how long we want the sequence to be, in this case 15. So the user will have to get the colours right until they reach 15 colours in a row.
#define SEQ_LENGTH 15
Our standard video variables
// 2D Video Globals static u32 *xfb; static GXRModeObj *rmode;
Now we declare our global variables. Firstly we define an array of characters that is 15 elements long. The next variable defined is the different colours; there are 4 colours. Red is 0, Green is 1, Blue is 2 and Yellow is 3.
Next up we have the sequence counters, the first seqnum is used when we are animating simon to show the user and seqlevel stores the ‘level’ the user is on. If the user has a sequence to play back of 210, they are up to level 3. If the sequence is up to 2102, they are up to level 4, etc.
Userseqnum stores where the user is up to in the sequence. The next booleans are to be used when we allow the user to press a button – userinput, if the user entered the correct input – correctinput and if the user is in the game – ingame.
// Vars char seq [SEQ_LENGTH]; char color [4] = "0123"; int seqnum, seqlevel, userseqnum; bool userinput, correctinput, ingame;
We then have the usual initialise function.
void Initialise() { … }
We have a SetScreen function which just flushes everything we wrote to the framebuffer to the screen. Note that in this Simon game things aren’t done as correctly as they should be. Generally you should clear the screen and then write to the framebuffer and display it but since there aren’t any cursors or moving objects it’s not really needed.
void SetScreen() { VIDEO_SetNextFramebuffer(xfb); VIDEO_Flush(); VIDEO_WaitVSync(); }
Now we’ll jump right down the bottom to the main function which is quite short. We call initialise, initialise the controller and call another function which handles restarting our gamecube but as we aren’t running this on a gamecube it doesn’t really matter.
Below that we have our drawpic function which has a similar concept as display_jpeg in tutorial 5. drawpic takes the x position (93), y position (0), the images height (48), the images width (120) and the actual picture to display (simontext) which is the title screen image. After that we call our game function and return 0 if the user ever decides to quit.
int main () { Initialise(); PAD_Init(); SYS_SetResetCallback (reset_cb); drawpic(93, 0, 48, 120, simontext); game(); return 0; }
Before exploring the game function we need to animate the title screen and initialise our variables. In the first run of the game function all our ingame variable is false so it skips all the code in game() and runs animate_simon().
void game() { u32 button = 0; while (1) { while (ingame == true) { … … // First run or Game over? Then go back to menu animate_simon(); restart_game(); }
Animate simon makes the lights spin on Simon and it acts as a menu screen waiting for the user to press A to begin. So we firstly need to know what colour light is currently lit, a variable which we can use to make the spinning go faster or slower and if this menu screen is running.
// Animate the "spinning" lights on simon void animate_simon() { int count = 0; int multipler = 0; bool running = true; u32 button = 0;
We display our starting text which says “Press A to start” and begin our while loop. We use our light counter (count) and if it’s 0 we show the red light, if it’s 1 we should the green light, etc. We then increment this counter and when it reaches a count of 3 it will be reset to -1 which then is incremented immediately to 0.
drawpic(96, 410, 27, 120, starttext); while (running == true) { if (count == 0) { drawpic(60, 60, 336, 192, simonred); } if (count == 1) { drawpic(60, 60, 336, 192, simongreen); } if (count == 2) { drawpic(60, 60, 336, 192, simonyellow); } if (count == 3) { drawpic(60, 60, 336, 192, simonblue); count = -1; } count++;
Now when we are animating Simon we need a pause between each light and we can do this using the usleep function. Usleep sleeps for x amount of microseconds. Now when we are sleeping it’s basically just pausing our whole program. But what if the user presses the A button when the program is sleeping? Nothing would happen.
We can avoid this in two ways, either use threads or possibly make the sleep times shorter and then query the user input in-between sleeps. I went with the easier second option. You can see below that we sleep in 20 short times. We have our multiplier which can speed up the animation if it’s increased and we query if the A button was pressed.
// Sleep a little bit in the for loop so we can grab user input int i = 0; for (i = 0; i< 20; i++) { usleep(25000 - (multipler * 100)); PAD_ScanPads(); button = PAD_ButtonsDown(0); if (button == PAD_BUTTON_A) { running = false; }
After that we have the controller input code which works for either joystick, if it’s pushed up the animation speeds up and if pushed down the animation decreases.
// Go faster or slower according to user input else if ((PAD_StickY(0) > 18 || PAD_SubStickY(0) > 18) && multipler <= 245) { multipler++; } else if ((PAD_StickY(0) < -18 || PAD_SubStickY(0) < -18) && multipler >= 1) { multipler--; } }
We then have our normal handling reboot/poweroff code which isn’t important and then we display what we have in our framebuffer with SetScreen().
// Handle reboot/poweroff if (event == 1) { printf("POWER!\n"); reload(); } else if (event == 2) { printf("RESET!\n"); reload(); } if (button == PAD_BUTTON_START) reload(); //printf("multipler = %i \n",multipler); SetScreen(); } }
So lets say that the user now wants to play, they press A and then running variable turns false which causes us to end the animate_simon function and move on to the restart_game function.
The restart game function just initialises all of our variables, sets the userinput to false as we will firstly show the user the sequence, set the correctinput to true and when they get the colour wrong we’ll set it to false and they are in the game so we ingame to true.
As you might remember we don’t clear the screen so and we don’t want to display the Press A to start text anymore so what we show is a blank black image and then show a Simon image which doesn’t have any lights showing.
// Reset the variables and text void restart_game() { seqnum = 0; seqlevel = 0; userseqnum = 0; userinput = false; correctinput = true; ingame = true; drawpic(96, 410, 28, 120, blanktext); drawpic(60, 60, 336, 192, simon);
Now we need to make the sequence that the user has to copy, it’s not something that we want to hardcode so we need to randomise it instead. We can use the current time to provide us with a ‘key’ and we’ll use the rand feature to generate us a random number from 0 to 3 which is our colours (rand() % 4).
Our sequences needs to be a of type char because we can’t have a sequence like 01230102321 because it starts with a 0 and it would be hard to say give me the second digit of this number without converting this number to a char. So we add ‘0’ to our rand() % 4 so that it will convert the 0 to 3 number into type char. We’ll then generate 15 of these numbers and add the number to the sequence.
// Randomise the sequence srand((unsigned)time(0)); int q; for(q=0; q<SEQ_LENGTH; q++){ char random_integer = '0' + rand() % 4; seq[q] = random_integer; //printf("%c \n", random_integer); } printf("Sequence is %s \n",seq); }
Now we can explore the game function. As the variable userinput is still false we can skip this part and go onto the part that shows the user which colour the first level of our sequence is. In our example we will use the sequence below:
Sequence 0 3 1 1 2 3 2 0 2 1 0 1 2 1 3 Level 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Seqlevel 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Seqlevel would equal 0 and seqnum (our counter) would be 0 as well. We now need a way to increase the speed of the animation of Simon when the level increases. We use usleep again and this time use seqlevel as the multipler. As you recall the seq variable has our sequence stored. As seq is of type char we can request the x position of this variable like seq[0] would be 0, seq[1] would be 3, seq[2] would be 1, etc.
In our first run we would sleep, compare seq[0] with colour[0] which are both 0 and that would make the if statement true and then we would display the Simon red light. After that’s done we check to see if our counter is equal to the level the user is on and if it is we set userinput to true and reset the seqnum to -1 which will be incremented back to 0. We will sleep again for a while, draw the blank Simon image and increment seqnum so we can show the next colour to the user if needed.
// If we are playing the sequence to the user if (userinput == FALSE) { usleep(1000000 - (seqlevel * 50000)); // Lower time every incrementation if (seq[seqnum] == color[0]) { drawpic(60, 60, 336, 192, simonred); } if (seq[seqnum] == color[1]) { drawpic(60, 60, 336, 192, simongreen); } if (seq[seqnum] == color[2]) { drawpic(60, 60, 336, 192, simonblue); } if (seq[seqnum] == color[3]) { drawpic(60, 60, 336, 192, simonyellow); } if (seqnum == seqlevel) { userinput = TRUE; seqnum = -1; } // Sleep a while and then show blank simon usleep(1000000 - (seqlevel * 50000)); drawpic(60, 60, 336, 192, simon); seqnum++; }
So now that we’ve shown the sequence to the user, all we need to do is check for which buttons the user presses, Y, X, B, or A. So say the user presses the A button which would be correct for the first level (userseqnum equals 0). Colour[0] is 0 and seq[userseqnum] is 0 so then correctinput stays as true.
while (ingame == true) { PAD_ScanPads(); button = PAD_ButtonsDown(0); // If we are waiting for user input if (userinput == TRUE) { if (button == PAD_BUTTON_Y) { drawpic(60, 60, 336, 192, simonred); if (color[0] != seq[userseqnum]) { correctinput = false; } } if (button == PAD_BUTTON_X) { drawpic(60, 60, 336, 192, simongreen); if (color[1] != seq[userseqnum]) { correctinput = false; } } if (button == PAD_BUTTON_B) { drawpic(60, 60, 336, 192, simonblue); if (color[2] != seq[userseqnum]) { correctinput = false; } } if (button == PAD_BUTTON_A) { drawpic(60, 60, 336, 192, simonyellow); if (color[3] != seq[userseqnum]) { correctinput = false; } }
So now we detect if any button of the A, B, X, Y buttons were pressed. We check that correctinput still equals true which in this case it does. If the userseqnumber (0) is less than the seqlevel (0) then it would increase the userseqnum. In this case it’s not true so we skip that and go to the next if statement which is true. We reset the userseqnum, increment the seqlevel (to 1) and set userinput to false as we would like to show the user the new sequence.
As seqlevel doesn’t equal SEQ_LENGTH (15) we’ll skip that part for now.
if (button == PAD_BUTTON_A || button == PAD_BUTTON_B || button == PAD_BUTTON_X || button == PAD_BUTTON_Y) { usleep(500000); drawpic(60,60, 336, 192, simon); //printf("userseq %i seqlevel %i \n",userseqnum, seqlevel); // If they entered the correct sequence if (correctinput == TRUE) { // Increment the users seq number if (userseqnum < seqlevel) { userseqnum++; } // If user has matched the seq level, increase the seq level and show the new sequence to the user else if (userseqnum >= seqlevel) { userseqnum = 0; seqlevel++; userinput = false; // Wait a little while usleep(500000); // If user has won if (seqlevel == SEQ_LENGTH) { … } } }
We show the new sequence to the user and then wait for their input again. This time around the if statement (userseqnum < seqlevel) is true so we increment userseqnum and wait for the user’s next input if they were correct. We’ll assume that one time that they were incorrect, we need to find what the correct colour was and show that to the user. So we compare seq[userseqnum] with 0, 1, 2, 3 until we find the right colour and then display that colour light. We then display the “You Lost” text, pause for a while and then show the blank simon and set ingame to false so we go back to the menu screen.
// Incorrect sequence else if (correctinput == FALSE) { //printf("Sorry wrong key…. \n"); usleep(1000000); //printf("Right key was %c \n",seq[userseqnum]); //printf("You lost! \n"); if (seq[userseqnum] == '0') { drawpic(60, 60, 336, 192, simonred); } if (seq[userseqnum] == '1') { drawpic(60, 60, 336, 192, simongreen); } if (seq[userseqnum] == '2') { drawpic(60, 60, 336, 192, simonblue); } if (seq[userseqnum] == '3') { drawpic(60, 60, 336, 192, simonyellow); } drawpic(96, 410, 27, 120, youlost); usleep(15000000); SetScreen(); drawpic(60, 60, 336, 192, simon); drawpic(96, 410, 28, 118, blanktext); usleep(500000); SetScreen(); ingame = false; }
Lets assume that the user has won the game by getting all the sequence correct. We check this by comparing seqlevel to SEQ_LENGTH (15) and if equal they have won. We display the “You Won” text and display the winning square by comparing the last level in the sequence with 0, 1, 2, and 3. We have a for loop to display the winning square, sleep a bit and then display the blank Simon image a few times so it’s shown as a blinking colour light. We pause for a while and then show the blank simon and set ingame to false so we go back to the menu screen.
// If user has won if (seqlevel == SEQ_LENGTH) { //printf("You win! \n"); drawpic(96, 410, 27, 120, youwin); // Blink the winning square int a; for (a = 0; a < 10; a++) { if (seq[SEQ_LENGTH-1] == '0') { drawpic(60, 60, 336, 192, simonred); } if (seq[SEQ_LENGTH-1] == '1') { drawpic(60, 60, 336, 192, simongreen); } if (seq[SEQ_LENGTH-1] == '2') { drawpic(60, 60, 336, 192, simonblue); } if (seq[SEQ_LENGTH-1] == '3') { drawpic(60, 60, 336, 192, simonyellow); } usleep(100000); SetScreen(); drawpic(60, 60, 336, 192, simon); usleep(100000); SetScreen(); } drawpic(96, 410, 28, 120, blanktext); usleep(500000); SetScreen(); //printf("Press A to play again \n"); ingame = false; }
And there we have it, the Simon game explained in detail. All together it looks like a lot but there’s a few repeated if statements, functions, etc. It’s about programming in small components; you might start off with making the colour light up when you press a key. Then you might move on to comparing the key pressed to a certain colour and then move on to comparing the key pressed to a certain position in a character array, etc.
I hoped this tutorial has helped you understand the Simon game and how things were done. There are plenty of ways that this Simon game can be improved so feel free to modify the code and show us how you’ve improved the game. I didn’t expect this tutorial to be this long but what can you do. If you have any questions or comments feel free to post them.
Good stuff as usual, will need to build this when I get a min.
Hope you had a good christmas and thanks for the web pointers too on another site 😉
Hi,
thanks for the great tutorials. I wrote my first wii game based on these tutorials. It’s a memory for the wii. Thank u for explaining so much.
hi, can you write a small manual how to enable the external lan ethernet device. my own tcp library does only support the internal wifi device. some people would really like lan support in my games. looking forward to your response
There isn’t anything special that I’m doing. The only thing is I initialise the network before initialising the storage device. joedj told me this and it seemed to work fine for users who tried it.
Hi Teknecal, Is it possible that i can receive the source code of the homebrew browser. Will not distributed it to someone else. Maybe i can find the issue in my source code by comparing our code with my code.
Hi, You should be able to use the netsend example on WiiBrew – http://www.wiibrew.org/wiki/Image:Sample_netsend.zip. Otherwise you can always view the ftpii source code and see how joedj does it. I’m still on DevkitPPC r15.
Hi, I’ve been following these tutorials and I managed to do everything that was explained, but when going to a custom code of mine I have no idea on how to add jpg images… how do I create the files I need? There’s nothing in your tutorial that can help me doing it, as it only makes you use files that you did in advance…
Then, a tutorial on how to use pngs wuold be great, as pointers with jpgs are horrible (and jpgs suck anyway…)
Hope you can help me explaining how I can use images..
(Sorry for the bad english but I’m italian :D)
Hi,
All that’s needed is for each image, is to put into the picture.s file and to follow Tutorial 5 specifically when we reach the part about “JPEGIMG about;”. http://www.codemii.com/2008/09/07/tutorial-5-jpeg-images/
You need to do the below for each picture:
JPEGIMG about;
memset(&about, 0, sizeof(JPEGIMG));
about.inbuffer = picdata;
about.inbufferlength = piclength;
JPEG_Decompress(&about);
I managed to make images work, but they look like they’ve been cut… but when I press home they are displayed normally. Is it a problem of where I place the code?
I managed to put some images in, but they look like they’ve been cut… and if I put a return the game goes back to the loader. I’ll keep on working on it ^^
Great tutorial! I have a problem though. When i try to compile the program i get
make[1]: *** No rule to make target `c:/devkitPro/examples/gamecube/tutorial8/source/main.c’, needed by `main.o’. Stop.
“make”: *** [build] Error 2
> Process Exit Code: 2
> Time Taken: 00:00
Can you make sure you have all the files from the zip correctly?
Also can you press Alt + 2 once you loaded up tutorial8.pnproj in Programmers Notepad?
Thanks a lot for these tutorials, they are great! I am attempting to use what i have learned to port this game for the Wii… any pointers?
Only thing I can think of is that you should use a better graphics library than I did. Other than that you should be fine, just remember to keep testing.
I got it working with the same graphics library, even added the loop from tut. 12, i have a question… when i try to call the nunchuk “z” button it seems to scan for the wpad_button_2… whats the deal with that
Hi! i love these tutorials and i was jsut wondering if you had one on how to use png images. Because most games will need png images eventually.
I don’t have one on using PNG images, but you might want to check out GRRLIB as that has PNG loading and is quite easy to use.