Skip to: Site menu | Main content


Welcome to PSP-Programming.com, a place for developers to get together.

Welcome to the forums. Here you can find other user tutorials as well as homebrew releases and the source code repository. You can also ask for help with your code here and post your own homebrew!

PSP-Programming.com Forums
February 10, 2012, 12:39:57 AM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

News: Join our IRC channel: ##psp-programming on freenode
Home Help Search Shop Login Register
Digg This!
Pages: [1] 2
Print
Author Topic: [Tutorial] Full Game Part 3 - Collision & Scoring  (Read 13108 times)
Insert_Witty_Name
Global Moderator
Hero Member
*

Karma: +149/-17
Offline Offline

Posts: 1602
1141.66 points

View Inventory
Send Money to Insert_Witty_Name

View Profile WWW
« on: August 20, 2006, 06:17:24 PM »

Continuing from this post:

http://www.psp-programming.com/dev-forum/viewtopic.php?t=386

Ok, in the third and final part we'll add scoring and collision detection to our game.

First of all let's open up our file game.c from part 2 of the tutorial.

Code:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <stdio.h> //ADDITION FOR SPRINTF
#include </usr/include/sys/stat.h> //ADDITION FOR TIME
#include "graphics.h"

#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16))


Add the above two marked lines, the comments above and the following code will explain why we need them.

Code:
static int GetRandomNum(int lo, int hi)
{
 SceKernelUtilsMt19937Context ctx;
 sceKernelUtilsMt19937Init(&ctx, time(NULL)); //SEED TO TIME
 u32 rand_val = sceKernelUtilsMt19937UInt(&ctx);
 rand_val = lo + rand_val % hi;
 return (int)rand_val;
}


We need to generate random numbers now, for the X position of our objects that Blinky collects for points. For this we will use the Mersenne Twister, which the PSP supports natively.

We set our GetRandomNum function as static, which basically means it can only be called by other functions within the same file (game.c).

The GetRandomNum function will give us a number between the two values that we pass it, you will see it utilised shortly.

Code:
void Game()
{
 SceCtrlData pad;
 int BlinkyPosX = 170;
 int BlinkTimer = 0;
 int Score = 0; //ADDITION
 char ScoreAsChar[3]; //ADDITION
 int DeadStars = 0; //ADDITION
 Color White = RGB(255, 255, 255);
 Color Black = RGB(0, 0, 0); //ADDITION


We declare a few more variables here. Score is for the score, ScoreAsChar is  the score as a string (for displaying to the screen), DeadStars is the number of dead stars (what Blinky collects for points) and finally a new black color.

Code:
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");

 Image* Star = loadImage("./Images/Star.png"); //ADDITION

 int FrameCounter = 0;

 //ADDITION
 int StarPosX = GetRandomNum(20, 420);
 int StarTimer = 0;
 
 extern int DifficultySetting;


We then load up our image for the star, and generate a random number for it's position on the screen (StarPosX), declare StarTimer which we will check the value of to decide whether or not to blit a star and then declare that we wish to use the external integer DifficultySetting, which was set when starting the game (Easy = 1, Medium = 2, Hard = 3).

Onto the start of our loop:

Code:
while(1)
 {
 sceCtrlReadBufferPositive(&pad, 1);

 FrameCounter = 0;
 StarTimer++; //ADDITION
 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);
    }


Nothing has changed in this part, apart from incrementing the StarTimer variable.  The code for the START button being pressed has also been commented out as this was just a temporary measure to allow us to exit back to the main menu, which is not needed now.

 
Code:
//ADDITION (COLLISION)
 if (StarPosX > BlinkyPosX)
    {
     if (StarPosX + Star->imageWidth < BlinkyPosX + BlinkyImages[FrameCounter]->imageWidth)
        {
        if (StarTimer > 60)
           {
           Score++;
           DeadStars++;
           StarPosX = GetRandomNum(20, 240);
           StarTimer = 0;
           }
        }
    }


The above section of code is all new. It's the collision detection part of our game.

Basically we are running through a series of checks to see if Blinky has collided with a star. The first check (StarPosX > BlinkyPosX) checks that the X position of the left hand edge of the star is greater than the X position of the left hand edge of Blinky, the second (StarPosX + Star->imageWidth < BlinkyPosX + BlinkyImages[FrameCounter]->imageWidth) checks that the right hand edge of the star is lower than the right hand edge of Blinky, thus meaning that the star is within the bounds of Blinky. Finally we check that the value within the StarTimer variable is greater than 60 ie. the star is visible on screen (we are having a 60 frame gap between blitting the stars, as you will see in the next section of code).

If all these condition are met we then increment the Score variable by 1, the DeadStars variable by 1, generate a new random number for the X position of the star and reset the StarTimer value to 0.

Code:
fillScreenRect(White, 0, 0, 480, 272);
 blitAlphaImageToScreen(0, 0, 106, 72, BlinkyImages[FrameCounter], BlinkyPosX, 170);
 //ADDITION
 if (StarTimer > (60 + (180/DifficultySetting)))
    {
     DeadStars++;
     StarPosX = GetRandomNum(20, 420);
     StarTimer = 0;
    }
 else if (StarTimer > 60)
      {
       blitAlphaImageToScreen(0, 0, 40, 35, Star, StarPosX, 200);
      }


The first two lines of this are the same as before, everything else is new.

Firstly, we check if the value held in StarTimer is greater than our 'need to create a new star' value if (StarTimer > (60 + (180/DifficultySetting))). Remember that based on what the user chose, DifficultySetting could be either 1 for Easy, 2 for Medium or 3 for hard, so the easier the difficulty chosen the more time the star is shown on screen.

This is really just a check to see if the star has 'expired' ie. has been on screen for the given length of time but has not been 'collected' by Blinky.

The else if (StarTimer > 60) after this is just to give us a 60 frame gap before blitting a new star. When a star is created the StarTimer is set to 0 and then incremented by 1 each frame, assuming 60 frames per second this creates approximately a 1 second gap between destroying the last star and showing the new one.

Code:
sprintf(ScoreAsChar, "%d", Score); //ADDITION
 printTextScreen(460, 252, ScoreAsChar, Black); //ADDITION


Here we sprintf the value held in Score to the char variable ScoreAsChar, so we can display it as text to the screen.

Code:
//ADDITION
 if (DeadStars == 30)
    {
     fillScreenRect(White, 0, 0, 480, 272);
     printTextScreen(150, 130, "Finished! You Scored", Black);
     printTextScreen(230, 140, ScoreAsChar, Black);
     flipScreen();
     sceKernelDelayThread(5000000);
     break;
    }


This is our 'end of game' check. The value in DeadStars is incremented by 1 every time a star is collected or expires. So when 30 stars have been displayed, it's game over.

This is fairly simple, we just fill the screen white, display a couple of lines showing that the game is finished, give the final score, 'pause' for 5 seconds and then issue a break which will break us out of our while(1) game loop.

Code:
sceDisplayWaitVblankStart();
 flipScreen();
 }


Same as before, wait for the drawing to finish, flip the screen and then end our while loop.

Assuming that the break has executed from the 'end of game' check the following code is then executed:

Code:
//ADDITION
 int i;
 for (i = 0;i < 6;i++)
     freeImage(BlinkyImages[i]);
     
 freeImage(Star);
 //END ADDITION

}


Basically we're using a loop to free the memory used by the images we loaded for Blinky, then freeing the memory for the Star image and finally ending our 'Game' function, thus bringing us back to the main menu again.

It's important to free the images as eventually the repeated loading of them everytime the game is started from the main menu would lead to us running out of available memory and causing the game to crash.

Your full code for game.c should now look like this:

Code:
#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspctrl.h>
#include <stdio.h> //ADDITION FOR SPRINTF
#include </usr/include/sys/stat.h> //ADDITION FOR TIME
#include "graphics.h"

#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16))

static int GetRandomNum(int lo, int hi)
{
 SceKernelUtilsMt19937Context ctx;
 sceKernelUtilsMt19937Init(&ctx, time(NULL)); //SEED TO TIME
 u32 rand_val = sceKernelUtilsMt19937UInt(&ctx);
 rand_val = lo + rand_val % hi;
 return (int)rand_val;
}

void Game()
{
 SceCtrlData pad;
 int BlinkyPosX = 170;
 int BlinkTimer = 0;
 int Score = 0; //ADDITION
 char ScoreAsChar[3]; //ADDITION
 int DeadStars = 0; //ADDITION
 Color White = RGB(255, 255, 255);
 Color Black = RGB(0, 0, 0); //ADDITION

 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");

 Image* Star = loadImage("./Images/Star.png");

 int FrameCounter = 0;

 //ADDITION
 int StarPosX = GetRandomNum(20, 420);
 int StarTimer = 0;
 
 extern int DifficultySetting;

 while(1)
 {
 sceCtrlReadBufferPositive(&pad, 1);

 FrameCounter = 0;
 StarTimer++; //ADDITION
 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);
    }
   
 //ADDITION (COLLISION)
 if (StarPosX > BlinkyPosX)
    {
     if (StarPosX + Star->imageWidth < BlinkyPosX + BlinkyImages[FrameCounter]->imageWidth)
        {
        if (StarTimer > 60)
           {
           Score++;
           DeadStars++;
           StarPosX = GetRandomNum(20, 240);
           StarTimer = 0;
           }
        }
    }

 fillScreenRect(White, 0, 0, 480, 272);
 blitAlphaImageToScreen(0, 0, 106, 72, BlinkyImages[FrameCounter], BlinkyPosX, 170);
 //ADDITION
 if (StarTimer > (60 + (180/DifficultySetting)))
    {
     DeadStars++;
     StarPosX = GetRandomNum(20, 420);
     StarTimer = 0;
    }
 else if (StarTimer > 60)
      {
       blitAlphaImageToScreen(0, 0, 40, 35, Star, StarPosX, 200);
      }

 sprintf(ScoreAsChar, "%d", Score); //ADDITION
 printTextScreen(460, 252, ScoreAsChar, Black); //ADDITION

 //ADDITION
 if (DeadStars == 30)
    {
     fillScreenRect(White, 0, 0, 480, 272);
     printTextScreen(150, 130, "Finished! You Scored", Black);
     printTextScreen(230, 140, ScoreAsChar, Black);
     flipScreen();
     sceKernelDelayThread(5000000);
     break;
    }

 sceDisplayWaitVblankStart();
 flipScreen();
 }
 //ADDITION
 int i;
 for (i = 0;i < 6;i++)
     freeImage(BlinkyImages[i]);
     
 freeImage(Star);
 //END ADDITION

}


Makefile:

Code:
TARGET = GameTutorial3
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 3

PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak


Files for this tutorial can be found here:

http://www.psp-programming.com/animate/GameTutorial3.rar

The 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 'GameTutorial3' folder (without the %).

There is one major issue with this game. If a randomly generated number for the X position of the star is within the position of Blinky, the user will score a 'free' point.  I purposely left this in as I'd like you to try and figure out how to fix it (look at how the collision detection works for hints).

This is the final part for this series. My next tutorial set will focus again on creating a game from scratch but this time using pure GU and creating our own graphics library and functions.

Hope you enjoyed it!
Logged

Coder formerly known as:

Check out my homebrew & C tutorials at http://insomniac.0x89.org
Last updated 6th Oct 06 - Tutorial 2 added.


Lobap
Jr. Member
**

Karma: +0/-0
Offline Offline

Posts: 55
0.00 points

View Inventory
Send Money to Lobap

View Profile
« Reply #1 on: August 21, 2006, 02:07:30 AM »

Oh man...i love you Wink.
Now i can learn more and more hehe. Thanks Smile
Logged
harleyg
Give miinaturvat Points!
Full Member
***

Karma: +11/-14
Offline Offline

Posts: 231
0.00 points

View Inventory
Send Money to harleyg

View Profile
« Reply #2 on: August 21, 2006, 02:28:04 AM »

Nice insomniac, i added it to the tutorials sticky.
Logged
LMelior
Full Member
***

Karma: +12/-0
Offline Offline

Posts: 223
4266.03 points

View Inventory
Send Money to LMelior

View Profile
« Reply #3 on: August 22, 2006, 04:02:05 PM »

Awesome!  I was wondering if this was going to come out.  I can't wait to try this out, and I'm definitely looking forward to the GU full game tutorials Smile.
Logged
LMelior
Full Member
***

Karma: +12/-0
Offline Offline

Posts: 223
4266.03 points

View Inventory
Send Money to LMelior

View Profile
« Reply #4 on: August 26, 2006, 06:30:38 PM »

Just finished it...and I successfully fixed the problem!  I would post my function that fixed it (because I'm darn proud of it Very Happy), but I don't want to ruin it for others.

I would like to ask for a little extra help though.  From what little I understand of pointers I think my function could have used one, even though it probably isn't too critical in a simple program like this.  My function declaration looks like this:
Code:
static int PlaceStar(int badx)

and when I call the function I put
Code:
StarPosX = PlaceStar(BlinkyPosX)


So, should I go back and read some more pointers tutorials, or am I correct in thinking this is where a good programmer would use a pointer?  Sorry if this is a very noob-like question. Embarassed


EDIT:  Embarassed Sorry for the double post, I wasn't paying attention. Embarassed
Logged
Insert_Witty_Name
Global Moderator
Hero Member
*

Karma: +149/-17
Offline Offline

Posts: 1602
1141.66 points

View Inventory
Send Money to Insert_Witty_Name

View Profile WWW
« Reply #5 on: August 26, 2006, 11:13:17 PM »

Without seeing the full function (and possible all the other code) it's hard to say if a pointer would be better mate.

Feel free to PM me.
Logged

Coder formerly known as:

Check out my homebrew & C tutorials at http://insomniac.0x89.org
Last updated 6th Oct 06 - Tutorial 2 added.
Kasumi
Newbie
*

Karma: +3/-1
Offline Offline

Posts: 47
2380.95 points

View Inventory
Send Money to Kasumi


View Profile
« Reply #6 on: August 27, 2006, 07:08:00 AM »

I can't wait for the full GU tutorial. It's what I've been looking for. I've been interested in making a PSP game for quite a while, and while LUA was easy, I always worried about people swiping my stuff (images and what not) so I never released that game. The one I'm trying to figure out how to make now will be a basically open exploration game using only original graphics. (one of them being my avatar. And... she's not normally that violent) I realize it will probably be a challenge to make, but hey, once I get passed the hurdle of people not being able to easily still my stuff, (having it compiled in with the eboot) I think I can do it.

Anyway, great tutorials. This is what the PSP scene needs.
Logged

I have no illusions of grandeur. I consider myself a bad programmer. If any of you experienced guys see something wrong with something I say, please, please correct it. I try my best to be helpful, but I don't want people being misled by bad help.

You look kind of bored. Why not read about my game? http://www.psp-programming.com/forums/index.php?topic=2556.0
MailasG
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 45
0.00 points

View Inventory
Send Money to MailasG

View Profile
« Reply #7 on: September 27, 2006, 08:02:48 PM »

UR A FREAKING GOD!!!!
Logged
psp35
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 13
791.75 points

View Inventory
Send Money to psp35

View Profile
« Reply #8 on: July 11, 2007, 10:31:17 AM »

Christopher@computer ~/megaman
$ make kxploit
psp-gcc -I. -I/usr/local/pspdev/psp/sdk/include -O2 -G0 -Wall -D_PSP_FW_VERSION=
150   -c -o main.o main.c
Assembler messages:
FATAL: can't create main.o: Permission denied
make: *** [main.o] Error 1

Christopher@computer ~/megaman
$ make
psp-gcc -I. -I/usr/local/pspdev/psp/sdk/include -O2 -G0 -Wall -D_PSP_FW_VERSION=
150   -c -o main.o main.c
Assembler messages:
FATAL: can't create main.o: Permission denied
make: *** [main.o] Error 1

Christopher@computer ~/megaman
$ make
psp-gcc -I. -I/usr/local/pspdev/psp/sdk/include -O2 -G0 -Wall -D_PSP_FW_VERSION=
150   -c -o main.o main.c
Assembler messages:
FATAL: can't create main.o: Permission denied
make: *** [main.o] Error 1

have any ideas what could be wrong?

EDIT: nevermind, i found the problem...
« Last Edit: July 11, 2007, 11:57:55 AM by psp35 » Logged
GoldenShadow
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 6
353.50 points

View Inventory
Send Money to GoldenShadow

The who?


View Profile WWW
« Reply #9 on: July 20, 2007, 12:28:55 PM »

I'm sorry I resurrected this topic (although the guy before me did, but no matter) but I was wondering if there's a part 4 in the making of this great tutorial? Maybe some expansions on how to move the screen with the player (scrolling, like any other random RPG), or how to use menu screens during gameplay (again, such as random RPGs)

Of course, the menu subject might be easy. Could it be simply storing player X and Y in a global variable, accessing a new codefile (eg. mainmenu.c) and once going back to map, restoring the X and Y to the player? But what will that bring to the screen that is scrolled, or the enemy or <insert static/non-static object>?

As you see... I'm -very- curious about the C/C++ game dev. Which leads me back to my initial question; is there a part 4 in the making?

Thanks for reading this. ^_^;
Logged

Expect the unexpected...
2.6,CRACKED!
Full Member
***

Karma: +0/-37
Offline Offline

Posts: 111
3864.87 points

View Inventory
Send Money to 2.6,CRACKED!


View Profile
« Reply #10 on: July 21, 2007, 12:06:48 AM »

its been practicaly a year, and IWN isnt a slow dev, plus look at progress logs, and you'll see what hes working on..
Logged
GoldenShadow
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 6
353.50 points

View Inventory
Send Money to GoldenShadow

The who?


View Profile WWW
« Reply #11 on: July 21, 2007, 01:32:15 AM »

Yeah... you're right... too bad though. ^_^; Ah well.. Smile Maybe someday... XD
Logged

Expect the unexpected...
Flatmush
Has a normal user title
Administrator
Hero Member
*

Karma: +84/-26
Offline Offline

Posts: 1046
12906.27 points

View Inventory
Send Money to Flatmush

The Omniscient One


View Profile WWW
« Reply #12 on: July 21, 2007, 03:03:02 PM »

Part 4 is learning to research independently, for this part you have to search 'the google' for useful information then make a game. Razz
Logged

Firmware History: 2.60 -> 2.71 -> 1.50 -> 3.03oe-c

I am nerdier than 66% of all people. Are you nerdier? Click here to find out!I am 62% loser. What about you? Click here to find out!NerdTests.com User Test: The Can I Run A Business Test.

Hehe I'm a "Hero Member" because I bought posts back when they were in the shop.

Creator of FlatEditPSP, funcLib and flAstro
GoldenShadow
Newbie
*

Karma: +0/-0
Offline Offline

Posts: 6
353.50 points

View Inventory
Send Money to GoldenShadow

The who?


View Profile WWW
« Reply #13 on: July 22, 2007, 08:37:49 AM »

'the google' ...

No, thanks. I'll just go through a few books, some references and I'll manage.

You can use 'the google', if you want.  Wink
Logged

Expect the unexpected...
Raphael
Global Moderator
Hero Member
*

Karma: +230/-10
Offline Offline

Posts: 1431
193700.11 points

View Inventory
Send Money to Raphael


View Profile WWW
« Reply #14 on: July 22, 2007, 09:04:23 AM »

Now I'm about to ask myself how you will know which books are best for reading without doing some information gathering first. Denying the usage of internet search sites is as denying to use the telephone because you can contact people by mail.

PS: Don't come up with "google always just throws useless ****". That's not true at all if you know how to correctly *use* such internet searches - which is no rocket science.
Logged

Don't push the river, it flows.
http://wordpress.fx-world.org - my devblog
http://wiki.fx-world.org - VFPU documentation wiki
http://www.homebrew-illuminati.co.uk - serious homebrew development for all platforms
Alexander Berl
"A good mod is a combination playground monitor, priest, big brother/sister, psychiatrist, professor and more."
Pages: [1] 2
Print
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.11 | SMF © 2006-2009, Simple Machines LLC Valid XHTML 1.0! Valid CSS!
Page created in 0.285 seconds with 39 queries.
Sister Sites: Guitar Hero 4   BrokeniTouch.com