Accepting events and SINGLE key presses

This forum is full of examples how to accept key presses (or mouse clicks) without counting time between presses. Like this:

from direct.showbase.DirectObject import DirectObject

class Controls(DirectObject):
    def __init__(self):
        self.keyMap = {"run":0}

        self.accept("space", self.setKey, ["run", 1])
        self.accept("space-up", self.setKey, ["run", 0])

    def setKey(self, key, value):
        self.keyMap[key] = value
............................................................
    if controls.keyMap["run"]:
        RunFast() # Check whether running is allowed by other conditions and do run

This code is suitable when you, for example, want the controlled actor to run somewhere.

If you what your actor to jump, this code is not what you need. If you use it, your actor would jump every frame while the jump key is pressed. The following modification calls the function only once:

from direct.showbase.DirectObject import DirectObject

class Controls(DirectObject):
    def __init__(self):
        self.keyTMap = {"jump":0}

        self.accept("time-space", self.setTKey, ["jump"])

    def setTKey(self, key, value):
        # Time between function calls (in seconds):
        time = 0.3
        if value - self.keyTMap[key] > time:
            self.keyTMap[key] = value
            JumpHigh() # # Check whether jumping is allowed by other conditions and do jump

This code does not accept the same key for the next time seconds after the key was pressed.

Pay attention, we accept not “space” but “time-space”. Also, “value” is supplied automatically, it’s the time when the key was pressed (in seconds).

EDIT: To accept another “time-space” event the player will need to release the space button first, and then press it again. Holding the space key does not send another “time-space” event.

EDIT2: Comments in the snippets are slightly edited.

Thanks for sharing this, this could simplify my code a little. :slight_smile:

Several questions I have.

#1 how do you press a “time-space” key on your qwerty keyboard?
#2 what is that third value '[jump] in the accept arguments and where does it come from. I can see that it is the value of a dictionary but will the SetTkey fuction automagicaly extract the value from the dictionary’s ‘key’ position?
#3 The line value-self.SetTKey[key] looks like it would always just equal zero so it would NEVER be greater than 0.3?

  1. “time-” event is automatically generated wen you press the assigned key. So, “time-space” is generated when you press space key, “time-a” - when you press “a” key, and so on.
  2. The “value” in def setTKey(self, key, value): is time elapsed since the application was started. This value is generated automatically by the “time-…” event. The “key” in def setTKey(self, key, value): is the key from the dictionary (in this example - [“jump”]).
    As you remember, the dictionary is the set of key-value pairs, and in this function I assign value (time when key was pressed) to the key (“jump”). I do that only if the “time-” is 0.3 seconds greater then the previous time recorded in the dictionary.
    After recording the time (if it satisfies the given conditions) I call the function I want (JumpHigh()).
  3. You can set any value for the time variable you want.

why not?

class Controls(object): 
    def __init__(self): 
        self.run = False
        base.accept("space", self.__setattr__, ["run", True]) 
        base.accept("space-up", self.__setattr__, ["run", False]) 
............................................................ 
    if self.run: 
        camera.setPos(camera,Vec(0,.1,0)) # Do your action

basically use the controls class (because that is the only thing it will have as a key holder hash map you had before). You have to derive from object to get a new style class (in python 3000 all classes will be new style). Its better to use true or false instead of 0 and 1.

I remember there was a talk about inheriting from DirectObject in another thread :wink:
I used it because most of other examples on this forum and in Panda’s Samples directory show how to inherit from DirectObject, and this is the way I learned it.
But anyway my goal when I wrote it was to show how to accept “time-…” events since I found them very handy. Forums don’t have much information about it.

birukoff, you should be able to jump only if you are on the ground. See discourse.panda3d.org/viewtopic.php?t=4068

In fact, I used “jump” only for this example. In my code I use “time-” events to toggle between several sets of controls and camera modes. It makes code shorter. As it is said the subject of the thread: “Accepting SINGLE key presses”.
If you think that word “jump” used in this snippet is misleading, I will re-write it using other words :slight_smile:

I have just discovered that I was totally wrong the above posted example… :frowning:

First of all, usual “key” and “key-up” events are not sent every frame, they are only sent exactly when the key is pressed or released. So, my examples “showing” the difference between usual events and “time-” events are just useless complication. They sill work, but the way they work is absolutely weird. The only good thing about them is that they still show how to use “time-” since there no other such examples on the forum.

Next, if someone still wants to know how to accept single key presses, much more correct way to do that is by using tasks:

from direct.showbase.DirectObject import DirectObject

class Controls(DirectObject):
    def __init__(self):
        self.keyMap = {"jump":0}

        self.accept("space", self.setKey, ["jump", 1])
        # If you want to do something when the key
        # is released, do this:
        #self.accept("space-up", self.setKey, ["jump", -1])

    def setKey(self, key, value):
        self.keyMap[key] = value
        frame = globalClock.getFrameCount()
        taskMgr.add(self.resetKeys, "resetKeys",
                    extraArgs = [key, frame],
                    appendTask = True)

    def resetKeys(self, key, frame, task):
        if globalClock.getFrameCount() > frame:
            self.keyMap[key] = 0
            return Task.done
        else:
            return Task.cont
............................................................
    if controls.keyMap["jump"] == 1:
        JumpHigh() # Check whether jumping is allowed by other conditions and do jump

I hope I didn’t mislead anyone, sorry.