読者です 読者をやめる 読者になる 読者になる

続cocos2d事始 - Menuを作る

Python

Layerを作る

前回作ったところまでで、最小限のSceneを作ってゲームに必要な要素を追加していく準備ができた。

SceneにはLayer, Menu, SpriteといったCocosNodeのサブクラスを追加していくことになるが、まずは一番基本的なLayerクラスを使って、背景を追加する。また、背景の描画にはtetricoのコードを参考に、pyglet経由でPNG画像を描画してみた。

# -*- coding: utf-8 -*-
import os

from cocos.director import director
from cocos.scene import Scene
from cocos.layer import *

import pyglet
from pyglet.gl import *

RESOURCE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data'))

class BackgroundLayer(Layer):
    def __init__(self):
        super(BackgroundLayer, self).__init__()
        # 背景画像を読み込み
        self.img = pyglet.resource.image('background.png')

    def draw(self):
        glPushMatrix()
        self.transform()
        self.img.blit(0, 0)
        glPopMatrix()

if __name__ == '__main__':
    # ビットマップ画像等の必要なリソースをpygletに登録する
    pyglet.resource.path.append(RESOURCE_PATH)
    pyglet.resource.reindex()

    director.init(resizable=True, width=400, height=500)
    scene = Scene()

    # Layerオブジェクトをsceneに追加
    background = BackgroundLayer()
    scene.add(background)

    director.run(scene)

BackgroundLayerというLayerクラスのサブクラスを作り、drawメソッドの中で描画を行う。生成したLayerオブジェクトをSceneに追加するときには、addメソッドを使う。ここまでは明快。

Menuを追加する

次にゲームのオプションの選択に使うMenuオブジェクトを追加する。メニューの個々の選択肢を表しているのがMenuItemオブジェクトで、それらを管理するのがMenuオブジェクト。先程のLayerと同じように、自分でメニューを作る時には、Menuのサブクラスをつくることになるだろう。

以下ではMainMenuというメニューて作ってSceneに追加している。

# -*- coding: utf-8 -*-
import os

from cocos.director import director
from cocos.scene import Scene
from cocos.layer import *
from cocos.menu import *

import pyglet
from pyglet.gl import *

RESOURCE_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data'))

class BackgroundLayer(Layer):
    def __init__(self):
        super(BackgroundLayer, self).__init__()
        self.img = pyglet.resource.image('background.png')

    def draw(self):
        glPushMatrix()
        self.transform()
        self.img.blit(0, 0)
        glPopMatrix()


class MainMenu(Menu):
    def __init__(self):
        super(MainMenu, self).__init__("Main Menu")

        items = []
        items.append(MenuItem("Spam", self.on_menu_selected, "spam"))
        items.append(MenuItem("Egg", self.on_menu_selected, arg="egg"))
        items.append(MenuItem("Ham", self.on_menu_selected))

        self.create_menu(items)

    def on_menu_selected(self, *args, **kwds):
        print "menu selected", args, kwds


if __name__ == '__main__':
    pyglet.resource.path.append(RESOURCE_PATH)
    pyglet.resource.reindex()

    director.init(resizable=True, width=400, height=500)
    scene = Scene()

    main_menu = MainMenu()
    scene.add(main_menu, z=1)

    background = BackgroundLayer()
    scene.add(background, z=0)

    director.run(scene)

特筆するべきことは2つ。まず、最初のBackgroundLayerの例と違って、今度は深度を指定してオブジェクトを追加している。

scene.add(main_menu, z=1)
scene.add(background, z=0)

の部分のこと。深度はz引数で指定し、数値が大きいほど上に表示される。

次にMenuItemを追加している部分だが、

items = []
items.append(MenuItem("Spam", self.on_menu_selected, "spam"))
items.append(MenuItem("Egg", self.on_menu_selected, arg="egg"))
items.append(MenuItem("Ham", self.on_menu_selected))
self.create_menu(items)

"Spam", "Egg", "Ham"というメニューを追加し、それぞれクリックされた時にMainMenuオブジェクトのon_menu_selectedメソッドが呼ばれるようにしている。MenuItemの第一引数(メニューラベルの文字列)、第二引数(コールバック関数)以降は、可変引数、キーワード引数をとり、クリックされた時にコールバックにわたされる引数を与えられる。

この例を実行して、メニューをクリックすると、標準出力に

menu selected ('spam',) {}
menu selected () {'arg': 'egg'}
menu selected () {}

のように出力される。

Menuがフォーカスされた時のエフェクトを追加する

上の例では、MainMenuのコンストラクタで、

self.create_menu(menu_items)

のようにMenuItemサブクラスのリストを与えてメニューを作っているが、これを、

self.create_menu(items, zoom_in(), zoom_out())

のように変更すると、メニューの選択肢をクリックした時の選択肢に対するエフェクトを追加することができる。 zoom_in(), zoom_out()だと、選択肢にフォーカスがあたった時にズームインし、フォーカスが外れた時にズームアウトする。

zoom_in(), zoom_out()はどこで定義されている何者だということになると思うが、cocos.menuモジュールの中に定義さている。

def zoom_in():
    '''Predefined action that scales to 1.5 factor in 0.2 seconds'''
    return ScaleTo( 1.5, duration=0.2 )

def zoom_out():
    '''Predefined action that scales to 1.0 factor in 0.2 seconds'''
    return ScaleTo( 1.0, duration=0.2 )

zoom_in()は0.2秒間で元のサイズの1.5倍に拡大するようなActionオブジェクト、zoom_out()は0.2秒で元のサイズに戻るようなActionオブジェクトを返す、ということなのだが、Actionオブジェクトに関しては、しかるべきところでもっと研究してみようと思う。


まだ続きます。たぶん。