Part 1 can be found here.
http://www.psp-programming.com/dev-forum/viewtopic.php?t=359In this next step we will create our game environment, implement movement and create our character animation.
Open up your main.c from part 1, at the top where your includes are find this section of code:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include "graphics.h"
Add an extra line so it reads:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include "graphics.h"
#include "game.h"
Find this section of code:
else if (OnDifficultyMenu == 1)
{
//HERE'S WHERE WE WOULD START THE ACTUAL GAME
}
And change it to:
else if (OnDifficultyMenu == 1)
{
//Start The Game
Game();
OnStartGameMenu = 1;
}
All we've done is added the code which will enable us to call our Game() function which will hold all the code for our actual game.
The line:
OnStartGameMenu = 1;
Just simply resets our menu so that it reverts to the 'Start Game' part when we return from the game.
Your new main.c should now look like this:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include "graphics.h"
#include "game.h"
PSP_MODULE_INFO("Game Tutorial Part 1: Menu", 0, 1, 1);
#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16))
/* Exit callback */
int exit_callback(int arg1, int arg2, void *common) {
sceKernelExitGame();
return 0;
}
/* Callback thread */
int CallbackThread(SceSize args, void *argp) {
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void) {
int thid = 0;
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0) {
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
//GLOBAL VARIABLES
int DifficultySetting = 1;
int main(void) {
SetupCallbacks();
initGraphics();
SceCtrlData pad, lastpad;
sceCtrlReadBufferPositive(&lastpad, 1);
Image* Background;
Background = loadImage("./Images/Background.png");
Color EasyColor = RGB(0, 0, 0);
Color MediumColor = RGB(0, 0, 0);
Color HardColor = RGB(0, 0, 0);
int OnStartGameMenu = 1;
int OnDifficultyMenu = 0;
extern int DifficultySetting;
while(1) {
sceCtrlReadBufferPositive(&pad, 1);
if(pad.Buttons != lastpad.Buttons) {
lastpad = pad;
if(pad.Buttons & PSP_CTRL_CROSS)
{
if (OnStartGameMenu == 1)
{
OnStartGameMenu = 0;
OnDifficultyMenu = 1;
DifficultySetting = 1;
}
else if (OnDifficultyMenu == 1)
{
//Start The Game
Game();
OnStartGameMenu = 1;
}
}
if(pad.Buttons & PSP_CTRL_CIRCLE)
{
if (OnDifficultyMenu == 1)
{
OnStartGameMenu = 1;
OnDifficultyMenu = 0;
}
}
if(pad.Buttons & PSP_CTRL_UP)
{
if (OnDifficultyMenu == 1)
{
DifficultySetting--;
if (DifficultySetting < 1)
DifficultySetting = 1;
}
}
if(pad.Buttons & PSP_CTRL_DOWN)
{
if (OnDifficultyMenu == 1)
{
DifficultySetting++;
if (DifficultySetting > 3)
DifficultySetting = 3;
}
}
}
blitAlphaImageToScreen(0, 0 , 480, 272, Background, 0, 0);
if (OnStartGameMenu == 1)
{
printTextScreen(170, 140, "Start Game", RGB(191, 0, 0));
}
else if (OnDifficultyMenu == 1)
{
if (DifficultySetting == 1)
{
EasyColor = RGB(191, 0, 0);
MediumColor = RGB(0, 0, 0);
HardColor = RGB(0, 0, 0);
}
else if (DifficultySetting == 2)
{
EasyColor = RGB(0, 0, 0);
MediumColor = RGB(191, 0, 0);
HardColor = RGB(0, 0, 0);
}
else if (DifficultySetting == 3)
{
EasyColor = RGB(0, 0, 0);
MediumColor = RGB(0, 0, 0);
HardColor = RGB(191, 0, 0);
}
printTextScreen(170, 140, "Easy", EasyColor);
printTextScreen(170, 150, "Medium", MediumColor);
printTextScreen(170, 160, "Hard", HardColor);
}
sceDisplayWaitVblankStart();
flipScreen();
}
sceKernelSleepThread();
return 0;
}
Save and close your main.c file.
Create a new file named game.h, this will be the header file for our game which we have already included in our main.c (#include "game.h").
Header files contain declarations to functions and variables and have a .h extension.
#ifndef __GAME__
#define __GAME__
extern void Game();
#endif
We are just defining our Game() function here - note the extern again, this is because our actual Game() function will be held in a different file (game.c).
Because we only want to define our function once we use the #ifndef command. Basically the above code translates to: if __GAME__ is not defined, then define __GAME__ and the Game() function. This is just a precaution in case you #include this header file in more than 1 other file.
Save and close game.h
Create a new file name game.c. First we'll add our includes:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include "graphics.h"
If you're not sure why we're including these, please refer to Part 1 of the tutorial.
We only have one declare in game.c, the same as is in main.c:
#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16))
Again, refer to Part 1 if you are unsure what this does.
Let's start our Game() function:
void Game()
{
And add some variables:
SceCtrlData pad;
int BlinkyPosX = 170;
int BlinkTimer = 0;
Color White = RGB(255, 255, 255);
pad will hold our controls variable, BlinkyPosX will be the X position of our character, BlinkTimer will be a timer which we'll get to later on in this tutorial and finally White is the color white.
Let's add some new images:
Image* BlinkyImages[6];
BlinkyImages[0] = loadImage("./Images/Idle.png");
BlinkyImages[1] = loadImage("./Images/IdleBlink.png");
BlinkyImages[2] = loadImage("./Images/Left.png");
BlinkyImages[3] = loadImage("./Images/LeftBlink.png");
BlinkyImages[4] = loadImage("./Images/Right.png");
BlinkyImages[5] = loadImage("./Images/RightBlink.png");
We are declaring an array of images (6) which will hold all our images for Blinky, then loading the various images into the array. Note how we declared 6 images and they range from [0] to [5] NOT [1] to [6].
int FrameCounter = 0;
Finally, we declare an integer named FrameCounter and set it to 0. FrameCounter will hold the number of the current image we will display for Blinky ie. The 'X' in BlinkyImages[X]. So FrameCounter will always be populated with a number from 0-5, same as our image array. Setting it to 0 will allow us to display BlinkyImages[0] later in our code by using BlinkyImages[FrameCounter].
Onto our loop.
while(1)
{
You should know what this does, starts our loop in which we will read controls, blit images etc.
sceCtrlReadBufferPositive(&pad, 1);
FrameCounter = 0;
BlinkTimer++;
We are taking a reading of the controls and putting the result in the pad variable, setting FrameCounter to 0 (Idle) and incrementing the BlinkTimer.
We set FrameCounter to 0 because if we don't receive any input from the controls we want to display the idle image and we have to check for this within the loop each iteration.
BlinkTimer++ just adds 1 to the current value stored in BlinkTimer.
Note how we don't have a lastpad repeat check for the controls in this file like we did in main.c. That's because we want the controls to repeat - if we hold left we want our character to keep moving left.
Onto reading the controls:
if (pad.Buttons & PSP_CTRL_START)
{
break;
}
If start is pressed we want to break out of our loop and end the Game() function, thus returning us to the 'Start Game' menu. This is just a temporary thing for this tutorial as a way of getting us back to the menus.
if (pad.Buttons & PSP_CTRL_LEFT)
{
BlinkyPosX -= 2;
FrameCounter = 2;
}
If left button is being pressed, we subtract 2 from the current BlinkyPosX variable, which will make Blinky move toward the left. We then set the FrameCounter variable to 2 - if you look back up at where we loaded our Blinky image array you will see that BlinkyImages[2] is in fact Left.png.
We then do a similar thing for right:
if (pad.Buttons & PSP_CTRL_RIGHT)
{
BlinkyPosX += 2;
FrameCounter = 4;
}
This time we add 2 to the BlinkyPosX variable, moving Blinky toward the right and then set FrameCounter to 4. BlinkyImages[4] is Right.png.
if (BlinkTimer > 200)
{
FrameCounter++;
}
Ok, so after 200 iterations of the loop we want Blinky to blink. If you look at the image array again, you will see that the blinking versions of each image are +1 to the non-blinking versions. ie. idle is BlinkyImages[0], idle with a blink is BlinkyImages[1], so we just add one to our FrameCounter variable.
Now we need a check to cancel the blinking animation, and reset the image back to the non-blinking version:
if (BlinkTimer > 220)
{
BlinkTimer = 0;
}
We reset the BlinkTimer variable to 0, so it has another 200 iterations of the loop before we blink again.
if (BlinkyPosX < 0)
{
BlinkyPosX = 0;
}
else if (BlinkyPosX > (480 - 106))
{
BlinkyPosX = (480 - 106);
}
The above code keeps Blinky on our screen. The first check just makes sure that BlinkyPosX is never lower than 0, otherwise Blinky would disappear off the left side of the screen if we went too far left.
The second check make sure Blinky doesn't go off the right side of the screen. Our Blinky images are 106 pixels wide, the PSP screen is 480 pixels wide, so we simply subtract one from the other. I've left the calculations involved in to illustrate this, but you could simply put 374 (which is what 480-106 equals) instead.
fillScreenRect(White, 0, 0, 480, 272);
We draw a rectangle the full size of the screen and make it white - this will be our 'background' of sorts.
blitAlphaImageToScreen(0, 0, 106, 72, BlinkyImages[FrameCounter], BlinkyPosX, 170);
This might look a bit different than what you're normally used to. We're blitting the image BlinkyImages[FrameCounter] (where FrameCounter is a value from 0 to 5) to the screen at our BlinkyPosX X position and Y position 170. The Blinky images are all 106x72 so we can hard code those values in.
I hope you can see now why we alter the BlinkyPosX variable and the FrameCounter variable.
Let's finish it up:
sceDisplayWaitVblankStart();
flipScreen();
}
}
Waiting for our vertical blank and then flipping the off-screen to the screen.
Your full code for game.c should look like this:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include "graphics.h"
#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16))
void Game()
{
SceCtrlData pad;
int BlinkyPosX = 170;
int BlinkTimer = 0;
Color White = RGB(255, 255, 255);
Image* BlinkyImages[6];
BlinkyImages[0] = loadImage("./Images/Idle.png");
BlinkyImages[1] = loadImage("./Images/IdleBlink.png");
BlinkyImages[2] = loadImage("./Images/Left.png");
BlinkyImages[3] = loadImage("./Images/LeftBlink.png");
BlinkyImages[4] = loadImage("./Images/Right.png");
BlinkyImages[5] = loadImage("./Images/RightBlink.png");
int FrameCounter = 0;
while(1)
{
sceCtrlReadBufferPositive(&pad, 1);
FrameCounter = 0;
BlinkTimer++;
if (pad.Buttons & PSP_CTRL_START)
{
break;
}
if (pad.Buttons & PSP_CTRL_LEFT)
{
BlinkyPosX -= 2;
FrameCounter = 2;
}
if (pad.Buttons & PSP_CTRL_RIGHT)
{
BlinkyPosX += 2;
FrameCounter = 4;
}
if (BlinkTimer > 200)
{
FrameCounter++;
}
if (BlinkTimer > 220)
{
BlinkTimer = 0;
}
if (BlinkyPosX < 0)
{
BlinkyPosX = 0;
}
else if (BlinkyPosX > (480 - 106))
{
BlinkyPosX = (480 - 106);
}
fillScreenRect(White, 0, 0, 480, 272);
blitAlphaImageToScreen(0, 0, 106, 72, BlinkyImages[FrameCounter], BlinkyPosX, 170);
sceDisplayWaitVblankStart();
flipScreen();
}
}
Now, the makefile for this will be slightly different to the original one, as we have to compile our game.c as well as the main.c:
TARGET = GameTutorial2
OBJS = main.o graphics.o framebuffer.o game.o
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
LIBDIR =
LIBS = -lpspgu -lpng -lz -lm
LDFLAGS =
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Game Tutorial Part 2
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
All we've done (apart from change the PSP_EBOOT_TITLE and TARGET) is add game.o to the end of the OBJS line. If there were more C files incorporated into this project, we would add them too.
Files for this tutorial can be found here:
http://www.psp-programming.com/animate/GameTutorial2.rarThe archive contains everything you need apart from the main.c, game.c, game.h and makefile.
Don't forget to copy the 'Images' folder to the 'GameTutorial2' folder (without the %).
Part 3 will finish our game off and will implement the actual gameplay, collision detection and scoring.