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

Unity / VRゲーム開発日記@長崎

Unityを使ったVRのゲーム開発をやってます。

UMLについて

オブジェクトモデリングの世界標準ツールとして
UMLというものがあります。


情報系の資格などでも、問題として出てくるので
少しは知っていたのですが、
具体的な使い方がいまいちつかめないという感じでした。


ただ、「体験オブジェクト指向 はじめるUMLモデリング」を読んでみたところ、
UML自体は、劇的に開発効率を上げるツールというわけではなく、
漠然とした抽象的な仕様を具体的な仕様に起こしていく為の
補助ツールって位置づけなのかなぁという印象を受けました。


UMLは書き方に規格がありますが、何をどこまで書くかというのは
利用者に委ねられます。


書きすぎたら、時間が膨大に必要になり、資料も多すぎて読みにくくなります。
書かなすぎたら、結局どんなシステムなのか把握ができなく、設計の意味を成さなくなります。

なので、何をどこまでどの図を用いて書くのか
という、UML自体を使いこなすスキルが必要になりそうです。


ただし、漠然とした仕様を図で整理したり、
プロジェクト内でシステムの構成を共有できるという大きなメリットもあります。


デザインパターンもクラス図で書かれてたりするので、
そういった資料も読めるようになりますね。



使いこなすまでには多くの実践が必要そうなので、
実際のプロジェクトに取り入れたりしながら
勉強していこうと思います。


体験オブジェクト指向 はじめるUMLモデリング

体験オブジェクト指向 はじめるUMLモデリング

サウンドを途中から再生する

音楽を途中から再生する方法について、
ネットで検索しても、情報を見つけれなかったので
念のため記事にしておきます。

途中再生自体が余り需要がないのかな??

詳細

手続きは簡単です。
AudioSourceにtimeという再生時間についてのプロパティが有ります。
これは、現在の再生時間を知ることができますが、
値を設定することも出来るので、再生したい時間を設定します。

	audioSource.clip = audioClip;
	audioSource.time = 5f;
	audioSource.Play();

1.audioSorce.clip に AudioClipをセット
2.audioSorce.time に 再生時間(秒)をセット
3.audioSource.Play() で再生

以上で、設定したAudioClipをtimeで指定した時間から再生できます。


超簡単ですね。


time値は参照すれば、現在の再生時間がとれるので、
たとえばRPGなんかで
マップの移動中に戦闘が発生して、
戦闘終了後にさっき再生してたとこからまた再生したいって時は、

戦闘前にtime値を保管しておいて、
戦闘終了後に、保管していたtime値を再設定してやれば
続きから再生できます。

時職人の作り方 (2)- オブジェクトの並べ方

時職人作り方2回目です。


今回はストップウォッチ部を解説予定でしたが、
メニュー部にまだネタがありそうだったので、
もう少しそちらを説明していきます。

構成

今回は、アイコンの並べ方のロジックについてです。

以前も軽く触れましたが、アイコンメニューは実行時に
自動生成し、戻るアイコンはEditor上で予め配置しています。


よって、実行前と実行後は以下の様な感じになります。

  • 実行前

f:id:icoc_dev:20141009092644j:plain

  • 実行後

f:id:icoc_dev:20141009092700j:plain


各ステージは、エリアという単位でくくられていて、
全7エリアの、12ステージでゲームが構成されています。

アイコンの数分ループ

アイコンの作り方としては、
エリア と ステージ のループで回して、
その中でアイコンのオブジェクトを生成していく形です。


ただし、画面表示の都合上、4ステージ毎に
アイコンの折り返しをしています。


そのため、ステージをさらに行と列のループに分けています。

const int ICON_COL_NUM = 4;
const int ICON_ROW_NUM = 3;


void IconLayout ()
{
	for (int areaNo =0; areaNo<_areaNum; areaNo++) {

		for (int y=0; y < ICON_ROW_NUM; y++) {
			for (int x=0; x < ICON_COL_NUM; x++) {
				CreateIcon (areaNo, x, y);
			}
		}
	}
}

areaNoのループがエリアのループ。
ステージが増えても良いように、ループ回数はメンバ変数としています。

その中で、y座標と x座標に関してループしています。
列と行は、変えるつもりはなかったのでconstです。

今回は_areaNumは7なので、
エリア(7) * 行(3) * 列(4) = 84
で全84ステージ分のアイコンをCreateIconで実体化させています。


個別アイコンの位置決め

先ほどのループで、どのアイコンを作るかというところまで
確定しているので、あとはそこから詳細な位置を導き出します。

略以降はアイコンの色変化とかをさせてますが、
アイコンパターンが幾つかあり煩雑なので、
次回の説明に回します。

以下、位置情報確定のコードです。

void CreateIcon (int areaNo, int x, int y)
{
	int posX = 
				x * 148 
				+ (int)(Icon.transform.localScale.x / 2) 
				+ OFFSET_X 
				+ (areaNo * Constants.DISP_W) 
				+ ICON_OFFSET_X;

	int posY = 
				(-y * 148) 
				- (int)(Icon.transform.localScale.y / 2) 
				+ ICON_OFFSET_Y;

	var obj = (GameObject)
				Instantiate (
					Icon, 
					new Vector3 (posX, posY, 1), 
					Quaternion.identity
				);

	〜〜 以下略 〜〜

}

久々に紐解いてみましたが、
やや複雑な計算ですね…


自分で作ったのに、どこで何しているのかよくわからないです。
148ってなんだよ…


リファクタリングがきちんとできていない証拠ですね(--;

x座標

まずはposXの座標をどう求めてるか見ていきます。

x * 148 

148はアイコン部分の基本幅になります。
なので、単純に並べるだけなら、この部分だけでOKで、
148の値を適当に調整すればなんとなくの位置に並べることも可能です。

(int)(Icon.transform.localScale.x / 2) 

Iconは、Iconのオブジェクトになります。
アイコンとアイコンの間隔をアイコンの大きさの半分に
したかったみたいです。

OFFSET_X

5000で設定されていました。
前回説明してましたが、アイコン部分と情報説明部分を
別のカメラで描画しており、それぞれが間違って被らないよう
Xを遠くに設定していたようです。

Yで距離をつけたほうが、Editor上で近くなるし、
今回はX位置を決めるのが煩雑だったので
ちょっと設計ミスだったかもです。

(areaNo * Constants.DISP_W)

エリアごとの幅調整をしています。
1エリアと、2エリア目は画面スクロールで別画面になるため、
アリア内でのアイコン幅より若干広めに取っておく必要があります。

ICON_OFFSET_X

今表示されてる画面の左からどれくらいオフセットを取るか
という数値です。


以上を足すことにより、X座標が求まりました。
一応、全部目的があって複雑になってたようです。

y座標

y座標は大分シンプルです。
また、x座標で既にでてきた考えと同様なので、簡潔に行きます。

(-y * 148) 

基本位置。
ただし、Unityのワールド座標のy値は
標準のカメラの位置では、画面下がマイナスになってるので、
マイナスを掛ける必要があります。

いつもこの辺がややこしいなぁと感じているんですが、
よく考えたら、カメラを上下反転させれば、
感覚的なy値と実際のy座標をあわせれるのでは!?
と今思いつきました。
今度やってみます。


(int)(Icon.transform.localScale.y / 2) 

アイコンの高さ間隔

ICON_OFFSET_Y

画面上部からのオフセット値

オブジェクト生成

	var obj = (GameObject)
				Instantiate (
					Icon, 
					new Vector3 (posX, posY, 1), 
					Quaternion.identity
				);

予め計算したx,y座標位置に、オブジェクトを生成。
今回は2D画面を想定してるので、z値は固定です。


座標を決めるのも意外と大変でした。


次回は、アイコン部分の生成です。

過去のシリーズ


SpriteRenderer、とりあえずの隣接ドット対策

2DのSpriteRendererで、Multiplueを使用して画像を区切った際、
例えばマップチップ的な使い方をしてると、縮尺によっては、線が入ってしまう時がある。

■線が入った状態
f:id:icoc_dev:20140929103724p:plain


■本来はこう表示したい
f:id:icoc_dev:20140929103754p:plain


ピクセル数が完全に決まってる画面なら、
ピクセルがずれないようにカメラの設定をしておけば問題ないが、
スマホ向けだと画面サイズと比率がまちまちなので、どうしても縮尺とピクセルが
うまくあわない可能性が出てくる。


今回は、この現象に対する1つの対処方法について。



ちなみに、この現象の線の部分というのは、
どうも隣のドットが表示されている様子。

f:id:icoc_dev:20140929104818j:plain


2Dとはいえ、仕組みとしては、オブジェクトに
テクスチャを貼り付けてる仕組みだと思うので
多分このズレは防ぎようがなさそう。

そこで、隣のドットを表示されることを諦めて、
1ドット余分に画像を書いておくという対処が考えられる。


ただし、本来表示したいものと別に1ドット余分になるので
元の画像のサイズが8で割れない数字になったり、
単純に書き込み作業が面倒だったりと、結構大変そう。



なので、何か他に良い方法がありましたらコメントいただけると
助かります!!



って事で、1ドット余分な画像を作る作業はちょっと
片手間ではできないので、現在の画像をひと回り小さく利用する形で
やってみました。


どういうことかというと、今マップチップが
16ドットで構成されているんですが、
それを14ドットとみなすことで、
上下左右1ドットを余分な部分とするということです。


早速やってみました。


まずは、Pixel To Units を現在16で設定しているので、
これを14にします。


f:id:icoc_dev:20140929105702p:plain


そして、Sprite Editorを開きます。
現在は、こんなかんじです。

f:id:icoc_dev:20140929105743p:plain

ちなみに、透明部分はGridでスライスした時に、
Sprite対象外になってしまうので、一時的に背景にダミーの塗りつぶしを設定しています。


スライス後に背景を非表示にして保存しなおせば、透明部分も
Sprite対象にできるので、画像の並び順に意味を持たせたいときには便利です。


・・・


次に、Slice設定を変更します。


f:id:icoc_dev:20140929110032p:plain


PxcelSizeを14にしました。
画像には、上下左右位置ドットの余白をつけてるつもりなので、
Offsetでxy、1ドットずつ
Paddingは、自分の1ドットと、隣の1ドットの距離があるので、
2,2で設定します。


そうすると、こんなかんじになりました。

f:id:icoc_dev:20140929110248p:plain


これで、Gameビューの縮尺を色々変えてみても、
線が入る事はなくなりました。

f:id:icoc_dev:20140929110403p:plain

時職人の作り方 (1)

前回の記事で、「時職人」を軽く紹介しましたが、
このアプリがどういった仕組みで構築されているかを
少しづつ紹介していこうと思います。


今回は、ステージ選択画面について。


ステージ選択画面は以下の様な構成になっています


・上部:ステージを選択するためのアイコン、
・中部:エリアの番号表示とエリア切り替えのためのボタン
・下部:ステージ情報表示

f:id:icoc_dev:20140925154632j:plain



また、以下2つのカメラでそれぞれの表示部分を
分けて表示しています。

サブカメラ:アイコン選択部分用カメラ(上部)
f:id:icoc_dev:20140925154752p:plain

メインカメラ:2ステージ情報&エリア切り替えUI用カメラ(中部、下部)
f:id:icoc_dev:20140925154746p:plain


メインカメラの方を手前に表示して、その後ろに
サブカメラの画面を投影している形です。

この使い分けによって、サブカメラだけ移動させて、
部分的にスクロールするという表現をしています。


また、ステージ情報は、全ステージ分一気に描画しておき、
そこをカメラが左右に移動する仕組みです。


f:id:icoc_dev:20140925155050p:plain


これにより、後はカメラ移動させるだけで、スクロールが表現できます。


場合によっては、メモリの節約のために、
移動時に、移動先を描画し、移動前のところを消して…
などの処理が必要になると思いますが、
今回はそんなにオブジェクトの数もないため、
この仕組にしています。


f:id:icoc_dev:20140925155442p:plain


また、メニューの一番左は、タイトルに戻る為のホームボタンになってるのですが、
あらかじめカメラの移動先の左端にエディタ上でボタンを設置しています。

今回のつくりの場合、今どこをスクロールしてるかを判断し、
描画するオブジェクトを変える処理がいらないので、ボタンの変更があっても
エディタ上の処理で済むので楽です。




ちなみに、カメラの移動にはiTweenを利用して、
スクロールを滑らかにしています。


複数のカメラを使うと、移動させたくないUIと
移動する画面を切り分けれるので便利です。



次回は、ストップウォッチを止める部分の処理を紹介予定です。

時職人の紹介記事はこちら

[001] 時職人 〜Time Artisan〜 | icocApps

最初のアプリリリース

icocAppsとして、Unityを使って開発をはじめて、
ようやく一年近くになります。


最初のアプリリリースは、時職人という、
指定時間でストップウォッチを止めるというゲームで、
AppStoreに並んだのが、去年の11/2でした。



去年の今頃から開発をはじめて、リリースまで一ヶ月半くらいかかっています。


最初なので、ライブラリの構築や、
広告を入れる仕組みがわからなかったりで、
単純なゲームな割には時間がかかってましたね。


というより、最初なので企画の流れとかもわからないで
この単純なゲームのアイデアを出すのにも一週間くらいかかってた気がしますw


当時はいiPhone5Sが発売され、さらにiOS7がリリースされて
フラットデザインというのが流行っていたので、ゲームもそれに合わせたスタイルにしていました。


最近はフラットデザインって言葉をネットで見る機会があんまりない気がするんですが、
単なる一時期のはやりだったんですかね?(^^;


■icocAppsの時職人紹介ページ
[001] 時職人 〜Time Artisan〜 | icocApps

CEDEC 2014に参加してきました

9/2〜9/4にかけて、
パシフィコ横浜で開催された
CEDEC2014に行ってきました。

f:id:icoc_dev:20140903091554j:plain

今回は、以下の講演に参加してきました。

9/2

一日目は、長崎からの移動日でもあったので、午前中の講演を見ることができませんでした。
ネットの記事を見てると、冲方丁さんの基調講演が素晴らしかったみたいで、
拝見できなかったのがすごい残念です。


今回は市場や企画についての情報を中心に講演を選択していきました。
AppAnnieの桑水さんが海外のアプリ市場の統計データを色々紹介してくださったのですが、
以下の様なことが印象的でした。
・海外でも課金市場が伸びてきている
・メキシコとブラジルで無料アプリDLのすごく増えている
・売上の割合はアプリ内課金が9割。 有料アプリは1割にも満たない。
・日本はかなりiPadでの売上が少なく、iPad市場自体がさほど成長していない。

9/3


二日目も海外市場の講演。
新清士さんによる、フィンランドゲーム市場のお話。
フィンランドには「クラッシュ・オブ・クラン」、「アングリーバード」等、アプリ市場で大施工している企業が存在
・アングリーバードの会社は、新しいヒットアプリを生み出せていない
・アングリーバードは冬季オリンピックの開催が重なり偶然ヒットが広がった可能性が見られる ※
・売れてるアプリは50%は運が作用している感じ

アングリーバードはすでに売上が頭打ち状態みたいで、アプリ売り切りスタイルでは
やはり伸びという部分で厳しい面があるようです。


※ 正確には冬季オリンピックである選手がインタビューでアプリ名を発言したのが発端みたい
http://www.macotakara.jp/blog/report/entry-15084.html





9/4


最終日。
この日も移動日で、最後の方の講演は見れませんでした。

基調講演は、龍が如くの名越さん!!
ゲーム雑誌やネットでもよく見かけていて、
特に龍が如くの開発に関わってから、見た目がガラッと変わったというのが印象的で
非常に興味がある方だったので、楽しみにしていた講演でした。


講演内容は、どうもTOKYO GAME SHOWで忙しい時期らしく、
資料などは用意されていなかったのですが、
コンシューマーの第一線で活躍されている中での経験談を色々話されていて
非常に面白かったです。


ネタ的な話になりますが、新人にある企業向けの企画書を書かせたら、
「ある企業名」「企画書」っていうのをネットで検索しだして、
すごい衝撃をうけられたそうです。


あまりにも衝撃過ぎて、怒っていいかもわからず、
それでいい企画書が上がってくるんならそれはそれでいいんだけど…
みたいな感じでどう対応していいか困ることも多いんだとか



三日間、色んなクリエイターのお話や調査結果などを見聞きできて
すごく刺激になりました!!

最後に、CEDEC公式サイトと、4gamerさんによる
CEDECまとめのリンクを張っておきます。

関連リンク

第22回 Unity勉強会に登壇してきました

先週の金曜日にTechBuzzSpaceにて開催された、
第22回 Unity勉強会 へ登壇者として参加させていただきました。


f:id:icoc_dev:20140627194550j:plain

【満員御礼170名‼︎】【#TechBuzz】第22回Unity勉強会 Unityを使って1日でアプリを完成させる為には / 「宴」実装時に得られたUnityプログラムノウハウ+4.5追記 / ほか : ATND


発表内容は、以前このブログでも紹介していた5日間連続アプリリースを元にした、
「Unityを使って1日でアプリを完成させるためには」という内容です。

5日間連続リリース、まとめ。 - Unityゲーム開発日記@長崎


発表内容をslide shareの方にアップしましたので、興味がある方は
御覧ください。

【#TechBuzz】第22回Unity勉強会 「Unityを使って1日でアプリを完成させる為には」

GameObjectの表示と当たり判定の設定を行う(親子階層構造対応)

GameObjectには、Active状態を切り替える
SetActive
というメソッドがあります。


こちらでActive状態をfalseにした場合、対象オブジェクトのコンポーネントが全て無効となります。
この場合、例えばそのオブジェクトがAudioSouceなどを持ったオブジェクトだと、
再生中のSEも途切れてしまうため、場合によっては表示と当たり判定のみを無効にしたい事が
あると思います。


そこで、SetActiveSoftというメソッドを考えてみました。

public static void SetActiveSoft(this GameObject me, bool val){

	var tr = me.transform;

	for(int i=0; i<tr.childCount; ++i){

		SetActiveSoft(tr.GetChild(i).gameObject, val);

	}

	if(tr.renderer != null){
		tr.renderer.enabled = val;
	}

	if(tr.collider2D != null){
		tr.collider2D.enabled = val;
	}

	if(tr.collider != null){
		tr.collider.enabled = val;
	}


}


まず、SetActiveと同様に、GameObjectのメソッドとして利用できるように
Extensionで拡張しています。


Extensionについては、過去記事を参照下さい。
クラスのメソッドを拡張するExtensionの使い方 - Unityゲーム開発日記@長崎


対象GameObjectに対し、bool値を渡し
trueなら、全オブジェクトのコリジョンとレンダラーをtureに設定しています。


また、transformに紐づく子を再帰で全走査しています。

コリジョンに関しては、collider2Dやcolliderのコンポーネントがあるかを確認し
存在すれば、enabled値を設定します。



設定してるのは表示とコリジョンだけで、
他のコンポーネントにもアクセスでき、色々と応用は利くかと思います。

Tiled map editorのxmlデータをクラスに流し込む

いま製作中のゲームで、マップの製作が必要なため、
フリーのマップ作成ツールで比較的高機能な
Tield map editorというのを使ってみました。


Tiled map editor
http://www.mapeditor.org/


今回は、このツールで出力したxmlデータを
Unityに取り込む為の手続きについてです。


最終的に画像と対応させるまでとなると、
画像とマップチップ番号を対応させる仕組み等も必要なので、
とりあえずは、Tiled map editor のxml形式を
プログラムで読めるようにするというところまでの段階の説明になります。



Tiled map editorで作ったデータをtmx形式で保存すると、
中身はxmlデータとなります。
ただし、unityでは、拡張子をみてTextAssetかを判断してるので、
「tmx」を「xml」に変更しておく必要があります。


また、ListとXml関連のクラスを利用するので
以下をusingしておいて下さい。

using System.Collections.Generic;
using System.Xml.Serialization;


そして、まずxmlを指定した型に流し込むクラス

	public class XmlReader
	{

		public static T LoadFromXml<T> (string path)
			where T : class
		{


			var xml = Resources.Load( path ) as TextAsset;

			if(xml == null){
				return null;
			}

			var ser = new XmlSerializer (typeof(T));

			var stringReader = new StringReader(tmx.text);

			var obj = ser.Deserialize(stringReader);

			var retClass = (T)obj;

			return retClass;
		}
	}

これは汎用的に利用できるクラスですが、
pathで指定されたResourcesフォルダにあるxmlファイルを
で指定されたclassとして認識させます。


そして、今回読み込みたいxmlファイルはこんな感じ


<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="10" height="10" tilewidth="16" tileheight="16">
 <tileset firstgid="1" name="char_16" tilewidth="16" tileheight="16">
  <image source="../../StageEdit/char_16.png" width="128" height="512"/>
 </tileset>
 <tileset firstgid="257" name="map_16" tilewidth="16" tileheight="16">
  <image source="../../StageEdit/map_16.png" width="512" height="512"/>
 </tileset>
 <layer name="layer_map" width="10" height="10">
  <data>
   <tile gid="258"/>
   <tile gid="258"/>
   <tile gid="258"/>
   <tile gid="258"/>
   <tile gid="258"/>

   〜 省略 〜

   <tile gid="260"/>
   <tile gid="260"/>
   <tile gid="260"/>
   <tile gid="260"/>
   <tile gid="260"/>
  </data>
 </layer>
 <layer name="layer_actor" width="10" height="10">
  <data>
   <tile gid="0"/>
   <tile gid="0"/>
   <tile gid="0"/>
   <tile gid="0"/>
   <tile gid="0"/>
  
   〜 省略 〜

   <tile gid="0"/>
   <tile gid="0"/>
   <tile gid="0"/>
   <tile gid="0"/>
   <tile gid="0"/>
  </data>
 </layer>
</map>

data内のtile要素は 10 x 10 = 100 個あるので省略してます。

そして、これをclassとして読み込みたいのですが、
このxmlに対応した構造のclassをあらかじめ作成しておかないといけません。

そのclassが以下の様な形になります。

	[XmlRoot("map")]
	public class Map
	{

		#region class

		public class TileSet
		{
			[XmlAttribute]
			public int
				firstgid;
			[XmlAttribute]
			public string
				name;
			[XmlAttribute]
			public int
				tilewidth;
			[XmlAttribute]
			public int
				tileheight;
			public Image imgae;
		}

		public class Layer
		{
			[XmlAttribute]
			public string
				name;
			[XmlAttribute]
			public int
				width;
			[XmlAttribute]
			public int
				height;
			public Data data;
		}

		public class Image
		{

			[XmlAttribute]
			public string
				source;
			[XmlAttribute]
			public int
				width;
			[XmlAttribute]
			public int
				height;
		}

		public class Data
		{
			[XmlAttribute]
			public string
				encoding;

			[XmlElement()]
			public List<Tile> tile;
		}

		public class Tile
		{
			[XmlAttribute]
			public int
				gid;
		}

		#endregion


		
		#region public property

		[XmlAttribute]
		public string
			version;
		[XmlAttribute]
		public string
			orientation;
		[XmlAttribute]
		public int
			width;
		[XmlAttribute]
		public int
			height;
		[XmlAttribute]
		public int
			tilewidth;
		[XmlAttribute]
		public int
			tileheight;

		[XmlElement()]
		public List<TileSet> tileset;

		[XmlElement()]
		public List<Layer> layer;





		#endregion
		

		
		
		#region public method




		public int[,] GetLayerData(string name){

			var oneLayer = GetLayer(name);

			if(oneLayer == null){
				return null;
			}

			var w = oneLayer.width;
			var h = oneLayer.height;

			var tiles = oneLayer.data.tile; 

			var grid = new int[w, h];

			for(int y=0; y<h; ++y){

				for(int x=0; x<w; ++x){

					var tileNo = x + (y * w);

					grid[x, y] = tiles[tileNo].gid;

				}


			}


			return grid;
		}




		#endregion
		
		
		#region private method
	
		private Layer GetLayer(string name){

			foreach(var l in layer){

				if(l.name == name){
					return l;
				}

			}

			return null;

		}




		#endregion
		

	}



なんか、結構複雑な構成です(^^;


xmlを見ながらクラスを逆構築していく作業は結構大変でした。
この変を自動化するツールとかないのかなぁ…


とりあえず、簡単に説明すると、
xmlの根になる要素がmapなのでMapクラスとしました。


そして、そこが根ということを示す為に
[XmlRoot("map")]
を宣言しています。


そのあと、#region class
内が、各要素の構成です。
アトリビュート項目は
[XmlAttribute]
で宣言します。


LayerやTileSet等、複数回登場する可能性のある要素は
[XmlElement()]
で宣言します。


また、重要な点として、変数名は
要素名と同じでないといけません。



とりあえず、構造だけあればデータは流し込めますが
ほとんどの場合、欲しいデータはレイヤーの情報だと思うので、
レイヤ名称を指定したらそのレイヤの数値データをintの配列で返す
GeetLayerData関数を用意しています。



以下は作成したクラスを実際に使って、
レイヤーの情報をDebug各値をDebug出力するサンプルです。

		public void ReadMap(){

			var path = "stageData";

			var map = XmlReader.LoadFromXml<Map>(path);

			var grid = _map.GetLayerData("layer_actor");



			foreach(var g in grid){
				Debug.Log(g);
			}

		}


とりあえず、今回はここまでです。
後は、複数のタイルを利用していた場合、
レイヤ内の値が全タイルでの通し番号となるようなので、
TileSetのfirstgidを使って番号を調整する必要がありそうです。


また、現段階では、オブジェクトレイヤーに対応できていないので、
その辺の追加などができたらまた修正版の記事を上げたいと思います。

Hashtable(ハッシュテーブル)をスッキリ書く

iTweenを利用する場合、Hashtableの記述がほぼ必須になってくるかなと思います。

単純に書いた場合は以下の様な感じですかね。

iTween.MoveTo(gameObject, iTween.Hash("speed", 1f, "x",10f, "y",10f, "easeType", iTween.EaseType.linear));


ただし、こう書いた場合、
キーとバリューが横一列に並んでちょっと見づらいです。
(後ろ、既に見えてないし(^^;)


そこで、Hashtableを予め作成しておいて、
それを第二引数に渡すという手もあります。

var hash = new Hashtable();

hash.Add("speed", 1f);
hash.Add("x", 10f);
hash.Add("y", 10f);
hash.Add("easeType", iTween.EaseType.linear);

iTween.MoveTo(gameObject, hash);


こうするとキーとバリューの対比がはっきりして
後で修正するときもやりやすいかなと思います。


しかし、初期化時に既に値が決定しているなら
以下の様な記述でnewの部分で値を設定するということもできます。

var hash = new Hashtable(){

	{"speed", 1f} ,
	{"x", 10f} ,
	{"y", 10f} ,
	{"easeType", iTween.EaseType.linear} ,

};

iTween.MoveTo(gameObject, hash);


こうすると、記述量も減り、ぱっと見もスッキリするので
個人的には初期化の時はだいたいこういう書き方をしています。
ちなみに、enumや普通の配列の定義同様、最後の項目のカンマはつけたままで良いです。


ちなみにListやDictionaryも同様に初期化できます。
詳細についてはあたもこさんのサイトで紹介されいていました。

C# コレクション初期化子


VisualStudioでの話ですが、C#の記述は同様なので
上記での2008バージョンの初期化が利用できます。

コードのフォーマットを自動で整形する

前回は、簡単にコメントを切り替える方法を説明しましたが、
今回は、コード全体のフォーマットを自動で整形する方法についてです。


検証環境:MonoDevelop 4.0.1


たとえば、以下の様にタブ幅がグチャグチャなコードが有るとします。
(普通にやってたらまずこんなインデントにはならないと思いますが(^^;)


f:id:icoc_dev:20140512161100p:plain


そして、


・編集 > フォーマット > ドキュメントをフォーマット
を選択します。
f:id:icoc_dev:20140512161227p:plain


すると、以下のように、コードがきれいなインデントで揃えられます。

f:id:icoc_dev:20140512161421p:plain

ちなみに、こちらのフォーマットはファイル全体に影響します。
また、フォーマットの基準となるタブ幅の設定については、以下の記事を確認下さい。
MonoDevelop 4.0.1のドキュメントフォーマットでタブ幅が正しく設定されない時の対処 - Unityゲーム開発日記@長崎



MonoDevelopを使っていれば、改行時にインデントを揃えてくれるので、
フォーマットを揃える必要がある機会はそんなにないと思いますが、
どっかからコピペで持ってきたコードのインデントが異なってた際などには、
こちらの機能を利用すると便利だと思います。

選択範囲をまとめてコメント化。 その機能をショートカットに割り当てる。

今回は、コメントアウトの手続きをちょっと楽にする方法を紹介します。

まとめてコメントアウト

最近の統合開発環境にはだいたい搭載されていると思いますが、
MonoDevelopには選択範囲をまとめてコメントアウトするという機能があります。

検証環境:MonoDevelop 4.0.1



手続きは簡単です。

・まずコメントアウトしたい場所を選択
f:id:icoc_dev:20140508104136p:plain



・編集メニュー > フォーマット > 行コメントをトグル
を選択します。
f:id:icoc_dev:20140508104140p:plain



・選択範囲がコメントアウトされます。
f:id:icoc_dev:20140508104143p:plain


また、トグルとは、切り替えって意味なので、
上記手続きで既にコメントアウトされた箇所を再度選択して
行コメントをトグルを選ぶと、コメントアウトが解除されます。


/*

*/

でもまとめてコメントアウトできますが、
入れ子になるとうまく行かなかったりするので、
この機能のほうが使いやすいかなと思います。

ショートカットに割り当て

また、この機能をショートカットで割り当てておくと
さらに便利になります。


ショートカットへの登録手続きは以下の通り。

MonoDevelop-Unity > Preferences
を選択
f:id:icoc_dev:20140508105518p:plain


・設定メニューが開く
f:id:icoc_dev:20140508105523p:plain


1. 「キーバインディング」を選択
2.「検索」欄に「トグル」と入力し、右のハケ?ボタンを押す
 *「かな」ボタンをおすと、入力になってしまうみたいなので、
 どこかに一度「トグル」と入力した文字をコピペで貼り付けて下さい。

3.「行コメントをトグル」が表示されていると思うので、選択
4.「バインディングの編集」欄で、コマンドを入力し、「適用」を押す
 *ちなみに、自分は「Command + Option + C」に割り当てています。


後は、まとめてコメントアウトの手続き同様、コメント化したい範囲を選択し、
先ほど割り当てたショートカットキーを押すと、コメントアウトされます。


これを登録しておくと、かなりコーティング時に楽になるかと思うので
是非試してみてください〜。

新規作成時スクリプトのテンプレートファイルを編集する

Unite2014の「Editor拡張 マニアクス2014」にて
安藤圭吾さんがUnityで新規作成した時に作成される
スクリプトのテンプレートファイルの編集について紹介されていました。


今回は、その手続きについての記事です。


  • 自信の環境がmacですので、macでの方法となります。
  • 確認したUnityのバージョンは4.3.4です。
  • バージョンによってファイル名やファイルの位置が変わる可能性があります。


概要としては、Unityアプリの中にテンプレートファイルのtxtがあるので、
それを編集するだけです。


まず、Finderで、Unity.appがインストールされている場所へ移動して下さい。
自分の場合は
Macintosh HD > アプリケーション > Unity > Unity.app
としています。


Unity.app上で右クリックメニューを表示すると、
「パッケージの内容を表示」
とありますので、これでapp内のファイルを表示させて下さい。

f:id:icoc_dev:20140428192617p:plain




その後、「Contents」というフォルダが表示されると思いますが、
そこを、以下の通りたどっていって下さい。
Contents > Resources > ScriptTemplates

f:id:icoc_dev:20140428192622p:plain


すると、80〜84までの頭数字がついたtxtファイルが表示されると思います。
そこで、自分が利用している言語のテンプレートファイルを修正すれば、
UnityMonoDevelopスクリプトファイルを新規作成した際に、
ここで編集したテンプレートの状態のものが表示されると思います。

訂正:MonoDevelopで新規作成した場合は、反映されませんでした。


ただし、元の状態に戻せるよう、念のため編集前のファイルの
バックアップをとっておくことをおすすめします。


また、C#のテンプレートの書き方について以前投稿していますので、
ご参考にどうぞ。
regionとコードのテンプレート - Unityゲーム開発日記@長崎

iTweenで動作しているオブジェクトを一括制御する

オブジェクトの移動を物理制御ではなく、
決められた位置に移動させたい場合に便利なのが
無料で公開されているAsset、iTweenです。


AssetStore - iTween


自信で制作してるゲームも2D系では
物理動作を利用しない場合が多いので、
iTweenを多用しています。


iTweenの使い方については、
ActionScript入門Wikiさんの
以下のページにわかりやすくまとめられています。
ActionScript入門Wiki - Unity - トゥイーンライブラリiTweenを使用する


先日リリースしたまっすぐ勇者は、
ゲーム開始後自動で勇者が移動するという仕様で、
iTweenを使ってマス移動をしているのですが
今回はゲームを停止させるためのPAUSE機能を実装しました。


そこで、利用したのが
iTweenを利用しているオブジェクトの一括制御です。


PAUSEを押した時に、一時的にiTweenを利用している
オブジェクトの制御を止め、
PAUSEを解除した時に、iTween制御を再開するというのをやりたかったのですが、
それぞれの個々のオブジェクトに対して、停止を掛ける必要はなく、
iTweenの下記staticメソッドで一括制御が出来ました。

// 一時停止
iTween.Pause();

// 再開
iTween.Resume();


ちなみに、iTween.Stop()でも停止はできるのですが、
こちらは完全停止という形になり、再開はできなくなります。


また、一点注意があるのですが、
iTween.Pause()
をかけたままオブジェクトを削除したり、シーンを遷移したりすると
オブジェクトへの参照が残るのか、次回iTweenの制御をした際に
エラーとなる場合があります。


なので、iTweenでの制御中にオブジェクトの削除、またはシーンの遷移をする場合は
オブジェクト削除前にiTween.Stop()をかけるようにしましょう。