Search code examples
pythonpython-3.xcurses

One Character "lag" in Python 3 Curses Program


I'm attempting to create a roguelike using Python3 and curses. I've got everything displaying the way I want it to, but I've come across a strange bug in the code. There is a 1 key stroke delay in processing the commands. So, assuming traditional roguelike commands, pressing "k" should move you 1 square to the right. The first time you press it, it does nothing. The second time, it will move. If you then press "g", you don't move back to the left, instead the 2nd "k" gets processed and the "g" ends up "on deck". Here's the loop that's supposed to be processing the moves.

  def main_loop(self):
#This will catch and handle all keystrokes. Not too happy with if,elif,elif or case. Use a dict lookup eventually
    while 1:
      self.render_all()

      c = self.main.getch()
      try:
        self.keybindings[c]["function"](**self.keybindings[c]["args"])
      except KeyError:
        continue

And here is the dictionary lookup I promised myself I'd use in that comment

    self.keybindings = {ord("h"): {"function":self.move_object, 
                               "args":{"thing":self.things[0], "direction":"North"}},
                        ord('j'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"South"}},
                        ord('g'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"West"}},
                        ord('k'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"East"}},
                        ord('y'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"NorthWest"}},
                        ord('u'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"NorthEast"}},
                        ord('b'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"SouthWest"}},
                        ord('n'): {"function":self.move_object,
                               "args":{"thing":self.things[0], "direction":"SouthEast"}},
                        ord('l'): {"function":self.look, "args":{"origin_thing":self.things[0],}},
                        ord('q'): {"function":self.save_game,
                               "args":{"placeholder":0}}}

Finally, here is the move_object function that's supposed to be called:

  def move_object(self, thing, direction): 
"""I chose to let the Game class handle redraws instead of objects.
I did this because it will make it easier should I ever attempt to rewrite
this with libtcod, pygcurses, or even some sort of browser-based thing.
Display is cleanly separated from obects and map data.
Objects use the variable name "thing" to avoid namespace collision."""
    curx = thing.x
    cury = thing.y
    newy = thing.y + directions[direction][0]
    newx = thing.x + directions[direction][1]
    if not self.is_blocked(newx, newy):
      logging.info("Not blocked")
      thing.x = newx
      thing.y = newy

Edited to cleanup code formatting.


Solution

  • I found the problem and it wasn't in the code I posted. It was inside my render_all() function. I needed to add call to the window's refresh() function after making the changes I was making. I must say, I really don't like curses!