I'm writing Conways game of life in C with SDL. The game logic and everything works fine but I want to have it when you press p for example that the game automatically updates until p is pressed again and the loop stops. Somehow it only repeats once and doesn't even register the second time p is pressed.
else if (e.key.keysym.sym == SDLK_p){
bool stop = false;
while (!stop){
nextEpoch();
updateGame(window, renderer, r);
msleep(500);
if (e.type == SDL_KEYDOWN){
if (e.key.keysym.sym == SDLK_p){
stop = true;
printf("s\n");
}
}
}
}
It doesn't register that p is pressed when it in the while-loop.
Here's the full code: https://gist.github.com/PhilippRados/2df8760cc55822c2eac62addafb24403
As already pointed out by someone else in the comments section, you are not updating e in the inner loop. If you want to update e with a new event, you must call SDL_PollEvent( &e ) again to fill it with a new event.
In your linked code, you seem to be attempting to implement a new state of your program outside the main event loop, which represents the running state of the program, whereas the main event loop represents the paused state of the program. That way, you effectively have two separate event loops, one for each state of the program. While it is possible to make your program work this way, I don't recommend it.
For example, the way you have designed your program, your program won't respond to SDL_QUIT events in the running state. It will only do so in the paused state.
Therefore, it would make more sense to have a single event loop for both the running and the paused states of your program.
I don't recommend that you call usleep or SDL_Delay for waiting until it is time to render the next frame, as your program will not be responding to user input during this time. Especially since you have a very low frame rate of 2 frames per second, this means that it will take up to half a second for your program to respond to user input (for example if the user resizes the window or attempts to close it). Instead, I recommend that you set up a timer using SDL_AddTimer. You can program the timer callback function to give you a SDL_USEREVENT event twice per second. That way, when you receive this event, you will know that it is time to update the game and render the next frame. While waiting for this event, your program will still be able to respond to other events.
Note that in order to use SDL timers, you must initialize the corresponding subsystem using the SDL_INIT_TIMER flag when calling SDL_Init. Strangely, your linked code does not seem to call SDL_Init at all, so I am surprised that your code works at all. According to the documentation, you should call SDL_Init like this:
SDL_Init( SDL_INIT_TIMER | SDL_INIT_VIDEO );
Also, calling SDL_PollEvent in a loop seems like a big waste of CPU resources, as your CPU usage will likely be 100% although you are effectively doing nothing most of the time. I recommend that you call SDL_WaitEvent instead, so that the thread will sleep until it receives a new event to respond to.
Another thing worth noting is that when handling an SDL_MOUSEBUTTONDOWN event, it does not seem appropriate to use the result of SDL_GetMouseState to determine the coordinates of the mouse click, as that function will return the current mouse coordinates and not the coordinates at the time of the click. Therefore, it would be more appropriate to read these coordinates from the SDL_MouseButtonEvent structure.
Here is an example of how you could rewrite your event loop to use SDL_WaitEvent instead of SDL_PollEvent and to support both a running and a paused state in the main event loop.
Uint32 my_callbackfunc( Uint32 interval, void *param )
{
SDL_Event e;
e.user.type = SDL_USEREVENT;
e.user.code = 0;
e.user.data1 = NULL;
e.user.data2 = NULL;
SDL_PushEvent( &e );
return interval;
}
int main (int argc, char** argv)
{
[...]
//set timer to trigger twice per second
SDL_TimerID timer = SDL_AddTimer( 500, my_callbackfunc, NULL );
if ( timer == 0 ) {
//TODO: handle error
return EXIT_FAILURE;
}
//start game in a paused state
bool paused = true;
while ( SDL_WaitEvent( &e ) ) {
switch ( e.type ) {
case SDL_QUIT:
goto quit_game;
case SDL_WINDOWEVENT:
//rerender in case of window state change
updateGame( window, renderer, r );
break;
case SDL_USEREVENT:
if ( !paused ) {
nextEpoch();
updateGame(window, renderer, r);
}
break;
case SDL_MOUSEBUTTONDOWN:
mouseX = getNearestMultiple( e.button.x ) / RECT_SIZE;
mouseY = getNearestMultiple( e.button.y) / RECT_SIZE;
if ( Field[mouseX][mouseY] ) {
//Deactivate cell
Field[mouseX][mouseY] = false;
updateGame(window,renderer,r);
}
else {
//activate cell at position x,y
Field[mouseX][mouseY] = true;
updateGame(window,renderer,r);
}
break;
case SDL_KEYDOWN:
switch ( e.key.keysym.sym ) {
case SDLK_SPACE:
if ( paused ) {
nextEpoch();
updateGame(window, renderer, r);
}
break;
case SDLK_r:
memset(Field,0,sizeof(Field[0][0]) * WIDTH * HEIGHT);
memset(nextState,0,sizeof(nextState[0][0]) * WIDTH * HEIGHT);
updateGame(window,renderer, r);
break;
case SDLK_p:
paused = !paused;
}
}
}
quit_game:
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
Related
We have several tasks running on an STM32 MCU. In the main.c file we call all the init functions for the various threads. Currently there is one renewing xTimer to trigger a periodic callback (which, at present, does nothing except print a message that it was called). Declarations as follows, outside any function:
TimerHandle_t xMotorTimer;
StaticTimer_t xMotorTimerBuffer;
EventGroupHandle_t MotorEventGroupHandle;
In the init function for the thread:
xMotorTimer = xTimerCreateStatic("MotorTimer",
xTimerPeriod,
uxAutoReload,
( void * ) 0,
MotorTimerCallback,
&xMotorTimerBuffer);
xTimerStart(xMotorTimer, 100);
One thread starts an infinite loop that pauses on an xEventGroupWaitBits() to determine whether to enter an inner loop, which is then governed by its own state:
DeclareTask(MotorThread)
{
bool done = false;
EventBits_t event;
for (;;)
{
Packet * pkt = NULL;
event = xEventGroupWaitBits( MotorEventGroupHandle,
EVT_MOTOR_START | EVT_MOTOR_STOP, // EventBits_t uxBitsToWaitFor
pdTRUE, // BaseType_t xClearOnExit
pdFALSE, // BaseType_t xWaitForAllBits,
portMAX_DELAY //TickType_t xTicksToWait
);
if (event & EVT_MOTOR_STOP)
{
MotorStop(true);
}
if (event & EVT_MOTOR_START)
{
EnableMotor(MOTOR_ALL);
done = false;
while (!done && !abortTest)
{
xQueueReceive(motorQueue, &pkt, portMAX_DELAY);
if (pkt == NULL)
{
done = true;
} else {
done = MotorExecCmd(pkt);
done = ( uxQueueMessagesWaiting(motorQueue) == ( UBaseType_t ) 0);
FreePacket(pkt);
}
}
}
}
}
xEventGroupWaitBits() fires successfully once, the inner loop enters, then exits when the program state meets the expected conditions. The outer loop repeats as it should, but when it arrives again at the xEventGroupWaitBits() call, it crashes almost instantly. In fact, it crashes a few lines down into the wait function, at a call to uxTaskResetEventItemValue(). I can't even step the debugger into the function, as if calling a bad address. But if I check the disassembly, the memory address for the BL instruction hasn't changed since the previous loop, and that address is valid. The expected function is actually there.
I can prevent this chain of events happening altogether by not calling that xTimerStart() and leaving everything else as-is. Everything runs just fine, so it's definitely not xEventGroupWaitBits() (or at least not just that). We tried switching to xEventGroupGetBits() and adding a short osDelay to the loop just as an experiment. That also froze the whole system.
So, main question. Are we doing something FreeRTOS is not meant to do here, using xEventGroupWaitBits() with xTimers running? Or is there supposed to be something between xEventGroupWaitBits() calls, possibly some kind of state reset that we've overlooked? Reviewing the docs, I can't see it, but I could have missed a detail. The
I'm creating a game in c. the game has a function called loop(), which calls other functions and has them update during each game loop.
The game character is simply a char image.
The issue that i'm having, is that I want this char image to continuously move in a direction after 1 keypress is made, however, I can only seem to get it to move once per keypress, meaning it needs to be held down to get continuous movement.
Things I tried:
I made a while loop, to specify that the char would continuously move until it hit a boundary on the screen...The issue I had here was the fact that the while loop was nested inside the entire games while loop, meaning that the nested loop, looped through all iterations inside the range of the nested loop before the outloop had executed it once, meaning the game character instantly jumped to the boundary of the game.
tried it with an if statement, which as expected only executes once.
I also tried to use boolean to read true once a keypress was made, and tried several different versions of this.
I just need ideas on how to get a char image to move continuously based on its stored velocity, and have the image step once per iteration of the entire game loop.
int is_moving = 0, speed = 0;
char direction = '0';
while(1){ //game loop
if(/* north keypress event */){
is_moving = 1;
speed = 1;
direction = 'n';
}
else if(/* east keypress event */){
is_moving = 1;
speed = 1;
direction = 'e';
}
else if(/* south keypress event */){
is_moving = 1;
speed = 1;
direction = 's';
}
else if(/* west keypress event */){
is_moving = 1;
speed = 1;
direction = 'w';
}
for(int steps = 0; steps < speed && is_moving; ++steps){
/* movement logic and other checks go here */
}
++speed;
}
Just make sure to set is_moving to 0 once the character should stop moving. Obviously this is just a simple example, but it sounds like you already have the directional component of the velocity figured out.
Edit:
Within the body of the for loop, you'll need to check the value of direction and then have the character move appropriately.
I have this loop, how would I end the loop?
void loop() {
// read the pushbutton input pin:
a ++;
Serial.println(a);
analogWrite(speakerOut, NULL);
if(a > 50 && a < 300){
analogWrite(speakerOut, 200);
}
if(a <= 49){
analogWrite(speakerOut, NULL);
}
if(a >= 300 && a <= 2499){
analogWrite(speakerOut, NULL);
}
This isn't published on Arduino.cc but you can in fact exit from the loop routine with a simple exit(0);
This will compile on pretty much any board you have in your board list. I'm using IDE 1.0.6. I've tested it with Uno, Mega, Micro Pro and even the Adafruit Trinket
void loop() {
// All of your code here
/* Note you should clean up any of your I/O here as on exit,
all 'ON'outputs remain HIGH */
// Exit the loop
exit(0); //The 0 is required to prevent compile error.
}
I use this in projects where I wire in a button to the reset pin. Basically your loop runs until exit(0); and then just persists in the last state. I've made some robots for my kids, and each time the press a button (reset) the code starts from the start of the loop() function.
Arduino specifically provides absolutely no way to exit their loop function, as exhibited by the code that actually runs it:
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
Besides, on a microcontroller there isn't anything to exit to in the first place.
The closest you can do is to just halt the processor. That will stop processing until it's reset.
Matti Virkkunen said it right, there's no "decent" way of stopping the loop. Nonetheless, by looking at your code and making several assumptions, I imagine you're trying to output a signal with a given frequency, but you want to be able to stop it.
If that's the case, there are several solutions:
If you want to generate the signal with the input of a button you could do the following
int speakerOut = A0;
int buttonPin = 13;
void setup() {
pinMode(speakerOut, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
int a = 0;
void loop() {
if(digitalRead(buttonPin) == LOW) {
a ++;
Serial.println(a);
analogWrite(speakerOut, NULL);
if(a > 50 && a < 300) {
analogWrite(speakerOut, 200);
}
if(a <= 49) {
analogWrite(speakerOut, NULL);
}
if(a >= 300 && a <= 2499) {
analogWrite(speakerOut, NULL);
}
}
}
In this case we're using a button pin as an INPUT_PULLUP. You can read the Arduino reference for more information about this topic, but in a nutshell this configuration sets an internal pullup resistor, this way you can just have your button connected to ground, with no need of external resistors.
Note: This will invert the levels of the button, LOW will be pressed and HIGH will be released.
The other option would be using one of the built-ins hardware timers to get a function called periodically with interruptions. I won't go in depth be here's a great description of what it is and how to use it.
The three options that come to mind:
1st) End void loop() with while(1)... or equally as good... while(true)
void loop(){
//the code you want to run once here,
//e.g., If (blah == blah)...etc.
while(1) //last line of main loop
}
This option runs your code once and then kicks the Ard into
an endless "invisible" loop. Perhaps not the nicest way to
go, but as far as outside appearances, it gets the job done.
The Ard will continue to draw current while it spins itself in
an endless circle... perhaps one could set up a sort of timer
function that puts the Ard to sleep after so many seconds,
minutes, etc., of looping... just a thought... there are certainly
various sleep libraries out there... see
e.g., Monk, Programming Arduino: Next Steps, pgs., 85-100
for further discussion of such.
2nd) Create a "stop main loop" function with a conditional control
structure that makes its initial test fail on a second pass.
This often requires declaring a global variable and having the
"stop main loop" function toggle the value of the variable
upon termination. E.g.,
boolean stop_it = false; //global variable
void setup(){
Serial.begin(9600);
//blah...
}
boolean stop_main_loop(){ //fancy stop main loop function
if(stop_it == false){ //which it will be the first time through
Serial.println("This should print once.");
//then do some more blah....you can locate all the
// code you want to run once here....eventually end by
//toggling the "stop_it" variable ...
}
stop_it = true; //...like this
return stop_it; //then send this newly updated "stop_it" value
// outside the function
}
void loop{
stop_it = stop_main_loop(); //and finally catch that updated
//value and store it in the global stop_it
//variable, effectively
//halting the loop ...
}
Granted, this might not be especially pretty, but it also works.
It kicks the Ard into another endless "invisible" loop, but this
time it's a case of repeatedly checking the if(stop_it == false) condition in stop_main_loop()
which of course fails to pass every time after the first time through.
3rd) One could once again use a global variable but use a simple if (test == blah){} structure instead of a fancy "stop main loop" function.
boolean start = true; //global variable
void setup(){
Serial.begin(9600);
}
void loop(){
if(start == true){ //which it will be the first time through
Serial.println("This should print once.");
//the code you want to run once here,
//e.g., more If (blah == blah)...etc.
}
start = false; //toggle value of global "start" variable
//Next time around, the if test is sure to fail.
}
There are certainly other ways to "stop" that pesky endless main loop
but these three as well as those already mentioned should get you started.
This will turn off interrupts and put the CPU into (permanent until reset/power toggled) sleep:
cli();
sleep_enable();
sleep_cpu();
See also http://arduino.land/FAQ/content/7/47/en/how-to-stop-an-arduino-sketch.html, for more details.
just use this line to exit function:
return;
I'm programming a robot, and unfortunately in its autonomous mode I'm having some issues.
I need to set an integer to 1 when a button is pressed, but in order for the program to recognize the button, it must be in a while loop. As you can imagine, the program ends up in an infinite loop and the integer values end up somewhere near 4,000.
task autonomous()
{
while(true)
{
if(SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
wait1Msec(0350);
}
}
}
I've managed to get the value by using a wait, but I do NOT want to do this. Is there any other way I can approach this?
assuming that the SensorValue comes from a physical component that is asynchronous to the while loop, and is a push button (i.e. not a toggle button)
task autonomous()
{
while(true)
{
// check whether
if(current_time >= next_detect_time && SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
// no waiting here
next_detect_time = current_time + 0350;
}
// carry on to other tasks
if(enemy_is_near)
{
fight();
}
// current_time
current_time = built_in_now()
}
}
Get the current time either by some built-in function or incrementing an integer and wrap around once reach max value.
Or if you are in another situation:
task autonomous()
{
while(true)
{
// check whether the flag allows incrementing
if(should_detect && SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect + 1;
// no waiting here
should_detect = false;
}
// carry on to other tasks
if(enemy_is_near)
{
if(fight() == LOSING)
should_detect = true;
}
}
}
Try remembering the current position of the button, and only take action when its state changes from off to on.
Depending on the hardware, you might also get a signal as though it flipped back and forth several times in a millisecond. If that's an issue, you might want to also store the timestamp of the last time the button was activated, and then ignore repeat events during a short window after that.
You could connect the button to an interrupt and then make the necessary change in the interrupt handler.
This might not be the best approach, but it will be the simplest.
From The Vex Robotics catalogue :
(12) Fast digital I/O ports which can be used as interrupts
So, most probably which ever micro-controller of Vex you are using will support Interrupts.
Your question is a bit vague
I m not sure why u need this variable to increment and how things exactly work...but i ll make a try.Explain a bit more how things work for the robot to move...and we will be able to help more.
task autonomous()
{
int buttonPressed=0;
while(true)
{
if(SensorValue[positionSelectButton] == 1)
{
positionSelect = positionSelect +1;
buttonPressed=1;
}
else{
buttonPressed = 0;
}
//use your variables here
if( buttonPressed == 1){
//Move robot front a little
}
}
}
The general idea is :
First you detect all buttons pressed and then you do things according to them
All these go in your while loop...that will(and should) run forever(at least as long as your robot is alive :) )
Hope this helps!
I've been trying to animate in a C program using Xlib and I wanna do something when an event occurs, otherwise I wanna keep animating. Here's an example code snippet of what I am doing currently:
while( 1 )
{
// If an event occurs, stop and do whatever is needed.
// If no event occurs, skip this if statement.
if ( XEventsQueued( display, QueuedAlready ) > 0 )
{
XNextEvent( display, &event )
switch ( event.type )
{
// Don't do anything
case Expose:
while ( event.xexpose.count != 0 )
break;
// Do something, when a button is pressed
case ButtonPress:
...
break;
// Do something, when a key is pressed
case KeyPress:
...
break;
}
}
animate(); // Do animation step i.e. change any drawings...
repaint(); // Paint again with the new changes from animation...
}
So basically, I wanna keep looping if the user hasn't clicked the mouse OR pressed a key in the keyboard yet. When the user presses a key OR clicks the mouse, I wanna stop and do a specific action. The problem in my above code is that, it doesnt stop whenever I do an action. If I remove the if statement, the animation blocks until an event occurs, however I do not want this. It's a simple problem, but I'm kinda new to Xlib/animations so any help would be highly appreciated. Thanks.
Use the file descriptor returned by ConnectionNumber(display) with select() and use the timeout argument. If select() returns 0, then draw some more frames. Remember to call XSync() before you select() so that the X server gets your update.
int fd,r;
struct timeval tv;
FD_SET rfds;
fd=ConnectionNumber(display);
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
memset(&tv,0,sizeof(tv));
tv.tv_usec = 100000; /* delay in microseconds */
r=select(fd+1,&rfds,0,0,&tv);
if(r == 0) { /* draw frame */ }
else if (r < 0) { /* error; try again if errno=EINTR */ }
else { /* pull events out */ }