Sign in to follow this  
RandomPixel

Problem with OO classes

Recommended Posts

I'm trying to create a Object Oriented OpenGL game using C++ with classes. But the problem is that I don't really know how to design the game. For some reason every class I create have only one instance. For example, I have a Player class. But this class is more of a Player Manager class because it have a list of all players in the game. Each player have a struct with its values and the Player class have methods such as Create(playerstruct), Destroy(playerstruct) etc. I also have a Print class that will write text on screen. This class initalize and destroys the text which is kept in memory, which makes it better to just keep one instance of this class to avoid initializing every time I need to write something on screen. But what if I have different classes that all needs to write something on screen? I then have to pass the Print class instance to all those classes. All these classes makes everything bloated and ugly. What am I doing wrong? Should I just skip the classes things because I don't have anything real use of it?

Share this post


Link to post
Share on other sites
You'll find a use for classes with multiple instances. Objects, Shaders, Textures, Callbacks, such things.

The examples you gave are well-suited for singletons, however personally I find the singleton approach overrated. Can't say I've ever used it.

In my projects, whenever I have these kinds of 'global' functionalities, I just dump them in a namespace instead of a class:

namespace Console
{
void Write(const char* format, ...);

void Initialize();
void Uninitialize();
}

So that I can use:

Console::Write("Blah blah: %s\n", message);

Namespaces still modularize the content, it shows what's going on, can be accessed from anywhere, and there's no need to pass pointers around.

Some people might say "well, what if you want to print in another way, you could've used a print interface and made a different printer instance, etc etc". I just ignore them [smile]. This way works for me.

Share this post


Link to post
Share on other sites
If the Player class doesn't actually represent a player, why did you name it that way?

You should create a player class that represents one player. Each player will store the data you are currently storing in the player struct, along with whatever member functions it needs. Then most of the code in Create()/Destroy() will move to the Player's constructor and destructor, respectively.

You can also have an std::vector of Players, and use that as the player list. The Players don't need to know that they are stored in a list (it makes the class more flexible), so the code to add and remove players from the list should be outside of the player class.

Passing around an instance of Print shouldn't be that bad because you should only have a handful of classes that print text on the screen. If you have many, you should probably rethink your design. One idea is for a class to return a string of what it wants printed, and have some other class actually print it. This will minimize the number of classes that need access to Print.

Since I'm not very experienced with this, that's all I'll say for now (I don't want to accidentally give you bad advice), but if you have any more questions, Ill try my best to answer (I really like these types of threads and sometimes can't resist replying. [smile])

Share this post


Link to post
Share on other sites
In C++ structs and classes are almost the same, except that structs normally have public members. Structs can even have constructors and methods. So your player structs are in fact classes that don't have any methods (at least I assume so).

There's nothing wrong with having a PlayermManager that holds a list of Players.

Also, passing the Print class around would not be that bad. Just think about if all those classes really need to print something themselves.

You also could have a Game/Engine class that holds a pointer to the print class and pass that around. Then, when you need the Print class, simply say game->getPrinter()->print(...);

Share this post


Link to post
Share on other sites
Quote:
Original post by Mike nl
You'll find a use for classes with multiple instances. Objects, Shaders, Textures, Callbacks, such things.

The examples you gave are well-suited for singletons, however personally I find the singleton approach overrated. Can't say I've ever used it.

In my projects, whenever I have these kinds of 'global' functionalities, I just dump them in a namespace instead of a class:

namespace Console
{
void Write(const char* format, ...);

void Initialize();
void Uninitialize();
}

So that I can use:

Console::Write("Blah blah: %s\n", message);

Namespaces still modularize the content, it shows what's going on, can be accessed from anywhere, and there's no need to pass pointers around.

Some people might say "well, what if you want to print in another way, you could've used a print interface and made a different printer instance, etc etc". I just ignore them [smile]. This way works for me.


class Console {
public:
Console() {
// Initialize
}

~Console() {
// Uninitialize
}
};

...

extern Console console;


You encapsulate the local data, get implicit construction/destruction, and allow creation of multiple instances.

At worst, in order to prevent initialization order problem, you construct the console instance at same point where you'd call Initialize function.

Destruction is taken care of. Even more:
void foo(int x, Console & c = console)
{
c.write("Hello World");
}

...

foo(20);

// let's write to error stream
Console anotherConsole(std::cerr);
foo(42, anotherConsole);


All the convenience, all the benefits. And yes, the default argument could also be a singleton, without enforcing the limitations:
void foo(int x, Console & c = Console::getInstance())
{
c.write("Hello World");
}


Quote:
For example, I have a Player class. But this class is more of a Player Manager class because it have a list of all players in the game. Each player have a struct with its values and the Player class have methods such as Create(playerstruct), Destroy(playerstruct) etc.


Create/Destroy in OO are realized as constructor/destructor.

Calling Player something that is PlayerManager also doesn't help with simplicity.

Share this post


Link to post
Share on other sites
Thanks alot for all the help.

I don't really have any code. More like empty classes. This is how my design looks like now:

main.cpp - Only have WinMain method.

globals.h - Have all the important includes that are used on many classes like <windows.h>

Input Class - Class to set and get inputs.

Window Class - Initialize and destroys the win32 window. Uses Input instance to update the input from example WM_MOUSEMOVE.

OpenGL class - Initialize and destroys OpenGL. Uses HWND handle from window class as input.

Print Class - Print message to screen.

World Class - Loads and draws the current map.

PlayerManager Class - Holds a list of all the players. (Renamed it to PlayerManager)

Menu Class - Creates a window box in-games for use as menu.

Game Class - Creates one instance of Input, Window and OpenGL. Creates one instance of the World Class, Player Class and Menu Class.

I haven't got further than this. About the print class, I'm not sure where to use it. I was thinking maybe the Game class needs it to print messages on screen, but the Menu class also needs it to print messages on the menu.

I'm not entirely sure how to make the menu class. I will be using several menus in the game, such as game menu, inventory, player stats etc. So I was thinking to make the Menu class general for all menus. But should I then have a new class that draws the contents of each window?

I think I'll do like Gage64 said about Player class. So the Game class will hold a list of all Player classes, and each player have their own create method etc.

This is hard. I'm probably missing alot of classes. Should I have a Object class and a Texture class that the Player class uses? Does anyone have a good example how a game design looks like with all the classes and what class uses what.

Share this post


Link to post
Share on other sites
Why don't you create a class to represent the player as well?

Also, like I said, you should give the Player class a constructor instead of having a create function. Same thing for the destructor.

Can you post some of the code for the PlayerManager class? (at the very least, what member functions it has) Also, can you show how you are using the player struct? It can help explain the benefits of upgrading the struct to a full blown class.

Finally, you might want to take a look at some of the articles in the first link in my sig. In particular, the one called "The Single-Responsibility Principle".

Share this post


Link to post
Share on other sites
Here is the code for PlayerManager class. I keep the list of all players on the Game class now. The loading is not finished, I will probably change it so it loads data from a file instead or something.

PlayerManager.cpp:


#include "PlayerManager.h"

#include <gl/gl.h>
#include <gl/glu.h>

/********************************************************************
*
********************************************************************/

void CPlayerManager::Load(stObject *p_pObject, float *p_pData, int p_nSize)
{
// Clear old values

if(p_pObject->pVertices !=NULL)
delete p_pObject->pVertices;
if(p_pObject->pColors != NULL)
delete p_pObject->pColors;
if(p_pObject->pTexCoords != NULL)
delete p_pObject->pTexCoords;

// Create arrays

p_pObject->pVertices = new stVertex[p_nSize];
p_pObject->pColors = new stColor[p_nSize];
p_pObject->pTexCoords = new stTexCoord[p_nSize];

// Set data

int nPos = 0;
for(int i = 0; i < p_nSize; i++)
{
p_pObject->pVertices[i].x = p_pData[nPos++];
p_pObject->pVertices[i].y = p_pData[nPos++];
p_pObject->pVertices[i].z = p_pData[nPos++];

p_pObject->pColors[i].r = p_pData[nPos++];
p_pObject->pColors[i].g = p_pData[nPos++];
p_pObject->pColors[i].b = p_pData[nPos++];

p_pObject->pTexCoords[i].t = p_pData[nPos++];
p_pObject->pTexCoords[i].u = p_pData[nPos++];
}
}

/********************************************************************
*
********************************************************************/

void CPlayerManager::Draw(stObject *p_pObject)
{
if(p_pObject != NULL)
{
glTranslatef(p_pObject->stPos.x, p_pObject->stPos.y, p_pObject->stPos.z);
glRotatef(p_pObject->nAngle, p_pObject->stRotate.x, p_pObject->stRotate.y, p_pObject->stRotate.z);

glVertexPointer(3, GL_FLOAT, 0, p_pObject->pVertices);
glColorPointer(3, GL_FLOAT, 0, p_pObject->pColors);
glTexCoordPointer(2, GL_FLOAT, 0, p_pObject->pTexCoords);

glDrawArrays(GL_QUADS, 0, 4);
}
}

/********************************************************************
*
********************************************************************/

void CPlayerManager::Delete(stObject *p_pObject)
{
if(p_pObject != NULL)
{
delete p_pObject->pVertices;
delete p_pObject->pColors;
delete p_pObject->pTexCoords;
}
}








PlayerManager.h:


#ifndef __GENERAL_PLAYERMANAGER_H
#define __GENERAL_PLAYERMANAGER_H

#include "globals.h"

struct stVertex { float x, y, z; };
struct stColor { float r, g, b; };
struct stTexCoord { float t, u; };

struct stObject
{
stVertex stPos;
stVertex stRotate;
float nAngle;

stVertex *pVertices;
stColor *pColors;
stTexCoord *pTexCoords;

stObject()
{
pVertices = NULL;
pColors = NULL;
pTexCoords = NULL;
}
};

class CPlayerManager
{
// Methods
public:
void Load(stObject *pObject, float *pData, int nSize);
void Draw(stObject *pObject);
void Delete(stObject *pObject);

// Members
private:
};

#endif








Game.cpp where I use the PlayerManager. For now I'm only using one player.


#include "Game.h"

#include <wchar.h>

/********************************************************************
*
********************************************************************/

CGame::CGame()
{
m_pInterface = new CInterface();
m_pGraphics = new CGraphics();
m_pInput = new CInput();

m_pPlayerManager = new CPlayerManager();
m_pFPS = new CFPS();
}

/********************************************************************
*
********************************************************************/

CGame::~CGame()
{
delete m_pFPS;
delete m_pPlayerManager;

delete m_pInput;
delete m_pGraphics;
delete m_pInterface;
}

/********************************************************************
*
********************************************************************/

int CGame::Create(void)
{
// Create the system

if(!m_pInterface->Create())
return(0);

if(!m_pInput->Create())
return(0);

if(!m_pGraphics->Create(m_pInterface->m_hWnd))
return(0);

// Create the tools

m_pFPS->Create();

// Create the player

static float g_fTestObject[] = { 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, };

m_pPlayerManager->Load(&m_stPlayer, g_fTestObject, 4);

m_stPlayer.stPos.x = 0.0f;
m_stPlayer.stPos.y = 0.0f;
m_stPlayer.stPos.z = -6.0f;

m_stPlayer.nAngle = 0.0f;
m_stPlayer.stRotate.x = 1.0f;
m_stPlayer.stRotate.y = 0.0f;
m_stPlayer.stRotate.z = 0.0f;

return(1);
}

/********************************************************************
*
********************************************************************/

int CGame::DisplayMode(void)
{
// Destroy the old display settings

m_pGraphics->Destroy();
m_pInterface->Destroy();

// Change mode

m_pInterface->SetDisplayMode();

// Create a new display settings

if(!m_pInterface->Create())
return(0);

if(!m_pGraphics->Create(m_pInterface->m_hWnd))
return(0);

return(1);
}

/********************************************************************
*
********************************************************************/

int CGame::Update(void)
{
// Quick exit game

if(m_pInput->GetKey(KEY_ESC))
return(0);

// Change display mode

if(m_pInput->GetKey(KEY_ALT) && m_pInput->GetKey(KEY_ENTER))
{
if(!DisplayMode())
return(0);
}

// Set to desktop mode on Window key

if(m_pInput->GetKey(KEY_LEFTWIN))
{
if(m_pInterface->GetDisplayMode() == 1)
{
if(!DisplayMode())
return(0);
}
}

// Draw the scene

m_pGraphics->Clear();

m_stPlayer.nAngle += 0.5f;
m_pPlayerManager->Draw(&m_stPlayer);

m_pGraphics->Render();

// Display FPS in title

m_pFPS->Count();

wchar_t szFPS[20];
swprintf(szFPS, L"%2.0f", m_pFPS->m_fFramesPerSecond);

wchar_t *szTitle = new wchar_t[strlen("Game Engine")+10];
swprintf(szTitle, L"Game Engine (%s FPS)", szFPS);
SetWindowText(m_pInterface->m_hWnd, szTitle);
delete[] szTitle;

// Make sure to give resources to windows

Sleep(1);

return(1);
}

/********************************************************************
*
********************************************************************/

void CGame::Destroy(void)
{
// Destroy the player

m_pPlayerManager->Delete(&m_stPlayer);

// Destroy the system

m_pInput->Destroy();
m_pGraphics->Destroy();
m_pInterface->Destroy();
}






[Edited by - RandomPixel on July 3, 2008 5:38:05 AM]

Share this post


Link to post
Share on other sites
Now I really recommend that you read the article I mentioned.

There are two things that define an object: It's data, and it's behavior (it's member functions). In this case you have separated those two things into two classes: stObject (I would lose the st prefix, it doesn't convey any useful information), which holds the data, and PlayerManager, which does the operations. This separation is confusing and pointless.

Unify these two into one class. Also, replace the Load() function with a constructor and the Delete() function with a destructor.

The article I mentioned basically says that each class should have only one responsibility. In this case, all the code you've shown basically represents a 3D model. If you put it in the Player class, the class will have two responsibilities: handle 3D models, and handle player functionality (what that is depends on you game). Doing this has several disadvantages that are explained in the article.

To fix this, put all the code you posted in a Model class. This class has only one responsibility: to draw a 3D model. Then when you create your Player class, it will just store an instance of the Model class and use it to handle the drawing. Thus, this responsibility has been removed from the Player class.

Doing this is sometimes called "delegation" because the Player class delegates a responsibility to the Model class.

BTW, calling delete on a NULL pointer does nothing, so there's no need to check if a pointer is NULL before calling delete on it.

EDIT: One last note - give your classes meaningful names. Like I explained, PlayerManager represents (the operations of) a 3D model, it has nothing to do with managing players. Besides, using Manager in a class' name is usually a bad idea because it doesn't have a clear meaning. It also often implies that the class does too many things and should be broken into separate classes (like described above).

Share this post


Link to post
Share on other sites
Thanks, It makes better sense. I have read the article (couldn't find it in the link you provided, but found it via google).

I guess I'm used to create big classes that does everything. The reason I created that struct, was because I thought it would be easier to make a list of that struct, which only contains variables, instead of the whole class with all its methods.

I like your method much better. So I will create a Model class and a Player class. How should they interact? Should I have another class that binds those two like the article suggested that you can do? I mean for example the Player location will probably be used in both the Model class and the Player class.

Share this post


Link to post
Share on other sites
There's also no need to have seperate arrays for vertex, color and texcoord. You can use the stride argument of the OpenGL calls to stuff everything in one structure:


struct Vector3 { float x, y, z; }
struct Vector2 { float x, y; }

struct Vertex
{
Vector3 position;
Vector3 color;
Vector2 texcoord;
}:

CPlayer::CPlayer(const float* data, size_t size)
{
pVertices = new Vertex[size];
}


And render them with:

glVertexPointer (3, GL_FLOAT, sizeof(Vertex), &pVertices[0].position);
glColorPointer (3, GL_FLOAT, sizeof(Vertex), &pVertices[0].color);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &pVertices[0].texcoord);

Share this post


Link to post
Share on other sites
Quote:
Original post by RandomPixel
So I will create a Model class and a Player class. How should they interact? Should I have another class that binds those two like the article suggested that you can do? I mean for example the Player location will probably be used in both the Model class and the Player class.


There are some fancy ways to accomplish this (loo for info on MVC (model-view-controller) for one example), but I don't understand them very well so I won't comment on them.

A simple solution is to have the player contain an instance of the Model class and make calls to it when needed.

About the position (and orientation), you will probably want to manipulate them in various ways (move forward, turn left, etc.), so I would probably create a class that would be responsible for this manipulation. Of course, this will also be used to draw the model, so the class should be able to pass appropriate data to OpenGL, for example by constructing a transformation matrix or making calls to glTranslatef(), glRotatef(), etc.

The player can store an instance of this class as well, and when it's time to draw the model, the player can pass info from this class to the model so that it draws itself in the appropriate position and orientation.

Like I said, I'm not very experienced with this so I'm just throwing out ideas.

Share this post


Link to post
Share on other sites
I'll try that. I'm not experienced either, but your ideas seems better than mine. Thanks for all the help.

Quote:
Original post by Mike nl
There's also no need to have seperate arrays for vertex, color and texcoord. You can use the stride argument of the OpenGL calls to stuff everything in one structure:


I didn't know that. Thanks. It looks much better that way. BTW is this method the ideal way to render models and objects? Or should I just stick with glBegin/glEnd?

Share this post


Link to post
Share on other sites
Quote:
Original post by RandomPixel
Quote:
Original post by Mike nl
There's also no need to have seperate arrays for vertex, color and texcoord. You can use the stride argument of the OpenGL calls to stuff everything in one structure:


I didn't know that. Thanks. It looks much better that way. BTW is this method the ideal way to render models and objects? Or should I just stick with glBegin/glEnd?


glBegin()/glEnd() is the slowest drawing method. Using vertex arrays is faster, but the fastest method (as far as I know) is to use vertex buffer objects (VBO).

Share this post


Link to post
Share on other sites
Its interesting to see Gage64 arguing in support of an over-OO way (designing a class for changing a position) and Mike nl in support of an under-OO way (free-functions-in-namespaces, singleton misuse), in the same thread [smile]

Object-Oriented Programming is a toolkit for making your programs easier to extend, which is achieved by making code easier to reuse in two different ways:
  • Slicing your functionality into bits that are as small as possible. This the core of the Single Responsibility Principle, which states that a given code entity (be it a class or a function) should be responsible for exactly one functionality, making it easier to reuse without having to drag along a heap of unnecessary functionality.

    Placing vertex-manipulation code in the player class will prevent you from using that code outside the player class without copy-pasting it. Moving it to an external entity would allow you to use it both within the player class and in other places as well.

  • By making functions assume as little as possible about other entities. A function which moves a player cannot be used to move a grenade, which makes reuse difficult. On the contrary, a function which moves an unidentified object by altering its position can be used to move both a player and a grenade (and anything else for that matter). Note that inheritance is not the best alternative for achieving this—instead of making your object movable, it's sometimes much easier to give it a position instead, and having the movement functions operate on positions.


However, the ultimate rule of development is to keep things as simple as possible.
  • If the language provides you with a feature, don't reinvent it (so, create functions are usually an useless overcomplication if a plan old constructor would have sufficed). The same goes for using std::vector wherever you would use new[] (as a matter of fact, I fail to see any possible usage of new [] aside for implementing a container).

  • When a class provides no benefit, don't make it a class. That is, if your class has the same possible set of states as a list of players, then use a list of players (with a typedef) with free functions to operate on it. Or, if your class is never manipulated as an instance and instead serves as a set of static functions, use a namespace instead. A class should be used when it provides additional functionality in terms of states (if you're only adding state-change functionality to an underlying data type, use free functions) or polymorphism (if you expect to provide distinct functionality with the same interface). Other cases introduce unnecessary complexity.


Always, always keep things as simple as possible.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Its interesting to see Gage64 arguing in support of an over-OO way (designing a class for changing a position)...


Over OO?

The class will be responsible for storing and manipulating both the position and orientation of an object in meaningful ways. For example, it will allow you to translate the position along a global or local axis, or rotate the orientation around a global or local axis, which are very useful operations. It can also produce a transformation matrix that can be passed to the graphics API.

How is encapsulating all this functionality in a class Over OO? Furthermore, what would be your proposed method for implementing this functionality?

Share this post


Link to post
Share on other sites
Keeping things simple as possible sound good. But my experience says that my code will be bloated really fast. That's why I'm trying to find a good design before starting coding the game.

I think I'll try to make it step by step. If I see any use of splitting a class into two classes, I'll go that approach. For example, the Model class would probably be useful for Players as well as Monsters.

But I still have some problem dividing the game into classes. For example, I maybe should have one class for the title screen, one for the setting screen and one for the gameplay. Each of these will then need the input class and grahics class.

Is there a book or tutorial that covers how to split up the source into classes and how they interact with each other?

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
The class will be responsible for storing and manipulating both the position and orientation of an object in meaningful ways. For example, it will allow you to translate the position along a global or local axis, or rotate the orientation around a global or local axis, which are very useful operations. It can also produce a transformation matrix that can be passed to the graphics API.


SRP violation. You'd have a class that does too much [smile]

I usually work with a transform class instead, as transforms can be used both to represent position-orientation pairs and to manipulate said pairs. Transforms in themselves implement a very simple interface: copyable-assignable, default-constructible (as identity), constructible (as the image of O, i, j, k vectors), applicable to other transforms, applicable to 3d and 4d vectors.

Then, I would develop transform-manipulation free functions: convert to a transform matrix compatible with a given API, rotate, translate, scale. These would be independent functions to avoid making the transform interface too heavy.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Quote:
Original post by ToohrVyk
Its interesting to see Gage64 arguing in support of an over-OO way (designing a class for changing a position)...


Over OO?

The class will be responsible for storing and manipulating both the position and orientation of an object in meaningful ways. For example, it will allow you to translate the position along a global or local axis, or rotate the orientation around a global or local axis, which are very useful operations. It can also produce a transformation matrix that can be passed to the graphics API.

How is encapsulating all this functionality in a class Over OO? Furthermore, what would be your proposed method for implementing this functionality?

Having a class, or instance, for translating forward/backward, another for orbiting and a third for up and down (jumping) motion seems rather contrived. It's hard to tell whether you're proposing that as a solution, but if so then I'd agree with ToohrVyk in that it is overly-OO.

I personally don't see anything intrinsically wrong with having a class/classes for modifying the spatial aspect of a player, or bullet etc. Just that I'd rather abstract that concept at a higher level.

In contrast to before, consider some classes that changes a transform (not a player or bullet, but a transform) to follow a projectile path, or another to follow some sinusoidal wave-like motion, a third to react under force and impulse dynamics, one that responds to events to make simple transformations (for controlling the player) and, lastly, one that performs inverse kinematics.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by Gage64
The class will be responsible for storing and manipulating both the position and orientation of an object in meaningful ways. For example, it will allow you to translate the position along a global or local axis, or rotate the orientation around a global or local axis, which are very useful operations. It can also produce a transformation matrix that can be passed to the graphics API.


SRP violation. You'd have a class that does too much


That is not exactly the same as saying that I suggested creating a class just to change a position. That is why your response annoyed me a bit. It also suggested that you haven't fully read my replies before commenting on them.

Having said that, I agree that your solution is more elegant.

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
Having a class, or instance, for translating forward/backward, another for orbiting and a third for up and down (jumping) motion seems rather contrived. It's hard to tell whether you're proposing that as a solution, but if so then I'd agree with ToohrVyk in that it is overly-OO.


I meant that one class should be used for all 3:


class Mover {
public:
void moveX(float amount, bool local);
void moveY(float amount, bool local);
void moveZ(float amount, bool local);

void rotateX(float angle, bool local);
void rotateY(float angle, bool local);
void rotateZ(float angle, bool local);

Matrix getMatrix();

private:
Vector3 pos;
Quaternion orient; // Or whatever
};

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
That is not exactly the same as saying that I suggested creating a class just to change a position. That is why your response annoyed me a bit. It also suggested that you haven't fully read my replies before commenting on them.


Yes. Because your reply was indeed not "creating a class for changing a position". What I was originally referring to is:

Quote:
I would probably create a class that would be responsible for this manipulation


This is a perfectly viable way of doing things (create a class that represents a modification) in object-oriented design, which is why I understood it as "create a Position class and a PositionMutator class responsible for manipulating it" (ultimately, a position class is responsible for the representation of that position, not for its manipulation). However, using such a heavy design pattern in such a simple situation struck me as overengineered. If, instead of "manipulating" you meant "representing", then don't mind my message.

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
In contrast to before, consider some classes that changes a transform (not a player or bullet, but a transform) to follow a projectile path, or another to follow some sinusoidal wave-like motion, a third to react under force and impulse dynamics, one that responds to events to make simple transformations (for controlling the player) and, lastly, one that performs inverse kinematics.


This is a good idea. However, in practice, these things are better represented as "polled transforms" (that is, a function which takes a time argument and returns an immutable transform for that time) than as objects that change an existing independent transform. Since transforms can be applied to each other, the two are equivalent, but encapsulation is easier in the first case.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
I meant that one class should be used for all 3:

*** Source Snippet Removed ***


Ah, I see.

To pick on that source a little:
That's a rather funky looking class. I'd say those functions should be free functions, and that class renamed to something like Transformation.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by Gage64
That is not exactly the same as saying that I suggested creating a class just to change a position. That is why your response annoyed me a bit. It also suggested that you haven't fully read my replies before commenting on them.


Yes. Because your reply was indeed not "creating a class for changing a position". What I was originally referring to is:

Quote:
I would probably create a class that would be responsible for this manipulation


This is a perfectly viable way of doing things (create a class that represents a modification) in object-oriented design, which is why I understood it as "create a Position class and a PositionMutator class responsible for manipulating it" (ultimately, a position class is responsible for the representation of that position, not for its manipulation). However, using such a heavy design pattern in such a simple situation struck me as overengineered. If, instead of "manipulating" you meant "representing", then don't mind my message.


Ah, I understand. BTW, my reply to dmatter above shows exactly what I meant.

Quote:
Original post by dmatter
To pick on that source a little:
That's a rather funky looking class. I'd say those functions should be free functions, and that class renamed to something like Transformation.


That's what ToohrVyk suggested above, and I agreed.

Somewhat unrelated - would it be better to instead make the functions static member functions of Transformation, to indicate that they are associated with that class?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this