PS2 [Programming Tutorial] (C PS2SDK) Space Shooter by Drakonchik

Drakonchik

Member
Hello everyone! About a year ago I went through this forum, and inside the Discord server, in hopes to see that someone had posted some sort of tutorial on how I can start making some games. Most probably others too had looked at some samples, and what they often would get, go see samples at the samples folder. Then what?

This might sound frustrating for those who are beginners in programming and want to try out it for PS2. Mainly the idea is that on PS2 it should have been easier, because the system is older... That might be the assumption, yet you are dead wrong my friend.

What awaits you in this tutorial is pain and suffering, which will result into this:

Now with that warm greeting by TnA I believe we can see what we have in store, a game with multiple states, falling asteroids and a spaceship that will shoot out a bullet. And last but not least, an actual animation.

Now before you all have all high hopes about some SSS class programming, hold your horses I wrote this game about 7 months ago? Spent a lot of time trying to figure out how to make things actually work.

Those who want to do this on Windows Machine... Well... Good Luck!

This is actually like those cooking writings, where users wait to see the needed ingredients, while the author writes about how they got the idea from the walk in the park. Nobody reads them. Like you could confess to murderer in these texts and nobody would notice it.

Anyway... where was I? Oh yeah let's start with setting up folders. Now you can do it the way you see it best for doing things. I don't judge if you'll put it all in one big pile of mess. You do you! I've found that using a structure to separate things make it all much easier.

This is a nice way to set up the folders like this
unknown.png

Data folder is where we will write most of the scripts. Graphics is where we will put in our sprites and images. And finally the Audio is for Audi 0 series car. Why not BMW tho...:indecisiveness:
You will need to add one more file in this parent folder. Called "audsrv.irx" I will add it to attachments. But I am sure you can find it out here too.

Let's open up the main.c file. We should start writing some code then.

I've got no clue how to write forum posts, but I do know how fonts work and how words work
------------------------CODE-----------------------
#include <stdio.h>
#include <malloc.h>

#include <gsKit.h>
#include <dmaKit.h>
#include <gsToolkit.h>

int main()
{

u64 Black = GS_SETREG_RGBAQ(0x00,0x00,0x00,0x00,0x00);

u64 TexCol = GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00);

GSGLOBAL *gsGlobal = gsKit_init_global();
// GS_MODE_PAL_I
// GS_MODE_VGA_640_60

// Check PAL or NTSC (Code by Daniel Santos)
gsGlobal->PSM = GS_PSM_CT24;
gsGlobal->PSMZ = GS_PSMZ_16S;

gsGlobal->Mode = gsKit_check_rom();
if(gsGlobal->Mode == GS_MODE_PAL)
{
gsGlobal->Height = 512;
}
else
{
gsGlobal->Height = 448;
}
dmaKit_init(D_CTRL_RELE_OFF,D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC,
D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);

// Initialize the DMAC
dmaKit_chan_init(DMA_CHANNEL_GIF);

gsKit_init_screen(gsGlobal);

gsGlobal->PrimAlphaEnable = GS_SETTING_ON;

// Sets up the mode for drawing.
// GS_ONESHOT-> every draw clears the draw queue
// GS_PERSISTENT-> gskit stores a draw queue and whenever you draw again, it just adds onto that queue
gsKit_mode_switch(gsGlobal, GS_ONESHOT);

gsKit_clear(gsGlobal, Black);

while(1)
{
gsKit_queue_exec(gsGlobal);
gsKit_sync_flip(gsGlobal);

}

sleep(60); // sleep before boing bai bai

return 0;
}

------------------------CODE END-----------------------
Whoa... what is that. Do I see HEX?
raf,750x1000,075,t,192033:321fc38aa7.jpg


IKR But don't worry, I myself don't fully understand what it all means either.... What I even more don't understand is what is coming next. Makefile. Which looks like this.
------------------------MAKEFILE-----------------------
EE_BIN = SpaceShooterTutorial.elf
EE_OBJS = main.o
EE_LIBS = -L$(PS2SDK)/ports/lib -L$(PS2DEV)/gsKit/lib/ -Lmodules/ds34bt/ee/ -Lmodules/ds34usb/ee/ -lpatches -lfileXio -lpad -ldebug -lmath3d -ljpeg -lfreetype -lgskit_toolkit -lgskit -ldmakit -lpng -lz -lmc -laudsrv -lelf-loader -laudsrv -lc

EE_INCS += -I$(PS2DEV)/gsKit/include -I$(PS2SDK)/ports/include -I$(PS2SDK)/ports/include/freetype2 -I$(PS2SDK)/ports/include/zlib
#EE_CFLAGS is passed to GCC when compiling
#EE_LDFLAGS is passed to GCC/ld when linking

EE_CFLAGS = -I$(PS2DEV)/gsKit/include
EE_LDFLAGS = -L$(PS2DEV)/gsKit/lib

all: $(EE_BIN) audsrv.irx

audsrv.irx:
cp $(PS2SDK)/iop/irx/audsrv.irx $@

clean:
rm -f $(EE_BIN) $(EE_OBJS)

run: $(EE_BIN)
ps2client execee host:$(EE_BIN)

reset:
ps2client reset

include $(PS2SDK)/samples/Makefile.pref
include $(PS2SDK)/samples/Makefile.eeglobal

#The GS Enviroment Variable and path had to be removed, so that the pad script would function.

------------------------MAKEFILEEND-----------------------
And after that. Let's compile and see if we have some result
To do that, you open up Terminal and....
unknown.png

Wow you can show this to your mom and say you hacked the National Bank! As it looks like that, but in reality, we just compiled the code.

I'll post each step in Github, so you can either try out it all for yourself understand and progress from any step you want. Or you can just download it and say that you managed to do it all by yourself!
Link: https://github.com/AndyDragon1998/SpaceShooterTutorial

Cheers! Next step coming soon! We will do State Machine!

 
Alrighty, let's start simple, and by simple, I mean lets do the state machine. It will be useful to use whenever you need to switch states and do things that you need. Let's make an include folder and some C files
unknown.png


That was easy... If you like to right click and make new file that way... you do you.
Anyway, using the text editor of your choice, let's go and open the file called stateManager.h

h stands for Header. Most C files have a header file... It was made that way to do things. Yes. I don't know why, I am not the author of the language. But inside the header we have to write something.
----------------------CODE----------------
#ifndef STATE_MANAGER
#define STATE_MANAGER

#include <gsKit.h>
#include <dmaKit.h>
#include <gsToolkit.h>

typedef void _voidCallBack();

typedef struct
{
_voidCallBack *Start;

_voidCallBack *Update;

_voidCallBack *End;

int TransisionIn;

int TransisionOut;
}StateManager;

typedef struct
{
int TransisionOutFrames;
int TransisionInFrames;

StateManager* CurrentState;
StateManager* ChangeTo;

}StateMachine;

void StateMachineStart(StateMachine* machine, StateManager* state, GSGLOBAL* gsGlobal);
void StateMachineChange(StateMachine* machine, StateManager* state, GSGLOBAL* gsGlobal);
void StateMachineUpdate(StateMachine* machine, GSGLOBAL* gsGlobal);

#endif


----------------------CODEEND-------------
This is the Header file for the state Manager. The more understanding of you might already see how this all is going to work. But here is a short explanation. This here is a state machine, which uses structs that has access to functions using function pointers. A really handy thing. This way we can have Many states that we can have for our game. Intro State, gaming state, game over state, even a state that will show user that they suck at the game. A hall of blame, if you will... Enough about that, let's see what is inside stateManager.c
--------------------CODE--------------------
#include "include/stateManager.h"

void StateMachineStart(StateMachine* machine, StateManager* state, GSGLOBAL* gsGlobal)
{
if (state != 0 && machine != 0)
{
machine->ChangeTo = 0;
machine->TransisionOutFrames = -1;
machine->TransisionInFrames = -1;

machine->CurrentState = state;
machine->CurrentState->Start(gsGlobal);
}
}


void StateMachineChange(StateMachine* machine, StateManager* state, GSGLOBAL* gsGlobal)
{
if (state != 0 && machine != 0)
{
machine->ChangeTo = 0;

machine->CurrentState->End(gsGlobal);

machine->CurrentState = state;

machine->CurrentState->Start(gsGlobal);
}
}


void StateMachineUpdate(StateMachine* machine, GSGLOBAL* gsGlobal)
{
if (machine != 0)
{
machine->CurrentState->Update(gsGlobal);
}
}

-------------------CODEEND--------------
Whoa what the hell is this? I did not sign up for that?lots of zeros and logic operations? When do I have to make my game? Patience. This is all part of the plan, once you've made this step and know how to use this code, you can just copy/paste it and use for all projects you want. That's how I do it. You'll notice that the functions have some variables inside them. We have to pass them from main.c file to make them run. It's not that difficult but important to have, due to how SDK draws stuff.

And now, let's take a look at the menuState.h
--------------------CODE--------------------
#ifndef MENU_STATE
#define MENU_STATE

#include "stateManager.h"

void MenuStart(GSGLOBAL* gsGlobal);
void MenuUpdate(GSGLOBAL* gsGlobal);
void MenuEnd(GSGLOBAL* gsGlobal);

extern StateManager MenuState;

#endif

-------------------CODEEND--------------

Was it that bad? You just write MenuStart, MenuUpdate and MenuEnd.

But what is extern StateManager MenuState?

That is something that will help main.c file to know what kind of state it is using... and other states to know what states are out there. This here is a struct. Where is the actual struct? in the menuState.c of course!
--------------------CODE--------------------
#include "include/gameState.h"
#include "include/menuState.h"
#include "include/overState.h"

extern StateMachine GameMachineState;

void MenuStart(GSGLOBAL* gsGlobal)
{

}

void MenuUpdate(GSGLOBAL* gsGlobal)
{

}

void MenuEnd(GSGLOBAL* gsGlobal)
{

}

StateManager MenuState =
{
MenuStart,
MenuUpdate,
MenuEnd
};

-------------------CODEEND--------------
This looks really familiar, if you used to work with XNA, you know what I mean.
Now rinse and repeat the same for all the states..... or you know... just download the github link and do it from there..... I don't know, I am not your dad!

But wait there's more! We have to go back to main.c now! Let's look at the changes here.
--------------------CODE--------------------
#include <stdio.h>
#include <malloc.h>

#include <gsKit.h>
#include <dmaKit.h>
#include <gsToolkit.h>

#include "Data/include/menuState.h"
#include "Data/include/gameState.h"
#include "Data/include/overState.h"


StateMachine GameMachineState;


int main()
{

u64 Black = GS_SETREG_RGBAQ(0x00,0x00,0x00,0x00,0x00);

u64 TexCol = GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00);

GSGLOBAL *gsGlobal = gsKit_init_global();
// GS_MODE_PAL_I
// GS_MODE_VGA_640_60

// Check PAL or NTSC (Code by Daniel Santos)
gsGlobal->PSM = GS_PSM_CT24;
gsGlobal->PSMZ = GS_PSMZ_16S;

gsGlobal->Mode = gsKit_check_rom();
if(gsGlobal->Mode == GS_MODE_PAL)
{
gsGlobal->Height = 512;
}
else
{
gsGlobal->Height = 448;
}
dmaKit_init(D_CTRL_RELE_OFF,D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC,
D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);

// Initialize the DMAC
dmaKit_chan_init(DMA_CHANNEL_GIF);

gsKit_init_screen(gsGlobal);

gsGlobal->PrimAlphaEnable = GS_SETTING_ON;

// Sets up the mode for drawing.
// GS_ONESHOT-> every draw clears the draw queue
// GS_PERSISTENT-> gskit stores a draw queue and whenever you draw again, it just adds onto that queue
gsKit_mode_switch(gsGlobal, GS_ONESHOT);

gsKit_clear(gsGlobal, Black);



StateMachineStart(&GameMachineState, &MenuState, gsGlobal);


while(1)
{

StateMachineUpdate(&GameMachineState, gsGlobal);


gsKit_queue_exec(gsGlobal);
gsKit_sync_flip(gsGlobal);

}

sleep(60); // sleep before boing bai bai

return 0;
}



--------------------CODEEND-------------
Did you notice the red text? Turns out you can colour text in this forum. I knew that, if you were eagle eyed, you would have noticed that I did that in the above post too hehehe.
So as you can see, we included the states, but not the actual state machine. We don't need it, all we need are the states themselves. And we created a state machine that will tage game state, Over state or Menu state, and swap them around and update them in the loop.

But as you saw, we needed to kickstart the state machine. So we do it with
StateMachineStart(&GameMachineState, &MenuState, gsGlobal);

Really cool stuff right? And after we do the updating of this state. Which is the MenuUpdate function in menuState.c Really handy!

After all this hot stuff is done. We should take one more look at the makefile, and see what should we do next.
-------------------MAKEFILE--------------
EE_OBJS = main.o Data/stateManager.o Data/menuState.o Data/gameState.o Data/overState.o
-------------------MAKEFILEEND--------

On second line, where you had only main.o now you add these 4 new files. We need to compile them, right?

Let's see the result... ANNNd?
unknown.png


...What is that? why so much text but before little.... oh yikes...:cower::cower::cower::cower: well good luck!
Link:https://github.com/AndyDragon1998/SpaceShooterTutorial/tree/main/Tutorial2

#ifndef MENU_STATE
#define MENU_STATE

#include "stateManager.h"

void MenuStart(GSGLOBAL* gsGlobal);
void MenuUpdate(GSGLOBAL* gsGlobal);
void MenuEnd(GSGLOBAL* gsGlobal);

extern StateManager MenuState;

#endif

-------------------CODEEND--------------
 
Nice to see some tutorials that could help more people getting into homebrew development. :)
Also sharing everything on GitHub is great

Btw, for completeness I'd add a "tutorial 0", and explain:
- how to install the PS2 toolchain
- install the PS2 open source SDK
- get the compiler running and test compiling a sample from the SDK
- perhaps explain how to run the compiled sample on the PS2 or emulator

Hopefully we'll see more tutorials soon! :D cheers
 
Tutorial 0:
Setup: https://www.psx-place.com/threads/wsl-1-wsl2-sdk-installation-for-multiple-platforms.31934/
You can play your .elf files in Emulator PCSX2 System->run elf. Remember to first go to System -> Game Settings - Enable Host Filesystem;


Ok now let's get back to actual tutorial

Previously on the tutorial: Black bland Screen

So today we need to have something in our game. What is something that almost all video games have. I think you all know the answer to that, they have electricity that power them... yea and they also have a title screen. Let's do that today!

First we open the GameScene.c and write something there. Let me show you the newest changes that we will do.
We will load image into memory and we will display it on the screen!
unknown.png

We will also be moving an image on the screen to have feel for how to move objects!

Before we look at menuState.c go to main.c and go to line 59. Comment it out

//gsGlobal->PrimAlphaEnable = GS_SETTING_ON;

Due to us using BMP which are more reliable for using, right now. PNG Support is a little quirky there.

---------------------CODE-----------------------
#include "include/gameState.h"
#include "include/menuState.h"
#include "include/overState.h"

extern StateMachine GameMachineState

GSTEXTURE TitleImage, SmileImage;

u64 colour = GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00);

float posX, posY, movement;


void MenuStart(GSGLOBAL* gsGlobal)
{
gsKit_texture_bmp(gsGlobal, &TitleImage, "host:Graphics/Title.bmp");
gsKit_texture_bmp(gsGlobal, &SmileImage, "host:Graphics/Texty.bmp");
colour = GS_SETREG_RGBAQ(0x80,0x80,0x80,0x80,0x00);
posX = 50.0f;
posY = 50.0f;
movement = 2.0f;

}

void MenuUpdate(GSGLOBAL* gsGlobal)
{

gsKit_prim_sprite_texture(gsGlobal, &TitleImage,0, // X1
0, // Y2
0.0f, // U1
0.0f, // V1
TitleImage.Width+ 0.0f, // X2
TitleImage.Height+ 0.0f, // Y2
TitleImage.Width + 0.0f, // U2
TitleImage.Height+ 0.0f, // V2
2,
colour);

if(posX > 250.0f)
{
movement = movement * -1;
}

if(posX < 50.0f)
{
movement = movement * -1;
}

posX += movement;



gsKit_prim_sprite_texture(gsGlobal, &SmileImage, posX, // X1
posY, // Y2
0.0f, // U1
0.0f, // V1
SmileImage.Width + posX, // X2
SmileImage.Height + posY, // Y2
SmileImage.Width, // U2
SmileImage.Height, // V2
2,
colour);


}

void MenuEnd(GSGLOBAL* gsGlobal)
{

}

StateManager MenuState =
{
MenuStart,
MenuUpdate,
MenuEnd
};

---------------------CODEEND-----------------

WOW Look at all that code. So what is happening here is that we have a GSTexture that we use as vessels for out images. We load them into the VRAM and after that we display them in the update method. This long text has X and Y positions And also U V values. I've added there +0.0f values. Change them to -23.0f and play around with them. If you do so, over time you'll learn how they work and what is what. If I would explain it all to you, we would have everything written for 3 Forum pages. I don't feel like it.

More smart people, please do start discussion on having transparent PNG and how to make them consistently show up on screen. What is max size and other parameters. My tests show that under some specific conditions they do show up, but sometimes they are loaded in VRAM but not displaying.

Link: https://github.com/AndyDragon1998/SpaceShooterTutorial/tree/main/Tutorial3

Say hi to final boss TnA!:chuncky:
 
Back
Top