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

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

2Dオブジェクトの幅を取得する方法

まずオブジェクトにSprite Renderのコンポーネントが追加されている必要があります。
スプライト化したオブジェクトをHierarchyに持っていけば勝手に追加されていると思います。


f:id:icoc_dev:20140124092506p:plain


後は、以下のコードで幅を取得可能です。

	var sr = object.GetComponent<SpriteRenderer>();
	var width = sr.bounds.size.x;

bounds.sizeでオブジェクトのサイズを取ってるので、
yを参照すれば高さを取得できます。

続きを読む

regionとコードのテンプレート

みなさんは、regionを使ってますか?


C#の機能になりますが、

regionを使うと、

#region 名称

コードコードコードコードコードコードコード
コードコードコードコードコードコードコード
コードコードコードコードコードコードコード
コードコードコードコードコードコードコード

#endregion

という部分を、エディタ上で折りたたむ(一時的に非表示)ことができます。


※折り畳み後

名称

折りたためるかは、エディタの機能になるので、
単純なテキストエディタとかでは折りたためないですが。


自分は、regionを公開単位や、メソッド・プロパティ、
単位等でくくって使ってます。


といっても、実際におりたたむこと自体はほとんどないです。


ただ、コードを書く場所が決定されるという事が非常に機能しています。


コードがないと説明しづらいので、
とりあえず、自分の使ってるMonoBehaviour用のテンプレートを貼ります。

using UnityEngine;
using System.Collections;

namespace _hoge
{
	public class _copypast : MonoBehaviour
	{

		#region enum
		
		#endregion

		
		#region const
		
		#endregion

		
		#region public property
		
		#endregion
		
		
		#region private propety
		
		#endregion
		
		
		#region public method
	
		#endregion
		
		
		#region private method
	
		
		#endregion
		
		
		#region event
	
		void Start ()
		{
		
		}

		void Update ()
		{
		
		}
		
		#endregion
	}

}


こんなかんじで、定数、プロパティ、メソッド、イベント
といったくくり、それぞれがprivateかpublicかでわけてます。


定数(enum,const)は実際使わないことも多いので、
private、publicまでは分けてません。

イベントに関しては、private メソッドとも取れますが、
event周りは挙動の主軸になるのと、どれも同じパターンがあるので
ひとくくりにしています。


public、privateの順番は個人的には公開しているものが上っていう感覚があるんですが、
この辺りは人によって感覚が違うでしょうね。


以上の様に、よく使うregionをテンプレートとしてつかってます。
これによって、関数やプロパティをコードのどのあたりに書けばよいかってのが
決まるので、記述の順番で迷うという時間を削減することができます。


ちなみに、publicなプロパティ内等のregion内での順番は、特に決めていなく
基本的には追加したものを下に、関連が近い関数は近づけたり等を
あとで行ったりします。


また、上のテンプレートは
_copypast.csという名前でプロジェクトに追加しています。


こうしておくと、テンプレートをすぐに参照できるので便利です。
コピペ補助アプリなんかで代用してもよいですが、
ちょっと整形したいときに、記述的に矛盾がないかをコンパイラが判断してくれるという
良い面もあります。



以上、regionとテンプレートについてでした。
実際にプログラムの挙動には関係ないですが、
コードの見やすさというのはコードのメンテナンスしやすさに直結し、
良いプログラムが作れるかの非常に重要な要素のひとつなので、
コードの書きやすさを一度意識してみてはどうでしょうか。

一次元セルオートマトン (JavaScript)

一次元セルオートマトンのデモです。

一行目をランダムに生成し
次の行から、設定したルールに従い描画を行います。


各値を設定した後に、runボタンを押してください。



※詳細解説

各 X 座標において、自分の上の行の

x-1
x
x+1

の3ドットの状態を見て、自分のドットの色を決定します。
黒いドットは 0
白いドットは 1
です。


出現パターンについては、
wikipediaにいくつかの例が絵柄つきで掲載されていました。

Elementary cellular automaton - Wikipedia, the free encyclopedia


セルオートマトンは、生物の模様としても確認されていて
非常に興味深いです。

Cellular automaton - Wikipedia, the free encyclopedia


また、コードについては、こちらを参照させていただきました。
1次元セルオートマトン - KAMEMO for SoftComp. and Intelli. Comp.


対象を6ドットに広げると、かなり複雑なパターンも生まれてくるようです

mac環境Unityで Android実機テスト

今のところiPhone向けにしか開発をしてませんでしたが、
今後はAndroid対応も視野にいれていくということで
とりあえず試しに今のプロジェクトがAndroidで動くのか試すことにしました。


いくつかGoogleさんで検索させていただきましたが、
Unityの公式が一番シンプルにまとまってました。


Android SDK セットアップ / Android SDK Setup
http://docs-jp.unity3d.com/Documentation/Manual/android-sdksetup.html


ここの手順にしたがって、
SDKの導入。
EclipseSDKの中に入ってるけど、Unityから直接起動するだけなら
Eclipseは多分いらなそう。


SDKは今後ずっと参照し続けることになるので、
それなりの場所を用意して参照する形になります。



後は、Unityの
File > Build Settings...
を開いて、PlatformをAndroidに切り替え(Switch platform)ます。


そして、Build!!


そしたら、途中でSDKの場所を聞かれるので、さっき設置した
SDKを指定。


その後JDKの6が必要というエラーが出ますが、
そのままインストールするかとの問い合わせもあるので、
ここでインストール。


この時点でビルドに一回失敗したことになるので、
JDKインストール完了後、もう一度しつこくビルド。


これで、コードに問題なければビルドが完了すると思います。



引き続き、実機でのテスト方法。


Androidは携帯としては使ってないので
まだ良く挙動を知らないのですが、
どうも開発者用モードというものがあるらしい。


ということで、そのモードに切り替えましょう。

一応、さっきのUnity公式サイトにも書いてはあるのですが、
絵付きで紹介されてるサイトが合ったので参照しておきます。


■USBデバッグを有効にする、提供元不明アプリのインストールを許可する
http://www.yahoo-help.jp/app/answers/detail/p/601/a_id/54447/~/usb%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B%E3%80%81%E6%8F%90%E4%BE%9B%E5%85%83%E4%B8%8D%E6%98%8E%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%82%92%E8%A8%B1%E5%8F%AF%E3%81%99%E3%82%8B


あと、macにandroidを認識させるために、Nexsus7の場合は
以下の様な設定を行いました。

■Nexus7にMacからUSB経由でファイル転送するには?
http://kinsentansa.blogspot.jp/2012/10/macnexus7usb.html


でも、デバッグモードを有効にした時点でこの設定はべつにいらないのかな??




とりあえず、ここまででAndroidをmacに認識させ、
UnityでのAndroidビルドも完了しました。


あとは、UnityからBuild And Runを選べば
Android実機上で実行されるはずです。



てことで、とりあえず動作は確認できたのですが、
SetActive周りがはたらいって無いっぽい感じ。
Global Defineによる定数値も効いてないかな??


色々とローカライズが必要そうな雰囲気ですが、
とりあえず、Android動かすまでってことで、今回はここまで。
ローカライズ作業について特筆があればまた記事書きます。

■2013/12/11 11:46追記
広告等のシステムをAndroid用に設定してなかったために、
一部プログラムが実行されなかったりという問題が発生していたのが原因でした。
なので、とりあえず、そのあたりを無効にしたら問題なく動作!!

Unityの適応力すごい!!

MonoDevelop での名前変換 (リファクタリング)

macでのコード開発環境については、まだあまり調査したりしてないのですが
標準でついてくるMonoDevelopは、日本語が入力できないという欠点はあるものの、
コード補完や、リファクタリング機能などもついていて、それなりに使えはするので
使い続けてます。

ただ、画面の上下、左右分割ができないのが微妙ですが・・・


今回は、リファクタリング機能の名前変更について。


といっても、操作自体はすごく簡単で、

変数名の上で右クリックメニューを出して
リファクタリング > 名前の変更

と選ぶだけです。

これだけで、プロジェクト内で利用している変数名の名前が
一気に変わります。

あとで自由に変えられるので、
ネーミングに深く悩む必要もなくかなり便利な機能です。


また、クラス名やメソッド名も変更できるので
すごく重宝します。


ただ、クラス名に関しては、ファイル名と合わせないと
オブジェクトにコンポーネントとしてアタッチできなかったり、
public なプロパティを変更すると、
すでにUnity上で割り当ててる値やGameObjectが解除されたりするので
注意が必要です。

※クラス名は、変更時にファイル名も変更する項目にチェックを入れておけば
ファイル名も自動で書き換えてくれます。



プログラミングにおいて、ネーミングセンスはロジックを構築する技術同様に
重要な要素ですが、最近の開発環境はこういうのが標準でついてることが多いので
あとで名づけがおかしいと感じてもすぐ対応できるので便利ですね。

ランダムな値(乱数)

Unityにはランダム生成専用のクラスが有ります。

■公式リファレンス:Random
http://docs-jp.unity3d.com/Documentation/ScriptReference/Random.html



使い方は単純で、

Random.Range(min, max)

とすると、min 〜 max-1
の範囲までの値が取得できます。



maxが-1になってるので、配列のサイズ内でランダム値を出したい場合は、

Random.Range(0, allay.Length)

みたいに、演算しないでそのままLength値が使えるという面では便利そうです。
また、minとmaxにはintまたはfloatが利用でき、
それぞれ与えた型で返却されます。



あとは、

Random.value

で 0.0 〜 1.0 範囲のfloatが返ってきます。
ただし、0.1、0.2、0.3
と少数第一位単位で刻むわけではなく、
0.287274
とか、範囲内でfloatが取りうる値で返してくるようです。
敵の回避率をランダムに設定とか、
パーセント的な値を設定する際には便利そうです。



また、ランダムにダンジョンのマップを生成するロジックがあったとして、
起動の度にダンジョンを変化させるのではなく、一時間おきにランダムパターンを
変更したいという際には、

Random.seed

の値が便利です。
この値を設定しておくと、毎回決まったランダムパターンを取得することが可能です。
なので、時間毎にランダムパターンを変化させたい場合は、seed値に
今2:46なら、2をseedに設定することで、2時台の間は同じランダムダンジョンを
楽しむことが可能となります。
ランダムの種を設定といったほうがわかりやすい人もいますかね。



リファレンスを見ると、他にも円や球体の範囲内で
ランダム値を取得するというゲームには活かしやすそうなプロパティもあるみたいですね。



この辺はまだ使ったことがないので、利用する機会があればまた
記事にしていきたいと思います。

Textureのスクロールについて

テクスチャをスクロールさせたくて、以下のコードを書いてみたけど、うまく行かない。

public class TextureScroll : MonoBehaviour
{

	public Vector2 speed;

	
	// Use this for initialization
	void Start ()
	{

	}

	// Update is called once per frame
	void Update ()
	{
		var x = (Time.time * speed.x) % 1;
		var y = (Time.time * speed.y) % 1;
		var vec = new Vector2(x, y);

		renderer.material.mainTextureOffset = vec;
		

	}
}

いろいろ調べてみたら、テクスチャのWrap ModeがRepeatになってないと
ダメらしい。



とりあえず、Repeatにすることで、スクロールは可能となった。
ただし、Texture TypeがGUIにはWrap Modeがなく、
CursorにはWrap Modeがあるのに、Repeatを設定しても、
Apply押下時にClampに戻ってしまう。


これはバグなのかな??

facebookのunityコミュで聞いてみたところ
そもそもCursorタイプはカーソルで利用するためのものであり、
テクスチャとして繰り返させること自体に意味が無いとのことでした。

つまり、Wrap Modeが設定項目にある事自体が
おかしいのかな??

どちらにしろ、CursorのWrap Modeは利用できないということで
考えておいたほうが良さそうです。

https://www.facebook.com/groups/unityuserj/permalink/603806109679333/


他のTexture Typeだとテクスチャが汚くなるし、どうしたもんか。


ちなみに、iPhone実機だと、単に時間経過でスクロールさせてると、
x,yの値がかなり大きくなり、
徐々にスクロールスピードが落ちていくため
テクスチャサイズ範囲内の値で繰り返されるように
(Time.time * speed) % 1
としてます。

カメラ比率に合わせ、GUIの位置を調整する

黄昏66さんの記事で、
スマフォ毎の解像度合わせて、カメラの比率を自動的に調整する
スクリプトが紹介されていました。

黄昏66 アスペクト比設定[Unity]


こちらのおかげで、比率さえ確定させてしまえば、
どの端末でもほぼ同じようにオブジェクトが表示され
非常に重宝しています。


ただし、Unityの通常のオブジェクトは比率に合わせて
伸縮表示されますが、GUIに関しては、ピクセル単位での値設定になるので、
カメラの比率とは別次元の位置とサイズの単位になります。

そこで、GUIも比率に合わせて伸縮させることができないかと思い、
黄昏66さんのコードを参照させていただき、
想定するピクセルサイズの位置を指定すると、
画面の比率に対してのRectサイズを返却するというクラスを
作成してみました。



比率の計算部分は、ほぼ参照で、
カメラ外のoffset値を付け加えたりしています。

また、広告にGAME FEATのアイコンを利用しているのですが、
こちらのUnityプラグインでのアイコンサイズは、
GUIの設定値とはまた別次元となっており、
通常のGUIの半分の値で設定するような仕様のようなので、
このための半分サイズの値も取れる関数も追加しています。

使い方的には、
コンストラクタ
画面の幅、画面の高さ、横画面かどうか
を渡してやり、

GetRect、GetRectHalfにRectを渡して
補正値をRectで受け取る構造にしています。

以下コードです。

public class GuiScaler
{
	
	#region public property



	#endregion
	
	#region private property

	float _scale;
	Vector2 _offset;
	
	#endregion
	
	#region constructer
	
	public GuiScaler (
		int fixWidth = 640, 
		int fixHeight = 960, 
		bool isPortrait = false
	)
	{
		Calc (fixWidth, fixHeight, isPortrait);
	}
	
	#endregion
	
	#region private method
	
	void Calc (int w, int h, bool portrait)
	{
		 
		float width = portrait ? h : w;
		float height = portrait ? w : h;
		
		float target_aspect = width / height;
		float window_aspect = (float)Screen.width / (float)Screen.height;
		float scale = window_aspect / target_aspect;

		Rect _rect = new Rect (0.0f, 0.0f, 1.0f, 1.0f);
		if (1.0f > scale) {
			_rect.x = 0;
			_rect.width = 1.0f;
			_rect.y = (1.0f - scale) / 2.0f;
			_rect.height = scale;
			
			_scale = (float)Screen.width / width;
		} else {
			scale = 1.0f / scale;
			_rect.x = (1.0f - scale) / 2.0f;
			_rect.width = scale;
			_rect.y = 0.0f;
			_rect.height = 1.0f;
			
			_scale = (float)Screen.height / height;
		}
		
		_offset.x = _rect.x * Screen.width;
		_offset.y = _rect.y * Screen.height;
		
	}
	
	#endregion
	
	#region public method
	

	
	public Rect GetRect (float x, float y, float width, float height)
	{ 
		

		Rect rect 
			= new Rect 
				(
					_offset.x + (x * _scale),
					_offset.y + (y * _scale), 
					width * _scale, 
					height * _scale
				);
		
		return rect;
		
	}
	
	public Rect GetRect (Rect rect)
	{
		return GetRect (rect.x, rect.y, rect.width, rect.height);
	}
	
	public Rect GetRectHalf (float x, float y, float width, float height)
	{
			

		
		Rect rect 
			= new Rect 
				(
					(_offset.x + (x * _scale)) / 2,
					(_offset.y + (y * _scale)) / 2, 
					(width * _scale) / 2, 
					(height * _scale) / 2
				);
		
		return rect;
		
	}
	
	public Rect GetRectHalf (Rect rect)
	{
		return GetRectHalf (rect.x, rect.y, rect.width, rect.height);	
	}
	
	#endregion
	

			
}