Seitenleiste

Community

Spieleprogrammierung

Allgemein

Tutorials

Das Pong-Beispielspiel

Das soll ein Proove-of-Concept-Spiel sein. Der Code ist weder ein gutes Beispiel für guten Programmierstil, noch für ein sauberes GBA-Programm. Man sollte sich unbedingt etwas anderes überlegen, wie man verhindern kann, dass der Ball oder die Schläger eine Spur hinter sich herziehen.

ball.h

#ifndef GBA_PONG_BALL
#define GBA_PONG_BALL
 
#define BALL_X 115
#define BALL_Y 85
#define BALL_SPEED_X 0.2
#define BALL_SPEED_Y 0.1
#define BALL_DIMENSIONS_X 10
#define BALL_DIMENSIONS_Y 10
 
#include "score.h"
#include "paddle.h"
 
struct paddle;
 
struct ball
{
    struct {
        float x, y;
    } speed;
    struct {
        char width, height;
    } dimensions;
 
    float x, y;
};
 
void update ( struct ball* bl, struct score* score );
 
void ball_reset ( struct ball* bl );
void collision ( struct ball* bl, struct paddle* player, struct paddle* cpu);
 
float _abs ( float val );
 
#endif

ball.c

#include <stdlib.h>
 
#include "ball.h"
#include "screen.h"
#include "score.h"
 
float _abs ( float val )
{
    if (val<0)
        return (val*-1);
    return val;
}
 
void update ( struct ball* bl, struct score* score )
{
    bl->x+=bl->speed.x;
    bl->y+=bl->speed.y;
 
    bl->speed.x*=_abs((3/bl->speed.x) - 1/bl->speed.x);
    bl->speed.y*=_abs((3/bl->speed.y) - 1/bl->speed.y);
 
    if (bl->y<0 || bl->y+bl->dimensions.height > HEIGHT)
    {
        bl->speed.y*=-1;
    }
 
    // one of them scored
    if (bl->x+bl->dimensions.width > WIDTH)
    {
        ball_reset(bl);
        bl->speed.x*=-1;
 
        int dice = rand() % 2;
        if (dice==1)
            bl->speed.y*=-1;
 
        clrScr();
        score->player++;
    }
 
    if (bl->x<0)
    {
        ball_reset(bl);
 
        int dice = rand()*10 % 2;
        if (dice==1)
            bl->speed.y*=-1;
 
        clrScr();
        score->cpu++;
    }
}
 
void collision (struct ball* bl, struct paddle* player, struct paddle* cpu)
{
    if (bl->y+bl->dimensions.height>=player->y &&
        bl->x<player->x+player->dimensions.width && 
        bl->y<=player->y+player->dimensions.height)
    {
        bl->speed.x*=-1;
    }
 
    if (bl->y+bl->dimensions.height>=cpu->y && 
        bl->x+bl->dimensions.width>=cpu->x+cpu->dimensions.width && 
        bl->y<=cpu->y+cpu->dimensions.height)
    {
        bl->speed.x*=-1;
    }
}
 
void ball_reset ( struct ball* bl )
{
    bl->speed.y=BALL_SPEED_Y;
    bl->speed.x=BALL_SPEED_X;
    bl->x=BALL_X;
    bl->y=BALL_Y;
}

score.h

#ifndef GBA_PONG_SCORE
#define GBA_PONG_SCORE
 
struct score
{
    char player;
    char cpu;
};
 
#endif

paddle.h

#ifndef GBA_PONG_PADDLE
#define GBA_PONG_PADDLE
 
#define PADDLE_SPEED 2
 
#define REG_INPUT 0x04000130
 
#define KEY_A 1
#define KEY_B 2
#define KEY_SELECT 4
#define KEY_START 8
#define KEY_RIGHT 16
#define KEY_LEFT 32
#define KEY_UP 64
#define KEY_DOWN 128
#define KEY_R 256
#define KEY_L 512
 
#include "ball.h"
 
struct ball;
 
struct paddle
{
    struct
    {
        char width, height;
    } dimensions;
 
    char x, y;
};
 
void intelligence( struct ball* bl, struct paddle* cpu );
 
void input ( struct paddle* player );
 
#endif

paddle.c

#include "paddle.h"
#include "screen.h"
#include "ball.h"
 
void intelligence( struct ball* bl, struct paddle* cpu )
{
    if (bl->y>cpu->y+cpu->dimensions.height-10 && 
       (HEIGHT-(cpu->y+cpu->dimensions.height))>=PADDLE_SPEED)
    {
        cpu->y+=PADDLE_SPEED;
    }
 
    if (bl->y<cpu->y+10 && cpu->y>=PADDLE_SPEED)
    {
        cpu->y-=PADDLE_SPEED;
    }
}
 
void input (struct paddle* player)
{
    int* KEYS = (int*)REG_INPUT;
 
    if (!(*KEYS & KEY_UP) && player->y>=PADDLE_SPEED)
    {
        player->y-=PADDLE_SPEED;
    }
 
    if (!(*KEYS & KEY_DOWN) && 
       (HEIGHT-(player->y+player->dimensions.height))>=PADDLE_SPEED)
    {
        player->y+=PADDLE_SPEED;
    }
}

screen.h

#ifndef GBA_SCREEN
#define GBA_SCREEN
 
#define _REG_FRAMEBUFFER 0x6000000 
#define RGB16(r,g,b)  ((r)+(g<<5)+(b<<10)) 
#define _BACKGROUND RGB16(0,0,0)
#define HEIGHT 160
#define WIDTH 240 
 
//unsigned short* REG_FRAME = (unsigned short*) _REG_FRAMEBUFFER; 
 
void clrScr();
void drawDot(char x, char y, unsigned short color);
void paddle (int height, int width, int beginx, int beginy, unsigned short color);
void drawNumber(char x, char y, char num, unsigned short color);
 
void draw0(char x, char y, unsigned short color);
void draw1(char x, char y, unsigned short color);
void draw2(char x, char y, unsigned short color);
void draw3(char x, char y, unsigned short color);
void draw4(char x, char y, unsigned short color);
void draw5(char x, char y, unsigned short color);
void draw6(char x, char y, unsigned short color);
void draw7(char x, char y, unsigned short color);
void draw8(char x, char y, unsigned short color);
void draw9(char x, char y, unsigned short color);
 
#endif

screen.c

#include "screen.h"
 
unsigned short* REG_FRAME = (unsigned short*) _REG_FRAMEBUFFER; 
 
void clrScr()
{
    for (char x=0; x<240;x++)
    {
        for(char y=0; y<160; y++)
        {
            REG_FRAME[x+y*240]=_BACKGROUND;  
        }
    }
}
 
void drawDot(char x, char y, unsigned short color)
{
    REG_FRAME[x+y*240]=color;
} 
 
void paddle (int height, int width, int beginx, int beginy, unsigned short color)
{
    for (char x=beginx-5; x<beginx+width+5; x++)
    {
        for (char y=beginy-5; y<beginy+height+5; y++)
        {
            if (y>beginy && y<beginy+height && x>beginx && x<beginx+width)
                drawDot(x, y, color);
            else
                drawDot(x, y, _BACKGROUND);
        }
    }
}
 
void drawNumber(char x, char y, char num, unsigned short color)
{
    switch (num)
    {
        case 0:
            draw0(x, y, color);
            break;
        case 1:
            draw1(x, y, color);
            break;
        case 2:
            draw2(x, y, color);
            break;
        case 3:
            draw3(x, y, color);
            break;
        case 4:
            draw4(x, y, color);
            break;
        case 5:
            draw5(x, y, color);
            break;
        case 6: 
            draw6(x, y, color);
            break;
        case 7:
            draw7(x, y, color);
            break;
        case 8:
            draw8(x, y, color);
            break;
        default:
            draw9(x, y, color);
            break;
    }
}
 
void draw0(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
        REG_FRAME[x+ex+((y+13)*240)]=color;
        REG_FRAME[x+ex+((y+14)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        REG_FRAME[x+((y+ey)*240)]=color;
        REG_FRAME[x+1+((y+ey)*240)]=color;
        REG_FRAME[x+10+((y+ey)*240)]=color;
        REG_FRAME[x+9+((y+ey)*240)]=color;
    }
}
void draw1(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+((y+14)*240)]=color;
        REG_FRAME[x+ex+((y+13)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        REG_FRAME[x+4+((y+ey)*240)]=color;
        REG_FRAME[x+5+((y+ey)*240)]=color;
    }
 
    REG_FRAME[x+3+((y+2)*240)]=color;
    REG_FRAME[x+2+((y+2)*240)]=color;
    REG_FRAME[x+3+((y+1)*240)]=color;
}
void draw2(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
 
        REG_FRAME[x+ex+((y+7)*240)]=color;
        REG_FRAME[x+ex+((y+6)*240)]=color;
 
        REG_FRAME[x+ex+((y+14)*240)]=color;
        REG_FRAME[x+ex+((y+13)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        char ex=10;
 
        if (ey>6) ex=1;
        REG_FRAME[x+ex+((y+ey)*240)]=color;
        REG_FRAME[x+ex-1+((y+ey)*240)]=color;
    }
}
void draw3(char x, char y, unsigned short color)
{
    for (char ey=0; ey<14; ey++)
    {
        REG_FRAME[x+10+((y+ey)*240)]=color;
        REG_FRAME[x+9+((y+ey)*240)]=color;
    }
 
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
 
        REG_FRAME[x+ex+((y+13)*240)]=color;
        REG_FRAME[x+ex+((y+14)*240)]=color;
 
        if (ex>=2)
        {
            REG_FRAME[x+ex+((y+6)*240)]=color;
            REG_FRAME[x+ex+((y+7)*240)]=color;
        }
    }
}
void draw4(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+((y+6)*240)]=color;
        REG_FRAME[x+ex+((y+7)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        char ex=1;
        if (ey>6) ex=5;
        REG_FRAME[x+ex+((y+ey)*240)]=color;
        REG_FRAME[x+ex-1+((y+ey)*240)]=color;
    }
 
    REG_FRAME[x+4+((y+5)*240)]=color;
    REG_FRAME[x+4+((y+4)*240)]=color;
    REG_FRAME[x+5+((y+5)*240)]=color;
    REG_FRAME[x+5+((y+4)*240)]=color;
}
void draw5(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
 
        REG_FRAME[x+ex+((y+7)*240)]=color;
        REG_FRAME[x+ex+((y+6)*240)]=color;
 
        REG_FRAME[x+ex+((y+14)*240)]=color;
        REG_FRAME[x+ex+((y+13)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        char ex=1;
 
        if (ey>6) ex=10;
        REG_FRAME[x+ex+((y+ey)*240)]=color;
        REG_FRAME[x+ex-1+((y+ey)*240)]=color;
    }
}
void draw6(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
 
        REG_FRAME[x+ex+((y+7)*240)]=color;
        REG_FRAME[x+ex+((y+6)*240)]=color;
 
        REG_FRAME[x+ex+((y+13)*240)]=color;
        REG_FRAME[x+ex+((y+14)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        REG_FRAME[x+((y+ey)*240)]=color;
        REG_FRAME[x+1+((y+ey)*240)]=color;
        if (ey>6)
        {
            REG_FRAME[x+10+((y+ey)*240)]=color;
            REG_FRAME[x+9+((y+ey)*240)]=color;
        }
    }
}
void draw7(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
    }
 
    char i=0;
    for (char ey=0; ey<14; ey++)
    {
        REG_FRAME[x+10-i+((y+ey)*240)]=color;
        REG_FRAME[x+9-i+((y+ey)*240)]=color;
        if (ey==5 || ey==10)
            i++;
    }
}
void draw8(char x, char y, unsigned short color)
{
    for (char ey=0; ey<14; ey++)
    {
        REG_FRAME[x+10+((y+ey)*240)]=color;
        REG_FRAME[x+9+((y+ey)*240)]=color;
 
        REG_FRAME[x+((y+ey)*240)]=color;
        REG_FRAME[x+1+((y+ey)*240)]=color;
    }
 
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
 
        REG_FRAME[x+ex+((y+13)*240)]=color;
        REG_FRAME[x+ex+((y+14)*240)]=color;
 
        REG_FRAME[x+ex+((y+7)*240)]=color;
        REG_FRAME[x+ex+((y+6)*240)]=color;
    }
}
void draw9(char x, char y, unsigned short color)
{
    for (char ex=0; ex<10; ex++)
    {
        REG_FRAME[x+ex+(y*240)]=color;
        REG_FRAME[x+ex+((y+1)*240)]=color;
 
        REG_FRAME[x+ex+((y+7)*240)]=color;
        REG_FRAME[x+ex+((y+6)*240)]=color;
 
        REG_FRAME[x+ex+((y+13)*240)]=color;
        REG_FRAME[x+ex+((y+14)*240)]=color;
    }
 
    for (char ey=0; ey<14; ey++)
    {
        if (ey<6)
        {
            REG_FRAME[x+((y+ey)*240)]=color;
            REG_FRAME[x+1+((y+ey)*240)]=color;
        }
        REG_FRAME[x+10+((y+ey)*240)]=color;
        REG_FRAME[x+9+((y+ey)*240)]=color;
    }
}

Main.c

#define RGB16(r,g,b)  ((r)+(g<<5)+(b<<10)) 
 
#include "screen.h"  
#include "ball.h"
#include "score.h"
#include "paddle.h"
 
#include <stdlib.h>
 
int main()
{
    *(unsigned long*)0x4000000 = 0x403;
    srand( 0 );
 
    struct ball bl;
    bl.x=BALL_X;
    bl.y=BALL_Y;
    bl.speed.x=BALL_SPEED_X;
    bl.speed.y=BALL_SPEED_Y;
    bl.dimensions.width=BALL_DIMENSIONS_X;
    bl.dimensions.height=BALL_DIMENSIONS_Y;
 
    struct score score;
    score.player=0;
    score.cpu=0;
 
    struct paddle cpu_paddle;
    cpu_paddle.x=225;
    cpu_paddle.y=20;
    cpu_paddle.dimensions.height=30;
    cpu_paddle.dimensions.width=5;
 
    struct paddle player_paddle;
    player_paddle.x=10;
    player_paddle.y=20;
    player_paddle.dimensions.height=30;
    player_paddle.dimensions.width=5;   
 
    while(1)
    {    
        paddle (player_paddle.dimensions.height,  player_paddle.dimensions.width,  
                player_paddle.x, player_paddle.y, RGB16(31, 0,0)); 
 
        paddle (cpu_paddle.dimensions.height, cpu_paddle.dimensions.width, 
                cpu_paddle.x, cpu_paddle.y, RGB16(31, 0,0));
 
        update(&bl, &score);
        collision(&bl, &player_paddle, &cpu_paddle);
 
        intelligence(&bl, &cpu_paddle);
 
        input (&player_paddle);
 
        drawNumber(40, 5, score.player, RGB16(31,31,31));
        drawNumber(190, 5, score.cpu, RGB16(31,31,31));
 
        // Ball
        paddle (bl.dimensions.width, bl.dimensions.height, 
                (char) bl.x, (char) bl.y, RGB16( 0,31,0));
    }
}

Make.sh

Ein Bash-Skript zum Bauen des Spiels. Der Compiler liegt in /devkitadv/bin ; der Programmcode liegt in adv/ . Ich benutze hier nicht den Linux-Port des Compilers, sondern den Windows-Port und führe den unter Linux mit Wine aus. Das geht besser als einen uralten GCC auf dem System zu haben.

cd ../devkitadv/bin
 
wine gcc.exe -o ../../adv/hello.elf ../../adv/main.c ../../adv/screen.c \
    ../../adv/ball.c ../../adv/paddle.c -std=c99 -Wall -pedantic
wine objcopy.exe -O binary ../../adv/hello.elf ../../adv/hello.gba