5

Python で pygame を使用して、アプリケーションの簡単なコントロールを作成しようとしています。基本的な作業は完了しましたが、奇妙な壁にぶつかっています。矢印キーを使用してキャラクターを制御しています。1 つの矢印キーを押したまま別の矢印キーを押したままにすると (斜めに移動するため)、キャラクターは期待どおりに動きます。ただし、押した2 番目のキーを (最初のキーを押したまま) 離すと、最初のキーを押したままでも、キャラクターの動きが止まります。これが私の簡単な動きのコードです:

for event in pygame.event.get():
    if event.type == QUIT:
        pygame.quit()
        sys.exit()
    elif event.type == KEYDOWN:
        if pygame.key.get_pressed()[K_LEFT]:
            player.pos = (player.pos[0] - 2, player.pos[1])
        if pygame.key.get_pressed()[K_RIGHT]:
            player.pos = (player.pos[0] + 2, player.pos[1])
        if pygame.key.get_pressed()[K_UP]:
            player.pos = (player.pos[0], player.pos[1] - 2)
        if pygame.key.get_pressed()[K_DOWN]:
            player.pos = (player.pos[0], player.pos[1] + 2)

さて、私は当然これに非常に混乱しました。そこで、デバッグするためにいくつかの行を印刷しようとしました。メイン制御ループの先頭に、次のように書きました。

print (pygame.key.get_pressed()[K_DOWN], pygame.key.get_pressed()[K_RIGHT])
print pygame.event.get()

...下矢印キーと右矢印キーの状態を表示するタプルを出力し、pygame イベント キューを表示します。私の結果は私をさらに困惑させました。キャラクターを斜め下と右に移動し、最初に下キーを押してから右キーを押し、次に右キーを放して単純に下に移動すると、キャラクターは以前のように動きを停止します...しかし、これはシェルに出力されます:

(1, 0)
[]

つまり、右矢印キーを離して下矢印キーを押したままにすると、pygame.key.get_pressed() は下矢印キーがまだ押されていることを理解しますが、イベント キューには何もありません。

また、コードの早い段階 (制御ループの前) で呼び出しています

pygame.key.set_repeat(1, 2)

キーを押している間、キャラクターが動き続けるようにします。

どんな助けでも大歓迎です!ありがとう :)

4

4 に答える 4

8

移動などについては、イベント (KEYDOWNや などKEYUP) をチェックするのではなく、移動キーが押された場合 ( を使用get_pressed) にメインループのすべての反復をチェックする必要があります。

コードでは、イベントもある場合にのみ、押されたキーをチェックしますKEYDOWN


他にも考慮すべき点がいくつかあります。

  • キーマッピングとプレーヤーの速度を分離する必要があるため、後でどちらかを変更するのが簡単になります。

  • 移動ベクトルを決定し、最初にそれを正規化する必要があります。そうしないと、垂直方向と水平方向の移動速度が10の場合、対角方向の移動速度は ~ になり14ます。

作業例:

import pygame

pygame.init()

screen = pygame.display.set_mode((200, 200))
run = True
pos = pygame.Vector2(100, 100)
clock = pygame.time.Clock()

# speed of your player
speed = 2

# key bindings
move_map = {pygame.K_LEFT: pygame.Vector2(-1, 0),
            pygame.K_RIGHT: pygame.Vector2(1, 0),
            pygame.K_UP: pygame.Vector2(0, -1),
            pygame.K_DOWN: pygame.Vector2(0, 1)}

while run:
  for e in pygame.event.get():
    if e.type == pygame.QUIT: run = False

  screen.fill((30, 30, 30))
  # draw player, but convert position to integers first
  pygame.draw.circle(screen, pygame.Color('dodgerblue'), [int(x) for x in pos], 10)
  pygame.display.flip()

  # determine movement vector
  pressed = pygame.key.get_pressed()
  move_vector = pygame.Vector2(0, 0)
  for m in (move_map[key] for key in move_map if pressed[key]):
    move_vector += m

  # normalize movement vector if necessary
  if move_vector.length() > 0:
    move_vector.normalize_ip()

  # apply speed to movement vector
  move_vector *= speed

  # update position of player
  pos += move_vector

  clock.tick(60)
于 2012-08-13T07:42:38.427 に答える
3

ポーリングを試みる代わりに、イベントがデータを返すだけを使用します。キーダウンイベントのタイプであるかどうかをすでに確認しているので、次のようにKEYインデックスに問い合わせるだけです。

for event in pygame.event.get():
    if event.type == QUIT:
        pygame.quit()
        sys.exit()
    elif event.type == KEYDOWN:
        if event.key == K_LEFT:
            player.pos = (player.pos[0] - 2, player.pos[1])

残りのコード.....

また、別のデータ構造を使用してコントロールの状態を保存し、イベントを使用してそのデータ構造を更新することも検討してください。これにより、キャラクターを動かすためにイベントキューに依存しないため、コントロールが少し柔軟になります。これは、私の経験では、一度に2つ以上のボタンを押すことができない、奇妙な遅延などの問題を引き起こします。またはキャラクターの動きに関するタイミングの問題。だから次のようなもの:

keystates={'up':False, 'down':False, 'left':False, 'right':False}
running=True

#start main pygame event processing loop here
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running=False

        #check for key down events
        if event.type == KEYDOWN:
            if event.key == K_UP:
                keystates['up']=True
            if event.key == K_DOWN:
                keystates['down']=True
            if event.key == K_LEFT:
                keystates['left']=True
            if event.key == K_RIGHT:
                keystates['right']=True

        #check for key up events
        if event.type == KEYUP:
            if event.key == K_UP:
                keystates['up']=False
            if event.key == K_DOWN:
                keystates['down']=False
            if event.key == K_LEFT:
                keystates['left']=False
            if event.key == K_RIGHT:
                keystates['right']=False

    #do something about the key states here, now that the event queue has been processed
    if keystates['up']:
        character.moveUp()  #or whatever your call for these are...
    if keystates['down']:
        character.moveDown()
    if keystates['left']:
        character.moveLeft()
    if keystates['right']:
        character.moveRight()

#gracefully exit pygame here
pygame.quit()
于 2012-08-13T09:27:44.840 に答える
2

イベントベースの入力を使用していますが、この場合はポーリングベースの入力が必要です。そうすれば、キーリピートをいじることはありません。

import pygame
from pygame.locals import *

done = False    
player.pos = Rect(0,0,10,10)

while not done:
    for event in pygame.event.get():
        # any other key event input
        if event.type == QUIT:
            done = True        
        elif event.type == KEYDOWN:
            if event.key == K_ESC:
                done = True
            elif event.key == K_F1:
                print "hi world mode"

    # get key current state
    keys = pygame.key.get_pressed()
    if keys[K_LEFT]:
        player.pos.left -= 10
    if keys[K_RIGHT]:
        player.pos.left += 10
    if keys[K_UP]:
        player.pos.top -= 10
    if keys[K_DOWN]:
        player.pos.left += 10
    if keys[K_SPACE]: 
        print 'firing repeated gun'
于 2012-08-11T22:12:41.110 に答える
1

私の推測では、セットリピートはあなたが思っているようには機能しません。基本的に、2 番目のキーが上がった後は、リピートは発生しません。これは私には理にかなっているように思えます: テキスト エディタを開いて "A" キーを押したままにします。「A」が画面全体にこぼれます。次に、「A」キーを押したまま「J」キーを押します。「A」の停止。それが典型的なキーリピートシステムです。

とにかく、この「set_repeat」メソッドの使用が最終的にうまくいくかどうかはわかりません。基本的に、プレイヤーが押すキーは、「発射」または「ジャンプ」をクリックしても「繰り返し」ます。

別の方法として、ユーザーが押したり離したりしたときに状態を保存してみてください。set_repeat は使用せず、次のようにします。

for event in pygame.event.get():
    if event.type == QUIT:
        pygame.quit()
        sys.exit()
    elif event.type == KEYDOWN:
        if pygame.key.get_pressed()[K_LEFT]:
            player.moving_left = True
        if pygame.key.get_pressed()[K_RIGHT]:
            player.moving_right = True
        if pygame.key.get_pressed()[K_UP]:
            player.moving_up = True
        if pygame.key.get_pressed()[K_DOWN]:
            player.moving_down = True
    elif event.type == KEYUP:
        if pygame.key.get_pressed()[K_LEFT]:
            player.moving_left = False
        if pygame.key.get_pressed()[K_RIGHT]:
            player.moving_right = False
        if pygame.key.get_pressed()[K_UP]:
            player.moving_up = False
        if pygame.key.get_pressed()[K_DOWN]:
            player.moving_down = False

# Somewhere else in your game loop...
if player.moving_left:
    player.pos[0] -= 2
if player.moving_right:
    player.pos[0] += 2
if player.moving_up:
    player.pos[1] -= 2
if player.moving_right:
    player.pos[1] += 2
于 2012-08-11T00:40:59.410 に答える