4

So I'm making a clone of Super Mario Bros in pygame. As of now I am in the middle of making my keypoll function, to check all the keys that are currently being pressed. I am wondering which would be better:

  1. Have the keypoll function modify marios attributes (ex. horizontal direction), then based on those, have mario decide which action he should be doing (ie. walk, run, jump, etc.) and call the function cooresponding to that action (ex. mario.moveHorz())

    OR

  2. Have the keypoll function call the function of the action he should be doing, and then the mario instance would decide what state he is currently in.

Here is some semi-pseudo code for the first one:

def doAction():
    if mario.horzDir == 'r' or 'l':
        mario.moveHorz()
    elif mario.action == 'jump':
        mario.jump()

 def keyPoll(keys):
    if keys[K_RIGHT]:
        mario.horzDir = 'r'
    elif keys[K_LEFT]:
        mario.horzDir = 'l'
    if keys[K_z]:
        mario.action = 'jump'

Here is some more semi-pseudo code for the second one:

def keyPoll(keys):
    if keys[K_RIGHT]:
        mario.moveHorz('r')
    elif keys[K_LEFT]:
        mario.moveHorz('l')
    if keys[K_z]:
        mario.jump()

def setProperties():
    if mario.x_vel > 0:
        mario.horzDir = 'r'
    elif mario.x_vel < 0:
        mario.horzDir = 'l'
    elif mario.y > ground:
        mario.action  = 'jump'

Both essentially accomplish the same thing, I just don't know which one is more correct.

Thanks!

Robbie
  • 222
  • 2
    The key polling should only set variables in your game model. Your animation loop should act on (get) the variables in your game model. This ensures that the key polling happens quickly (within one game cycle). – Gilbert Le Blanc Jul 23 '15 at 20:14
  • What do you mean? Could you put it in terms of my mario example? – Robbie Jul 23 '15 at 20:49

1 Answers1

3

Because key polling usually happens at the very top of the game loop, the specific question you're asking is hard to separate completely from all the other extremely important issues one needs to be aware of when designing a game loop (Gilbert's comment appears to be referring to some of them). To get those out of the way, I'll simply suggest you read this: http://gameprogrammingpatterns.com/game-loop.html Now to answer your specific question.

It is better for your keypoll function to call mario methods than to set mario properties, mainly because all of these commands are going to have at least one or two extra steps beyond setting a field on the object. mario may have an internal state machine that keeps track of whether jumping or changing direction is even allowed in its current state. It may be keeping track of how many times it's been told to move right or left in the past several frames so it can speed up or slow down gradually. It may have yet more state to help it work out when you've done the inputs for various special combo moves. keyPoll() shouldn't have to know about any of that.

By setting a field and then calling a method to do all of this work, you're essentially doing in two steps what you could be doing with one step. That's usually not a good thing. Yes, it's likely that mario will have an update() or render() method to get called later on, but those should be completely separate from the methods for handling input.

Now, whether this is the best option depends on how complex your game is, but for most games, it's better to go even farther, and not have your keyPoll() function directly refer to mario at all. In principle, the only thing keyPoll() needs to do is check the state of the keyboard, and output zero or more objects that represent commands. If you ever want to support key rebinding or multiple control schemes, that logic would go in keyPoll(). Then the main loop gives these commands to whatever entity the player is currently controlling, presumably mario. By decoupling the input handling from the specific game entity, you make it easy to add new abilities to mario or support key rebinding or allow switching between multiple characters or add AI/demo characters and other cool stuff without having to worry about both the internals of keyPoll() and the internals of mario at the same time.

Ixrec
  • 27,771
  • This is a great answer! I agree with what you say about decoupling mario and the state machine. If I were to do this, what would I do exactly? Would the main loop contain some variables about which keys are being pressed and then mario gets these variables and decides what to do? Also, I knew about the stuff posted in the first link, I just didn't think it was necessary. @lxrec – Robbie Jul 24 '15 at 18:08
  • 1
    @Robbie Yes, the main loop would have to store something returned by keyPoll() and then pass that into some method on mario. That's why I snuck in a link about the Command Pattern in my last paragraph; command objects are exactly the sort of thing keyPoll() ought to be returning. – Ixrec Jul 24 '15 at 18:15
  • Ok. So, pygame (the python extension i am using) has a built in function called key.get_pressed() that creates a list representing all the keys that are currently being pressed. Using this, would I even need to create a keypoll function in the main loop since I could just store the key.get_pressed() function as variable? @lxrec – Robbie Jul 24 '15 at 18:43
  • 1
    @Robbie If that really is all the input handling logic you want, then yes you wouldn't need your own function. But what about mouse/controller/joystick input? Holding down and releasing keys? Multiple or custom keybindings? And how does it handle international keyboards? I'd be willing to bet there's still some extra piece of logic you want that justifies giving it at least its own function. – Ixrec Jul 24 '15 at 18:48
  • @lxrec I see what you mean. I do want to eventually add other stuff, so for now I will just give it it's own function. Thanks!! This helped a lot – Robbie Jul 24 '15 at 18:51
  • Okay, so I have on more question. In Mario's function that tells him what to do based on the commands object in the main loop (lets call it getActions()), should I only call methods that involve moving mario, or would defining some of Mario's attributes within the function be okay as well? For example, I want to define Mario's max speed based on whether the shift button is being held and I don't know if it's a good idea to define his speed in getActions() or not. @lxrec – Robbie Jul 24 '15 at 19:28
  • Mario's methods should certainly have access to Mario's attributes. If Mario himself can't change any of his own properties, who can? But for that specific example: Wouldn't the max speed be a constant defined at compile? (also, you may wish to join us in chat rather than ask additional questions in comments). – Ixrec Jul 24 '15 at 19:31