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

Author Topic: GDF - GameDevFramwork  (Read 2781 times)

0 Members and 1 Guest are viewing this topic.

man'O'war

  • Newbie
  • *
  • Posts: 46
  • What needs to be done is done by those capable.
    • View Profile
GDF - GameDevFramwork
« on: August 21, 2016, 12:43:23 am »
Hello everyone,

I would like to share a framework i am currently developing which is dedicated for 2D game development

updated:

Preamble
The story began when i wanted to develop a game like Secret of Grindea. great game by the way :3
As you may definitely noticed, when starting up a game for the first time, at a certain point, your code starts to become heavy in a way that, it is not flexible anymore. It works only for what you've made and any future changes, or adding new ideas requires major changes in core code. 
(click to show/hide)

This is why it is necessary to structure your code, and create some base classes to manage your game, before even creating your first asset.

I was inspired from several references, such as the SFML Game Development ebook from packtpub. It was very good to start with, but does not have a flexible structuring of code.

Quote
What i mean by flexible structuring of code, is that, The game may grasp almost any kind of new concept/idea without having to make changes in core code. (or at least very few changes)

I've also tested UDK and Unity (documentation), which they helped me a LOT to learn more about organizing things together. Especially the GameObject/Component pattern.

The GameObject/Component design makes your game entities INCREDIBLY flexible.
Will be discussed later

Presentation
GameDevFramwork (GDF) is an open-source C++ framework for 2D Game development extending:
  • The SFML library 2.3.2
  • Box2D Library 2.3.1: for physics
  • Qt 5.7.0 : used for the meta-object system ( i will find a new non-qt replacement for that )
  • and more ...

More .... ?
Yes... The purpose of the project is to build a totally modular open-source framework, by providing a set of base classes ( kernel module ) to make it possible to accept and easily integrate newly created modules.

The Kernel module
The kernel is a minimum set of classes that implements the core of the framework,
Here is the class diagram of the kernel


starting by:
  • GameInfo: Class that defines the application logic, how to initialize, handle events, update and draw the object.
  • Scene: The scene class provides a way for creating and organizing items(GameObjects) in a tree structure.
  • GameObject: GameObjects are the main items for scene's nodes, every thing on the scene is a GameObject. However, GameObjects have no concrete representation on the scene, they are nothing but containers for Components
  • Component: A component is an object that defines a specific logic and is attached to certain object ( such as GameObjects ),
    • HierarchicalContainer: This class provides a multi-root tree structure in order to organize Components. And any class that inherits from this class becomes a container for Components, as it is for GameObject, Scene and GameInfo
    • Transform: A Transform is a component that provides a GameObject with a 2D Coordinate system (position, rotation, scale) and, organize it in a Tree-Structure ( Parent/Children)

The GameObject/Component pattern allows the creation of a meaningless GameObject. And its context is defined by the logic carried by its components. Components can be added and removed at will.
For example
It can be seen as, equipping your hero (GameObject) with different weapons and skills (Component).
If you give your hero a sword, a helmet and a shield, then you've made a Warrior. But if you give him a staff, and a spellbook , then you've chosen to be a Sorcerer, despite it is the same GameObject.

    GameObject* hero = GameObject::instantiate();
    // Choosing to be a Warrior
    hero->addComponent<Sword>();
    hero->addComponent<Shield>();
    hero->addComponent<Helmet>();

    // Choosing to be a Sorcerer
    hero->addComponent<Staff>();
    hero->addComponent<Spellbook>();
    // or ...
    Spellbook* spell_book = hero->addComponent<Spellbook>();
 

But....
What if i want my Sorcerer to have not more than one Staff and one Spellbook. And cannot have a Staff without  wearing a Cape ?

Well, thanks to the KernelRules class that defines a set of rules ( dependency relationship between components ) in order to check the satisfiability of the add and the remove actions.
In other way
Quote
rule: X → A, B, C
A Component X cannot be added inside GameObject G if A, B, C Components are not available within G
vice-versa
A Component A cannot be removed if one or more other Components depends on it. ( herein, X )

Example-Code
    make_singleton("Staff");
    make_singleton("Spellbook");
   
    create_rule("Staff", "Cape");
 

make_singleton creates a cardinality rules, imposing the (HierarchicalContainer) or GameObject to only accept one instance of a given Component-type. Herein, "Staff", "Spellbook" are limited to one instance each.
AS for, create_rule method, it creates a dependency relationship between Staff and Cape, telling that, Staff depends on/ requires Cape to be available first in order to be created..

Quote
There are pre-defined rules applied for built-in components in order to ensure the good functioning of the kernel module


By taking advantage of these methods, and of the generic implementation of the GameInfo, any kind of component can be attached to the GameObject.

Herein above, one elementary cycle of the gameloop.
Based on timestep update. Each elementary cycle, the GameInfo
  • Handles application events
  • Performs regular updates of the Scene
  • Performs physics-related updates of the Scene
  • Performs (late) update of the Scene
  • Then proceed to rendering.

Minimal example-code
To start in with the framework,
You must create a sub-class of GameInfo and Scene. ( And also override pure methods )

Quote
Note that, i am not using too much 'getters' in my code, but accessing data members directly, this is why some instruction are too long. 

TestCaseGameInfo.h
(click to show/hide)


TestCaseGameInfo.cpp

(click to show/hide)



TestCaseScene.h

(click to show/hide)

TestCaseScene.cpp
(click to show/hide)


Adding a new Component: the pre-declared MoveObject
g0 had a component called "MoveObject" that makes it move in the scene. i.e
  • MoveObject is retrieving the Transform Component of g0 in order to apply a translate.
  • MoveObject requires Transform: i.e:
create_rule("MoveObject", "Transform");
  • must be defined in KernelRules
  • MoveObject is systematically initialized, updated and drawn if necessary thanks to the GameInfo & Scene.

Here is the implementation of MoveObject

MoveObject.h
(click to show/hide)

MoveObject.cpp
(click to show/hide)

Quote
A MonoBehavior is a Component with more features than a regular Component, it is the base class for user-components
Quote
Note that a GameObject cannot be overriden. 

Built-in Components
In addition to the kernel module, there are some built-in components used in order to create basic objects, like:
Graphics, Sounds, Animations & animator, Shaders, Physics , Colliders, Joints, Gui, Renderers, AudioListener, Cameraetc.

Modules... as addons
Creating new modules and integrating them to the framework is very simple.
What needs to be done is to
  • Define your set of Component-classes
  • Attaches them to the right Container
And they'll be fully functioning. ( as long as Component's methods are overridden and used )



I've made aside some external modules to implement new functionalities for the framework.
resource management module
Resource management module offers a set of classes in order to load, use and unload resource automatically avoiding any kind of memory leak.
  • Resource Manager: The resource manager keeps track of all loaded resources, and manages their lifetime. i.e Allocating when it is required, and deallocating resource whenever it's not needed
  • Resource is the base class for all resource.

The resource manager unloads resource in a safe manner, means that, if a resource is still being used by any object, it cannot be unloaded. This is achieved using smart pointers ( the use_count member of std::shared_ptr ) . See, documentation for implementation details

time management module
The time management module define concepts about time.
the main classes of this module are:
  • Chrono: Is a component that gives an object time properties, and become part of a timeline. ( spawn_time, lifetime etc )
  • TimeKeeper: Is a GameObject's component that records the timeline of a GameObject, i.e recording the past of the GameObject. This technique is Events-triggered rather than Time-Events ( Continuous recording )
  • TimeWinderThe TimeWinder is the core Component of this module, it manipulates the time of any GameObject. Time freeze, time reversal,  normal play with a play factor. 
  • Event Is a base class for time related events, used by the timekeeper to record the timeline, they define the type of the event it happened, the target object, the previous value before the action happened, and the new one

exec ....
When the TimeWinder is launched ( eventually from the scene ), it retrieves all the TimeKeeper Components of all GameObjects. And plays the recorded timeline of each GameObject backwards, by generating an opposite event of the one initially recorded. Thus, simulating a backward execution.
However, any property that needs to be affected by time must have a dedicated event and must be called at the right place.
Example:
  • VelocityEvent: Stores velocity data ( old, new, target, timestamp )
  • TranslationEvent: Stores translation data ( old, new, target, timestamp )
  • ValueChangedEvent: Stores changes in value ( old, new, target, timestamp )
  • ....




In the diagram above, it displays the stack's content of the timekeeper at time = ti. The stack is divided in two part:
Events from the past, and events from the future

On a normal play, the timekeeper registers time-events in the past part of the stack, as shown on 'normal play' case

When entering a time manipulation session. The timekeeper stops recording events.
- ON BACKWARD PLAY - rewind: The timekeeper pops events one by one from the past, and pushes them onto future.
- ON FORWARD PLAY: The timekeeper pops events one by one from the future, and pushes them onto the past.

This allows to go back and forth in time at will,

When the session ends, events from the future are discarded, and time keeper starts to record new events.
 
Similar mechanisms of time manipulation can be found in the game Braid, steam link:  http://store.steampowered.com/app/26800/.

And so on ....
As future tasks, I am willing to add and/or integrate already built modules to the framework
  • IA Algorithm: Search algorithm, Dijkstra, A* etc.
  • Complex Systems algorithms:  Artificial Neural Network, Celullar Automaton, Swarm intelligence etc
  • Parallel programming plateform - open-mpi: Can be used in combination with IA & Complex systems.
  • Interact with an Arduino Board: Makes it possible to communicate with an Arduino-board through the application,

    As a sample, Making a LED blink whenever a GameObject leaves or enter the Viewport of the Camera .
    simply by using the Renderer's callback functions 'onBecomeVisible' and onBecomeInvisible', to send a message through the serial port to the arduino. See, ArduinoTest in source code

Examples
There is not a lot of things to display right now, but i can show you a sample of the time management module. ( Time Reversal mechanisms )
c.f Attached video.


The video shows Time Reversal mechanism. The scene contains 2 (Chicken=Cockatrice) GameObjects (  c.f RPG Maker Sprite ) spawned at the same location.
One Cockatrice does not move, while the other is moving under constant velocity ( x-axis only ) for a short period of time ..

  • Whenever the user presses the left mouse's button, it teleports the cockatrice to the cursor position.
  • When Left Shift is button is held, time reversal starts, on release, time goes back to normal.

Scenario:
  • The Cockatrice starts moving positive x-axis due to pre-applied velocity. Then teleports several time to cursor position ( while it keeps moving )
  • After a short period of time, the Cockatrice goes to idle state ( stops moving )
  • [Time Reversal is triggered at this moment- LeftShift pressed]
  • The TimeWinder is executed causing the Cockatrice to play its TimeLine backward. Until its lifetime reaches 0. Thus, its state will be the same as it was on t=0.
  • When time reversal is canceled - Left Shift released
  • The Cockatrice will start to move again as it is its first time

Content of the Log window
What is displayed at the terminal is:
  • Current applied rules of the KernelRules class

(click to show/hide)
&#8594; means → : The forum cant draw it ?!! ( or may be it is inside 'code' block )
  • Added Components for each Object
(click to show/hide)

  • Content of the Resource Manager (loaded resource)
(click to show/hide)

  • Event recording ( Normal Play )

(click to show/hide)

  • On Time reversal ( Backward Play )

(click to show/hide)


  • Stops the time reversal ( Left Shift released )

(click to show/hide)


Contribution
The framework is designed to be fully open-source and community repo,

I designed a minimal kernel to be used as a starting point for anyone who wants to develop modules from scratch.
However, built-in modules can be included at will depending on the type of the developed module.
You are welcome to:

  • Suggest Code improvements ( as i'm writing very basic things right now to get a global architecture, then i'll refine every class ) [Note that kernel is refined but incomplete (still in progress) ]
  • Suggest modifications about the kernel or an existing module.
  • Suggest your own modules. even if there is an already existing one,
    There is no better code. but one can fit better than the other, depending on the problem
  • Platform portability, { currently tested on Ubuntu 16.04. }
    But since Qt & SFML & Box2D are multi-platform, there should not be a problem.(not tested on other platforms )

Thank you for your time,
And feel free to contact me or give your opinion, your feedback is welcome.

Cordially


Technical specification ( reminder )
« Last Edit: August 24, 2016, 03:24:13 pm by man'O'war »

man'O'war

  • Newbie
  • *
  • Posts: 46
  • What needs to be done is done by those capable.
    • View Profile
Re: GDF - GameDevFramwork
« Reply #1 on: August 24, 2016, 03:31:30 pm »
Hi,

The Github repo has been updated. including the README.
  • Under kernel branch: You'll find the kernel code almost completed
  • Instructions to compile it and get started with a first example
  • Check the README.md and the wiki for more explanation
  • Documentation file is available: 'doc.tar.gz' under kernel branch

hawke55

  • Newbie
  • *
  • Posts: 1
    • View Profile
Re: GDF - GameDevFramwork
« Reply #2 on: August 30, 2016, 05:09:03 am »
In the part where you explain how to compile it says to run the makefile with make but I cant seem to find a makefile. Im kinda new to compiling stuff this way and I may just be missing something.