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

基于SFML的康威生命游戏细胞网格创建及交互实现咨询

康威生命游戏开发问题

我有Java开发背景,现在用C++结合SFML库开发首个图形化程序,目标是实现康威生命游戏。目前的思路是创建基础细胞网格,每个细胞包含x、y坐标与状态(初始为死亡)。但因为SFML和Java布局机制不同,不清楚如何创建带有独立数据的细胞网格,同时需要实现点击细胞时控制台输出响应的功能。以下是我写的不完整代码,请求指导完成:


main.cpp

#include <iostream>
#include "Include/Grid.h"

const int32_t windowWidth = 1000; // 窗口像素宽度
const int32_t windowHeight = 800; // 窗口像素高度

int main() {
    // 测试需求:运行程序后弹出网格,可点击放置黑色方块
    Grid gameOfLife(windowWidth, windowHeight);
    gameOfLife.run();
    return 0;
}

cell.h

#ifndef CELLULARAUTOMATA2D_CELL_H
#define CELLULARAUTOMATA2D_CELL_H

#include <iostream>
#include <vector>
#include <cstdio>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>

// 用枚举记录细胞状态,初始为死亡,点击后可切换为存活
class Cell {
public:
    enum State {
        ALIVE,
        DEAD
    };

private:
    std::int32_t xCell;
    std::int32_t yCell;
    State cellState;

public:
    sf::RectangleShape cell;

    [[nodiscard]] int getX() const;
    [[nodiscard]] int getY() const;

    State getState();
    void toggleState(); // 新增:切换细胞状态并更新视觉表现

    void setPosition(int xPos, int yPos);

    Cell(int xPos, int yPos, Cell::State state);
};
#endif //CELLULARAUTOMATA2D_CELL_H

cell.cpp

#include "Include/Cell.h"
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>

Cell::Cell(int xPos, int yPos, Cell::State state) {
    this->xCell = xPos;
    this->yCell = yPos;
    this->cellState = state;
    this->cell = sf::RectangleShape(sf::Vector2f(10,10));
    cell.setFillColor(sf::Color::White);
    cell.setOutlineColor(sf::Color::Black);
    cell.setOutlineThickness(1); // 新增:设置轮廓厚度,让网格边界更清晰
    // 将细胞坐标转换为窗口像素位置
    cell.setPosition(static_cast<float>(xPos * 10), static_cast<float>(yPos * 10));
}

int Cell::getX() const {
    return xCell;
}

int Cell::getY() const {
    return yCell;
}

Cell::State Cell::getState() {
    return cellState;
}

void Cell::setPosition(int xPos, int yPos) {
    xCell = xPos;
    yCell = yPos;
    cell.setPosition(static_cast<float>(xPos * 10), static_cast<float>(yPos * 10));
}

// 新增:切换细胞状态并同步颜色
void Cell::toggleState() {
    cellState = (cellState == DEAD) ? ALIVE : DEAD;
    cell.setFillColor(cellState == ALIVE ? sf::Color::Black : sf::Color::White);
}

grid.h

#ifndef CELLULARAUTOMATA2D_GRID_H
#define CELLULARAUTOMATA2D_GRID_H

#include <iostream>
#include <vector>
#include <cstdio>
#include "Cell.h"

class Grid {
private:
    std::vector<std::vector<Cell>> gridVector;
    int32_t cellRows; // 细胞行数
    int32_t cellCols; // 细胞列数
    bool isRunning;
    const int CELL_SIZE = 10; // 单个细胞的像素尺寸

public:
    void initGridVector();

    void setWidth(int pixelWidth);
    void setHeight(int pixelHeight);

    void toggleCell(int x, int y); // 新增:切换指定位置细胞状态

    void gridCopy();
    void update();

    void run();

    void display(int pixelWidth, int pixelHeight); // 去掉const,因为需要修改细胞状态

    Grid(int pixelWidth, int pixelHeight);
};

#endif //CELLULARAUTOMATA2D_GRID_H

grid.cpp

#include "Include/Grid.h"
#include "Include/Cell.h"
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>

Grid::Grid(int pixelWidth, int pixelHeight) {
    // 将窗口像素尺寸转换为细胞行列数
    this->cellRows = pixelHeight / CELL_SIZE;
    this->cellCols = pixelWidth / CELL_SIZE;
    this->isRunning = false;
}

void Grid::initGridVector() {
    gridVector.clear(); // 初始化前清空,避免重复创建
    for(int i = 0; i < cellRows; i++){
        std::vector<Cell> cellVec;
        for(int j = 0; j < cellCols; j++){
            // 直接构造Cell对象,避免new导致的内存泄漏
            cellVec.emplace_back(j, i, Cell::DEAD);
        }
        gridVector.push_back(cellVec);
    }
}

void Grid::display(int pixelWidth, int pixelHeight) {
    sf::RenderWindow window(sf::VideoMode(pixelWidth,pixelHeight), "Cellular Automata");

    while (window.isOpen() && isRunning)
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
            // 处理鼠标左键点击事件
            if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) {
                // 将鼠标像素位置转换为细胞坐标
                int cellX = event.mouseButton.x / CELL_SIZE;
                int cellY = event.mouseButton.y / CELL_SIZE;
                // 检查坐标是否在网格范围内
                if (cellX >= 0 && cellX < cellCols && cellY >=0 && cellY < cellRows) {
                    toggleCell(cellX, cellY);
                    // 控制台输出点击响应
                    std::cout << "点击细胞 (" << cellX << ", " << cellY << "),当前状态:" 
                              << (gridVector[cellY][cellX].getState() == Cell::ALIVE ? "存活" : "死亡") << std::endl;
                }
            }
        }
        window.clear(sf::Color::White); // 设置窗口背景为白色

        for (auto & row : gridVector){
            for (auto & cell : row){
                window.draw(cell.cell);
            }
        }

        window.display();
    }
}

void Grid::setWidth(int pixelWidth) {
    this->cellCols = pixelWidth / CELL_SIZE;
}

void Grid::setHeight(int pixelHeight) {
    this->cellRows = pixelHeight / CELL_SIZE;
}

void Grid::toggleCell(int x, int y) {
    gridVector[y][x].toggleState();
}

void Grid::gridCopy() {
    // 后续实现生命游戏状态复制逻辑
}

void Grid::update() {
    // 后续实现生命游戏规则更新逻辑
}

void Grid::run() {
    std::cout << "Automata is running" << std::endl;
    isRunning = true;
    initGridVector();
    display(cellCols * CELL_SIZE, cellRows * CELL_SIZE); // 传递窗口像素尺寸
}

关键修正与实现说明
  1. 修复内存泄漏:原initGridVector中使用*new Cell(...)会导致内存泄漏,改为直接用emplace_back构造Cell对象,避免手动分配内存。
  2. 统一坐标映射:定义CELL_SIZE常量,将窗口像素尺寸转换为细胞行列数,同时在Cell构造时将细胞坐标转换为窗口像素位置,确保网格正确显示。
  3. 实现点击交互:在display的事件循环中处理鼠标点击事件,将像素坐标转换为细胞坐标,调用toggleCell切换状态,并在控制台输出点击信息。
  4. 完善状态视觉反馈:新增toggleState方法,切换细胞生死状态的同时更新填充颜色,让操作反馈更直观。
  5. 调整尺寸逻辑:将Grid构造函数中的像素宽高转换为细胞行列数,避免生成超出窗口范围的无效细胞。

内容的提问来源于stack exchange,提问作者Cr3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 22:25:18