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

人工知能に関する断創録

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



マップとの当たり判定

Pygame

前回のスクリプトでは、プレイヤーは海のマスにも移動できてしまいました。今回はプレイヤーが海のマスに移動できないようにしてみます。こういう特定のマップチップ(たとえば、海、山、壁など)に移動できないようにする処理をマップとの当たり判定と呼びます。マップとは別にマップの上に置くイベント(たとえば、他のキャラクター)の上に移動できないようにする処理はイベントとの当たり判定と呼び、マップとの当たり判定とは区別します。

pyrpg04.zip
f:id:aidiary:20100605234340p: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
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 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 04 マップとの当たり判定")
 
# イメージロード
playerImg = load_image("player1.png", -1)  # プレイヤー
grassImg = load_image("grass.png")         # 草地
waterImg = load_image("water.png")         # 水
 
x,y = 1,1  # プレイヤーの位置(単位:マス)
 
while True:
    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:
            if is_movable(x, y+1):
                y += 1
        if event.type == KEYDOWN and event.key == K_LEFT:
            if is_movable(x-1, y):
                x -= 1
        if event.type == KEYDOWN and event.key == K_RIGHT:
            if is_movable(x+1, y):
                x += 1
        if event.type == KEYDOWN and event.key == K_UP:
            if is_movable(x, y-1):
                y -= 1

マップとの当たり判定

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

プレイヤーの移動(2008/5/24)では、キーイベントの部分にプレイヤーの移動処理を書いていました。今回は、座標 (x,y) を更新する前に移動先のマスが移動可能か調べる処理 is_movable(x, y) を追加しています。is_movable(x, y) は、マップの (x,y) が移動可能なときTrueを返し、移動不可のときFalseを返す関数です。移動可能なときのみプレイヤーの座標を更新している点に注意してください。移動不可のときは is_movable() がFalseを返すのでif文の中身は実行されません。つまりプレイヤーは移動しません。

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

is_movable() では、まずマップの範囲外のマスはただちにFalseを返しています。今回は周りが海に囲まれているのでマップ範囲外へ移動できませんが一応書いておいた方がよいでしょう。次にマップチップが移動可能か調べています。今回は、草地(0)と海(1)しかありませんが、map[y][x]が海の場合のみ移動不可なのでFalseを返します。それ以外のときは移動可能なので最後にTrueを返しています。後で海以外のマップチップ(たとえば、山や壁など)を追加したときにはここで移動不可の処理を追加します。さらに後になりますが、他のキャラクターを追加したときのイベントの当たり判定もここに書くことになります。

これでプレイヤーを小さな島に閉じ込められました(笑)