6 분 소요


image
In today’s article, we’re going to see drawing the sprites on the screen.

Disclaimer

This code is written only in C. I used OpenGL to implement the 3D drawing in C. Shout out to ‘3D Sage’ who gave me an inspiration and way to make a game.

Code Breakdown

main.c

void drawPlayerSprite() {
    int spriteSize = 32; // the sprite is 32x32 pixels
    int pixelSize = 15; // size of each 'pixel' of the sprite on the screen
    int screenPosX = 480 - (spriteSize * pixelSize); // center of the sprite on the screen width
    int screenPosY = 640 - (spriteSize * pixelSize);

    int(*sprite)[32][32] = (currentFrame == 0) ? &playerSprite1 : &playerSprite2;

    for (int y = 0; y < spriteSize; y++) {
        for (int x = 0; x < spriteSize; x++) {
            if ((*sprite)[y][x] == 1) { // If the value is 1, color white
                glColor3f(1.0, 1.0, 1.0);
            }
            else if ((*sprite)[y][x] == 2) { // If the value is 2, color black
                glColor3f(0.0, 0.0, 0.0);
            }
            else {
                continue;
            }
            glBegin(GL_QUADS);
            glVertex2i(screenPosX + x * pixelSize, screenPosY + y * pixelSize);
            glVertex2i(screenPosX + x * pixelSize + pixelSize, screenPosY + y * pixelSize);
            glVertex2i(screenPosX + x * pixelSize + pixelSize, screenPosY + y * pixelSize + pixelSize);
            glVertex2i(screenPosX + x * pixelSize, screenPosY + y * pixelSize + pixelSize);
            glEnd();
        }
    }
}

To draw in OpenGL, you need a function named display(){...}. C is procedual program so, OpenGL draws in order of funtions called.
void drawPlayerSprite() is the main function for drawing the player. I’ve used TPS(Third Person Shoot), Over the Shoulder View like “Resident Evil 4”

int spriteSize = 32; int pixelSize = 15; int screenPosX = 480 - (spriteSize * pixelSize); int screenPosY = 640 - (spriteSize * pixelSize); I’ve draw sprite in $32*32$. int pixelSize=15 can make a change of sprite size drawing in front of the screen. int screenPosX and int screenPosY is showing where the sprite is drawn.
int(*sprite)[32][32] = (currentFrame == 0) ? &playerSprite1 : &playerSprite2; I’ve used pointer to point 2 different sprites. Two sprites are for walking animation. Sprite can be found in “sprites.h” and “sprites.c”. The following Nested For loop is for drawing sprites in different color. Player sprite is drawn in 2 colors. And each array is defined as 0, 1, 2. If array has a value of 1 pixel is colored as white. If array is 2, pixel is colored with black.

glBegin(GL_QUADS) to glEND() is for drawing in OpenGL. It’s drawn by GL_QUADS not GL_POINTS or GL_LINES. Following has the same mechanism with drawPlayerSprite()

void drawZoomSprite() {
    int spriteSize = 32;
    int pixelSize = 15;
    int screenPosX = 480 - (spriteSize * pixelSize);
    int screenPosY = 640 - (spriteSize * pixelSize);

    int(*zSprite)[32][32] = (currentFrame == 0) ? &zoomSprite1 : &zoomSprite2;

    for (int y = 0; y < spriteSize; y++) {
        for (int x = 0; x < spriteSize; x++) {
            if ((*zSprite)[y][x] == 1) { // If the value is 1, color white
                glColor3f(1.0, 1.0, 1.0);
            }
            else if ((*zSprite)[y][x] == 2) { // If the value is 2, color black
                glColor3f(0.0, 0.0, 0.0);
            }
            else {
                continue;
            }
            glBegin(GL_QUADS);
            glVertex2i(screenPosX + x * pixelSize, screenPosY + y * pixelSize);
            glVertex2i(screenPosX + x * pixelSize + pixelSize, screenPosY + y * pixelSize);
            glVertex2i(screenPosX + x * pixelSize + pixelSize, screenPosY + y * pixelSize + pixelSize);
            glVertex2i(screenPosX + x * pixelSize, screenPosY + y * pixelSize + pixelSize);
            glEnd();
        }
    }
}

void drawFireSprite() {
    int spriteSize = 32;
    int pixelSize = 15;
    int screenPosX = 480 - (spriteSize * pixelSize);
    int screenPosY = 640 - (spriteSize * pixelSize);

    for (int y = 0; y < spriteSize; y++) {
        for (int x = 0; x < spriteSize; x++) {
            if ((fireSprite)[y][x] == 1) {glColor3f(1.0, 1.0, 1.0);} // If the value is 1, color white
            else if ((fireSprite)[y][x] == 2) { glColor3f(0.0, 0.0, 0.0);}// If the value is 2, color black
            else if ((fireSprite)[y][x] == 3) {glColor3f(1.0, 0.0, 0.0);} // If the value is 2, color black
            else if ((fireSprite)[y][x] == 4) {glColor3f(1.0, 1.0, 0.0);} // If the value is 2, color black
            else {continue;}
            glBegin(GL_QUADS);
            glVertex2i(screenPosX + x * pixelSize, screenPosY + y * pixelSize);
            glVertex2i(screenPosX + x * pixelSize + pixelSize, screenPosY + y * pixelSize);
            glVertex2i(screenPosX + x * pixelSize + pixelSize, screenPosY + y * pixelSize + pixelSize);
            glVertex2i(screenPosX + x * pixelSize, screenPosY + y * pixelSize + pixelSize);
            glEnd();

        }
    }
}

Drawing zoomSprite and fireSprite has the same mechanism with drawPlayerSprite(). I made zoomSprite and fireSprite additionally to make a shooting mechanism and accurate shooting.

Drawing MonsterSprite

Drawing MonsterSprite is a little bit different. For the PlayerSprite, it’s drawn over the screen. So, you don’t need to modify the location or pixels, making it smaller when it goes far away. But, Drawing Monster Sprite is different. Monster keeps moving so, we need to trace the location of Monster.

typedef struct
{
    int type;       //static, key, enemy
    int state;      //on, off
    int map;        //texture to show
    double x, y, z;    //position
}sprite; sprite sp[4];
int depth[120];


void drawMonsterSprite()
{
    int x, y, s;

    int spx = (int)sp[0].x >> 6, spy = (int)sp[0].y >> 6;
    int spx_add = ((int)sp[0].x + 15) >> 6, spy_add = ((int)sp[0].y + 15) >> 6;
    int spx_sub = ((int)sp[0].x - 15) >> 6, spy_sub = ((int)sp[0].y - 15) >> 6;

    if (sp[0].x > px && mapW[spy * 8 + spx_sub] == 0) { sp[0].x -= 0.03 * fps; }
    if (sp[0].x < px && mapW[spy * 8 + spx_add] == 0) { sp[0].x += 0.03 * fps; }
    if (sp[0].y > py && mapW[spy_sub * 8 + spx] == 0) { sp[0].y -= 0.03 * fps; }
    if (sp[0].y < py && mapW[spy_add * 8 + spx] == 0) { sp[0].y += 0.03 * fps; }


    for (s = 0; s < 2; s++) {

        float sx = sp[s].x - px;
        float sy = sp[s].y - py;
        float sz = sp[s].z;

        float CS = cos(degToRad(pa)), SN = sin(degToRad(pa)); // rotate based on player side
        float a = sy * CS + sx * SN;
        float b = sx * CS - sy * SN;
        sx = a; sy = b;

        sx = (sx * 108.0 / sy) + (120 / 2); //convert to screen 
        sy = (sz * 108.0 / sy) + (80 / 2);

        int scale = 45 * 80 / b;
        if (scale < 0) { scale = 0; }
        if (scale > 120) { scale = 120; }

        //texture
        float t_x = 0, t_y = 31, t_x_step = 32.0 / (float) scale, t_y_step = 32.0/(float) scale;

        for (x = sx - scale / 2; x < sx + scale / 2; x++)
        {
            t_y = 31;
            for (y = 0; y < scale; y++) 
            {
                if (x > 0 && x < 120 && b < depth[x]) //check the depth and see draw if it's on screen.
                {
                    
                    int pixel = ((int)t_y * 32 + (int)t_x) * 3;
                    int red = monster_walkSprite1[pixel + 0];
                    int green = monster_walkSprite1[pixel + 1];
                    int blue = monster_walkSprite1[pixel + 2];
                    if (red != 255, green != 255, blue != 255) {
                        glPointSize(8);
                        glColor3ub(red, green, blue);
                        glBegin(GL_POINTS);
                        glVertex2i(x * 8, sy * 8 - y * 8);
                        glEnd();
                    }
                    
                    t_y -= t_y_step; if (t_y < 0) { t_y = 0; }
                }
            }
            t_x += t_x_step;
        }
    }
}

struct on the top of the code is for Monsters. Type is defined to differentiate keys, monsters, or others. This is to reuse the funtion. state is for decide to draw or not. int depth[120] is counting how may rays to draw. In this case, we are drawing 120 rays.

카테고리:

업데이트: