SDL2 OpenGL C++ 移动使用 VBO 和 FBO 绘制的精灵
SDL2 OpenGL C++ moving a sprite drawn with VBO and FBO
我使用 fbo 和 vbo 在我的 Sprite.cpp 中绑定并绘制了一个由两个三角形组成的矩形,这是我的初始化方法
void Sprite::init(float x, float y, float width, float height) {
_x = x;
_y = y;
_width = width;
_height = height;
if (_vboID == 0) {
glGenBuffers(1, &_vboID);
}
vertexData[0] = x + width;
vertexData[1] = y + height;
vertexData[2] = x;
vertexData[3] = y + height;
vertexData[4] = x;
vertexData[5] = y;
vertexData[6] = x + width;
vertexData[7] = y + height;
vertexData[8] = x;
vertexData[9] = y;
vertexData[10] = x + width;
vertexData[11] = y;
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
这里是绘制方法
void Sprite::draw() {
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, 0x1406, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
我显然遗漏了一些非常明显的概念,因为我似乎无法在任何地方找到解决方案,我的问题是当我按下 s 时,我的矩形没有向下移动。 (因为我正在使用 WASD 运动)这是输入处理程序的代码
void InputHandler::handleInput(SDL_Event* event, Sprite *toMove) {
switch (event->type) {
case SDL_KEYDOWN:
{
if (event->key.keysym.sym == SDLK_a) {
toMove->setMovementBooleans(MovementTuples::LEFT, true);
std::cout << "Set left ";
}
if (event->key.keysym.sym == SDLK_d) {
toMove->setMovementBooleans(MovementTuples::RIGHT, true);
std::cout << "Set right ";
}
if (event->key.keysym.sym == SDLK_s) {
toMove->setMovementBooleans(MovementTuples::DOWN, true);
std::cout << "Set down ";
}
if (event->key.keysym.sym == SDLK_w) {
toMove->setMovementBooleans(MovementTuples::UP, true);
std::cout << "Set up ";
}
break;
}
case SDL_KEYUP:
{
if (event->key.keysym.sym == SDLK_a) {
toMove->setMovementBooleans(MovementTuples::LEFT, false);
}
if (event->key.keysym.sym == SDLK_d) {
toMove->setMovementBooleans(MovementTuples::RIGHT, false);
}
if (event->key.keysym.sym == SDLK_s) {
toMove->setMovementBooleans(MovementTuples::DOWN, false);
}
if (event->key.keysym.sym == SDLK_w) {
toMove->setMovementBooleans(MovementTuples::UP, false);
}
}
}
}
此方法是静态访问的。这是在我的精灵 cpp
中单击 s 键时向下移动精灵的代码
void Sprite::update() {
if (movementBooleans[MovementTuples::DOWN]) {
_x -= .1f;
init(_x, _y, _width, _height);
std::cout << "Moved to: (" << _x << ", " << _y << ")"<< std::endl;
}
当我向下点击时,我的矩形完全消失,_x 值保持不变。我测试了输入,它完全按照我想要的方式工作,我似乎无法让矩形移动。谁能帮我弄清楚如何移动我的 Sprite,谢谢!
我正在使用 OpenGL。虽然我没有使用 SDL,因为我使用的是用户实现的输入处理程序,但概念应该是相同的。
我当前使用的游戏引擎有一个 Game class 对象,它继承自 Engine class 对象,其中 Engine class 是一个 Singleton 对象。由于此游戏引擎的结构;引擎 class 要求游戏 class 实现虚拟键盘输入功能。这是原型的样子
引擎Class
class Engine : public Singleton {
protected:
// Protected Members
private:
// Private Members
public:
// virtual destructor & public functions
protected:
// explicit protected Constructor & protected functions
private:
bool messageHandler( unsigned uMsg, WPARAM wParam, LPARAM lParam );
virtual void keyboardInput( unsigned vkCode, bool isPressed ) = 0;
// Other Private Functions
}; // Engine
游戏Class
class Game sealed : public Engine {
private:
// private members here
public:
// Constructor and virtual Destructor
private:
virtual void keyBoardInput( unsigned vkCode, bool isPressed ) override;
}; // Game
这是 Windows
的 messageHandler() 函数
// ----------------------------------------------------------------------------
// messageHandler()
bool Engine::messageHandler( unsigned uMsg, WPARAM wParam, LPARAM lParam ) {
switch( uMsg ) {
case WM_CLOSE: {
PostQuitMessage( 0 );
return true;
}
case WM_SYSKEYDOWN : {
if ( ( VK_MENU == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RMENU; // Alt Key
}
// Fall Through
}
case WM_KEYDOWN: {
if ( ( VK_RETURN == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_SEPARATOR;
} else if ( ( VK_CONTROL == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RCONTROL;
}
if ( 0 == ( lParam & 0x40000000 ) ) { // Supress Key Repeats
keyboardInput( wParam, true );
}
return true;
}
case WM_SYSKEYUP: {
if ( ( VK_MENU == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RMENU; // Alt Key
}
// Fall Through
}
case WM_KEYUP: {
if ( ( VK_RETURN == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_SEPARATOR;
} else if ( ( VK_CONTROL == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RCONTROL;
}
keyboardInput( wParam, false );
return true;
}
case WM_MOUSEMOVE: {
// Mouse Motion Detected, Coordinates Are WRT Window Therefore
// 0,0 Is The Coordinate Of The Top Left Corner Of The Window
m_mouseState.position = glm::ivec2( LOWORD( lParam ), HIWORD( lParam ) );
mouseInput();
return true;
}
case WM_LBUTTONDOWN: {
m_mouseState.isButtonPressed[MOUSE_LEFT_BUTTON] = true;
mouseInput();
return true;
}
case WM_LBUTTONUP: {
m_mouseState.isButtonPressed[MOUSE_LEFT_BUTTON] = false;
mouseInput();
return true;
}
case WM_RBUTTONDOWN: {
m_mouseState.isButtonPressed[MOUSE_RIGHT_BUTTON] = true;
mouseInput();
return true;
}
case WM_RBUTTONUP: {
m_mouseState.isButtonPressed[MOUSE_RIGHT_BUTTON] = false;
mouseInput();
return true;
}
case WM_MBUTTONDOWN: {
m_mouseState.isButtonPressed[MOUSE_MIDDLE_BUTTON] = true;
mouseInput();
return true;
}
case WM_MBUTTONUP: {
m_mouseState.isButtonPressed[MOUSE_MIDDLE_BUTTON] = false;
mouseInput();
return true;
}
case WM_MOUSEWHEEL: {
// Mouse Wheel Moved
// wParam Contains How Much It Was Moved
return true;
}
default: {
return false; // Did Not Handle The Message
}
}
} // messageHandler
最后这是 keyboardInput() 函数
// ----------------------------------------------------------------------------
// keyboardInput()
void Game::keyboardInput( unsigned vkCode, bool isPressed ) {
std::ostringstream strStream;
strStream << "Key 0" << std::hex << vkCode << " was " << ( isPressed ? "Pressed" : "Released" );
Logger::log( strStream );
if ( VK_ESCAPE == vkCode ) {
PostQuitMessage( 0 );
}
static bool keyPressed[256] = { 0 };
if ( vkCode < 256 ) {
keyPressed[vkCode] = isPressed;
}
if ( isPressed ) {
return;
}
switch ( vkCode ) {
case VK_DOWN:
case 'S' : {
// do logic here
break;
}
case VK_LEFT:
case 'A' : {
// do logic here
break;
}
case VK_RIGHT:
case 'D' : {
// do logic here
break;
}
case VK_UP:
case 'W' : {
// do logic here
break;
}
}
} // handleKeyboard
当您仔细查看代码时;我不是在轮询以查看何时按下键,而是在查询或等待查看何时释放键。这种逻辑的原因是这样的;当您按下一个键时,可以重复按下而不会达到向上状态。这就像在任何文本编辑器中按住相同的键,您会在屏幕上看到相同的键。解决这个问题或避免这种行为;我们做相反的逻辑。
我们寻找密钥何时被释放。该键只能释放一次,必须再次按下才能再次进入释放状态。这样,每次按下然后释放时,程序都会发生一次。
现在,如果您注意到我们会检查按键是否处于按下状态,如果是,那么我们 return;这很重要,因为如果您不这样做,您的程序将自动执行消息处理程序中的操作,因为默认情况下按键状态已经处于释放状态。所以这里发生的是在 Game::keyboardInput()
和 Engine::messageHandler()
函数被调用时渲染帧或更新帧期间,如果它检测到按键已将其状态更改为真,则 return s 来自函数并被连续调用,直到您释放键;一旦键被释放并且状态变回已释放,它就会跳过 if 语句并直接转到您正在处理的键的 switch 语句。
如果你想四处移动 OpenGL 形状,你真的应该进行矩阵变换。
我个人使用 GLM 为我完成所有繁重的工作,所以我要做的就是在转换完成后将矩阵传递给 GLSL。
我使用 fbo 和 vbo 在我的 Sprite.cpp 中绑定并绘制了一个由两个三角形组成的矩形,这是我的初始化方法
void Sprite::init(float x, float y, float width, float height) {
_x = x;
_y = y;
_width = width;
_height = height;
if (_vboID == 0) {
glGenBuffers(1, &_vboID);
}
vertexData[0] = x + width;
vertexData[1] = y + height;
vertexData[2] = x;
vertexData[3] = y + height;
vertexData[4] = x;
vertexData[5] = y;
vertexData[6] = x + width;
vertexData[7] = y + height;
vertexData[8] = x;
vertexData[9] = y;
vertexData[10] = x + width;
vertexData[11] = y;
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
这里是绘制方法
void Sprite::draw() {
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, 0x1406, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
我显然遗漏了一些非常明显的概念,因为我似乎无法在任何地方找到解决方案,我的问题是当我按下 s 时,我的矩形没有向下移动。 (因为我正在使用 WASD 运动)这是输入处理程序的代码
void InputHandler::handleInput(SDL_Event* event, Sprite *toMove) {
switch (event->type) {
case SDL_KEYDOWN:
{
if (event->key.keysym.sym == SDLK_a) {
toMove->setMovementBooleans(MovementTuples::LEFT, true);
std::cout << "Set left ";
}
if (event->key.keysym.sym == SDLK_d) {
toMove->setMovementBooleans(MovementTuples::RIGHT, true);
std::cout << "Set right ";
}
if (event->key.keysym.sym == SDLK_s) {
toMove->setMovementBooleans(MovementTuples::DOWN, true);
std::cout << "Set down ";
}
if (event->key.keysym.sym == SDLK_w) {
toMove->setMovementBooleans(MovementTuples::UP, true);
std::cout << "Set up ";
}
break;
}
case SDL_KEYUP:
{
if (event->key.keysym.sym == SDLK_a) {
toMove->setMovementBooleans(MovementTuples::LEFT, false);
}
if (event->key.keysym.sym == SDLK_d) {
toMove->setMovementBooleans(MovementTuples::RIGHT, false);
}
if (event->key.keysym.sym == SDLK_s) {
toMove->setMovementBooleans(MovementTuples::DOWN, false);
}
if (event->key.keysym.sym == SDLK_w) {
toMove->setMovementBooleans(MovementTuples::UP, false);
}
}
}
}
此方法是静态访问的。这是在我的精灵 cpp
中单击 s 键时向下移动精灵的代码void Sprite::update() {
if (movementBooleans[MovementTuples::DOWN]) {
_x -= .1f;
init(_x, _y, _width, _height);
std::cout << "Moved to: (" << _x << ", " << _y << ")"<< std::endl;
}
当我向下点击时,我的矩形完全消失,_x 值保持不变。我测试了输入,它完全按照我想要的方式工作,我似乎无法让矩形移动。谁能帮我弄清楚如何移动我的 Sprite,谢谢!
我正在使用 OpenGL。虽然我没有使用 SDL,因为我使用的是用户实现的输入处理程序,但概念应该是相同的。
我当前使用的游戏引擎有一个 Game class 对象,它继承自 Engine class 对象,其中 Engine class 是一个 Singleton 对象。由于此游戏引擎的结构;引擎 class 要求游戏 class 实现虚拟键盘输入功能。这是原型的样子
引擎Class
class Engine : public Singleton {
protected:
// Protected Members
private:
// Private Members
public:
// virtual destructor & public functions
protected:
// explicit protected Constructor & protected functions
private:
bool messageHandler( unsigned uMsg, WPARAM wParam, LPARAM lParam );
virtual void keyboardInput( unsigned vkCode, bool isPressed ) = 0;
// Other Private Functions
}; // Engine
游戏Class
class Game sealed : public Engine {
private:
// private members here
public:
// Constructor and virtual Destructor
private:
virtual void keyBoardInput( unsigned vkCode, bool isPressed ) override;
}; // Game
这是 Windows
的 messageHandler() 函数// ----------------------------------------------------------------------------
// messageHandler()
bool Engine::messageHandler( unsigned uMsg, WPARAM wParam, LPARAM lParam ) {
switch( uMsg ) {
case WM_CLOSE: {
PostQuitMessage( 0 );
return true;
}
case WM_SYSKEYDOWN : {
if ( ( VK_MENU == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RMENU; // Alt Key
}
// Fall Through
}
case WM_KEYDOWN: {
if ( ( VK_RETURN == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_SEPARATOR;
} else if ( ( VK_CONTROL == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RCONTROL;
}
if ( 0 == ( lParam & 0x40000000 ) ) { // Supress Key Repeats
keyboardInput( wParam, true );
}
return true;
}
case WM_SYSKEYUP: {
if ( ( VK_MENU == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RMENU; // Alt Key
}
// Fall Through
}
case WM_KEYUP: {
if ( ( VK_RETURN == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_SEPARATOR;
} else if ( ( VK_CONTROL == wParam ) && ( lParam & 0x1000000 ) ) {
wParam = VK_RCONTROL;
}
keyboardInput( wParam, false );
return true;
}
case WM_MOUSEMOVE: {
// Mouse Motion Detected, Coordinates Are WRT Window Therefore
// 0,0 Is The Coordinate Of The Top Left Corner Of The Window
m_mouseState.position = glm::ivec2( LOWORD( lParam ), HIWORD( lParam ) );
mouseInput();
return true;
}
case WM_LBUTTONDOWN: {
m_mouseState.isButtonPressed[MOUSE_LEFT_BUTTON] = true;
mouseInput();
return true;
}
case WM_LBUTTONUP: {
m_mouseState.isButtonPressed[MOUSE_LEFT_BUTTON] = false;
mouseInput();
return true;
}
case WM_RBUTTONDOWN: {
m_mouseState.isButtonPressed[MOUSE_RIGHT_BUTTON] = true;
mouseInput();
return true;
}
case WM_RBUTTONUP: {
m_mouseState.isButtonPressed[MOUSE_RIGHT_BUTTON] = false;
mouseInput();
return true;
}
case WM_MBUTTONDOWN: {
m_mouseState.isButtonPressed[MOUSE_MIDDLE_BUTTON] = true;
mouseInput();
return true;
}
case WM_MBUTTONUP: {
m_mouseState.isButtonPressed[MOUSE_MIDDLE_BUTTON] = false;
mouseInput();
return true;
}
case WM_MOUSEWHEEL: {
// Mouse Wheel Moved
// wParam Contains How Much It Was Moved
return true;
}
default: {
return false; // Did Not Handle The Message
}
}
} // messageHandler
最后这是 keyboardInput() 函数
// ----------------------------------------------------------------------------
// keyboardInput()
void Game::keyboardInput( unsigned vkCode, bool isPressed ) {
std::ostringstream strStream;
strStream << "Key 0" << std::hex << vkCode << " was " << ( isPressed ? "Pressed" : "Released" );
Logger::log( strStream );
if ( VK_ESCAPE == vkCode ) {
PostQuitMessage( 0 );
}
static bool keyPressed[256] = { 0 };
if ( vkCode < 256 ) {
keyPressed[vkCode] = isPressed;
}
if ( isPressed ) {
return;
}
switch ( vkCode ) {
case VK_DOWN:
case 'S' : {
// do logic here
break;
}
case VK_LEFT:
case 'A' : {
// do logic here
break;
}
case VK_RIGHT:
case 'D' : {
// do logic here
break;
}
case VK_UP:
case 'W' : {
// do logic here
break;
}
}
} // handleKeyboard
当您仔细查看代码时;我不是在轮询以查看何时按下键,而是在查询或等待查看何时释放键。这种逻辑的原因是这样的;当您按下一个键时,可以重复按下而不会达到向上状态。这就像在任何文本编辑器中按住相同的键,您会在屏幕上看到相同的键。解决这个问题或避免这种行为;我们做相反的逻辑。
我们寻找密钥何时被释放。该键只能释放一次,必须再次按下才能再次进入释放状态。这样,每次按下然后释放时,程序都会发生一次。
现在,如果您注意到我们会检查按键是否处于按下状态,如果是,那么我们 return;这很重要,因为如果您不这样做,您的程序将自动执行消息处理程序中的操作,因为默认情况下按键状态已经处于释放状态。所以这里发生的是在 Game::keyboardInput()
和 Engine::messageHandler()
函数被调用时渲染帧或更新帧期间,如果它检测到按键已将其状态更改为真,则 return s 来自函数并被连续调用,直到您释放键;一旦键被释放并且状态变回已释放,它就会跳过 if 语句并直接转到您正在处理的键的 switch 语句。
如果你想四处移动 OpenGL 形状,你真的应该进行矩阵变换。
我个人使用 GLM 为我完成所有繁重的工作,所以我要做的就是在转换完成后将矩阵传递给 GLSL。