個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Microsoft MVP for Developer Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Xamarin + Cocos Sharp で iOS, Android 対応のゲームを開発する手順 (6) 画面遷移

ご注意

こちらは、Cocos Sharp 1.6.2 までの場合の方法です。
Cocos Sharp 1.7.1 以上をお使いの場合、以下をご覧下さい。

hiro128.hatenablog.jp



今回は画面遷移を行ないます。画像の配置以外は、PCLの中で完結する内容となります。

タイトル画面上の「スタート」ボタンをタップして、ゲーム画面へと遷移するシナリオです。

まずは、ゲーム画面用の Layer を作成しますが、
ここで、今後複数の Layer を使用するので、Layer の基本クラスを作成します。

CocosSharpGameSample.Core プロジェクトの Layers に LayerBase.cs を追加します。

f:id:hiro128:20160311153957p:plain

このクラスに、各 Layer で共通で使用するメソッドを実装していきます。
Cocos Sharp では画面内のオブジェクトである CCNode を Tag と呼ばれる int 型の数値で管理しています。

タグは int ですが、そのまま数値をハードコーディングすると、後々管理が大変なので、
一括管理できるようにするため下記のように Enum で定義しておきます。

Enums/NodeTag.cs を追加します。

f:id:hiro128:20160311154336p:plain

using System;
using System.Collections.Generic;
using System.Text;

namespace CocosSharpGameSample.Core
{
	public enum NodeTag
	{
		StartButton
	}
}

LayerBase.cs にタグで画面内の CCNode を探すメソッドを実装します。

using System.Collections.Generic;
using CocosSharp;

namespace CocosSharpGameSample.Core
{
	public abstract class LayerBase : CCLayerColor
	{

		public LayerBase()
			: base()
		{
		}

		/// <summary>
		/// 指定されたタグ番号に該当するすべての CCNode を取得します。
		/// </summary>
		/// <param name="tag">タグ番号</param>
		/// <returns>指定されたタグ番号に該当するCCNodeのリスト</returns>
		protected List<CCNode> GetChildrenByTag(int tag)
		{
			var nodes = new List<CCNode>();
			foreach (var node in this.Layer.Children)
			{
				if (node.Tag == tag)
				{
					nodes.Add(node);
				}
			}
			return nodes;
		}

	}
}

TitleLayer.cs の継承元クラスも修正しておきます。

public class TitleLayer : LayerBase

遷移先の Layer を作成します。

ゲーム画面の背景の画像をそれぞれのプロジェクトに配置します。

f:id:hiro128:20160311154636p:plain

CocosSharpGameSample.iOS

Images/Images/Background/GameBackground.png

f:id:hiro128:20160311154103p:plain

CocosSharpGameSample.Android

Assets/Images/Background/GameBackground.png

f:id:hiro128:20160311154114p:plain

Layers/GameLayer.cs を追加します。

f:id:hiro128:20160311154125p:plain

GameLayer.cs にゲーム画面の背景を配置します。

using CocosSharp;

namespace CocosSharpGameSample.Core
{
	public class GameLayer : LayerBase
	{

		public GameLayer()
			: base()
		{
		}

		protected override void AddedToScene()
		{
			base.AddedToScene();
			// ゲーム画面の背景画像を配置
			var gameBackground = new CCSprite("/Resources/Images/Background/GameBackground.png", null);
			gameBackground.Position = new CCPoint(this.ContentSize.Center.X, this.ContentSize.Center.Y);
			AddChild(gameBackground);
		}

	}
}

次にスタートボタンを配置します。

f:id:hiro128:20160311154626p:plain

スタートボタンの画像をそれぞれのプロジェクトに配置します。

CocosSharpGameSample.iOS プロジェクト

Images/Images/Button/ButtonStart.png

f:id:hiro128:20160311154522p:plain

CocosSharpGameSample.Android プロジェクト

Assets/Images/Button/ButtonStart.png

f:id:hiro128:20160311154535p:plain

TitleLayer にスタートボタンを配置します。
このときあとから参照できるようにタグも設定しておきます。

// スタートボタンを配置
var startButton = new CCSprite("/Resources/Images/Button/ButtonStart.png", null);
startButton.Position = new CCPoint(this.ContentSize.Center.X, 100f);

// 後から参照できるようにタグを設定
startButton.Tag = (int)NodeTag.StartButton;
this.AddChild(startButton);

また、ボタンのタップを検出するイベントリスナーも作成し、設定しておきます。

// ボタンタップ検出用のイベントリスナー作成
var eventListener = new CCEventListenerTouchOneByOne();
eventListener.OnTouchBegan = this.EventListener_TouchBegan;
eventListener.OnTouchEnded = this.EventListener_TouchEnded;
this.AddEventListener(eventListener, this);

このとき OnTouchBegan イベントのハンドラで true を返しておかないと、
OnTouchEnded イベント自体が発動しなくなるので、
OnTouchBegan イベント時に何もしないとしても、ハンドラを作成し、true を返すようにしておきます。

private bool EventListener_TouchBegan(CCTouch touch, CCEvent e)
{
	// OnTouchBegan で true を返却しないと、OnTouchEndedが発動しない。
	return true;
}

OnTouchEnded イベントではタッチされた座標で、スタートボタンをタップしたのかどうかを判断し、
ゲーム画面への遷移を行ないます。

CCTransitionScene は画面遷移時の効果で CCTransitionFade 以外にもいくつか効果が準備されいます。

private void EventListener_TouchEnded(CCTouch touch, CCEvent e)
{
	// タッチした座標でボタンが押されたかを判定
	CCNode startButton = this.GetChildByTag((int)NodeTag.StartButton);
	if (startButton.BoundingBox.ContainsPoint(touch.Location))
	{
		// 移動先のシーンを作成
		var newScene = new CCScene(this.Window);
		var gameLayer = new GameLayer();
		newScene.AddChild(gameLayer);

		// シーン切り替え時の効果を設定
		CCTransitionScene cCTransitionScene = new CCTransitionFade(1.0f, newScene);

		// ゲーム画面へシーン切り替え
		this.Director.ReplaceScene(cCTransitionScene);
	}
}

以上をまとめると TitleLayer は下記のようになります。

using System.Collections.Generic;
using CocosSharp;

namespace CocosSharpGameSample.Core
{
	public class TitleLayer : LayerBase
	{

		public TitleLayer()
			: base()
		{
		}

		protected override void AddedToScene()
		{
			base.AddedToScene();
			// タイトル画像を配置
			var title = new CCSprite("/Resources/Images/Background/TitleBackground.png", null);
			title.Position = new CCPoint(this.ContentSize.Center.X, this.ContentSize.Center.Y);
			this.AddChild(title);

			// スタートボタンを配置
			var startButton = new CCSprite("/Resources/Images/Button/ButtonStart.png", null);
			startButton.Position = new CCPoint(this.ContentSize.Center.X, 100f);

			// 後から参照できるようにタグを設定
			startButton.Tag = (int)NodeTag.StartButton;
			this.AddChild(startButton);

			// ボタンタップ検出用のイベントリスナー作成
			var eventListener = new CCEventListenerTouchOneByOne();
			eventListener.OnTouchBegan = this.EventListener_TouchBegan;
			eventListener.OnTouchEnded = this.EventListener_TouchEnded;
			this.AddEventListener(eventListener, this);
		}

		private bool EventListener_TouchBegan(CCTouch touch, CCEvent e)
		{
			// OnTouchBegan で true を返却しないと、OnTouchEndedが発動しない。
			return true;
		}

		private void EventListener_TouchEnded(CCTouch touch, CCEvent e)
		{
			// タッチした座標でボタンが押されたかを判定
			CCNode startButton = this.GetChildByTag((int)NodeTag.StartButton);
			if (startButton.BoundingBox.ContainsPoint(touch.Location))
			{
				// 移動先のシーンを作成
				var newScene = new CCScene(this.Window);
				var gameLayer = new GameLayer();
				newScene.AddChild(gameLayer);

				// シーン切り替え時の効果を設定
				CCTransitionScene cCTransitionScene = new CCTransitionFade(1.0f, newScene);

				// ゲーム画面へシーン切り替え
				this.Director.ReplaceScene(cCTransitionScene);
			}
		}

	}
}

以上でプログラムが完成しましたので、タイトル画面上のスタートボタンを押すと、ゲーム画面へと遷移します。

すべてPCLの範囲なので、iOS, Android どちらでも動作します。

f:id:hiro128:20160311154608p:plain

f:id:hiro128:20160311154616p:plain

今回はここまでです。