読者です 読者をやめる 読者になる 読者になる

人工知能に関する断創録

人工知能、認知科学、心理学、ロボティクス、生物学などに興味を持っています。このブログでは人工知能のさまざまな分野について調査したことをまとめています。最近は、機械学習・Deep Learningに関する記事が多いです。



カニ足移動を直す

Pygame

前回までのプレイヤーはどの方向に移動してもこっちを向いていました(こっち見んな!)。これはドラクエ1と同じ移動方式です。これだとあまりかっこよくないので移動する方向をちゃんと向くようにしてみます。

pyrpg06.zip
f:id:aidiary:20100606103906p:plain

サンプルスクリプト

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys
import os
 
SCR_RECT = Rect(0, 0, 640, 480)
ROW,COL = 15,20
GS = 32
DOWN,LEFT,RIGHT,UP = 0,1,2,3
map = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
       [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]
 
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
 
def split_image(image):
    """128x128のキャラクターイメージを32x32の16枚のイメージに分割
    分割したイメージを格納したリストを返す"""
    imageList = []
    for i in range(0, 128, GS):
        for j in range(0, 128, GS):
            surface = pygame.Surface((GS,GS))
            surface.blit(image, (0,0), (j,i,GS,GS))
            surface.set_colorkey(surface.get_at((0,0)), RLEACCEL)
            surface.convert()
            imageList.append(surface)
    return imageList
 
def draw_map(screen):
    """マップを描画する"""
    for r in range(ROW):
        for c in range(COL):
            if map[r][c] == 0:
                screen.blit(grassImg, (c*GS,r*GS))
            elif map[r][c] == 1:
                screen.blit(waterImg, (c*GS,r*GS))
 
def is_movable(x, y):
    """(x,y)は移動可能か?"""
    # マップ範囲内か?
    if x < 0 or x > COL-1 or y < 0 or y > ROW-1:
        return False
    # マップチップは移動可能か?
    if map[y][x] == 1:  # 水は移動できない
        return False
    return True
 
pygame.init()
screen = pygame.display.set_mode(SCR_RECT.size)
pygame.display.set_caption(u"PyRPG 06 カニ足移動を直す")
 
# イメージロード
playerImgList = split_image(load_image("player.png"))  # プレイヤー
grassImg = load_image("grass.png")         # 草地
waterImg = load_image("water.png")         # 水
 
x,y = 1,1  # プレイヤーの位置(単位:マス)
direction = DOWN
animcycle = 24  # アニメーション速度
frame = 0
 
clock = pygame.time.Clock()
 
while True:
    clock.tick(60)
    
    # 経過フレーム数に応じて表示する画像を変える
    frame += 1
    playerImg = playerImgList[direction*4+frame/animcycle%4]
    
    draw_map(screen)  # マップ描画
    screen.blit(playerImg, (x*GS,y*GS))  # プレイヤー描画
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == QUIT:
            sys.exit()
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            sys.exit()
        
        # プレイヤーの移動処理
        if event.type == KEYDOWN and event.key == K_DOWN:
            direction = DOWN
            if is_movable(x, y+1):
                y += 1
        if event.type == KEYDOWN and event.key == K_LEFT:
            direction = LEFT
            if is_movable(x-1, y):
                x -= 1
        if event.type == KEYDOWN and event.key == K_RIGHT:
            direction = RIGHT
            if is_movable(x+1, y):
                x += 1
        if event.type == KEYDOWN and event.key == K_UP:
            direction = UP
            if is_movable(x, y-1):
                y -= 1

イメージの分割

まず、プレイヤーが各方向を向いている画像を用意します。

f:id:aidiary:20100606103907p:plain

足踏みさせる(2008/5/29)で紹介したようにこの一枚絵を32x32ピクセル、4x4=16枚の画像に分割してリストに格納します。今回はforループが二重になっている点に注意してください。

def split_image(image):
    """128x128のキャラクターイメージを32x32の16枚のイメージに分割
    分割したイメージを格納したリストを返す"""
    imageList = []
    for i in range(0, 128, GS):
        for j in range(0, 128, GS):
            surface = pygame.Surface((GS,GS))
            surface.blit(image, (0,0), (j,i,GS,GS))
            surface.set_colorkey(surface.get_at((0,0)), RLEACCEL)
            surface.convert()
            imageList.append(surface)
    return imageList

この関数は下のように使います。

# イメージロード
playerImgList = split_image(load_image("player.png"))  # プレイヤー

playerImgListには分割された画像16枚が入ります。たとえば、playerImgList[4]には上の4番目の画像(左を向いたプレイヤー)が格納されています。

どの方向を向いているか?

DOWN,LEFT,RIGHT,UP = 0,1,2,3
direction = DOWN

# プレイヤーの移動処理
if event.type == KEYDOWN and event.key == K_DOWN:
    direction = DOWN
    if is_movable(x, y+1):
        y += 1
if event.type == KEYDOWN and event.key == K_LEFT:
    direction = LEFT
    if is_movable(x-1, y):
        x -= 1
if event.type == KEYDOWN and event.key == K_RIGHT:
    direction = RIGHT
    if is_movable(x+1, y):
        x += 1
if event.type == KEYDOWN and event.key == K_UP:
    direction = UP
    if is_movable(x, y-1):
        y -= 1

向いている方向によって画像を切り替えたいので、まずは、プレイヤーの向いている方向を表す変数directionを用意します。 directionはDOWN(下向き)で初期化しています。次に移動したときに方向を変えたいのでキーイベントの移動処理の部分に向きを変える処理を付け加えます。移動した方向(キーを押した方向)に応じてdirectionの値を変えています。ここで、移動先に障害物があって移動できなかった場合でも向いている方向を変えている点に注意してください。ドラクエでは壁があって進めなくてもプレイヤーの向きだけは変わっていました。

キャラクターアニメーション

    # 方向がなく足踏みさせるだけの場合
    playerImg = playerImgList[frame/animcycle%4]
                         ↓
    # 向いている方向がある場合
    playerImg = playerImgList[direction*4+frame/animcycle%4]

最後にプレイヤーが向いている方向に合わせてキャラクターアニメーションを変える処理です。これは、少し修正するだけで簡単に直せます。足踏みさせる(2008/5/29)では方向がなかったのでdirectionは関係ありませんでしたが、今回は directionによって表示する画像を変えます。これは、添字にdirection*4を足してやるだけでOKです。なぜかは実際に計算してみるとわかります。frame/animcycle%4の部分は0〜3の値を取ることは足踏みさせる(2008/5/29)で解説済みです。なので各directionを取るときの添字の値は

  direction=DOWN (0)   → 0 〜 3
  direction=LEFT (1)   → 4 〜 7
  direction=RIGHT (2)  → 8 〜 11
  direction=UP (3)     → 12 〜15

となります。イメージの分割の画像番号と比べてみてください。ちゃんとdirectionと対応した画像が選択されてますね!