Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie
Hi there,
There is an issue with role permissions that is being worked on at the moment.
If you are having trouble with access or permissions on regional forums please post here to get access: https://www.boards.ie/discussion/2058365403/you-do-not-have-permission-for-that#latest

(OOP) Software design for multiple platforms

  • 24-02-2010 7:55pm
    #1
    Registered Users, Registered Users 2 Posts: 8,449 ✭✭✭


    I'm creating a game in C++ but I have designed the logical classes so they are independent of the renderer (in this case a 2d library, SFML). I did this so I can easily port the game to a 3d engine later without having to change the game code.

    My question is, whether I should implement wrapper classes on top of the renderable game objects. At the moment, I am writing SFML specific classes which derive from my base classes. However all these really do is implement one function - Render(), which does the SFML specific rendering.

    Is this the best way to do things? Can anyone with cross-platform development experience shed light on what the best practice is?

    Thanks


Comments

  • Registered Users, Registered Users 2 Posts: 981 ✭✭✭fasty


    Abstracting things out is the way to go. The way I do things is by having a rendering system that I just send geometry, state and materials to. It seemed to work okay, my initial efforts were designed for DX10/DX11 and I was able to port it to OpenGL/SDL and get it running on a Mac in a few weeks.

    I'm not a professional game developer though, but the impression I've got from mates in a few small and a few large companies in the UK is that it's a mixture of in house libraries that usually use preprocesser defines for platform specific stuff and abstrations for things like file io, rendering, sound, input etc.

    You should also separate your data from your rendering. Does each sprite need a render function for example? Why not just store into about your game objects in a class and have some helper class iterate over them and call a render function that can use SFML or whatever.

    I know you think "objects, they should have a render function" but if you focus on the data, you can render things however you want later!


  • Registered Users, Registered Users 2 Posts: 8,449 ✭✭✭Call Me Jimmy


    Thanks for the reply mate.

    You're right, I initially had my logical classes set up with pure virtual rendering functions which I intended to override when deriving my renderer specific classes, so I had a BaseText class which stored all the information that any renderable text would need. Then I had an SFMLText class which dervied from BaseText and simply implemented the render function, using the data in BaseText to do so.

    However, the more I looked at it the more unintuitive it seemed - even if from an OO perspective it was the usual way.

    I considered using preprocessor directives but again something didn't feel right about it when I'm simply designing it so that it could be implemented in a different renderer.

    I realised that I had to think about the whole picture, and in that case it would be best to abstract the renderer, so have maybe a BaseRenderer class which is pure virtual and defines the different render functions for each of my Renderable objects.

    So any renderer I implement would have to override Render(BaseText), Render(BaseShape) etc.

    I'd be interested to hear what more people in this situation think though, because the Objects themselves would no longer be calling the render function, which is not strictly OO design but I figure, what works best should probably be the way to go.


  • Registered Users, Registered Users 2 Posts: 981 ✭✭✭fasty


    OO isn't always about modelling components exactly as you see them, it's about abstract data types and encapsulation. Don't get hung up on things rendering themself being the OO way to do it, it isn't always.

    All your game data should do is describe what your rendering system should do imo. But hell, whatever works for you.

    Anyway, you could use polymorphism. I do the same for for a 3D renderer. So instead of sprites, I set buffers of geometry and materials (basically, shaders + inputs) and other rendering states via a pure virtual class that acts as an interface to a platform specific renderer. Hell, it's even allowed me to create a software render that I can test in my game with little effort.
    class Renderer
    {
    public:
    	virtual void RenderText(std::string text, int x, int y, float rotation) = 0;
    	virtual void RenderSprite(Sprite& sprite, int x, int y, float rotation) = 0;
    	virtual void Resize(int x, int y) = 0;
    };
    
    class SFMLRenderer : Renderer
    {
    public:
    	virtual void RenderText(std::string text, int x, int y, float rotation) { /* implementation */ }
    	virtual void RenderSprite(Sprite& sprite, int x, int y, float rotation) { /* implementation */ }
    	virtual void Resize(int x, int y) { /* implementation */ }
    }
    
    class AnotherRenderer : Renderer
    {
    public:
    	virtual void RenderText(std::string text, int x, int y, float rotation) { /* implementation */ }
    	virtual void RenderSprite(Sprite& sprite, int x, int y, float rotation) { /* implementation */ }
    	virtual void Resize(int x, int y) { /* implementation */ }
    };
    

    Then to create your render at game init time...
    enum RenderType
    {
    	SFML;
    	Another;
    };
    
    Renderer* CreateRenderer(RenderType type /* and whatever other params you want */)
    {
    	switch(type)
    	{
    	case SFML:
    		return new SFMLRenderer(/*params*/);
    	case Another:
    		return new AnotherRenderer(/*params*/);
    	default:
    		/* throw exception */
    	}
    };
    

    And when rendering
    Renderer* myRenderer = CreateRenderer(SFML);
    
    for(/*all of the sprites in my game*/)
    {
    	myRenderer.RenderSprite(/*params*/);
    }
    

    Another alternative would be static polymorphism. Where you use a rendering class definition without virtual functions and conditionally compile the implementation for the platfomr you're running via preprocessers. This avoids the overhead of virtual functions, something that would matter for a high performance game, but maybe not for you or me!


  • Registered Users, Registered Users 2 Posts: 8,449 ✭✭✭Call Me Jimmy


    Yea, as I said, when I was thinking about it more last night, the way u described there is almost exactly what I came up with, I just wasn't sure if that would be a standard way of doing it as I have little experience designing anything more than test programs, but I think I have a good understanding of OOP.

    The more I read about software design theory the more scared I get though: dangers of Singletons etc. so I'm just gonna stick to what I think works best.


  • Registered Users, Registered Users 2 Posts: 981 ✭✭✭fasty


    Aye, I know I was repeating what you said, so I apologise if it seemed like I was ignoring you. I think the best advice is to go with what works and you seem to have nailed it. I'll leave this thread to anyone else who might have better advice!

    Singletons eh? They're just globals in denial! Not that I think globals are a bad thing...


  • Advertisement
  • Registered Users, Registered Users 2 Posts: 1,916 ✭✭✭ronivek


    Have a look at the Factory and Abstract Factory patterns; they're effectively what you're talking about and they're a fairly well understood pattern in OOP.


  • Registered Users, Registered Users 2 Posts: 1,481 ✭✭✭satchmo


    Yup, fasty pretty much hit the nail on the head - use polymorphism with an interface class, and a platform-specific implementation of that interface. It's pretty much what polymorphism was designed to do.

    We do that for our rendering code and have platform-specific implementations for PS3, 360 & DX9 but generic rendering calls from game code to keep it platform-agnostic. We also go the non-virtual route of using the preprocessor to avoid all those vtable lookups, which saved us a considerable amount of CPU time.


  • Registered Users, Registered Users 2 Posts: 1,922 ✭✭✭fergalr


    I'm going to play devils advocate here and advise that while most of the advice you've gotten on here is technically very sound, you may be asking the wrong question.
    I'm creating a game in C++ but I have designed the logical classes so they are independent of the renderer (in this case a 2d library, SFML). I did this so I can easily port the game to a 3d engine later without having to change the game code.

    I'd advise that you write your game first, and focus on whatever end user visible features are most important to you.

    When you have a working game that is good, and if you still want to consider porting it to a 3d engine, port it then.

    The most important thing is that you hit your current goals in the most technically efficient way possible before you run out of time/money/momentum.

    By the time you want to make a 3d engine version, if you get that far, you can rethink it then - you'll probably have more experience and knowledge of what your needs are at that point.
    Just throwing it out there...

    My question is, whether I should implement wrapper classes on top of the renderable game objects. At the moment, I am writing SFML specific classes which derive from my base classes. However all these really do is implement one function - Render(), which does the SFML specific rendering.

    Is this the best way to do things? Can anyone with cross-platform development experience shed light on what the best practice is?
    Thanks


  • Registered Users, Registered Users 2 Posts: 8,449 ✭✭✭Call Me Jimmy


    Thanks for all the replies guys.

    And yea, I am focusing on the game itself in the current engine but it isn't going to take much work to make it adaptable and make the top level code abstract. In a way I think it's helping me in the design process as I really have to think about exactly how each part interacts with another, but I won't get lost in a world of theory either.


Advertisement