Writing a simple game, can you help me to organize it

Trying to make a simple 2D game on sprites
Look my approach, I will appreciate your advices to make it any way better

First:
I have made Objects and Render_Objects classes. Objects are certainly organized tree, Render_Objects - just a sequence (line). One or few Render_Objects can be assigned to an Object. When Object is moved to another branch, left and right Render_Objects pointers get attached to new points.

Here is my RENDER.H:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef KRENDER_H_INCLUDED
#define KRENDER_H_INCLUDED

#include <gl/gl.h>

namespace klib {
    enum render_sort {
        RENDER_SCREEN_COLOR,
        RENDER_SPRITE
    };

    class render_t {
        render_t * prev;
        render_t * next;

        int data_int_1, data_int_2, data_int_3;
        float data_float_1, data_float_2, data_float_3, data_float_4, data_float_5, data_float_6, data_float_7, data_float_8;

        friend class CreateObject;

        public:
            //Screen color
            render_t(int data_int_1, float data_float_1, float data_float_2, float data_float_3);
            //Sprite
            render_t(render_t * prev,
                     int data_int_1, int data_int_2, int data_int_3,
                     float data_float_1, float data_float_2, float data_float_3, float data_float_4, float data_float_5, float data_float_6, float data_float_7, float data_float_8);

            void proc();
    };
}

#endif // KRENDER_H_INCLUDED 

These are 2 sorts of Render_Object: Screen Color (just fills a screen with color) and Sprite.
I use only few int and float variables, common for all the sorts of Render_Object. There will be more sorts in future, but I don't think, the count will be much different.

RENDER_T::PROC() is such a function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void render_t::proc() {
    switch (data_int_1) {
        case RENDER_SCREEN_COLOR:
            glFlush();
            glClear(GL_COLOR_BUFFER_BIT);
            glClearColor(data_float_1, data_float_2, data_float_3, 1.0f);
            break;
        case RENDER_SPRITE:
            glLoadIdentity();
            glTranslatef(data_float_1, data_float_2, 0.0f);
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, data_int_2);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, data_int_3);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, data_int_3);
            glBegin(GL_POLYGON);
                glTexCoord2f(data_float_5, data_float_6);
                glVertex2f(0, 0);
                glTexCoord2f(data_float_5, data_float_8);
                glVertex2f(0, data_float_4);
                glTexCoord2f(data_float_7, data_float_8);
                glVertex2f(data_float_3, data_float_4);
                glTexCoord2f(data_float_7, data_float_6);
                glVertex2f(data_float_3, 0);
            glEnd();
            glDisable(GL_TEXTURE_2D);
            break;
        //will be more
    }
    if (next != 0)
        next->proc();
}

So I use SWITCH here, as you see. Is it ok?
When I did that, I thought, there isn't going to be very many sorts of Render_Object, so switch isn't gonna be large.
(If you look at what I did for Object later, it's completely opposite)

! Screen Color is must have Render_Object. It always exist along with Background Object, so I never check if previous object exist when destroy one.

Here is my OBJECT.H:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef KOBJECT_H_INCLUDED
#define KOBJECT_H_INCLUDED

#include "krender.h"

namespace klib {
    enum object_sort {
        OBJECT_BACKGROUND,
        OBJECT_CURSOR,
    };

    class object_t {
        object_t * prev;
        object_t * next;

        int data_int_1;
        float data_float_1, data_float_2, data_float_3;

        render_t * render;

        friend struct CreateObject;

        public:
            void proc();
    };

    struct CreateObject {
        static object_t * Background(float r, float g, float b);
        static object_t * Cursor();
    };
}

#endif // KOBJECT_H_INCLUDED
There will be more Object types (sorts) for sure as it will progress
As I think, this is gonna be UNIVERSAL class for any object. Then 1 integer variable determines type (sort) of the Object, and depending on this integer different functions are called upon WORLD_REFRESH_FUNCTION. (Basically, I have an array of such functions)
Unlike Render_Objects created with constructor (as they are all simple), Objects are created with functions of special CreateObject class.
So, if we need to create an object of type BBB, we write CreateObject::BBB(different arguments).

Here is how OBJECT.CPP is written now:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "kobject.h"
#include "kprog.h"

using namespace klib;

///Create functions

object_t * CreateObject::Background(float r, float g, float b) {
    object_t * object = new object_t;
    object->prev = 0;
    object->next = prog::getObjectList();
    prog::setObjectList(object);

    object->data_int_1 = OBJECT_BACKGROUND;
    object->data_float_1 = r;
    object->data_float_2 = g;
    object->data_float_3 = b;

    object->render = new render_t(RENDER_SCREEN_COLOR, r, g, b);
    object->render->prev = 0;
    object->render->next = prog::getRenderList();
    prog::setRenderList(object->render);
    return object;
}

object_t * CreateObject::Cursor() {
    object_t * object = new object_t;

    object->data_int_1 = OBJECT_CURSOR;

//MISSING CODE

    return object;
}

///Refresh functions

void OBJECT_BACKGROUND_FUNC(object_t * object) {
//MISSING CODE
}

void OBJECT_CURSOR_FUNC(object_t * object) {
//MISSING CODE
}

void (* const OBJECT_FUNC[])(object_t * object) = {&OBJECT_BACKGROUND_FUNC,
                                                   &OBJECT_CURSOR_FUNC};

///Object

void object_t::proc() {
    OBJECT_FUNC[data_int_1](this);
    if (next != 0)
        next->proc();
}

What you think?
Probably, can be done much better?
Last edited on
Double-posting because too long

Here is PROG.H, my first header, to better understand what is going on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#ifndef KPROG_H_INCLUDED
#define KPROG_H_INCLUDED

#include <tchar.h>
#include <windows.h>
#include <windowsx.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "kobject.h"
#include "krender.h"

namespace klib {
    class prog {
        static TCHAR * name;
        static TCHAR * title;
        static int refreshTime;
        static int mainframes;

        static int mainframe;
        static object_t ** objectList;
        static render_t ** renderList;

        static UINT_PTR timer;
        static HDC hDC;
        static HGLRC hRC;
        static PIXELFORMATDESCRIPTOR pfd;

        static void start();
        static void close();
        static void refresh();

        static LRESULT CALLBACK proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

        public:
            static void init(TCHAR * name, TCHAR * title, int refreshTime, int colorBits, int zBufferBits, int mainframes, int mainframe);
            static int WINAPI main(HINSTANCE hThisInstance,
                                HINSTANCE hPrevInstance,
                                LPSTR lpszArgument,
                                int nCmdShow);

            static TCHAR * getName() {return name;};
            static TCHAR * getTitle() {return title;};
            static int getRefreshTime() {return refreshTime;};
            static int getMainframes() {return mainframes;};

            static int getMainframe() {return mainframe;};
            static void setMainframe(int mainframe) {prog::mainframe = mainframe;};
            static object_t * getObjectList() {return objectList[mainframe];};
            static void setObjectList(object_t * object) {prog::objectList[mainframe] = object;};
            static render_t * getRenderList() {return renderList[mainframe];};
            static void setRenderList(render_t * render) {prog::renderList[mainframe] = render;};

            class display {
                static BYTE colorBits;
                static BYTE zBufferBits;

                static int width;
                static int height;
                static int halfwidth;
                static int halfheight;
                static float aspect;

                public:
                    static void init(int colorBits, int zBufferBits);

                    static BYTE getColorBits() {return colorBits;};
                    static BYTE getZBufferBits() {return zBufferBits;};

                    static int getWidth() {return width;};
                    static int getHeight() {return height;};
                    static int getHalfwidth() {return halfwidth;};
                    static int getHalfheight() {return halfheight;};
                    static int getAspect() {return aspect;};
            };
    };
}

#endif // KPROG_H_INCLUDED 

Mainframe is basically a screen. A game itself is 1 mainframe, options menu is another, tutorials - third, etc. Different mainframes have different ObjectLists and RenderLists.
I didn't have time to read through your code, but a game's structure can be done many different ways. Usually it's something like this:

Main - makes a game object and calls the game's gameLoop function
Game object - runs a game loop that loops every frame

1
2
3
4
5
6
7
8
9
void Game::gameLoop()
{
     while(running)
     {
          userInput();
          update();
          render();
     }
}


userInput() gets any key pressed, mouse clicked, option chosen, etc
update() updates enemies, the player, checks for collision, moves time in the game along
render() draws everything

In game there's usually a graphics system, and some sort of graphics buffer to draw everything (unless whatever you're using has a draw function). Here is also where you'd add your player or enemies.

Enemy Class would have a sprite object in it or if you want an animation for the enemy it would have an animation object with a bunch of sprite objects to make it up.

Each sprite should have it's own render or draw function. So of example in game's render() you'd call the enemies render() which would call the animation render() which loops through its sprites and call all their render() functions.

There's tons of other classes you can make, like a menu, inventory, maybe some manager to manage all the enemies and so on. At a quick glance you had a mainframe class, so maybe in main you could have the main menu mainframe, and can switch over to the game mainframe in main. personally I would have a menu object and just pause the game's update and pop the menu object over the game until the player decides to hit play again. Hahaha, There's so many ways to do it!

Not sure if that's what you were looking for or not but there you go.
Thanks, you are on topic
Let's take an object class. Object of all unique classes must have different Update functions and set of Data. So, what I am doing is:
1. Give an integer value to Object Class that determines its "sort" (such a word because class and type belong to C++)
2. Give a pointer to update function, that is addressed to a member of array FUNCTION[SORT], which are all pre-written
Then what to do with Data?
3. Give a pointer to special Data struct?
or
3. Make common must-have set of Data and a pointer to additional, smaller Data struct?
or somehow else?

Is it legit that I not use sub-classes and virtual methods, and do everything with 1 basic class, then dynamically attach to it different things to make it different kind of object?
Last edited on
Is it legit that I not use sub-classes and virtual methods, and do everything with 1 basic class, then dynamically attach to it different things to make it different kind of object?

Yes. This actually sounds very similar to the Component-Entity-System architecture, which can be much easier to maintain and extend later.

Useful link -> http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013
Topic archived. No new replies allowed.