How can I use multiple timers to plan events in my program? - c++

I am building a rockband-like program using C++ and SDL, and want to be able to time events so I can orchestrate a song in the program. Here is what I have accomplished so far:
4 circles which fall from the top of the window to the middle into 4 designated hitting spots.
The circles drop at random intervals (not using time, a random number generator determines how far from the top of the window they begin to fall)
I am able to determine when a note is hit, and a score is displayed in the top right hand corner
Simple sparks are applied around a marker to let you know a note was hit
I can open a file and read text from it
Now I want to be able to use that file to write songs for the program to read and execute. I was thinking something along the lines of "1g,2g,4y,3r etc. etc. etc." the numbers being milliseconds to wait until the next note and the letters designating which color should fall.

You don't really need (or want) multiple timers; just the single timer that drives your window refresh (at 30fps or whatever) is sufficient.
When you load in your song file, for each note in the song you should store the number of milliseconds that should elapse between the moment the song starts playing and the moment that particular note is played, e.g (pseudocode):
int millisCounter = 0;
int note, noteLengthMillis;
while(ReadNextNoteFromSongFile(note, noteLengthMillis))
{
songNoteRecordsVector.push_back(NoteRecord(millisCounter, note));
millisCounter += noteLengthMillis;
}
Then, when you start the game level going, at the instant the song starts playing, record the current time in milliseconds. You will use this value as your time-zero reference for as long as the song keeps playing.
Now at every video-frame (or indeed at any time), you can calculate the number of milliseconds until a given note will be played, relative to the current system-clock-time:
int NoteRecord :: GetMillisecondsUntilNoteIsPlayed(int songStartTimeMillis, int currentTimeMillis) const
{
return this->myNoteOffsetMillis - (currentTimeMillis - songStartTimeMillis);
}
Note that the value returned will be negative if the note's time-to-be-played has already passed.
Once you have that, it's just a matter of converting each note's current milliseconds-until-note-is-played result into a corresponding on-screen position, and you know where to draw the note-circle for the current frame:
int millisUntilNotePlayTime = note.GetMillisecondsUntilNoteIsPlayed(songStartTimeMillis, currentTimeMillis);
int circleY = someFixedOffsetY + (millisUntilNotePlayTime/(1000/pixelsScrolledPerSecond));
DrawCircleAt(circleX, circleY);
... and if the user presses a key, you can calculate how far off the user was from the correct time for a given note using the same function, e.g.:
int errorMillis = note.GetMillisecondsUntilNoteIsPlayed(songStartTimeMillis, currentTimeMillis);
if (errorMillis < -50)
{
printf("You're too slow!\n");
}
else if (errorMillis > 50)
{
printf("You jumped the gun!\n");
}
else
{
printf("Good job!\n");
}

Related

How to call a function every x seconds but be able to do stuff in the meantime

I'm building a tetris game and I need the pieces to fall every x seconds; something like:
while(true){
moveDown();
sleep(x)
}
The problem is, I need to be able to move the pieces left and right in the meantime, i.e., call a function while it's sleeping.
How can I do that in c++?
Both time and key presses can be events which can be used to wait on. On UNIXes you'd use something like poll() with a suitable time for timeout and the input device used to recognize key presses. On other systems there are similar facilities (I'm a UNIX persons and I have never worked on Windows specific stuff although it seems the Windows facilities are actually more flexible). Depending on the result of poll() (timeout or activity on the I/O device in that case) you'd do the appropriate action.
This problem is solvable in multiple ways (another idea that comes to mind is multithreading, but that seems overkill). One approach would be to keep track of the number of "game cycles" and execute some function every n-th cycle like this:
for(int32_t count{1};;count++)
{
if (!count % 5)
{
// do something every 5th cycle
}
// do something every cycle
sleep(x);
}
you can measure how much time has passed since last fall and move piece down after given amount and then reset counter. In pseudo-code it could look like this:
while(true)
{
counter.update();
if(counter.value() == fall_period)
{
move_piece_down();
couter.reset();
}
// rotate pieces
}
If you are using typical implementation of game loop your counter can just accumulate elapsed time since last frame.

Problem with programming a basic hardware

I have an animation shown on LEDs. When the button is pressed, the animation has to stop and then continue after the button is pressed again.
There is a method that processes working with the button:
void checkButton(){
GPIO_PinState state;
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_RESET) {
while(1){
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_SET){
break;
}
}
//while (state == GPIO_PIN_RESET) {
//state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
//}
}
}
GPIO_PIN_SET is the default button position. GPIO_PIN_RESET is the condition when the button is pressed. The commented section is what I tried instead of the while(1){...} loop. The checkButton() method is called in the main loop from time to time to be run. The program runs on STM32 with an extension module (here the type of an extension module does not matter).
The fact is that this method stops animation just for a moment and does not work as I would like it to. Could you correct anything about this program to make it work properly?
Could you correct anything about this program to make it work
properly?
My guess is that you are trying to add a 'human interaction' aspect to your design. Your current approach relies on a single (button position) sample randomly timed by a) your application and b) a human finger. This timing is simply not reliable, but the correction is possibly not too difficult.
Note 1: A 'simple' mechanical button will 'bounce' during it's activation or release (yes, either way). This means that the value which the software 'sees' (in a few microseconds) is unpredictable for several (tbd) milliseconds(?) near the button push or release.
Note 2: Another way to look at this issue, is that your state value exists two places: in the physical button AND in the variable "GPIO_PinState state;". IMHO, a state value can only reside in one location. Two locations is always a mistake.
The solution, then (if you believe) is to decide to keep one state 'record', and eliminate the other. IMHO, I think you want to keep the button, which seems to be your human input. To be clear, you want to eliminate the variable "GPIO_PinState state;"
This line:
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
samples the switch state one time.
HOWEVER, you already know that this design can not rely on the one read being correct. After all, your user might have just pressed or released the button, and it is simply bouncing at the time of the sample.
Before we get to accumulating samples, you should be aware that the bouncing can last much more than a few microseconds. I've seen some switches bounce up to 10 milliseconds or more. If test equipment is available, I would hook it up and take a look at the characteristics of your button. If not, well, you can try the adjusting the controls of the following sample accumulator.
So, how do we 'accumulate' enough samples to feel confident we can know the state of the switch?
Consider multiple samples, spaced-in-time by short delays (2 controls?). I think you can simply accumulate them. The first count to reach tbr - 5 (or 10 or 100?) samples wins. So spin sample, delay, and increment one of two counters:
stateCount [2] = {0,0}; // state is either set or reset, init both to 0
// vvv-------max samples
for (int i=0; i<100; ++i) // worst case how long does your switch bounce
{
int sample = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15); // capture 1 sample
stateCount[sample] += 1; // increment based on sample
// if 'enough' samples are the same, kick out early
// v ---- how long does your switch bounce
if (stateCount[sample] > 5) break; // 5 or 10 or 100 ms
// to-be-determined --------vvv --- how long does switch bounce
std::this_thread::sleep_for(1ms); // 1, 3, 5 or 11 ms between samples
// C++ provides, but use what is available for your system
// and balanced with the needs of your app
}
FYI - The above scheme has 3 adjustments to handle different switch-bounce durations ... You have some experimenting to do. I would start with max samples at 20. I have no recommendation for sleep_for ... you provided no other info about your system.
Good luck.
It has been a long time, but I think I remember the push-buttons on a telecom infrastructure equipment bounced 5 to 15 ms.

Making a specific object pause C++

I am looking at trying to pause something in C++. Specifically a bullet you shoot in a space invaders game. Each time you press the UP key it fires a shot, I have been trying to find a way to pause it for a number of seconds before being able to fire again.
I've tried Sleep(); but it freezes the entire game rather than pauses the ability to press UP again.
Firing code
if (CInput::getInstance()->getIfKeyDownEvent(DIK_UP))
{
g_pGame->AddSprite(new CMissile(m_fX, m_fY+0.5*m_fH, 0.09, 0.9, 2));
}
Try taking the current time and then adding your delay to it. Store that in your shooting object. The next time through your program loop, if the current time is less than the time stored in the object, ignore the UP arrow.
Here are two simple ways to manage this.
When you fire a bullet, take the current system time and add the delay you want to it. If the player attempts to fire again while the current time is less than the variable you set, nothing happens.
Or, when you fire a bullet, set a timer variable to the delay you want. Each update, subtract delta time from the timer. When the timer is <=0, the user can fire.
Typically when you want to deal with real time seconds, you need something called delta time. Due to the inconsistency with frame rates, you need a way to measure real time. Typically you do this by counting the amount of time elapsed between frames. Here's an example of this implementation:
Source
int timeSinceStart = glutGet(GLUT_ELAPSED_TIME);
int oldTimeSinceStart = 0;
while( ... ) // main game loop
{
int timeSinceStart = glutGet(GLUT_ELAPSED_TIME);
int deltaTime = timeSinceStart - oldTimeSinceStart;
oldTimeSinceStart = timeSinceStart;
secondsSinceLastShot += deltaTime;
if (secondsSinceLastShot > shotTimer)
{
canShoot = true;
secondsSinceLastShot = 0;
}
if ( // press space or something )
{
canShoot = false;
// shoot
}
}
Note that this uses GLUT's implementation of a timer, but you need to implement that yourself (probably using clock()).
I've tried Sleep(); but it freezes the entire game rather than pauses the ability to press UP again.
Sleeping will freeze the thread, which is not what you want to do. However, sleep() is typically used in an implementation that contains delta time, usually sleep()ing for the amount of time elapsed between frames. For an example, see Lazy Foo's SDL tutorials
Ignore the fact that I linked to both OpenGL and SDL links, the principle is the same no matter the graphics library used.

running a background process on arduino

I am trying to get my arduino mega to run a function in the background while it is also running a bunch of other functions.
The function that I am trying to run in the background is a function to determine wind speed from an anemometer. The way it processes the data is similar to that of an odometer in that it reads the number of turns that the anemometer makes during a set time period and then takes that number of turns over the time to determine the wind speed. The longer time period that i have it run over the more accurate data i receive as there is more data to average.
The problem that i have is there is a bunch of other data that i am also reading in to the arduino which i would like to be reading in once a second. This one second time interval is too short for me to get accurate wind readings as not enough revolutions are being completed by the anemometer to give high accuracy wind data.
Is there a way to have the wind sensor function run in the background and update a global variable once every 5 seconds or so while the rest of my program is running simultaneously and updating the other data every second.
Here is the code that i have for reading the data from the wind sensor. Every time the wind sensor makes a revolution there is a portion where the signal reads in as 0, otherwise the sensor reads in as a integer larger than 0.
void windmeterturns(){
startime = millis();
endtime = startime + 5000;
windturncounter = 0;
turned = false;
int terminate = startime;
while(terminate <= endtime){
terminate = millis();
windreading = analogRead(windvelocityPin);
if(windreading == 0){
if(turned == true){
windturncounter = windturncounter + 1;
turned = false;
}
}
else if(windreading >= 1){
turned = true;
}
delay(5);
}
}
The rest of the processing of takes place in another function but this is the one that I am currently struggling with. Posting the whole code would not really be reasonable here as it is close to a 1000 lines.
The rest of the functions run with a 1 second delay in the loop but as i have found through trial and error the delay along with the processing of the other functions make it so that the delay is actually longer than a second and it varies based off of what kind of data i am reading in from the other sensors so a 5 loop counter for timing i do not think will work here
Let Interrupts do the work for you.
In short, I recommend using a Timer Interrupt to generate a periodic interrupt that measures the analog reading in the background. Subsequently this can update a static volatile variable.
See my answer here as it is a similar scenario, detailing how to use the timer interrupt. Where you can replace the callback() with your above analogread and increment.
Without seeing how the rest of your code is set up, I would try having windturncounter as a global variable, and add another integer that is iterated every second your main program loops. Then:
// in the main loop
if(iteratorVariable >= 5){
iteratorVariable = 0;
// take your windreading and implement logic here
} else {
iteratorVariable++;
}
I'm not sure how your anemometer stores data or what other challenges you might be facing, so this may not be a 100% solution, but it would allow you to run the logic from your original post every five seconds.

how to use time in C++

I am a beginner in programming and I started to create a game in C++ where the player disappears from the screen(he dies) when a rock hits it.
What can i do to put the player back on the screen after 2 seconds?
I have number of lifes(LifeNb) a function which delete the player from the screen(removePlayer) and a function that add the player on the screen(addPlayer).
How can i do this using ?
int time = std::clock()/1000;
if(the rock hit) {
number of lives --;
remove player;
if(time == 2)
add player;
}
It's something like this?
One way to do it: When your player dies, store the current time (plus two seconds) to a variable. On each iteration of the game's event loop, check to see if the current time is greater than or equal to the time in the variable. If it is, restore the player, and set the variable's value to (a very large value that the clock will never reach).
clock_t timer = clock();
if ((clock()/CLOCKS_PER_SEC)-(timer/CLOCKS_PER_SEC) >= 2)
player.add();
If you just want to wait two seconds, however, you could also use the system library function sleep() for two seconds.
The sleep() function will delay for a specified number of seconds before continuing execution. It seems to be what you are looking for.
See here: http://pubs.opengroup.org/onlinepubs/009604599/functions/sleep.html