用户可以有很多种方式与游戏交互,这一章详细讲解多种输入设备。除了从设备获取信息,我们也要探索如何将用户所做的事情翻译成游戏中有意义的事件。容易交互对任何游戏都至关重要,不管你有多好的外观和声音。

控制游戏

玩过游戏的都知道鼠标和键盘是游戏不可或缺的输入设备。键盘可以控制有限的方向和诸多的命令操作,而鼠标更是提供了全方位的方向和位置操作。不过这两个设备并不是为游戏而生,专业的游戏手柄给玩家提供了更好的操作感,加上力反馈等技术,应该说游戏设备越来越丰富,玩家们也是越来越幸福。

理解键盘控制

现在大多数键盘都是qwerty键盘。我们可以使用pygame.key模块检测所有的键。

检测键盘按下

在Pygame里面有两种方式检测键盘按下。一种方式是处理KEYDOWN事件和KEYUP事件,分别对应键盘按下和键盘松开。当我们使用键盘输入代表移动时,我们只需知道键是否被按下,这种情况可以直接使用pygame.key模块。

每一个键盘上的键都有一个常量键值关联它。每一个常量以**K_**开头,比如字母是K_a到K_z,数字是K_0到K_9,其它的如K_F1,K_LEFT,K_RETURN等。完整列表参考(http://www.pygame.org/docs/ref/key.html)。没有大写字母对应的键值,因为大写字母是小写字母和其它键的组合。

我们可以使用pygame.key.get_pressed函数检查一个键是否被按下。它返回一个布尔值列表,每一个布尔值对应一个键值常量。要查询某一个键,使用其键值常量作为列表下标。比如查看空格是否被按下:

1
2
3
4
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_SPACE]:
    # Space key has been pressed
    fire()

注意 由于硬件的限制,有些键组合不能被检测。原因请看http://www.sjbaker.org/steve/omniv/keyboards_are_evil.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pygame
from pygame.locals import *
from sys import exit

pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)

font = pygame.font.SysFont('arial', 32)
font_height = font.get_linesize()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    screen.fill((255, 255, 255))

    pressed_key_text = []
    pressed_keys = pygame.key.get_pressed()
    y = font_height

    for key_constant, pressed in enumerate(pressed_keys):
        if pressed:
            key_name = pygame.key.name(key_constant)
            text_surface = font.render(key_name + ' pressed', True, (0, 0, 0))
            screen.blit(text_surface, (8, y))
            y += font_height

    pygame.display.update()

让我们更详细地复习pygame.key

  • pygame.key.get_focused - Pygame窗口只有获得焦点后才会接受键事件。如果窗口获得焦点,get_focused函数返回真,否则返回假。全屏模式,总是返回真。
  • pygame.key.get_pressed - 返回一个包含每一个键的布尔值列表。如果值为True,则对应键被按下。
  • pygame.key.get_mods - 返回一个值,指示哪一个修饰键被按下。要检查哪一个修饰键被按下,可以对KMOD_常量使用位与操作。比如检查左shift键被按下,pygame.key.get_mods() & KMOD_LSHIFT。
  • pygame.key.set_mods - 可以设置修饰键模拟组合键按下的效果。要设置一个或多个修饰键,对KMOD_常量使用位或操作。比如设置shift和alt键,可以使用pygame.key.set_mods(KMOD_SHIFT | KMOD_ALT)。
  • pygame.key.set_repeat - 可以使用set_repeat函数重复KEY_DOWN事件,它接受一个初始延迟时间和重复事件延迟时间,单位为毫秒。set_repeat不带参数禁用重复按键事件。
  • pygame.key.name - 接受一个KEY_常量返回一个描述该键的字符串。

使用键盘控制方向

四个基本方向向量:

方向 向量
Left (-1.0, 0.0)
Right (1.0, 0.0)
Up (0.0, –1.0)
Down (0.0, 1.0)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
background_image_filename = 'sushiplate.jpg'
sprite_iamge_filename = 'fugu.png'

import pygame
from pygame.locals import *
from sys import exit
from gameobjects.vector2 import Vector2

pygame.init()

screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()
sprite = pygame.image.load(sprite_iamge_filename).convert_alpha()

clock = pygame.time.Clock()

sprite_pos = Vector2(200, 150)
sprite_speed = 300.

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    pressed_keys = pygame.key.get_pressed()
    key_direction = Vector2(0, 0)
    if pressed_keys[K_LEFT]:
        key_direction.x = -1
    elif pressed_keys[K_RIGHT]:
        key_direction.x = +1
    if pressed_keys[K_UP]:
        key_direction.y = -1
    elif pressed_keys[K_DOWN]:
        key_direction.y = +1

    key_direction.normalize()

    screen.blit(background, (0, 0))
    screen.blit(sprite, sprite_pos)

    time_passed = clock.tick(30)
    time_passed_seconds = time_passed / 1000.0

    sprite_pos += key_direction * sprite_speed * time_passed_seconds

    pygame.display.update()

键盘旋转移动

往八个方向移动有点像人造的,现实中很少看到这样移动的,大多数东西都能自由转向。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
background_image_filename = 'sushiplate.jpg'
sprite_image_filename = 'fugu.png'

import pygame
from pygame.locals import *
from sys import exit
from gameobjects.vector2 import Vector2
from math import *

pygame.init()

screen = pygame.display.set_mode((640, 480), 0, 32)

background = pygame.image.load(background_image_filename).convert()
sprite = pygame.image.load(sprite_image_filename).convert_alpha()

clock = pygame.time.Clock()

sprite_pos = Vector2(200, 150)
sprite_speed = 300.
sprite_rotation = 0.
sprite_rotation_speed = 360. # Degrees per second

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

    pressed_keys = pygame.key.get_pressed()

    rotation_direction = 0.
    movement_direction = 0.

    if pressed_keys[K_LEFT]:
        rotation_direction = +1.0
    if pressed_keys[K_RIGHT]:
        rotation_direction = -1.0
    if pressed_keys[K_UP]:
        movement_direction = +1.0
    if pressed_keys[K_DOWN]:
        movement_direction = -1.0

    screen.blit(background, (0, 0))

    rotated_sprite = pygame.transform.rotate(sprite, sprite_rotation)
    w, h = rotated_sprite.get_size()
    sprite_draw_pos = Vector2(sprite_pos.x - w / 2, sprite_pos.y - h / 2)
    screen.blit(rotated_sprite, sprite_draw_pos)

    time_passed = clock.tick()
    time_passed_seconds = time_passed / 1000.0

    sprite_rotation += rotation_direction * sprite_rotation_speed * time_passed_seconds

    heading_x = sin(sprite_rotation * pi / 180.0)
    heading_y = cos(sprite_rotation * pi / 180.0)
    heading = Vector2(heading_x, heading_y)
    heading *= movement_direction

    sprite_pos += heading * sprite_speed * time_passed_seconds

    pygame.display.update()