我目前正在尝试用SFML构建游戏引擎的框架,并正在学习如何利用Gamestate堆栈结构将代码划分为可读的部分。
我已经创建了一个基本的TestGameState类,它扩展了一个抽象类GameState,其他GameState类将在此基础上构建。在我的Game类中,我只需创建一个窗口,运行一个基本的游戏循环,并调用Gamestate更新方法。每个类别的代码是:
GameState.h
#pragma once
#include "Game.h"
class GameState {
public:
Game* game = NULL;
virtual void update() = 0;
virtual void draw() = 0;
virtual void handleInput() = 0;
};
TestState.cpp是GameState类的扩展
#include "TestState.h"
#include "testGameplayState.h"
using namespace sf;
TestState::TestState(Game* g){
game = g;
texture.loadFromFile("graphics/grass.png");
sprite.setTexture(texture);
sprite.setPosition(Vector2f(0, 0));
}
void TestState::handleInput(){
Event event;
while (game->window.pollEvent(event)) {
if (event.type == Event::KeyPressed) {
if (event.key.code == Keyboard::O) {
game->pushState(new TestState(game));
}
else if (event.key.code == Keyboard::P) {
game->popState();
}
}
}
}
void TestState::update(){}
void TestState::draw(){
game->window.draw(sprite);
}
最后,游戏对象,它处理状态:Game.cpp
#include "Game.h"
#include "GameState.h"
using namespace sf;
//Please note that states is a private stack variable.
Game::Game(){
window.create(VideoMode(1920, 1080), "GameState Test", Style::Fullscreen);
}
Game::~Game() {
while (!states.empty()) {
popState();
}
}
void Game::run() {
while (window.isOpen()) {
if (currentState() == nullptr) {
std::cout << "Nullptr" << std::endl;
window.close();
}
//Handle input
currentState()->handleInput();
//Update
currentState()->update();
//Draw
window.clear();
currentState()->draw();
window.display();
}
}
GameState* Game::currentState() {
if (this->states.empty()) {
return nullptr;
}
else {
return this->states.top();
}
}
void Game::pushState(GameState* state) {
this->states.push(state);
}
void Game::popState() {
delete this->states.top();
this->states.pop();
}
每当我尝试创建新的游戏状态对象时,我都可以将它们添加到堆栈中并毫无问题地使用它们。然而,当我尝试弹出当前游戏状态时,它会立即退出,并在TestState.cpp中的while (game->window.pollEvent(event))
行上抛出一个错误,称为:Exception thrown: read access violation. **this** was 0xDDDDDDDD.
错误中的"this"可能是每个状态都保持的游戏对象指针,因为在调试器中,它显示游戏对象的值是0xDDDDDDDD。我知道0xDDDDDDDD意味着在该地址读取的内存已被删除,但是,我不明白为什么会出现这种情况。我希望只有游戏状态会被删除,但出于某种原因,游戏指针似乎也被删除了。
我用这个和这个来指导如何构建我的游戏状态类。
我该如何解决此问题?非常感谢你的帮助。
使用@OS2和@Some程序员的技巧,我解决了这个问题。在添加了检查以确保我永远不会对空指针进行操作之后,我更改了主游戏循环中方法调用的顺序。以下是新循环的样子:
Game.cpp
void Game::run()
{
while (window.isOpen())
{
//Handle input
currentState()->handleInput();
//Check if there are more states
if (currentState() == nullptr)
{
std::cout << "Nullptr, exiting program" << std::endl;
window.close();
break;
}
//Update
currentState()->update();
//Draw
window.clear();
currentState()->draw();
window.display();
}
}
发生的事情是,我弹出了堆栈,它正常工作,但随后立即对空指针进行操作。通过检查之后是否有空指针,我处理输入,然后避免这个问题。
更重要的是-修复0xDDDDDD错误-只需要一行就可以修复内存错误。
如果您通过用户输入弹出状态,就像我上面提到的那样,请确保在弹出状态后脱离循环。否则,while((循环将在状态终止后继续执行,并将抛出错误,因为该状态已不存在。我编辑的输入处理代码看起来像:
TestState.cpp
void TestState::handleInput()
{
std::cout << "Handling input" << std::endl;
Event event;
while (game->window.pollEvent(event))
{
if (event.type == Event::KeyPressed)
{
if (event.key.code == Keyboard::O)
{
game->pushState(new TestState(game));
}
else if (event.key.code == Keyboard::P)
{
game->popState();
break;
}
}
}
}
我希望这能帮助其他有这个问题的人。感谢@OS2和@Some程序员帮你找到问题。