Advertisement

clock ticks

Started by January 23, 2025 01:37 AM
10 comments, last by pbivens67 7 hours, 39 minutes ago

I want to use clock ticks in seconds to move the tortise/hare in the toretise/hare race game. I am new to this concept so I need a little help.

If you are a beginner you can just use GetTickCount() which returns how many milliseconds have passed. Typically this function only returns in increments of like 10 milliseconds. float(GetTickCount()) /1000.0 will get you how much time passed per game loop to move something based on real time.

NBA2K, Madden, Maneater, Killing Floor, Sims

Advertisement

Here's some example using std::chrono.
It's cross plarform and accurate.

#include <chrono>

void main()
{
	std::chrono::time_point prevTime = std::chrono::steady_clock::now();
	while (true)
	{
		const std::chrono::time_point curTime = std::chrono::steady_clock::now();
		float seconds = std::chrono::duration<float> (curTime - prevTime).count();
		prevTime = curTime; 
		
		Print ("Frame took %f seconds.", seconds);
		
		myGame.Update(seconds);
		myGame.Render();
	}
}

Edited after proposal below.

JoeJ said:
Here's some example using std::chrono. It's cross plarform and accurate.

+1 for using chrono, though, high_resolution_clock is not the best choice, especially since you mentioned cross-platform. high_resolution_clock is atm just a typedef for either system or steady-clock, depending on the platform and compiler-configuration. See the notes section of cpp-reference (https://en.cppreference.com/w/cpp/chrono/high_resolution_clock), specifically:

It is often just an alias for std::chrono::steady_clock or std::chrono::system_clock, but which one it is depends on the library or configuration. When it is a system_clock, it is not monotonic (e.g., the time can go backwards). For example, as of 2023, libstdc++ has it aliased to system_clock "until higher-than-nanosecond definitions become feasible"[1], MSVC has it as steady_clock[2], and libc++ uses steady_clock when the C++ standard library implementation supports a monotonic clock and system_clock otherwise[3].

So unless you want your non-windows builds to potentially fallback to a non-monotonic system_clock when the specific compiler feels like it, use steady_clock instead 🙂

Juliean said:
use steady_clock instead

Oh thanks, good to know!
Currently i'm using still QueryPerformanceCounter(), but want to use std::chorno instead. This will help.

Juliean said:
+1 for using chrono

Even after a hour on cpp-reference i could not figure out how to get microseconds or nanoseconds. It's so confusing!
So yes - i think everybody who uses std::chrono deserves at least +1. :D

JoeJ said:
Even after a hour on cpp-reference i could not figure out how to get microseconds or nanoseconds. It's so confusing!

Its not really obvious, yes, because its not sometihng on the time_point or duration-variables itself. But its also simple if you know that you have to use duration_cast:

const auto start = std::chrono::system_clock::now();

// longer task

const auto msPassed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);

I think I also only found this in a stackoverflow answer and not cpp-reference :D

Advertisement

how do I incorporate the clock into my tortoise/hare game to move the animals

pbivens67 said:
how do I incorporate the clock into my tortoise/hare game to move the animals

I guess that's a board game, and you want to animate some animal moving from point A to point B.
There are many ways to do this, and your options depend on the data you have already,
or what things you need to add depending on the option you pick.

I see those two primary options:

1. The character knows it's current position and the target position, and a third position for display which is animated.
While it's moving, only the animated position changes, doing some simple math to move along the line from current to target.
Only if the animated position reaches the target, the current position is set to the target, since at this point the information about the former current place is no longer needed.
(Makes sense for a turn based game, not so much for action games.)

2. The character knows only it's current position and the target.
We do some simple math so it moves from it's current position to the target at constant speed.
(Good for action games, but may be fine for a turn based board game too.)

So, i think the hard part her is not the simple math, but the design choices you need to make.
The problem with animation is the duration it takes to get from A to B.
During that time, it may not be allowed to interrupt the process, or it can be difficult to decide what the program should do while the animation is running, other than updating the animation itself.
Usually a state machine is used to manage the logic. Your states might be: ‘standing idle’, ‘moving to target’, ‘kill another character’, etc.

Here is some pseudo code, but really simple without much state:

struct NPC
{
	int posX = 0;
	int posY = 0;	
};

void main()
{
	// some global state:
	NPC npc;
	int targetX = 0;	
	int targetY = 0;	
	
	while (true) // game loop
	{
		// update the target if the user clicks on screen
		if (MouseButtonPressed()) 
		{
			targetX = MouseGetX();
			targetY = MouseGetY();
		}
		
		// move the NPC towards the target
		if (npc.posX > targetX) npc.posX -= 1; // we move just one unit each frame, ignoring a desired speed for now
		if (npc.posX < targetX) npc.posX += 1;
		if (npc.posY > targetY) npc.posY -= 1;
		if (npc.posY < targetY) npc.posY += 1;
		
		DrawNPC(npc);
	}
}

You see that's very simple, but it's a good start.
Ofc. if your game runs at 1000 fps, it will move too fast. This could be adressed with using floating point positons, a floating point timestep, and a given desired speed. (My std::chrono code snippet calculates the ‘timestep’ or ‘delta time’ in seconds, for example.)

Reagarding data structures, say we have many NPCs in our game, and they all should run to the target.
This would be a motivation to mkae the movement logic a member function our NPC struct (or a global function if you would prefer):

struct NPC
{
	int posX = 0;
	int posY = 0;	
	
	void MoveToTarget (int targetX, int targetY)
	{
		if (posX > targetX) posX -= 1; // we move just one unit each frame, ignoring a desired speed for now
		if (posX < targetX) posX += 1;
		if (posY > targetY) posY -= 1;
		if (posY < targetY) posY += 1;
	}
};

void main()
{
	// some global state:
	NPC npcs[3];
	npcs[0].posX = 10;
	npcs[1].posX = 20;
	npcs[2].posX = 30;
	
	int targetX = 0;	
	int targetY = 0;	
	
	while (true) // game loop
	{
		// update the target if the user clicks on screen
		if (MouseButtonPressed()) 
		{
			targetX = MouseGetX();
			targetY = MouseGetY();
		}
		
		// move the NPCs towards the target
		for (int i=0; i<3; i++)
			npcs[i].MoveToTarget (targetX, targetY);
		
		DrawNPCs(npcs, 3);
	}
}

Notice it's still the same amount of code, almost. But now you handle 3 or more NPCs without writing the same logic multiple times.
That's the kind of stuff you have to start using… ; )

here is my code hope this helps

#include <iostream>
#include <time.h>
#include "Race.h"

using namespace std;

Race::Race() {}
Race::~Race() {}

void Race::draw_screen()
{
	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 70; y++)
		{
			track[x][y] = (char)254u;
		}
		cout << endl;
	}

	track[0][0] = 'T';
	track[1][0] = 'H';

	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 70; y++)
		{
			cout << track[x][y] << " ";
		}
		cout << endl;
	}
}

void Race::move_tortise(int& num)
{
	track[i][j] = (char)254u;
	if (num >= 1 && num <= 5)
	{
		j += 5;
	}
	else if (num >= 6 && num <= 7)
	{
		j -= 6;
	}
	else if (num >= 8 && num <= 10)
	{
		j++;
	}
	if (j < 1)
	{
		j = 1;
	}
	track[i][j] = 'T';
	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 70; y++)
		{
			cout << track[x][y] << " ";
		}
		cout << endl;
	}
}

void Race::move_hare(int& num)
{
	track[m][n] = (char)254u;
	if (num >= 1 && num <= 2)
	{
		//Do Nothing
	}
	else if (num >= 3 && num <= 4)
	{
		n += 9;
	}
	else if (num == 5)
	{
		n -= 12;
	}
	else if (num >= 6 && num <= 8)
	{
		n++;
	}
	else if (num >= 9 && num <= 10)
	{
		n -= 2;
	}
	if (n < 1)
	{
		n = 1;
	}
	track[m][n] = 'H';
	for (int x = 0; x < 2; x++)
	{
		for (int y = 0; y < 70; y++)
		{
			cout << track[x][y] << " ";
		}
		cout << endl;
	}
}

int main()
{
	Race race;
	srand(time(NULL));
	race.draw_screen();
	int num_one = rand() % 10 + 1;
	int num_two = rand() % 10 + 1;
	race.move_tortise(num_one);
	race.move_hare(num_two);

	return 0;
}


Are you familiar with velocity and acceleration?

Advertisement