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

個人的なメモ 〜Cocos Sharp 情報を中心に‥

Tomohiro Suzuki @hiro128_777 のブログです。Cocos Sharp の事を中心に書いています。 Microsoft MVP for Visual Studio and Development Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

Cocos Sharp Deep Dive(1) CCAction

Xamarin Cocos Sharp

これまでCCMoveToや、CCRotateToなど個々のアクションの使い方をご説明しましたが、今回は、実際にこれらのアクションがどのような内部動作をしているのかを深く解析したいと思います。

例えば、CCMoveToをはじめとする全てのアクションを利用する場合には以下のようにCCNodeRunActionメソッドを使用します。

var moveToAction = new CCMoveTo(3f, new CCPoint(50f, 100f));
node.RunAction(moveToAction);

まずは、RunActionしたアクションがどのように管理されているかを確認してみましょう。

CCNodeRunActionメソッドのソースコードを見てみると以下のようになっています。

public CCActionState RunAction(CCAction action)
{
	return ActionManager != null ? ActionManager.AddAction(action, this, !IsRunning) : AddLazyAction(action, this, !IsRunning);
}

RunActionしたアクションをActionManagerに登録しています。AddLazyActionの方も追っていくと、最終的にはActionManagerに登録するようになっています。

よって、RunActionしたアクションは全てActionManagerで一括管理されるようになっています。

また、ActionManagerAddActionを確認すると、追加されたアクションのStartActionをコールし、CCActionStateを返すようになっています。

public CCActionState AddAction(CCAction action, CCNode target, bool paused = false)
{
	// 省略
	var state = action.StartAction(target);
	// 省略
	return state;
}

次に、実際のアクションの動きがどのように動作しているのかを確認します。
基本的な動作原理として、1フレームごとに、ActionManagerUpdateメソッドがコールされます。
引数のdtは deltaTime つまり、前のフレームから現在のフレームまでの経過時間です。

public void Update(float dt)
{
	// 省略
	for (int i = 0; i < count; i++)
	{
		// 省略
		currentTarget.CurrentActionState.Step(dt);
		// 省略
	}
}

この中で、登録されているCCActionStateStepメソッドをコールしています。

Stepメソッドは、CCMoveToや、CCRotateToStartActionしたときに生成されるCCMoveToStateおよび、CCRotateToStateの継承元であるCCFiniteTimeActionStateに実装されています。

Stepメソッドでは、当該のアクションのすでに完了した部分の時間を分子に、アクション全体の所要時間を分母にし、アクションが何%進んだかを示す値を引数にしUpdateメソッドをコールしています。

protected internal override void Step(float dt)
{
	if (firstTick)
	{
		firstTick = false;
		Elapsed = 0f;
	}
	else
	{
		Elapsed += dt;
	}

	Update (Math.Max (0f,
			Math.Min (1, Elapsed / Math.Max (Duration, float.Epsilon)
			)
		)
	);
}

これを、例としてCCMoveToStateで確認すると、以下のようになっています。

残りの未完了のアクションであるPositionDeltaのうち、今回のフレームで消化すべき量を求めてその分だけアクションするようになっています。

public class CCMoveToState : CCMoveByState
{

	public CCMoveToState (CCMoveTo action, CCNode target)
		: base (action, target)
	{ 
		StartPosition = target.Position;
		PositionDelta = action.PositionEnd - target.Position;
	}

	public override void Update (float time)
	{
		if (Target != null)
		{
			CCPoint currentPos = Target.Position;

			CCPoint newPos = StartPosition + PositionDelta * time;
			Target.Position = newPos;
			PreviousPosition = newPos;
		}
	}
}

つまりアクションは一見するとスケジューラのようなフレームで管理された動作とは違うように見えますが、詳しく見てみると、スケジューラと同じように1フレームごとの動作に細切れにされた上で実行されているのが分かります。

というわけで、今回はここまでです。

Cocos Sharp での基本的なゲームの制御の方法(5) フェードイン、フェードアウト。

Xamarin Cocos Sharp

今回はフェードイン、フェードアウトする方法についてご説明します。
フェードイン、フェードアウトすること自体は非常に簡単ですが、それぞれ1つずつ注意点があります。

フェードイン、フェードアウトには以下のクラスを使用します。

public CCFadeIn(float durataion);
public CCFadeOut(float durtaion);

それぞれの使用方法ですが、

フェードインを実行する。

public CCFadeIn(float durataion);

durataion で指定された所要時間でフェードインします。

注意点:CCFadeIn を実行する前に CCNodeOpacity を 0 に設定しないと、今からフェードインする CCNode が画面上に表示されてしまいます。
CCFadeInCCNodeOpacitydurataion で指定された所要時間で 0 から 255 にリニアに変化させるためです。

(例)スプライトを3秒の所要時間でフェードイン

var sprite = new CCSprite("Image.png", null);
//  Opacity を 0 に設定するのを忘れずに!
sprite.Opacity = 0;
this.AddChild(sprite);

// 3秒の所要時間でフェードイン
var fadeIn = new CCFadeIn(3f);
sprite.RunAction(fadeIn);

フェードアウトを実行する。

public CCFadeOut(float durtaion);

durataion で指定された所要時間でフェードアウトします。

注意点:CCFadeOut で画面上から見えなくなっても、Opacity = 0 で画面上にオブジェクトは残ったままですので、きちんと Remove する必要があります。


(例)スプライトを3秒の所要時間でフェードアウトし、2秒待って、スプライトを消去する

var sprite = new CCSprite("Image.png", null);

var fadeOut = new CCFadeOut(3f);
var delayTime = new CCDelayTime(2f);
var removeSelf = new CCRemoveSelf(true);

// 逐次実行アクション作成
var sequence = new CCSequence(fadeOut, delayTime, removeSelf);

sprite.RunAction(sequence);

というわけで、今回はここまでです。

Cocos Sharp での基本的なゲームの制御の方法(4) アクションにひも付けてメソッドを実行する。

Xamarin Cocos Sharp

今回はアクションにひも付けてメソッドを実行する方法についてご説明します。

アクションにひも付けてメソッドを実行するというのは具体的には、
・キャラのアクションに合わせて効果音を鳴らす。
・キャラのアクションにひも付けて何らかのフラグを立てる。
・敵キャラが弾を発射するなどの、CCMoveTo, CCScaleBy, CCRotateBy 等では表現できない複合的なアクション。
というようなものが該当します。

以下の4つのクラスがあります。

public CCCallFunc(Action selector);
public CCCallFuncN(Action<CCNode> selector);
public CCCallFuncND(Action<CCNode, object> selector, object d);
public CCCallFuncO(Action<object> selector, object pObject);

それぞれの使用方法ですが、

CCCallFunc … 引数なしの callback を実行する。

public CCCallFunc(Action selector);

(例)ステージ1クリアのフラグを立てる

var isFirstStageClearToTrue = new CCCallFunc(() =>
{
	this.IsFirstStageClear = true;
}

これは結構使います。特に前回の CCSequence と組み合わせて使うことが多いです。

CCCallFuncN … アクションが実行される CCNode を引数とした callback を実行する。

public CCCallFuncN(Action<CCNode> selector);

(例)ボスを倒したアクションを発動し、BGMを停止し、ボスを倒したSEを鳴らす。

var bossDefeated = new CCCallFuncN(node =>
{
	CCMoveTo bossDefeatedAction = new CCMoveTo(3f, new CCPoint(node.PositionX, node.PositionY - 1000f));
	CCSimpleAudioEngine.SharedEngine.StopBackgroundMusic();
	CCSimpleAudioEngine.SharedEngine.PlayEffect("Audio/SE/BossDefeated.xnb");
	node.RunAction(bossDefeatedAction);
});

これも結構使います。同様に前回の CCSequence と組み合わせて使うことが多いです。

CCCallFuncND … アクションが実行される CCNode および、任意のオブジェクトを引数とした callback を実行する。

public CCCallFuncND(Action<CCNode, object> selector, object d);

(例)ボスを倒したアクションを発動し、BGMを停止し、bossDefeatedSeFile で指定されたSEを鳴らす。

var bossDefeated = new CCCallFuncND((node, fileName) =>
{
	CCMoveTo bossDefeatedAction = new CCMoveTo(3f, new CCPoint(node.PositionX, node.PositionY - 1000f));
	CCSimpleAudioEngine.SharedEngine.StopBackgroundMusic();
	CCSimpleAudioEngine.SharedEngine.PlayEffect((string)fileName);
	node.RunAction(bossDefeatedAction);
},
bossDefeatedSeFile
);

実際にはこれは使ったことがないですね(笑)

CCCallFuncO… 指定した pObject を引数とする callback を実行する。

var CCCallFuncO(Action<object> selector, object pObject);

(例)う~ん、CCCallFuncO を効果的に使用できる状況が思いつきません!

正直 CCCallFuncO は、使わないですね…

CCCallFunc と CCCallFuncN を使いこなせればほぼ思い通りの動作ができると思います。

というわけで、今回はここまでです。

Cocos Sharp での基本的なゲームの制御の方法(3) 連続したアクションを実行する。

Xamarin Cocos Sharp

今回は連続したアクションの作成方法についてご説明します。

連続したアクションを実行するには、アクションを複数作成し、CCSequence を作成し、CCNode の RunAction メソッドで実行します。

public CCSequence(params CCFiniteTimeAction[] actions);

連続したアクション間に「待ち」を入れたい場合には、 CCDelayTime で待ち時間を設定します。

例として
移動 -> 1秒待ち -> 拡大 -> 2秒待ち -> 回転
の連続したアクションを作成しましょう。

// 連続して実行したいアクションをそれぞれ作成
var moveByAction = new CCMoveTo(3f, new CCPoint(50f, 100f));
var scaleByAction = new CCScaleBy(3f, 2f);
var rotateByAction = new CCRotateBy(3f, 45f);

// アクション間の待ち時間
var delayTime0 = new CCDelayTime(1f);
var delayTime1 = new CCDelayTime(2f);

// 連続アクション作成
var sequence = new CCSequence(moveByAction, delayTime0, scaleByAction, delayTime1, rotateByAction);

// アクション実行
node.RunAction(sequence);

これだけで連続したアクションを作成できます。

次回は、アクションにひも付けてメソッドを実行する方法をご紹介します。

今回はここまでです。

Cocos Sharp での基本的なゲームの制御の方法(2) 代表的なアクションの種類

Xamarin Cocos Sharp

前回の繰り返しになりますが、大雑把に言えば、Cocos Sharp での基本的なゲームの制御の方法をマスターするには以下の2つの概念を理解すれば十分です。

① スケジューラ 一定間隔で繰り返し何らかの処理を行なう。
② アクション  画面上のオブジェクトに対して何らかの動きを指示する。また、オブジェクトにひも付けて何らかの処理を行なう。

今回は、②のアクションについてご説明します。

アクションとは、画面上のオブジェクトに対して何らかの動きを指示したり、オブジェクトにひも付けて何らかの処理を行なうものです。
例えば、画面上のキャラの移動などが該当します。

Cocos Sharp のアクションは、CCAction の派生クラスになります。

以下に、代表的なアクションをご紹介します。

public CCMoveTo(float duration, CCPoint position);
public CCMoveBy(float duration, CCPoint position);
public CCScaleTo(float duration, float scale);
public CCScaleBy(float duration, float scale);
public CCRotateTo(float duration, float deltaAngle);
public CCRotateBy(float duration, float deltaAngle);

To と By の違いですが、
To はアクション完了時の絶対値、By は現在からの相対値になります。

それぞれの使用方法ですが、

絶対位置指定の移動

public CCMoveTo(float duration, CCPoint position);

position で指定された画面上の座標に duration の所要時間で移動します。

(例)画面上の 50, 100 の座標に3秒の所要時間で移動します。

var moveToAction = new CCMoveTo(3f, new CCPoint(50f, 100f));
node.RunAction(moveToAction);

相対位置指定の移動

public CCMoveBy(float duration, CCPoint position);

現在の座標から position で指定された座標分だけ duration の所要時間で移動します。

(例)現在位置より画面上の座標で、 X軸方向に 50、 Y軸方向に100の座標に3秒の所要時間で移動します。

var moveByAction = new CCMoveBy(3f, new CCPoint(50f, 100f));
node.RunAction(moveByAction);

絶対倍率指定の拡大縮小

public CCScaleTo(float duration, float scale);

オブジェクトの当初のサイズを基準として scale で指定された倍率に duration の所要時間で拡大縮小します。

(例)オブジェクトを3秒の所要時間で当初のサイズより2倍に拡大(場合によっては縮小)します。

var scaleToAction = new CCScaleTo(3f, 2f);
node.RunAction(scaleToAction);

相対倍率指定の拡大縮小

public CCScaleBy(float duration, float scale);

現在のサイズから scale で指定された倍率に duration の所要時間で拡大縮小します。

(例)オブジェクトを3秒の所要時間で現在のサイズより2倍に拡大します。

var scaleByAction = new CCScaleBy(3f, 2f);
node.RunAction(scaleByAction);

絶対角度指定の回転

public CCRotateTo(float duration, float deltaAngle);

オブジェクトの0度を基準として deltaAngle で指定された角度に duration の所要時間で回転します。

(例)オブジェクトを3秒の所要時間で45度の角度に回転させます。

var rotateToAction = new CCRotateTo(3f, 45f);
node.RunAction(rotateToAction);

相対角度指定の回転

public CCRotateBy(float duration, float deltaAngle);

オブジェクトの現在の角度を基準として deltaAngle で指定された角度分だけ duration の所要時間で回転します。

(例)オブジェクトを3秒の所要時間で当初の角度を基準として45度回転させます。
※現在の角度が45度なら当初の角度から90度の回転となります。

var rotateByAction = new CCRotateBy(3f, 45f);
node.RunAction(rotateByAction);

以上です。

次回は連続したアクションの作成方法についてご説明します。

今回はここまでです。

Cocos Sharp での基本的なゲームの制御の方法(1) スケジューラ

Xamarin Cocos Sharp

これまで、とりあえずサンプルコードを見ていただいたほうがわかりやすいと思ったので、あまり説明をせず、サンプルコードをご紹介しましたが、ここで、一度基本的なゲームの作り方を整理したいと思います。

大雑把に言ってしまえば、Cocos Sharp での基本的なゲームの制御の方法をマスターするには以下の2つの概念を理解すれば十分です。

① スケジューラ 一定間隔で繰り返し何らかの処理を行なう。
② アクション  画面上のオブジェクトに対して何らかの動きを指示する。また、オブジェクトにひも付けて何らかの処理を行なう。

基本的にこれだけです。

ですが、この2つは処理の方向性がまったく違うのでそれぞれの特性をよく理解し使用しましょう。

今回は、①のスケジューラについてご説明します。

スケジューラとは一定間隔で繰り返し何らかの処理を行なう機能です。
ゲームの中ではゲームを進行する上で必要な様々な処理に使用します。
例えば、自機の移動、当たり判定、スコアなどの各種表示項目の制御などです。

Cocos Sharp のスケジューラは具体的には、CCNode クラスの Schedule メソッドです。
いくつかオーバーロードがありますが、次の2つだけ覚えれば十分です。

public void Schedule(Action<float> selector, float interval);
public void ScheduleOnce(Action<float> selector, float delay);

それぞれの使用方法ですが、

public void Schedule(Action<float> selector, float interval);

interval で指定された間隔ごとに selector で指定されたデリゲートを実行します。
※デリゲートはラムダ式で書くので、要はラムダ式内で指定したメソッドを実行するということになります。

例えば、以下のような場合、0.1秒ごとに、DetectCollisions() メソッドを実行します。

this.Schedule(t => this.DetectCollisions(), 0.1f);

private void DetectCollisions()
{
	// 当たり判定の処理
}

この場合、0.1秒ごとに当たり判定が実行されます。

public void ScheduleOnce(Action<float> selector, float delay);

selector で指定されたデリゲートを delay で指定された時間遅延させて実行します。

例えば、以下のような場合、1秒後に、PerformGameOver() メソッドを実行します。

this.ScheduleOnce(t => this.PerformGameOver(), 1f);

private void PerformGameOver()
{
	// ゲームオーバーの処理
}

この場合、1秒後にゲームオーバーの処理が実行されます。

スケジューラは、Cocos Sharp でゲームを作成する場合最も重要な機能です。
Cocos Sharp ではスケジューラが回ることでゲームが構成されます。

今回はここまでです。

Xamarin + Cocos Sharp で iOS, Android 対応のゲームを開発する手順 (14) 当たり判定② 敵に当たった場合のアクションの作成

Xamarin Cocos Sharp

今回は自機が敵キャラに当たった時のアクションを作成します。

具体的には、以下の処理を行ないます。
1. 自機に爆発するアニメーションを設定する。
2. 自機を画面上から削除する。

まず、自機が爆発するアニメーションを作成します。

アニメーションを作成する場合、それぞれのコマの画像を準備します。

CocosSharpGameSample.iOS プロジェクトに爆発する画像を追加します。

f:id:hiro128:20160920210550p:plain

/Resources/Images/Character/Explosion/Explosion00.png ~ Explosion04.png

f:id:hiro128:20160920210632p:plainf:id:hiro128:20160920210633p:plainf:id:hiro128:20160920210634p:plainf:id:hiro128:20160920210635p:plainf:id:hiro128:20160920210636p:plain

CocosSharpGameSample.Android プロジェクトに爆発する画像を追加します。

f:id:hiro128:20160920210559p:plain

/Assets/Images/Character/Explosion/Explosion00.png ~ Explosion04.png

CocosSharpGameSample.Core プロジェクトに処理を作成します。

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

まずは、アニメーションの状態を把握できるフィールドを追加します。

public class GameLayer : LayerBase
{

	private ExtendedCCSprite player;
	private bool enableCollisionDetection = true;
	private CCActionState playerShotDownActionState; // <-追加

次に爆発のアニメーションを作成します。

アニメーションは以下の要領で作成できます。
1. 各コマの画像の CCSprite を作成
2. CCSprite から CCSpriteFrame を作成
3. List としてアニメーションを構築
4. ループ回数、所要時間を設定し、アニメーションを適用させたい CCSprite に設定し実行
5. アニメーションの状態を返却

この手順をコードにすると以下の通りになります。

protected CCActionState PerformExplosionAnimation(CCSprite sprite)
{
	// 各コマの画像の CCSprite を作成
	var sprite00 = new CCSprite("/Resources/Images/Character/Explosion/Explosion00.png", null);
	var sprite01 = new CCSprite("/Resources/Images/Character/Explosion/Explosion01.png", null);
	var sprite02 = new CCSprite("/Resources/Images/Character/Explosion/Explosion02.png", null);
	var sprite03 = new CCSprite("/Resources/Images/Character/Explosion/Explosion03.png", null);
	var sprite04 = new CCSprite("/Resources/Images/Character/Explosion/Explosion04.png", null);

	// CCSprite から CCSpriteFrame を作成
	var frame00 = new CCSpriteFrame(sprite01.Texture, sprite01.TextureRectInPixels);
	var frame01 = new CCSpriteFrame(sprite02.Texture, sprite02.TextureRectInPixels);
	var frame02 = new CCSpriteFrame(sprite03.Texture, sprite03.TextureRectInPixels);
	var frame03 = new CCSpriteFrame(sprite04.Texture, sprite04.TextureRectInPixels);
	var frame04 = new CCSpriteFrame(sprite03.Texture, sprite03.TextureRectInPixels);
	var frame05 = new CCSpriteFrame(sprite02.Texture, sprite02.TextureRectInPixels);
	var frame06 = new CCSpriteFrame(sprite01.Texture, sprite01.TextureRectInPixels);
	var frame07 = new CCSpriteFrame(sprite00.Texture, sprite00.TextureRectInPixels);

	// List<CCSpriteFrame> としてアニメーションを構築
	var frames = new List<CCSpriteFrame>();
	frames.Add(frame00);
	frames.Add(frame01);
	frames.Add(frame02);
	frames.Add(frame03);
	frames.Add(frame03);
	frames.Add(frame04);
	frames.Add(frame05);
	frames.Add(frame06);
	frames.Add(frame07);

	// ループ回数、所要時間を設定し、アニメーションを適用させたい CCSprite に設定し実行
	var animation = new CCAnimation(frames, 0.15f);
	animation.Loops = 1;
	var animate = new CCAnimate(animation);
	var state = sprite.RunAction(animate);

	// アニメーションの状態を返却
	return state;
}

衝突を検出した場合、爆発のアニメーションを実行するように、当たり判定検出のメソッドに下記の爆発のアニメーションを実行処理を追加します。

追加する処理

this.playerShotDownActionState = this.PerformExplosionAnimation(this.player);

追加場所は下から6行目です。

private void DetectCollisions()
{
	// プレーヤーがやられてしまった後など、プレーヤーが画面上に居ない場合
	if (this.player == null)
	{
		return;
	}
	// ゲーム開始直後、リスタート直後、ステージクリア時など、当たり判定をキャンセルする場合
	if (this.EnableCollisionDetection == false)
	{
		return;
	}
	// 当たり判定領域をプレーヤー中心部分だけにする
	var rectPlayer = player.BoundingBox;
	var rectTrimedPlayer = new CCRect(
									rectPlayer.MidX - (GameSettings.PlayerWidth / 6),
									rectPlayer.MidY - (GameSettings.PlayerHeight / 3),
									GameSettings.PlayerWidth / 3,
									GameSettings.PlayerHeight / 3);
	//// 実際の当たり判定領域の確認用コード(コメントを外すと実際の当たり判定領域を可視化できます。)
	//var acutualRect = new CCSprite();
	//acutualRect.TextureRectInPixels = rectTrimedPlayer;
	//acutualRect.Color = new CCColor3B(0, 255, 0);
	//acutualRect.Position = rectTrimedPlayer.LowerLeft;
	//acutualRect.Tag = 100000000;
	//this.RemoveChildByTag(100000000);
	//this.AddChild(acutualRect);

	// 当たり判定実施
	var enemies = new List<CCNode>();
	enemies.AddRange(this.GetChildrenByTag((int)NodeTag.Enemy));
	if (enemies.Count > 0)
	{
		foreach (var node in enemies)
		{
			// 同一エリア以外は当たり判定スキップ
			if (this.player.ScreenArea != ((ExtendedCCSprite)node).ScreenArea)
			{
				continue;
			}
			var rectEnemy = node.BoundingBox;
			if (rectTrimedPlayer.IntersectsRect(rectEnemy) == true)
			{
				Debug.WriteLine("敵キャラに当たった!");
				this.EnableCollisionDetection = false;
				this.playerShotDownActionState = this.PerformExplosionAnimation(this.player); // 爆発のアニメーションを実行処理を追加 <-変更箇所
				break;
			}
		}
	}
}

爆発のアニメーションが終了した後、自機を画面上から削除します。

private void RemovePlayer()
{
	if (this.player == null)
	{
		return;
	}
	if (this.playerShotDownActionState == null)
	{
		return;
	}
	if (this.playerShotDownActionState.IsDone == true)
	{
		this.RemoveChild(this.player);
		Debug.WriteLine("プレーヤー削除!");
		this.player = null;
	}
}

自機削除を0.5秒に1回行なうようスケジューラに登録します。

private void StartScheduling()
{
	this.Schedule(t => this.UpdatePlayer(), 0.02f);
	this.Schedule(t => this.RemoveOffScreenNodes(), 1.0f);
	this.Schedule(t => this.DetectCollisions(), 0.1f);
	this.Schedule(t => this.RemovePlayer(), 0.5f);// <-自機削除をスケジューラに登録
}

既存のメソッドで自機を削除することによる、null参照エラーが発生する部分に対策を入れます。

private void UpdatePlayer()
{
	if (this.player == null)
	{
		return;
	}
private void DetectCollisions()
{
	// プレーヤーがやられてしまった後など、プレーヤーが画面上に居ない場合
	if (this.player == null)
	{
		return;
	}


以上をまとめると、CocosSharpGameSample.Core プロジェクトの Layers/GameLayer.cs は以下の通りになります。

using System.Collections.Generic;
using System.Diagnostics;
using CocosSharp;

namespace CocosSharpGameSample.Core
{
	public class GameLayer : LayerBase
	{

		private ExtendedCCSprite player;
		private bool enableCollisionDetection = true;
		private CCActionState playerShotDownActionState;

		public bool EnableCollisionDetection
		{
			get { return this.enableCollisionDetection; }
			set { this.enableCollisionDetection = value; }
		}

		public GameLayer()
			: base()
		{
			var listener = new CCEventListenerTouchOneByOne();
			listener.OnTouchBegan = this.CCEventListener_TouchBegan;
			this.AddEventListener(listener, this);
		}

		protected bool CCEventListener_TouchBegan(CCTouch touch, CCEvent touchEvent)
		{
			Debug.WriteLine(this.ChildrenCount);
			// タッチした場所に敵キャラ配置
			var touchedLocation = touch.Location;
			// 画面の上部25%なら敵キャラ配置
			if (touchedLocation.Y > GameSettings.ScreenHeight * 3 / 4)
			{
				var addingEnemy = new ExtendedCCSprite("/Resources/Images/Character/Enemy/Enemy001.png", null);
				addingEnemy.Position = touchedLocation;
				addingEnemy.Tag = (int)NodeTag.Enemy;
				this.ApplyGoStraightFromTopDownAction(addingEnemy);
				this.AddChild(addingEnemy);
				return true;
			}
			return false;
		}

		// Y軸のみの移動で画面外まで行くアクションを適用
		public void ApplyGoStraightFromTopDownAction(CCNode enemy)
		{
			var destinationPoint = new CCPoint(enemy.PositionX, -100);
			var action = new CCMoveTo(8.0f, destinationPoint);
			enemy.RunAction(action);
		}

		// 画面外のNodeを除去
		public void RemoveOffScreenNodes()
		{
			foreach (var node in this.Children)
			{
				if (node.Position.Y < 0)
				{
					this.RemoveChild(node);
				}
			}
		}

		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);

			// 自機を配置
			this.AddPlayer();
			this.StartScheduling();
		}

		private void StartScheduling()
		{
			this.Schedule(t => this.UpdatePlayer(), 0.02f);
			this.Schedule(t => this.RemoveOffScreenNodes(), 1.0f);
			this.Schedule(t => this.DetectCollisions(), 0.1f);
			this.Schedule(t => this.RemovePlayer(), 0.5f);
		}

		private void AddPlayer()
		{
			this.player = new ExtendedCCSprite("/Resources/Images/Character/Player/Player.png", null);
			this.player.Position = new CCPoint(this.ContentSize.Center.X, this.ContentSize.Center.Y);
			this.AddChild(this.player);
		}

		private void UpdatePlayer()
		{
			if (this.player == null)
			{
				return;
			}

			this.player.PositionX = this.player.PositionX;
			this.player.PositionY = this.player.PositionY;

			// 画面からはみ出ないように、位置の値の上限、下限を設定します。
			// 左
			if (this.player.PositionX < 0 + GameSettings.PlayerWidth)
			{
				this.player.PositionX = 0 + GameSettings.PlayerWidth;
			}
			// 右
			if (this.player.PositionX > GameSettings.ScreenWidth - GameSettings.PlayerWidth)
			{
				this.player.PositionX = GameSettings.ScreenWidth - GameSettings.PlayerWidth;
			}
			// 下
			if (this.player.PositionY < 0 + GameSettings.PlayerHeight)
			{
				this.player.PositionY = 0 + GameSettings.PlayerHeight;
			}
			// 上
			if (this.player.PositionY > GameSettings.ScreenHeight - GameSettings.PlayerHeight)
			{
				this.player.PositionY = GameSettings.ScreenHeight - GameSettings.PlayerHeight;
			}
		}

		private void DetectCollisions()
		{
			// プレーヤーがやられてしまった後など、プレーヤーが画面上に居ない場合
			if (this.player == null)
			{
				return;
			}
			// ゲーム開始直後、リスタート直後、ステージクリア時など、当たり判定をキャンセルする場合
			if (this.EnableCollisionDetection == false)
			{
				return;
			}
			// 当たり判定領域をプレーヤー中心部分だけにする
			var rectPlayer = player.BoundingBox;
			var rectTrimedPlayer = new CCRect(
											rectPlayer.MidX - (GameSettings.PlayerWidth / 6),
											rectPlayer.MidY - (GameSettings.PlayerHeight / 3),
											GameSettings.PlayerWidth / 3,
											GameSettings.PlayerHeight / 3);
			//// 実際の当たり判定領域の確認用コード(コメントを外すと実際の当たり判定領域を可視化できます。)
			//var acutualRect = new CCSprite();
			//acutualRect.TextureRectInPixels = rectTrimedPlayer;
			//acutualRect.Color = new CCColor3B(0, 255, 0);
			//acutualRect.Position = rectTrimedPlayer.LowerLeft;
			//acutualRect.Tag = 100000000;
			//this.RemoveChildByTag(100000000);
			//this.AddChild(acutualRect);

			// 当たり判定実施
			var enemies = new List<CCNode>();
			enemies.AddRange(this.GetChildrenByTag((int)NodeTag.Enemy));
			if (enemies.Count > 0)
			{
				foreach (var node in enemies)
				{
					// 同一エリア以外は当たり判定スキップ
					if (this.player.ScreenArea != ((ExtendedCCSprite)node).ScreenArea)
					{
						continue;
					}
					var rectEnemy = node.BoundingBox;
					if (rectTrimedPlayer.IntersectsRect(rectEnemy) == true)
					{
						Debug.WriteLine("敵キャラに当たった!");
						this.EnableCollisionDetection = false;
						this.playerShotDownActionState = this.PerformExplosionAnimation(this.player);
						break;
					}
				}
			}
		}

		private void RemovePlayer()
		{
			if (this.player == null)
			{
				return;
			}
			if (this.playerShotDownActionState == null)
			{
				return;
			}
			if (this.playerShotDownActionState.IsDone == true)
			{
				this.RemoveChild(this.player);
				Debug.WriteLine("プレーヤー削除!");
				this.player = null;
			}
		}

		protected CCActionState PerformExplosionAnimation(CCSprite sprite)
		{
			// 各コマの画像の CCSprite を作成
			var sprite00 = new CCSprite("/Resources/Images/Character/Explosion/Explosion00.png", null);
			var sprite01 = new CCSprite("/Resources/Images/Character/Explosion/Explosion01.png", null);
			var sprite02 = new CCSprite("/Resources/Images/Character/Explosion/Explosion02.png", null);
			var sprite03 = new CCSprite("/Resources/Images/Character/Explosion/Explosion03.png", null);
			var sprite04 = new CCSprite("/Resources/Images/Character/Explosion/Explosion04.png", null);

			// CCSprite から CCSpriteFrame を作成
			var frame00 = new CCSpriteFrame(sprite01.Texture, sprite01.TextureRectInPixels);
			var frame01 = new CCSpriteFrame(sprite02.Texture, sprite02.TextureRectInPixels);
			var frame02 = new CCSpriteFrame(sprite03.Texture, sprite03.TextureRectInPixels);
			var frame03 = new CCSpriteFrame(sprite04.Texture, sprite04.TextureRectInPixels);
			var frame04 = new CCSpriteFrame(sprite03.Texture, sprite03.TextureRectInPixels);
			var frame05 = new CCSpriteFrame(sprite02.Texture, sprite02.TextureRectInPixels);
			var frame06 = new CCSpriteFrame(sprite01.Texture, sprite01.TextureRectInPixels);
			var frame07 = new CCSpriteFrame(sprite00.Texture, sprite00.TextureRectInPixels);

			// List<CCSpriteFrame> としてアニメーションを構築
			var frames = new List<CCSpriteFrame>();
			frames.Add(frame00);
			frames.Add(frame01);
			frames.Add(frame02);
			frames.Add(frame03);
			frames.Add(frame03);
			frames.Add(frame04);
			frames.Add(frame05);
			frames.Add(frame06);
			frames.Add(frame07);

			// ループ回数、所要時間を設定し、アニメーションを適用させたい CCSprite に設定し実行
			var animation = new CCAnimation(frames, 0.15f);
			animation.Loops = 1;
			var animate = new CCAnimate(animation);
			var state = sprite.RunAction(animate);

			// アニメーションの状態を返却
			return state;
		}

	}
}

以上で、自機が敵キャラにぶつかった時、爆発する動きが完成しました。

f:id:hiro128:20160920210710g:plain

今回は、ここまでです。

何かご質問などございましたらコメント頂ければご回答させていただきますのでお気軽にどうぞ!

また、間違い等ございましたら、ご指摘頂ければ幸いです。