個人的なメモ

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

Cocos Sharp ハンズオン(1)地図の表示

はじめに

こんにちは、@hiro128_777です。


今回は JXUGC #23 でお見せした関東地方の地図がぐりぐり動くサンプルアプリを作成できるようにハンズオン用のテキストを作成しました。
オフラインでハンズオンも実施しますが、参加できない方でも、こちらを参考にすればサンプルアプリを作成できますので、ご興味がある方はぜひ試してみてください。

f:id:hiro128:20170623155841p:plain

完成版のソースコードはこちら

github.com

今回のゴール

Xamarin.Forms 上で Cocos Sharp のアニメーションが動かせるようになる!

ハンズオンを始める前に

Cocos Sharp について

「Cocos Sharp って何??」という方はこちらをご一読ください。

hiro128.hatenablog.jp

Cocos Sharp の基本的な使い方

まずは Cocos Sharp の基本的な使い方をご説明します。

Cocos Sharp の画面の階層構造

こちらをご覧ください。

hiro128.hatenablog.jp

Cocos Sharp のオブジェクトの制御 スケジューラ

こちらをご覧ください。

hiro128.hatenablog.jp

Cocos Sharp のオブジェクトの制御 アクション

こちらをご覧ください。

hiro128.hatenablog.jp

Cocos Sharp のオブジェクトの制御 連続したアクション

こちらをご覧ください。

hiro128.hatenablog.jp

ハンズオン

それでは、早速アプリを作っていきましょう!

Prism Template Pack のインストール、Prism for Xamarin.Forms 適用済みのソリューションの作成

まずはソリューションを作成します。
手順はこちらをご覧ください。

hiro128.hatenablog.jp


Visual studio for Mac では Prism Template Pack がインストールできないので
以下の作成済みのソリューションをご利用下さい。

github.com

Cocos Sharp のインストール

次にCocos Sharpをインストールします。
手順はこちらをご覧ください。

hiro128.hatenablog.jp

ViewModelの作成 : MainPageViewModel.cs

ViewModels/MainPageViewModel.cs を下記のように書き換えてください。

Cocos Sharp のキャンバスGameSceneのプロパティを作成し、各県ごとのコマンドにダミーのデリゲートを設定しておきます。

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;

namespace CocosHandsOn01.ViewModels
{
	public class MainPageViewModel : BindableBase
	{

		public GameScene GameScene { get; set; }

		public DelegateCommand TokyoCommand => new DelegateCommand(() => { });
		public DelegateCommand KanagawaCommand => new DelegateCommand(() => { });
		public DelegateCommand SaitamaCommand => new DelegateCommand(() => { });
		public DelegateCommand ChibaCommand => new DelegateCommand(() => { });
		public DelegateCommand GunmaCommand => new DelegateCommand(() => { });
		public DelegateCommand TochigiCommand => new DelegateCommand(() => { });
		public DelegateCommand IbarakiCommand => new DelegateCommand(() => { });

		private INavigationService navigationService;

		public MainPageViewModel(INavigationService navigationService)
		{
			this.navigationService = navigationService;
		}

	}
}

画面に地図を表示する

XAMLの作成 : MainPage.xaml

Views/MainPage.xaml を下記のように書き換えてください。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
			 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
			 xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
			 prism:ViewModelLocator.AutowireViewModel="True"
			 Title="関東地方"
			 x:Class="CocosHandsOn01.Views.MainPage">
	<Grid x:Name="MainGrid" RowSpacing="0">
		<Grid.RowDefinitions>
			<RowDefinition Height="*"/>
			<RowDefinition Height="*"/>
		</Grid.RowDefinitions>
		<StackLayout Grid.Column="0" Grid.Row="1" Padding="10,7,10,0" Margin="0,0,0,0" VerticalOptions="FillAndExpand" Orientation="Vertical">
			<StackLayout.Spacing>
				<OnPlatform x:TypeArguments="x:Double">
					<OnPlatform.iOS>7</OnPlatform.iOS>
					<OnPlatform.Android>0</OnPlatform.Android>
				</OnPlatform>
			</StackLayout.Spacing>
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="群馬県" FontSize="Micro" Command="{Binding GunmaCommand}" />
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="栃木県" FontSize="Micro" Command="{Binding TochigiCommand}" />
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="茨城県" FontSize="Micro" Command="{Binding IbarakiCommand}" />
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="千葉県" FontSize="Micro" Command="{Binding ChibaCommand}" />
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="埼玉県" FontSize="Micro" Command="{Binding SaitamaCommand}" />
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="神奈川県" FontSize="Micro" Command="{Binding KanagawaCommand}" />
			<Button HeightRequest="35" BackgroundColor="Gray" TextColor="White" Text="東京都" FontSize="Micro" Command="{Binding TokyoCommand}" />
		</StackLayout>
	</Grid>
</ContentPage>

XAMLは通常の Xamarin.Forms の場合と何も変わりません。上下2段のGridが配置してあり、下のセルにボタンが配置されています。
CocosSharp のコントロールは XAML 上には配置されていません。CocosSharp のコントロールは XAML 上で追加すると落ちるため、後ほどコードビハインドで追加します。

コードビハインド : MainPage.xaml


usingを以下のように書き換えます。

using System;
using System.Collections.Generic;
using Xamarin.Forms;
using CocosSharp;
using CocosHandsOn01.ViewModels;


コンストラクタを以下のように書き換えます。
InitializeComponentの後にCocosSharpViewのコントロールを作成し配置します。。

public MainPage()
{
	InitializeComponent();

	var grid = this.FindByName<Grid>("MainGrid");

	var gameView = new CocosSharpView()
	{
		HorizontalOptions = LayoutOptions.FillAndExpand,
		VerticalOptions = LayoutOptions.FillAndExpand,
		ViewCreated = CocosSharpView_ViewCreated
	};
	grid.Children.Add(gameView, 0, 0);
}


下記のイベントハンドラを追加します。
CocosSharpViewの生成時にゲーム画面CCGameViewの初期設定を行います。

private void CocosSharpView_ViewCreated(object sender, EventArgs e)
{
	var gameView = sender as CCGameView;

	if (gameView != null)
	{
		gameView.DesignResolution = new CCSizeI(350, 300);
		gameView.ResolutionPolicy = CCViewResolutionPolicy.ShowAll;
		gameView.ContentManager.SearchPaths = new List<string> { "Images" };
		var gameScene = new GameScene(gameView);
		gameView.RunWithScene(gameScene);
		var viewModel = BindingContext as MainPageViewModel;
		viewModel.GameScene = gameScene;
	}
}

参考

ゲーム画面の初期化の内容を具体的に見ていきましょう。

gameView.DesignResolution = new CCSizeI(350, 300);

ゲーム内の仮想解像度を横350ユニット、縦300ユニットに設定します。

gameView.ResolutionPolicy = CCViewResolutionPolicy.ExactFit;

アスペクト比を保ったまま最大の大きさで表示します。

gameView.ContentManager.SearchPaths = new List<string> { "Images" };

アセットを探しに行くパスを設定します。

var viewModel = BindingContext as MainPageViewModel;
viewModel.GameScene = gameScene;

また、ViewModel側からゲーム操作できるように参照を渡しています。この部分本当は疎結合にしたいのですが、現状では厳しそうな感じです。

アセットの配置

Imageファイルをプラットフォームごとのプロジェクトに配置します。

Imageファイルは以下をご利用ください。

tokyo.png
f:id:hiro128:20170627155409p:plain



kanagawa.png
f:id:hiro128:20170627155654p:plain



saitama.png
f:id:hiro128:20170627155701p:plain



chiba.png
f:id:hiro128:20170627155711p:plain



gunma.png
f:id:hiro128:20170627155719p:plain



tochigi.png
f:id:hiro128:20170627155732p:plain



ibaraki.png
f:id:hiro128:20170627155747p:plain


iOSの場合

Content/Images の中にpngファイルを配置してください。

f:id:hiro128:20170627181950p:plain

pngファイルのビルドアクションはBundleResourceに設定してください。

f:id:hiro128:20170627182303p:plain

Androidの場合

Assets/Content/Images の中にpngファイルを配置してください。

f:id:hiro128:20170627184434p:plain

pngファイルのビルドアクションはAndroidAssetに設定してください。

f:id:hiro128:20170627184443p:plain

レイヤーの作成 : GameLayer

CocosSharp では、キャンパスであるCCSceneの中に、グラフィックレイヤーであるCCLayerを配置し、その中に動くオブジェクトCCSpriteを配置します。

最初にCCLayer上にそれぞれの県のCCSpriteを配置します。

using System;
using System.Diagnostics;
using CocosSharp;

namespace CocosHandsOn01
{
	public class GameLayer : CCLayerColor
	{
		private CCTexture2D tokyoTexture;
		private CCTexture2D kanagawaTexture;
		private CCTexture2D saitamaTexture;
		private CCTexture2D chibaTexture;
		private CCTexture2D gunmaTexture;
		private CCTexture2D tochigiTexture;
		private CCTexture2D ibarakiTexture;

		private CCSprite tokyoSprite;
		private CCSprite kanagawaSprite;
		private CCSprite saitamaSprite;
		private CCSprite chibaSprite;
		private CCSprite gunmaSprite;
		private CCSprite tochigiSprite;
		private CCSprite ibarakiSprite;

		public GameLayer(CCColor4B? color = default(CCColor4B?)) : base(color)
		{
			tokyoTexture = new CCSprite("tokyo.png", null).Texture;
			kanagawaTexture = new CCSprite("kanagawa.png", null).Texture;
			saitamaTexture = new CCSprite("saitama.png", null).Texture;
			chibaTexture = new CCSprite("chiba.png", null).Texture;
			gunmaTexture = new CCSprite("gunma.png", null).Texture;
			tochigiTexture = new CCSprite("tochigi.png", null).Texture;
			ibarakiTexture = new CCSprite("ibaraki.png", null).Texture;

			var offsetX = 50f;

			tokyoSprite = new CCSprite(tokyoTexture)
			{
				PositionX = 96.25f + offsetX,
				PositionY = 126.25f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Tokyo,
			};
			AddChild(tokyoSprite);

			kanagawaSprite = new CCSprite(kanagawaTexture)
			{
				PositionX = 90f + offsetX,
				PositionY = 93f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Kanagawa,
			};
			AddChild(kanagawaSprite);

			saitamaSprite = new CCSprite(saitamaTexture)
			{
				PositionX = 84.5f + offsetX,
				PositionY = 163f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Saitama,
			};
			AddChild(saitamaSprite);

			chibaSprite = new CCSprite(chibaTexture)
			{
				PositionX = 177.5f + offsetX,
				PositionY = 104f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Chiba,
			};
			AddChild(chibaSprite);

			gunmaSprite = new CCSprite(gunmaTexture)
			{
				PositionX = 60f + offsetX,
				PositionY = 220f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Gunma,
			};
			AddChild(gunmaSprite);

			tochigiSprite = new CCSprite(tochigiTexture)
			{
				PositionX = 131f + offsetX,
				PositionY = 238f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Tochigi,
			};
			AddChild(tochigiSprite);

			ibarakiSprite = new CCSprite(ibarakiTexture)
			{
				PositionX = 174.5f + offsetX,
				PositionY = 200f,
				Scale = 0.3f,
				Tag = (int)Prefecture.Ibaraki,
			};
			AddChild(ibarakiSprite);

		}

	}

	public enum Prefecture
	{
		Tokyo,
		Kanagawa,
		Saitama,
		Chiba,
		Gunma,
		Tochigi,
		Ibaraki
	}

}

シーンの作成

CCScene上に今作成したCCLayerを配置します。

using System;
using CocosSharp;

namespace CocosHandsOn01
{
	public class GameScene : CCScene
	{
		GameLayer layer;

		public GameScene(CCGameView gameView) : base(gameView)
		{
			layer = new GameLayer(CCColor4B.Black);
			AddLayer(layer);
		}
	}
}

確認

ここまでをデバッグ実行すると画面上に地図が表示されるはずです。確認してみましょう。

f:id:hiro128:20170628205102p:plain

f:id:hiro128:20170628204258p:plain