需要帮助在C中使用结构的二维数组

时间:2017-04-25 21:46:59

标签: c arrays pointers sdl

我正在使用C语言进行俄罗斯方块游戏,我正在尝试存储已经落在二维数组中的块,然后在屏幕上绘制它们并随后使用它们。我试图通过引用创建一个函数来将块添加到数组中。具体来说,我想知道如何将数据块存储在数组中,以便我可以在之后使用draw(Block * block,SDL_Renderer * renderer)。我创建了一个指针矩阵,我试图在它落到地面后将gameBlock中的块添加到它中。现在块下降后,它就会停留在那里,即使我在其上调用initGameBlock。我不知道发生了什么,请帮忙。我正在添加块并重新初始化" activeBlock"在updateGameBlock中。如何使这个矩阵起作用?

#include <stdio.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL_image.h>

//Initialize the constants for the screen width, height and block width
const int width = 600;
const int height = 800;
const int bwidth = 40;

//Enum for storing the types of blocks
typedef enum {BLOCK, LINE, CHAIR, TABLE, BED} Type; 

//This variable is used in the update function in order to make timing work
float prevTicks = 0;

//Function that returns ceiling of a number
int mceil(double num){
    return (int)num + 1;
}

//Struct ofr position/velocity
typedef struct{
    float x, y;
} Vector2;

//Function to keep a value between some constrains
void limit(int* value, int min, int max){
    if(*value > max){
        *value = max;
    }
    if(*value < min){
        *value = min;
    }
}

//The blocks in the game
typedef struct Block{
    Vector2 position;
    SDL_Texture* texture;
    SDL_Rect rect;
} Block;

//"Consturctor" for the block
void initBlock(Block* block, float x, float y, SDL_Texture* texture){
    block->position.x = x;
    block->position.y = y;
    block->rect.x = (int) x - bwidth / 2;
    block->rect.y = (int) y - bwidth / 2;
    block->rect.w = bwidth;
    block->rect.h = bwidth;
    block->texture = texture;
}

//The gameblock consisting of 4 blocks
typedef struct GameBlock{
    Vector2 position;
    Vector2 velocity;
    int falling;
    Type type;
    Block blocks[4];
    SDL_Texture* texture;
} GameBlock;

//"Constructor" for the falling blocks
void initGameBlock(GameBlock* gameBlock, float x, float y, float xvel, float yvel, Type type, SDL_Texture* texture){
    gameBlock->falling = 1; //Variable which tells whether the block is falling
    gameBlock->position.x = x;  
    gameBlock->position.y = y;
    gameBlock->velocity.x = xvel;   
    gameBlock->velocity.y = yvel;
    gameBlock->type = type;
    gameBlock->texture = texture;
    switch(gameBlock->type){
        //Depending on the type of the gameBlock, arranges the blocks in a certain way
        case BLOCK: {
            initBlock(&(gameBlock->blocks[0]), gameBlock->position.x - bwidth / 2, gameBlock->position.y - bwidth/2, texture);
            initBlock(&(gameBlock->blocks[1]), gameBlock->position.x + bwidth / 2, gameBlock->position.y - bwidth/2, texture);
            initBlock(&(gameBlock->blocks[2]), gameBlock->position.x + bwidth / 2, gameBlock->position.y + bwidth/2, texture);
            initBlock(&(gameBlock->blocks[3]), gameBlock->position.x - bwidth / 2, gameBlock->position.y + bwidth/2, texture);
            break;
        }
        case LINE: {
            initBlock(&(gameBlock->blocks[0]), gameBlock->position.x - bwidth / 2 - bwidth, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[1]), gameBlock->position.x + bwidth / 2 + bwidth, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[2]), gameBlock->position.x + bwidth / 2, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[3]), gameBlock->position.x - bwidth / 2, gameBlock->position.y, texture);
            break;
        }
        case CHAIR: {
            initBlock(&(gameBlock->blocks[0]), gameBlock->position.x, gameBlock->position.y - bwidth, texture);
            initBlock(&(gameBlock->blocks[1]), gameBlock->position.x + bwidth, gameBlock->position.y - bwidth, texture);
            initBlock(&(gameBlock->blocks[2]), gameBlock->position.x - bwidth, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[3]), gameBlock->position.x, gameBlock->position.y, texture);
            break;
        }
        case TABLE: {
            initBlock(&(gameBlock->blocks[0]), gameBlock->position.x, gameBlock->position.y - bwidth, texture);
            initBlock(&(gameBlock->blocks[1]), gameBlock->position.x + bwidth, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[2]), gameBlock->position.x - bwidth, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[3]), gameBlock->position.x, gameBlock->position.y, texture);
            break;
        }
        case BED: {
            initBlock(&(gameBlock->blocks[0]), gameBlock->position.x, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[1]), gameBlock->position.x + bwidth, gameBlock->position.y - bwidth, texture);
            initBlock(&(gameBlock->blocks[2]), gameBlock->position.x - bwidth, gameBlock->position.y, texture);
            initBlock(&(gameBlock->blocks[3]), gameBlock->position.x + bwidth, gameBlock->position.y, texture);
            break;
        }
    }   
}

//The matrix containing blocks on the ground
Block* blocks[20][15] = {NULL};

//Function to add a block in this matrix
void addBlock(Block* block, Block* blocks[][15]){
    int col = (int) (block->position.x / bwidth);
    int row = (int) (block->position.y / bwidth);
    blocks[row][col] = block;
}

//Function to add the blocks from a gameBlock in the matrix
void addBlocks(GameBlock* gameBlock){
    for(unsigned int i = 0; i < 4; i++){
        addBlock(&(gameBlock->blocks[i]), blocks);
    }
}

//The logic for the gameBlock
void updateGameBlock(GameBlock** gameBlock){
    //If the gameObject is already on the ground do this:
    if((*gameBlock)->position.y > height - bwidth && (*gameBlock)->falling == 1){
        //Tell that it's not falling
        (*gameBlock)->falling = 0;
        //Add the blocks from the gameBlock into the matrix
        addBlocks(*gameBlock);
        //Reset the pointer and create a new gameBlock at the top
        *gameBlock = NULL;
        *gameBlock = (GameBlock*) malloc(sizeof(GameBlock));
        initGameBlock(*gameBlock, width / 2, bwidth / 2, 1.f, 1.f, BED, (*gameBlock)->texture);
    }
    //If the game object is still in the air
    if((*gameBlock)->falling == 1){
        //This makes it so that the object falls in steps
        if(SDL_GetTicks() - prevTicks >= 100){      
            (*gameBlock)->position.y += (*gameBlock)->velocity.y*bwidth/2;
        }
        //Update the position of the blocks in the gameBlock(all at the same time)
        for(unsigned int i = 0; i < 4; i++){
            (*gameBlock)->blocks[i].rect.x += (*gameBlock)->velocity.x*bwidth;
            (*gameBlock)->blocks[i].position.x += (*gameBlock)->velocity.x*bwidth;
            if(SDL_GetTicks() - prevTicks >= 100){      
                (*gameBlock)->blocks[i].rect.y += (*gameBlock)->velocity.y*bwidth/2;
                (*gameBlock)->blocks[i].position.y += (*gameBlock)->velocity.y*bwidth/2;
                if(i == 3)
                    prevTicks = SDL_GetTicks();
            }
        }
    }
}

//Function for displaying the block on the screen
void drawBlock(Block* block, SDL_Renderer* renderer){
    SDL_RenderCopy(renderer, block->texture, NULL, &(block->rect));
}

//Function for displaying the whole gameBlock on the screen
void drawGameBlock(GameBlock* gameBlock, SDL_Renderer* renderer){
    for(unsigned int i = 0; i < 4; i++){
        drawBlock(&(gameBlock->blocks[i]), renderer);
    }
}

int main(){

    //Initialize SDL, create a window and a renderer
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_Window* window = SDL_CreateWindow("Tetris", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);

    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    SDL_SetRenderDrawColor(renderer, 34, 134, 134, 255);
    int running = 1;    

    GameBlock testBlock;
    SDL_Surface* testSurface = IMG_Load("./res/blueCube.png");
    SDL_Texture* testTexture = SDL_CreateTextureFromSurface(renderer, testSurface);
    SDL_FreeSurface(testSurface);
    testSurface == NULL;
    GameBlock* activeBlock = &testBlock;


    initGameBlock(&testBlock, width / 2, bwidth / 2, 1.f, 1.f, BED, testTexture);

    SDL_Event e;
    //These variables are for controlling the fps
    float df = 0.f;
    float past = SDL_GetTicks();
    float fps;
    float now;
    float dt;
    while(running){
        //Reset the x velocity of the block
        testBlock.velocity.x = 0.f;
        while(SDL_PollEvent(&e)){
            switch(e.type){
                case SDL_QUIT: running = 0;
                case SDL_KEYDOWN:
                    switch(e.key.keysym.scancode){
                        case SDL_SCANCODE_LEFT:
                            activeBlock->velocity.x += -1;
                            break;
                        case SDL_SCANCODE_RIGHT:
                            activeBlock->velocity.x += 1;
                            break;
                        case SDL_SCANCODE_ESCAPE:
                            running = 0;
                            break;
                    }
            }
        }
        //Clear the screen and draw the testBlock
        SDL_RenderClear(renderer);
        drawGameBlock(activeBlock, renderer);
        //Draw the blocks from the matrix
        for(int i = 0; i < 20; i++){
            for(int j = 0; j < 15; j++){
                if(blocks[i][j] != NULL){
                    drawBlock(blocks[i][j], renderer);
                    printf("x: %d y: %d\n", (int) blocks[i][j]->position.x, (int) blocks[i][j]->position.y);
                }
            }
        }
        updateGameBlock(&activeBlock);
        SDL_RenderPresent(renderer);

        //Limit the fps
        now = SDL_GetTicks();
        df++;
        dt = (now - past)/1000;
        fps = df / dt;
        if(fps > 60){
            SDL_Delay(mceil((1.f / 30.f - dt / df)*1000));
        }
        if(SDL_GetTicks() % 1000 == 0)
            printf("fps = %d\n", (int) fps);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;

}

要编译我使用:

gcc -o tetris tetris.c $(pkg-config --cflags --libs sdl2)-lSDL2_image -lSDL2_ttf

2 个答案:

答案 0 :(得分:0)

免责声明:您的代码很难阅读和理解(没有评论,我无法运行它来查看正在发生的事情,某些地方的缩进很差)。我的答案更像是一个简短的评论和一些提示,我可以在几分钟的主演代码后得到的。如果您编辑代码/添加更多详细信息,我将相应地编辑我的答案。

在编写应用程序时,要记住的一件事是将业务逻辑与可视逻辑分开。这有助于可视化所发生的事情,因为接口的代码往往非常冗长,如果将它与逻辑代码混合,很快就会开始变得混乱。此外,您可以使用瘦接口(仅文本)替换接口的代码,以便调试逻辑部件。

要考虑的另一件事是数据不一致。当您将相同的数据存储在2个位置并且只修改其中一个时,就会发生这种情况。您将块放在矩阵中,但每个块都知道其位置,因此数据不一致。您甚至不需要将位置存储在块结构中,只需将其放置在矩阵中即可。

话虽如此,您需要提高代码的模块性。应该有一个函数,它接受一个块矩阵并打印它,这应该是连接到视图库的唯一点。在业务逻辑中,您应该只关心游戏块。处理完毕后,只需将块放在矩阵中并绘制矩阵即可。你可能会遇到一些困难(主要是关于碰撞),但从这种心态开始。

答案 1 :(得分:0)

问题是我应该使用gameBlocks数组而不是指针数组。这样我实际上可以从数组中的gameBlock复制块而不是复制指针,这导致我从gameBlock引用块并告诉他们留在地板上,这是一个问题,因为我使用相同的gameBlock我的比赛,我没有创造另一个,而只是重新初始化旧的,所以程序是冲突的,块只是留在了地板上。