我正在使用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
答案 0 :(得分:0)
免责声明:您的代码很难阅读和理解(没有评论,我无法运行它来查看正在发生的事情,某些地方的缩进很差)。我的答案更像是一个简短的评论和一些提示,我可以在几分钟的主演代码后得到的。如果您编辑代码/添加更多详细信息,我将相应地编辑我的答案。
在编写应用程序时,要记住的一件事是将业务逻辑与可视逻辑分开。这有助于可视化所发生的事情,因为接口的代码往往非常冗长,如果将它与逻辑代码混合,很快就会开始变得混乱。此外,您可以使用瘦接口(仅文本)替换接口的代码,以便调试逻辑部件。
要考虑的另一件事是数据不一致。当您将相同的数据存储在2个位置并且只修改其中一个时,就会发生这种情况。您将块放在矩阵中,但每个块都知道其位置,因此数据不一致。您甚至不需要将位置存储在块结构中,只需将其放置在矩阵中即可。
话虽如此,您需要提高代码的模块性。应该有一个函数,它接受一个块矩阵并打印它,这应该是连接到视图库的唯一点。在业务逻辑中,您应该只关心游戏块。处理完毕后,只需将块放在矩阵中并绘制矩阵即可。你可能会遇到一些困难(主要是关于碰撞),但从这种心态开始。
答案 1 :(得分:0)
问题是我应该使用gameBlocks数组而不是指针数组。这样我实际上可以从数组中的gameBlock复制块而不是复制指针,这导致我从gameBlock引用块并告诉他们留在地板上,这是一个问题,因为我使用相同的gameBlock我的比赛,我没有创造另一个,而只是重新初始化旧的,所以程序是冲突的,块只是留在了地板上。