Operation KiWi

一生使える言語はPythonだと信じてる

Python3でRPGを作る時に使えそうな技術の断片 -プレイヤーを移動出来るようにしよう-

f:id:sakage24:20170628200017p:plain

引き続き、今回も張り切っていきましょう!!今回はプレイヤーが画面内を縦横無尽に移動できるようにしましょう。

前回のあらすじ

…何したっけ?

www.kiwi-bird.xyz

あー座標を保存できるようにしたんですね。それでは、今回は実際にプレイヤーを動かせるようにしましょうね。

事前準備

警告!!!! 特にPyCharmで開発している方へ

今回扱うモジュールmsvcrtは、PyCharmで普通に実行しても動きません。terminalもしくはコマンドプロンプトから

py run.py

で実行するか、以下の手順を踏んで下さい。

  1. Edit Configurationsを実行する
  2. Emulate terminal in output consoleにチェックを入れる
  3. OKを押す

f:id:sakage24:20170703000723j:plain

f:id:sakage24:20170703000729j:plain

普通にrunで実行すると動かないので、無駄にハマらないように!!w

今回やること

  1. Managerクラス内に新たにmove()関数を作りましょう
  2. move()関数内を作り込んでいきましょう

理想

f:id:sakage24:20170630171940g:plain

なんかの亡霊が残っていますが、gifにした時に何かがおかしくなっているだけですwプログラムはちゃんと動いてます。

move()関数でやること

  • キーボード入力を受け取れるようにしましょう。
    • WASDキーが押下されたら、上左下右に移動するようにしましょう。
    • input()関数だといちいちENTERを押下して確定する必要があるので、msvcrtモジュールを利用しましょう。

msvcrtモジュールについて

過去記事で既に触れていますので、良かったらどうぞ!日本語ドキュメントもあります。

www.kiwi-bird.xyz

やってみよう

player.pyManagerクラスにmove()関数を追加しましょう。前述の通り、msvcrtはwindows上でしか動かないので、Linuxとかでimportしているとエラー落ちします。

そのため、ModuleNotFoundErrorが合った場合、input()関数を使うようにしています。

ソースコード

# player.py
import sources.coordinates
import sys
modules_flag = True
try:
    import msvcrt
except ModuleNotFoundError:
    modules_flag = False


class Manager(object):
    def __init__(self, name):
        self.name = name
        self.current_position = sources.coordinates.Manager()
        self.current_position.y = 5
        self.current_position.x = 5

    def move(self, rect):
        height = rect[0]
        width = rect[1]
        print(self.current_position.position)
        if modules_flag:
            raw_inputs = msvcrt.getwch()
        else:
            raw_inputs = input()
        if raw_inputs == "q":
            print("プログラムを終了します...")
            sys.exit(0)
        elif raw_inputs == "w" and self.current_position.y >= 1:
            self.current_position.y -= 1
        elif raw_inputs == "a" and self.current_position.x >= 1:
            self.current_position.x -= 1
        elif raw_inputs == "s" and self.current_position.y <= height - 2:
            self.current_position.y += 1
        elif raw_inputs == "d" and self.current_position.x <= width - 2:
            self.current_position.x += 1

move()関数の引数*rectはマップの高さ、横幅をタプルで受け取ります。変数の前に*がつくと、C言語経験者の方はもしかすると、ポインタかな?と思うかもしれませんが、Pythonにはポインタはありません。

先頭に*を付けた変数は、呼び出し元によって渡された全ての引数をタプルにまとめます。ちなみに**を先頭につけると辞書形式で受け取ります。

条件分岐でWASDキーが押された場合に座標の位置を加減算します。また、マップの範囲外に移動することを避けるため、__y, __xがそれぞれ一定以下、一定以上の値の場合は処理を行わないような条件も付与しています。

pprint()からの卒業

そろそろpprint()に愛想が尽きたので、マップをちゃんと表示してあげましょう。ViewFilterクラス内のcreate_view()関数の改造と、マップをプレイヤーに見やすい形で表示してくれるprinter()関数を制作しましょう。

# view.py
# create_viewの改造、printer()関数の作成

def create_view(self, player_pos):
    s = ""
    for i in range(self._view_height):
        for j in range(self._view_width):
            if i == player_pos[0] and j == player_pos[1]:
                s = "@"
            elif self._view[i][j] == 0:
                s = "・"
            elif self._view[i][j] == 1:
                s = "村"
            self.printer(width=j, strings=s)

def printer(self, width, strings):
    if width < self._view_width - 1:
        print(strings, end='')
    else:
        print(strings)

run.pyを修正して実行しよう

# run.py
import copy
from sources.maps import WorldMap
from sources.view import ViewFilter
import sources.player


# 初期設定
world_map = WorldMap()
player = sources.player.Manager(name="あなた")
world_map.set_village(y=2, x=4)  # ほげ村
world_map.set_village(y=8, x=3)  # ぴよ村
map_viewer = ViewFilter(v=(copy.deepcopy(world_map.get_world_map())), rect=world_map.get_rect())

# create_view()へプレイヤーへの座標を渡しています。
map_viewer.create_view(player_pos=player.current_position.position)
player.move((world_map.get_height(), world_map.get_width()))

試しに実行してみて下さい。一回移動しただけでプログラムが終了してしまったと思います。こんな時は無限ループを使いましょう。

while True:
    # create_view()へプレイヤーへの座標を渡しています。
    map_viewer.create_view(player_pos=player.current_position.position)
    player.move((world_map.get_height(), world_map.get_width()))

それでは、実行してみて下さい。前述しましたが、実行する際はPyCharmの設定を弄るか、Terminal上でpy run.pyを実行して下さい。ちなみに、qキーでプログラムを終了出来るのようにしています。

どうでしょうか…何かおかしなことになっていませんか?

実行結果

f:id:sakage24:20170703000354g:plain

何故gifにするとこんなことになるのかw

ここまでひどくはないですが、移動するたびに画面が下にずれていって、うっとおしいことこの上ないと思います。

そんな時にお役立ち!エスケープシーケンスを利用しましょう…と思いましたが、疲れたのでまた次回!!

終わり

やっぱり、1日2記事が限界ですね…ww

あ、そろそろPythonに慣れてきましたか?色々自分で試行錯誤してみるのも楽しいですよ! 以下の本は本当におすすめです。ネットの知識だけだと色々偏ります。

オススメの教科書類

指定教科書

入門 Python 3

入門 Python 3

これ無しでPython開発は無理です。私的にはあまり演習問題の類は説かずに知りたいことがあったら読む、辞書的な感じで使っています。

  • スライスって何?
  • リスト内包表記って何なんだよあれ?
  • リストとタプルはどう違うの?
  • そもそも集合って何に使うの?

少しでも上記に当てはまるなら購入しましょう。せっかくPythonを使っているんですから、有効に使えるようにしてみませんか?

副読本

Effective Python ―Pythonプログラムを改良する59項目

Effective Python ―Pythonプログラムを改良する59項目

  • 他言語を得意としているプログラマがPythonを書いた時にやりがちなこと…
  • ゲームの保存に使えるpickle
  • この機能はこう使うべきである
  • while, for文のelseを使うべきではない理由
  • Pythonでセッター, ゲッターを使うべきではない理由

みたいに、様々な事例とともにPythonのベストノウハウ、やってはいけないアンチパターンが紹介されています。pycon2016でも確か推されてましたw

初心者から上級者まで、手元に置いておくべき本です。

副読本2

退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング

退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング

ノンプログラマー向けとありますが、経験者の方も結構アイディアを貰える本でした。 序盤は文法の解説ですので、読み飛ばせます。後半は様々な局面で使えそうな自動化処理の書き方が載っていました。

ゲームの所持アイテムを辞書で表現しよう…みたいにゲームプログラマーに役立つ演習もありましたw

副読本3

ゲーム開発者のためのAI入門

ゲーム開発者のためのAI入門

C言語での解説が主ですが、しっかりとした説明があるので、だいたい分かりました。Pythonでも使える知識がほとんどです。ただ、ファジー理論など、数学的な要素は私にはどうともしがたい躓きポイントでしたw

ローグライクゲームに使える最短経路探索法やAIの作成法、確率の基本まで教えてもらえる良本でした!!

ソースコード

www.dropbox.com

続き

www.kiwi-bird.xyz