人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

左右移動

蛇(パイソン)を左キー、右キーで左右に移動できます。アクションゲームではキャラクターの微妙な動きが必要なので移動は小数で計算し、画面に描画するときに整数座標に戻しています。

move.zip
f:id:aidiary:20100812205336p:plain

サンプルスクリプト

#!/usr/bin/env python
#coding: utf-8
import pygame
from pygame.locals import *
import os
import sys

SCR_RECT = Rect(0, 0, 640, 480)

class PyAction:
    def __init__(self):
        pygame.init()
        screen = pygame.display.set_mode(SCR_RECT.size)
        pygame.display.set_caption("左右移動")
        
        # 画像のロード
        Python.left_image = load_image("python.png", -1)                     # 左向き
        Python.right_image = pygame.transform.flip(Python.left_image, 1, 0)  # 右向き
        
        # オブジェクとグループと蛇の作成
        self.all = pygame.sprite.RenderUpdates()
        Python.containers = self.all
        Python()
        
        # メインループ
        clock = pygame.time.Clock()
        while True:
            clock.tick(60)
            self.update()
            self.draw(screen)
            pygame.display.update()
            self.key_handler()

    def update(self):
        """スプライトの更新"""
        self.all.update()
    
    def draw(self, screen):
        """スプライトの描画"""
        screen.fill((0,0,0))
        self.all.draw(screen)
    
    def key_handler(self):
        """キー入力処理"""
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()

class Python(pygame.sprite.Sprite):
    """パイソン"""
    MOVE_SPEED = 5.0  # 移動速度
    
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = self.right_image
        self.rect = self.image.get_rect()
        self.rect.bottom = SCR_RECT.bottom
        
        # 浮動小数点の位置と速度
        self.fpx = float(self.rect.x)
        self.fpy = float(self.rect.y)
        self.fpvx = 0.0
        self.fpvy = 0.0
    
    def update(self):
        """スプライトの更新"""
        # キー入力取得
        pressed_keys = pygame.key.get_pressed()

        # 左右移動
        if pressed_keys[K_RIGHT]:
            self.image = self.right_image
            self.fpvx = self.MOVE_SPEED
        elif pressed_keys[K_LEFT]:
            self.image = self.left_image
            self.fpvx = -self.MOVE_SPEED
        else:
            self.fpvx = 0.0
        
        # 浮動小数点の位置を更新
        self.fpx += self.fpvx
        
        # 浮動小数点の位置を整数座標に戻す
        # スプライトを動かすにはself.rectの更新が必要!
        self.rect.x = int(self.fpx)
        self.rect.y = int(self.fpy)

def load_image(filename, colorkey=None):
    """画像をロードして画像と矩形を返す"""
    filename = os.path.join("data", filename)
    try:
        image = pygame.image.load(filename)
    except pygame.error, message:
        print "Cannot load image:", filename
        raise SystemExit, message
    image = image.convert()
    if colorkey is not None:
        if colorkey is -1:
            colorkey = image.get_at((0,0))
        image.set_colorkey(colorkey, RLEACCEL)
    return image

if __name__ == "__main__":
    PyAction()

スプライトの移動

スプライトは位置と速度を持ちます。一般的にスプライトの位置はself.rectで指定し、画面のその位置に自動的に描画されます→スプライトの使い方(2008/5/17)。当然ながらself.rectは画面のピクセルと対応するので整数値です。しかし、アクションゲームのように微妙な動きが要求されるゲームでは、整数値での位置の更新は限界があります。そこで、小数で位置と速度を定義します。(fpx, fpy)は小数で表した位置、(fpvx, fpvy)は小数で表した速度です。fpとは浮動小数点数(floating point number)の略です。

        # 浮動小数点の位置と速度
        self.fpx = float(self.rect.x)
        self.fpy = float(self.rect.y)
        self.fpvx = 0.0
        self.fpvy = 0.0

位置と速度を定義すると、今 (200.5, 300.2) の位置にいて、次のフレームで (0.1, 0.1) 移動して (200.6, 300.1) に移動といった細かい位置調整ができるようになります。移動処理はupdate()です。

    MOVE_SPEED = 5.0  # 移動速度

    def update(self):
        """スプライトの更新"""
        # キー入力取得
        pressed_keys = pygame.key.get_pressed()

        # 左右移動
        if pressed_keys[K_RIGHT]:
            self.image = self.right_image
            self.fpvx = self.MOVE_SPEED
        elif pressed_keys[K_LEFT]:
            self.image = self.left_image
            self.fpvx = -self.MOVE_SPEED
        else:
            self.fpvx = 0.0
        
        # 浮動小数点の位置を更新
        self.fpx += self.fpvx
        
        # 浮動小数点の位置を整数座標に戻す
        # スプライトを動かすにはself.rectの更新が必要!
        self.rect.x = int(self.fpx)
        self.rect.y = int(self.fpy)

右キーが押されたときは速度を5.0、左キーが押されたときは速度を-5.0にしています。押さないと速度は0.0です。そして、小数で表した位置fpxを速度で更新しています。ここら辺の移動処理は画像の移動と跳ね返り処理(2008/5/9)を見てください。今回は左右移動しかできないのでX座標だけ更新します。

小数の位置 (fpx, fpy) を更新しただけでは画面には反映されません。スプライトの位置はあくまでself.rectだからです。そこで、小数の位置 (fpx, fpy) を整数に変換して self.rect に格納してます。こうすれば、スプライトは自動的にself.rectの位置に描画されます。

(注)ここでは、移動速度が5という整数なので直接self.rectを更新してもよいですが、上のようにしておくと移動速度を0.8にして遅くするといったこともできます。

画像の反転

左へ移動するときと右へ移動するときは画像を左右方向に反転させる必要があります。あらかじめ左向き、右向きの画像を用意しておいてもいいですが、ここではプログラム内で反転させます。

        # 画像のロード
        Python.left_image = load_image("python.png", -1)                     # 左向き
        Python.right_image = pygame.transform.flip(Python.left_image, 1, 0)  # 右向き

python.pngは左向きの蛇の画像です。これを右向きにするためにpygame.transform.flip()関数を使っています。第1引数には元の画像を与えます。左右に反転させたい場合は第2引数を1とし、上下に反転させたい場合は第3引数を1にします。戻り値は反転させた画像です。ここでは、左右に反転させたいので第2引数だけ1にしています。