モーリーのメモ

アプリ開発等(プログラミング、CG作成)、興味を持ったことを実践してまとめるブログです。

モーリーのメモ

クリッピングマスク(画像の切り抜き)の設定方法:Cocos2d-x v3.7(JavaScript)

f:id:mmorley:20151021195739p:plain
 クリッピングマスクは、画像の見せたい部分だけを自由な形状で切り抜いて表示する機能です。逆に言うと見せたくない部分を透明化して隠すことが出来ます。
 切り抜きたい形に色を塗ったマスク画像によって、表示する部分と隠す部分を切り分けます。
 
 アニメーションを行う画像を切り抜いたり、マスク画像にアクションを設定して動かしたりすることも可能です。
 
 実際にクリッピングマスクを使用したデモを作成しましたので、その作成方法を通してクリッピングマスクの設定方法について説明します。

クリッピングマスクを使用したデモ

 下記のリンクからデモを確認できます。
http://githubmorley.github.io/cocosprojects-pages/hellococos06/
 このデモでは下記の内容を作成しました。

  • レンガのテクスチャの画像をキャラクターの形に切り抜く
    • マスク用のスプライトにアニメーションを設定
  • 『New』の文字の画像が光る演出
    • 文字の上だけが光るように、光がはみ出した部分をクリッピングマスクで非表示
  • ボタンが光る演出
    • ボタンの上だけが光るように、光がはみ出した部分をクリッピングマスクで非表示
    • タッチイベントでボタンが押された時の効果(縮小、色を暗くする)を設定

『Cocos Studio 2』での作業

  1. 『Cocos Studio2』を起動
  2. アプリケーションメニューの『File』-『New Project...』をクリック
  3. 開いたウィンドウで『All』の『Cocos Project』を選択し、『Next』をクリック
  4. 次のように設定を行った後、『Finished』をクリック
    • 『Project Name(プロジェクト名)』:HelloCocos(任意のプロジェクト名)
    • 『Orientation(画面の向き)』:『縦向きの画面のアイコン』を選択
    • 『Engine Version(cocos2d-xのバージョン)』:『cocos2d-x-3.7』を選択
    • 『Project Language(使用するプログラミング言語)』:『JavaScript』を選択

使用する画像

 画像を右クリックして、画像の右に書いた名前を付けて保存して下さい。
 *文字とボタンの光用の画像は見えないですが、周囲をぼかした白い画像です。
 *ボタンの表面の画像は光らせたいところを透明にしています。

レンガのテクスチャ f:id:mmorley:20151021114004p:plain:w100 wall.png
キャラクタ型のマスク f:id:mmorley:20151021114135p:plain:w80 chara_clip.png
文字のベース f:id:mmorley:20151021114311p:plain:w80 new_base.png
文字用の光 f:id:mmorley:20151021114547p:plain:h80 new_light.png
文字用のマスク f:id:mmorley:20151021114445p:plain:w80 new_clip.png
ボタンのベース f:id:mmorley:20151021114713p:plain:w100 button_base.png
ボタン用の光 f:id:mmorley:20151021114935p:plain:h80 button_light.png
ボタンの表面 f:id:mmorley:20151021114838p:plain:w100 button_lfront.png
ボタン用のマスク f:id:mmorley:20151021115006p:plain:w100 button_clip.png

プロジェクトへの画像の追加

  1. 『Cocos Studio 2』の画面左下の『Resourcesウインドウ』内で右クリックし、『Import Resources...』をクリック
  2. 先ほど保存した画像を選択し、『Open』をクリック

スプライトシートの追加

 インポートした画像をスプライトシートにまとめます。

  1. アプリケーションメニューの『File』-『New File...』をクリック
  2. 開いたウインドウで、下記のように設定し、『New』をクリック
    • 『Type』:SpriteSheet
    • 『Name』:Plist
  3. 『Resourcesウインドウ』でインポートした画像を選択し、『Canvas(画面作成等の作業をするウインドウ)』の『Plist.csi』にドラッグアンドドロップ

文字用の『Nodeファイル』を作成

  1. アプリケーションメニューの『File』-『New File...』をクリック
  2. 開いたウインドウで下記の設定を行い、『Newボタン』をクリック
    • 『Type』:『Node』
    • 『File Name』:NewNode

文字の『Spriteオブジェクト』の配置とプロパティの設定

  1. 『Cocos Studio 2』の画面左上の『Objectsウインドウ』の『Basic Objects』の『Spriteオブジェクト』を『Canvas』にドラッグアンドドロップ
  2. 画面右の『Propertiesウインドウ』で『Spriteオブジェクト』のプロパティを下記のように変更
    *書いていないパラメータは変更していません。
    • 『Name』:SpriteNewBase
    • 『Position & Size』
      • 『Position』:X 0、Y 0
    • 『Feature』
      • 『Image Resource』:new_base.png

 『Resourcesウインドウ』から画像ファイルをドラッグアンドドロップして登録します。

文字用の光の『Spriteオブジェクト』の配置とプロパティの設定

  1. 『Cocos Studio 2』の画面左上の『Objectsウインドウ』の『Basic Objects』の『Spriteオブジェクト』を『Canvas』にドラッグアンドドロップ
  2. 画面右の『Propertiesウインドウ』で『Spriteオブジェクト』のプロパティを下記のように変更
    • 『Name』:SpriteNewLight
    • 『Feature』
      • 『Image Resource』:new_light.png

文字用の光のアニメーションを作成

アニメーションの作成方法の詳細については、『Cocos Studio 2によるアニメーションの作成 その1:Cocos2d-x v3.7(JavaScript) - モーリーのメモ』でも解説しています。
 『Animation』ウインドウで下記の操作を行います。

  1. 『AutoRecord Frame』にチェックを入れる
  2. フレームレートを30fpsに設定
  3. 『SpriteNewLight』の行のf:id:mmorley:20151005215915p:plainをクリックし、隠れていた項目を表示
  4. 『SpriteNewLight』の『Position』の行の0フレームをクリック
  5. 『Properties』ウインドウで、下記のように設定
    • 『Position & Size』
      • 『Position』:X -90、Y 0
  6. 『SpriteNewLight』の『Position』の行の15フレームをクリック
  7. 『Properties』ウインドウで、下記のように設定
    • 『Position & Size』
      • 『Position』:X 90、Y 0
  8. 『SpriteNewLight』の『Position』の行の30フレームをクリック
  9. 『Add Frameボタンf:id:mmorley:20151005220410p:plain』をクリックし、キーフレームを追加
  10. 『Animationウインドウ』の左上の『Add Animationボタンf:id:mmorley:20151005231929p:plain』をクリック
  11. 『Add Animationウインドウ』で下記のように設定して『OK』をクリック
    • 『Name』:animation
    • 『Start Frame』:0
    • 『End Frame』:30

f:id:mmorley:20151021200410p:plain

ボタン用の『Nodeファイル』を作成

  1. アプリケーションメニューの『File』-『New File...』をクリック
  2. 開いたウインドウで下記の設定を行い、『Newボタン』をクリック
    • 『Type』:『Node』
    • 『File Name』:ButtonNode

ボタン背景の『Spriteオブジェクト』の配置とプロパティの設定

  1. 『Cocos Studio 2』の画面左上の『Objectsウインドウ』の『Basic Objects』の『Spriteオブジェクト』を『Canvas』にドラッグアンドドロップ
  2. 画面右の『Propertiesウインドウ』で『Spriteオブジェクト』のプロパティを下記のように変更
    • 『Name』:SpriteButtonBase
    • 『Position & Size』
      • 『Position』:X 0、Y 0
    • 『Feature』
      • 『Image Resource』:button_base.png

ボタン用の光の『Spriteオブジェクト』の配置とプロパティの設定

  1. 『Cocos Studio 2』の画面左上の『Objectsウインドウ』の『Basic Objects』の『Spriteオブジェクト』を『Canvas』にドラッグアンドドロップ
  2. 画面右の『Propertiesウインドウ』で『Spriteオブジェクト』のプロパティを下記のように変更
    • 『Name』:SpriteButtonLight
    • 『Position & Size』
      • 『Anchor Point』:X 0.8、Y 0.8
      • 『Position』:X 0、Y 0
    • 『Feature』
      • 『Image Resource』:button_light.png

ボタン表面の『Spriteオブジェクト』の配置とプロパティの設定

  1. 『Cocos Studio 2』の画面左上の『Objectsウインドウ』の『Basic Objects』の『Spriteオブジェクト』を『Canvas』にドラッグアンドドロップ
  2. 画面右の『Propertiesウインドウ』で『Spriteオブジェクト』のプロパティを下記のように変更
    • 『Name』:SpriteButtonFront
    • 『Position & Size』-『Position』:X 0、Y 0
    • 『Feature』
      • 『Image Resource』:button_front.png

ボタン用の光のアニメーションを作成

 『Animation』ウインドウで下記の操作を行います。

  1. 『AutoRecord Frame』にチェックを入れる
  2. フレームレートを30fpsに設定
  3. 『SpriteButtonLight』の行のf:id:mmorley:20151005215915p:plainをクリックし、隠れていた項目を表示
  4. 『SpriteButtonLight』の『Skew』の行の0フレームをクリック
  5. 『Properties』ウインドウで、下記のように設定
    • 『General』
      • 『Rotation』:0
  6. 『SpriteButtonLight』の『Skew』の行の30フレームをクリック
  7. 『Properties』ウインドウで、下記のように設定
    • 『General』
      • 『Rotation』:360
  8. 『Animationウインドウ』の左上の『Add Animationボタンf:id:mmorley:20151005231929p:plain』をクリック
  9. 『Add Animationウインドウ』で下記のように設定して『OK』をクリック
    • 『Name』:animation
    • 『Start Frame』:0
    • 『End Frame』:30

f:id:mmorley:20151021200446p:plain
 ボタンは下からベース、光、表面となるように画像を重ねています。

プロジェクトの保存とパブリッシュ

  1. アプリケーションメニューの『File』-『Save All』をクリック
  2. アプリケーションメニューの『Project』-『Publish and Package...』をクリック
  3. 『Publish』- 『Publish Type』- 『Publish To Code IDE』をクリック
  4. 『OK』をクリック

作成したプロジェクトがパブリッシュされて、『Cocos Code IDE』が起動します。

『Cocos Code IDE』での作業

『resource.js』の編集

 スプライトシートを登録します。
 スプライトシートのファイルは、『res』フォルダ内の『Plist.png』と『Plist.plist』です。

  1. 『Cocos Code IDE』の画面左の『Explorer』で、『srcフォルダ』にある『resource.js』をダブルクリックして開く
  2. 『resource.js』に、下記のようにリソースファイルを登録
var res = {
    HelloWorld_png : "res/HelloWorld.png",
    MainScene_json : "res/MainScene.json",
    NewNode_json : "res/NewNode.json", // 文字用Nodeのjsonファイル
    ButtonNode_json : "res/ButtonNode.json", // ボタン用Nodeのjsonファイル
    Plist_png : "res/Plist.png", // スプライトシートの画像ファイル
    Plist_plist : "res/Plist.plist" // スプライトシートのplistファイル
};

『app.js』の編集

『HelloWorldLayer』に、下記のようにコードを書き加えます。

var HelloWorldLayer = cc.Layer.extend({
    sprite:null,
    ctor:function () {
        this._super();
        var size = cc.winSize;

        var mainscene = ccs.load(res.MainScene_json);
        this.addChild(mainscene.node);

        cc.spriteFrameCache.addSpriteFrames(res.Plist_plist); // スプライトシートをキャッシュに登録

        // レンガのテクスチャの画像をキャラクタの形に切り抜く
        var spriteWall = new cc.Sprite( // マスク(切り抜き)されるスプライトを作成
        		cc.spriteFrameCache.getSpriteFrame("wall.png")); // スプライトシートから画像を取得
        var spriteCharaClip = new cc.Sprite( // マスク用のスプライトを作成
        		cc.spriteFrameCache.getSpriteFrame("chara_clip.png")); // スプライトシートから画像を取得
        spriteCharaClip.runAction( // マスク用のスプライトにアクションを設定
        		cc.repeatForever( // アクションをループ実行する
        				cc.sequence( // アクションを連続実行する
        						cc.scaleTo(2, 2, 2), // 拡大する
        						cc.scaleTo(1, 1, 1).easing(cc.easeBounceOut()) // バウンドしながら元の大きさに戻る
        		)));
        var clippingWall = this.clippingNode(spriteWall, spriteCharaClip, cc.p(320, 720)); // マスクされたオブジェクトを作成
        this.addChild(clippingWall); // レイヤーに追加
        
        //『New』の文字の画像が光る演出
        var jsonNewNode = ccs.load(res.NewNode_json); // 『New』スプライト用jsonファイルを読み込む
        jsonNewNode.node.runAction(jsonNewNode.action); // アニメーションを組み込む
        jsonNewNode.action.play("animation", true); // アニメーションを繰り返し再生
        var spriteNewClip = new cc.Sprite( // マスク用のスプライトを作成
        		cc.spriteFrameCache.getSpriteFrame("new_clip.png")); // スプライトシートから画像を取得
        var clippingNew = this.clippingNode(jsonNewNode.node, spriteNewClip, cc.p(320, 480)); // マスクされたオブジェクトを作成
        this.addChild(clippingNew); // レイヤーに追加
     
        // ボタンが光る演出
        var jsonBurronNode = ccs.load(res.ButtonNode_json); // ボタン用jsonファイルを読み込む
        jsonBurronNode.node.runAction(jsonBurronNode.action); // アニメーションを組み込む
        jsonBurronNode.action.play("animation", true); // アニメーションを繰り返し再生
        var spriteButtonClip = new cc.Sprite( // マスク用のスプライトを作成
        		cc.spriteFrameCache.getSpriteFrame("button_clip.png"));
        var clippingButton = this.clippingNode(jsonBurronNode.node, spriteButtonClip, cc.p(320, 240)); // マスクされたオブジェクトを作成
        this.addChild(clippingButton); // レイヤーに追加
        cc.eventManager.addListener({ // スプライトにボタンの機能(タッチイベント)を設定
        	event: cc.EventListener.TOUCH_ONE_BY_ONE, // シングルタッチのみ対応
        	swallowTouches:false, // 以降のノードにタッチイベントを渡す
        	onTouchBegan:function(){ // タッチ開始時
        		clippingButton.setScale(0.95); // サイズを縮小
        		jsonBurronNode.node.setColor(cc.color(200, 200, 200)); // 暗くする
        		return true;
        	}.bind(this), 
        	onTouchMoved:function(){ // タッチ中
        	}.bind(this), 
        	onTouchEnded:function(){ // タッチ終了時
        		clippingButton.setScale(1.0); // サイズを元に戻す
        		jsonBurronNode.node.setColor(cc.color(255, 255, 255)); // 色を元に戻す
        	}.bind(this),
        	onTouchCanceled:function(){  // タッチキャンセル時
        		clippingButton.setScale(1.0); // サイズを元に戻す
        		jsonBurronNode.node.setColor(cc.color(255, 255, 255)); // 色を元に戻す
        	}.bind(this),
        }, this);
        
        return true;
    },
    clippingNode:function(nodeTarget, spriteStencil, pos){ // マスクされたオブジェクトを作成する
    	var clippingNode = new cc.ClippingNode(); // クリッピングマスク用のオブジェクトを作成
    	clippingNode.setStencil(spriteStencil); // マスク用のスプライトを設定
    	clippingNode.setInverted(false); // 表示と非表示の場所を反転
    	clippingNode.setAlphaThreshold(0.5); // しきい値:マスク用の画像の、この透明度以上の部分が表示される
    	clippingNode.addChild(nodeTarget); // マスクされるオブジェクトを設定
    	clippingNode.setPosition(pos); // 場所を設定
    	return clippingNode; // マスクされたオブジェクトを返す
    }
});

クリッピングマスク』のクラス・メソッド

『cc.ClippingNode()』
機能 マスクされたオブジェクトを作成します。
『clippingNode.setStencil(マスク画像)』
機能 『clippingNode』にマスク用の画像を設定します。
引数 マスク用画像:cc.Node
『cc.Node』を継承したクラスのオブジェクトが設定出来ます。
スプライト(『cc.Sprite』)も『cc.Node』を継承しています。
『clippingNode.setInverted(反転フラグ)』
機能 『clippingNode』の表示と非表示を反転します。
引数 反転フラグ:boolean値
true:マスク画像の透明部分を表示
false:マスク画像の不透明部分を表示
『clippingNode.setAlphaThreshold(しきい値);』
機能 『clippingNode』のマスク画像の
表示と非表示を分ける透明度のしきい値を設定します。
引数 しきい値:Number値
『clippingNode.addChild(マスクされる画像)』
機能 『clippingNode』のマスクされる画像(オブジェクト)を設定します。
引数 マスクされる画像:cc.Node

補足説明

マスク用の画像について

 マスク用の画像は背景が透明な画像で、切り抜きたい形状に着色しています。今回は黒色にしましたが、何色でも良いようです。

座標系について

 『clippingNode.setStencil(マスク画像)』で設定されるマスク画像と『clippingNode.addChild(マスクされる画像)』で設定されるマスクされる画像の座標は、『clippingNode』のアンカーポイントを中心とした座標です。2つの画像に重なる部分がないと何も表示されません。
 
以上です。

あとがき

 ボタンを光らせる演出がしたくて画像を切り抜く方法を調べました。
 難しくなるかなと思いましたが、クリッピングマスク用のクラスがあり、アニメーションを含むオブジェクトも切り抜くことができたので、このクラスを利用して作成しました。
 マスクする部分としない部分をピクセル単位で分けるため、境界がギザギザになるので、そこは考慮にいれないといけないですね。