使用SFML开发C++打砖块游戏触发非法指令错误求助
Arkanoid游戏状态切换后崩溃问题排查
问题描述
使用C++结合SFML开发打砖块游戏,当游戏从游玩状态切换至结束状态后,分数会在控制台短暂显示,随即触发崩溃,错误提示:
make: *** [Makefile:19: all] Invalid instruction (a memory dump was created)
环境信息
- 操作系统:Archlinux
- 依赖版本:make 4.4.1-2,sfml 2.6.0-3
- 运行方式:执行
make命令启动游戏
相关代码片段
Game.cpp
#include "../include/Game.hpp" Game::Game() { initWindow(); initFont(); currentState = std::make_unique<GameState>(window, font); } void Game::initWindow() { window.create(VideoMode(SCREEN_WIDTH, SCREEN_HEIGHT), "Arkanoid"); window.setFramerateLimit(60); } void Game::initFont() { if (!font.loadFromFile("fonts/mont_extralightdemo.ttf")) exit(0); } void Game::changeState(std::unique_ptr<State> newState) { currentState = std::move(newState); } void Game::handleInput() { currentState->handleInput(); } void Game::update() { currentState->update(); if (currentState->isGameOver()) { int score = currentState->getScore(); std::unique_ptr<State> newState = std::make_unique<EndGameState>(window, font, score); changeState(std::move(newState)); } } void Game::render() { window.clear(); currentState->render(window); window.display(); } void Game::run() { while (window.isOpen()) { handleInput(); update(); render(); } }
GameState.cpp
#include "../include/GameState.hpp" GameState::GameState(RenderWindow& windowGame, Font& fontGame) : window(windowGame) , font(fontGame) , ball() , board() , blocks() , text(fontGame) , score(0) { } void GameState::handleInput() { if (!ballMove) { if (Keyboard::isKeyPressed(Keyboard::D)) { board.moveRight(); ball.setPosition(Vector2f(board.getPosition().x + board.getSize().x / 2 - ball.getRadius(), ball.getPosition().y)); } if (Keyboard::isKeyPressed(Keyboard::A)) { board.moveLeft(); ball.setPosition(Vector2f(board.getPosition().x + board.getSize().x / 2 - ball.getRadius(), ball.getPosition().y)); } } else if (ballMove) { if (Keyboard::isKeyPressed(Keyboard::D)) { board.moveRight(); } if (Keyboard::isKeyPressed(Keyboard::A)) { board.moveLeft(); } } while (window.pollEvent(ev)) { switch (ev.key.code) { case Keyboard::Escape: window.close(); break; case Keyboard::Space: ballMove = true; } } } void GameState::update() { if (ballMove) { ball.move(); if (ball.getGlobalBounds().intersects(board.getGlobalBounds())) ball.setVelocityY(-(rand() % 7 + 3)); if (blocks.handleCollision(ball.getGlobalBounds())) { ball.setVelocityY((rand() % 7 + 3)); score += 50; } if (ball.getPosition().y > board.getPosition().y) setGameOver(true); } } void GameState::render(RenderWindow& window) { text.draw(&window, score); ball.draw(&window); board.draw(&window); blocks.draw(&window); } int GameState::getScore() { return score; } bool GameState::isGameOver() const { return gameOver; } void GameState::setGameOver(bool value) { gameOver = value; }
EndGameState.cpp
#include "../include/EndGameState.hpp" EndGameState::EndGameState(RenderWindow& windowGame, Font& fontGame, int& score) : window(windowGame) , text(fontGame) , scoreFinal(score) { text.setPosition(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 9); } void EndGameState::handleInput() { while (window.pollEvent(ev)) { switch (ev.key.code) { case Keyboard::Escape: window.close(); break; } } } void EndGameState::update() { } void EndGameState::render(sf::RenderWindow& window) { text.draw(&window, scoreFinal); } bool EndGameState::isGameOver() const { } int EndGameState::getScore() { }
TextGame.cpp
#include "../include/TextGame.hpp" TextGame::TextGame(Font& font) { text.setFont(font); text.setCharacterSize(36); text.setColor(Color::White); text.setPosition(60, 30); } void TextGame::setPosition(float x, float y) { text.setPosition(Vector2f(x, y)); } void TextGame::setCharacterSize(int size) { text.setCharacterSize(size); } void TextGame::draw(RenderWindow* window, int score) { text.setString("Score : " + std::to_string(score)); window->draw(text); }
Makefile
CXX = g++ CXXFLAGS = -Wall -Wreorder SFMLFLAGS = -lsfml-graphics -lsfml-system -lsfml-window SRCDIR = src INCDIR = include OBJDIR = obj BINDIR = bin TARGET = main SOURCES = $(wildcard $(SRCDIR)/*.cpp) HEADERS = $(wildcard $(INCDIR)/*.hpp) OBJECTS := $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o,$(SOURCES)) .PHONY: all clean all: $(BINDIR)/$(TARGET) @clear @./$(BINDIR)/$(TARGET) $(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(HEADERS) $(CXX) $(CXXFLAGS) -I$(INCDIR) -c -o $@ $< $(BINDIR)/$(TARGET): $(OBJECTS) $(CXX) $^ -o $@ $(SFMLFLAGS) clean: @rm -rf $(OBJDIR)/* @rm -rf $(BINDIR)/*
错误原因分析
- 非void函数未返回值:EndGameState中的
isGameOver()和getScore()是继承自State类的虚函数,但这两个函数没有任何返回语句。C++标准规定,非void函数无返回值会触发未定义行为,直接导致程序崩溃(表现为指令错误)。 - 临时变量绑定引用:EndGameState构造函数的第三个参数是
int& score,但在Game.cpp中传入的是临时变量int score = currentState->getScore();。C++不允许将非const引用绑定到临时变量,这同样属于未定义行为,可能引发内存访问异常。
修复方案
1. 补全EndGameState的虚函数返回值
修改EndGameState.cpp中的两个函数:
bool EndGameState::isGameOver() const { // 结束状态下始终返回true,可根据需求调整逻辑 return true; } int EndGameState::getScore() { return scoreFinal; }
2. 修改EndGameState构造函数的参数类型
将构造函数的第三个参数从引用改为值传递,避免绑定临时变量:
EndGameState::EndGameState(RenderWindow& windowGame, Font& fontGame, int score) : window(windowGame) , text(fontGame) , scoreFinal(score) { text.setPosition(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 9); }
3. 编译时开启更多警告
修改Makefile中的CXXFLAGS,添加强制检查函数返回值的警告,提前发现此类问题:
CXXFLAGS = -Wall -Wreorder -Wreturn-type -Werror
内容的提问来源于stack exchange,提问作者Dxrk_
相关产品推荐
相关产品推荐

