やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

pyxelで画像をアニメーションする(スプライト。パラパラ漫画)

 いいね、生き生きしたね!

成果物

demo editor

コード

#!/usr/bin/env python3
# coding: utf8
import pyxel, os
class App:
    def __init__(self):
        self.window = Window()
        Resource()
        self.pc = PlayerCharacter()
        print('DEFAULT_FPS:', pyxel.DEFAULT_FPS)
        pyxel.run(self.update, self.draw)
    def update(self):
        self.pc.update()
    def draw(self):
        self.window.draw()
        self.pc.draw()

class Resource:
    def __init__(self):
        pyxel.load(self.ResourcePath)
    def __this_dir(self): return os.path.dirname(__file__)
    def __parent_dir(self, path): return os.path.dirname(path)
    @property
    def RootDir(self): return self.__parent_dir(self.__this_dir())
    @property
    def ResourcePath(self): return os.path.join(self.RootDir, 'res/python.pyxres')

class Window:
    def __init__(self, width=128, height=96, border_width=0):
        pyxel.init(width, height, border_width=border_width)
    def draw(self): pyxel.cls(0)

class PlayerCharacter:
    def __init__(self, x=0, y=0, width=8, height=8, img=0, u=0, v=0, colkey=0):
        self.w = width
        self.h = height
        self.x = (pyxel.width / 2) - (self.w / 2)
        self.y = (pyxel.height/ 2) - (self.h / 2)
        self.img = img
        self.u = u
        self.v = v
        self.colkey = colkey
        self.frame = 0
        self.direction = 1 # 1=left, -1=right
    def update(self):
        if pyxel.btn(pyxel.KEY_LEFT) and self.x > 0: self.x -= 1
        if pyxel.btn(pyxel.KEY_RIGHT) and self.x < pyxel.width - self.w: self.x += 1
        if pyxel.btn(pyxel.KEY_UP) and self.y > 0: self.y -= 1
        if pyxel.btn(pyxel.KEY_DOWN) and self.y < pyxel.height - self.h: self.y += 1
        self.__frame()
    def draw(self):
        pyxel.blt(self.x, self.y, self.img, self.u + (self.frame * 8), self.v, self.w, self.h, self.colkey)
    def __frame(self):
        if 0 == pyxel.frame_count % (pyxel.DEFAULT_FPS / 2):
            self.frame += 1
            if self.frame > 1: self.frame = 0

App()

要点

フレーム画像の切替

pyxel.blt(self.x, self.y, self.img, self.u + (self.frame * 8), self.v, self.w, self.h, self.colkey)

 (self.frame * 8)の部分が大事。ここでImageバンク0番の(u, v)座標にある領域を取得する。X座標を8ピクセル分だけ加算したときは2フレーム目の画像位置を指し示すことになる。

 フレーム数は以下で制御する。01

def __frame(self):
    self.frame += 1
    if self.frame > 1: self.frame = 0

 つまり(u, v)(0, 8), (8, 8)のいずれかの座標位置を指すことになる。

アニメ速度の制御

if 0 != pyxel.frame_count % (pyxel.DEFAULT_FPS / 2): return

 この行でアニメーションする速度を遅くしている。さもなくば以下のように爆速になってしまう。

too_fast

FPSとは?

 FPSとは、Frame Per Second の略。1秒あたり何回描画するかを示す値。数値が大きいほどなめらかに動く。フレームとは1回あたりの描画画面のこと。

FPSのデフォルト=30

 FPSのデフォルト値は30。今回は変更していないため、デフォルト値の30である。

FPSの現在値を取得する変数が不明

 pyxel.init()FPSの値を任意に設定できるのだが、その値をどうやって取得するのかわからなかった。

 pyxel.DEFAULT_FPSならあった。なのでデフォルト値のまま使う。FPSはアニメ速度の調整で必要。

pyxel.frame_countとは?

 pyxel.frame_countは起動時からのフレーム数。延々とカウントアップしていく。

計算

 以下の式は、FPS=30のとき、30回に1回を意味する。つまり1秒に1回である。

if 0 == pyxel.frame_count % pyxel.DEFAULT_FPS:

 演算子%は剰余。割った余りを返す。30で割ったなら029を返す。30種類ある。このうち0、つまり1種類の値のみヒットさせると1/30、30回中1回のみヒットとなる。

 上記のコードは、1秒に1回、画像を切替している。でもこれでは遅すぎる。そこで0.5秒に1回、切替することにした。以下のように。

if 0 == pyxel.frame_count % (pyxel.DEFAULT_FPS / 2):

 30/2=1515の剰余は014の15種類。このうち0という1種のみヒットさせる。

 15フレームごとに1回ヒットする。実際は1秒間に30回描画するので、15回に1回ヒットなら、30回のときは2回ヒットする。1秒間に1回ヒットだから、1秒間に2回ヒットするなら0.5秒ごとにヒットすることになる。

 0.5秒ごとに取得する画像の座標をずらしている。これでパラパラ漫画みたいに画像を切替している。

所感

 反対側を向かせたい。左なら反転すればいいだけだし、簡単なのでは? 次回やってみる。

前回まで

対象環境

$ uname -a
Linux raspberrypi 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l GNU/Linux