0

I am trying to get a basic simultaneous movement of both a player and an enemy working. Is there any way I can accomplish this correctly?

Below is a crude way of accomplishing. I would like to move while the x is also moving but I can only get one of them at a time to work. I tried using while loops but perhaps something else is needed...

Any tips?

#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <windows.h>

WINDOW* createwindow();
char theplayer();
char theenemy();

int main()
{
    initscr();

    WINDOW* border=createwindow();

    while(1)
    {
    theplayer();
    theenemy();
    }
    wgetch(border);
    endwin();
    return 0;
}

WINDOW* createwindow()

{
    WINDOW* temp=newwin(15,40,10,10);
    box(temp,0,0);
    return temp;
}

char theplayer(WINDOW* border)
{
    int playerlocationy=3;
    int playerlocationx=3;
    int input;
    char player='@';
    keypad(border,true);

    mvwprintw(border,3,3,"%c",player);

    while(1)
    {
        input=wgetch(border);

        switch (input)
        {
            case KEY_LEFT:
            mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
            playerlocationx--;
            mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
            break;
            case KEY_RIGHT:
            mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
            playerlocationx++;
            mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
            break;
            case KEY_UP:
            mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
            playerlocationy--;
            mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
            break;
            case KEY_DOWN:
            mvwprintw(border,playerlocationy,playerlocationx,"%c",' ');
            playerlocationy++;
            mvwprintw(border,playerlocationy,playerlocationx,"%c", player);
            break;
            default:
            break;
        }
break;
    }
return player;
}

char theenemy(WINDOW* border)
{
    char enemy='X';
    int enemylocationy=9;
    int enemylocationx=9;

    while(1)
    {
        mvwprintw(border,enemylocationy,enemylocationx,"%c", enemy);

        mvwprintw(border,enemylocationy,enemylocationx,"%c",' ');
        enemylocationx++;
        mvwprintw(border,enemylocationy,enemylocationx,"%c", enemy);
        wrefresh(border);
        Sleep(1000);
    }
return 0;
}
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
  • Not clear which kind of (a)synchronicity you want in between the moves, but your actual code is wrong for sure : `theplayer()` and `theenemy()` contain a never ending loop. Remove these loops and you'll get better results. – Jean-Baptiste Yunès Apr 27 '22 at 14:49

1 Answers1

1

To start, these function declarations

char theplayer();
char theenemy();

are an obsolescent feature of C, that say these functions will take an unspecified but fixed number of arguments. With this, the compiler cannot reason about what an invocation of these functions should look like.

This is hiding the fact that your program has undefined behaviour. Both function definitions require a WINDOW * argument, but you call them with no arguments.

while(1)
{
    theplayer();
    theenemy();
}

To any extent that this program functions as is, is pure chance. Always use proper function prototypes in your declarations

WINDOW *createwindow(void);
char theplayer(WINDOW *);           
char theenemy(WINDOW *);

which will help to find errors.


Using <windows.h> for Sleep reduces the portability of your program. You are already using curses, which provides the very similar napms function.


As for "simultaneous movement", the general idea is to have only one main event loop. Every iteration, this loop

  1. handles input
  2. updates entities
  3. redraws the screen

Nothing else should block execution of the program (i.e., loop forever).

To update your enemy, you will need some way of tracking how much time has passed. This can be as advanced as delta timing or as simple as a frame counter, as shown below.

Here is a cursory example to get you started:

#include <curses.h>

struct entity {
    int y;
    int x;
    unsigned char repr;
};

void update_player(struct entity *, int);
void update_enemy(struct entity *, unsigned);
void draw_entity(struct entity *, WINDOW *);

int main(void)
{
    initscr();
    noecho();
    curs_set(0);

    WINDOW *field = newwin(15, 40, 10, 10);
    keypad(field, TRUE);
    wtimeout(field, 0);

    struct entity player = { 3, 3, '@' };
    struct entity enemy = { 9, 9, 'X' };
    unsigned tick = 1;

    while (1) {
        /* handle input */
        int c = wgetch(field);

        if ((c & A_CHARTEXT) == 'q')
            break;

        /* update stuff */
        update_player(&player, c);
        update_enemy(&enemy, tick);

        /* draw things */
        werase(field);
        box(field, 0, 0);
        draw_entity(&player, field);
        draw_entity(&enemy, field);
        wrefresh(field);

        tick = (tick > 60) ? 0 : tick + 1;
        napms(16);
    }

    delwin(field);
    endwin();
}

void update_player(struct entity *p, int ch)
{
    switch (ch) {
        case KEY_LEFT:
            p->x--;
            break;
        case KEY_RIGHT:
            p->x++;
            break;
        case KEY_UP:
            p->y--;
            break;
        case KEY_DOWN:
            p->y++;
            break;
    }
}

void update_enemy(struct entity *e, unsigned t)
{
    if (t == 60)
        e->x++;
}

void draw_entity(struct entity *et, WINDOW *f)
{
    mvwaddch(f, et->y, et->x, et->repr);
}
Oka
  • 23,367
  • 6
  • 42
  • 53