How can I make my cellular automata quicker - c++

I have a grid of 300x300 cells. I am checking each cell individually which means I am checking 90000 cells atleast every second. Now, I know, one second is pretty fast considering the cpu is going through 300^2 different cells. But this stuff just isn't fast enough. I watched a guy on youtube who got his simulation to update every 20 ms. And he was running 512^2 cells. How is this possible? Optimizations? Is my computer a potato? I'll show my code if anyone wants to see exactly what i'm doing.
for (int y = GY; y >= 0; y--)
for (int x = 0; x < GX; x++) {
cell* cell_pointer = &grid[x][y];
if (grid[x][y].CellT == type::SAND) {
if (grid[x][y+1].CellT == type::AIR) {
if (y+1 > GY-1) {
continue;
}
cell* cell_below = &grid[x][y+1];
cell_below->Color ={ 255,0,0 };
cell_below->CellT = type::SAND;
cell_pointer->Color ={ 0,0,0 };
cell_pointer->CellT = type::AIR;
}
else if (grid[x+1][y+1].CellT == type::AIR) {
cell* cell_below = &grid[x+1][y+1];
cell_below->Color ={ 255,0,0 };
cell_below->CellT = type::SAND;
cell_pointer->Color ={ 0,0,0 };
cell_pointer->CellT = type::AIR;
}
else if (grid[x-1][y+1].CellT == type::AIR) {
cell* cell_below = &grid[x-1][y+1];
cell_below->Color ={ 255,0,0 };
cell_below->CellT = type::SAND;
cell_pointer->Color ={ 0,0,0 };
cell_pointer->CellT = type::AIR;
}
}
}
The code above is my update function. I am updating the cells bottom to top, left to right. It's a sand simulation which is why we're checking the bottom left, bottom right as well as the bottom neighboring cells.
//Square//
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_RenderClear(renderer);
//////////
int X = floor(mousePos.x/r.w);
int Y = floor(mousePos.y/r.h);
if (left) {
grid[X][Y].CellT = type::SAND;
grid[X][Y].Color ={ 255,0,0 };
}
if (right) {
grid[X][Y].CellT = type::WALL;
grid[X][Y].Color ={ 0,255,0 };
}
for (int y = 0; y < GY; y++)
for (int x = 0; x < GX; x++) {
cell* cell_pointer = &grid[x][y];
SDL_SetRenderDrawColor(renderer, cell_pointer->Color.r, cell_pointer->Color.g, cell_pointer->Color.b, 255);
r.x = cell_pointer->PosG.x;
r.y = cell_pointer->PosG.y;
SDL_RenderFillRect(renderer, &r);
}
SDL_RenderPresent(renderer);
SDL_UpdateWindowSurface(window);
The code above is my draw function. I am using SDL as my graphics API. I am getting the mouse position relative to the grids so that when I click left or right mouse buttons I can draw a sand cell or a wall cell. So I am checking each cell individually top to bottom and left to right and then drawing a square individually where the cell's position is.

Related

Raylib my Screen Collisions are not accurate

So I'm new to raylib and, basically, I'm trying to make a sandbox game and I am trying to make it so that when the player places a square or material when that material hits the edge of the screen it stops. Currently, my square when it falls it goes to the edge of the screen and it stops. but there's noticeable space between the screen, and the squares flicker and the squares don't stop on the same X level.
This Code is called when the user clicks on the screen. This DrawMat function is called when the user clicks and from there the square falls to the bottom of the screen.
Heres My Code
struct Mat
{
float X, Y;
float SpeedX, SpeedY;
float Force;
float Gravity;
Vector2 MousePos;
Vector2 size;
Color color;
void DrawMat() {
DrawRectangle(MousePos.x, MousePos.y, size.x, size.y, color);
MousePos.y += 9.81f;
if (MousePos.x < 0 || MousePos.x > GetScreenWidth()) {
MousePos.x *= -1;
}
if (MousePos.y < 0 || MousePos.y > GetScreenHeight()) {
MousePos.y *= -1;
}
}
};
int main() {
InitWindow(800, 600, "Speed Z Presends to you... Satisfiying, Amazing, Niffty, Dreamy. SIMULATOR");
SetWindowState(FLAG_VSYNC_HINT);
Mat Sand;
Sand.MousePos = { -100, -100 };
Sand.size = { 5, 5 };
Sand.color = YELLOW;
Sand.X = Sand.MousePos.x;
Sand.Y = Sand.MousePos.y;
Sand.SpeedX = 300;
Sand.SpeedY = 500;
Vector2 Mousepos = {-100, -100};
bool Mouseclicked = false;
Vector2 RectSize = { 2, 2 };
int Numof = 0;
std::vector<Mat> positions = {};
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(BLACK);
for (size_t i = 0; i < positions.size(); i++)
{
positions[i].DrawMat();
}
if (IsKeyPressed(KEY_ONE)) {
Numof = 1;
}
if (Numof == 1)
{
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
{
Mouseclicked = true;
Mousepos = GetMousePosition();
Sand.MousePos = GetMousePosition();
positions.push_back(Sand);
}
}
Here is an image of What I mean:
I don't really understand how this code would work at all.
But if what you mean is that the Y coordinate should be as close as possible to the edge, your if statement should be like so:
if (MousePos.y < 0) {
MousePos.y *= -1;
}
else if (MousePos.y > GetScreenHeight()) {
MousePos.y = GetScreenHeight();
}
Adjust the last expression as required. Maybe you need to subtract the height of the object so it doesn't disappear:
else if (MousePos.y > GetScreenHeight() - size.y) {
MousePos.y = GetScreenHeight() - size.y;
}
Why is that necessary?
I would imagine that the rendering you make uses MousePos.x as the left position and MousePos.y as the top position like so:
/---- this corner is (MousePos.x, MousePos.y)
|
| v |
| +----------------+---- |
| | | ^ |<- screen bottom edge
| | | | size.y |
+-------------|----------------|--|--------------+
| | v
+----------------+----
So to keep the sand grain on screen, you must make sure that the corner is at a location that makes it visible. As we see on the ASCII picture, for Y it means the maximum is GetScreenHeight() - size.y.
Note that you certainly have the same issue with the X coordinate (i.e. you may want the maximum to be GetScreenWidth() - size.x).

Low framerate with only map and minimap drawing (SFML)

I'm working on a small "game" like project as a practice, and I've managed to get my framerate down to not even 3 FPS. While the only thing that is being drawn is screen filling tiles and a minimap.
Now I've found that the problem is with the minimap, without it caps at 60 FPS. But unfortunately I'm not experienced enough to find out what the real problem is with it...
My draw function:
void StateIngame::draw()
{
m_gui.removeAllWidgets();
m_window.setView(m_view);
// Frame counter
float ct = m_clock.restart().asSeconds();
float fps = 1.f / ct;
m_time = ct;
char c[10];
sprintf(c, "%f", fps);
std::string fpsStr(c);
sf::String str(fpsStr);
auto fpsText = tgui::Label::create();
fpsText->setText(str);
fpsText->setTextSize(16);
fpsText->setPosition(15, 15);
m_gui.add(fpsText);
//////////////
// Draw map //
//////////////
int camOffsetX, camOffsetY;
int tileSize = m_map.getTileSize();
Tile tile;
sf::IntRect bounds = m_camera.getTileBounds(tileSize);
camOffsetX = m_camera.getTileOffset(tileSize).x;
camOffsetY = m_camera.getTileOffset(tileSize).y;
// Loop and draw each tile
// x and y = counters, tileX and tileY is the coordinates of the tile being drawn
for (int y = 0, tileY = bounds.top; y < bounds.height; y++, tileY++)
{
for (int x = 0, tileX = bounds.left; x < bounds.width; x++, tileX++)
{
try {
// Normal view
m_window.setView(m_view);
tile = m_map.getTile(tileX, tileY);
tile.render((x * tileSize) - camOffsetX, (y * tileSize) - camOffsetY, &m_window);
} catch (const std::out_of_range& oor)
{}
}
}
bounds = sf::IntRect(bounds.left - (bounds.width * 2), bounds.top - (bounds.height * 2), bounds.width * 4, bounds.height * 4);
for (int y = 0, tileY = bounds.top; y < bounds.height; y++, tileY++)
{
for (int x = 0, tileX = bounds.left; x < bounds.width; x++, tileX++)
{
try {
// Mini map
m_window.setView(m_minimap);
tile = m_map.getTile(tileX, tileY);
sf::RectangleShape miniTile(sf::Vector2f(4, 4));
miniTile.setFillColor(tile.m_color);
miniTile.setPosition((x * (tileSize / 4)), (y * (tileSize / 4)));
m_window.draw(miniTile);
} catch (const std::out_of_range& oor)
{}
}
}
// Gui
m_window.setView(m_view);
m_gui.draw();
}
The Tile class has a variable of type sf::Color which is set during map generating. This color is then used to draw the minimap instead of the 16x16 texture that is used for the map itself.
So when I leave out the minimap drawing, and only draw the map itself, it's more fluid than I could wish for...
Any help is appreciated!
You are generating the view completly new for every frame. Do this once on startup should be enought.

Bounding box for C++ particle system not working

I have created a particle system in c++ using Qt which includes gravity. I would like to include a bounding box so that the system can also include collisions however I cannot seem to get it to work. Here is my .h code:
typedef struct {
int top;
int bottom;
int left;
int right;
}BoundingBox;
BoundingBox makeBoundingBox(int top, int bottom, int left, int right);
.cpp:
BoundingBox makeBoundingBox(int top, int bottom, int left, int right)
{
BoundingBox boundingBox;
boundingBox.top = top;
boundingBox.bottom = bottom;
boundingBox.left = left;
boundingBox.right = right;
return boundingBox;
}
I have then updated the bounding box in my emitter class using this loop:
for(int i=0; i<m_numParticles; ++i)
{
if (m_pos.m_y >= _boundingBox.top)
{
m_dir.m_y = (-1)*(m_dir.m_y);
}
if (m_pos.m_y <= _boundingBox.bottom)
{
m_dir.m_y = (-1)*(m_dir.m_y);
}
if (m_pos.m_x <= _boundingBox.left)
{
m_dir.m_x = (-1)*(m_dir.m_x);
}
if (m_pos.m_x >= _boundingBox.right)
{
m_dir.m_x = (-1)*(m_dir.m_x);
}
m_particles[i].update(m_gravity, _boundingBox);
And have set the bounding box in my window as so:
m_emitter->setBoundingBox(makeBoundingBox(m_height, 0, 0, m_width));
I am not getting any errors however it doesn't seem to be working,
Any advice would be appreciated
Thanks
Assuming that decreasing X means going towards left and decreasing Y means going towards top, your conditions seem incorrect. I would also update the position alongside the velocity to make sure it doesn't get triggered multiple times in a row.
Example for the Y coordinate:
// Are we going up too much?
if (m_pos.m_y < _boundingBox.top)
{
m_dir.m_y = (-1)*(m_dir.m_y);
m_pos.m_y = _boundingBox.top + 1;
}
// Or are we going down too much?
else if (m_pos.m_y > _boundingBox.bottom)
{
m_dir.m_y = (-1)*(m_dir.m_y);
m_pos.m_y = _boundingBox.bottom - 1;
}
Shouldn't:
m_emitter->setBoundingBox(makeBoundingBox(m_height, 0, 0, m_width));
Be:
m_emitter->setBoundingBox(makeBoundingBox(0, m_height, 0, m_width));

Blitting a surface onto another surface (SDL2, C++, VS2015)

I'm working on a small game for school. I tiled an image on screen, but every time my character moves I have to re-tile it (the tiles are behind the character, because it's a grid and the character moves in the cells). I tried to tile everything onto a different surface, and then have that surface blit onto my screen surface to avoid having to retile it every single time and save on process time.
It didn't really work, it's like the surface that I tile on forgets what was tiled onto it. It doesn't error it, it just doesn't display the tiled surface on my window surface.
Here's my code (the relevant part at least)
void postaviTiles() {
SDL_BlitSurface(cell, NULL, polje, &offsetcell); //cell
for (int i = 0; i < 89; i++) {
SDL_Delay(5);
if (offsetcell.x < 450) {
offsetcell.x += 50;
SDL_BlitSurface(cell, NULL, polje, &offsetcell);
}
else {
offsetcell.x = 9;
offsetcell.y += 50;
SDL_BlitSurface(cell, NULL, polje, &offsetcell);
}
SDL_UpdateWindowSurface(okno);
}
poljezrisano = true;
}
//--------------------------------------------------------------//
void tileCells() {
if (poljezrisano == false) {
postaviTiles();}
SDL_BlitSurface(polje, NULL, oknoSurface, NULL); //cell
SDL_UpdateWindowSurface(okno);
}
//--------------------------------------------------------------//
Worth mentioning is that tiling it every single time works fine, but I want to tile it once, have that on a surface and then just blit that surface onto my screen surface.
P.S.: Sorry about most of the variables and function names not being in English
The SDL_BlitSurface takes in a source surface, a clip of that source surface, then the destination surface and a position where you want to display (blit) your source.
The last parameter thats passed to SDL_BlitSurface ignores the width and height, it just takes in the x an y.
Here is a quote from the documentation:
The width and height in srcrect determine the size of the copied rectangle. Only the position is used in the dstrect (the width and height are ignored).
And the prototype for the function:
int SDL_BlitSurface(SDL_Surface* src,
const SDL_Rect* srcrect,
SDL_Surface* dst,
SDL_Rect* dstrect)
That's one thing to keep in mind, not sure if that applies to your case, since your variable names aren't English.
But essentially with this line:
SDL_BlitSurface(cell, NULL, polje, &offsetcell);
You are telling SDL that you want all of cell placed inside polje at the position offsetcell.x and offsetcell.y with the width of cell.w and the height of cell.h.
If you wanted to place cell inside polje using the width and height of offsetcell then you would have to use another blit function, namely SDL_BlitScaled
Here is how I would blit tiles inside a grid (map).
SDL_Surface* grid; // assuming this is the new grid surface that holds the blited tiles
SDL_Surface* tile; // assuming this is a single tile of width 50, height 50
SDL_Surface* windowSurface;
SDL_Window* window;
int TileWidth = 50;
int TileHeight = 50;
int NumTiles = 90;
int TileColumns = 450 / TileWidth; // = 9, so we have a grid of 9X10
bool isFillGridTiles = false;
void FillGridTiles()
{
for (int i = 0;i < NumTiles; i++)
{
auto y = i / TileColumns; // divide to get the y position and...
auto x = i % TileColumns; // the remainder is the x position inside the grid
SDL_Rect srcClip;
srcClip.x = 0;
srcClip.y = 0;
srcClip.w = TileWidth;
srcClip.h = TileHeight;
SDL_Rect dstClip;
dstClip.x = x * TileWidth;
dstClip.y = y * TileHeight;
dstClip.w = TileWidth;
dstClip.h = TileHeight;
SDL_BlitSurface(tile, &srcClip, grid, &dstClip); //since we have the same width and height, we can use SDL_BlitSurface instead of SDL_BlitScaled
}
isFillGridTiles = true;
}
void BlitOnScreen()
{
if(!isFillGridTiles)
{
FillGridTiles();
}
SDL_BlitSurface(grid, NULL, windowSurface, NULL);
SDL_UpdateWindowSurface(window);
}
Not sure if the code is complete as posted, but it seems you are not initializing offsetcell. That fits the symptom of having nothing show up. Explicit definition of offsetcell might be better than the incremental method you've provided. For example:
for( offsetcell.x = 0; offsetcell.x < 450; offsetcell.x += 50) {
for( offsetcell.y = 0; offsetcell.y < 450; offsetcell.y += 50) {
...
}
}

Allegro 5 drawing a bitmap over a primitive

I've recently tried making an inventory system in allegro 5, where I draw a grid of squares 20x20 and drag-drop items around. The problem is, I can see the item sprite going under the actual grid that I drew, which is an unwanted effect. Here's my code:
if(draw)
{
draw = false;
al_draw_bitmap(image, item.posx, item.posy, 0);
if(mouseKey)
{
grab = true;
item.posx = mouse.posx - (item.boundx-5);
item.posy = mouse.posy - (item.boundy-5);
}
else if(mouseKey == false && grab == true)
{
for(int i = 0; i < mouse.posx; i += 20)
{
if(i < mouse.posx)
item.posx = i;
}
for(int j = 0; j < mouse.posy; j += 20)
{
if(j < mouse.posy)
{
item.posy = j;
}
}
grab = false;
}
for(int i = 0; i <= width; i += 20)
{
al_draw_line(i, 0, i, height, al_map_rgb(0, 0, 0), 1);
al_draw_line(0, i, width, i, al_map_rgb(0, 0, 0), 1);
}
al_flip_display();
al_clear_to_color(al_map_rgb(40,40,40));
}
(I know it's terribly written and un-optimized but I wrote it in about 10 minutes simply as a test)
How can I make it so the item sprite does not display the lines over it? Here's an example of my problem if I was too vague:
I'm using Codeblocks IDE on windows XP
Unless you fiddle with OpenGL settings, you're going to always get the things you draw last on top. So in this case, simply move al_draw_bitmap(image, item.posx, item.posy, 0); to be directly above al_flip_display().
Note that you will have some problems because you are manipulating item.posx and item.posy in that section, so you'd have to first cache the results:
int x = item.posx;
int y = item.posy;
// ...
al_draw_bitmap(image, x, y, 0);
al_flip_display();
However, that's just a bandaid over the larger problem: you shouldn't be changing anything inside your drawing block. The entire if/else block should be elsewhere. i.e.:
if (event timer is a game tick)
{
do all logic stuff
draw = true
}
if (draw)
{
do all drawing stuff
draw = false;
}