Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: A SFML Prototypes collection  (Read 4263 times)

0 Members and 1 Guest are viewing this topic.

GameBear

  • Jr. Member
  • **
  • Posts: 73
    • View Profile
    • Email
A SFML Prototypes collection
« on: May 09, 2017, 12:54:42 am »
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

« Last Edit: May 09, 2017, 09:40:13 am by GameBear »
string message = "some random dude";
cout << "I'm just " << message;

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: A SFML Prototypes collection
« Reply #1 on: May 10, 2017, 02:16:53 pm »
I used to have a similar idea about making a bunch of prototypes/proofs of concept/minimal viable products and then extracting common parts out of them but then I lost motivation, got busy with other stuff and nothing ever came out of it.

I have a simple game source though, it doesn't even use image files, but it's not one file: https://en.sfml-dev.org/forums/index.php?topic=20228.0
Back to C++ gamedev with SFML in May 2023

GameBear

  • Jr. Member
  • **
  • Posts: 73
    • View Profile
    • Email
Re: A SFML Prototypes collection
« Reply #2 on: May 10, 2017, 05:06:40 pm »
I know that feeling...
The whole idea of putting something together fast/without optimizing on the go is intimidating.
I blame tutorials... Well, some.. A lot of tutorials teaches "to code perfectly/clean" so it feels wrong not to...

But awesome you put up a link :) I did read that game entry before, but have not tried it out... I will do :)
string message = "some random dude";
cout << "I'm just " << message;

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: A SFML Prototypes collection
« Reply #3 on: May 10, 2017, 09:47:08 pm »
I find it easy actually, I think I work well under a deadline IF the goal is clear and limited. There is an exe of my game in that thread and it could probably be made to fit into a single main.cpp file if you really wanted.
Back to C++ gamedev with SFML in May 2023