Thread: RTS: wargame

  1. #1
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743

    RTS: wargame

    Hey guys,

    Lately I have been doing a substantial amount of work on an RTS game which I simply call "wargame."

    2 years ago before I went to Italy I had a large amount of the project done, and I was quite happy with my progress. When I returned from Italy a few months ago, I took a look at my code, and I was kind of disgusted by it in many aspects.

    I had done quite a bit of code commenting, so after 2 years I was able to come back and understand my code with ease, so that was not the problem. The main problem I was having were some memory leaks that I had not resolved 2 years earlier, and I had let myself become a little sloppy in my coding in some respects.

    Taking all that into account, I decided to start anew, but I did not want to lose all of my previous work. Therefore I am coding everything from scratch, but I am using the old project almost as a template for the new project (minus all the grevious mistakes I made in memory management and sloppyness 2 years ago).

    Anyways, this game project is not meant to be graphically amazing in any respects. I am using simple 2D SDL (no OpenGL). So the graphics are not extremely difficult. I just finished the large part of the graphical subsystem, in fact.

    Now I am turning myself to work on other things in the project. I was having some questions about the design in particular, that I wanted to run by you guys.

    Question 1: A game clock

    I have been contemplating the idea of a universal game clock. Almost like the CPU clock which synchronizes the timing of events happening inside of the CPU, I was thinking about making a class that interfaces with the system clock, and this class would synchronize all events that happen in the game.

    In past gaming projects that I have done, I have done all my synchronization based on the frame rate. The graphical frame rate was basically the clock on when things happened. Such and such thing would happen 10 frames after the other thing happened, etc.

    I decided this probably isn't a good idea...even if I used a technique to give an upper bound to the frame rate.

    What kind of things do you guys implement in your own game projects concerning this subject?

    Question 2: The entity heirarchy

    I was designing a lot of the class layout: how each class is related to other classes in the project. The latest thing I was working on is the entity heirarchy. This is my current plan:

    Base Class: ENTITY

    class ANIMATE_ENTITY derives from ENTITY
    class INANIMATE_ENTITY derives from ENTITY

    class RESOURCE derives from INANIMATE_ENTITY
    class EDIFICE derives from INANIMATE_ENTITY

    the list of things that would derive from ANIMATE_ENTITY has not yet been created.

    What do you guys think of that particular structure? Or more in particular, how have you structured entities in your own projects?
    My Website

    "Circular logic is good because it is."

  2. #2

    Join Date
    May 2005
    Posts
    1,042
    I decided this probably isn't a good idea...even if I used a technique to give an upper bound to the frame rate.

    What kind of things do you guys implement in your own game projects concerning this subject?
    I don't see exactly how you'd manage to have one class synchronize all of the events in the game. I personally think that you are already over-designing.

    The best way to synchronize with respect to time is to keep a single global clock. Each object adds the delta_time from the clock to its own internal (edited) time buffer. You can then make decisions based on how much time is in the buffer.

    This is how I tell certain doors to stay open, how long for an actor to wait between transitioning states, and when the physics should render a physics frame, etc.

    I'm not even going to touch upon the entity question. I think that it ultimately boils down to finding what works best for you...asking something like that online typically just opens endless ambiguous debate about the best way to do things.
    Last edited by BobMcGee123; 12-30-2006 at 01:07 PM.
    I'm not immature, I'm refined in the opposite direction.

  3. #3
    Question 1: A game clock

    I use a CSyncTimer class designed independantly of the frame rate just as you imagined. It performs several key functions.

    A) It controls the frame rate to keep things running smoothly. On fast frames, it pre-processes information for the next frame, and can optionally perform any number of sorting algos, and memory segment re-ordering (to speed up later access). On slow frames it is able to potentially flag some less important tasks as either to-be-dropped or to-be-performed-later. Most of this stuff may be a little over the top, but I got carried away...

    B) It exposes a timer, starting from the moment the app begins. Additional custom timers can be requested from the interface, starting and stopping at any point. The main timer is what you'd use to syncronize your events. You record the time an event last occured, then check how much time has passed since then to determine if it's time for the next event.

    Question 2: The entity heirarchy

    I have a base engine class with controls this type of thing, and is (ideally) designed to allow different types of games to use its system... Only time will tell if this actually works.

    I have a Base Entity class which has the potential to be basically any type of object. The Entity class holds all basic information, Location, Velocity, Mass, etc, as well as a pointer to a Base Visual class. This Base Visual class has several derived children which allow the entity to persist as either a Static Mesh, a Skinned Mesh, a Particle System, or any other custom object style you may desire, without changing the Entity interface.

    /* Edit */

    BobMcGee123 beats me to it, and ultimately, he's right, its all a matter of what works for you.
    "There's always another way"
    -lightatdawn (lightatdawn.cprogramming.com)

  4. #4
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    I don't see exactly how you'd manage to have one class synchronize all of the events in the game. I personally think that you are already over-designing.

    The best way to synchronize with respect to time is to keep a single global clock. Each object adds the delta_time from the clock to its own internal (edited) time buffer.
    Although I completely agree with you about the entities, I must say I agree with lightatdawn when it comes to the clock. Having each object hold an internal time variable seems quite clunky to me, and a little bad design.

    My plan was to create a very simple class that interfaces with the system clock, similar to what lightatdawn was explaining, but maybe a little less involved. The clock synchronization would insure that the animation of characters doesn't go too fast, although not limiting the actual frame rate itself, and also synchronize the sound effects and AI of with the animations and such.
    My Website

    "Circular logic is good because it is."

  5. #5

    Join Date
    May 2005
    Posts
    1,042
    A) It controls the frame rate to keep things running smoothly. On fast frames, it pre-processes information for the next frame, and can optionally perform any number of sorting algos, and memory segment re-ordering (to speed up later access). On slow frames it is able to potentially flag some less important tasks as either to-be-dropped or to-be-performed-later. Most of this stuff may be a little over the top, but I got carried away...
    None of this will be needed in a 2D game.

    The main timer is what you'd use to syncronize your events.
    ...
    Although I completely agree with you about the entities, I must say I agree with lightatdawn when it comes to the clock. Having each object hold an internal time variable seems quite clunky to me, and a little bad design.
    Timing occurs based on the time since something happened, or the amount of time in the time buffer (which is how I do things). Animations occur with a time buffer (accumulate the time), to get the interpolation values of the current animation keyframe time (least, this is how I did it when implementing a skeletal animation system based on keyframes...interpolating orientations). Either way, you end up holding an internal variable...you can't possibly get away from it.
    I'm not immature, I'm refined in the opposite direction.

  6. #6
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    One quick concern...I was looking at time.h, since it has been quite awhile since I have used any time functions. I was contemplating using the clock() function...but I was wondering if it's return value is clock_t, which is normally defined as an unsigned long, I could see it overflowing after an extended time of gameplay.
    My Website

    "Circular logic is good because it is."

  7. #7

    Join Date
    May 2005
    Posts
    1,042
    Yep. I use 64 bit large integers and high precision, when available:

    my time.h
    Code:
    #ifndef	TIMER_H
    #define	TIMER_H
    
    #include	<windows.h>
    
    /*
    	November	27, 2004
    	Class definition for a high resolution timer
    	Note that it would probably be a good idea to write the software for a high resolution timer as well 
    	as a LOW resolution timer, because right now if the system doesn't support high res timing ur fuxed
    */
    
    /*
    	A timer needs to keep track of the amount of time that has passed.  Anything can use a timer,
    	specifically objects that need to perform events based on a certain amount of time that has passed.
    	A timer needs to be able to:
    		-Keep a 'time buffer' (the amount of time that passed since the last time update)
    		-Reset the time buffer to zero
    		-Update the time buffer by 
    */
    class	HighResSecondsTimer
    {
    public:
    	HighResSecondsTimer()
    	{
    		if(sNumHighResTimers	==	0)
    		{
    			LARGE_INTEGER	Resolution;
    			QueryPerformanceFrequency(&Resolution);
    			unsigned	int	TicksPerSecond	=	Resolution.QuadPart;	//FIXME:	Don't want to do this EVERY time an object is made!
    			smInverseTicksPerSecond	=	1/(float)TicksPerSecond;
    		}
    		mTimeBuffer	=	0;
    		mLastTickCount	=	0;
    		LARGE_INTEGER	Temp	=	LARGE_INTEGER();
    		QueryPerformanceCounter(&Temp);
    		mLastTickCount	=	Temp.QuadPart;
    	}
    	~HighResSecondsTimer()
    	{
    
    	}
    
    	void	Update(); //Adds the time passed SINCE THE LAST UPDATE to the time buffer
    	void	ResetTimeBuffer()//Resets the number of seconds in the time buffer
    	{
    		mTimeBuffer	=	0;
    		LARGE_INTEGER	temp;
    		QueryPerformanceCounter(&temp);
    		mLastTickCount	=	temp.QuadPart;
    	}
    	
    	static		int	sNumHighResTimers;
    	//The quadpart of the LARGE_INTEGER actually holds the tick count
    	__int64			mLastTickCount;
    	float			mTimeBuffer;	//Seconds
    	static	float	smInverseTicksPerSecond;	//static member inverse ticks per second
    };
    
    /*
    	January	21	
    	Low resolution timer, relies on timeGetTime()
    */
    class	LowResSecondsTimer
    {
    public:
    	LowResSecondsTimer()
    	{
    		mTimeBuffer	=	0;
    		mLastTimeCount	=	timeGetTime();
    	}
    	~LowResSecondsTimer()
    	{
    
    	}
    	
    	void	Update();//Adds the time passed SINCE THE LAST UPDATE to the time buffer
    	
    	//Not sure if I should also set the lasttimecount to the time this function was called...hmm
    	void	ResetTimeBuffer()
    	{
    		mTimeBuffer	=	0;
    		mLastTimeCount	=	timeGetTime();
    	}
    	
    	int		mLastTimeCount;	
    	float	mTimeBuffer;	//Seconds
    };
    
    #endif
    time.cpp
    Code:
    #include	"Timer.h"
    
    //Static member variables must be initialized and this must be done so here
    int	HighResSecondsTimer::sNumHighResTimers	=	0;
    float	HighResSecondsTimer::smInverseTicksPerSecond	=	0;
    
    void	HighResSecondsTimer::Update()
    {
    	LARGE_INTEGER	CurrentTickCount;
    	QueryPerformanceCounter(&CurrentTickCount);
    	__int64	tickdiff = CurrentTickCount.QuadPart	-	mLastTickCount;
    	
    	mLastTickCount	=	CurrentTickCount.QuadPart;
    	
    	float	timefrac = tickdiff * smInverseTicksPerSecond;
    	
    	mTimeBuffer	+=	timefrac;
    }
    
    void	LowResSecondsTimer::Update()
    {
    	int	thisTime = timeGetTime();
    	int	msDiff	 = thisTime	-	mLastTimeCount;
    	mLastTimeCount	=	thisTime;
    	mTimeBuffer	+=	(float)(msDiff*.001);
    }
    I'm not immature, I'm refined in the opposite direction.

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Why floats? . . .

    I was wondering if it's return value is clock_t, which is normally defined as an unsigned long, I could see it overflowing after an extended time of gameplay.
    Yeah, it could. From http://math.nist.gov/oommf/software-...20021119.patch
    For an 4-byte wide
    ! // clock_t with CLOCKS_PER_SECOND at 1024, the time to overflow is
    ! // about 48.5 days (Windows NT). With CLOCKS_PER_SECOND at 1000000
    // overflow time is just over 71 minutes (Linux/x86).
    OTOH, an 8-byte
    // wide clock_t with a nanosecond tick rate takes over 584 years to
    ! // overflow.
    With time_t values (which time() returns) you're safe until 2038 though. http://pw1.netcom.com/~rogermw/Y2038.html

    What kind of things do you guys implement in your own game projects concerning this subject?
    I implemented my own which bears remarkable similarity to SDL_gfx's fraterate functions. It just uses the SDL time functions which return millisecond values, and are accurate to about 10 ms on most computers.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    ah I forgot that SDL has some time related functions. They should be good to use since I am using SDL as my main library here.

    I calculated out that the 32-bit unsigned long shouldnt overflow until about 49 days out (which was confirmed by dwks)...so I might stick with that then. If anyone plays my game for more than 49 days then they deserve to get off their computer and do something else.
    My Website

    "Circular logic is good because it is."

  10. #10
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Did you know that that bug was in Windows 98? If you left it on for about 49 days it would crash because the timing variables wrapped.

    I can post my timing code if you like. Or take a look at the SDL_gfx timing functions, which are pratically identical. http://www.ferzkopp.net/joomla/content/view/19/14/
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  11. #11
    l'Anziano DavidP's Avatar
    Join Date
    Aug 2001
    Location
    Plano, Texas, United States
    Posts
    2,743
    just an interesting note:

    I just noticed that in both versions of time.h that I have: one from MS Visual Studio and the other from gcc, clock_t is defined in both as long and not as an unsigned long.

    That cuts it to 24 days.
    My Website

    "Circular logic is good because it is."

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Yeah, that was mentioned in my link. For time_t variables, anyway, and the same points apply to clock_t ones. http://pw1.netcom.com/~rogermw/Y2038.html
    What about making time_t unsigned in 32-bit software?

    One of the quick-fixes that has been suggested for existing 32-bit software is to re-define time_t as an unsigned integer instead of a signed integer. An unsigned integer doesn't have to waste one of its bits to store the plus/minus sign for the number it represents. This doubles the range of numbers it can store. Whereas a signed 32-bit integer can only go up to 2 147 483 647, an unsigned 32-bit integer can go all the way up to 4 294 967 295. A time_t of this magnitude could represent any date and time from 12:00:00 AM 1-Jan-1970 all the way out to 6:28:15 AM 7-Feb-2106, surely giving us more than enough years for 64-bit software to dominate the planet.
    It sounds like a good idea at first. We already know that most of the standard time_t handling functions don't accept negative time_t values anyway, so why not just make time_t into a data type that only represents positive numbers?

    Well, there's a problem. time_t isn't just used to store absolute dates and times. It's also used, in many applications, to store differences between two date/time values, i.e. to answer the question of "how much time is there between date A and date B?". (MFC's CTimeSpan class is one notorious example.) In these cases, we do need time_t to allow negative values. It is entirely possible that date B comes before date A. Blindly changing time_t to an unsigned integer will, in these parts of a program, make the code unusable.

    Changing time_t to an unsigned integer would, in most programs, be robbing Peter to pay Paul. You'd fix one set of bugs (the Year 2038 Problem) only to introduce a whole new set (time differences not being computed properly).
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  13. #13
    You could always check for overflow, reset clock, and set a flag to reset all referencing timers. I'd be interested to play this game thats so addictive I won't stop for 24 days though!

    And to clarify; My individual classes requiring timed events do contain a timing variable. All timing variable are not entirely contained in the CSyncTimer class, just the clock and related functionality. I suppose they easily could, but it would likely incur extra overhead to access/set-up.
    "There's always another way"
    -lightatdawn (lightatdawn.cprogramming.com)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Best Graphics Library for 2d RTS?
    By arew264 in forum Game Programming
    Replies: 4
    Last Post: 04-18-2007, 12:05 PM
  2. Forming RTS Demo Team – Sixth Extinction
    By SteelRain4eva in forum Projects and Job Recruitment
    Replies: 0
    Last Post: 06-08-2006, 08:47 PM
  3. General Advice (RTS Game)
    By b00l34n in forum Game Programming
    Replies: 2
    Last Post: 05-07-2005, 12:34 PM
  4. 2D RTS Games
    By Lurker in forum Game Programming
    Replies: 9
    Last Post: 05-19-2004, 07:26 PM
  5. 2 player RTS
    By kooma in forum Game Programming
    Replies: 16
    Last Post: 02-21-2002, 10:10 AM