mieki256's diary



2017/08/24(木) [n年前の日記]

#1 [python][cocos2d] Python + cocos2d で Tiled の tmx を表示

Python + cocos2d で、タイルマップエディタ _Tiled の .tmx ファイルを表示できるか動作確認中。

サンプルその1。 :

とりあえず表示はできた。ソースを貼ってみたり。

_cocos2d_tmx.py
_city_bg2.png
_city_bg2.tmx
_enemy_ball.png

u"""
Tiledで作った.tmxファイルを読み込んで表示してみるテスト.

BGを立てスクロール、かつ、sin値で横にもスクロールさせてみる.

Windows10 x64 + Python 2.7.13 32bit + cocos2d 0.6.4
"""

import cocos
from cocos.director import director
import math


class MyAction(cocos.actions.Action):
    u"""スプライトに与えるアクション."""

    def init(self):
        u"""初期化処理."""
        self.count = 0
        self.scrl_y = 0

    def step(self, dt):
        u"""毎フレーム呼ばれる処理. BGをスクロールさせる."""
        wdw_w, wdw_h = director.get_window_size()
        self.count = (self.count + 45.0 * dt) % 360.0
        self.scrl_y = (self.scrl_y + 256.0 * dt) % 1024
        rad = math.radians(self.count)
        x = int(384 * math.sin(rad) + 1024)
        y = int(self.scrl_y + (wdw_h / 2))

        # スクロール座標を指定
        # 指定座標が画面中央にくるようにスクロールする
        scroller.set_focus(x, y)


class MyLayer(cocos.layer.Layer):
    u"""キャラを乗せるレイヤー. BGスクロールには追従しない."""

    def __init__(self):
        u"""初期化処理."""
        super(MyLayer, self).__init__()
        wdw_w, wdw_h = director.get_window_size()
        sprite = cocos.sprite.Sprite("enemy_ball.png")
        sprite.position = (wdw_w / 2, wdw_h / 2)
        self.add(sprite)
        sprite.do(MyAction())


def main():
    u"""メイン処理."""
    global scroller

    director.init(width=640, height=480,
                  autoscale=True, resizable=False)
    director.show_FPS = True

    # スクロールマネージャを生成
    scroller = cocos.layer.ScrollingManager()

    # .tmxファイルを読み込み
    map_res = cocos.tiles.load('city_bg2.tmx')

    # タイルレイヤー情報を取得。レイヤー名でアクセスできる
    bg_city_layer = map_res['bg_city']
    bg_cloud_layer = map_res['bg_cloud']

    # パララックス(視差)を指定
    # 奥のBGのスクロール量を少なくしてる
    bg_city_layer.parallax = 0.5

    # ScrollingManagerに登録
    scroller.add(bg_city_layer)
    scroller.add(bg_cloud_layer)

    # スクロール座標を指定
    scroller.set_focus(512, 0)

    # シーン生成。ScrollingManagerを登録
    scene = cocos.scene.Scene(scroller)

    # キャラを乗せるレイヤーを生成してシーンに追加登録
    chara_layer = MyLayer()
    scene.add(chara_layer)

    director.run(scene)


if __name__ == '__main__':
    main()

実行するとこんな感じに。



一応描画できてる、ようではあるなと。

ただ、フレームレートが安定してないあたりが、なんだかちょっと気になるけど。もしかして、描画が間に合ってない…?

少し説明。 :

.tmx ファイルを読み込む際は、cocos.tiles.load('tmxファイル名') を使う。map_res = cocos.tiles.load('tmxファイル名') みたいな感じ。

.tmxファイル内に含まれてるタイルマップレイヤーの情報は、レイヤー名で取得できる。layer_a = map_res['layer0'] とかそんな感じで。上記ソースの場合、「bg_city」レイヤーと、「bg_cloud」レイヤーが tmx内に含まれてるので、map_res['bg_city'] みたいな感じで取り出せる。

タイルマップレイヤーにはパララックス情報を指定することができる。パララックスってのは…視差とでも思っておけばいいのだろうか? 手前のBG( = BackGround、背景のこと)と、奥のBGのスクロール速度が違うことで、そこに奥行きがあるように感じたりするけど、そういう効果を巷では「パララックス」と呼んでるっぽい。昔風に言えば「多重スクロール」だろうか。

パララックスは、bg_city_layer.parallax = 0.5 みたいな感じで指定する。この場合、他のレイヤーと比べて0.5倍、つまりは半分のスクロール量にせよ、という指定。

BGの範囲外が表示されそうな時は、範囲内に収まるように補正してくれるっぽい。逆に言えば、ScrollingManager を使うとBGの範囲外を画面に描画できない。

タイルマップレイヤーをスクロールさせるには、cocos.layer.ScrollingManager() を使うと楽、らしい。set_focus(x, y) を使うと、x,y座標の位置が画面の中央にくるようなスクロール座標にしてくれる模様。

注意点というか不満点。 :

cocos2d のタイルマップは、ラップアラウンドをサポートしてないっぽい。ラップアラウンドってのは、右端と左端が繋がってるというか、上端と下端が繋がってるというか…。コレ、どう説明すればいいのだろう。

個人的には、2Dゲーム制作が前提のライブラリなのにタイルマップのBGがラップアラウンドになってないなんて、ちょっと信じられない仕様なのだけど。昔のPCにしろゲーム機にしろ、ハードウェア構成からしてラップアラウンドは当たり前だった印象があるのだけど…。スクロール値に応じでVRAMの一部を書き換える処理を入れるほうがちょっとビミョーに面倒臭いよね、みたいな。まあ、cocos2dの作者さんは、ちょっとずれた文化圏で育った人なんだろう…。

サンプルその2。 :

カーソルキーの入力でスプライトを動かして、そのスプライトの位置に応じたスクロールをするようなサンプルソースを書いてみたり。

_cocos2d_tmx2.py

u"""
Tiledで作った.tmxファイルを読み込んで表示してみるテスト.

カーソルキーでスプライトを移動しつつスクロール。
Zキーで75%と100%表示を切り替え。
Dキーでデバッグ情報表示。

Windows10 x64 + Python 2.7.13 32bit + cocos2d 0.6.4
"""

import cocos
from cocos import actions
from cocos.director import director
import pyglet
from pyglet import gl
from pyglet.window import key


class MyAction(cocos.actions.Action):
    u"""スプライトに与えるアクション."""

    def step(self, dt):
        u"""毎フレーム呼ばれる処理. カーソルキーの入力で移動."""
        x, y = self.target.position

        # キー入力で座標を変更
        # keyboard[key.HOGE] は、ON/OFF が 1/0 で入るっぽい?
        spd = 512.0 * dt
        x += (keyboard[key.RIGHT] - keyboard[key.LEFT]) * spd
        y += (keyboard[key.UP] - keyboard[key.DOWN]) * spd

        # 画面外に出ないように補正
        if x <= 32:
            x = 32
        if x >= 2048 - 32:
            x = 2048 - 32
        if y <= 32:
            y = 32
        if y >= 2048 - 32:
            y = 2048 - 32

        # スプライトの座標を更新
        self.target.position = (x, y)

        # 指定座標が画面中央にくるようにスクロール
        scroller.set_focus(x, y)


def main():
    u"""メイン処理."""
    global keyboard, scroller

    director.init(width=640, height=480,
                  autoscale=False,  # 自動拡大縮小
                  resizable=True    # ウインドウサイズ変更可・不可
                  )

    director.show_FPS = True        # FPS表示

    # スクロールマネージャを生成
    scroller = cocos.layer.ScrollingManager()

    # キャラが乗るスクロールレイヤーを生成
    chara_layer = cocos.layer.ScrollableLayer()

    # スプライトを生成して、スクロールレイヤーに登録
    sprite = cocos.sprite.Sprite("enemy_ball.png")
    sprite.position = (320, 240)
    chara_layer.add(sprite)
    sprite.do(MyAction())

    # .tmxファイルを読み込み
    map_resource = cocos.tiles.load('city_bg2.tmx')

    # タイルマップ用画像がぼやけないようにフィルタを設定
    for res in map_resource.contents.values():
        if isinstance(res, cocos.tiles.TileSet):  # タイルセットだけを対象に
            for tile in res.values():
                gl.glBindTexture(tile.image.texture.target,
                                 tile.image.texture.id)
                gl.glTexParameteri(tile.image.texture.target,
                                   gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST)
                gl.glTexParameteri(tile.image.texture.target,
                                   gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST)

    # タイルレイヤー情報を取得。レイヤー名でアクセスできる
    bg_city_layer = map_resource['bg_city']
    bg_cloud_layer = map_resource['bg_cloud']

    # パララックス(視差)を設定。奥のBGのスクロール量を少なくしている
    bg_city_layer.parallax = 0.25

    # スクロールマネージャに登録
    scroller.add(bg_city_layer)
    scroller.add(bg_cloud_layer)

    # キャラが載るレイヤーもスクロールマネージャに登録
    scroller.add(chara_layer)

    scene = cocos.scene.Scene(scroller)

    # pygletのキーボード入力関係をdirectorに渡してるっぽい
    keyboard = key.KeyStateHandler()
    director.window.push_handlers(keyboard)

    # ウインドウにキーボード入力があった際の処理を関数として記述
    def on_key_press(key, modifier):
        if key == pyglet.window.key.Z:
            # Zキーで拡大縮小
            if scroller.scale == .75:
                scroller.do(actions.ScaleTo(1, 0.5))
            else:
                scroller.do(actions.ScaleTo(.75, 0.5))
        elif key == pyglet.window.key.D:
            # Dキーでデバッグ情報表示
            bg_city_layer.set_debug(True)

    director.window.push_handlers(on_key_press)

    director.run(scene)


if __name__ == '__main__':
    main()

_cocos2d/pyglet でぼやけないスプライトを描画する - Qiita を参考に、タイルマップ用画像がぼやけないようなフィルタ設定も入れてみた。

スプライトをBGスクロールと関連付けたいときは、フツーのレイヤーではなくて、cocos.layer.ScrollableLayer() を生成してそこにスプライトを登録してやる。更に、その ScrollableLayer は、タイルマップと同様に ScrollingManager に登録する、みたいな。

directorの初期化時に、autoscale と resizable を与えることで、サンプルその1とは少し動作が違ってくる。
  • resizable を True にすると、ウインドウサイズを変更することが可能になる。
  • autoscale を False にすることで、ウインドウサイズを変更したり、フルスクリーン表示(Ctrl+F)にした際に、画面内が拡大縮小しない状態になる。

疑問点。 :

マップレイヤーは set_view(x, y, w, h) なるメソッドも持つらしいけど、使い方が分からん…。ScrollingManager() を使って set_focus(x, y) でスクロール値を指定しないといかんのだろうか。しかしそれでは、ビミョーに自由度が足りないような。

set_focus(x, y) ではなく、force_focus(x, y) を使えば、画面端の補正をしないようではあるな…。BGの範囲外(真っ暗な領域)も表示される模様。

以上です。

過去ログ表示

Prev - 2017/08 - Next
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project