与超过 1 个对象的碰撞 SDL 2.0-C++

Collision with more than 1 Object SDL 2.0-C++

我是新来的。我一直在学习 类 并尝试制作一个非常简单的平台游戏。但我现在有问题。我想设置 Class "Player" 与 Class "Block" 的 2 个对象发生碰撞,但碰撞对其中一个不起作用。

这是我的代码:

#include <iostream>
#include <SDL.h>
#include <SDL_image.h>
#undef main

class Block
{
private:
    SDL_Texture *BlockTexture;
public:
    Block(SDL_Renderer *renderTarget, std::string filePath, int xPos, int yPos, int Width, int Height);
    ~Block();
    void Draw(SDL_Renderer *renderTarget);
    SDL_Rect BlockPos;
};
Block::Block(SDL_Renderer *renderTarget, std::string filePath, int xPos, int yPos, int Width, int Height)
{
    SDL_Surface *surface = IMG_Load(filePath.c_str());
    {
        BlockTexture = SDL_CreateTextureFromSurface(renderTarget, surface);
    }
    SDL_FreeSurface(surface);

    BlockPos.x = xPos;
    BlockPos.y = yPos;
    BlockPos.w = Width;
    BlockPos.h = Height;
}
Block::~Block()
{
    SDL_DestroyTexture(BlockTexture);
}
void Block::Draw(SDL_Renderer *renderTarget)
{
    SDL_RenderCopy(renderTarget, BlockTexture, NULL, &BlockPos);
}
class Player
{
private:
    SDL_Texture *Texture;
    float moveSpeed;
    float jumpSpeed;
    int falling = 0;
    SDL_Scancode keys [3];
public:
    SDL_Rect PlayerPos;
    Player(SDL_Renderer *renderTarget, std::string filePath, int PosX, int PosY,     int Width, int Height);
    ~Player();

    void Update(float delta, const Uint8 *Keystate);
    void Draw(SDL_Renderer *renderTarget);

    bool Collision(Block &p);
};
Player::Player(SDL_Renderer *renderTarget, std::string filePath, int PosX, int     PosY, int Width, int Height)
{
    SDL_Surface *surface = IMG_Load(filePath.c_str());
    {
        Texture = SDL_CreateTextureFromSurface(renderTarget, surface);
    }
    SDL_FreeSurface(surface);

    PlayerPos.x = PosX;
    PlayerPos.y = PosY;
    PlayerPos.w = Width;
    PlayerPos.h = Height;

    keys[0] = SDL_SCANCODE_UP;
    keys[1] = SDL_SCANCODE_LEFT;
    keys[2] = SDL_SCANCODE_RIGHT;

    moveSpeed = 200.f;
    jumpSpeed = 100.f;
}
Player::~Player()
{
    SDL_DestroyTexture(Texture);
}
void Player::Update(float delta, const Uint8 *KeyState)
{
    if(KeyState[keys[0]])
    {
        PlayerPos.y -= moveSpeed * delta;
    }
    if(KeyState[keys[1]])
    {
        PlayerPos.x -= (moveSpeed / 2) * delta;
    }
    if(KeyState[keys[2]])
    {
        PlayerPos.x += moveSpeed * delta;
    }
    if(falling == 0)
    {
        PlayerPos.y += jumpSpeed * delta;
    }
}
void Player::Draw(SDL_Renderer *renderTarget)
{
    SDL_RenderCopy(renderTarget, Texture, NULL, &PlayerPos);
}
bool Player::Collision(Block &p)
{
    if(PlayerPos.x + PlayerPos.w <= p.BlockPos.x || PlayerPos.x >= p.BlockPos.x + p.BlockPos.w ||
        PlayerPos.y + PlayerPos.h <= p.BlockPos.y || PlayerPos.y >= p.BlockPos.y + p.BlockPos.h)
    {
        falling = 0;
        return true;
    }
    else
        falling = 1;
        return false;
}
SDL_Texture *LoadTexture(std::string filePath, SDL_Renderer *Renderer)
{
    SDL_Texture *texture = NULL;
    SDL_Surface *surface = IMG_Load(filePath.c_str());
    {
        texture = SDL_CreateTextureFromSurface(Renderer, surface);
    }
    SDL_FreeSurface(surface);
    return texture;
}
int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Window *window = SDL_CreateWindow("Platform", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    SDL_Renderer *renderTarget = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    int imgFlags = IMG_INIT_PNG;

    int currentTime = 0;
    int previousTime = 0;
    float delta = 0;

    const Uint8 *Keystate;

    Player Player(renderTarget, "BlockP.png", 100, 100, 50, 50);

    Block Block1(renderTarget, "Block.png", 0, 500, 800, 100);
    Block Block2(renderTarget, "Block.png", 100, 300, 300, 50);

    bool isRunning = true;
    SDL_Event ev;

    while(isRunning)
    {
        Keystate = SDL_GetKeyboardState(NULL);

        Player.Collision(Block1);
        Player.Collision(Block2);

        previousTime = currentTime;
        currentTime = SDL_GetTicks();
        delta = (currentTime - previousTime) / 1000.0f;

        Player.Update(delta, Keystate);

        while(SDL_PollEvent(&ev) != 0)
        {
            if(ev.type == SDL_QUIT)
                isRunning = false;
        }
        SDL_RenderClear(renderTarget);

        Player.Draw(renderTarget);

        Block1.Draw(renderTarget);
        Block2.Draw(renderTarget);

        SDL_RenderPresent(renderTarget);
    }
    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderTarget);
    window = NULL;
    renderTarget = NULL;

    SDL_Quit();

    return 0;
}

您的代码的问题是每次调用 Player.Collision 都会覆盖 "falling" 变量。

Player.Collision(Block1); //this call calculates a falling value
Player.Collision(Block2); //...then this call overwrites falling with a new value

如此有效,您的代码仅测试玩家是否与 Block2 发生碰撞,因此与 Block1 的碰撞将被忽略。

目前你的 Collision 函数是:

bool Player::Collision(Block &p)
{
    if(PlayerPos.x + PlayerPos.w <= p.BlockPos.x || PlayerPos.x >= p.BlockPos.x + p.BlockPos.w ||
        PlayerPos.y + PlayerPos.h <= p.BlockPos.y || PlayerPos.y >= p.BlockPos.y + p.BlockPos.h)
    {
        falling = 0;
        return true;
    }
    else
        falling = 1;
        return false;
}

首先,您的 "return false;" 实际上不是 else 的一部分,因为您没有 {}。在这种特殊情况下,它没有区别,因为 else 退出然后 return 发生但你的缩进会建议你期望 "return false;" 行作为 else 块的一部分执行,所以你应该放:

else
{
    falling = 1;
    return false;
}

接下来你想说如果你已经检测到碰撞(例如,与 Block1)则不要将下降设置为 1,为此添加一个 if 语句。

else
{
    if(falling != 0) //if we haven't already detected a collision this frame
    {
        falling = 1;
    }
    return false;
}

然而,您需要在每一帧开始时将回落设置为 1,否则如果在一帧上检测到碰撞,那么玩家将永远不会在后续帧上掉落,即使它们没有与方块碰撞.

作为旁注,如果下落 == 0,您的 Player.Update 代码会修改玩家的 y 位置,这似乎违反直觉,因为通常 0 为假而 1 为真,因此您似乎在说 if not下降然后更新 y,如果下降更新 y 应该在什么地方。我个人会使用 bool 而不是 int 来保存 falling 的值,然后说 if(falling) update y,这会让你的代码更清晰。