1

I am having an issue getting continuous movement whilst using Metulburr's state machine code.

https://python-forum.io/Thread-PyGame-Creating-a-state-machine

As of now, holding down a key will not cause continuous and fluid movement. Only each key press is registered.

Here is his code, with a sprite added.

import pygame as pg
import sys
from spritez import MainChar

GREEN = (000,000,000)

pg.init()

main_char = MainChar(GREEN, 100, 100)

sprite_list = pg.sprite.Group()

sprite_list.add(main_char)
sprite_list.update()



class States(object):
    def __init__(self):
        self.done = False
        self.next = None
        self.quit = False
        self.previous = None

class Menu(States):
    def __init__(self):
        States.__init__(self)
        self.next = 'game'
    def cleanup(self):
        print('cleaning up Menu state stuff')
    def startup(self):
        print('starting Menu state stuff')
    def get_event(self, event):
        if event.type == pg.KEYDOWN:
            print('Menu State keydown')
        elif event.type == pg.MOUSEBUTTONDOWN:
            self.done = True
    def update(self, screen, dt):
        self.draw(screen)
    def draw(self, screen):
        screen.fill((255,0,0))

class Game(States):
    def __init__(self):
        States.__init__(self)
        self.next = 'menu'
    def cleanup(self):
        print('cleaning up Game state stuff')
    def startup(self):
        print('starting Game state stuff')
    def get_event(self, event):
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_LEFT:
                main_char.moveLeft(5)
            elif event.key == pg.K_RIGHT:
                main_char.moveRight(5)
        elif event.type == pg.MOUSEBUTTONDOWN:
            self.done = True


    def update(self, screen, dt):
        self.draw(screen)
    def draw(self, screen):
        screen.fill((0,0,255))
        sprite_list.draw(screen)
        pg.key.set_repeat(10)

class Control:
    def __init__(self, **settings):
        self.__dict__.update(settings)
        self.done = False
        self.screen = pg.display.set_mode(self.size)
        self.clock = pg.time.Clock()
    def setup_states(self, state_dict, start_state):
        self.state_dict = state_dict
        self.state_name = start_state
        self.state = self.state_dict[self.state_name]
    def flip_state(self):
        self.state.done = False
        previous,self.state_name = self.state_name, self.state.next
        self.state.cleanup()
        self.state = self.state_dict[self.state_name]
        self.state.startup()
        self.state.previous = previous
    def update(self, dt):
        if self.state.quit:
            self.done = True
        elif self.state.done:
            self.flip_state()
        self.state.update(self.screen, dt)
    def event_loop(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            self.state.get_event(event)
    def main_game_loop(self):
        while not self.done:
            delta_time = self.clock.tick(self.fps)/1000.0
            self.event_loop()
            self.update(delta_time)
            pg.display.update()




settings = {
    'size':(600,400),
    'fps' :60
}

app = Control(**settings)
state_dict = {
    'menu': Menu(),
    'game': Game()
}

app.setup_states(state_dict, 'menu')
app.main_game_loop()
pg.quit()
sys.exit()

The sprites method for movement:

def moveLeft(self, px):
    self.rect.x -= px

def moveRight(self, px):
    self.rect.x += px

I understand that typically one would use a boolean, such as: left = False ... ... if left: main_char.moveLeft(5)

Then, the event handler would be:

if event.key == pg.K_LEFT:
    left = True

Despite trying this many times and reformatting many times, I cannot implement it properly. Any ideas on how to fix this and get it working?

  • if you have problem with `pygame.event` then maybe you should check `pygame.key.get_pressed()[pg.K_LEFT]` in every loop ? It will gives `True` in every loop so it will move object in every loop. `event` can be usefull in different moments - ie. when you want to catch single mouse click on button or other element on screen. And then continuous clicking (when you keep pressed mouse) is useless. – furas Dec 10 '20 at 00:12
  • Where would I place that in my code? –  Dec 10 '20 at 00:50

1 Answers1

2

Use pygame.key.get_pressed() rather than the keyboard events.

The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or a step-by-step movement.

pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.

class States(object):
    # [...]

    def get_event(self, event):
        if event.type == pg.MOUSEBUTTONDOWN:
            self.done = True

    def get_states(self):

        keys = pygame.key.get_pressed()
        if keys[pg.K_LEFT]:
            main_char.moveLeft(5)
        if keys[pg.K_RIGHT]:
            main_char.moveRight(5)
class Control:
    # [...]
     def event_loop(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            self.state.get_event(event)

        self.state.get_states()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    Thanks a lot. I was misunderstanding fundamentally how events worked. –  Dec 10 '20 at 06:37