Home > Enterprise >  How to implement independent movement apart from user input in Ncurses with C?
How to implement independent movement apart from user input in Ncurses with C?

Time:12-18

I'm trying to have an "o" move down independent from user input. I plan on using user input for another object. Without the if statement, "o" will increase and go down. With a getch, the "o" moves only when there's an input.

 while(1) {
 clear(); // Clear the screen of all
 // previously-printed characters
 mvprintw(y, x, "o"); // Print our "ball" at the current xy position
        int ch = getch();     //THE PROBLEM
        // Move the player's ship left or right based on user input, currently controls the "o" IKNOW. IT's JUST TESTING. THAT"S BESIDES THE PROBLEM
        if (ch == 'a') x--;
        else if(ch == 'd') x  ;
        else if(ch == 'q') break;// quit option  i
 refresh();

 usleep(DELAY);
 // Shorter delay between movements
 y  ; // Advance the ball down
 }

I tried different loops, putting the movement part into a different function, but I still require the movement to be in the while loop. I've been told the while(1) is for every 1 frame, but the output seems to be stuck on requiring an input to progress to the next frame. How could I move "o" independently?

CodePudding user response:

Read the edit

I'm keeping the original answer because it may be useful to someone with a similar problem that doesn't need multithreading.

I don't know exactly what you're trying to achieve, but hopefully this gets you a little closer to your goal. Really, all you need to do is add a timeout of DELAY milliseconds at the top of your main function.

This works, however it is perhaps not ideal -- the ship goes down 1 line every time the a/d keys are pressed. You can reverse this by adding a 'continue' after the if (ch == 'a'/'d') x--/ ; // continue; (by reverse I mean "not move down at all when the a d keys are pressed")

This could be the behaviour you want, however a possibly better? solution could be to use multithreading (pthreads on *nix systems and I guess the Win32 API could do the same on Windows). I tried this, however you might need to do some more research because I couldn't get it working after a few minutes of trying (the ncurses library might not like multithreading or maybe I was doing something wrong [probably the latter]) (again, see edit).

#include <ncurses.h>

const int DELAY = 1000;

int x = 0;
int y = 0;

int main() {
    initscr();
    timeout(DELAY);
    for (;;) {
        clear();
        mvprintw(y, x, "o");

        int ch = getch();
        if (ch == 'a') x--;
        else if (ch == 'd') x  ;
        else if (ch == 'q') return 0;

        y  ;
    }

    return 0;
}

Hope this helps!

EDIT/UPDATE: I got the threaded version working

So, I have no idea why threads didn't work before, but now I have a working program that has none of the problems of the previous versions:

#include <ncurses.h>
#include <pthread.h>
#include <unistd.h>

const int DELAY = 1000000;

int x = 0;
int y = 0;
pthread_mutex_t y_mutex;

void *inc_y(void* ptr) {
    for (;;) {
        pthread_mutex_lock(&y_mutex);
        y  ;
        pthread_mutex_unlock(&y_mutex);
        usleep(DELAY);
    }
}

void *draw(void* ptr) {
    initscr();
    timeout(1);
    for (;;) {
        int ch = getch();
        if (ch == 'a') x--;
        else if (ch == 'd') x  ;
        else if (ch == 'q') return 0;

        clear();
        mvprintw(y, x, "o");
    }
}

int main() {
    pthread_mutex_init(&y_mutex, NULL);

    pthread_t inc_y_thread, draw_thread;
    pthread_create(&inc_y_thread, NULL, inc_y, NULL);
    pthread_create(&draw_thread, NULL, draw, NULL);
    pthread_join(inc_y_thread, NULL);
    pthread_join(draw_thread, NULL);

    pthread_mutex_destroy(&y_mutex);

    return 0;
}

Note that although I am not experienced with C multithreading and the mutex for y may not be strictly necessary, however I think that it would be to make sure that while draw is reading the y variable, it is not also being written to.

CodePudding user response:

delaymove will increment the row on a one second delay.
manualmove will modify row and col based on user input. Arrow keys may be used as well as a, d, w or x.
These functions are called in a loop.
timeout sets the milliseconds that getch will wait for an input before returning ERR.

#include <stdio.h>
#include <ncurses.h>
#include <time.h>

#define DELAY  1.0
#define BILLION  1E9

void delaymove ( int *x, int *y) {
    static struct timespec start;
    static double delay = 0.0;
    struct timespec stop;
    double elapsed;

    if ( delay < .0001) {
        clock_gettime ( CLOCK_REALTIME, &start);
        delay = DELAY;
    }
    clock_gettime ( CLOCK_REALTIME, &stop);
    elapsed = stop.tv_sec - start.tv_sec;
    elapsed  = ( stop.tv_nsec - start.tv_nsec) / BILLION;
    if ( elapsed < delay) {
        return;//if delay is still in effect
    }
    clock_gettime ( CLOCK_REALTIME, &start);
      (*x);
}

int manualmove ( int *x, int *y) {
    int ch = 0;

    ch = getch(); // if no input before timeout, ch will be ERR
    switch ( ch) {
        case ERR:
            break;
        case 1:
            while ( getch() != 1) {} //resume with another ctrl a
            break;
        case 'q':
            return 1;
        case KEY_LEFT:
        case 'a':
            if ( *y > 0) {
                --(*y);
            }
            break;
        case KEY_RIGHT:
        case 'd':
              (*y);
            break;
        case KEY_UP:
        case 'w':
            if ( *x > 0) {
                --(*x);
            }
            break;
        case KEY_DOWN:
        case 'x':
              (*x);
            break;
    }
    return 0;
}

int main ( void) {
    int row = 0;
    int col = 0;
    int rw = 0;
    int cl = 0;
    int r = 0;
    int c = 0;

    initscr ( );
    noecho ( );
    timeout ( 20); // milliseconds getch waits for a character
    keypad ( stdscr, TRUE);
    curs_set ( 0); // hide cursor
    getmaxyx ( stdscr, row, col);
    rw = 0;
    cl = col / 2;
    while(1)
    {
        r = rw;
        c = cl;
        delaymove ( &rw, &cl);
        if ( manualmove ( &rw, &cl)) {
            break;
        }
        if ( rw > row - 4) {
            rw = row - 4;
        }
        if ( cl > col - 2) {
            cl = col - 2;
        }
        move ( r, c);
        printw ( " ");
        move ( rw, cl);
        printw ( "o");
        refresh ( );
    }
    endwin ( );
    return 0;
}
  • Related