You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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)/*

错误原因分析

  1. 非void函数未返回值:EndGameState中的isGameOver()和getScore()是继承自State类的虚函数,但这两个函数没有任何返回语句。C++标准规定,非void函数无返回值会触发未定义行为,直接导致程序崩溃(表现为指令错误)。
  2. 临时变量绑定引用: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_

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:45:16