4

I'm trying to make a text animation for an application made in ncurses.

User presses a key, selects a direction and an object in a text grid should move from one cell of the grid to the next in the given direction, waiting 500ms before it moves. The code I used is

while (!checkcollisions(pos_f, input)) { // Checks if it can move to next grid
    pos_f = moveobject(pos_f, input, ".."); // Moves object to next cell
    usleep(50000);
}

But when I execute it, instead of moving, waiting and moving again, it waits a long time, and the object suddenly appears at the final cell of the grid, without showing the animation.

Is this because of how ncurses work? I already tried using other solutions like the select() stalling function.

user1002327
  • 533
  • 2
  • 8
  • 23
  • Do any answers on this question help at all? (http://stackoverflow.com/questions/2076380/linux-terminal-animation-best-way-to-delay-printing-of-frame-in-c) – summea Mar 18 '12 at 03:41
  • Yes, already tried with `wrefresh(win.window);` but it won't do anything. – user1002327 Mar 18 '12 at 03:45
  • Are you blocking on keyboard or mouse reads? – JimR Mar 18 '12 at 04:01
  • Uh, yes, it's always waiting for user input so it can perform an action. It's a while loop with a getch() inside. http://pastebin.com/p0wVu91f – user1002327 Mar 18 '12 at 04:05
  • Note: I looked at pastebin, you're using getch, which blocks. Use non-blocking kbd/mouse reads. – JimR Mar 18 '12 at 04:07
  • This is one of my first experiences with interactive text games, so I don't really know what to use. Is there anything you recommend? – user1002327 Mar 18 '12 at 04:09
  • `ncurses` has a nonblocking input mode (in which `getch` or `wgetch` will return a "no input available" non-`char` value, so don't store the return value in a `char`). This won't help you with a mouse but will let your game run with a timing loop. But that's something to do later, once you get the logic and logistics worked out here first. – torek Mar 18 '12 at 04:54
  • I tried using nodelay() (i think that's the mode you're talking about) but it would eat one of my CPU cores and it wouldn't work. – user1002327 Mar 18 '12 at 05:00

3 Answers3

7

You need to call refresh() (before the usleep).


Update: the new pastebin-ed code segments (in several comments) point to the real problem, which is the same one in ncurses-refresh: mixing stdscr (implied by the next two calls) and getch and refresh with newwin and wrefresh.
Update 2: using the full code, plus some hacking, I got it to work (for some value of "work", I'm clearly not calling printmap() correctly, and I made up a bogus "map" file).

Without looking closely, I just changed all occurrences of getch() to wgetch(win.window), all mvprintw calls to mvwprintw (to use that same window), and removed at least one unneeded getch/wgetch. Then the heart of the problem:

                while (!checkcollisions(pos_f, input)) {
-                       pos_f = moveobject(pos_f, input, "..");
-                       // sleep + wrefresh(win.window) doesn't work, neither does refresh()
+                       struct position new_pos = moveobject(pos_f, input, "..");
+                       printmap(pos_f, new_pos);
+                       pos_f = new_pos;
+                       wrefresh(win.window);
+                       fflush(stdout);
+                       usleep(50000);
                }

The above call to printmap is definitely wrong, but still you definitely need to do something in the loop to change what's in win.window (or stdscr or some other window you put up or whatever); and then you need to force it to refresh, and force the output to stdout with fflush(stdout), before sleeping.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Tried with wrefresh() and refresh(), both before and after the usleep, didn't work. – user1002327 Mar 18 '12 at 03:48
  • I don't see anything relevant wrong there. Ah, but wait, there's more in one of the other comments above, and a different pastebin... let me edit the answer. – torek Mar 18 '12 at 04:16
  • Uh, I already tried that and it didn't work. Replacing getch() with wgetch(win.window) made the shoot() function behave weirdly. It doesn't read the input when it should (it's delayed like 1 or 2 cycles, it's hard to see) – user1002327 Mar 18 '12 at 04:28
  • That's... pretty cool. Yes, the printmap() call is wrong but I can fix it. Now I can see a slowly moving map, which is not what I want, but it's responding to the sleep now. Thanks a lot. – user1002327 Mar 18 '12 at 04:57
1

Try something like

while (!checkcollisions(pos_f, input)) { // Checks if it can move to next grid
    pos_f = moveobject(pos_f, input, ".."); // Moves object to next cell
    refresh();
    napms(200);
}
Duck
  • 26,924
  • 5
  • 64
  • 92
  • Already tried it, makes the wait a bit longer and still doesn't animate it. – user1002327 Mar 18 '12 at 04:06
  • Huh. Well there is always the question if you are certain your two functions are actually doing what you think. You ran them through a debugger? – Duck Mar 18 '12 at 04:19
  • Yes, they're doing what I want. The player moves freely around the map, the collision checker works fine and the only issue here is the animation. Looks like posting small snippets of code isn't enough, so here's the complete code. It's rather short. http://pastebin.com/mL2FkDTg – user1002327 Mar 18 '12 at 04:22
1

See the accepted answer here. You're causing everything to block by using getch, then once getch is unblocked by a key being available to read everything moves as you expect.

Your loop should look sort of like this, using the code from the link...

while( !kbhit() )
{
     sleep( 500 ); // You get to determine how long to sleep here...
}

input = getch();

// Your old logic, roughly, goes here.
Community
  • 1
  • 1
JimR
  • 15,513
  • 2
  • 20
  • 26