C++/SFML, object-oriented curriculum design, tank war

Posted by Canman2005 on Fri, 25 Feb 2022 15:45:54 +0100

1, Design task and requirement analysis

"Tank battle" is a plane shooting game developed by Namco game company in Japan. It was sold in 1985. The theme of the game is tank battle and defending the base, which belongs to the strategic online class. With the theme of World War II tanks, it not only retains the operability of shooting games, but also improves the high threshold characteristics that shooting games are too complex and difficult to play, integrating leisure and competition.
Use the third-party graphics library SFML to restore FC classic tank war, and use 13 * 13 blocks to form the game map. The terrain includes air, steel plate and brick wall. In the original tank war, tank walking, collision between tank and wall, tank firing bullets, enemy generation, enemy random walking, interaction between bullets and entities, game keyboard operation and game end interface are realized
Game controls, WASD controls the player's tank to move up, down, left and right, and J fires bullets

2, Overall design scheme

Using game class as the intermediary class, there are pointers to all game objects in memory, and each game object has a pointer to game class, which is used for the communication between different game modules. Game class is responsible for the operation of the whole game (the Run() member function is used to process the events to be processed in each frame, and PushEvent gives each keyboard event to be processed to the Control object for processing)

The Control class is used to process keyboard input information to make the game object respond to each obtained event, such as WASD changing the player's turn and movement

The Drawer class is used to draw game objects in all maps and control the drawing order

GameObject class is the parent class of all game objects, including the connotation texture, the attributes of texture objects, and the pure virtual function Draw() interface

As the parent class of all Block objects, Block class contains enumeration type Block type. The WallBlock constructor assigns value to BlockType. The same is true for other blocks
Map class is the aggregation of Block class. The constructor initializes the position of each box for it, uses string array to quickly edit the map box layout, and setLostMap() is the interface to set the map display after the game

The Player class contains speed, isDead, ismoving and direction attributes,
Move() method: move at speed according to the direction direction, and judge whether there is a collision with all game objects every time. If the collision goes back and no longer moves
Turn(Direction dir) method: set the motion direction and rotate the map

Enemy class contains the static attribute StartPos, which sets the position of each generation. The attribute sf::Clock timer is used to control the time when each enemy tank fires bullets

The Bullet abstract class contains speed and isDead attributes. The Move method will judge whether there is a collision with the game object every time it moves. If there is a collision, set the Bullet and the pair of "death" and clear them in game - > run(). PlayerBullet implements the Move function, and EnemyBullet implements the Move function

The Home class is the base object in the game. If the isDead property is True, the game ends

UML class diagram

source code

//main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include "Game.h"
using namespace std;

int main() {
    sf::RenderWindow window(sf::VideoMode(832, 832), "BattleCity");
    window.setFramerateLimit(60);
    window.setKeyRepeatEnabled(false);
    Game game(&window);
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            game.PushEvent(event);
        }
        game.Run();
        window.clear();
        game.drawer->DrawAll();
        window.display();
    }
    return 0;
}

//Game.h
#pragma once
#include <iostream>
#include <list>
#include <vector>
#include <SFML/Graphics.hpp>
#include "Player.h"
#include "Block.h"
#include "Drawer.h"
#include "Enemy.h"
#include "Control.h"
#include "Map.h"
#include "Bullet.h"
#include "Home.h"
class Player;
class Bullet;
class Enemy;
class Block;
class Drawer;
class Control;
class Map;
class Home;
enum class Direction {
    Up,
    Down,
    Left,
    Right
};
class Game {
public:
    Game(sf::RenderWindow* window);
    sf::Clock* clock;
    Player* player;
    std::list<Bullet*> bulletList;
    sf::RenderWindow* window;
    std::list<Enemy*> enemyList;
    Drawer* drawer;
    Control* control;
    Map* map;
    Home* home;
    bool isFailed;
    void PushEvent(sf::Event event);
    void Run();
    void Over();
};


//Game.cpp
#include "Game.h"
Game::Game(sf::RenderWindow* window) {
    this->control = new Control(this);
    this->map = new Map(this);
    this->drawer = new Drawer(this);
    this->player = new Player(this);
    this->window = window;
    this->clock = new sf::Clock;
    this->home = new Home(this);
    this->isFailed = false;
}
void Game::PushEvent(sf::Event event) {
    control->Input(event);
}
void Game::Run() {
    if(isFailed == true){
        this->map->setLostMap();
    }
    else {
        player->Move();
        for (auto& item : bulletList) {
            item->Move();
        }
        for (auto& item : enemyList) {
            item->Move();
        }
        for (auto& item : enemyList) {
            item->Shoot();
        }
        for (std::list<Bullet*>::iterator iter = bulletList.begin(); iter != bulletList.end();) {
            if ((*iter)->isDead) {
                delete (*iter);
                bulletList.erase(iter);
                iter = bulletList.begin();
            }
            else
                iter++;
        }
        for (std::list<Enemy*>::iterator iter = enemyList.begin(); iter != enemyList.end();) {
            if ((*iter)->isDead) {
                delete (*iter);
                enemyList.erase(iter);
                iter = enemyList.begin();
            }
            else
                iter++;
        }
        if (this->clock->getElapsedTime().asSeconds() >= 3.0f && this->enemyList.size() < 4) {
            Enemy* enemy = new Enemy(this);
            enemyList.push_back(enemy);
            this->clock->restart();
        }
        if (this->player->isDead || this->home->isDead) {
            this->isFailed = true;
        }
    }
    
}
void Game::Over() {
    this->map->setLostMap();
    this->player->sprite.setPosition(-10.0f, -10.0f);
    for (std::list<Enemy*>::iterator iter = enemyList.begin(); iter != enemyList.end(); iter++) {
        delete (*iter);
    }
    enemyList.clear();
    delete this->player;
}

//Block.h
#pragma once
#include <SFML/Graphics.hpp>
#include "Game.h"
#include "GameObject.h"
class Game;
enum class BlockType {
    Empty,
    Wall,
    Iron
};
class Block : public GameObject {
protected:
    Block(Game* game);
    Game* game;
public:
    BlockType type;
    void setPosition(float x, float y);
    void Draw();
};
class WallBlock: public Block {
public:
    WallBlock(Game* game);
};
class EmptyBlock : public Block {
public:
    EmptyBlock(Game* game);
};
class IronBlock : public Block {
public:
    IronBlock(Game* game);
};

//Block.cpp
#include "Block.h"
Block::Block(Game* game) {
    this->game = game;
}
void Block::setPosition(float x, float y) {
    this->sprite.setPosition(x, y);
}
void Block::Draw() {
    game->window->draw(this->sprite);
}
WallBlock::WallBlock(Game* game): Block(game) {
    
    this->texture.loadFromFile("Src\\Wall.png");
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->type = BlockType::Wall;
}
EmptyBlock::EmptyBlock(Game* game):Block(game) {
    this->texture.loadFromFile("Src\\Empty.png");
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->type = BlockType::Empty;
}
IronBlock::IronBlock(Game* game):Block(game) {
    this->texture.loadFromFile("Src\\Iron.png");
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->type = BlockType::Iron;
}
//Bullet.h
#pragma once
#include "Game.h"
class Game;
enum class Direction;
class Bullet : public GameObject {
protected:
    Game* game;
public:
    Direction dir;
    float speed;
    bool isDead;
    Bullet(Game* game, Direction dir);
    virtual void Move() = 0;
    void Draw();
};

class PlayerBullet : public Bullet {
public:
    PlayerBullet(Game* game, Direction dir);
    void Move();
};
class EnemyBullet : public Bullet {
public:
    EnemyBullet(Game* game, Direction dir);
    void Move();
};
//Bullet.cpp
#include "Bullet.h"

Bullet::Bullet(Game* game, Direction dir) {
    this->game = game;
    this->texture.loadFromFile("Src\\Bullet.png");
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->speed = 16.0f;
    this->sprite.setOrigin(1.5f, 2.0f);
    this->dir = dir;
    switch (dir) {
    case Direction::Up:
        this->sprite.setRotation(0);
        break;
    case Direction::Down:
        this->sprite.setRotation(180);
        break;
    case Direction::Left:
        this->sprite.setRotation(270);
        break;
    case Direction::Right:
        this->sprite.setRotation(90);
        break;
    }
    this->isDead = false;
}
PlayerBullet::PlayerBullet(Game* game, Direction dir) :Bullet(game, dir) {}
EnemyBullet::EnemyBullet(Game* game, Direction dir) : Bullet(game, dir) {}

void Bullet::Draw() {
    game->window->draw(this->sprite);
}

void PlayerBullet::Move() {
    switch (dir) {
    case Direction::Up:
        this->sprite.move(0, -this->speed); break;
    case Direction::Down:
        this->sprite.move(0, this->speed); break;
    case Direction::Left:
        this->sprite.move(-this->speed, 0); break;
    case Direction::Right:
        this->sprite.move(this->speed, 0); break;
    }
    for (int i = 0; i < game->map->Block2D.size(); i++) {
        for (int j = 0; j < game->map->Block2D[0].size(); j++) {
            if (game->map->Block2D[i][j]->type == BlockType::Wall && this->sprite.getGlobalBounds().intersects(game->map->Block2D[i][j]->sprite.getGlobalBounds())) {
                this->isDead = true;
                delete game->map->Block2D[i][j];
                game->map->Block2D[i][j] = new EmptyBlock(game);
            }
            if (game->map->Block2D[i][j]->type == BlockType::Iron && this->sprite.getGlobalBounds().intersects(game->map->Block2D[i][j]->sprite.getGlobalBounds())) {
                this->isDead = true;
            }
        }
    }
    if (this->sprite.getPosition().x > 830 || this->sprite.getPosition().x < 0 || this->sprite.getPosition().y > 832 || this->sprite.getPosition().y < 0)
        this->isDead = true;
    for (auto itm : game->enemyList) {
        if (this->sprite.getGlobalBounds().intersects(itm->sprite.getGlobalBounds())) {
            this->isDead = true;
            itm->isDead = true;
        }
    }
    if (this->sprite.getGlobalBounds().intersects(game->home->sprite.getGlobalBounds()))
        game->home->isDead = true;
}

void EnemyBullet::Move() {
    switch (dir) {
    case Direction::Up:
        this->sprite.move(0, -this->speed); break;
    case Direction::Down:
        this->sprite.move(0, this->speed); break;
    case Direction::Left:
        this->sprite.move(-this->speed, 0); break;
    case Direction::Right:
        this->sprite.move(this->speed, 0); break;
    }
    for (int i = 0; i < game->map->Block2D.size(); i++) {
        for (int j = 0; j < game->map->Block2D[0].size(); j++) {
            if (game->map->Block2D[i][j]->type == BlockType::Wall && this->sprite.getGlobalBounds().intersects(game->map->Block2D[i][j]->sprite.getGlobalBounds())) {
                this->isDead = true;
                delete game->map->Block2D[i][j];
                game->map->Block2D[i][j] = new EmptyBlock(game);
            }
            if (game->map->Block2D[i][j]->type == BlockType::Iron && this->sprite.getGlobalBounds().intersects(game->map->Block2D[i][j]->sprite.getGlobalBounds())) {
                this->isDead = true;
            }
        }
    }
    if (this->sprite.getPosition().x > 830 || this->sprite.getPosition().x < 0 || this->sprite.getPosition().y > 832 || this->sprite.getPosition().y < 0)
        this->isDead = true;
    if (this->sprite.getGlobalBounds().intersects(game->player->sprite.getGlobalBounds())) {
        this->isDead = true;
        game->player->isDead = true;
    }
    if (this->sprite.getGlobalBounds().intersects(game->home->sprite.getGlobalBounds()))
        game->home->isDead = true;
}

//Control.h
#pragma once
#include "Game.h"
class Game;
class Control {
public:
    Game* game;
    Control(Game* game);
    void Input(sf::Event event);
};
//Control.cpp
#include "Control.h"
#include <iostream>
Control::Control(Game* game) {
    this->game = game;
}
void Control::Input(sf::Event event) {
    if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed &&event.key.code == sf::Keyboard::Escape)) {
        game->window->close();
    }
    if (game->isFailed == false) {
        if (event.type == sf::Event::KeyPressed) {
            if (event.key.code == sf::Keyboard::A) {
                if (game->player->dir != Direction::Left)
                    game->player->Turn(Direction::Left);
                game->player->isMoving = true;
            }
            if (event.key.code == sf::Keyboard::W) {
                if (game->player->dir != Direction::Up)
                    game->player->Turn(Direction::Up);
                game->player->isMoving = true;
            }
            if (event.key.code == sf::Keyboard::S) {
                if (game->player->dir != Direction::Down)
                    game->player->Turn(Direction::Down);
                game->player->isMoving = true;
            }
            if (event.key.code == sf::Keyboard::D) {
                if (game->player->dir != Direction::Right)
                    game->player->Turn(Direction::Right);
                game->player->isMoving = true;
            }
            if (event.key.code == sf::Keyboard::J) {
                float tx, ty;
                Bullet* bullet = NULL;
                tx = game->player->sprite.getPosition().x;
                ty = game->player->sprite.getPosition().y;
                switch (game->player->dir) {
                case Direction::Up:
                    bullet = new PlayerBullet(game, Direction::Up);
                    ty -= 16; break;
                case Direction::Down:
                    bullet = new PlayerBullet(game, Direction::Down);
                    ty += 16; break;
                case Direction::Left:
                    bullet = new PlayerBullet(game, Direction::Left);
                    tx -= 16; break;
                case Direction::Right:
                    bullet = new PlayerBullet(game, Direction::Right);
                    tx += 16; break;
                }
                bullet->sprite.setPosition(tx, ty);
                game->bulletList.push_back(bullet);
            }
        }
        if (event.type == sf::Event::KeyReleased) {
            game->player->isMoving = false;
        }
    }
}
//Drawer.h
#pragma once
#include "Game.h"
class Game;
class Drawer {
    Game* game;
public:
    Drawer(Game* game);
    void DrawAll();
};
//Drawer.cpp
#include "Drawer.h"
Drawer::Drawer(Game* game) {
    this->game = game;
}
void Drawer::DrawAll() {
    this->game->map->Draw();
    if (game->isFailed == false) {
        this->game->player->Draw();
        for (std::list<Bullet*>::iterator iter = game->bulletList.begin(); iter != game->bulletList.end(); iter++) {
            (*iter)->Draw();
        }
        for (std::list<Enemy*>::iterator iter = game->enemyList.begin(); iter != game->enemyList.end(); iter++) {
            (*iter)->Draw();
        }
        this->game->home->Draw();
    }
}
//Enemy.h
#pragma once
#include "Game.h"
#include "GameObject.h"
class Game;
enum class Direction;
enum class StartPos {
    Left,
    Middle,
    Right
};
class Enemy : public GameObject {
    Game* game;
public:
    sf::Clock clock;
    static int remain;
    static StartPos spos;
    Direction dir;
    float speed;
    bool isDead;
    bool isFrozen;
    Enemy(Game* game);
    void Draw();
    void Move();
    void Shoot();
};
//Enemy.cpp
#include "Enemy.h"
Enemy::Enemy(Game* game) {
    this->game = game;
    this->texture.loadFromFile("Src\\TankEnemy.png");
    this->isDead = false;
    this->speed = 4.0f;
    this->dir = Direction::Down;
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->sprite.setOrigin(6.5f, 7.5f);
    this->isFrozen = false;
    switch (this->spos)
    {
    case StartPos::Left:
        this->sprite.setPosition(32.0f, 32.0f);
        this->spos = StartPos::Middle;
        break;
    case StartPos::Middle:
        this->sprite.setPosition(416.0f, 32.0f);
        this->spos = StartPos::Right;
        break;
    case StartPos::Right:
        this->sprite.setPosition(800.0f, 32.0f);
        this->spos = StartPos::Left;
        break;
    }
}
void Enemy::Draw() {
    game->window->draw(this->sprite);
}
void Enemy::Move() {
    if (isFrozen == false) {
        if (clock.getElapsedTime().asSeconds() > 1) {
            dir = (Direction)(rand() % 4);
            clock.restart();
        }
        switch (dir) {
        case Direction::Up:
            this->sprite.setRotation(0); this->dir = Direction::Up; break;
        case Direction::Down:
            this->sprite.setRotation(180); this->dir = Direction::Down; break;
        case Direction::Left:
            this->sprite.setRotation(270); this->dir = Direction::Left; break;
        case Direction::Right:
            this->sprite.setRotation(90); this->dir = Direction::Right; break;
        }
        float dx, dy;
        switch (this->dir) {
        case Direction::Up:
            dx = 0; dy = -speed; break;
        case Direction::Down:
            dx = 0; dy = speed; break;
        case Direction::Left:
            dx = -speed; dy = 0; break;
        case Direction::Right:
            dx = speed; dy = 0; break;
        }
        this->sprite.move(dx, dy);
        for (int i = 0; i < game->map->Block2D.size(); i++) {
            for (int j = 0; j < game->map->Block2D[0].size(); j++) {
                if (game->map->Block2D[i][j]->type != BlockType::Empty && this->sprite.getGlobalBounds().intersects(game->map->Block2D[i][j]->sprite.getGlobalBounds())) {
                    this->sprite.move(-dx, -dy);
                }
            }
        }
        for (auto itm : game->enemyList) {
            if(this->sprite.getGlobalBounds().intersects(itm->sprite.getGlobalBounds()) && itm != this)
                this->sprite.move(-dx, -dy);
        }
        if (this->sprite.getPosition().x - 28.0f < 0 || this->sprite.getPosition().y - 28.0f < 0 || this->sprite.getPosition().x + 28.0f > 832 || this->sprite.getPosition().y + 28.0f > 832)
            this->sprite.move(-dx, -dy);
        if(this->sprite.getGlobalBounds().intersects(game->player->sprite.getGlobalBounds()))
            this->sprite.move(-dx, -dy);
    }
    else {
        if (clock.getElapsedTime().asSeconds() > 8) {
            this->isFrozen = false;
            this->clock.restart();
        }
    }
}
void Enemy::Shoot() {
    if ((int)clock.getElapsedTime().asMicroseconds() % 500 < 5 && this->isFrozen == false) {
        float tx, ty;
        Bullet* bullet = NULL;
        tx = this->sprite.getPosition().x;
        ty = this->sprite.getPosition().y;
        switch (this->dir) {
        case Direction::Up:
            bullet = new EnemyBullet(game, Direction::Up);
            ty -= 16; break;
        case Direction::Down:
            bullet = new EnemyBullet(game, Direction::Down);
            ty += 16; break;
        case Direction::Left:
            bullet = new EnemyBullet(game, Direction::Left);
            tx -= 16; break;
        case Direction::Right:
            bullet = new EnemyBullet(game, Direction::Right);
            tx += 16; break;
        }
        bullet->sprite.setPosition(tx, ty);
        game->bulletList.push_back(bullet);
        clock.restart();
    }
}
StartPos Enemy::spos = StartPos::Left;
//GameObject.h
#pragma once
#include <SFML/Graphics.hpp>
class GameObject {
public:
    sf::Texture texture;
    sf::Sprite sprite;
    virtual void Draw() = 0;
};
//Home.h
#pragma once
#include "Game.h"
#include "GameObject.h"
class Game;
class Home : public GameObject {
    Game* game;
public:
    bool isDead;
    Home(Game* game);
    void Draw();
};
//Home.cpp
#include "Home.h"
Home::Home(Game* game) {
    this->game = game;
    this->texture.loadFromFile("Src\\Home.png");
    this->isDead = false;
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->sprite.setPosition(384.0, 768.0f);
}
void Home::Draw() {
    game->window->draw(this->sprite);
}
//Map.h
#pragma once
#include <vector>
#include "Block.h"
#include "Game.h"
class Game;
class Block;
class Map {
    Game* game;
public:
    std::vector<std::vector<Block*>> Block2D;
    Map(Game* game);
    void Draw();
    void setLostMap();
};
//Map.cpp
#include "Map.h"
Map::Map(Game* game) {
    this->game = game;
    std::vector<std::string> tMap = {
        "..........................",
        "..........................",
        "..##..##..##..##..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..##%%##..##..##..",
        "..##..##..##%%##..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..........##..##..",
        "..##..##..........##..##..",
        "..........##..##..........",
        "..........##..##..........",
        "##..####..........####..##",
        "%%..####..........####..%%",
        "..........##..##..........",
        "..........######..........",
        "..##..##..######..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..##..##..##..##..",
        "..##..##..........##..##..",
        "..##..##..........##..##..",
        "..##..##...####...##..##..",
        "...........#..#...........",
        "...........#..#..........."
    };
    this->Block2D.resize(26);
    for (auto& itm : this->Block2D) {
        itm.resize(26);
    }
    for (int i = 0; i < 26; i++) {
        for (int j = 0; j < 26; j++) {
            if (tMap[i][j] == '.') {
                this->Block2D[j][i] = new EmptyBlock(game);
                this->Block2D[j][i]->setPosition(j * 32, i * 32);
            }
            else if (tMap[i][j] == '#') {
                this->Block2D[j][i] = new WallBlock(game);
                this->Block2D[j][i]->setPosition(j * 32, i * 32);
            }
            else if (tMap[i][j] == '%') {
                this->Block2D[j][i] = new IronBlock(game);
                this->Block2D[j][i]->setPosition(j * 32, i * 32);
            }
        }
    }
}
void Map::Draw() {
    for (int i = 0; i < this->Block2D.size(); i++) {
        for (int j = 0; j < this->Block2D.size(); j++) {
            Block2D[i][j]->Draw();
        }
    }
}
void Map::setLostMap() {
    std::vector<std::string> rMap = {
        "..........................",
        "..........................",
        "..###...###...##.##..####.",
        ".#.....#...#.#..#..#.#....",
        ".#.....#####.#..#..#.####.",
        ".#..##.#...#.#..#..#.#....",
        ".#...#.#...#.#..#..#.#....",
        "..###..#...#.#..#..#.####.",
        "..........................",
        "..........................",
        "..###..#...#.####.###..#..",
        ".#...#.#...#.#....#..#.#..",
        ".#...#.#...#.####.#..#.#..",
        ".#...#.#...#.#....###..#..",
        ".#...#..#.#..#....#..#....",
        "..###....#...####.#..#.#..",
        "..........................",
        "..%..........%%%..........",
        "..%.........%...%.......%.",
        "..%%..%.%.%.%...%.%.%..%..",
        "..%.%.%.%...%...%.%.%.%%%.",
        "..%%...%%.%..%%%...%%..%..",
        "........%.......%...%..%..",
        "........%...........%..%..",
        ".......%...........%..%...",
        ".........................."
    };
    for (int i = 0; i < 26; i++) {
        for (int j = 0; j < 26; j++) {
            if (rMap[i][j] == '.') {
                delete this->Block2D[j][i];
                this->Block2D[j][i] = new EmptyBlock(game);
                this->Block2D[j][i]->setPosition(j * 32, i * 32);
            }
            else if (rMap[i][j] == '#') {
                delete this->Block2D[j][i];
                this->Block2D[j][i] = new WallBlock(game);
                this->Block2D[j][i]->setPosition(j * 32, i * 32);
            }
            else if (rMap[i][j] == '%') {
                delete this->Block2D[j][i];
                this->Block2D[j][i] = new IronBlock(game);
                this->Block2D[j][i]->setPosition(j * 32, i * 32);
            }
        }
    }
}
//Player.h
#pragma once
#include <SFML/Graphics.hpp>
#include "Game.h"
#include "GameObject.h"
class Game;
enum class Direction;
class Player : public GameObject {
    Game* game;
public:
    Direction dir;
    bool isDead;
    float speed;
    bool isMoving;
    Player(Game* game);
    void Move();
    void Turn(Direction dir);
    void Draw();
};
//Player.cpp
#include "Player.h"
Player::Player(Game* game) {
    this->speed = 8.0f;
    this->isDead = false;
    this->texture.loadFromFile("Src\\TankPlayer.png");
    this->sprite.setTexture(texture);
    this->sprite.setScale(4.0f, 4.0f);
    this->sprite.setOrigin(6.5f, 6.5f);
    this->sprite.setPosition(288.0f, 768.0f);
    this->dir = Direction::Up;
    this->isMoving = false;
    this->game = game;
}
void Player::Move() {
    if (!this->isMoving)
        return;
    float dx, dy;
    switch (this->dir) {
    case Direction::Up:
        dx = 0; dy = -speed; break;
    case Direction::Down:
        dx = 0; dy = speed; break;
    case Direction::Left:
        dx = -speed; dy = 0; break;
    case Direction::Right:
        dx = speed; dy = 0; break;
    }
    this->sprite.move(dx, dy);
    for (int i = 0; i < game->map->Block2D.size(); i++) {
        for (int j = 0; j < game->map->Block2D[0].size(); j++) {
            if (game->map->Block2D[i][j]->type != BlockType::Empty&&this->sprite.getGlobalBounds().intersects(game->map->Block2D[i][j]->sprite.getGlobalBounds())) {
                this->sprite.move(-dx, -dy);
            }
        }
    }
    if (this->sprite.getPosition().x - 28.0f < 0 || this->sprite.getPosition().y - 28.0f < 0 || this->sprite.getPosition().x + 28.0f > 832 || this->sprite.getPosition().y + 28.0f > 832)
        this->sprite.move(-dx, -dy);
    for (auto itm : game->enemyList) {
        if (this->sprite.getGlobalBounds().intersects(itm->sprite.getGlobalBounds()))
            this->sprite.move(-dx, -dy);
    }
}
void Player::Turn(Direction dir) {
    switch (dir) {
    case Direction::Up:
        this->sprite.setRotation(0); this->dir = Direction::Up; break;
    case Direction::Down:
        this->sprite.setRotation(180); this->dir = Direction::Down; break;
    case Direction::Left:
        this->sprite.setRotation(270); this->dir = Direction::Left; break;
    case Direction::Right:
        this->sprite.setRotation(90); this->dir = Direction::Right; break;
    }
}
void Player::Draw() {
    game->window->draw(this->sprite);
}

Screenshot of program running effect

Topics: C++ UML