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.


Messages - Critkeeper

Pages: 1 2 [3] 4 5
31
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

32
Feature requests / Re: Can we get collision detection in SFML?
« on: March 12, 2015, 10:05:45 pm »
I think I understand what you are saying, so let me state what I meant, rather than what I typed.

By "collision detection" all I meant was that there should be something like a flag on an sfml sprite / shape / anything that has a bounding box that tells me whether it is intersecting with anything else that has a box/shape/area, if I pass a flag to the shapes/boxes involved telling them to "turn on collision detection for this entity" in the constructor.

That doesn't really complicate the API at all and the performance penalty under the hood for managing the intersection system is opt-in.

This has lots of applications outside of games because as you said, it is just intersection detection.

Currently sfml intersection detection requires you to query for an intersection between two provided shapes. What I am suggesting  is that querying for intersection is slow and can often be complicated if the entities in question are not localized to a single data structure, and it would be simpler if "intersection" were a property rather than a return value. In other words, the shape/sprite/whatever just "magically knows" whether it is intersecting or not, because work is being done behind the scene to update that property based on all other registered shapes/boxes/whatevers.

The user doesn't have to know how the shapes/boxes/whatevers are implemented underneath and inside SFML, only SFML does has to know that. And SFML can exploit that knowledge to provide more efficient persistent intersection detection that the user could. Boxes are pretty transparent, but curves or arbitrary polygons are not necessarily.

Under the hood this allows SFML to use temporal coherence for efficiency. Temporal coherence is the property that two intersecting shapes will likely be intersecting next frame, and two non-intersecting shapes will likely be not intersecting next frame. A query based approach does not exploit temporal coherence-- you have to keep querying whether the two shapes intersect over and over in order to determine when the shapes stop intersecting or start intersecting.

A persistent collision system can provide a guarantee that the intersection state will remain valid as long as the API is used to move / transform / translate / rotate the entities involved. If an intersection occurs then the system can record information for each type of transformation that determines what transformations (and which arguments) could cause the intersection state to change. For example, if a box is wholly contained within another box, then rotation and stretching about their centers cannot alter the intersection state. But translating could. The intersection is compute only when the intersection state is set, and until it is cleared the data stored and modified is the information pertaining to what has to happen to change the state. This is more abstract an idea than AABB and the implementation can be extended to arbitrarily complicated polygons and curves while remaining invisible to the user.

The benefit of offering something like this through SFML is that collision detection systems of choice can be built on top of it. For example, to get sweeping collision detection for bullets you can create a new sfml::shape who's outline is the outline of the swept out area of the bullet along its trajectory. It is totally up to you how you want to determine or compute that swept out shape, but the intersection part of the problem is solved on the SFML side, rather than the application side.

So yes, I am saying you should provide half of the picture so that I can paint the other half so and I don't think that is greedy or bad or lazy or anything but sane.

There is no reason that intersection HAS to be a query. Query is just one tool in the software engineering toolkit. It isn't the best one for intersection testing for most multimedia applications in my honest opinion-- a persistent state model would be better.

33
Feature requests / Re: Can we get collision detection in SFML?
« on: March 12, 2015, 08:08:00 pm »
automatic AABB 2d collision on any "rect2d" isn't too much to ask for and has applications outside of games.

34
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?

35
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.

36
If you need speed you could do a geometric approach.

Just treat the map the character walks on as a plane. The range of the movement corresponds to the height of a point above the plane (in the 3rd dimension).

The point coincides with the tip of a cone, and the code passes through the plane so that the intersection is a filled circle.

Its like shining a light from a point onto a map.

If you want to restrict movement based on occupied squares, that is tantamount to putting an object in front of the light to darken certain squares.

So far the algorithim is just creating a circle and seeing if the center of the square exists inside the circle or not, and subtracting some squares afterward based on whether not it is already occupied.

This isn't yet accurate though. Imagine creating a set of possible squares that you can move to, and then without any further checks you would just be able to move from the center to near the edge, then double back and go through the center to the other side for example-- that clearly isn't what you want.

If you employed this algorithm as is you'd see strange defects like not having the range to move all the way to the bend of a U shaped hall just yet, but as you move close enough to make it to the bend, all of a sudden you have the range to go all the way to the other end of the hall!

If the range is 1 then the algorithm as is is 100% accurate.

If the range is 2 or more then the algorithm is only 100% accurate if there is no blocking objects.

So essentially an object in one square may block movement in one or more squares, by preventing you from reaching them because you have to weave around in order to get to it and you don't have the range to do that.

So the trick is to devise an algorithm that takes as its input:

the set of all squares ( a filled circle based on your range )
a subset that set-- the set of all blocking squares ( individual squares blocked by terrain or units )
a square at which the character is located (not really, since ranged based movment in the case for your game is figured as a circle and a circle has one unique center)

As output it produces a single set:

 a superset of the set of blocking squares which is also a subset of the set of all squares-- it represents the squares that cannot be reached.


And you just take that output and negate it with the set of all squares to produce the set of squares your character CAN reach.




A square can either be blocking or non blocking. The area of a circle is proportional to the area of a square, for some proportionality constant. That means that the total number of squares is proportional to n^2, where n is the number of squares on one side-- in other words it is proportional to the radius of the circle.

Each square is either blocked or non blocked, so there are 2^s combinations, where s is the number of squares.

Putting that together, there is X = 2^(K*n^2) combinations, where K is roughly equal to 3.141592... divided by 4 and n is the radius of the circle.

This is a continous function, and you have a discrete problem, so keep in mind this is just an approximation that only becomes very accurate as n increases. It still gives a good rough estimate for n in the neighboorhood of half a dozen or so.

At very low values the continuos function is an innaccurate approximation for two reasons. First, it cannot approximate the discrete nature until there are sufficiently many squares to bring it closer to its limiting behaviour. Second, a "range 1" indicates that you have access only to the square you are in, and that you can only move to that square. A "range 2" indicates you can move to 5 squares-- the one you are in or the 4 adjacent to it. A "range 3" means you can move to a total of 8 different squares, or perhaps 12. The point is that going from range 1 to range 2 is a quadrupling, and going from range 2 to range 3 is less than a quadrupling.

We already know the function for the number of squares goes as n^2, so this major discrepancy between the continous model and the discrete reality is excarcebated by the differences at low numbers. Fortunately for numbers greater than 3 or 4 it becomes a decent approximation.

For n = 4.5 ( diameter of 9 squares, so a range of 5 if you count range of 1 as being able to move to only the square you are currently in )

X = 61330

This makes it small enough to precompute a comprehensive table for range 5 movement. You can just apply the table twice to do range 10 movement. Or 3 times for range 15.

If the table is too large and you want to trade memory size for number of times spent accessing the table, you can just use n = 3.5 or n = 2.5.

If you want a more accurate number then make a pixel circle by hand and count the number of pixels. Now raise 2 to that power. For range 3 (by my way of counting range) it is 13, so 2^13 = 8192

37
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?

38
General / Re: Putting my game through a public design review.
« on: February 10, 2015, 02:31:49 am »
Thankyou both for your contributions. I will change my schedule to set aside a couple of hours each day for the next couple of weeks in order to read the Optank and watch the Handmade hero and I am certain I will learn many new things that will invigorate me and help me to self-review with new insights.

39
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!

40
Thankyou very much Laurent I didn't think of shadowing!

As for a design / philosophical point of view I'm open to any criticism! Criticism breeds better code.

EDIT:
The RTTI is something I've never seen before, but after careful inspection it looks like this is exactly what I want. Issue solved!

41
At runtime.

Please read before commenting, the problem is very subtle (Its not as simple as using an assert typeid )

I have made my own implementation of a scene graph and I have it so that you can attach children nodes to any other node in the graph as normal. What is special in my implementation is this:

you can attach a child node to a parent node only if the child's type is derived from the parent or is the same as the parent.

In order to understand why I have this restriction, consider what happens when a Node calls its update() in a Scene Graph. It must call its own (virtual) updateThis() and then walk through the graph and update each child, so each child must derive from type Node.

Now consider I want to create a brand new KIND of node called "Renderable". Renderable has draw() and virtual drawThis(). Renderable is a type of Node, so it can call update() and has virtual updateThis(). The problem comes from void attachChild( Node* child ). If I attach a simple Node it will compile but when I call draw() on the highest node of type Renderable, and it tries to drawThis() on a child of type Node, it will obviously crash. I have to ensure that all its children are of type Renderable, rather than just type Node if I mandate that draw() propagates to every child.

That is the dillemma. I want draw() to propagate to every child.

I'm using this pattern heavily. I have an Audible class with a play() and virtual playThis() method which does what you expect.

And I have multiple (virtual) inheritance to create classes which define objects that can be updated, can be drawn, and can be heard. For example, a background can be drawn and updated, a vessel can be drawn and updated and heard, and a stage cannot be drawn or heard but it can be updated. In order to make the stage "visible" you add a background to it by attachChild( Background* ), where Background is derived from Node* ( and happens to derive from Renderable as well ).

This scheme gives me a lot of flexibility but it is type unsafe. I have to excersise caution and due diligence to ensure that I do not try to static_cast a Node to an Audible for example. From a design / philosophical point of view I do this by mandating that every child of something that can be heard, must also be something that can be heard. Every child of something that can be seen must also be something that can be seen. Every child of something that can be updated must also be something that can be updated.

First let me say that IT WORKS if I am careful and I ensure that every child is "derived" enough. But the problem is how do I tell the compiler to warn me when it isn't? And what about run time safety? I want to compiler to tell me when I broke my own rules, so I need a way to tell the compiler what my rules are.

Its not as easy as it seems to solve the type safety problem. You would think that C++ gives you tools to check the type and make an error if the type isn't correct, but there are various barriers that prevent me from using the standard tools:

I can't use assert ( typeid( this* ) == typeid( argumentToAttachChild ) ) because the most derived type is always used, and according to the documentation these expressions will not compare equal unless the types are exactly the same-- a derived type will not compare equal to a type it derives from. Assert also doesn't give me run time safety.

I can't simply override the attachChild( Node* ) with attachChild( Renderable* ) in the class definition for Renderable because it creates ambiguity since every Renderable is a Node and technically the signatures of these functions are different so its not really an override.

I can't use the curiously recurring template pattern because it is possible to attach a Renderable to a Node.

I can't figure out how to make this more safe. It works if used correctly, and it is quite powerful, but I can't figure out how to make it safe.

Also keep in mind that this "rule" only applies to what I term "propagating" node subtypes. A propagating node subtype is one that has a operate() and virtual operateThis(). A turret isn't necessarily a vessel even though it can be attached as a child of a vessel for example. But the fact that a Vessel is renderable mandates that a Turret is renderable.

So the title to this question and the previous statement "you can attach a child node to a parent node only if the child's type is derived from the parent or is the same as the parent" isn't quite accurate. Through multiple inheritance an object is composed of an agglomeration of different types, so I want to ensure that only the "propagating" types actually propagate through the sceneGraph.

42
General / Re: A question about shared_ptr (not strictly sfml specific)
« on: January 16, 2015, 01:11:45 am »
Hope that helps.

It does, thanks. I've already come up with a solution not much different than the one you are suggesting though. I'm going to have a ResourceHolder class that has 3 methods.

shared_ptr< resource > get( id )
void keep( id )
void unkeep( id )

it has 2 private members

map< id, string filename > disk
map< id, shared_ptr< resource > > memory

when asked to get( id ) the object will look inside of memory to see whether or not an id with the requested value exists, and if it does it will return a shared_ptr with the same value. If not, it will load from memory the resource requested using the information in the member disk, and it will return a shared_ptr to that resource as the return value of get( id ). It will NOT store a second shared_ptr in the member memory.

In order to manually tell the object to not release a resource from memory you must tell it to keep it in memory using keep( id ), which just creates an (id, shared_ptr) pair inside of the map member memory. Unkeep undoes the operation.

Does the theory of this look sound, or is there something I might be missing?

43
General / A question about shared_ptr (not strictly sfml specific)
« on: January 15, 2015, 01:45:08 am »
I had this idea bout using shared_ptr to keep a single object alive while multiple pointers point to it, namely resource type objects like textures and sound effects and even ai scripts.

Imagine having a arcade shooter scene and you have a bunch of the same kind of vessel floating around. With shared_ptr they can all access the same resources, the sprite, the sound effects, etc, for that specific kind of vessel. Thats great, and thats what we want.

And when the last vessel of a particular kind is destroyed and we don't see another of that type for 5 minutes, then it is unloaded from memory. Thats fine too.

What if we are in a situation where we require a single pointer to particular resource and no more, intermittently over a period of time? For example if we have one vessel of a particular type that requires access to a particular set of resources, textures, sounds, etc, and we only ever have one in the scene at a time, but every time it is destroyed it respawns after a second or so. It would be wasteful to unload and reload it from memory over and over.

So in order to get around that we would want to tell the shared_ptr implementation to not delete the resource object when it hits a count of zero referring vessel objects (or preferably, to just increment the count by 1 and change nothing else). However, I cannot access the reference count directly in order to modify it-- its a private member of the shared_ptr implementation. The only recourse I can think of is to create "dummy" pointers that are only used to keep the count one higher than it would otherwise be, and therefore keep the resources in memory so that they do not need to be reloaded.

Seems ugly though. A person reading code who sees a pointer expects it to be used for something other than keeping the reference count of a shared pointer 1 higher than it would otherwise be. Is there a better way to do this that is no more complicated?

EDIT:

As I think about it though, if there are no references to the object how can i know how to dereference it in the first place? I guess the "dummy" pointer would really be a kind of codeless pointer factory.

44
I'm sorry for wasting your time then I will study up on it. I knew I was missing something fundamental. Seems kind of sad I can have had a year of software engineering and never have been exposed to the const keyword used in that manner.

In the meantime so I know in what general direction to focus my effort, I am not wrong in assuming that I can store a Transform, right? I mean, it seems silly to think that I can't, even if doing it the way I tried to do it is erroneous.

45
Yes it is a member declared privately as:

Code: [Select]
sf::Transform absoluteTransform;
And you are correct I am trying to define SFML's draw for a specific type of scene node.

Pages: 1 2 [3] 4 5
anything