RPG風にキャラクタを動かす - Kivy Advent Calendar 2013
Animationオブジェクトの実験ですが、無駄にゲーム機っぽいインタフェースにしてみました。
でもA/B/X/Yキーはダミーです。動くのは上下左右キーだけ。
エラーチェック何もしてませんので、平気で画面外へ出て行っちゃいます...
※spellyonさんのサイト「点睛集積」から素材をお借りしています (http://dispell.net/)
(/sdcard/kivy/thrpg/)
android.txt | お約束 |
main.py | スクリプト本体 |
th7_remari.png | 霊夢と魔理沙の24x32チビキャラファイル(背景を透過化) |
chara.atlas | 上の画像に紐付けるatlasファイル 霊夢のデータしか使ってません... |
(android.txt)
title=thrpg author=cheeseshop orientation=landscape
(chara.atlas)
{ "th7_remari.png": { "N0": [ 24, 224, 24, 32], "N1": [ 0, 224, 24, 32], "N2": [ 24, 224, 24, 32], "N3": [ 48, 224, 24, 32], "E0": [ 24, 192, 24, 32], "E1": [ 0, 192, 24, 32], "E2": [ 24, 192, 24, 32], "E3": [ 48, 192, 24, 32], "S0": [ 24, 160, 24, 32], "S1": [ 0, 160, 24, 32], "S2": [ 24, 160, 24, 32], "S3": [ 48, 160, 24, 32], "W0": [ 24, 128, 24, 32], "W1": [ 0, 128, 24, 32], "W2": [ 24, 128, 24, 32], "W3": [ 48, 128, 24, 32] } }
(main.py)
from kivy.app import App from kivy.animation import Animation from kivy.uix.label import Label from kivy.uix.button import Button from kivy.uix.image import Image from kivy.uix.boxlayout import BoxLayout from kivy.uix.relativelayout import RelativeLayout from itertools import cycle from kivy.lang import Builder Builder.load_string('''\: orientation: 'vertical' Label: size_hint_y: 3./8. GridLayout: size_hint_y: 2./8. rows: 3 Label: Button: id: btn_N text: '^' on_press: app.move('N') Label: Button: id: btn_W text: '<' on_press: app.move('W') Label: Button: id: btn_E text: '>' on_press: app.move('E') Label: Button: id: btn_S text: 'v' on_press: app.move('S') Label: Label: size_hint_y: 3./8. : orientation: 'vertical' Label: size_hint_y: 3./8. GridLayout: size_hint_y: 2./8. rows: 3 Label: Button: text: 'X' Label: Button: text: 'Y' Label: Button: text: 'A' Label: Button: text: 'B' Label: Label: size_hint_y: 3./8. : game: _game orientation: 'horizontal' ControlPad: size_hint_x: 1./8. Game: id: _game size_hint_x: 6./8. ActionPad: size_hint_x: 1./8. : canvas: Color: rgb: (.25, .50, .25) Rectangle: size: self.size pos: (0, 0) : drct: 'E' source: 'atlas://chara/E0' allow_stretch: True size: (192, 256) size_hint: (None, None) ''') class Root(BoxLayout): pass class ControlPad(BoxLayout): pass class ActionPad(BoxLayout): pass class Game(RelativeLayout): pass class Character(Image): pass class GameApp(App): def cycle(self, iter=cycle(list('0123'))): return iter.next() def reload(self, anim, ch, progress): ch.source = 'atlas://chara/%s%s' % (ch.drct,self.cycle()) ch.reload() def clear(self, anim, ch): self.moving = False def move(self, drct, *args): if self.moving: return False self.moving = True self.anim = Animation( d=1./1., s=1./8., t='linear', x=self.ch.x+(drct=='E')*192-(drct=='W')*192, y=self.ch.y+(drct=='N')*256-(drct=='S')*256) self.ch.drct = drct self.anim.bind(on_progress=self.reload) self.anim.bind(on_complete=self.clear) self.anim.start(self.ch) return False def build(self): root = Root() self.moving = False self.ch = Character() self.ch.pos = (0, 0) self.ch.drct = 'S' root.game.add_widget(self.ch) return root if __name__ == '__main__': GameApp().run()
解説
Kivyのアニメーション機能
Animationオブジェクトは、描画タイミングにあわせてウィジェットのサイズや位置といったプロパティを逐次変更していくというものです。
引数には次のようなものを指定してインスタンスを生成します。
- サイズや位置の最終値 (x, y, pos, size)
- 描画タイミング (d:アニメーションの時間、s:描画間隔)
- トランジション関数 (t:文字列または3つの引数[初期値,最終値,0〜1の値]を取る関数)
その後 start(widget) メソッドで、引数に指定したウィジェットの移動を開始します。
歩行アニメーション
Animationオブジェクトは、あくまでウィジェットの「数値の」プロパティを順次変更するものです。
関数をうまく使えば画像を変更するのも可能かもしれませんが、ちょっと面倒なので別の方法を取ることにしました。
このサンプルでは、描画タイミングで「on_progressイベントが発生する」ことを利用しています。on_progressで呼び出すハンドラの第2引数には移動中のウィジェットが入るので、そこで同じ向きのキャラクタ画像 (3パターン) を順次入れ替えて歩行のアニメーションを実現しています。
排他処理
アニメーションはプログラムをブロックせず並行に走るので、動作中に別の移動ボタンを受け付けてしまう可能性があります。このサンプルでは「ひとつの移動が終わってから次のキーを受け付けるようにしたい」ので、移動が終わったときのon_completeイベントとフラグ (moving) を使って排他制御をしています。
キー操作がリピートしない
ゲームキーパッドもどきはon_pressを使っているので、その都度離して押してを繰り返さないといけないです。この件については後で解決策を探してみます...
(たぶんon_touch_downを使えばいいんでしょうが、本当にそのボタンを押したかをcollide_pointでチェックする必要があります)