8
\$\begingroup\$

I implemented the following Tron cycle game so that it can be run on GameCube / Wii devices (quarantine...). Indeed, there exist many tools (I rely on devkitPPC) and I thought a Tron game would be an easy starting point for this kind of development.

I have no experience in C, neither in enforcing "classes" in C, or in game development (the naming might be horrible), therefore, any help would be welcomed!

The directory structure is as follows:

├── Makefile
├── source
│   └── tron.c
└── textures
    ├── ballsprites.png
    └── textures.scf

And the different files are presented below (the only ones I wrote are tron.c and the sprites, all the credit for the rest goes to the team behind the devkit!).

Wall walls[NUM_PLAYERS][NUM_WALLS]; is a global variable, and not an attribute of the VersusEngineManager class, because the emulator kept firing weird memory errors. Any help on this point would be great!

textures.scf

<filepath="ballsprites.png" id="ballsprites" colfmt=6 />

tron.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <gccore.h>

#include <ogc/tpl.h>

#include "textures_tpl.h"
#include "textures.h"

#define DEFAULT_FIFO_SIZE   (256*1024)

static void *frameBuffer[2] = { NULL, NULL};
static GXRModeObj *rmode;

#define NUM_PLAYERS 2
#define NUM_WALLS 20000
#define SPEED 0x200
#define SPRITE_SIZE 4

#define WIDTH 640
#define HEIGHT 480
#define UPPER_MARGIN 20
#define TV_MARGIN 50

typedef struct 
{
    int x, y;
} Point;

bool Point_CheckInRectangle(Point *self, int xmin, int xmax, int ymin, int ymax);
bool Point_CheckCollision(Point *self, Point *other);


typedef struct 
{
    int x, y;
    int dx, dy;
    int image;
} Player;

int Player_init(Player *self, int x, int y, int dx, int dy, int image);
void Player_UpdatePosition(Player *self);

typedef struct
{
    int x, y;
    int image;
} Wall;

int playerLossesCount[NUM_PLAYERS];
Wall walls[NUM_PLAYERS][NUM_WALLS];

typedef struct
{
    int speed;
    int nPlayers;
    int wallIndex;
    Player players[NUM_PLAYERS];
} VersusEngineManager;

int VersusEngineManager_init(VersusEngineManager *self, int speed, int nPlayers);
bool VersusEngineManager_CheckPlayersCollision(VersusEngineManager *self);
bool VersusEngineManager_CheckWallCollision(VersusEngineManager *self, int playerId);
int VersusEngineManager_UpdateGameState(VersusEngineManager *self);
void VersusEngineManager_UpdatePlayerDirectionFromPAD(VersusEngineManager *self, int pad, int playerId);
void VersusEngineManager_UpdatePlayersDirectionFromPADs(VersusEngineManager *self);
void VersusEngineManager_UpdatePlayersDirectionFromPADs(VersusEngineManager *self)
{
    VersusEngineManager_UpdatePlayerDirectionFromPAD(self, 0, 0); 
    VersusEngineManager_UpdatePlayerDirectionFromPAD(self, 1, 1);
}

typedef struct 
{
    int spriteSize;
} SpriteDrawer;

int SpriteDrawer_init(SpriteDrawer *self, int spriteSize);
void SpriteDrawer_Rectangle(SpriteDrawer *self, int x, int y, int width, int height, int image);
void SpriteDrawer_SpriteTex(SpriteDrawer *self, int x, int y, int width, int height, int image);
void SpriteDrawer_AllSprites(SpriteDrawer *self);
void SpriteDrawer_Sprites(SpriteDrawer *self, VersusEngineManager *versusEngineManager);
void SpriteDrawer_Scores(SpriteDrawer *self, VersusEngineManager *versusEngineManager);
void SpriteDrawer_ArenaSprites(SpriteDrawer *self);

GXTexObj texObj;

int main( int argc, char **argv ){
    u32 fb;     // initial framebuffer index
    u32 first_frame;
    f32 yscale;
    u32 xfbHeight;
    Mtx44 perspective;
    Mtx GXmodelView2D;
    void *gp_fifo = NULL;

    GXColor background = {0, 0, 0, 0xff};

    VIDEO_Init();

    rmode = VIDEO_GetPreferredMode(NULL);

    fb = 0;
    first_frame = 1;
    // allocate 2 framebuffers for double buffering
    frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
    frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

    VIDEO_Configure(rmode);
    VIDEO_SetNextFramebuffer(frameBuffer[fb]);
    VIDEO_SetBlack(FALSE);
    VIDEO_Flush();
    VIDEO_WaitVSync();
    if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();

    fb ^= 1;

    // setup the fifo and then init the flipper
    gp_fifo = memalign(32,DEFAULT_FIFO_SIZE);
    memset(gp_fifo,0,DEFAULT_FIFO_SIZE);

    GX_Init(gp_fifo,DEFAULT_FIFO_SIZE);

    // clears the bg to color and clears the z buffer
    GX_SetCopyClear(background, 0x00ffffff);

    // other gx setup
    GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
    yscale = GX_GetYScaleFactor(rmode->efbHeight,rmode->xfbHeight);
    xfbHeight = GX_SetDispCopyYScale(yscale);
    GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
    GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight);
    GX_SetDispCopyDst(rmode->fbWidth,xfbHeight);
    GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter);
    GX_SetFieldMode(rmode->field_rendering,((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));

    if (rmode->aa)
        GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
    else
        GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);


    GX_SetCullMode(GX_CULL_NONE);
    GX_CopyDisp(frameBuffer[fb],GX_TRUE);
    GX_SetDispCopyGamma(GX_GM_1_0);

    // setup the vertex descriptor
    // tells the flipper to expect direct data
    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);


    GX_SetNumChans(1);
    GX_SetNumTexGens(1);
    GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
    GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
    GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);


    GX_InvalidateTexAll();

    TPLFile spriteTPL;
    TPL_OpenTPLFromMemory(&spriteTPL, (void *)textures_tpl,textures_tpl_size);
    TPL_GetTexture(&spriteTPL,ballsprites,&texObj);

    GX_LoadTexObj(&texObj, GX_TEXMAP0);

    guOrtho(perspective,0,479,0,639,0,300);
    GX_LoadProjectionMtx(perspective, GX_ORTHOGRAPHIC);

    PAD_Init();

    srand(time(NULL));

    GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
    guMtxIdentity(GXmodelView2D);
    guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -5.0F);
    GX_LoadPosMtxImm(GXmodelView2D,GX_PNMTX0);

    GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
    GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
    GX_SetAlphaUpdate(GX_TRUE);
    GX_SetColorUpdate(GX_TRUE);

    VersusEngineManager versusEngineManager;
    VersusEngineManager_init(&versusEngineManager, SPEED, NUM_PLAYERS);

    SpriteDrawer spriteDrawer;
    SpriteDrawer_init(&spriteDrawer, SPRITE_SIZE);

    while(1) {

        GX_InvVtxCache();
        GX_InvalidateTexAll();
        GX_ClearVtxDesc();
        GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
        GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);

        VersusEngineManager_UpdatePlayersDirectionFromPADs(&versusEngineManager);
        int gameState = VersusEngineManager_UpdateGameState(&versusEngineManager);
        if(gameState==1) { VersusEngineManager_init(&versusEngineManager, SPEED, NUM_PLAYERS); }

        SpriteDrawer_Sprites(&spriteDrawer, &versusEngineManager);
        GX_DrawDone();

        GX_CopyDisp(frameBuffer[fb],GX_TRUE);

        VIDEO_SetNextFramebuffer(frameBuffer[fb]);
        if(first_frame) {
            VIDEO_SetBlack(FALSE);
            first_frame = 0;
        }
        VIDEO_Flush();
        VIDEO_WaitVSync();
        fb ^= 1;        // flip framebuffer
    }
    return 0;
}


float texCoords[] = {
    0.0 ,0.0 , 0.5, 0.0, 0.5, 0.5, 0.0, 0.5,
    0.5 ,0.0 , 1.0, 0.0, 1.0, 0.5, 0.5, 0.5,
    0.0 ,0.5 , 0.5, 0.5, 0.5, 1.0, 0.0, 1.0,
    0.5 ,0.5 , 1.0, 0.5, 1.0, 1.0, 0.5, 1.0
};


int SpriteDrawer_init(SpriteDrawer *self, int spriteSize)
{
    self->spriteSize = spriteSize;
    return 0;
}


void SpriteDrawer_Sprites(SpriteDrawer *self, VersusEngineManager *versusEngineManager) {
    int spriteSize = self->spriteSize;
    for(int playerId=0;playerId<NUM_PLAYERS;playerId++){
        SpriteDrawer_SpriteTex(self, 
            versusEngineManager->players[playerId].x >> 8, 
            versusEngineManager->players[playerId].y >> 8, 
            spriteSize, 
            spriteSize, 
            versusEngineManager->players[playerId].image);
        for(int wallId=0; wallId < NUM_WALLS; wallId++) {
            SpriteDrawer_SpriteTex(self, 
                walls[playerId][wallId].x >> 8, 
                walls[playerId][wallId].y >> 8, 
                spriteSize, 
                spriteSize, 
                walls[playerId][wallId].image);
        }
    }
    SpriteDrawer_ArenaSprites(self);
    SpriteDrawer_Scores(self, versusEngineManager);
};

void SpriteDrawer_ArenaSprites(SpriteDrawer *self) {
    int spriteSize = self->spriteSize;
    SpriteDrawer_Rectangle(self, TV_MARGIN,UPPER_MARGIN+TV_MARGIN, WIDTH-2*TV_MARGIN-spriteSize, HEIGHT - 2*TV_MARGIN - spriteSize,3);
};

void SpriteDrawer_Rectangle(SpriteDrawer *self, int x,int y,int width,int height,int sprite) {
    int spriteSize = self->spriteSize;
    for(int i=x; i < x+width;i+=spriteSize){ 
        SpriteDrawer_SpriteTex(self, i,y,spriteSize, spriteSize, sprite); 
    }
    for(int i=x; i < x+width+spriteSize;i+=spriteSize){ 
        SpriteDrawer_SpriteTex(self, i,y+height,spriteSize, spriteSize, sprite);
    }
    for(int i=y; i < y+height;i+=spriteSize){ 
        SpriteDrawer_SpriteTex(self, x,i,spriteSize, spriteSize, sprite); 
    }
    for(int i=y; i < y+height;i+=spriteSize){ 
        SpriteDrawer_SpriteTex(self, x+width,i,spriteSize, spriteSize, sprite);
    }
};


void SpriteDrawer_Scores(SpriteDrawer *self, VersusEngineManager *versusEngineManager) {
    int spriteSize = self->spriteSize;
    for(int playerId=0;playerId<NUM_PLAYERS;playerId++){
        for(int score=0;score < playerLossesCount[playerId]; score++){
            SpriteDrawer_SpriteTex(self, TV_MARGIN + ( (score * SPEED) >> 8),  TV_MARGIN + 2* SPEED * (1 + playerId) >> 8, spriteSize, spriteSize, versusEngineManager->players[playerId].image);
        }
    }
};

void SpriteDrawer_SpriteTex(SpriteDrawer *self, int x, int y, int width, int height, int image ) {
    int texIndex = image * 8;

    GX_Begin(GX_QUADS, GX_VTXFMT0, 4);          // Draw A Quad
    GX_Position2f32(x, y);                  // Top Left
    GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
    texIndex+=2;
    GX_Position2f32(x+width-1, y);          // Top Right
    GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
    texIndex+=2;
    GX_Position2f32(x+width-1,y+height-1);  // Bottom Right
    GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
    texIndex+=2;
    GX_Position2f32(x,y+height-1);          // Bottom Left
    GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);
    GX_End();                                   // Done Drawing The Quad 

}

int Player_init(Player *self, int x, int y, int dx, int dy, int image)
{
    self->x = x;
    self->y = y;
    self->dx = dx;
    self->dy = dy;
    self->image = image;
    return 0;
}

void Player_UpdatePosition(Player *self)
{
        self->x += self->dx; 
        self->y += self->dy; 
}

bool Point_CheckInRectangle(Point *self, int xmin, int xmax, int ymin, int ymax) 
{
    return self->x < xmin || self->x > xmax || self->y < ymin || self->y > ymax;
}

bool Point_CheckCollision(Point *self, Point *other)
{
    return self->x == other->x && self->y==other->y;
}

int VersusEngineManager_init(VersusEngineManager *self, int speed, int nPlayers)
{
    self->speed = speed;
    self->nPlayers = nPlayers;
    self->wallIndex = 0;

    Player player0, player1;
    Player_init(&player0, ((WIDTH - SPRITE_SIZE ) / 2 ) << 8,100 << 8,0, SPEED, 0);
    Player_init(&player1, ((WIDTH - SPRITE_SIZE ) / 2 ) << 8,400 << 8,0, -SPEED, 1);

    self->players[0] = player0; 
    self->players[1] = player1; 

    for(int playerId=0;playerId<self->nPlayers;playerId++){
        for(int wallId=0; wallId < NUM_WALLS; wallId++) {
            walls[playerId][wallId].x = 0; 
            walls[playerId][wallId].y = 0; 
            walls[playerId][wallId].image = 2; 
        }
    }
    return 0;
}

bool VersusEngineManager_CheckPlayersCollision(VersusEngineManager *self)
{
    for(int i=0; i < self->nPlayers; i++)
    {
        for(int j=i+1; j < self->nPlayers; j++)
        {
            if(Point_CheckCollision(&self->players[i], &self->players[j]) ) {
                return true; }
        }
    }
    return false;
};

bool VersusEngineManager_CheckWallCollision(VersusEngineManager *self, int playerId)
{
    if(Point_CheckInRectangle(&self->players[playerId], 
            TV_MARGIN<<8,
            WIDTH-SPRITE_SIZE-TV_MARGIN << 8,
            (UPPER_MARGIN + TV_MARGIN) <<8 ,
            (HEIGHT+UPPER_MARGIN-SPRITE_SIZE-TV_MARGIN) << 8)) 
    {return true;}

    for(int i=0;i<NUM_PLAYERS;i++){
        for(int wallId=0; wallId < self->wallIndex; wallId++) {
            if(self->players[playerId].x == walls[i][wallId].x && self->players[playerId].y == walls[i][wallId].y) {
                return true;
            }   
        }
    }
    return false;

}

int VersusEngineManager_UpdateGameState(VersusEngineManager *self)
{
    for(int playerId=0;playerId<self->nPlayers;playerId++){
        Player_UpdatePosition(&self->players[playerId]);

        bool collision = false;
        if (VersusEngineManager_CheckPlayersCollision(self)) {
            collision = true;
        }
        else if(VersusEngineManager_CheckWallCollision(self, playerId)) {
            playerLossesCount[playerId] += 1;
            collision = true;
        }

        walls[playerId][self->wallIndex].x = self->players[playerId].x;
        walls[playerId][self->wallIndex].y = self->players[playerId].y;
        walls[playerId][self->wallIndex].image = playerId;

        if(collision) {return 1;} ;
    }
    self->wallIndex += 1;
    return 0;
};

void VersusEngineManager_UpdatePlayerDirectionFromPAD(VersusEngineManager *self, int pad, int playerId)
{
    PAD_ScanPads();

    if (PAD_ButtonsDown(pad) & PAD_BUTTON_LEFT) {
        if (self->players[playerId].dx ==0) {
            self->players[playerId].dx = -self->speed;
            self->players[playerId].dy = 0;
            return;
        }
    }

    if (PAD_ButtonsDown(pad) & PAD_BUTTON_RIGHT) {
        if (self->players[playerId].dx ==0) {
            self->players[playerId].dx = self->speed;
            self->players[playerId].dy = 0;
            return;
        }
    }

    if (PAD_ButtonsDown(pad) & PAD_BUTTON_UP) {
        if (self->players[playerId].dy ==0) {
            self->players[playerId].dx = 0;
            self->players[playerId].dy = -self->speed;
            return;
        }
    }

    if (PAD_ButtonsDown(pad) & PAD_BUTTON_DOWN) {
        if (self->players[playerId].dy ==0) {
            self->players[playerId].dx = 0;
            self->players[playerId].dy = self->speed; 
            return;
        }
    }
}

Makefile

#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
.SECONDARY:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
endif

include $(DEVKITPPC)/wii_rules

#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET      :=  $(notdir $(CURDIR))
BUILD       :=  build
SOURCES     :=  source
DATA        :=
TEXTURES    :=  textures 
INCLUDES    :=

#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------

CFLAGS  = -g -O2 -Wall $(MACHDEP) $(INCLUDE)
CXXFLAGS    =   $(CFLAGS)

LDFLAGS =   -g $(MACHDEP) -Wl,-Map,$(notdir $@).map

#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS    :=  -logc -lm

#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS :=

#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------

export OUTPUT   :=  $(CURDIR)/$(TARGET)

export VPATH    :=  $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
                    $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
                    $(foreach dir,$(TEXTURES),$(CURDIR)/$(dir))

export DEPSDIR  :=  $(CURDIR)/$(BUILD)

#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES      :=  $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES    :=  $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES      :=  $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES      :=  $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES    :=  $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
SCFFILES    :=  $(foreach dir,$(TEXTURES),$(notdir $(wildcard $(dir)/*.scf)))
TPLFILES    :=  $(SCFFILES:.scf=.tpl)

#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
    export LD   :=  $(CC)
else
    export LD   :=  $(CXX)
endif

export OFILES_BIN   :=  $(addsuffix .o,$(BINFILES)) $(addsuffix .o,$(TPLFILES))
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(sFILES:.s=.o) $(SFILES:.S=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)

export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(addsuffix .h,$(subst .,_,$(TPLFILES)))

#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE  :=  $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
                    $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
                    -I$(CURDIR)/$(BUILD) \
                    -I$(LIBOGC_INC)

#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS :=  $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
                    -L$(LIBOGC_LIB)

export OUTPUT   :=  $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean

#---------------------------------------------------------------------------------
$(BUILD):
    @[ -d $@ ] || mkdir -p $@
    @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile

#---------------------------------------------------------------------------------
clean:
    @echo clean ...
    @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol

#---------------------------------------------------------------------------------
else

#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)

$(OFILES_SOURCES) : $(HFILES)

#---------------------------------------------------------------------------------
# This rule links in binary data with the .bin extension
#---------------------------------------------------------------------------------
%.bin.o %_bin.h :   %.bin
#---------------------------------------------------------------------------------
    @echo $(notdir $<)
    @$(bin2o)

#---------------------------------------------------------------------------------
%.tpl.o %_tpl.h :   %.tpl
#---------------------------------------------------------------------------------
    @echo $(notdir $<)
    @$(bin2o)


-include $(DEPSDIR)/*.d

#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

ballsprites.png

The ball sprite image

\$\endgroup\$
4
  • \$\begingroup\$ textures.scf smells like XML but doesn't have an enclosing XML tag. Is this expected? \$\endgroup\$ – Reinderien Mar 22 '20 at 14:28
  • 1
    \$\begingroup\$ firing weird memory errors. Any help on this point would be great! That isn't what CodeReview is for; for that you'll need to go on StackOverflow. \$\endgroup\$ – Reinderien Mar 22 '20 at 14:29
  • \$\begingroup\$ textures.scf smells like XML but doesn't have an enclosing XML tag. Is this expected? Yes \$\endgroup\$ – RUser4512 Mar 22 '20 at 14:30
  • 1
    \$\begingroup\$ To the contrary, your function names and variable names are lovely. I couldn't do any better. But you need to add some spaces. I won't put this in a review because it's not enough, though. \$\endgroup\$ – S.S. Anne Mar 22 '20 at 16:00
6
\$\begingroup\$

Statics

You have a one-file program, so more of your methods and globals, including these:

int playerLossesCount[NUM_PLAYERS];
Wall walls[NUM_PLAYERS][NUM_WALLS];

GXTexObj texObj;

should be made static.

Declarations for variables

int main( int argc, char **argv ){
    u32 fb;     // initial framebuffer index
    u32 first_frame;
    f32 yscale;
    u32 xfbHeight;
    Mtx44 perspective;
    Mtx GXmodelView2D;
    void *gp_fifo = NULL;

This style is typical for pre-C99 code, but generally it's more difficult to follow the code this way. It's more legible if the variables are declared and initialized closer to where they're actually used.

Spaces

C is generally more free-form than (say) Python when it comes to whitespace, but this:

GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);

still needs more. Probably one space after each comma for this to be legible. Also, your brace style varies wildly; compare

        if(Point_CheckCollision(&self->players[i], &self->players[j]) ) {
            return true; }

with

if(Point_CheckInRectangle(&self->players[playerId], 
        TV_MARGIN<<8,
        WIDTH-SPRITE_SIZE-TV_MARGIN << 8,
        (UPPER_MARGIN + TV_MARGIN) <<8 ,
        (HEIGHT+UPPER_MARGIN-SPRITE_SIZE-TV_MARGIN) << 8)) 
{return true;}

with

        if(self->players[playerId].x == walls[i][wallId].x && self->players[playerId].y == walls[i][wallId].y) {
            return true;
        }  

The last one seems the sanest, but whatever you do, pick a style and stick with it.

Coordinates

Based on a reading of this:

float texCoords[] = {
    0.0 ,0.0 , 0.5, 0.0, 0.5, 0.5, 0.0, 0.5,
// ...
GX_TexCoord2f32(texCoords[texIndex],texCoords[texIndex+1]);

it seems that you're flattening an array of coordinates to be represented as x, y, x, y... Your code would be more legible if you were to use an array of Point structures (you've even declared one yourself; you might as well use it).

Loop combination

This:

for(int i=y; i < y+height;i+=spriteSize){ 
    SpriteDrawer_SpriteTex(self, x,i,spriteSize, spriteSize, sprite); 
}
for(int i=y; i < y+height;i+=spriteSize){ 
    SpriteDrawer_SpriteTex(self, x+width,i,spriteSize, spriteSize, sprite);
}

should become the equivalent

for(int i=y; i < y+height;i+=spriteSize){ 
    SpriteDrawer_SpriteTex(self, x,i,spriteSize, spriteSize, sprite); 
    SpriteDrawer_SpriteTex(self, x+width,i,spriteSize, spriteSize, sprite);
}
\$\endgroup\$
2
\$\begingroup\$

There are no classes in C. However, there are functions.

Regarding:

CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) 

When compiling, always enable the warnings, then fix those warnings. ( for gcc, at a minimum use: -Wall -Wextra -Wconversion -pedantic -std=gnu11 )

the = will cause this macro to be re-evaluated every time it is referenced. Suggest:

CFLAGS := -g -O2 -Wall $(MACHDEP) $(INCLUDE) 

Notice the := rather than =

The declared structs are not given a tag name. This becomes a problem when using a debugger as most debuggers require a tag name to be able to display the individual fields within the struct.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.