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

Author Topic: New to SFML, game design questions  (Read 15513 times)

0 Members and 1 Guest are viewing this topic.

Sync Views

  • Newbie
  • *
  • Posts: 12
    • View Profile
New to SFML, game design questions
« Reply #15 on: September 05, 2011, 12:46:05 pm »
I spent soem time looking at creating this "Renderer" class, however Ive run into some issue that I'm  not sure on how to solve, and I couldnt find any existing code that uses such a Renderer class to see exactly what people are suggesting :(

My assumption was the the intention is for Paddle, Ball, etc, to not contain an sf::Sprite or similar graphics member objects. This works fine for Pong, although I had to add in some "magic" number for width,height,etc of some objects, but in a real game I suspect Id put more detailed collision info in some kind of data file anyway.

The issue I ran into here is when one object may have one of many different images. E.g. a "Ship" class may use one of fighter.png, frigate.png, bomber.png, heavy_fighter.png, etc, etc, but each of these different ships is still only a Ship class (the difference coming from the data itself thats externally defined). I considered creating some kind of "enum ShipImage", but that idea seems even worse than the Ship having an sf::Sprite in the first place....

Also I was thinking of how to remove code duplication on the interpolation front by perhaps having a common base class to hold the pos, lastPos, angle, lastAngle, etc. members (as protected, with public getters) so the renderer could have for example "sf::Vectro2f Renderer::interpolatePosition(const Object &obj)". I guess I could also make interpolatePosition a template method where each object using interpolation just has to provide a suitable getPos and getLastPos, but I'm not sure thats any cleaner than a common base class and it still creates duplication in the generated code due to the nature of templates?


The other issue I ran into was how best to handle HUD rendering. Would the best way simply be to have some HUD class that renders itself (since HUD isnt really doing any logic in the first place, I guess this is ok?)?

Joshua Flynn

  • Full Member
  • ***
  • Posts: 133
    • View Profile
New to SFML, game design questions
« Reply #16 on: September 05, 2011, 03:10:12 pm »
Not sure if it's any use, but for programming organisation, I make use of 'stackable' classes, that is, classes that build on top of each other, with the higher class offering higher functionality for the lower classes, and combining with other classes to offer unique functionality, and eventual ease-of-use (because the higher class must preferrably automate the lower classes functions).

For example, I have five classes:

CharList (a dynamic list of chars)
DataArray (a dynamic array of chars)
FileProc (a simplified and safe file interface class)
HTMLFileProc (an automated interface to SFML's HTML functionality)
StringTokeniser (a simplified and safe StringTokeniser class)

And this is the part that is brilliant:

FileProc -> CharList/DataArray
HTMLFileProc -> DataArray
CharList/DataArray -> char string
CharList/DataArray/char string -> StringTokeniser
StringTokeniser -> char string
char string/CharList/DataArray -> FileProc

In short, you can combine the classes to work with each other, theoretically in any order (the exception being the HTMLFileProc given it's only a one way process).

But these are just a few. There's the advanced classes built on top of these (FileProcAdvanced, StringTokeniserAdvanced, FileProcModular, DataArrayDynamic etc etc). Downloading an image from a website with the right classes is literally four commands, all conducted by the assignment operator and clever C++ trickery.

So to save yourself time writing out functions, you might want to consider combining the 'Draw' function, with a class and the assigment (=) operator so you could just do something like:

Code: [Select]
RenderProc Renderer;
sf::Image BasicImage;
sf::Image ImageArr[10];
YourOwnImageClass TestImage;

Renderer = BasicImage;
Renderer = ImageArr;
Renderer = TestImage;


Or, you might want to be snazzier, and do something like this:

Code: [Select]
RenderProc: Renderer;
ImageStack Queue1, Queue2; //Dynamic list of images class

sf::Image BasicImage;
sf::Image ImageArr[10];

YourOwnImageClass TestImage;
sf::Image OtherImage;

Queue1 = BasicImage;
Queue1 = ImageArr;

Queue2 = TestImage;
Queue2 = OtherImage;

Renderer = Queue1;
Renderer = Queue2;


Seems cool to be able to do just that with the assignment operator doesn't it? Believe me it's entirely possible, only limit is imagination and technical ability.

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
New to SFML, game design questions
« Reply #17 on: September 05, 2011, 07:54:47 pm »
Quote from: "Sync Views"
I considered creating some kind of "enum ShipImage", but that idea seems even worse than the Ship having an sf::Sprite in the first place....


Breaking things down like this isn't an uncommon way to go about things ^^ Most programs can be broken down to databases at the end of the day, it's not an uncommon or ineffective starting point for designs by any means to treat them as such.
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

Sync Views

  • Newbie
  • *
  • Posts: 12
    • View Profile
New to SFML, game design questions
« Reply #18 on: September 05, 2011, 09:19:34 pm »
Well perhaps, but it also creates a hardcoded data component. Say I want to create a fighter variation and made a fighterb.xml, fighterb.png, etc. But wait I cant, because ShipImage enum has no value for a fighterb.png, I need to change the program... I guess I could store the "fighterb.png" string in my object, but thats a string search on every single ship instance every single frame...

I could also use an id/handle system, but that seems no better than an sf::Texture* or whatever... (or sf::Sprite*, if there is some significant performance hit from constantly "reusing" a single sf::Sprite for each item rendered?)

Code: [Select]

struct ShipType
{
    float turnRate;
    float maxSpeed;
    WeaponMounts weapons;
    ...everything else describing a given ship...
    sf::Sprite spr;///?
    sf::Texture *tex;///better and reuse an internal sf::Sprite for each draw.
    std::string imageFile;//and some string lookup on each render? Performance?
    int imageId;//allocated in some way with some kind of lookup? How is that better than a spr/*tex ?
};
class Ship
{
...
    const ShipType *GetType(){return type;}
private:
    ShipType *type;//Each ship instance uses one of the described ship types to define its mount points, accelleration, turn rate, etc
};

For both *img and spr it would be obtained at load time from the render system with some kind of LoadGraphicsInfo function (I'm also assuming SFML doesnt do automatic texture caching if I was to load the foo.png 10 times), and only really used by the graphical components. I guess I could put it in a "RenderInfo" child struct as well? Perhaps even private fields where the rendering class is a friend?

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
New to SFML, game design questions
« Reply #19 on: September 05, 2011, 10:08:55 pm »
You could use an id system or a flat pointer or some sort of iterator-reference, either way basically the same system and for efficiency sake the pointer or iterator to a ShipType object works better.

As for multiple sprites, well if you change one sprite you change it for all the  sprites so a local copy for each ship probably would work better. And since the sprite contains the texture, you could have the texture in the "ShipType" too or even hide it behind some "CreateSprite()" function that returns the default sprite for that ship type.

Of course that approach does spread SFML's code into multiple places around your program and whilst that's not always a bad thing, it does mean a significant change to the SFML API mid-way through development could be a bit annoying to patch so there is the argument for ease-of-management. Also it'll be more effort to replace in case suddenly SFML isn't good enough for you ^^

You could give each class it's own "Position", "Angle" etc. properties to use to figure out the sf::Sprite and draw it in the renderer.

It's a matter of what you think the ship needs to know and how you think it needs to know it. Though I'd have a constant reference to the ShipType object or hide all it's members behind "Get_X()" functions or have them all internally constant, not a flat pointer, simply so it's clear the information about different ShipTypes cannot be changed by the Ship.

At the end of the day, the best advice in coding is: do what works.
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

Sync Views

  • Newbie
  • *
  • Posts: 12
    • View Profile
New to SFML, game design questions
« Reply #20 on: September 05, 2011, 11:23:54 pm »
Well I think since there only there in the structs/classes for "storage", but created and used by rendering components, even somthing dramatic like going to straight OpenGL wouldnt be to much of a nightmare, apart from the doing the rendering with OpenGL part :) Definatly seemign better right now than trying to make some kind of seperate storage space with some kind of uid lookup (be it string, int, enum, etc).

The graphics code can then also take care of texture caching and lifespan (refcounting I guess, and am I also right in thinking sfml doesnt auto cache images and textures internally?).

I'm still not sure on the details of sf::Sprite. Can I just use a single sprite object to render the entire game, changing it's texture as required, or does that incur some large performance penalty I might not be noticing in small tests? I put some small pieces of code below on the ideas I was thinking.


So the only other major issue I have right now is the renderer getting the positional information for an instance and carrying out the interpolation. A common base class seems the most straight forward since I can have a "sf::Vector2f GetCurrentPosition(const Object &obj, float dt);float GetCurrentAngle(const Object &obj, float dt); " type methods, but I'm not sure I really want a common base class for every single visibile object in the game... The template solution doesnt really seem a whole lot better, although I guess it reduces source code duplication in some cases... I certainly don't want to type out the same interpolation code (likly with the same bugs :) ) for every single moving object,

Code: [Select]

//Idea A
void Renderer::Draw(const Ship *ship)
{
    sf::Vector2f pos = CalcCurrentPos(ship);
    float angle = CalcCurrentAngle(ship);
   
    spr.SetPosition(pos);
    spr.SetRotation(angle);
    spr.SetTexture(ship->GetType()->tex);
    spr.SetOrigin(ship->GetType()->texOrigin);
    //...anything else I decide to add...
    DrawSpr();
}
//Idea B
void Renderer::Draw(Ship &ship)
{
    sf::Vector2f pos = CalcCurrentPos(ship);
    float angle = CalcCurrentAngle(ship);
    //Ship and GetType cant be const for this, unless I use a const casting hack...
    sf::Sprite *spr = ship->GetType()->spr;
    spr->SetPosition(pos);
    spr->SetRotation(angle);
    DrawSpr(spr);
}

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
New to SFML, game design questions
« Reply #21 on: September 06, 2011, 12:06:32 am »
Switching texture would most likely not cause a hit,, or at least I can't think of a reason it would.

Changing things like position and rotation maaaybe? Depends how SFML implements them.

I'd have to delve into the way sprites work but these changes would most likely require the sprite's transformation matrix to be rebuilt and using multiple sprites could limit that penalty to only when each object changes position/rotation/etc. instead of for every drawing always no matter what, assuming the matrix is only rebuilt when changes are made.

When keeping a local copy of the sprite, the "worst case" scenario (everything moving every update) wouldn't be any different but if you have a tick with something stationary, then there may be extra and unneeded work being done there. But since you're gonna be at that worst case scenario or close to it most of the time, probably not be something you notice.

Of course it could be said reusing the Sprite like that defeats the 'purpose' of the Sprite class, which I believe is for defining separate "entities" in the worldspace ^^
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

Sync Views

  • Newbie
  • *
  • Posts: 12
    • View Profile
New to SFML, game design questions
« Reply #22 on: September 06, 2011, 01:49:58 am »
Quote from: "MorleyDev"

Of course it could be said reusing the Sprite like that defeats the 'purpose' of the Sprite class, which I believe is for defining separate "entities" in the worldspace ^^


Well how is it "designed" to be used, given it has already been established that using its position/rotation for logic purposes isnt a great idea, and is not possible with interpolation anyway, leaving position/rotation to be set at render time (and therefore as far as I can tell, loosing pretty much any benefit I can see of a per world object instance sf::Sprite except possible matrix benefits which I guess is possibly useful for static objects, but is still a fairly cheap part of rendering)? Even with some depth sorting scheme (which I still havnt really solved and tested) I'm not sure how a per-instance sf::Sprite helps? Unless perhaps position setting (on a per frame bases to interpolate) and rendering are performed as two seperate steps, but again I see no benefit to do this, just extra loop overhead?

If SFML was providing some kind of scene structure that it then rendered in the most optimal way (i.e. avoiding uneeded state changes, batching alike objects where the Z order allows) via some kind of RenderWindow::RenderEverything or somthing I could understand, but SFML doesnt seem to provide that functionalty?

MorleyDev

  • Full Member
  • ***
  • Posts: 219
  • "It is not enough for code to work."
    • View Profile
    • http://www.morleydev.co.uk/
New to SFML, game design questions
« Reply #23 on: September 06, 2011, 02:31:32 am »
SFML does not provide a scene graph. It's a 2D rendering library, any optimisation a complex scene graph can provide is going to be nothing short of overkill in 99% of cases and may even introduce unneeded overhead in that 99%.

Though don't quote me on this but I believe Laurent has been trying to find a "Sprite Batch" design that satisfies him and renders sprites in batches in a more optimal manner.

Basically instead of
Window.Draw(Sprite1);
Window.Draw(Sprite2);
Window.Draw(Sprite3);
Window.Display();

You could do something like:

Window.Begin(SpriteBatch);
SpriteBatch.Draw(Sprite1);
SpriteBatch.Draw(Sprite2);
SpriteBatch.Draw(Sprite3);
Window.End(SpriteBatch);
Window.Display();

And the SpriteBatch would sort them based on the settings passed to it and render more optimally.

Which is, incidentally, basically how XNA does 2D (though XNA doesn't provide a scene graph system for 3D either, that's the coders job).
UnitTest11 - A unit testing library in C++ written to take advantage of C++11.

All code is guilty until proven innocent, unworthy until tested, and pointless without singular and well-defined purpose.

Sync Views

  • Newbie
  • *
  • Posts: 12
    • View Profile
New to SFML, game design questions
« Reply #24 on: September 06, 2011, 11:06:13 am »
Yes which  is what it appeard from the docs I read on SFML. And as such I'm still not surehow Laurent intends the Sprite object to be used exactly.

Previously in other stuff Ive just had a Sprite as a collection of graphical data (1 or more texture subrects, multiple being used in the case of animations, and the origin point), then there were methods like DrawSprite(spr, frame, x, y, angle), so it was clear that I had one Sprite object per different "visuals". SFML seems to have put that positional data into the Sprite objects itself, but one still doesnt use that data for logic, some I'm not entirly sure where that leaves sf::Sprite?

Well I guess batching isnt needed in the same form as D3D requires it since you havnt got this massive per call overhead that (the older versions at least) D3D has due to Kernel mode transitions. The graphics card still likes to render lots of vertices without constantly being asked to change state, but I'm not sure if in OpenGL/SFML you might actually get that by just drawing the same sf::Texture (with same blend etc) in a row (not sure, but whatever SFML is doing it passed my previous 200line d3d quad renderer I made at uni last year for a particle engine expierment in performance by a huge margin, and I know OpenGL and D3D should be near equals when you do things the right way :) ).

 

anything