Author Topic: Refreshing only on Keypress  (Read 13158 times)

hilbert90

  • Newcomer
  • Posts: 10
  • Karma: +0/-0
    • View Profile
    • Email
Refreshing only on Keypress
« on: July 28, 2014, 01:50:05 AM »
Hey,

I've been starting on writing a roguelike for the first time using pygame and pygcurse. These are basically designed to continuously refresh the screen at some fps so that you can easily animate things. I asked about getting a refresh only upon keypress here: http://stackoverflow.com/questions/24771462/pygame-loop-only-upon-keypress I've successfully implemented a template and can't really see how this will be a problem for what I want.

It seems it is somewhat frowned upon to do things this way, even for turn-based things. What are people's thoughts? Have people done the refresh upon keypress method of displaying things and wished they hadn't later for some reason? I intend my game to be completely in the classical ASCII style with no tiles or anything.

Thanks.

Kevin Granade

  • Rogueliker
  • ***
  • Posts: 83
  • Karma: +0/-0
    • View Profile
Re: Refreshing only on Keypress
« Reply #1 on: July 28, 2014, 02:00:04 AM »
It's tautological that if you don't want redraws between your keypresses it won't cause problems, but if you ever change your mind, it will likely be a difficult assumption to clear out of your code.  It's less the interface to pygame or whatever other library you're using that's a problem, and more all the code that will likely internalize this assumption that refreshes and keypresses are linked.

In CDDA, we end up doing somewhat silly things like proceeding through frames of animation in a secondary or even tertiary game loop because the main loop trigers overhead and side-effects, like per-turn updates that we don't want to occur during animation sequences.  It hasn't become painful enough to do an overhaul, but as we continue to refactor I wouldn't be surprised if we grew a proper centralized render loop instead of the combined game and draw loop monstrosity we have now.

From a high-level programming cleanliness issue, your game loop, input loop, and render loop shouldn't have anything to do with each other.  One of them or a main loop should explicitly handle sequencing between these separate tasks.  whether you *need* such strict separation of concerns is a matter of personal taste, personally I'd have it all be separate and explicit if I were starting from scratch.

mounta1nman

  • Newcomer
  • Posts: 14
  • Karma: +0/-0
  • [._.]
    • View Profile
Re: Refreshing only on Keypress
« Reply #2 on: July 28, 2014, 04:14:35 AM »
Its fine when you are just starting out.

You'll notice when it becomes a problem and you want to do something else.
« Last Edit: July 28, 2014, 11:14:19 AM by mounta1nman »

Paul Jeffries

  • 7DRL Reviewer
  • Rogueliker
  • *
  • Posts: 257
  • Karma: +1/-0
    • View Profile
    • Vitruality.com
Re: Refreshing only on Keypress
« Reply #3 on: August 06, 2014, 11:42:24 PM »
Do you have any particular reason for wanting to only redraw manually?

I've done it both ways before.  Manually redrawing was simpler to set up initially and didn't give me any serious problems, however it was a little limiting.  I found that I needed to add in a little pause between the player turn and the AI turn just so that it didn't all blend into one confusing mess.  Since the rendering code and the game code were interwoven this meant that I ended up having to pause everything for a fraction of a second when I might more profitably have just delayed changing the display while doing some AI processing or something in the background.  So my advice would be; if the library you're using is already set up to run a separate drawing loop, roll with it - it might seem wasteful to keep redrawing constantly but on modern hardware it barely costs you anything and it will make it much easier to add in simple timing and animation effects that are easy to overlook but can make a big difference.

Nowadays, my preferred way of doing things is to follow a MVVM-like pattern, where I keep my drawing and game logic systems almost entirely independent and link between them using event-driven data binding.  It takes a fair bit of up-front work to set something like that up, however, so I don't really recommend it as something that everyone should do.

Kyzrati

  • 7DRL Reviewer
  • Rogueliker
  • *
  • Posts: 508
  • Karma: +0/-0
    • View Profile
    • Grid Sage Games
    • Email
Re: Refreshing only on Keypress
« Reply #4 on: August 07, 2014, 01:07:24 AM »
It seems it is somewhat frowned upon to do things this way, even for turn-based things. What are people's thoughts? Have people done the refresh upon keypress method of displaying things and wished they hadn't later for some reason?
It can make most kinds of animation more difficult to handle (yes, even ASCII games can use animation effectively!). Interestingly (and perhaps very telling), the opposite of your question is a *lot* more common--devs wanting to constantly update their game loop, or attempting to do something that requires such a setup.

Going out of your way to unnecessarily avoid a potentially useful feature that the engine already provides is probably not a good idea.

All that said, since you're just starting out, really just do whatever you think is easiest ;)

hilbert90

  • Newcomer
  • Posts: 10
  • Karma: +0/-0
    • View Profile
    • Email
Re: Refreshing only on Keypress
« Reply #5 on: August 10, 2014, 02:43:01 PM »
Thanks for the replies. I've been using python for years for all sorts of things, but I've never tackled anything as large scale as a small game. I'm pretty sure I have the programming skills to pull one off. I guess there are a few reasons I was trying to do it this "easier" way. The main one is to keep everything as simple as possible to maximize my chances of succeeding.

1. I was having massive difficulty with the constant looping, despite it being setup to work that way naturally. For example, if I press "right" then my @ would sometimes move 2 or even 3 squares over. This was due to the constant looping. If you didn't release the key before the next loop it would count it as 2 (or 3) keypresses.

Alright. That is easy enough to fix with time delays and things, but I started getting nervous that everything I tried to implement would become a lot more complicated than it needed to be by unnecessarily looping.

2. The whole logic of keeping things separate is confusing me. It will still be quite a project to implement with updating only on keypress, and that logic is something I fully understand. Trying to add in stuff I didn't understand felt like a procrastination issue. I knew I'd have to take time to learn a bunch of stuff just to get the logical flow right, and that was time I wouldn't be working on the game.

Anyway, excuses, I know. I'm mostly replying, because I'm curious if there are any toy examples I could look at to see how the "pro" way of doing it works. Rather than reinventing the wheel, I might feel differently if I could just look at the logic in code already written (I'm comfortable enough with C, C++, python, ruby, java, and similar that I should at least be able to follow the logic).

I know there are lots of finished games I could look at, but that would require sorting through tons of irrelevant details. Or are there any good game programming books or online resources that describe the general flow? Sorry. I probably should have researched this better before beginning.

mushroom patch

  • Rogueliker
  • ***
  • Posts: 554
  • Karma: +0/-0
    • View Profile
Re: Refreshing only on Keypress
« Reply #6 on: August 10, 2014, 03:16:19 PM »

I've been starting on writing a roguelike for the first time using pygame and pygcurse. [...]

I intend my game to be completely in the classical ASCII style with no tiles or anything.


If this is true, you should just use curses. Curses is better than graphical fakery because it's based on data streams that can be used in various ways, e.g. transmitted over a network via standard tools available on any modern computer, multiplexed by standard utilities, etc., that the graphics produced by SDL (or pygame) can't.

Bear

  • Rogueliker
  • ***
  • Posts: 308
  • Karma: +0/-0
    • View Profile
Re: Refreshing only on Keypress
« Reply #7 on: August 10, 2014, 04:42:28 PM »

1. I was having massive difficulty with the constant looping, despite it being setup to work that way naturally. For example, if I press "right" then my @ would sometimes move 2 or even 3 squares over. This was due to the constant looping. If you didn't release the key before the next loop it would count it as 2 (or 3) keypresses.

Alright. That is easy enough to fix with time delays and things, but I started getting nervous that everything I tried to implement would become a lot more complicated than it needed to be by unnecessarily looping.


A better answer is don't count another "right" until you've seen a loop that DIDN'T move right.  Don't count it as a keypress if the key has never been released. 

But seriously, your environment ought to have the RIGHT answer, which is to only report a keypress when a key has actively been pressed.  That's what getc does in C, getch in ncurses, get_wch in ncursesw, etc.  That's a real fundamental of game input. I don't know how your environment does it, but it's almost impossible that it doesn't do it.  In fact, there are Python bindings for C functions, so you could be using any of the above if you have the right bindings imported. 

The way these functions are implemented is to use the BIOS directly.  There is a hardware interrupt on a keypress that happens exactly once when the key is pressed.   The BIOS handles that interrupt by adding data to a buffer.  The functions that use it read data from that buffer, then remember where in the buffer they are so they don't read the same data again.   

Bear

Bear

  • Rogueliker
  • ***
  • Posts: 308
  • Karma: +0/-0
    • View Profile
Re: Refreshing only on Keypress
« Reply #8 on: August 10, 2014, 04:51:38 PM »
I intend my game to be completely in the classical ASCII style with no tiles or anything.

If this is true, you should just use curses. Curses is ... based on data streams that can be used in various ways, e.g. transmitted over a network via standard tools available on any modern computer, multiplexed by standard utilities, etc.

True that.  And if you're using ncursesw, you can do it with the full Unicode repertoire of characters. This almost seems like cheating because it includes things like emoji, symbols, daggers, geometric shapes, and box drawing characters.  Not to mention a half-dozen font variants of a couple different western alphabets in the "math" section.  You can use the 'degree' sign, which actually looks like a ring, to represent rings.  Use the 'dagger' punctuation to represent a dagger.  Use braille dot patterns to represent swarming bugs.  And so on. 

hilbert90

  • Newcomer
  • Posts: 10
  • Karma: +0/-0
    • View Profile
    • Email
Re: Refreshing only on Keypress
« Reply #9 on: August 10, 2014, 07:55:41 PM »
I don't know how the behind the scenes of pygcurse works, but I imagine it is just a port of ncurses to work with pygame. So in effect, I think I am using a form of curses.

mushroom patch

  • Rogueliker
  • ***
  • Posts: 554
  • Karma: +0/-0
    • View Profile
Re: Refreshing only on Keypress
« Reply #10 on: August 11, 2014, 12:45:59 AM »
pygcurse most likely provides the appearance of a terminal using SDL graphics via pygame and a curses-like API. It might be easy to port to actual curses, but like I said, it's still probably better to use actual curses in the first place (or if you feel ambitious, write a wrapper library that encapsulates various possible "graphics" implementations for your games).
« Last Edit: August 11, 2014, 03:48:35 PM by mushroom patch »

Kevin Granade

  • Rogueliker
  • ***
  • Posts: 83
  • Karma: +0/-0
    • View Profile
Re: Refreshing only on Keypress
« Reply #11 on: August 12, 2014, 04:36:23 AM »
1. I was having massive difficulty with the constant looping, despite it being setup to work that way naturally. For example, if I press "right" then my @ would sometimes move 2 or even 3 squares over. This was due to the constant looping. If you didn't release the key before the next loop it would count it as 2 (or 3) keypresses.

Alright. That is easy enough to fix with time delays and things, but I started getting nervous that everything I tried to implement would become a lot more complicated than it needed to be by unnecessarily looping.
Separating concerns simplifies things, it's having your code trying to do everything at once that's complicated.
If you have an input loop that polls for keypresses, you can handle delay, repeat, keybinding, etc all in one small chunk of code, and the rest of your code just operates on unambiguous commands returned by your input handler.
DDA inherited this same problem from the original Cataclysm, I added a wrapper that consumes all repeated keypresses and it cleared right up.
https://github.com/CleverRaven/Cataclysm-DDA/blob/master/src/input.cpp#L1074

graspee

  • Guest
.
« Reply #12 on: September 07, 2014, 10:02:48 PM »
.
« Last Edit: April 30, 2015, 04:00:56 PM by graspee »