Hello everybody! I want to create a simple game called Bug by the New Year. To do that, I'm developing a SGM (Simple Game Engine). This is how it works:
There are multiple level data files. There is one linear list of objectives. SGM takes one objective, constantly checks if it is accomplished, and when it is, loads a new level data file. Even though it's horribly inefficient, it allows very high scenario flexibility and super easy scripting. I've successfully completed one of the most complicated code segments inside the SGM: square grid collision detection and correct response mechanism. The segment itself is mere 37 lines of code, with 100 in total to be able to test it. Here it is:
#include <fstream>
#include <SFML/Graphics.hpp>
int main ()
{
int w, h, **lvl, posX, posY, block;
float velX(0), velY(0), inertia, friction;
std::ifstream data("data.txt");
/// I ASSUME THAT FILE EXISTS AND HAS VALID DATA. BEWARE - ERRORS UNHANDLED!
data >> w >> h >> posX >> posY >> block >> inertia >> friction;
posX *= block; posY *= block;
lvl = new int*[h]; for (int i = 0; i < h; ++i) lvl[i] = new int[w];
for (int i = 0; i < h; ++i)
for (int j = 0; j < w; ++j)
data >> lvl[i][j];
data.close();
sf::RenderWindow window(sf::VideoMode(w*block,h*block),"Collision", sf::Style::Close);
window.setFramerateLimit(30);
sf::Event event;
sf::Keyboard kboard;
sf::RectangleShape rectangle(sf::Vector2f(block,block));
event.type = sf::Event::GainedFocus; // In case event.type==sf::Event::Closed at the very beginning
while (window.isOpen())
{
if (kboard.isKeyPressed(sf::Keyboard::Up )) velY -= inertia;
if (kboard.isKeyPressed(sf::Keyboard::Down )) velY += inertia;
if (kboard.isKeyPressed(sf::Keyboard::Left )) velX -= inertia;
if (kboard.isKeyPressed(sf::Keyboard::Right)) velX += inertia;
if (velX > friction) velX -= friction; else
if (velX < -friction) velX += friction; else
velX = 0;
if (velY > friction) velY -= friction; else
if (velY < -friction) velY += friction; else
velY = 0;
/// SQUARE GRID COLLISION DETECTION AND CORRECT RESPONSE MECHANISM
bool collisionX(false), collisionY(false);
int positionX, positionY;
sf::IntRect hero(posX+velX, posY+velY, block, block);
for (int i = 0; i < h; ++i)
for (int j = 0; j < w; ++j)
if (lvl[i][j] == 1)
{
sf::IntRect rect(j*block, i*block, block, block);
if (hero.intersects(rect))
{
hero.left = posX;
if(hero.intersects(rect))
{
collisionY = true;
positionY = rect.top;
}
hero.left += velX; hero.top = posY;
if (hero.intersects(rect))
{
collisionX = true;
positionX = rect.left;
}
hero.top += velY;
}
}
if (collisionX)
{
if (velX > 0) posX = positionX - block; else
if (velX < 0) posX = positionX + block;
velX = 0;
}
if (collisionY)
{
if (velY > 0) posY = positionY - block; else
if (velY < 0) posY = positionY + block;
velY = 0;
}
/// END OF SQUARE GRID COLLISION DETECTION AND RESPONSE MECHANISM
posX += velX; posY += velY;
rectangle.setPosition(posX, posY);
window.clear(sf::Color(255,255,255));
rectangle.setFillColor(sf::Color(255,0,0));
window.draw(rectangle);
rectangle.setFillColor(sf::Color(0,0,0));
for (int i = 0; i < h; ++i)
for (int j = 0; j < w; ++j)
if (lvl[i][j] == 1)
{
rectangle.setPosition(j*block,i*block);
window.draw(rectangle);
}
window.display();
window.pollEvent(event);
if (event.type == sf::Event::Closed) window.close();
}
for (int i = 0; i < h; ++i) delete [] lvl[i];
delete [] lvl;
return 0;
}
I hope that maybe someone someday will find it useful. Code improvement discussion would be very appreciated :)
Oh, and "data.txt" contents:
10 10 1 1 20 0.9 0.5
1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 1 1 0 0 0 1
1 0 0 0 1 1 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1
Might as well do a bit of code review.
- Do not use manual memory management without a very good reason. lvl should be an STL container of some kind (presumably a vector of vectors), not a pointer to a pointer that requires tons of new/delete calls and is guaranteed to leak if there are any exceptions or early returns.
- Obviously, you should handle those potential ifstream errors. At least do an if(data) to make sure the file opened successfully.
- You don't need to instantiate sf::Keyboard. All of its methods are static, so it's more typical and less misleading to call them as sf::Keyboard::isKeyPressed().
- If event.type was one of the sf::Event types before you called pollEvent, that would be a bug in SFML. If you are experiencing this bug, please start a thread with a minimal and complete example that reproduces it. If not, take out that hack.
- You should really use curly braces around the bodies of at least some of your if/else/for statements.
- The "block" variable does not appear to be representing a block. Should it be called "blockSize"?
- imo the lines like this:
if (velX > friction) velX -= friction; else
if (velX < -friction) velX += friction; else
velX = 0;
would be more readable as:
if(velX < 0) { velX += friction } else { velX -= friction }
if(std::abs(velX) < friction) { velX = 0; }
Assuming I understand the intent correctly.
- In general I'm a bit confused about the relationship between "hero", "rectangle", "rect" and "posX/Y". More descriptive names might help, eg I think at least one of those is "herosNextPosition" but I can't quite tell which. I also *think* you're reusing "rectangle" to display the hero and all the blocks he can collide with, when it would be clearer and easier (and probably just as efficient) to have one heroRectangle and a std::vector of RectangleShapes called "blocks" (which you only need to set the position/color of once).
That's probably enough for one post.
would be more readable as:
if(velX < 0) { velX += friction } else { velX -= friction }
if(std::abs(velX) < friction) { velX = 0; }
Assuming I understand the intent correctly.
if (velX > friction)
velX -= friction;
else if (velX < -friction)
velX += friction;
else
velX = 0;
?