Unity3Dゲーム制作入門編(旧フットボールバカども)

Unity3Dゲーム制作入門編(旧フットボールバカども)

週末HOBBY!!
Unity3dアプリ制作奮闘記 (現在入門編)+フットボール観戦記などなど

Amebaでブログを始めよう!

やや久しぶりの更新になります。

仕事が忙しくなってきたとか、趣味プログラム時間の約半分が某PS4ゲームに取られているとか理由はありますが、具体的に始めているゲーム試作が若干行き詰っている感があることもあります。

試作は、ここまでで記事に書いた仕組み以外は、ほぼ使用しておりません。
揃え系のパズルゲームです。
その中で紹介できるようなものは特に・・・・


なので、今回は Unity のちょっとした注意点を。
いや、結構気をつけないといけないことです。

スクリプトファイルの文字コードと改行コードについてです。


MonoDevelop で日本語のコメントが文字化けしたり、android でビルドしたときだけエラーが出たり、などなど・・・

文字コードと改行コードは、決めてしまいます。

文字コードは「UTF8 (BOMつき)」、改行コードは「CR+LF」です。
これで間違いありません。
MonoDevelop で設定するのは面倒?なので、なにか外部エディタで設定するのが良いでしょう。
(そもそもMonoDevelopでは、確認すらしにくいです)


わたし自身は、一度作ったファイルをコピーして、中身だけを入れ替えるので、普段はそこまで気にしません。
スクリプトの記述も、普段は外部テキストエディタを使用しているため、自動的に変わることもありません。

しかし、毎回スクリプトファイルを新規作成したり、MonoDevelop で記述する人は、改行コードあたりは知らぬうちに変わっていることも。

ではまず MonoDevelop を使う際の注意点。
24_01 
スクリプトファイルを保存するとき、このような警告が出ることがあります。
ここで「Convert」を選択すると、文字コードは「UTF8 (BOMつき)」、改行コードは「LF」に変換されてしまいます。
間違えずに毎回「Keep line endings」を選択しましょう。

では、そもそもこの警告を出さない方法はないのか。

MonoDevelop 上で、設定を変更すれば(たぶん)出なくなるっぽいです。
24_02 
Solution タブ内のプロジェクト上で右クリック、「Options」を選択します。
24_03 
Source Code → Code Formatting → C# source code を選択。
24_04 
「Use default settings from "Text file"」のチェックを外し、
Line endings : の項目を、「Microsoft Windows」に変更し、「OK」で決定します。
これで改行コードは「CR+LF」がデフォルトになると思います。
毎回「Convert」を聞かれることもなくなります。


さて、最後は便利な裏技?を。

Unity エディタ上で、右クリック → Create → C# Script の手順でファイルを作成する場合、
こんなファイルが作成されると思います。


using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}



さて、このファイル、実はとある場所に元データがあり、そこをいじることで、デフォルトで作成されるファイルを好きなように変更することができます。

Unity4.6.1f1\Editor\Data\Resources\ScriptTemplates
の中にある「81-C# Script-NewBehaviourScript.cs」です。

中身がそのまま C# の新スクリプトファイルにコピーされます。
ファイル内の 「#SCRIPTNAME#」は、ファイル名がそのまま代入されます。
Unityエディタからスクリプトファイルを作成している人は、使いやすいようカスタマイズしましょう。
ちなみに、このファイル自体を文字コード「UTF8 (BOMつき)」、改行コード「CR+LF」にすると、その形式で作られるようになります。
せっかくなので、変更しておきましょう。


個人的に MonoDevelop では、スクリプトは書きません。
なぜなら全角日本語の記入が見えなかったり、そもそも使い慣れた設定にできなかったりするからです。
デバッグからの流れで書くくらいです。
普段は昔から使い慣れている WZエディタ を使用しています。補完機能なんかはほとんどないんで、便利ではないですが。
画面はこんな感じです。
24_05 
以上。
なんか・・PCが変わったからか、全体的に画像がでかかった・・・

Unity C# で define を定義してみたいと思います。

define を定義したい場面は、個人的には2タイプ。
単純な定数定義と、コンパイルオプションとして使用する、の2つです。

定数定義は、ここまででも出てきましたが、
public const int   TEISU = 1;
などで代わりに使用しています。

グローバルな定数の場合は、


static public class cClass
{
	public	const	int	TEISU	= 1;
};



と、クラス自身をグローバル扱いしています。
若干面倒ですが、問題はありません。

コンパイルオプションとして定義したい場合は、方法がいくつかあります。


その前に、コンパイルオプションとは、簡単に説明すると

#if ○○○
プログラムA
#endif

○○○を define で定義しておくと、「プログラムA」の部分はプログラムに組み込まれ、
定義しなければ「プログラムA」の部分はプログラムから省かれます。

さらには

#if ○○○
プログラムA
#else
プログラムB
#endif

○○○を define で定義しておくと、「プログラムA」の部分のみプログラムに組み込まれ、
定義しなければ「プログラムB」の部分のみプログラムに組み込まれます。

スクリプトから消したくはないけれど、組み込みたくない場面があるものなどに使います。
開発中のみ使用するデバッグ用プログラムなどに、よく使います。

それでは、いくつかの方法を。

[1]
cs ファイルの先頭に、#define で定義する。


#define _DEBUG_ON	// 定義でデバッグあり

using UnityEngine;
using System.Collections;

public class SysUpdate : MonoBehaviour
{
	void	Update()
	{
		
#if _DEBUG_ON
		print(\"デバッグあり\");
#else
		print(\"デバッグなし\");
#endif
		
	}
}


ファイルの一番上に定義します。
このファイル内でのみ効果のある定義です、他の cs ファイルには効果がありません。

ちなみにこのプログラムは
print("デバッグあり");
のみ実行されます。


[2]
Build Settings... → Player Settings... → Inspector
Other Settings → Scripting Define Symbols に定義します。

_DEBUG_ON;_DEBUG_MENU_ON

ここで定義したものは、全てのスクリプトファイルに効果があります。
複数定義する場合は、セミコロンで区切ります。
ここを書き換えると、自動的にコンパイルが始まります。
また、この方法で設定する場合は、プラットフォームごとに設定できます。


[3]
外部ファイルで定義する。

gmcs.rsp もしくは smcs.rsp というファイルを作成し、Assets フォルダの直下に置きます。

smcs.rsp


-define:_DEBUG_ON




ここで定義した define は、全てのスクリプトファイルに効果があります。
この方法で設定する場合は、プラットフォームごとに設定できません。
さらに、問題点がひとつ、このファイルは更新しても再ビルドされません。
このファイルを更新した場合は、適当にソースファイルを書き換えるなど、どうにかしてコンパイルさせなければなりません。
あまり書き換えない define 定義なら、ここに書いておくと、視認性が良くて便利です。

さて、ここで小ネタをひとつ。

デバッグ用の定義の場合、普通は
#define _DEBUG
のみ定義すると思います。
わざわざ _DEBUG_ON としているのは、
[3] のファイルでは、コメントが使用できないので、定義しているものを一時的に定義を止めたいときは、その行を消去しなければなりません。
[2] の場合も同様に _DEBUG の定義自体を削除しないといけません。

消去してしまうと、再定義するのは面倒だし、消去したまま数日とか経ってしまうと存在そのものを忘れてしまいかねません。
なので、
-define:_DEBUG_ON

-define:_DEBUG_OFF
で書き換えるようにしています。
削除しなくていいので便利。

#if _DEBUG_ON
  print("デバッグあり");
#else
  print("デバッグなし");
#endif

_DEBUG_OFF が定義されている場合は
print("デバッグなし");
が実行され、結果が同じです。


覚えておいて損はないですよ。

今回はデータ保存を実装したいと思います。

Unity の PlayerPrefs というクラスを使います。
http://docs-jp.unity3d.com/Documentation/ScriptReference/PlayerPrefs.html

名前(ラベル)つきで特定の型で値を保存してくれます。

例えば int 型
PlayerPrefs.SetInt("名前", 値);

これでセットして、
PlayerPrefs.Save();
これで実際にストレージやハードディスクに書き込みます。

Save() のほうは必ず必要なわけではありません。
アプリが正常に終了された場合は、その時点で自動的に書き込まれます。
しかし、強制終了されてしまった場合は保存されませんので、値をセットしたら一緒に書き込みまで行いましょう。

以下、実際に使用するためのラッピングクラスです。
たいしたことはしていませんが。

LocalSave.cs


using UnityEngine;
using System.Collections;


/*!
 *@brief	ローカルデータ保存制御クラス
 */
static public class LocalSave
{
	
	/*!
	 *@brief	ローカルのデータを実際に保存
	 *
	 *@param	なし
	 *@return	なし
	 */
	static	public	void	SetWrite()
	{
		PlayerPrefs.Save();
	}
	/*!
	 *@brief	ローカルのデータを削除
	 *
	 *@param[in]	name	:	データ名
	 *@return	なし
	 */
	public	static	void	SetDelete(string name)
	{
		PlayerPrefs.DeleteKey(name);
	}
	/*!
	 *@brief	ローカルにデータをセーブ (string 型)
	 *
	 *@param[in]	name	:	データ名
	 *@param[in]	data	:	データ
	 *@return	なし
	 */
	static	public	void	SetString(string name, string data)
	{
		PlayerPrefs.SetString(name, data);
	}
	/*!
	 *@brief	ローカルにデータをセーブ (int 型)
	 *
	 *@param[in]	name	:	データ名
	 *@param[in]	data	:	データ
	 *@return	なし
	 */
	static	public	void	SetInt(string name, int data)
	{
		PlayerPrefs.SetInt(name, data);
	}
	/*!
	 *@brief	ローカルにデータをセーブ (bool 型)
	 *
	 *@param[in]	name	:	データ名
	 *@param[in]	data	:	データ
	 *@return	なし
	 */
	static	public	void	SetBool(string name, bool data)
	{
		int		dt1 = 0;
		
		if(data){
			dt1 = 1;
		}
		PlayerPrefs.SetInt(name, dt1);
	}
	/*!
	 *@brief	ローカルのデータをロード (string 型)
	 *
	 *@param[in]	name	:	データ名
	 *@return		:	データ
	 */
	static	public	string	GetString(string name)
	{
		return PlayerPrefs.GetString(name);
	}
	/*!
	 *@brief	ローカルのデータをロード (int 型)
	 *
	 *@param[in]	name	:	データ名
	 *@return		:	データ
	 */
	static	public	int	GetInt(string name)
	{
		return PlayerPrefs.GetInt(name);
	}
	/*!
	 *@brief	ローカルのデータをロード (int 型)
	 *@note		読み込むパラメータがない場合はデフォルト値をセット
	 *
	 *@param[in]	name	:	データ名
	 *@param[in]	def	:	デフォルト値
	 *@return		:	データ
	 */
	static	public	int	GetInt(string name, int def)
	{
		return PlayerPrefs.GetInt(name, def);
	}
	/*!
	 *@brief	ローカルのデータをロード (bool 型)
	 *
	 *@param[in]	name	:	データ名
	 *@return		:	データ
	 */
	static	public	bool	GetBool(string name)
	{
		bool	ret = false;
		
		if(PlayerPrefs.GetInt(name) != 0) {
			ret = true;
		}
		return ret;
	}
	
}	// static public class LocalSave



ゲームからは、このクラスにアクセスします。
実際はこんな感じです。


using UnityEngine;
using System.Collections;


/*!
 *@brief	オプションデータ管理クラス
 */
static class GameOption
{
	private	const	int	DATA_VER	= 15012401;	//!< 現在のオプションデータVer.
	
	
	static	private	long	m_HighScore	= 0;		//!< ハイスコア
	static	private	int	m_DataVer	= 0;		//!< 現在のオプションデータVer.
	
	
	/*!
	 *@brief	ゲッター・セッター
	 */
	// ハイスコア
	static	public	long	HighScore
	{
		get { return m_HighScore; }
	}
	/*!
	 *@brief	コンストラクタ
	 *
	 *@param	なし
	 *@return	なし
	 */
	static	GameOption()
	{
		
	}
	/*!
	 *@brief	オプションデータ読み込み
	 *
	 *@param	なし
	 *@return	なし
	 */
	static	public	void	ContLoad()
	{
		
		// 保存データのオプションデータVer.取得
		m_DataVer = LocalSave.GetInt(\"m_DataVer\");
		
		// 最新のオプションデータなし
		if (m_DataVer != DATA_VER)
		{
			// 新規オプションデータ作成
			ContSave();
			return;
		}
		
		m_HighScore = long.Parse(LocalSave.GetString(\"m_HighScore\"));
	}
	/*!
	 *@brief	オプションデータ保存
	 *
	 *@param	なし
	 *@return	なし
	 */
	static	public	void	ContSave()
	{
		
		LocalSave.SetInt(\"m_DataVer\", DATA_VER);
		m_DataVer = DATA_VER;
		
		LocalSave.SetString(\"m_HighScore\", m_HighScore.ToString());
		
		// 保存実行
		LocalSave.SetWrite();
	}
	/*!
	 *@brief	ハイスコア保存
	 *
	 *@param[in]	score	:	ハイスコア
	 *@return	なし
	 */
	static	public	void	SetHighScore(long score)
	{
		
		if (m_HighScore > score) {
			return;
		}
		m_HighScore = score;
		
		// オプションデータ保存
		ContSave();
	}
	
	
}	// static class GameOption


ContLoad() でゲーム開始直後にオプションデータを読み込みます。
オプションデータがないようなら、オプションデータを作成します。
さらには、DATA_VER で設定しているバージョンNo.ではないデータが読み込まれた時にも、新たに保存しなおしています。

データ保存時は、その場で PlayerPrefs.Save(); まで実行するようにしています。

ゲームでよくある BGM の ON/OFF なんかは、この保存で十分かと思います。
またゲーム中に通信が全くないゲームなら、プレイヤー名や名前などもこれで十分です。