2017/08/21(月) [n年前の日記]
#1 [python][cocos2d] Python + cocos2dでアクションを自作
Python + cocos2d をまだ勉強中。cocos2d はアクションという機能(?)を使ってスプライト等の動きを指定できるけど、既に用意されたアクションではなく、自作のアクションを使ってスプライトを動かす方法を試したり。
◎ サンプルソース。 :
試しに書いてみたソースは以下。大量の玉モドキが違う方向に直線移動して、画面外まで行くと方向を変える、いつものソレ。
_cocos2d_myaction.py
_enemy_ball.png
動かすとこんな感じに。
一応動いてくれたっぽい。
_cocos2d_myaction.py
_enemy_ball.png
u"""
cocos2d : sprite and action sample.
アクションを自作して大量にスプライトを表示してみる版.
"""
import cocos
from cocos.director import director
import math
import pyglet
from pyglet import gl
class MyAction(cocos.actions.Action):
u"""スプライトに与えるアクション.
画面外に出たら速度を反転する。
"""
def init(self, vec):
u"""初期化処理. 速度をタプルで渡す."""
self.vec = vec
def step(self, dt):
u"""毎フレーム呼ばれる処理."""
wdw_w, wdw_h = director.get_window_size()
wh = self.target.image.width / 2
hh = self.target.image.height / 2
x, y = self.target.position
dx, dy = self.vec
# 本来はdtを使って計算したほうがいいが
# 処理落ち時に動作がおかしくなるのでdtは使ってない
x += dx
y += dy
# 画面外にぶつかったら速度を反転
if x <= wh or x >= wdw_w - wh:
dx *= -1
if y <= hh or y >= wdw_h - hh:
dy *= -1
# 座標と速度を更新
self.target.position = (x, y)
self.vec = (dx, dy)
class MyLayer(cocos.layer.Layer):
u"""メインとなるレイヤー. cocos.layer.Layerのサブクラス."""
def __init__(self):
u"""初期化処理."""
super(MyLayer, self).__init__()
# pygletで画像を読み込み
image = pyglet.image.load('enemy_ball.png')
# 拡大縮小時にボケないようにフィルタを指定。
gl.glBindTexture(image.texture.target, image.texture.id)
gl.glTexParameteri(image.texture.target,
gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST)
gl.glTexParameteri(image.texture.target,
gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST)
# batchノードを生成登録
self.batchnode = cocos.batch.BatchNode()
self.add(self.batchnode)
# sprite群を生成してbatchノードに登録
num = 160
spd = 3.0
a = 360.0 / num
wdw_w, wdw_h = director.get_window_size()
cx, cy = wdw_w / 2, wdw_h / 2
for i in range(num):
sprite = cocos.sprite.Sprite(image.texture)
sprite.position = (cx, cy)
rad = math.radians(a * i)
dx = spd * math.cos(rad)
dy = spd * math.sin(rad)
sprite.do(MyAction((dx, dy)))
self.batchnode.add(sprite)
def main():
u"""メイン処理."""
director.init()
layer = MyLayer()
scene = cocos.scene.Scene(layer)
director.show_FPS = True
director.run(scene)
if __name__ == "__main__":
main()
動かすとこんな感じに。
一応動いてくれたっぽい。
◎ 少し解説。 :
アクションを自作する場合は、cocos.actions.Action を継承したクラスを作ればいいらしい。上記ソースでは MyAction がソレ。その場合、init() が初期化時(?)に呼ばれる関数のようで。引数は…自分で勝手に増やしていい、のかな。たぶん。
step() が毎フレーム呼ばれる関数。通常は def step(self, dt): みたいな感じに書いて、dt に、前回呼ばれてからの経過時間が入ってくるものと思われる。なので、x += dx * dt みたいな感じで書けば、処理落ち時も速度がおかしくなったりはしないはず。
ではあるのだけど、例えば Ctrl + X を押してFPS表示を有効にした際などは数秒ほど画面が止まったりして、そんな時はとんでもない大きさの変化量になってしまって動作がおかしくなったりして。なので今回は dt を無視することにした。…いや、本来であれば、画面外判定と移動量計算等をもっとちゃんとやればおかしくならないはずだけど、所詮サンプルだし、面倒臭くて…。
自作アクションの中では、self.target に、アクションを割り当てた何か、が入ってる。上記ソースの場合、self.target は玉モドキのスプライトを示してるので、self.target.position を変更すればスプライトの表示位置も変わる。
step() が毎フレーム呼ばれる関数。通常は def step(self, dt): みたいな感じに書いて、dt に、前回呼ばれてからの経過時間が入ってくるものと思われる。なので、x += dx * dt みたいな感じで書けば、処理落ち時も速度がおかしくなったりはしないはず。
ではあるのだけど、例えば Ctrl + X を押してFPS表示を有効にした際などは数秒ほど画面が止まったりして、そんな時はとんでもない大きさの変化量になってしまって動作がおかしくなったりして。なので今回は dt を無視することにした。…いや、本来であれば、画面外判定と移動量計算等をもっとちゃんとやればおかしくならないはずだけど、所詮サンプルだし、面倒臭くて…。
自作アクションの中では、self.target に、アクションを割り当てた何か、が入ってる。上記ソースの場合、self.target は玉モドキのスプライトを示してるので、self.target.position を変更すればスプライトの表示位置も変わる。
◎ BatchNodeについて。 :
アクションとはまた別の話だけど。今回は BatchNode てのを使った。
通常は、レイヤーにスプライトを次々に登録していけばそれで済むのだけど。大量に同じ画像を描画しようとした場合は意外と処理時間がかかってしまうようで。そんな時は、BatchNode てのを使って、スプライト群を BatchNode に登録、かつ、BatchNode をレイヤーに登録すれば高速化ができる。
_CocosNodes - cocos v0.6.4 documentation によると…。スプライトを25枚ぐらい描画するとかだったらフツーにスプライトを登録していく感じでもまあいいんだけど、25〜100枚ぐらい描画するぜ、みたいな時はBatchNodeを使うと4倍ぐらい高速に描画できると思うよ、みたいなことが書かれてるっぽい。たぶん。おそらく。自分は英語赤点野郎なので全然違うことが書いてあるかもしれんけど。
通常は、レイヤーにスプライトを次々に登録していけばそれで済むのだけど。大量に同じ画像を描画しようとした場合は意外と処理時間がかかってしまうようで。そんな時は、BatchNode てのを使って、スプライト群を BatchNode に登録、かつ、BatchNode をレイヤーに登録すれば高速化ができる。
_CocosNodes - cocos v0.6.4 documentation によると…。スプライトを25枚ぐらい描画するとかだったらフツーにスプライトを登録していく感じでもまあいいんだけど、25〜100枚ぐらい描画するぜ、みたいな時はBatchNodeを使うと4倍ぐらい高速に描画できると思うよ、みたいなことが書かれてるっぽい。たぶん。おそらく。自分は英語赤点野郎なので全然違うことが書いてあるかもしれんけど。
◎ スプライトの枚数。 :
古の2Dゲーム機、メガドライブは、QVGA程度(320x224)の画面で32x32のスプライトを80枚表示できたので…。VGA(640x480)の画面だったら64x64のスプライトを80枚表示できればメガドライブ程度の見た目のゲームをおそらくは作れるだろうと。仮に、メガドラのソレがスプライトダブラーで枚数を倍近く増やしていたとしても、160枚。
ということで、上記ソースでは、640x480の画面で、64x64のスプライトを160枚描画してみたのだけど。Core i5-2500 + GeForce GTX 750 Ti の環境では60FPSに近い値で動いてくれているようなので、これなら2Dゲームも作れそうかなと。…まあ、スプライトしか使わないなら、だけど。
問題は、メガドラ同様にBGを2枚描画するとどうなるか…。この手のソレで、BG x 2枚を描画すると、ライブラリによっては結構遅くなる可能性も…。
ということで、上記ソースでは、640x480の画面で、64x64のスプライトを160枚描画してみたのだけど。Core i5-2500 + GeForce GTX 750 Ti の環境では60FPSに近い値で動いてくれているようなので、これなら2Dゲームも作れそうかなと。…まあ、スプライトしか使わないなら、だけど。
問題は、メガドラ同様にBGを2枚描画するとどうなるか…。この手のソレで、BG x 2枚を描画すると、ライブラリによっては結構遅くなる可能性も…。
[ ツッコむ ]
以上、1 日分です。