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

Author Topic: Game Development Design articles  (Read 36199 times)

0 Members and 2 Guests are viewing this topic.

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Game Development Design articles
« Reply #30 on: February 12, 2013, 07:57:44 am »
If you separate your data model, business logic and rendering, you're always doing "MVC", so yes.

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Game Development Design articles
« Reply #31 on: February 23, 2013, 11:05:24 pm »
Unrelated to GDD, I finally trusted your vim article and got gVim and it's not that bad, GUI makes it a bit better because prompt flashes nastily if you keep pressing j while at last line and you can always look up the command in the menus, I might use it for taking notes, typing code for languages that don't benefit from vs and maybe integrate it into vs as tips wiki says, and if ever on Linux I'll probably not use gedit, emacs or whatever at all. It's actually easy to use, I mean really.. optional number + operation + optional number + move is just genius and intuitive way to edit things. It'll probably take me some time to learn it all but getting used to hjkl instead of arrows was quicker than I'd thought, the tutorial is right. I'm really surprised, normal editors seem a bit cumbersome. And running command in and then dumping command result into file without quitting vim by just typing few letters and then the command.. wow.. :o

And of course, you look so damn pro to someone who's watching when using it and things just pop in and out of the text seemingly at random very quickly. 8)
« Last Edit: February 23, 2013, 11:08:49 pm by FRex »
Back to C++ gamedev with SFML in May 2023

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Game Development Design articles
« Reply #32 on: February 24, 2013, 01:54:47 pm »
I'm so glad that I could help to enlighten someone. :P Congrats on your decision, you won't regret it. :)

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Game Development Design articles
« Reply #33 on: February 24, 2013, 02:07:19 pm »
It's not like I'm writing much anyway and vs10 still wins for c++ but I'm really surprised by the functionality I've seen so far.
Back to C++ gamedev with SFML in May 2023

Indorile

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Game Development Design articles
« Reply #34 on: March 24, 2013, 07:18:04 pm »
Hey there.
I started writing my platformer game in C++/SFML and inspired by your article I went with the self-attached component system route. So I started implementing and came with a working solution. Thing is its a "working solution", my C++ skills are rusty as hell after spending about 6+ years with C# and NeoAxis engine, so code could use some refactoring even at this early stage. So here are some relevant parts of the code:

BaseAttribute:
// HPP
#ifndef BASE_ATTRIBUTE_HPP_INCLUDED
#define BASE_ATTRIBUTE_HPP_INCLUDED

#include <string>

namespace ksg
{
    class BaseAttribute
    {
    public:
        virtual ~BaseAttribute() = 0;

        const std::string& getName() { return m_Name; }

    protected:
        std::string m_Name;
    };
}

#endif // BASE_ATTRIBUTE_HPP_INCLUDED

// CPP
#include "../include/BaseAttribute.hpp"

namespace ksg
{
    BaseAttribute::~BaseAttribute()
    {
    }
}

Attribute:
#ifndef ATTRIBUTE_HPP
#define ATTRIBUTE_HPP

#include <string>
#include <memory>

namespace ksg
{
    template <typename T>
    class Attribute : public BaseAttribute
    {
    public:
        Attribute(T value, const std::string& name)
        {
            m_Value = value;
            m_Name = name;
        }

        T& getValue() { return m_Value; }

    protected:
        T m_Value;
    };
}

#endif // ATTRIBUTE_HPP

BaseComponent:
//HPP
#ifndef BASE_COMPONENT_HPP
#define BASE_COMPONENT_HPP

#include "BaseAttribute.hpp"
#include <vector>
#include <memory>

namespace ksg
{
    class GameObject;

    class BaseComponent
    {
    public:
        BaseComponent(std::shared_ptr<GameObject> parent);
        virtual ~BaseComponent();

        virtual std::unique_ptr<BaseComponent> clone(std::shared_ptr<GameObject> parent) = 0;
        virtual void update(float dt) = 0;

        bool checkRequiredAttributes(std::shared_ptr<GameObject> obj);

    protected:
        static std::vector<std::string> m_RequiredAttributes;
        std::shared_ptr<GameObject> m_Parent;
    };
}

#include "GameObject.hpp"

#endif // BASE_COMPONENT_HPP

//CPP
#include "../include/BaseComponent.hpp"

namespace ksg
{
    std::vector<std::string> BaseComponent::m_RequiredAttributes;

    BaseComponent::BaseComponent(std::shared_ptr<GameObject> parent)
    {
        m_Parent = parent;
    }

    BaseComponent::~BaseComponent()
    {
    }

    bool BaseComponent::checkRequiredAttributes(std::shared_ptr<GameObject> obj)
    {
        if(!obj)
            return false;

        std::vector<std::string>::const_iterator iter;
        for(iter = m_RequiredAttributes.begin(); iter != m_RequiredAttributes.end(); ++iter)
            if(!obj->getAttribute(*iter))
                return false;

        return true;
    }
}

GameObject:
//HPP
#ifndef GAME_OBJECT_HPP_INCLUDED
#define GAME_OBJECT_HPP_INCLUDED

#include "BaseAttribute.hpp"
#include "BaseComponent.hpp"
#include <unordered_map>
#include <vector>
#include <memory>

namespace ksg
{
    class GameObjectFactory;

    class GameObject
    {
    friend class GameObjectFactory;

    public:
        void update(float dt);

        std::shared_ptr<BaseAttribute> getAttribute(std::string name) { return m_Attributes[name]; }

    private:
        std::unordered_map<std::string, std::shared_ptr<BaseAttribute>> m_Attributes;
        std::vector<std::unique_ptr<BaseComponent>> m_Components;
    };
}

#include "GameObjectFactory.hpp"

#endif // GAME_OBJECT_HPP_INCLUDED

//CPP
#include "../include/GameObject.hpp"

namespace ksg
{
    void GameObject::update(float dt)
    {
        std::vector<std::unique_ptr<BaseComponent>>::const_iterator iter;
        for(iter = m_Components.begin(); iter != m_Components.end(); ++iter)
            (*iter)->update(dt);
    }
}

GameObjectFactory:
//HPP
#ifndef GAME_OBJECT_FACTORY_HPP
#define GAME_OBJECT_FACTORY_HPP

#include "GameObject.hpp"
#include "BaseAttribute.hpp"
#include "BaseComponent.hpp"
#include <vector>
#include <memory>

namespace ksg
{
    class GameObjectFactory
    {
    public:
        GameObjectFactory();
        ~GameObjectFactory();

        static bool initialize();
        static std::unique_ptr<GameObjectFactory>& getInstance() { return m_Instance; }

        void createGameObject(const std::vector<BaseAttribute*>& attributes);
        void update(float dt);

    private:
        static std::unique_ptr<GameObjectFactory> m_Instance;

        std::vector<std::unique_ptr<BaseComponent>> m_Components;
        std::vector<std::shared_ptr<GameObject>> m_GameObjects;
    };
}

#endif // GAME_OBJECT_FACTORY_HPP

//CPP
#include "../include/GameObjectFactory.hpp"
#include "../include/ScrollingBackgroundComponent.hpp"

namespace ksg
{
    std::unique_ptr<GameObjectFactory> GameObjectFactory::m_Instance;

    GameObjectFactory::GameObjectFactory()
    {
        ScrollingBackgroundComponent::initRequiredAttributes();
        m_Components.push_back(std::unique_ptr<ScrollingBackgroundComponent>(new ScrollingBackgroundComponent(nullptr)));
    }

    GameObjectFactory::~GameObjectFactory()
    {

    }

    bool GameObjectFactory::initialize()
    {
        m_Instance.reset();
        m_Instance = std::unique_ptr<GameObjectFactory>(new GameObjectFactory());

        if(!m_Instance)
            return false;

        return true;
    }

    void GameObjectFactory::createGameObject(const std::vector<BaseAttribute*>& attributes)
    {
        std::shared_ptr<GameObject> gameObject = std::shared_ptr<GameObject>(new GameObject);

        for(std::vector<BaseAttribute*>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
            gameObject->m_Attributes.insert(std::make_pair((*iter)->getName(), std::shared_ptr<BaseAttribute>(*iter)));

        for(std::vector<std::unique_ptr<BaseComponent>>::const_iterator iter = m_Components.begin(); iter != m_Components.end(); ++iter)
            if((*iter)->checkRequiredAttributes(gameObject))
                gameObject->m_Components.push_back(std::unique_ptr<BaseComponent>((*iter)->clone(gameObject)));

        m_GameObjects.push_back(gameObject);
    }

    void GameObjectFactory::update(float dt)
    {
        std::vector<std::shared_ptr<GameObject>>::iterator iter;

        for(iter = m_GameObjects.begin(); iter != m_GameObjects.end(); ++iter)
            (*iter)->update(dt);
    }
}

And finally something more specific - ScrollingBackgroundComponent:
//HPP
#ifndef SCROLLING_BACKGROUND_COMPONENT_HPP
#define SCROLLING_BACKGROUND_COMPONENT_HPP

#include "BaseComponent.hpp"
#include "Attribute.hpp"
#include "ScrollingBackground.hpp"
#include <SFML/Graphics.hpp>
#include <memory>

namespace ksg
{
    class ScrollingBackgroundComponent : public BaseComponent
    {
    public:
        ScrollingBackgroundComponent(std::shared_ptr<GameObject> parent);
        ~ScrollingBackgroundComponent();

        std::unique_ptr<BaseComponent> clone(std::shared_ptr<GameObject> parent) { return std::unique_ptr<BaseComponent>(new ScrollingBackgroundComponent(parent)); }
        void update(float dt);

        static void initRequiredAttributes();

    private:
        std::shared_ptr<Attribute<ScrollingBackground*>> m_Visual;
    };
}

#endif // SCROLLING_BACKGROUND_COMPONENT_HPP

//CPP
#include "../include/ScrollingBackgroundComponent.hpp"

namespace ksg
{
    ScrollingBackgroundComponent::ScrollingBackgroundComponent(std::shared_ptr<GameObject> parent) : BaseComponent(parent)
    {
        if(parent)
        {
            m_Visual = std::dynamic_pointer_cast<Attribute<ScrollingBackground*>>(parent->getAttribute("sb_visual"));
            std::shared_ptr<Attribute<sf::Vector2i>> position = std::dynamic_pointer_cast<Attribute<sf::Vector2i>>(parent->getAttribute("sb_position"));
            std::shared_ptr<Attribute<sf::Vector2f>> scrollSpeed = std::dynamic_pointer_cast<Attribute<sf::Vector2f>>(parent->getAttribute("sb_scroll_speed"));
            std::shared_ptr<Attribute<bool>> autoScroll = std::dynamic_pointer_cast<Attribute<bool>>(parent->getAttribute("sb_auto_scroll"));

            if(position)
                m_Visual->getValue()->setPosition(position->getValue());

            if(scrollSpeed)
                m_Visual->getValue()->setScrollSpeed(scrollSpeed->getValue());

            if(autoScroll)
                m_Visual->getValue()->setAutoScroll(autoScroll->getValue());
        }
    }

    ScrollingBackgroundComponent::~ScrollingBackgroundComponent()
    {

    }

    void ScrollingBackgroundComponent::update(float dt)
    {
        m_Visual->getValue()->update(dt);
    }

    void ScrollingBackgroundComponent::initRequiredAttributes()
    {
        m_RequiredAttributes.push_back("sb_visual");
    }
}
 

Somewhere in the Game.cpp:
std::vector<BaseAttribute*> attr;
attr.push_back(new Attribute<ScrollingBackground*>(&back1, "sb_visual"));
attr.push_back(new Attribute<sf::Vector2f>(sf::Vector2f(1, .25f), "sb_scroll_speed"));
GameObjectFactory::getInstance()->createGameObject(attr);

attr.clear();
attr.push_back(new Attribute<ScrollingBackground*>(&back2, "sb_visual"));
attr.push_back(new Attribute<sf::Vector2i>(sf::Vector2i(0, 32), "sb_position"));
attr.push_back(new Attribute<sf::Vector2f>(sf::Vector2f(16, .5f), "sb_scroll_speed"));
attr.push_back(new Attribute<bool>(true, "sb_auto_scroll"));
GameObjectFactory::getInstance()->createGameObject(attr);
       
attr.clear();
attr.push_back(new Attribute<ScrollingBackground*>(&back3, "sb_visual"));
attr.push_back(new Attribute<sf::Vector2f>(sf::Vector2f(1.5f, .75f), "sb_scroll_speed"));
GameObjectFactory::getInstance()->createGameObject(attr);
attr.clear();

All runs fine so far, though as I said my C++ skills are rusty and I need code revision badly :)
For start I would like to change the way of storing available components into the game factory, having empty components for the sake of it is just meh to me.

Anyway thanks in advance for help, and thanks for your game design articles :)

P.S. Two links I found during my research that could be of some use:
Pitfalls of Object Oriented Programming
Theory and Practice of Game Object Component Architecture

EDIT: Is there like some spoiler tag or show hide code option? Too much code = unnecessary lengthy post  :-\
« Last Edit: March 24, 2013, 07:20:04 pm by Indorile »

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Game Development Design articles
« Reply #35 on: March 28, 2013, 08:32:06 am »
Your approach looks quite good! I can find some similarities between mine and yours. :)

Quote
For start I would like to change the way of storing available components into the game factory, having empty components for the sake of it is just meh to me.
That's what I mean in the How to tell the system what controllers exist section. Having "dummy" objects is indeed quite meh. ;) Here's what I do in my implementation:

The components all have a static function get_requirements(), which returns what the component needs (in terms of properties, in your case attributes). In order to register a component at the system I use component factories. Two classes are needed for that:

  • The base factory, which is an interface and declares two pure virtual functions: One to create the component, and another one to fetch the requirements.
  • The concrete factory, which is a template class and inherits from the base factory, taking the component class type as template argument. It also implements the pure virtual functions from the base factory: The create function will simply create the component and return it (in my case wrapped in a shared pointer), and the call for getting the requirements is 1:1 redirected to the component's requirements function (the static one I mentioned before).

What you now do is you store those factories in your component system's manager or whatever you call it. And whenever an entity is created or attributes are added/removed/changed, you check that entity against all the factories by calling their requirements. If the requirements match, you call the factory's create function to get a new component for that entity.

Feel free to ask if anything's unclear.

Indorile

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Game Development Design articles
« Reply #36 on: March 28, 2013, 07:10:09 pm »
Yeah, I went with the BaseFactory/Factory<T> approach and I have renamed GameObjectFactory to GameObjectSystem, so in the constructor now I do this:

m_Factories.push_back(std::unique_ptr<Factory<ScrollingBackgroundComponent>>(new Factory<ScrollingBackgroundComponent>()));

I'll have some more questions later on when I progress with my game's development :) Thanks for the help.

danman

  • Hero Member
  • *****
  • Posts: 1121
    • View Profile
    • Email
Re: Game Development Design articles
« Reply #37 on: March 29, 2013, 10:36:51 pm »
FRex : In my opinion:
components should not really interfere in their behavior other than by doing what they are aimed to on the properties.
In fact, it looks like a complete system and not like OOP. You have a component that must be designed to process data (for exemple a PhysicBody component would update the position of the object), and that data must then be handled by another component (Graphics API use the position to check if object is dawable and then draw the object on the screen).
They are multiple point very simplificated in my explanation, and certainly english mistakes, but you've got my point of view ;)

an #Altdevblogaday blogger has made 5 articles on that kind of system, you can check it ;)
Pointilleur professionnel

Indorile

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Game Development Design articles
« Reply #38 on: April 01, 2013, 07:18:11 pm »
Now that I think of it, is there some way to differentiate components that require same attributes? For example both PlayerController and AIController require Position and Velocity, how should system know if it should attach one or another? I could solve it by using different names like PC_Position and NPC_Position, but then Collision that requires Position wouldn't know about them...

danman

  • Hero Member
  • *****
  • Posts: 1121
    • View Profile
    • Email
Re: Game Development Design articles
« Reply #39 on: April 01, 2013, 07:28:14 pm »
The fact is that both MUST require the same position attribute, or it's not really an entity position.
Entity-system doesn't make entity handle the properties of subsystem, you won't add a AIi_node[] attribute for example. But if AI need position, it knows where to find it, and can eventually update it (though i don't see any advantage to edit it in AI_system :p).
Position is an ENTITY attribute and not an AI  attribute.

in this way, i'm thinking about adding a subset of properties as "capabilities" (for example collidable, gravitable, extandable, etc) that would induce a defined way to make entities, and I have in mind an incredible idea of adding "behavior" classes, but can't determine why either how implement this yet.

edit:
You must define a new attribute that represent the next position you want to get to, the AI calculator would set it, and the Physic/Logic/Interpolating system would use it to update the position/velocity or other.
« Last Edit: April 01, 2013, 08:42:05 pm by danman »
Pointilleur professionnel

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Game Development Design articles
« Reply #40 on: April 02, 2013, 10:33:51 am »
Now that I think of it, is there some way to differentiate components that require same attributes? For example both PlayerController and AIController require Position and Velocity, how should system know if it should attach one or another?
The fact that only position and velocity trigger PlayerController AND AIController is too unspecific. Think of it: An entity being controlled by the player probably has something like "controllable = true", and AIController requires e.g. "brain_type".

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Game Development Design articles
« Reply #41 on: April 02, 2013, 10:52:16 pm »
Hey guys,

I got some free time left and decided to write a new article. This one's about a technique for greatly reducing code dependencies, reducing compile times and a workflow that allows to add features step by step and isolated.

Please welcome the message bus.

WEBLINK Game Development Design 3: Message Bus
« Last Edit: April 03, 2013, 02:16:33 pm by Tank »

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Game Development Design articles
« Reply #42 on: April 02, 2013, 11:10:44 pm »
Quote
Please welcome the message bus.
Hi. :)

I opened it up and first thing that caught my eye is this :o:
http://stefan.boxbox.org/wp-content/uploads/2013/04/dependencies.png
I didn't even read a single line yet, but this.. wow.., this just looks like if a Java or C# Object Obsessed Programmer got his hands on  c++'s multiple inheritance. Crap, no, full arrow is composition. My bad. That still looks 'great' though.
« Last Edit: April 02, 2013, 11:14:14 pm by FRex »
Back to C++ gamedev with SFML in May 2023

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Re: Game Development Design articles
« Reply #43 on: April 02, 2013, 11:18:42 pm »
Haha. ;) Keep in mind that it's a negative example. The arrows are no UML-style arrows. They're just there to show what's dependent on what.

FRex

  • Hero Member
  • *****
  • Posts: 1848
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Game Development Design articles
« Reply #44 on: April 02, 2013, 11:31:35 pm »
Quote
Personally I chose to use hashed strings for performance reasons by still keeping readability
'by' looks like it should be 'but'. This sounds like the fact of keeping readability is a way to achieve hashing and performance. I might be wrong. ::)

The article might contain product placement adverts. http://www.flexworld-game.com/home.php
Just kidding. ;)
« Last Edit: April 02, 2013, 11:59:58 pm by FRex »
Back to C++ gamedev with SFML in May 2023