Okay, so i guess the title says it a bit.
I felt like making a SFML game prototype, a fully functional game incorporating both keyboard, mouse, rotation, lists, iteration, hittests, vectors all that, in as small a code as i could without any needless optimization.. a game that anyone who wants, can take apart and put together as they want...
But even more, i would Love if we could make a collection of such short, fully functional games. some like this in just one file.
some using functions.
some using multiple files and headers
but all small, gritty fully functional for others to pick apart.
maybe make tutorials on them...
This is not about CORRECT code... this is about WORKING code.... the key stone to prototyping....
Why? Because that was how i learned to program back in the day... i took working games (Pacal, VB and later Flash AS2 games btw) and made changes until they did something differently so i could figure out what it was...
So... here is around an hours work.
First the full code without comments and without extra line space.
Main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <list>
void main(int argc, char** argv[]) {
sf::RectangleShape m_tankBody;
m_tankBody.setFillColor(sf::Color(120, 150, 120));
m_tankBody.setSize(sf::Vector2f(30, 20));
m_tankBody.setOrigin(12, 10);
m_tankBody.setPosition(400, 300);
sf::RectangleShape m_tankGun;
m_tankGun.setFillColor(sf::Color(30, 50, 30));
m_tankGun.setSize(sf::Vector2f(25, 4));
m_tankGun.setOrigin(2, 2);
m_tankGun.setPosition(400, 300);
float m_tankSpeed = 3;
std::list<sf::Vector3f> m_bullets;
sf::CircleShape m_bullet;
m_bullet.setFillColor(sf::Color(130, 50, 30));
m_bullet.setRadius(2);
m_bullet.setOrigin(1, 1);
float m_bulletSpeed = 15;
sf::Clock m_fireTime;
m_fireTime.restart();
float m_fireRate = 0.5;
sf::CircleShape m_target;
m_target.setFillColor(sf::Color(200, 30, 30, 150));
m_target.setRadius(15);
m_target.setOrigin(15, 15);
m_target.setPosition(600, 300);
sf::Clock m_gameTime, m_frameTime;
m_gameTime.restart();
m_frameTime.restart();
int m_UPS = 60, m_FPS = 30;
static const float PI = 3.14159265359;
sf::RenderWindow m_window(sf::VideoMode(800, 600), "SFML simple tank");
while (m_window.isOpen()) {
sf::Event event;
while (m_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
m_window.close();
}
}
if (m_gameTime.getElapsedTime().asSeconds() > 1 / m_UPS) {
m_gameTime.restart();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right)) {
m_tankBody.setRotation(m_tankBody.getRotation() + 0.1);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left)) {
m_tankBody.setRotation(m_tankBody.getRotation() - 0.1);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) {
float _xPos = m_tankBody.getPosition().x + ((m_tankSpeed / m_UPS) * cos(m_tankBody.getRotation() / 180 * PI));
float _yPos = m_tankBody.getPosition().y + ((m_tankSpeed / m_UPS) * sin(m_tankBody.getRotation() / 180 * PI));
if (_xPos > m_window.getSize().x) _xPos = 0;
else if (_xPos < 0) _xPos = m_window.getSize().x;
if (_yPos > m_window.getSize().y) _yPos = 0;
else if (_yPos < 0) _yPos = m_window.getSize().y;
m_tankBody.setPosition(_xPos, _yPos);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) {
float _xPos = m_tankBody.getPosition().x - ((m_tankSpeed / m_UPS) * cos(m_tankBody.getRotation() / 180 * PI));
float _yPos = m_tankBody.getPosition().y - ((m_tankSpeed / m_UPS) * sin(m_tankBody.getRotation() / 180 * PI));
if (_xPos > m_window.getSize().x) _xPos = 0;
else if (_xPos < 0) _xPos = m_window.getSize().x;
if (_yPos > m_window.getSize().y) _yPos = 0;
else if (_yPos < 0) _yPos = m_window.getSize().y;
m_tankBody.setPosition(_xPos, _yPos);
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left) && m_fireTime.getElapsedTime().asSeconds() > m_fireRate) {
m_fireTime.restart();
m_bullets.push_back(sf::Vector3f(m_tankBody.getPosition().x, m_tankBody.getPosition().y, m_tankGun.getRotation()));
}
std::list<sf::Vector3f>::iterator i = m_bullets.begin();
while (i != m_bullets.end())
{
bool isDead = false;
//we also need to know how far from the target the bullets are
int xDist = i->x - m_target.getPosition().x;
int yDist = i->y - m_target.getPosition().y;
float m_distance = sqrtf((xDist * xDist) + (yDist*yDist));
if (i->x > m_window.getSize().x || i->x < 0 || i->y > m_window.getSize().y || i->y < 0) isDead = true;
else if (m_distance < 15) {
isDead = true;
//But we also need to move the target if it is hit.
float nY = rand() % (m_window.getSize().y - 100) + 50;
float nX;
//lets make sure it ends up on hte opposite side of the screen than the tank.
if (m_tankBody.getPosition().x > (m_window.getSize().x / 2)) nX = rand() % (m_window.getSize().x / 3);
else nX = rand() % (m_window.getSize().x / 3) + ((m_window.getSize().x / 3) * 2);
m_target.setPosition(nX, nY);
}
if (isDead) {
m_bullets.erase(i++);
}
else
{
i->x += ((m_bulletSpeed / m_UPS) * cos(i->z / 180 * PI));
i->y += ((m_bulletSpeed / m_UPS) * sin(i->z / 180 * PI));
i++;
}
}
m_tankGun.setPosition(m_tankBody.getPosition());
m_tankGun.setRotation(atan2(sf::Mouse::getPosition(m_window).y - m_tankGun.getPosition().y, sf::Mouse::getPosition(m_window).x - m_tankGun.getPosition().x) * 180 / PI);
}
if (m_frameTime.getElapsedTime().asSeconds() > 1 / m_FPS) {
m_frameTime.restart();
//lets make a nice green field for us to drive the tank on.
m_window.clear(sf::Color(0, 110, 50));
for (sf::Vector3f it : m_bullets) {
m_bullet.setPosition(it.x, it.y);
m_window.draw(m_bullet);
}
m_window.draw(m_tankBody);
m_window.draw(m_tankGun);
m_window.draw(m_target);
m_window.display();
}
}
}
and of course, one with my comments. And somewhat better spacing....
Main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <list>
void main(int argc, char** argv[]) {
//Set up a figure to use as a tank
sf::RectangleShape m_tankBody;
m_tankBody.setFillColor(sf::Color(120, 150, 120));
m_tankBody.setSize(sf::Vector2f(30, 20));
m_tankBody.setOrigin(12, 10);
m_tankBody.setPosition(400, 300);
//Set up a box to use as the gun of the tank
sf::RectangleShape m_tankGun;
m_tankGun.setFillColor(sf::Color(30, 50, 30));
m_tankGun.setSize(sf::Vector2f(25, 4));
m_tankGun.setOrigin(2, 2);
m_tankGun.setPosition(400, 300);
//We need to know how fast the tank can drive
float m_tankSpeed = 3;
//make a list for bullets, it needs to be a 3 dimenisonel vector, tow dimensions will be used for x and y, and the last one will be used for rotation.
//so the vectors x, and y will be x and y. its z will be rotation.
std::list<sf::Vector3f> m_bullets;
//Make a circle for bullets
sf::CircleShape m_bullet;
m_bullet.setFillColor(sf::Color(130, 50, 30));
m_bullet.setRadius(2);
m_bullet.setOrigin(1, 1);
//We need to know how fast do bullets fly?
float m_bulletSpeed = 15;
//and a timer for fiering:
sf::Clock m_fireTime;
m_fireTime.restart();
float m_fireRate = 0.5;
//Make a circle for a target
sf::CircleShape m_target;
m_target.setFillColor(sf::Color(200, 30, 30, 150));
m_target.setRadius(15);
m_target.setOrigin(15, 15);
m_target.setPosition(600, 300);
//We also need a way to keep updates and frame rate stable (We could use delta time, but for now we go with the most simple solution)
sf::Clock m_gameTime, m_frameTime;
m_gameTime.restart();
m_frameTime.restart();
int m_UPS = 60, m_FPS = 30;
//PI is a great number, and we need it to calculate rotation and movement.
static const float PI = 3.14159265359;
//Set up a window to render our game in.
sf::RenderWindow m_window(sf::VideoMode(800, 600), "SFML simple tank");
//Update the game as long as the window is open
while (m_window.isOpen()) {
//This allows us to close the game window
sf::Event event;
while (m_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
m_window.close();
}
}
//if enough time has passed, update the game, remember to restart the clock.
if (m_gameTime.getElapsedTime().asSeconds() > 1 / m_UPS) {
m_gameTime.restart();
//Rotate the tank when left or right key is pressed
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right)) {
m_tankBody.setRotation(m_tankBody.getRotation() + 0.1);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left)) {
m_tankBody.setRotation(m_tankBody.getRotation() - 0.1);
}
//move the tank forward based on rotation if UP is pressed
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) {
float _xPos = m_tankBody.getPosition().x + ((m_tankSpeed / m_UPS) * cos(m_tankBody.getRotation() / 180 * PI));
float _yPos = m_tankBody.getPosition().y + ((m_tankSpeed / m_UPS) * sin(m_tankBody.getRotation() / 180 * PI));
if (_xPos > m_window.getSize().x) _xPos = 0;
else if (_xPos < 0) _xPos = m_window.getSize().x;
if (_yPos > m_window.getSize().y) _yPos = 0;
else if (_yPos < 0) _yPos = m_window.getSize().y;
m_tankBody.setPosition(_xPos, _yPos);
}
//move the tank back based on rotation if DOWN is pressed
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) {
float _xPos = m_tankBody.getPosition().x - ((m_tankSpeed / m_UPS) * cos(m_tankBody.getRotation() / 180 * PI));
float _yPos = m_tankBody.getPosition().y - ((m_tankSpeed / m_UPS) * sin(m_tankBody.getRotation() / 180 * PI));
if (_xPos > m_window.getSize().x) _xPos = 0;
else if (_xPos < 0) _xPos = m_window.getSize().x;
if (_yPos > m_window.getSize().y) _yPos = 0;
else if (_yPos < 0) _yPos = m_window.getSize().y;
m_tankBody.setPosition(_xPos, _yPos);
}
//if the mouse button LEFT is pressed, and time enough has passed since last bullet was fired. add a bullet to the bullet list.
if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left) && m_fireTime.getElapsedTime().asSeconds() > m_fireRate) {
m_fireTime.restart();
m_bullets.push_back(sf::Vector3f(m_tankBody.getPosition().x, m_tankBody.getPosition().y, m_tankGun.getRotation()));
}
//Move, hittest and erase each bullet as needed.
std::list<sf::Vector3f>::iterator i = m_bullets.begin();
while (i != m_bullets.end())
{
//we need a bolean to see if the bullets are dead.
bool isDead = false;
//we also need to know how far from the target the bullets are
int xDist = i->x - m_target.getPosition().x;
int yDist = i->y - m_target.getPosition().y;
float m_distance = sqrtf((xDist * xDist) + (yDist*yDist));
//if the bullets are to close at or over edge of the window, they are dead.
if (i->x > m_window.getSize().x || i->x < 0 || i->y > m_window.getSize().y || i->y < 0) isDead = true;
//or if the bullet is close enough to the target that they hit it.
else if (m_distance < 15) {
isDead = true;
//But we also need to move the target if it is hit.
float nY = rand() % (m_window.getSize().y - 100) + 50;
float nX;
//lets make sure it ends up on hte opposite side of the screen than the tank.
if (m_tankBody.getPosition().x > (m_window.getSize().x / 2)) nX = rand() % (m_window.getSize().x / 3);
else nX = rand() % (m_window.getSize().x / 3) + ((m_window.getSize().x / 3) * 2);
m_target.setPosition(nX, nY);
}
//So if hte bullet is dead, erase it.
if (isDead) {
m_bullets.erase(i++);
}
//or, if hte bullet is alive, move it based on its rotation and the bullet speed.
else
{
i->x += ((m_bulletSpeed / m_UPS) * cos(i->z / 180 * PI));
i->y += ((m_bulletSpeed / m_UPS) * sin(i->z / 180 * PI));
i++;
}
}
//move the gun with the tank and Make the gun point towards the mouse!
m_tankGun.setPosition(m_tankBody.getPosition());
m_tankGun.setRotation(atan2(sf::Mouse::getPosition(m_window).y - m_tankGun.getPosition().y, sf::Mouse::getPosition(m_window).x - m_tankGun.getPosition().x) * 180 / PI);
}
//Draw the update if the target time has gone by since last frame, restart the frame clock.
if (m_frameTime.getElapsedTime().asSeconds() > 1 / m_FPS) {
m_frameTime.restart();
//lets make a nice green field for us to drive the tank on.
m_window.clear(sf::Color(0, 110, 50));
//draw bullets:
for (sf::Vector3f it : m_bullets) {
m_bullet.setPosition(it.x, it.y);
m_window.draw(m_bullet);
}
m_window.draw(m_tankBody);
m_window.draw(m_tankGun);
m_window.draw(m_target);
m_window.display();
}
}
}
This code teaches a lot of the skills i feel is necessary to make a full game prototype.
or even a full game...
but it would be awesome if others could add small game codes like this... comment or ask questions. even if someone would make a tutorial on the individual things in this code...
oh yes, and an image of the game in attachment