I need some advice on designing a 2d game engine

Started by
10 comments, last by Wiggin 17 years, 9 months ago
okay, i'm trying to design my first game engine, just wanted to get your opinions and constructive criticism on the basic structure i'd made. so, my engine is OO based, C++. i've made up some classes: Point : low-level class, represents a 2d point in the game. Vector : 2d vector class, contains length and angle, mainly used as velocity. Entity : base, abstarct class to represent every object in the game - player, enemies, obstacles etc. MovingEntity -> Entity : inherits from "Entity", represents a moving object in the game, has a velocity vector to represent its speed, e.g enemies. StillEntity -> Entity : inherits from "Entity", represents a still object, doesn't have velocity, e.g floor, still boxes mario style. EntityList : a linked list/tree (haven't decided) of related objects, list of enemies, list of bonuses etc. EntityControll : controlls "entitylist" objects, modifies their properties, shows them etc. CollisionEntity : abstract class. represents a entity with an ability to interact with other objects. recieves an "Entity" object, and creates a suitable collision form for this entity, elipse or rectangle. CollisionElipse -> CollisionEntity : inherits from "CollisionEntity", implements the "CollisionEntity" methods according to an elipse. CollisionRectangle -> CollisionEntity : inherits from "CollisionEntity", implements the "CollisionEntity" methods according to an Rectangle. CollisionList : a linked list/tree (haven't decided) of related colliding entities, e.g obstacles, laser beams, walking enemies, flying enemies etc. CollisionControll : a class to check collision between objects of "CollisionList", and themselves. enables the designer to add colliding groups to be checked at any time. TileMap : a class to handle creation of the map, drawing a background image, and drawing map elements such as floors. consists of Entity objects. Engine : a class to handle the game actually. has a "CollisionControll" object to handle collisions, an "EntityControll" object to handle entities in the game, event handeling proceduers, "TileMap" object to draw the map etc. leaving sound issues aside, how does this look? any ideas, comments, and help are very much appreciated!
Advertisement
Quote:Original post by Dizzy1
Entity : base, abstarct class to represent every object in the game - player, enemies, obstacles etc.

MovingEntity -> Entity : inherits from "Entity", represents a moving object in the game, has a velocity vector to represent its speed, e.g enemies.

StillEntity -> Entity : inherits from "Entity", represents a still object,
doesn't have velocity, e.g floor, still boxes mario style.

I don't really see the need for this. Why not give all entities a velocity vector and just make the still ones have a velocity of zero. The more generality in your design the better. On a side note, in my side scroller, the "still" things do actually move because of scrolling. Not sure if this applies to you though.

Quote:Original post by Dizzy1
EntityList : a linked list/tree (haven't decided) of related objects, list of enemies, list of bonuses etc.

CollisionList : a linked list/tree (haven't decided) of related colliding entities, e.g obstacles, laser beams, walking enemies, flying enemies etc.

Why not just use std::list<Entity> or std::vector<CollisionEntity>? This way you get a robust list object for free!

Quote:Original post by Dizzy1
EntityControll : controlls "entitylist" objects, modifies their properties, shows them etc.

In OOD, objects are supposed to be able to modify their own properties. Why have another class to act as a go between?

Quote:Original post by Dizzy1
CollisionEntity : abstract class. represents a entity with an ability to interact with other objects. recieves an "Entity" object, and creates a suitable collision form for this entity, elipse or rectangle.

CollisionElipse -> CollisionEntity : inherits from "CollisionEntity", implements the "CollisionEntity" methods according to an elipse.

CollisionRectangle -> CollisionEntity : inherits from "CollisionEntity", implements the "CollisionEntity" methods according to an Rectangle.

Not a bad idea, but why not just have just the CollisionEntity class which is itself capable of bounding box or bounding elipse checks. It could have a checkCollision function which callss either checkBox or checkEllipse. That's how I'd do it, but your way should be fine.

Quote:Original post by Dizzy1
CollisionControll : a class to check collision between objects of "CollisionList", and themselves. enables the designer to add colliding groups to be checked at any time.

I'm not quite sure what you mean by this. What is its purpose? Just to loop through the list of CollisionEntity's and test them? If so this probably could just be a function.

Quote:Original post by Dizzy1
TileMap : a class to handle creation of the map, drawing a background image, and drawing map elements such as floors. consists of Entity objects.

Engine : a class to handle the game actually. has a "CollisionControll" object to handle collisions, an "EntityControll" object to handle entities in the game,
event handeling proceduers, "TileMap" object to draw the map etc.


Sounds good to me!
What kind of game is it?
Thanks for the quick reply!

i currently aim towards a platform game / FPS engine.

Quote:
Quote:
Quote:Original post by Dizzy1
Entity : base, abstarct class to represent every object in the game - player, enemies, obstacles etc.

MovingEntity -> Entity : inherits from "Entity", represents a moving object in the game, has a velocity vector to represent its speed, e.g enemies.

StillEntity -> Entity : inherits from "Entity", represents a still object,
doesn't have velocity, e.g floor, still boxes mario style.



I don't really see the need for this. Why not give all entities a velocity vector and just make the still ones have a velocity of zero. The more generality in your design the better. On a side note, in my side scroller, the "still" things do actually move because of scrolling. Not sure if this applies to you though.


guess you're right, just wanted to make it easier for me to handle collisions between objects capable of moving, and those who aren't, suppose an enemy hits the wall, i don't expect the wall to move.

Quote:
Quote:
Quote:Original post by Dizzy1
EntityList : a linked list/tree (haven't decided) of related objects, list of enemies, list of bonuses etc.

CollisionList : a linked list/tree (haven't decided) of related colliding entities, e.g obstacles, laser beams, walking enemies, flying enemies etc.



Why not just use std::list<Entity> or std::vector<CollisionEntity>? This way you get a robust list object for free!


silly me, just wanted a type name :)
i can actually use typedef for that i guess.

Quote:
Quote:
Quote:Original post by Dizzy1
EntityControll : controlls "entitylist" objects, modifies their properties, shows them etc.



In OOD, objects are supposed to be able to modify their own properties. Why have another class to act as a go between?


one of the things i'm trying to find out with this project,
is whether a MVC model is suitable for game development, and a main part of the model is to seperate the data,view and controll of a model.
here i'm trying a less strict approach, and apply the "Controll" part to a group of entities.

Quote:
Quote:
Quote:Original post by Dizzy1
CollisionControll : a class to check collision between objects of "CollisionList", and themselves. enables the designer to add colliding groups to be checked at any time.



I'm not quite sure what you mean by this. What is its purpose? Just to loop through the list of CollisionEntity's and test them? If so this probably could just be a function.


suppose the class will look like this:

// a sructure that holds two pointers to collisionlists that can possibly collide
struct CL_Pair
{
CollisionList *first,*second;
};

class CollisionControll
{
private:

// a vector that contains the pairs to be checked for collision
std::vector< CL_Pair > cl_pairs;
public:

// some constructor... not sure what his job is at the moment
CollisionControll() {}

// pushes new CL_Pair to cl_pairs
AddCollidingPair( CollisionList *first, CollisionList *second );

// checks collsion between CollisionList pairs in cl_pairs
CheckCollision();
}

thought that will be more affective than checking all the objects in the scene.
Some points:

  • I doubt you will only need rectangles (possibly ellipses) for collision detection. Consider making a Rectangle class.
  • You will need classes for managing graphics, like e.g. Animation, GraphicsLoader, ...
  • Since collision handling for each collision pair might be different, you could pass a collision handler to the AddCollidingPair member function:
    void AddCollidingPair(CollisionList* first, CollisionList* second, void (*collisionHandler)(CollisionEntity*, CollisionEntity*))

Quote:
Original post by Barius
I doubt you will only need rectangles (possibly ellipses) for collision detection. Consider making a Rectangle class.

what do you mean by Rectangle class?
i've done CollisionRectangle and CollisionElipse.

Quote:
Original post by Barius
You will need classes for managing graphics, like e.g. Animation, GraphicsLoader, ...

I actually plan to use animation mechanism inside the Entity class, something like an advancing iterator.
I'm also planning to load the graphics directly into the Entity object,
something like an xml file, that defines the frames of the sprite, the delay of the animation etc, that will be loaded by the contructor of Enity.

anyway, can you go into more detail about your ideas?

Quote:
Original post by Barius
Since collision handling for each collision pair might be different, you could pass a collision handler to the AddCollidingPair member function:

nice one! I'll try to implement it the best way.



Quote:
what do you mean by Rectangle class?
i've done CollisionRectangle and CollisionElipse.

I just meant that rectangles are very useful in 2d games in general, not just for collision detection.

Quote:
I actually plan to use animation mechanism inside the Entity class, something like an advancing iterator.
I'm also planning to load the graphics directly into the Entity object,
something like an xml file, that defines the frames of the sprite, the delay of the animation etc, that will be loaded by the contructor of Enity.


anyway, can you go into more detail about your ideas?


Well, an Animation class would simply be a sequence of images with respective frame delays and perhaps some flags like "looping", and have methods like currentFrame() and animate().
A GraphicsLoader class would be a central repository for loading images and storing them in a data structure like a std::map. If your level contains 100 trolls, you don't want to load troll.bmp 100 times.

<lecture-mode>
In general, if you can offload some task to its own class, do so. Stuffing lots of things into a single class (like you seem to intend to do with your Entity class) means that class becomes hard to test and inflexible (google for the OO terms "coupling" and "cohesion").
Of course, while more flexible in the long run, splitting things up into different classes can sometimes be inconvenient in the short term because you need to set up the "connections" (e.g. pointers) between the objects and (at least in C++) worry about low-level details like memory management (but smart pointers like boost::shared_ptr help with the second issue).
</lecture-mode>
okay i've done some testing with my Vector class.

I used the following code:

std::cout << "Enter new length and angle : (-1 to exit)";std::cin >> l >> a;if (l < 0 || a < 0)	quit = true;gettimeofday(&t1,NULL);gettimeofday(&t3,NULL);while (!quit) {	gettimeofday(&t2,NULL);	std::cout << "current : " << /*current*/t2.tv_usec << "\n" << "delay : " << /*delay*/t1.tv_usec << "\n";	factor = (/*current - delay*/t2.tv_usec - t1.tv_usec)/(float)(100000/fps);	std::cout << "factor : " << factor << "\n";	v.setLength((((float)l/(1000/fps))*frame)*factor);	v.setAngle(a);	frame++;	std::cout << "(" << v.getPoint().getX() << "," << v.getPoint().getY() << ")\n";	t1 = t2;	if (t2.tv_usec >= t3.tv_usec + 100000)		quit = true;}


hope it's pretty understandable' however, i'll explain it:
what i'm trying to do here is to create a mechanism to check how the vector functions.
i get vector's length and angle from the user, length is the velocity measured in pixels/sec.
because i'm not limiting the FPS, i calculate the speed factor,
using the difference in microseconds between two frames, dividing the result by the optimal number of microseconds per frame.
in order for me to get the next (x,y) position, I get the length the entity needs to pass, by dividing the length per second by the optimal number of miliseconds per second, multiplying that by the frame number,
and by the speed factor.

in the end i check whether a second passed since the loop started, and terminate the loop.

as you probably guessed this is not going 100% as expected.

1. i get quite strange results, sometimes it's correct (or at least very close to that), and somtimes way off. i guess it can have something to do with the timer resolution?
for example,
a 200 length and a 315 degree angle (45 in computer graphics) gives me this result:

current : 508489delay : 508354factor : 0.0472524(139,139)


about a second later, quite good actually, only 2 pixels off.

but on the other hand, sometimes i get unexpected results like (same input, last results shown here):

current : 179776delay : 179618factor : 0.0553028(124,124)OR:pretty messed up result giving me this:current : 951157delay : 951141factor : 0.00560028(17,17)WTF?


2. the factor gives me this bumps in the results:

current : 502450delay : 502313factor : 0.0479524(138,138)current : 506674delay : 502450factor : 1.47847(4263,4263)current : 506820delay : 506674factor : 0.0511026(147,147)


here's the vector class:
class Vector{private:	int angle;	float length;	Point pnt;	void updateXY(float s, int d)	{		pnt.setY((int)(sinTable[angle]*length));		pnt.setX((int)(cosTable[angle]*length));	}public:	Vector(float s, int d)	{		length = s;		angle = d;		updateXY(length,angle);	} 	Vector() {}	float getLength()	{			return length;	}	int getAngle()	{		return angle;	}	Point getPoint()	{		return pnt;	}	void setLength(float s)	{		length=s;		updateXY(length,angle);	}	void setAngle(int d)	{		angle=d;		updateXY(length,angle);	}};


any help will be very appreciated!!!
Most people store vectors as an x component and a y component, not as an angle and a length. Try adding two of your vectors together and find out why.
If you will look closely you'll see that my vector class calculates its x and y components when i update the length or the angle.
I did see that. But like I said, have you tried adding two vectors together? Have you tried calculating a dot product?

This topic is closed to new replies.

Advertisement