在 C++ 中将 Box2d 与 Sdl2 同化的问题

问题描述 投票:0回答:0

我是新来的,我在将 box2d 与 sdl2 同化时遇到问题,主要是纹理渲染部分,我会尽力解释。我总共花了一周零两天的时间试图解决这个问题,甚至在网上和 youTube 上都浏览了 box2d 的临时教程(它们并不多)。所以我认为这是最后的手段。

我有三个班级:

  1. Texture 类(没有继承/但是是 BoxObject 类的基类)。
  2. EdgeShape类(无继承/暂不关注)。
  3. BoxObject类(继承自Texture类/现在的话题重点)。 有 2 个主要头文件:world.h 和 Sdl2Includes.h,2 个 cpp 文件:unique.cpp 和 main.cpp.
// world.h
#pragma once

#include "Box2D.h"

inline b2Vec2 gravity{ 0.0f, 9.8f };
inline b2World gworld{ gravity };

inline float32 timeStep{ 1 / 60.0f };
inline int32 velocityIterations{ 8 };
inline int32 positionIterations{ 3 };
// Sdl2Includes.h
#pragma once

#include "SDL.h"
#include "SDL_image.h"
#include "SDL2_gfxPrimitives.h"
#include "SDL2_gfxPrimitives_font.h"
#include "SDL2_imageFilter.h"
#include "SDL2_framerate.h"
#include "SDL2_rotozoom.h"
// Texture.h
#pragma once

#include "SDLincludes.h"

#include "C:\Users\MIKE EMEKA\Documents\C++\includes.hpp" // c++ header files<iostream> and others.
using namespace std::numbers;

class Texture
{
private:
    SDL_Texture* m_texture{ nullptr };
    SDL_Renderer* m_renderer{ nullptr };

protected:
    float m_imageWidth{};
    float m_imageHeight{};

public:
    // Renderer to which would be used by each instance of the Texture class in public member functions such as loadTexture and renderF,e.t.c
    // Note: renderer would not be destroyed when the destructor is called, so as to prevent, pontential breakdown of the host renderer.
    Texture(SDL_Renderer* renderer);
    Texture() = default;
    ~Texture();

    // path: path directory to the image you wish to load.
    // rgb(Red, Green, Blue): colour to make transparent in the loaded image.
    bool loadTexture(std::string path, Uint8 r = 255, Uint8 g = 255, Uint8 b = 255);

    void renderF(SDL_Rect* sourceRect, SDL_FRect* destinationRect, double angle, SDL_FPoint* center, SDL_RendererFlip direction = SDL_FLIP_NONE);

    float getImageWidth() const;
    float getImageHeight() const;
private:
    void free();
};

//Texture.cpp
#include "Texture.h" 

Texture::Texture(SDL_Renderer* renderer):
    m_texture{ nullptr }, m_renderer{ renderer }, m_imageWidth{}, m_imageHeight{}
{}

Texture::~Texture()
{
    free();
}

bool Texture::loadTexture(std::string path, Uint8 r, Uint8 g, Uint8 b)
{
    free();
    SDL_Texture* newTexture{ nullptr };

    SDL_Surface* temp{ IMG_Load(path.c_str()) };
    if (!temp)
    {
        std::cerr << "Unable to load surface: " << path.c_str() << " Error: " << SDL_GetError() << '\n';
        return false;
    }
    m_imageWidth = (float)temp->w;
    m_imageHeight = (float)temp->h;

    SDL_SetColorKey(temp, SDL_TRUE, SDL_MapRGB(temp->format, r, g, b));

    newTexture = SDL_CreateTextureFromSurface(m_renderer, temp);
    if (!newTexture)
    {
        std::cerr << "Unable to load new Texture from surface, Error: " << SDL_GetError() << '\n';
        return false;
    }

    m_texture = newTexture;
    SDL_FreeSurface(temp);
    return true;
}

void Texture::renderF(SDL_Rect* sourceRect, SDL_FRect* destinationRect, double angle, SDL_FPoint* center, SDL_RendererFlip direction)
{
    SDL_RenderCopyExF(m_renderer, m_texture, sourceRect, destinationRect, angle, center, direction);
}

float Texture::getImageWidth() const
{
    return m_imageWidth;
}
float Texture::getImageHeight() const
{
    return m_imageHeight;
}

void Texture::free()
{
    SDL_DestroyTexture(m_texture);
    m_texture = nullptr;
}
//Boxobject.h
#pragma once

#include "SDLincludes.h"
#include "Texture.h"
#include "world.h"

#include "C:\Users\MIKE EMEKA\Documents\C++\includes.hpp"
using namespace std::numbers;

class BoxObject: private Texture
{
    struct PuesdoRect
    {
        float xAxis{};
        float yAxis{};
        float width{};
        float height{};
    };
    
    static constexpr float metersToPixels{ 30.0f };
    static constexpr float pixelToMeters{ 1 / metersToPixels };

    b2Body* m_Body{ nullptr };
    float32 m_angle{};
    PuesdoRect m_object{};

public:
    // Note:
    // Public member function loadBoxImage must be called after(should be the second function to call) an object of class Box is instantiated.
    // renderer is safe from the destructor, prevent crash or dangling pointer.
    BoxObject(b2BodyType typeOfBody, SDL_Renderer* renderer, float xAxis, float yAxis, float width, float height);
    BoxObject() = default;

    void render();
private:
    // Converts Object to a sdl protype rectangle
    SDL_FRect convertToSdlRect() const;
public:
    float getXaxis() const;
    float getYaxis() const;
    float getWidth() const;
    float getHeight() const;

    // Updates object's position and velocity with other relations to object physics
    void update(b2BodyType type);

    // This function makes use of an inherited function, the following are the details of said function:
    // path: path directory to the image you wish to load.
    // rgb(Red, Green, Blue): colour to make transparent in the loaded image. 
    bool loadBoxImage(std::string path, Uint8 r = 255, Uint8 g = 255, Uint8 b = 255);
    void moveRight();
    void moveLeft();
};

//BoxObject.cpp
#include "BoxObject.h"

BoxObject::BoxObject(b2BodyType typeOfBody, SDL_Renderer* renderer, float xAxis, float yAxis, float width, float height) :
    Texture{ renderer },  
    m_object { xAxis, yAxis, width, height },
    m_angle{}
{
    if (typeOfBody == b2_dynamicBody)
    {
        b2BodyDef dynamicBodyDef{};
        dynamicBodyDef.type = typeOfBody;  
        dynamicBodyDef.position.Set(xAxis * pixelToMeters, yAxis * pixelToMeters);
        m_Body = gworld.CreateBody(&dynamicBodyDef);

        b2PolygonShape box{};
        box.SetAsBox( (width * pixelToMeters) / 2.0f - box.m_radius, (height * pixelToMeters) / 2.0f - box.m_radius ); 

        b2FixtureDef fixtureDef{};
        fixtureDef.density = 1.0f;
        fixtureDef.shape = &box;
        fixtureDef.friction = 1.0f;
        fixtureDef.restitution = 0.2f;

        m_Body->CreateFixture(&fixtureDef);
    }
    else if (typeOfBody == b2_staticBody)
    {
        b2BodyDef staticBodyDef{};
        staticBodyDef.position.Set(xAxis * pixelToMeters, yAxis * pixelToMeters);

        m_Body = gworld.CreateBody(&staticBodyDef);

        b2PolygonShape box{};
        box.SetAsBox( (width * pixelToMeters) / 2.0f, (height * pixelToMeters) / 2.0f );

        // No need for fixturedef since the bodyType is static, Factory settings are good enough  
        m_Body->CreateFixture(&box, 1.0f);
    }
}


void BoxObject::render()
{
    SDL_FRect temp{ convertToSdlRect() };
    renderF(nullptr, &temp, m_angle, nullptr);
}

SDL_FRect BoxObject::convertToSdlRect() const
{
    return { m_object.xAxis, m_object.yAxis, m_object.width, m_object.height };
}

float BoxObject::getXaxis() const
{
    return m_object.xAxis;
}
float BoxObject::getYaxis() const
{
    return m_object.yAxis;
}
float BoxObject::getWidth() const
{
    return m_object.width;
}
float BoxObject::getHeight() const
{
    return m_object.height;
}

void BoxObject::update(b2BodyType type)
{
    
    if (type == b2_dynamicBody)
    {
        b2Vec2 position = m_Body->GetPosition();
        
        m_angle = (m_Body->GetAngle() * 180.0f) / pi_v<float>;
        position *= metersToPixels;
        m_object.xAxis = position.x; 
        m_object.yAxis = position.y;
    }
    else if (type == b2_staticBody)
    {
        b2Vec2 position = m_Body->GetPosition();
        position *= metersToPixels;
        m_object.xAxis = position.x;
        m_object.yAxis = position.y;
    }

}

bool BoxObject::loadBoxImage(std::string path, Uint8 r, Uint8 g, Uint8 b)
{
    if (!loadTexture(path, r, g, b))
        return false;

    return true;
}

void BoxObject::moveRight()
{
    b2Vec2 velocity{ 6.0f, 0.0f };
    m_Body->SetLinearVelocity(velocity);
    //m_object.xAxis += (m_Body->GetLinearVelocity()).x;
}
void BoxObject::moveLeft()
{
    b2Vec2 velocity{ -6.0f, 0.0f };
    m_Body->SetLinearVelocity(velocity);
    //m_object.xAxis -= (m_Body->GetLinearVelocity()).x;
}
//unique.h
#pragma once

#include "SDLincludes.h"
#include "world.h"
#include "BoxObject.h"
#include "EdgeObject.h"

#include "C:\Users\MIKE EMEKA\Documents\C++\includes.hpp"
using namespace std::numbers;


using WindowDimensions = int;

inline SDL_Window* global_window{ nullptr };
inline SDL_Renderer* global_renderer{ nullptr };
inline SDL_Cursor* customCursor{ nullptr };
inline SDL_Surface* global_surface{ nullptr };

inline constexpr WindowDimensions w_width{ 640 };
inline constexpr WindowDimensions w_hieght{ 480 }; 

inline bool isRunning{};


bool init();
void setUP();
void processInput();
void update();
void render();
void cleanup();

//unique.cpp
#include "unique.h"

static BoxObject ground{};
static BoxObject rect{};
static std::vector<BoxObject> multiple{};

bool init()
{
    if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        std::cerr << "Problem from initiaization of SDL: " << SDL_GetError() << '\n';
        return false;
    }

    global_window = SDL_CreateWindow(nullptr, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w_width, w_hieght, SDL_WINDOW_SHOWN);
    if (!global_window)
    {
        std::cerr << "Problem from creation of window: " << SDL_GetError() << '\n';
        return false;
    }

    global_renderer = SDL_CreateRenderer(global_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (!global_renderer)
    {
        std::cerr << "Problem from creation of renderer: " << SDL_GetError() << '\n';
        return false;
    }

    ground = { b2_staticBody, global_renderer, 0.0f, w_hieght - 50.0f, w_width, 50.0f };
    rect = { b2_dynamicBody, global_renderer, 40.0f, 0.0f, 100.0f, 100.0f };
    
    return true;
}

void setUP()
{
    global_surface = IMG_Load("C:/Users/MIKE EMEKA/Downloads/icons8-minecraft-sword-50 (1).png");
    SDL_SetColorKey(global_surface, SDL_TRUE, SDL_MapRGB(global_surface->format, 255, 255, 255));

    customCursor = SDL_CreateColorCursor(global_surface, 45, 5);
    if (!customCursor)
    {
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Problem from cursor creation: ", SDL_GetError(), global_window);
    }
    SDL_SetCursor(customCursor);

    ground.loadBoxImage("C:/Users/MIKE EMEKA/Downloads/ground.bmp");
    rect.loadBoxImage("C:/Users/MIKE EMEKA/Downloads/box.bmp");
}

void processInput()
{
    SDL_Event event{};

    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
        case SDL_QUIT: isRunning = false; break;
        /*case SDL_KEYDOWN:
            if (event.key.keysym.sym == SDLK_ESCAPE)
                isRunning = false;
            else if (event.key.keysym.sym == SDLK_RIGHT)
                small.moveRight();
            else if (event.key.keysym.sym == SDLK_LEFT)
                small.moveLeft();
            else if (event.key.keysym.sym == SDLK_a)
                lagre.moveLeft();
            else if (event.key.keysym.sym == SDLK_s)
                lagre.moveRight();

            break;*/
        case SDL_MOUSEBUTTONDOWN:
            
            multiple.emplace_back(b2_dynamicBody, global_renderer, (float)event.button.x, (float)event.button.y, 30.0f, 30.0f);
            for (auto& i : multiple)
            {
                i.loadBoxImage("C:/Users/MIKE EMEKA/Downloads/box.bmp");
            }
            
            break;

        default: isRunning = true; break;
        }
    }
}

void update()
{
    ground.update(b2_staticBody);
    rect.update(b2_dynamicBody);
    for (auto& i : multiple)
    {
        i.update(b2_dynamicBody);
    }
}

void render()
{
    SDL_SetRenderDrawColor(global_renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(global_renderer);
    
    ground.render();
    rect.render();
    for (auto& i : multiple)
    {
        i.render();
    }

    SDL_RenderPresent(global_renderer);
}

void cleanup()
{
    SDL_DestroyRenderer(global_renderer);
    SDL_DestroyWindow(global_window);
    SDL_FreeCursor(customCursor);
    SDL_FreeSurface(global_surface);
    
    SDL_Quit();
    global_renderer = nullptr;
    global_window = nullptr;
    customCursor = nullptr;
    global_surface = nullptr;
}

最后是 main.cpp.

//main.cpp
#include "unique.h"

int main(int agrc, char* agrv[])
{
    isRunning = init(); 
        setUP();

    while (isRunning)
    {
        processInput();
        gworld.Step(timeStep, velocityIterations, positionIterations);
        update();
        render();
    }
    gworld.Dump();
    return EXIT_SUCCESS;
}

说明: 我这里有我的 Box2d 的 helloworld 版本,执行时会打开一个 sdl 窗口,在 unique.cpp 的函数设置中,我创建了一个自定义光标 - 如果您决定复制并运行程序,您可以删除它。我还加载了两个图像 - 使用 MsPaint 创建。因此,该程序的目的是在鼠标按钮事件时创建对象(正方形),然后为这些新创建的对象赋予纹理,并进行更新然后渲染。他们执行自由落体直到被地面(物体)停止。基本上,只是为了模拟物体(主要是矩形)的自由落体。

问题:-> 无数 列举几个。 地面 BoxObject 在 unique.cpp 中声明为静态: - 地面物体的主体被减半,导致坠落物体掉落超过半地面,即使地面纹理是使用宽度和高度数据渲染的。 坠落物:

  • 好吧,这个有各种各样的问题,示例:当我创建一个大对象和一个小对象来模拟运动时(如 unique.cpp 中函数 processInput 中注释掉的代码所示),小对象移动更大的物体。取决于宽度,如果是 100 的一半,如果是 50,我认为是四分之一。还有更多。

程序片段: enter image description here

综上所述,我这里的问题比较多,你自己测试一下会更好,所以我把所有的东西都发过来检查了(即使指出最细微的细节也会非常感激) .我感谢任何形式的反馈。 提前致谢。(p.s.你可以从 nutget 包中下载 sdl2、sdl2_image 和 box2d,如果你使用 visual studio,不需要安装它会为你处理所有这些,这简直太神奇了)。

c++ sdl box2d
© www.soinside.com 2019 - 2024. All rights reserved.