Using C , I wanted to create my first C graphical programs by learning the SFML library through creating a Conway's Game of Life implementation. My general thought process is by first creating a simple grid of cells that have the following data just starting off: x, y, and a state (either dead or alive but is set to dead initially). However I am having difficulty understanding how to create this in SFML, coming from a Java background one can make this with relative ease using a gridlayout, however, I am not sure how to approach making a grid of cells with each cell having individual data. How would I go about creating a grid of cells with what I currently have? Here is what I have so far, bear in mind majority of the code is incomplete and additions, and subtractions to the code, will be made, however my first step is to just create a grid of cells in which if I click a cell, it should print a response in console (similar to event listeners in java):
main.cpp:
#include <iostream>
#include "Include/Grid.h"
const int32_t windowWidth = 1000; // Cols
const int32_t windowHeight = 800; // Rows
int main() {
//For testing purposes, when the program is run, the grid should pop up,
//and I should be able to place black squares on a white box grid for today
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>
//Use enum to keep track of the state of each cell
//When grid is initialized, each cell will have a state of dead
//However when the user places the cells by clicking on the boxes,
//and clicks the "Run" button, then the program will start
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 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);
}
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;
}
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 height;
int32_t width;
bool isRunning;
public:
void initGridVector();
void setWidth(int width);
void setHeight(int height);
void placeCell();
void deleteCell();
void gridCopy();
void update();
void run();
void display(int gWidth, int gHeight) const;
Grid(int gridWidth, int gridHeight);
};
#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 gridWidth, int gridHeight) {
this->height = gridHeight;
this->width = gridWidth;
this->isRunning = false;
}
void Grid::initGridVector() {
for(int i = 0; i < height; i ){
std::vector<Cell> cellVec;
for(int j = 0; j < width; j ){
cellVec.push_back(*new Cell(i,j,Cell::DEAD));
}
gridVector.push_back(cellVec);
}
}
void Grid::display(int gWidth, int gHeight) const {
sf::RenderWindow window(sf::VideoMode(gWidth,gHeight), "Cellular Automata");
//Run the program as long as the window is open
while (window.isOpen() && isRunning)
{
//Check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event))
{
//"close requested" event: closes the window
if (event.type == sf::Event::Closed) window.close();
}
window.clear();
for (const auto & i : gridVector){
for (const auto & j : i){
window.draw(j.cell);
}
}
window.display();
}
}
void Grid::setWidth(int gWidth) {
this->width = gWidth;
}
void Grid::setHeight(int gHeight) {
this->height = gHeight;
}
void Grid::gridCopy() {
}
void Grid::update() {
}
void Grid::deleteCell() {
}
void Grid::placeCell() {
}
void Grid::run() {
std::cout << "Automata is running" << std::endl;
isRunning = true;
initGridVector();
display(width, height);
}
The run function will basically handle all the grid functionality, (Creating the grid with initialized cells, handling event listeners such as clicking a "start" button and allowing for the user to place cells).
CodePudding user response:
The problem lies in your cell class. When updating position you also must update the sf::RectangleShape cell;
position because it is what really is important in drawing into the window.
It seems to you that you only have a one rect top-left corner because in reality all of them are in the same spot since the deafult position is [0,0].
In order to fix this you need to change setPosition
and if you don't call it anywhere after creating and before drawing, then you also must change the constructor.
void Cell::setPosition(int xPos, int yPos) {
...
cell.setPosition(xCell,yCell);//xCell and yCell must also had been updated
}
Also I want to adress the comment:
its pretty slow (probably because I have a nested for loop within the while loop).
Yes, because you are calling many draw function they are in one frame (one while loop iteration), and in order to prevent that you could use sf::VertexArray. It enables you to create your own "drawing type" however, in your case you would be able to create something similar to their tilemap example but without texture.