我是新来的,我在将 box2d 与 sdl2 同化时遇到问题,主要是纹理渲染部分,我会尽力解释。我总共花了一周零两天的时间试图解决这个问题,甚至在网上和 youTube 上都浏览了 box2d 的临时教程(它们并不多)。所以我认为这是最后的手段。
我有三个班级:
// 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 中声明为静态: - 地面物体的主体被减半,导致坠落物体掉落超过半地面,即使地面纹理是使用宽度和高度数据渲染的。 坠落物:
综上所述,我这里的问题比较多,你自己测试一下会更好,所以我把所有的东西都发过来检查了(即使指出最细微的细节也会非常感激) .我感谢任何形式的反馈。 提前致谢。(p.s.你可以从 nutget 包中下载 sdl2、sdl2_image 和 box2d,如果你使用 visual studio,不需要安装它会为你处理所有这些,这简直太神奇了)。