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

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Critkeeper

Pages: [1] 2
1
Transformable code keeps track of what transforms have been applied and updates the transform if need be.

Transform code provides methods for combined translation and rotation, and combined translation and scale.

For example, suppose we have a method called translate and rotate that does what you'd expect, and which returns a fresh new Transform with the given data.

Rotating around a point with the method rotate(angle, centerX, centerY) will return a transform that is exactly the same as matrix multiplication of translate(centerX, centerY) * rotate(angle, 0, 0) * translate(-centerX, -centerY). In fact thats how the matrix expression for the combined translation rotation was generated. Formula for basic rotation around the origin, scaling around the origin, shearing around the origin and translating can be found on wikipedia, and they can be matrix multiplied to generate other matrices.

"Rotating around a given center" or "Scaling around a given center" are so common that it makes sense for us to combine the formula and provide it as rotate(angle, centerX, centerY), which is exactly what SFML does.

So I was wondering why not just combine all of the transforms that way and throw in shear for good measure, using the standard opengl style of matrix order? For example, a shear around a point, followed by a rotation around a different point, followed by a scale around a third point, followed by a translation:

Code: [Select]
typedef
struct
{
        float v[16];
} Transform;

Transform
transform
(       float shcx
,       float shcy
,       float shvx
,       float shvy
,       float rcx
,       float rcy
,       float angle
,       float sccx
,       float sccy
,       float scvx
,       float scvy
,       float tx
,       float ty
)
{
        const float cosine = cos (angle);
        const float sine = sin (angle);
        const float a = -shvy * scvx * sine + scvx * cosine;
        const float b = shvx * scvx * cosine - scvx * sine;
        const float c = shvy * scvy * cosine + scvy * sine;
        const float d = shvx * scvy * sine + scvy * cosine;
        const float x = tx + scvx * (rcx * (1 - cosine) + rcy * sine) + shvy * scvx * shcy * sine - shvx * scvx * shcx * cosine + sccx * (1 - scvx);
        const float y = ty + scvy * (rcy * (1 - cosine) + rcy * sine) - shvx * scvy * shcx * sine - shvy * scvy * shcy * cosine + sccy * (1 - scvy);
        const Transform r =
        {       a,      c,      0,      0
        ,       b,      d,      0       0
        ,       0,      0,      1,      0
        ,       x,      y,      0,      1
        };
        return r;
}
(notice in the above struct initialization you have to conceptually flip the math matrix across the perceived diagonal. If you format it in a list and look at the indexes you will see that it maps correctly to opengl's way of doing things)

I don't know if this will be useful to anyone here, or whether using it (well namely the method of generating it) could speed up the implementation of SFML Transformable as I haven't really given it much thought.

Here is how I generated the equations:




The left most matrix is obviously a scale(k,h) around point(s,t), the right most is a shear(f,g) around point (q,r), and the middle is rotation(theta) around point(x,y).

The resulting matrix is specifically what you would get for first shearing, then rotating, then scaling around specific points.
The function I provided tacks on translation after doing those three things in that specific order (translation as a last operation is fairly simple and not worth typing out so I didn't, in general you would need to multiply the transformation matrix as well)

Note that you get a different matrix if you want to do those things in a different order. All in all there are 4 operations, so there are 4! = 24 unique transformation matrices, which would be generated similar to the above one, gotten by multiplying 4 matrices together, one matrix for each fundamental kind of operation: rotation about a point, translation, shear about a point and scale about a point, in all possible different orders.

if you had functions for each possible order, 24 functions in all, you could compute a transform for an arbitrary shear around a point, rotation around a point, scale around a point and translation in whatever order all while only making a single function call. And 24 isn't so high that you'd thrash the I-Cache.

Just food for thought.

2
General discussions / Why use SFML over SDL?
« on: July 12, 2015, 08:10:43 am »
This spicy question isn't meant to give you heartburn. I just want side by side comparison of these two libraries from someone who has used both and knows their strengths and weaknesses.

3
General / Programming puzzle / quiz.
« on: June 21, 2015, 03:30:20 pm »
suppose i have a tuple containing tuples and arrays

Code: [Select]
tuple
< tuple
< map<int, int>
, float
>
, tuple
< int
, int
>
, array
< int
5
>
, tuple
< vector<int>
, queue<float>
>
>
state;

the normal way to access an element in a tuple is to use

Code: [Select]
get <Index> (tuple);
so for the above, to access the array I would use

Code: [Select]
get <2> (state);
but in order to access the 3rd element of the array I would have to say:

Code: [Select]
get <3> ( get <2> (state) );
this is quite ugly.


here is an imaginary function that looks prettier:

Code: [Select]
get <3> (state, 2);
what would the full function signature for such a function look like?

4
General / How would I initialize a RenderWindow inside a tuple?
« on: June 19, 2015, 10:15:33 am »
Code: [Select]
tuple
<   RenderWindow
>
interface
{   RenderWindow (VideoMode(1920, 1080), "") };

Before anyone says "why would you want ... " just don't. I could use window.create(), but I want to be sure that what I'm trying to do isn't actually possible. The compiler spits out complaints about RenderWindow being noncopyable.

How would I initialize a tuple element without performing a copy? In other words, how do I emplace an element into a tuple?

5
of ints.

This isn't a request or a complaint, and it doesn't need to be since this is just General Discussion.

I'm just curious. I want to understand the reasoning behind the design decisions of SFML, since I regard it as one of the cleanest libraries I have ever seen; I hope that by understanding the reasons behind various design decisions I can pull some of that good hygiene into my own work.

Code: [Select]
namespace sf
   39 {
   44 class Event
   45 {
   46 public:
   47
   52     struct SizeEvent
   53     {
   54         unsigned int width; 
   55         unsigned int height;
   56     };
   57
   62     struct KeyEvent
   63     {
   64         Keyboard::Key code;   
   65         bool          alt;     
   66         bool          control;
   67         bool          shift;   
   68         bool          system; 
   69     };
   70
   75     struct TextEvent
   76     {
   77         Uint32 unicode;
   78     };
   79
   84     struct MouseMoveEvent
   85     {
   86         int x;
   87         int y;
   88     };
   89
   95     struct MouseButtonEvent
   96     {
   97         Mouse::Button button;
   98         int           x;     
   99         int           y;     
  100     };
  101
  109     struct MouseWheelEvent
  110     {
  111         int delta;
  112         int x;     
  113         int y;     
  114     };
  115
  120     struct MouseWheelScrollEvent
  121     {
  122         Mouse::Wheel wheel;
  123         float        delta;
  124         int          x;     
  125         int          y;     
  126     };
  127
  133     struct JoystickConnectEvent
  134     {
  135         unsigned int joystickId;
  136     };
  137
  142     struct JoystickMoveEvent
  143     {
  144         unsigned int   joystickId;
  145         Joystick::Axis axis;       
  146         float          position;   
  147     };
  148
  154     struct JoystickButtonEvent
  155     {
  156         unsigned int joystickId;
  157         unsigned int button;     
  158     };
  159
  164     struct TouchEvent
  165     {
  166         unsigned int finger;
  167         int x;               
  168         int y;               
  169     };
  170
  175     struct SensorEvent
  176     {
  177         Sensor::Type type;
  178         float x;           
  179         float y;           
  180         float z;           
  181     };
  182
  187     enum EventType
  188     {
  189         Closed,                 
  190         Resized,               
  191         LostFocus,             
  192         GainedFocus,           
  193         TextEntered,           
  194         KeyPressed,             
  195         KeyReleased,           
  196         MouseWheelMoved,       
  197         MouseWheelScrolled,     
  198         MouseButtonPressed,     
  199         MouseButtonReleased,   
  200         MouseMoved,             
  201         MouseEntered,           
  202         MouseLeft,             
  203         JoystickButtonPressed, 
  204         JoystickButtonReleased,
  205         JoystickMoved,         
  206         JoystickConnected,     
  207         JoystickDisconnected,   
  208         TouchBegan,             
  209         TouchMoved,             
  210         TouchEnded,             
  211         SensorChanged,         
  212
  213         Count                   
  214     };
  215
  217     // Member data
  219     EventType type;
  220
  221     union
  222     {
  223         SizeEvent             size;             
  224         KeyEvent              key;               
  225         TextEvent             text;             
  226         MouseMoveEvent        mouseMove;         
  227         MouseButtonEvent      mouseButton;       
  228         MouseWheelEvent       mouseWheel;       
  229         MouseWheelScrollEvent mouseWheelScroll; 
  230         JoystickMoveEvent     joystickMove;     
  231         JoystickButtonEvent   joystickButton;   
  232         JoystickConnectEvent  joystickConnect;   
  233         TouchEvent            touch;             
  234         SensorEvent           sensor;           
  235     };
  236 };
  237
  238 } // namespace sf

See the (x,y) pairs everywhere? Why not use sf::Vect ?

6
Texture and Shader are already both loadable members of RenderStates

It would be fairly trivial to store and load BlendMode and Transform, given how simplistic they are.

And that would allow us to make RenderStates a loadable resource.



If we also made Drawable a loadable resource then resource management would become so much simpler for the client. You could load up a drawable and renderStates and just tell the renderWindow to draw it.

idk, just seems like it would be easier to use for me personally. That doesn't mean its the right way to go, but I'm interested in hearing your thoughts.

7
This is a controversial question that is intended to stir up a discussion about what SFML actually is, not an actual feature request.

Lets define what a media is. I believe media is any data that can be consumed by a human. For example, music is media because it can be parsed by a human, and visual art is a media. Sculpture is a form of visual art and is its own kind of media if you are permitted to touch it. Naively , since humans only have 6 senses through which to experience the world, sight, smell, taste, touch, hearing and time, media can be broken up into 6 categories.

But human experience is wider than the 6 physical sensations! There are internal sensations, like love and hate. So then it would be more precise to say that there are 2 classes of media, physical and nonphysical, and 6 classes of physical media. Mutimedia just means multiple mediums at once, for example a movie is multimedia. In fact, art can be multimedia if it causes you to feel something nonphysical, like wistfulness.

Naturally it is not possible to cause a person to feel an emotion without exposing them to some kind of physical stimuli. Therefore, all arts that can be classified as nonphysical media are also physical media as a matter of course.

This brings me to SFML, which stands for simple fast multimedia library. Media here obviously refers to physical media. With the current state of the art there are barely over 3 physical media that can be conveyed through the computer-- sight and sound and time obviously, and perhaps a limited tactile experience through vibrating rumble controllers or touch screens. So our ability to convey nonphysical media is restricted to what we can invoke through half of the human physical senses!

Despite this overwhelming drawback, compelling arts that invoke an emotional reaction in the consumer have been actualized through computers, and libraries like SFML enable artists to make these experiences happen.

If you take a good look at the SFML's documentation you will see that everything operates on media in some form. Whether it be storing the media or retrieving it, sending it across a network, parsing it or sending it to a device that will make it visible to the human. Not all operations are visible to the person, and a great deal is hidden in order to provide the programmer with a simple interface through which to convey his art.

SFML enables programmers to convey physical media so that the consumers of that physical media will experience a nonphysical response, like enjoyment, anger, or even something more cerebral like understanding -- as is the case with most non-entertainment based multimedia applications.

One question I would like you to think about is whether or not SFML would be better off or worse off combining media and treating combinations as first class entities. With barely more than 3 media through which to convey information to the consumer via the computer, there are only 7 combinations: {{Sound sight time}{sound time}{sight time}{sound sight}{sound}{sight}{time}}

Currently SFML's API revolves around using composition of the last 3 to build the other 4. What are the benefits and drawbacks of this approach?

8
RenderTarget specifies that it requires a const Vertex*

VertexArray doesn't specify whether it can (or even should) be type cast to that.

Not that you would ever have to.

You could just use vertexArray.draw( ... ) rather than renderTarget.draw( ... ) since VertexArray does inhert from drawable.

But the syntax is nicer the other way. Just wondering.

9
I'm putting it in the public domain.

I tested it so it should be bug free, but having more than one person test it is always one of the fantastic benefits of sharing code with a community.

Its just a circular buffer that uses a std::vector as a back end. When the buffer becomes full it requests that the vector allocate more space and then it copies itself into the vector. The copying is almost trivial but not quite, because circular buffers that are completely full have no empty elements, and yet extending the size of a circular buffer mandates that the new container will have empty elements. Deciding where to put these empty elements in order to preserve the qualities of a circular queue is an easy problem, but nevertheless here is my solution:

Code: [Select]
#ifndef QUEUE_H
#define QUEUE_H

#include <vector>
#include <algorithm>

/**
@brief A queue based on an underlying dynamic circular array buffer
*/
template< typename T, typename Alloc = std::allocator<T> >
class
Queue
{
    public:
        Queue( unsigned int initialCapacity = 0);

        bool
        empty();

        unsigned int
        size();

        unsigned int
        capacity();

        T
        front();

        T
        back();

        void
        push( T );

        void
        pop();

        template <class... Args>
        void
        emplace (Args&&... args);

        void
        swap( Queue& );

    protected:
    private:
        std::vector< T, Alloc >
        buffer;

        unsigned int
        frontIndex;

        unsigned int
        backIndex;

        unsigned int
        numberOfElements;
};

template< typename T, typename Alloc >
Queue< T, Alloc >::
Queue( unsigned int initialCapacity )
:   buffer( initialCapacity > 0 ? initialCapacity : 16 )
,   frontIndex(0)
,   backIndex(0)
,   numberOfElements(0)
{
}

template< typename T, typename Alloc >
void
Queue< T, Alloc >::
swap(Queue& q)
{
    buffer.swap( q.buffer );
}

template< typename T, typename Alloc >
template <class... Args>
void
Queue< T, Alloc >::
emplace( Args&& ... args )
{
    if( numberOfElements == buffer.capacity() )
    {
        std::vector< T, Alloc > v;
        v.reserve( 2*buffer.capacity() );
        std::move( buffer.begin(), buffer.begin() + frontIndex + 1, v.begin() );
        if( frontIndex != numberOfElements )
        {
            std::move_backward( buffer.begin() + backIndex, buffer.end(), v.end() );
            backIndex  += v.capacity() - buffer.capacity();
        }
        v.swap( buffer );
    }
    if( frontIndex == buffer.capacity() )
        frontIndex = 0;
    buffer.emplace( frontIndex, args ... );
    // incrementing front index of circular buffer should not require setting it to 0
    // if additional space was successfully allocated; otherwise bad_alloc exception.
    ++frontIndex;
    ++numberOfElements;
    return;
}

template< typename T, typename Alloc >
void
Queue< T, Alloc >::
pop()
{
    if( numberOfElements == 0 )
        return;
    if( backIndex == buffer.capacity() - 1 )
        backIndex = 0;
    else
        ++backIndex;
    --numberOfElements;
    return;
}

template< typename T, typename Alloc >
void
Queue< T, Alloc >::
push( T x )
{
    if( numberOfElements == buffer.capacity() )
    {
        std::vector< T, Alloc > v;
        v.reserve( 2*buffer.capacity() );
        std::move( buffer.begin(), buffer.begin() + frontIndex + 1, v.begin() );
        if( frontIndex != numberOfElements )
        {
            std::move_backward( buffer.begin() + backIndex, buffer.end(), v.end() );
            backIndex  += v.capacity() - buffer.capacity();
        }
        v.swap( buffer );
    }
    if( frontIndex == buffer.capacity() )
        frontIndex = 0;
    buffer[frontIndex] = x;
    // incrementing front index of circular buffer should not require setting it to 0
    // if additional space was successfully allocated; otherwise bad_alloc exception.
    ++frontIndex;
    ++numberOfElements;
    return;
}

template< typename T, typename Alloc >
T
Queue< T, Alloc >::
back()
{
    return buffer[frontIndex];
}

template< typename T, typename Alloc >
T
Queue< T, Alloc >::
front()
{
    return buffer[backIndex];
}

template< typename T, typename Alloc >
bool
Queue< T, Alloc >::
empty()
{
    return numberOfElements == 0;
}

template< typename T, typename Alloc >
unsigned int
Queue< T, Alloc >::
size()
{
    return numberOfElements;
}

template< typename T, typename Alloc >
unsigned int
Queue< T, Alloc >::
capacity()
{
    return buffer.capacity();
}


#endif // QUEUE_H


10
I tried downloading the TDM-GCC 4.9 series minGW compiler and creating a new compiler option in the build settings for the project pointing to it, and I made sure to use the correct SFML package that was compiled using that compiler.

But I still get unwind errors indicating that there is a problem with how the stack is being used, and that is typically caused by the library being compiled with a different compiler or version than the source you are linking it with.

Does that mean TDM's implementation of minGW isn't compatible?

11
In 2.2 it is this:

sfml-graphics-s-d
freetype
glew
jpeg
sfml-windows-s-d
opengl32
gdi32
sfml-audio-s-d
sndfile
openal32
sfml-network-s-d
ws2_32
sfml-system-s-d
winmm

In 2.3 what is it?

is it this?

sfml-graphics-s-d
freetype
glew
jpeg
sfml-windows-s-d
opengl32
gdi32
sfml-audio-s-d
FLAC
ogg
vorbis
vorbisenc
vorbisfile
openal32
sfml-network-s-d
ws2_32
sfml-system-s-d
winmm

12
Feature requests / Can we get collision detection in SFML?
« on: March 12, 2015, 07:36:34 pm »
Its a simple fast multimedia library, so I can understand if you just wanna reply "nope" on that basis alone, but hear me out:

Collision detection can be useful outside of games. For example, detecting the collision/intersection of a cursor with an "ok" box, or more generally using it as a factor in GUI message handling.

Collision detection is usually application specific, but it isn't the only application specific data structure. For example, std::list used to have O(1) splice() and O(n) size(), but the C++ committee decided to swap the performance characteristics so that list would have O(1) size like every other stl data structure. Despite the fact that lists (among other data structures) can have an application specific implementation, the c++ committee tries to satisfy as many people as possible by providing a "generally good" implementation.

Is collision detection so application specific that there is no such thing as a "generally good" collision detection algorithm across most applications?

Why can't we have collision detection in SFML to satisfy as many people as possible?

13
Step 0:

Install TDM MinGW for windows
http://tdm-gcc.tdragon.net/download
Rather than installing it with the default directory name, save it in a new directory named MinGW, since eclipse will look for that exact name.

Step 1:

Do you have luna eclipse already installed? If not, go to step 2a, otherwise go to step 2b.

Step 2a:

https://eclipse.org/downloads/
Select the 64 bit version eclipse ide for c/c++ development
go to step 3.

Step 2b:

Do you have c++ development tools already installed in eclipse? If not, go to step 2c, otherwise go to step 3.

Step 2c:

Go to help -> install new software -> Work with luna -> Programming languages
check C/C++ Development tools and C/C++ Library API Documentation Hover Help
go to step 3.

Step 3:

http://www.sfml-dev.org/download/sfml/2.2/
download GCC 4.8.1 TDM (SJLJ) - 32-bit and save it

Step 4:

Start eclipse, use the default workspace or your own specific if you have made one.

File -> New -> Project -> C++ -> C++ Project

Name it "SFML_CPP11_Template"

Step 5:

Right click the Project int he Project explorer and go to properties, then go to C/C++ Build -> Settings
Choose [All configurations]

Step 6:

GCC C++ Compiler -> Dialiect -> language standard -> ISO C++11 (-stc=c++0x)

Step 7:

GCC C++ Compiler -> Preprocessor -> Defined symbols
add the following:
SFML_STATIC
__GX_EXPERIMENTAL_CXX_0X__
_cplusplus=201103L

Step 8:

GCC C++ Compiler -> Includes -> Include paths
Let the path to where you saved the SFML library be called "path", then you would add the following:
path\include
so for example if you saved it in C:\Users\User\Documents\
it would be:
C:\Users\User\Documents\SFML-2.2\include

Step 9:

change the configuration to debug configuration

MinGW C++ Linker -> Libraries -> Library search path
add the same thing you added in step 8 except instead of \include add \lib

add the following, in this exact order, to Libraries

sfml-graphics-s-d
freetype
glew
jpeg
sfml-window-s-d
opengl32
gdi32
sfml-audio-s-d
sndfile
openal32
sfml-network-s-d
ws2_32
sfml-system-s-d
winmm

Step 10:

change the configuration to release configuration

MinGW C++ Linker -> Libraries -> Library search path
add the same thing you added in step 8 except instead of \include add \lib

add the following, in this exact order, to Libraries

sfml-graphics-s
freetype
glew
jpeg
sfml-window-s
opengl32
gdi32
sfml-audio-s
sndfile
openal32
sfml-network-s
ws2_32
sfml-system-s
winmm


Step 11:

Save the project and copy / paste it from the workspace to somewhere safe.
Any time you want to make a new SFML C++11 project with eclipse you can copy / paste this template and rename the project, and then start with a working empty slate.



EDIT:

I tried reproducing the exact steps in this thread on another computer and I had problems. I revised the steps in order to make it successful on both computers. It would help me greatly if you could do these steps (even if you don't intend to use sfml in eclipse) so I can understand whether it is reliable as is, or whether it needs to be changed again.

14
if I am in the initializer list of the constructor of a class which inherits from std::function<stuff> ?

is it:

std::function< void ( Type argument, AnotherType anotherArgument )>() ?

So if I wanted to use the non-default constructor for std::function< stuff > I would do something like the following right?

Code: [Select]
Subclass::
Subclass( std::function< void ( Type argument, AnotherType anotherArgument )> happen )
    : std::function< void ( Type argument, AnotherType anotherArgument )>( happen )
{
    // stuff that needs to be initialized for this class
}



Code: [Select]
void functionImplementation( Type first, AnotherType second )
{
    // implementation of function
}


void (*functionPointer)( Type, AnotherType )
functionPointer = &functionImplementation;


Subclass action = new Subclass( functionPointer )


or I would use it like this with lambda:
Code: [Select]
Subclass
action =
new Subclass
(

    []
    ( Type firstArgument, AnotherType secondArgument)
    -> void
    {
        //implementation of function
    };
)


Does my constructor for Subclassneed to be changed in order to call the constructor for std::function< stuff > properly?

15
General / Putting my game through a public design review.
« on: February 10, 2015, 12:48:56 am »
Introduction:

Studying as a software engineer has taught me that design reviews are crucial to producing good robust designs that can be extended and maintained easily. In the interest of making sure that the games I will be making will share a common robust design, I want to put up this design up for public scrutiny. No design should be totally hooked to a library if the API is clean, but nevertheless I want to make sure I'm using SFML to its maximum potential, and that is a valid design criteria. I can't think of any other community that knows how to use SFML better than this one!

The primary goal of this thread is to either verify that the design doesn't need changes, or hopefully to circulate ideas about how to improve the design so that it can be better. A second goal is to demonstrate how SFML can be used in an application. The design will be communicated in 3 ways: a uml class diagram made with http://www.umlet.com/ attached at the bottom, code, and an explanation to fill in the blanks. Keep in mind that this is still in the early design phase, so much of the code remains unwritten and many methods are empty and it certainly isn't complete enough to compile-- hopefully the explanation should fill in the holes.

"What kind of game" in some ways is a fair question, but in other ways is not. If the design for the game is entirely contingent on the type of the game, then no code can be recycled if you decide to write a different kind of game-- and that is obviously nonsense. I am early enough in the design phase that I am writing code that will be recycled again and again, so it behooves me to make sure the design is robust, extendable, versatile and maintainable.

In order to make sure that subsequent revisions to the design can be made, any time a large revision is made I will update the design and post it as a reply to this thread, and I will edit the post you are reading to include a hyperlink to the latest revision.



UML class diagram:





Code:

Audible:
Code: [Select]
#include "Audible.h"

void
Audible::
play() const
{
    playThis();
    for ( Node* child : children )
    {
        Audible* audible = dynamic_cast<Audible*>( child );
        if ( audible )
            audible->play( target, states );
    }
}

Background:
Code: [Select]
#include "Background.h"

Background::
Stage()
{
}

void
Background::
updateThis( sf::Time dt )
{

}

void
Background::
drawThis( sf::RenderTarget& target, sf::RenderStates states )
{

}

void
Background::
playThis()
{

}

Collidable:
Code: [Select]
void
Collidable::
collide() const
{
    collideThis();
    for ( Node* child : children )
    {
        Collidable* collidable = dynamic_cast<Collidable*>( child );
        if ( collidable )
            collidable->collide();
    }
}


Command:
Code: [Select]
#ifndef COMMAND_H
#define COMMAND_H

#include "SFML/System/Time.hpp"
#include <functional>
#include <list>


template < typename Sender, typename Receiver >
class
Command
{
    public:
        sf::Time
        activate;

        sf::Time
        deactivate;

        sf::Time
        sent;

        sf::Time
        received;

        sf::Time
        started;

        sf::Time
        completed;

        std::function< void (Receiver&, sf::Time) >
        operation;

        Sender*
        sender;

        Receiver*
        receiver;
};

#endif // COMMAND_H

CommandResolver:
Code: [Select]
#include "CommandResolver.h"


void
CommandResolver::
addCommand(Command< Sender, Receiver >*)
{
   
}

void
CommandResolver::
resolveCommands()
{
   
}

Game:
Code: [Select]
#include "Game.h"

Game::
Game()
{
}

void
Game::
attachMode( Mode* mode )
{
    this->attachNode( mode );
}

void
Game::
updateThis( sf::Time dt )
{

}

Mode:
Code: [Select]
#include "Mode.h"

Mode::
Mode()
{
}

void
Mode::
updateThis( sf::Time dt )
{
    for ( Node* child : children )
    {
        Stage* stage = dynamic_cast<Stage*>( child );
        if ( stage )
        {
            stage->draw( target, states );
            stage->play();
        }
    }
}

Node:
Code: [Select]
#include "Node.h"
#include <algorithm>
#include <cassert>
#include <queue>

void
Node::
attachNode( Node* child )
{
    children.push_back( child );
}

Node*
Node::
detachNode( const Node& child )
{
    auto found = std::find_if
    (
        children.begin(),
        children.end(),
        [&] ( Node* p ) -> bool { return p == &child; }
    );
    assert( found != children.end() );

    Node* result = found;
    children.erase( found );
    return result;
}

void
Node::
queueCommand( Command* command )
{
    commands.push( command );
}

void
Node::
update( sf::Time dt )
{
    for( int i = 0; i < commands.size(); i++ )
    {
        commands.front()->operation( this, dt );
        commands.pop();
    }
    updateThis(dt);
    for( const auto& child : children )
        child->update(dt);
}

Renderable:
Code: [Select]
#include "Renderable.h"
#include <SFML/Graphics/Transform.hpp>
#include <SFML/Graphics/RenderStates.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <memory>


void
Renderable::
draw( sf::RenderTarget& target, sf::RenderStates states ) const
{
    transformFromRoot = states.transform *= getTransform();
    drawThis(target, states);
    for ( Node* child : children )
    {
        Renderable* renderable = dynamic_cast<Renderable*>( child );
        if ( renderable )
            renderable->draw( target, states );
    }
}

sf::Vector2f
Renderable::
getDisplacementFromRoot() const
{
    return transformFromRoot * sf::Vector2f();
}

Resources:
Code: [Select]
#ifndef RESOURCES_H_INCLUDED
#define RESOURCES_H_INCLUDED

#include <map>
#include <memory>
#include <cassert>
#include <stdexcept>


/** \brief A template for creating resource management classes.
 *
 *  The resources are handled with shared pointers.
 *  It is possible to keep a resource in memory, even if no shared pointers exist which point to it, by using member keep.
 *  Unless a resource is kept, it will be reloaded distinctly every time it is requested, which can waste memory if there is no intention of modifying the data of that resource.
 *  Therefore using the shared pointer returned by the get member to generate more shared pointers is recommended if the data for the resource does not need to be instanced.
 *  The target user of this class template is other classes, particularly constructors which can make a copy the shared pointer provided by the member get.
 *
 * \param Resource defines the type of the resource to be loaded.
 * \param Identifier defines an alias by which the resource can be referred to.
 *
 */

template < typename Resource, typename Identifier >
class
Resources
{
public:
    void
    keep( Identifier id );

    template < typename Parameter >
    void
    keep( Identifier id, Parameter secondParam );

    void
    unkeep( Identifier id );

    std::shared_ptr< Resource >
    get( Identifier id );

    const std::shared_ptr< Resource >
    get( Identifier id ) const;

    template < typename Parameter >
    std::shared_ptr< Resource >
    get( Identifier id, Parameter secondParam );

    template < typename Parameter >
    const std::shared_ptr< Resource >
    get( Identifier id, Parameter secondParam ) const;

    void
    file( Identifier id, std::string filename );
private:
    std::map< Identifier, std::shared_ptr<Resource> >
    memory;

    std::map< Identifier, std::string filename >
    disk;

    std::shared_ptr< Resource >
    load( Identifier id );

    template < typename Parameter >
    std::shared_ptr< Resource >
    load( Identifier id, const Parameter& secondParam );
};

/** \brief Keeps the resource in memory so that it does not have to be reloaded from the disk when requested even if the last shared pointer to it was previously deleted.
 *
 */

template < typename Resource, typename Identifier >
void
Resources < Resource, Identifier >::
keep( Identifier id )
{
    // do nothing if the resource is already kept, otherwise load into it into memory and store a shared pointer to it
    auto found = memory.find( id );
    if( found != memory.end() )
        return;
    else
        memory[ id ] = load( id );
}

/** \brief Identical to the overloaded method of the same name but additionally passes a second parameter to the loadFromFile member of the resource being loaded.
 *
 */

template < typename Resource, typename Identifier >
template < typename Parameter >
void
Resources < Resource, Identifier >::
keep( Identifier id, Parameter secondParam )
{
    // do nothing if the resource is already kept, otherwise load into it into memory and store a shared pointer to it
    auto found = memory.find( id );
    if( found != memory.end() )
        return;
    else
        memory[ id ] = load( id, secondParam );
}

/** \brief Undoes keep.
 *
 */

template < typename Resource, typename Identifier >
void
Resources < Resource, Identifier >::
unkeep( Identifier id )
{
    // do nothing if the resource is not kept, otherwise remove from memory the shared pointer to it
    auto found = memory.find( id );
    if( found != memory.end() )
        return;
    else
        memory.erase( id );
}

/** \brief Acquires a pointer to the specified resource.
 *
 *  Loads a resource of a type specified by the class template parameter Resource.
 *  The resource must implement a loadFromFile member that takes a string filename as an argument.
 *  The Identifier class template parameter is used to alias the resources, so that they can be referred to and specified efficiently.
 *  Enum class or enum are good identifiers for most purposes, for example.
 *
 *  If the shared pointer is in memory this member will return it.
 *  Otherwise it will load the resource from file and return a brand new shared pointer without storing a copy of the pointer in memory.
 *  Unless an order to keep the resource in memory is made, the resource will be removed from memory the moment the last shared pointer to it is deleted.
 *
 * \param The alias of the resource being requested is id.
 * \return Returns a shared pointer to the specified resource.
 *
 */
template < typename Resource, typename Identifier >
std::shared_ptr< Resource >
Resources < Resource, Identifier >::
get( Identifier id )
{
    auto found = memory.find( id );
    if( found != memory.end() )
        return memory[id];
    else
    {
        // if this fails then a resource was requested without defining where it resides on disk
        found = disk.find( id );
        assert( found != disk.end() );

        return load( id );
    }
}

 /** \brief Identical to the overloaded method of the same name but passes a second parameter to the loadFromFile member of the resource being loaded.
  *
  *  The type of the additional parameter is provided in the function template parameter labeled Parameter.
  *  the value of the additional parameter is provided as an argument labeled secondParam.
  *
  */

template < typename Resource, typename Identifier >
template < typename Parameter >
std::shared_ptr< Resource >
Resources < Resource, Identifier >::
get( Identifier id, secondParam )
{
    auto found = memory.find( id );
    if( found != memory.end() )
        return memory[id];
    else
    {
        // if this fails then a resource was requested without defining where it resides on disk
        found = disk.find( id );
        assert( found != disk.end() );

        return load <Parameter>( id, secondParam );
    }
}

/** \brief Identical to the overloaded method of the same name but with different qualifiers
 *
 */

template < typename Resource, typename Identifier >
std::shared_ptr< Resource >
Resources < Resource, Identifier >::
get( Identifier id ) const
{
    auto found = memory.find( id );
    if( found != memory.end() )
        return memory[id];
    else
    {
        // if this fails then a resource was requested without defining where it resides on disk
        found = disk.find( id );
        assert( found != disk.end() );

        return load( id );
    }
}

/** \brief Identical to the overloaded method of the same name but with different qualifiers
 *
 */

template < typename Resource, typename Identifier >
template < typename Parameter >
std::shared_ptr< Resource >
Resources < Resource, Identifier >::
get( Identifier id, secondParam ) const
{
    auto found = memory.find( id );
    if( found != memory.end() )
        return memory[id];
    else
    {
        // if this fails then a resource was requested without defining where it resides on disk
        found = disk.find( id );
        assert( found != disk.end() );

        return load<Parameter>( id, secondParam );
    }
}

template < typename Resource, typename Identifier >
std::shared_ptr< Resource >
Resources < Resource, Identifier >::
load( Identifier id )
{
    std::shared_ptr< Resource > resource( new Resource() );

    // if this fails then a resource was requested that is not accessible on disk
    if( not resource->loadFromFile( disk[id] ) )
        throw std::runtime_error( "Resources::load - Failed to load " + disk[id] );
}

// Specifically passes a second parameter of type Parameter to resource->loadFromFile
// the type of the resource is provided in the class template and must implement two argument loadFromFile
template < typename Resource, typename Identifier >
template < typename Parameter >
std::shared_ptr< Resource >
Resources < Resource, Identifier >::
load( Identifier id, const Parameter& secondParam )
{
    std::shared_ptr< Resource > resource( new Resource() );

    // if this fails then a resource was requested that is not accessible on disk
    if( not resource->loadFromFile( disk[id], secondParam ) )
        throw std::runtime_error( "Resources::load - Failed to load " + filename );
}

template < typename Resource, typename Identifier >
void
Resources < Resource, Identifier >::
file( Identifier id, std::string filename )
{
    disk[id] = filename;
}
#endif // RESOURCES_H_INCLUDED


Stage:
Code: [Select]
#include "Stage.h"
#include <SFML/Graphics/RenderStates.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

Stage::
Stage()
{
}

void
Stage::
attachBackground( Background* background )
{
    this->attachNode( background );
}

void
Stage::
attachCollidable( Collidable* collidable )
{
    this->attachNode( collidable );
}

void
Stage::
updateThis( sf::Time dt )
{

}

void
Stage::
drawThis( sf::RenderTarget& target, sf::RenderStates states ) const
{

}

void
Stage::
playThis() const
{

}

Vessel:
Code: [Select]
#include "Vessel.h"
#include <SFML/Graphics/RenderStates.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

Vessel::Vessel()
{
}

void
Vessel::
updateThis( sf::Time dt )
{

}

void
Vessel::
drawThis( sf::RenderTarget& target, sf::RenderStates states ) const
{

}

void
Vessel::
playThis() const
{

}




Explanation:

One major design criteria is the amount of effort required to make large structural changes to the game. I would strongly prefer to have a design that allows me to make sweeping structural changes with little effort. One way to do this is to use a sceneGraph type pattern to ensure that the structure of the game is reflected by a data structure, and is the pattern I have chosen to adapt.

A concrete example of what I mean follows. From a top down point of view I want to be able to erect the framework for a game by saying something like the following:

Code: [Select]
Root root = new Root();
Game shmup = new Game( /* parameters */ );
Mode singlePlayer = new Mode();
Mode pause = new Mode();
Vessel player = new Vessel();
/* etc */

root.attachGame( shmup );
shmup.attachMode( singlePlayer );
singlePlayer.attachStage( firstStage );
firstStage.attachBackground( space );
background.attachSpecialEffects( twinklingStars);
firstStage.attachCollidable( enemy );
firstStage.attachCollidable( player );
player.attachTurret( laser );
player.attachTurret( missiles );
/* more stuff for the first stage */
singlePlayer.attachStage( secondStage );
/* stuff for the second stage */
/* etc */
shmup.attachMode( pause );
pause.attachStage( pauseMainScreen );
statScreen.attachBackground( halfTransparent );
statScreen.attachCollidable( cursor );
statScreen.attachCollidable( inventoryTab );
statScreen.attachCollidable( questsTab );
/* etc */
statScreen.attachCollidable( unpauseButton);
pause.attachStage( pauseInventoryScreen );
/* stuff for the inventory screen */
pause.attachStage( pauseQuestsScreen );
/* stuff for the quests screen */
/* and so on */

Then I would simple call update on the root node with root.update().

The update will propagate through the child nodes as with most scene graphs. Unlike other implementations, in my design the entire functionality of the game must be captured by the structure of the sceneGraph and the messages which are passed between the elements within the graph. Messages in my game are simply commands.

A command can be followed or it can be ignored. It is up to the receiver of the command to ignore the message or follow the message. For example, a bomb may damage everything in an area so it broadcasts a command to all nearby entities by pushing those commands into the entity with addCommand( blowUp ). If an player is nearby and recently stepped on spikes and is invulnerable, it would simply ignore the command explicitly since it knows about this command by name.

One point of fair criticism is that the player entity would have to know about this command by name, and so it defeats the point of encapsulation. However, the underlying player class itself is still encapsulated and so is the bomb. The command itself is defined using functional members that belong to the player, namely takeDamage( whatever ). So encapsulation is actually preserved, but the structure of the relationships between the classes are defined by HOW the commands are used.

By ensuring that the structure of the relationships between all the entities in the game isn't hard coded by inheritance or object oriented methodologies, I can quickly alter those relationships by changing only a few lines of code-- namely what command is being broadcast or ignored and under what condition. The design cost is that I must predefine what commands exist-- in effect a command becomes an atomic entity that serves a syntactic purpose, like a basic operation.

This has a tradeoff. I gain the ability to define the language in which i want to "explain" to the compiler what my game is and what its rules are, the language's tokens are the names of the commands. I lose readability because anyone reading the program must also understand the new language I have created.



Design Revisions:

None yet!

Pages: [1] 2