4
\$\begingroup\$

I wrote a simple tron clone in C++ and would appreciate some feedback.

The pixelutility.h isn't included on purpose. Feel free to reuse the code where ever you want (It shouldn't be much effort to change from my library to std::vector etc.). My IDE does not support C++11 features so I am not able to use C++11 in general.

globals.h:

#ifndef GUARD_MANGO_GLOBALS_H
#define GUARD_MANGO_GLOBALS_H

#include <Gaming\pixelutility.h>

//these variables are just convenience, I know it would be better practice to place them as a static member of the game class 

static const mango::int32 ColourList[12]  = {  ORANGE,   BLUE,   RED,   YELLOW };
static const std::string  ColourNames[12] = { "ORANGE", "BLUE", "RED", "YELLOW" };

static const char ControlSets[4][4] = { { 'w','a','s','d' },{ 'i','j','k','l' },{ 't','f','g','h' },{ '8','4','5','6' } };

static const graphic::KatCoord startPositions[4] = { graphic::KatCoord(0, 40), graphic::KatCoord(640, 260), graphic::KatCoord(400, 300), graphic::KatCoord(240, 0) };

typedef mango::archive::thread_pool worker_thread;

#define MANGO_NO_KEY   ' '
#define MANGO_EXIT_KEY 'X'

#define MANGO_W 'w'
#define MANGO_A 'a'
#define MANGO_S 's'
#define MANGO_D 'd'

#define MANGO_I 'i'
#define MANGO_J 'j'
#define MANGO_K 'k'
#define MANGO_L 'l'


#endif//GUARD_MANGO_GLOBALS_H

engine.h:

#ifndef GUARD_MANGO_ENGINE_H
#define GUARD_MANGO_ENGINE_H

#include <Gaming/pixelutility.h>

#include "input.h"
#include "globals.h"

namespace engine
{
    class Game
    {
    public: //Typedefs
        typedef mango::shared_ptr<graphic::GraphicController>       graphics_ptr;
        typedef mango::shared_ptr<graphic::Character>               character_ptr;
        typedef mango::semimap<std::string, character_ptr>          character_map;
        typedef mango::pair<std::string, mango::int32>              animation;

        typedef mango::vector<graphic::GraphicController::Simple32> vertex_vector;
        typedef graphic::GraphicController::Simple32                Simple32;
        typedef graphic::KatCoord                                   resolution;
        typedef graphic::KatCoord                                   position;

        typedef mango::synchro::boolean                             flag;
        typedef mango::shared_ptr<engine::input::Controller>        input_ptr;

        typedef mango::semimap<std::string, position>               player_map;
        typedef mango::semimap<std::string, mango::int32>           colour_map;
        typedef mango::semimap<std::string, input_ptr>              idevice_map;

        enum direction
        {
            right,
            left,
            up,
            down,
            dead
        };


    public: //Functions
        Game(graphic::KatCoord resolution = graphic::KatCoord(640, 300));

        void init(mango::int32 playerNumber = 4);
        void run();
        void exit();
        void collision();
        void logic();
        void update();
        void draw();
        void present();
        void loadModel(const graphic::KatCoord& pos, const graphic::KatCoord& resolution, mango::vector<mango::pair<std::string, mango::int32>>& animations, const std::string& name);
        void setCurrentBackground(const std::string& name, mango::int32 frames);
        void movePlayer(mango::int32 playerNumber);
        void playDestructionAnimation();
        void reset();

        idevice_map getInputDevice();
        bool collision(position player);

    private:                                                      
        graphics_ptr              m_GraphicDevice;
        idevice_map               m_InputDevices;

        character_ptr             m_CurrentBackground;
        character_map             m_Characters;

        player_map                m_Players;
        colour_map                m_PlayerColours;

        position                  m_PositionCorrection;

        vertex_vector             m_UnboundVertices; //Vertices which are not part of any mesh--> unbound vertex
        resolution                m_Resolution;

        flag                      m_IsRunning;
    };
}


#endif//GUARD_MANGO_ENGINE_H

engine.cpp

#include "engine.h"

engine::Game::Game(graphic::KatCoord resolution)
    : m_GraphicDevice(mango::make_threadsafe(new graphic::GraphicController())), m_Resolution(resolution),
      m_PositionCorrection(10, 10)
{}

void engine::Game::init(mango::int32 playerNumber)
{
    setCurrentBackground("MISC\\", 25);

    mango::int32 dir = 0;

    for (mango::int32 current = 0, playerNo = 1; playerNumber > current; ++current, ++playerNo)
    {
        m_Players.add("Player" + mango::to_string(playerNo), startPositions[current]);

        m_InputDevices.add("Player" + mango::to_string(playerNo), mango::make_threadsafe(new engine::input::Controller(0, 2, 1, 3, current)));

        m_PlayerColours.add("Player" + mango::to_string(playerNo), ColourList[current]);

        mango::vector<animation> animationsPl;
        animationsPl.push_back(animation("PLAYER\\" + ColourNames[current] + "\\right", 1));
        animationsPl.push_back(animation("PLAYER\\" + ColourNames[current] + "\\left", 1));
        animationsPl.push_back(animation("PLAYER\\" + ColourNames[current] + "\\up", 1));
        animationsPl.push_back(animation("PLAYER\\" + ColourNames[current] + "\\down", 1));
        animationsPl.push_back(animation("PLAYER\\Death\\", 4));

        position playerModelPos = m_Players.at(current);

        playerModelPos.Y -= m_PositionCorrection.Y;

        loadModel(playerModelPos, resolution(20, 20), animationsPl, "Player" + mango::to_string(playerNo));

        m_InputDevices.back()->setCurrentDirection((input::Controller::direction)dir);
        ++dir;

        if (dir >= 4)
        {
            dir = 0;
        }
    }
}

void engine::Game::run()
{
    m_IsRunning = true;

    while (m_IsRunning)
    {
        logic();
        update();
        draw();
        present();
    }
}

engine::Game::idevice_map engine::Game::getInputDevice()
{
    return m_InputDevices;
}

void engine::Game::exit()
{
    m_IsRunning = false;
}

bool engine::Game::collision(position player)
{
    for (mango::int32 current = 0; m_UnboundVertices.size() > current; ++current)
    {
        if (m_UnboundVertices.at(current).m_Position.X == player.X && m_UnboundVertices.at(current).m_Position.Y == player.Y)
        {
            return true;
        }
        if (player.X > m_Resolution.X || player.Y > m_Resolution.Y)
        {
            return true;
        }
        if (player.X < 0 || player.Y < 0)
        {
            return true;
        }
    }

    return false;
}

void engine::Game::collision()
{
    for (mango::int32 current = 0, playerNo = 1; m_Players.size() > current; ++current, ++playerNo)
    {
        if (collision(m_Players.at("Player" + mango::to_string(playerNo))))
        {
            m_Characters.at("Player" + mango::to_string(playerNo))->SetActiveAnimation(dead);
        }
    }
}

void engine::Game::logic()
{
    static clock_t updateTime = 0;

    if (clock() - updateTime > 5)
    {
        updateTime = clock();

        collision();

        for (mango::int32 current = 0, playerNo = 1; m_Players.size() > current; ++current, ++playerNo)
        {
            m_UnboundVertices.push_back(Simple32(m_Players.at("Player" + mango::to_string(playerNo)), m_PlayerColours.at("Player" + mango::to_string(playerNo))));
            movePlayer(playerNo);
        }
    }
}

void engine::Game::update()
{
    static clock_t updateTime = 0;
    static clock_t updateTimeBG = 0;

    if (clock() - updateTime > 100)
    {
        updateTime = clock();

        lambda((graphic::Character* ptr) { ptr->Update(); return 0; }, 1); //exp1
        mango::for_each(&m_Characters.front(), &m_Characters.back(), exp1);
    }

    if (clock() - updateTimeBG > 200)
    {
        updateTimeBG = clock();

        if (0 != m_CurrentBackground)
        {
            m_CurrentBackground->Update();
        }
    }
}

void engine::Game::draw()
{
    m_GraphicDevice->ClearBuffer();

    if (0 != m_CurrentBackground)
    {
        m_GraphicDevice->Draw(m_CurrentBackground.get());
    }

    m_GraphicDevice->SetVertices(m_UnboundVertices);
    m_GraphicDevice->Draw(m_Characters);
}

void engine::Game::present()
{
    m_GraphicDevice->Present();
}

void engine::Game::loadModel(const graphic::KatCoord& pos, const graphic::KatCoord& resolution, mango::vector<mango::pair<std::string, mango::int32>>& animations, const std::string& name)
{
    character_ptr tmp(mango::make_threadsafe(new graphic::Character(pos, resolution.X, resolution.Y)));

    for (mango::int32 current = 0; animations.size() > current; ++current)
    {
        tmp->LoadAnimation(current, animations.at(current), animations.at(current));
    }

    m_Characters.add(name, tmp);
}

void engine::Game::setCurrentBackground(const std::string& name, mango::int32 frames)
{
    m_CurrentBackground = mango::make_threadsafe(new graphic::Character(graphic::KatCoord(0, 0), m_Resolution.X, m_Resolution.Y));
    m_CurrentBackground->LoadAnimation(0, name, frames);
}

void engine::Game::movePlayer(mango::int32 playerNumber)
{
    std::string playerKey = "Player" + mango::to_string(playerNumber);
    input::Controller::direction plDir = m_InputDevices.at(playerKey)->getCurrentDirection();

    if (dead != m_Characters.at(playerKey)->GetActiveAnimation())
    {
        if (input::Controller::right == plDir)
        {
            ++m_Players.at(playerKey).X;
        }
        if (input::Controller::left == plDir)
        {
            --m_Players.at(playerKey).X;
        }
        if (input::Controller::down == plDir)
        {
            ++m_Players.at(playerKey).Y;
        }
        if (input::Controller::up == plDir)
        {
            --m_Players.at(playerKey).Y;
        }

        m_Characters.at(playerKey)->SetActiveAnimation(plDir);

        position newModelDir = m_Players.at(playerKey);
        newModelDir.Y -= m_PositionCorrection.Y;
        newModelDir.X -= m_PositionCorrection.X;

        m_Characters.at(playerKey)->SetPosition(newModelDir);
    }

    mango::int32 deathCount = 0;

    for (mango::int32 current = 0; m_Characters.size() > current; ++current)
    {
        if (dead == m_Characters.at(current)->GetActiveAnimation())
        {
            ++deathCount;
        }
    }

    if ((m_Players.size() - 1) <= deathCount)
    {
        playDestructionAnimation();
        reset();
        return;
    }
}

void engine::Game::playDestructionAnimation()
{
    clock_t time = clock();
    while (clock() - time <= 2000)
    {
        update();
        draw();
        present();
    }
}

void engine::Game::reset()
{
    mango::int32 dir = 0;

    m_UnboundVertices.clear();

    for (mango::int32 current = 0, playerNo = 1; m_Characters.size() > current; ++current, ++playerNo)
    {
        m_Players.at("Player" + mango::to_string(playerNo)) = startPositions[current];
        position playerModelPos = m_Players.at(current);

        playerModelPos.Y -= m_PositionCorrection.Y;

        m_Characters.at("Player" + mango::to_string(playerNo))->SetPosition(playerModelPos);
        m_Characters.at("Player" + mango::to_string(playerNo))->SetActiveAnimation(dir);

        m_InputDevices.at("Player" + mango::to_string(playerNo))->setCurrentDirection((input::Controller::direction)dir);
        ++dir;

        if (dir >= 4) //4 directions--> up, down, left, right--> sets the player directions back to the initial direction
        {
            dir = 0;
        }
    }

    mango::csleep(500);
}

input.h:

#ifndef GUARD_MANGO_INPUT_H
#define GUARD_MANGO_INPUT_H

#include <mangoarchive.h>
#include "globals.h"

namespace engine
{
    namespace input
    {
        class Controller
        {
        public:
            typedef mango::Byte  Key;
            typedef mango::mutex Lock;

            enum direction
            {
                right,
                left,
                up,
                down
            };

        public:
            Controller(Key keyUp, Key keyDown, Key keyLeft, Key keyRight);
            Controller(mango::int32 keyUp, mango::int32 keyDown, mango::int32 keyLeft, mango::int32 keyRight, mango::int32 set);

            direction getCurrentDirection();

            void keyPressed(const Key key);
            void setCurrentDirection(direction newDirection);

            static Key getCurrentInput();

        private:

            direction m_CurrentDirection;

            Key       m_Up;
            Key       m_Down;
            Key       m_Left;
            Key       m_Right;

            Lock      m_InputLock;
        };
    }
}


#endif//GUARD_MANGO_INPUT_H

input.cpp:

#include "input.h"

engine::input::Controller::Controller(Key keyUp, Key keyDown, Key keyLeft, Key keyRight)
    : m_CurrentDirection(right), m_Up(keyUp), m_Down(keyDown), m_Left(keyLeft), m_Right(keyRight)
{}

engine::input::Controller::Controller(mango::int32 keyUp, mango::int32 keyDown, mango::int32 keyLeft, mango::int32 keyRight, mango::int32 set)
    : m_CurrentDirection(right), m_Up(ControlSets[set][keyUp]), m_Down(ControlSets[set][keyDown]), m_Left(ControlSets[set][keyLeft]), m_Right(ControlSets[set][keyRight])
{}

engine::input::Controller::direction engine::input::Controller::getCurrentDirection()
{
    mango::lock_guard<Lock> guard(m_InputLock);
    direction dir = m_CurrentDirection;

    return dir;
}

void engine::input::Controller::keyPressed(const Key key)
{
    mango::lock_guard<Lock> guard(m_InputLock);

    if (key == m_Up && m_CurrentDirection != up && m_CurrentDirection != down)
    {
        m_CurrentDirection = up;
        return;
    }
    if (key == m_Down && m_CurrentDirection != down && m_CurrentDirection != up)
    {
        m_CurrentDirection = down;
        return;
    }
    if (key == m_Left && m_CurrentDirection != left && m_CurrentDirection != right)
    {
        m_CurrentDirection = left;
        return;
    }
    if (key == m_Right && m_CurrentDirection != right && m_CurrentDirection != left)
    {
        m_CurrentDirection = right;
        return;
    }
}

engine::input::Controller::Key engine::input::Controller::getCurrentInput()
{
    return mango::archive::mgetch();
}

void engine::input::Controller::setCurrentDirection(direction newDirection)
{
    mango::lock_guard<Lock> guard(m_InputLock);

    m_CurrentDirection = newDirection;
}

main.cpp:

#include <trymacro.h>

#include "engine.h"
#include "input.h"

mango::int32 main()
{
    mango::shared_ptr<engine::Game>           game   = mango::make_threadsafe(new engine::Game());
    mango::shared_ptr<mango::archive::worker> worker = mango::make_threadsafe(new mango::archive::worker());

    mango::int32 playerNumber = 2;

    mango::archive::print_t("enter player number (max 4): ");
    std::cin >> playerNumber;
    system("CLS");

    game->init(playerNumber);

    engine::Game::idevice_map ics = game->getInputDevice();

    M_TRY
    {
        lambda((mango::shared_ptr<engine::Game> game) { game->run(); return 0; }, 1); //exp1

        worker->run(exp1, game);

        engine::input::Controller::Key input = MANGO_NO_KEY;

        while (MANGO_EXIT_KEY != input)
        {
            input = engine::input::Controller::getCurrentInput();

            lambda((engine::Game::input_ptr controller, engine::input::Controller::Key key) 
            {
                controller->keyPressed(key); 
                return 0; 
            }, 2);

            mango::for_each(&ics.front(), &ics.back(), exp2, input); //exp2
        }

        game->exit();
    }
    M_CATCH("main.txt")
}

//M_TRY {} M_CATCH(main.txt) --> try catch block to catch 
//mango::exception:: and critical errors and logging them to main.txt.

And a screenshot from the finished game: enter image description here

and for those wo looked over my previous game samples:

Tetris as general game example

An example for a space arcade 2d shooter in C++

this is how I started

\$\endgroup\$
  • \$\begingroup\$ I have too much work to completely review the code now, but one thing I notice the aligning of variables and such. I think you shouldn't do that. It takes a lot of work to maintain, especially when you change something. And it does not really add in readability, especially now, with these big type names: it have to scroll left to read everything. \$\endgroup\$ – JHBonarius Mar 29 '17 at 18:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.