月別アーカイブ: 2014年7月

PCブラウザで無料で遊べるゲームを公開しています!

ここ数ヶ月、絶賛スランプ中でスマホアプリのリリースが滞っているのですが、肩ならし的にPCブラウザで遊べる無料ゲームを作って公開しています。

こちらのサイトで公開させてもらってます。

  うに部屋

多くのゲームが投稿されているので是非とも遊んでみて下さい!
最初に遊ぶ時にはUnity WebPlayerというプラグインのインストールが必要になりますが、画面に指示が出るはずなので難しいことは無いと思います。

ゾンビ・ヘッドショット
ゾンビ・ヘッドショット
「迫り来るゾンビを倒せ!
ゾンビを倒せるのはヘッドショットだけだ!」

ひたすらヘッド・ショットのみを狙ってゾンビを倒すゲームです。
マウスボタン押下中はズームアップしていき、最大ズーム時にボタンを離すと射撃します。

Ocean Racer
Ocean Racer
「ボートを操作して海面を走り抜けろ!」

自動で加速します。
マウスで左右にドラッグで方向転換、上下で加速/減速します。

コーナーリング時には左右ドラッグで船体を目的の方向に向けてから、上ドラッグで加速するとスムーズに抜けられます!

焼き払えぇぃっ!
焼き払えぇぃっ!
「爆煙をかき分け迫り来る敵を焼き払え!薙ぎ払え!」

映画版『ナウシカ』において庵野秀明氏が作画した巨神兵が王蟲にプロトンビームを発射するシーンに感銘を受けて作ってみました。

クリックしたところに砲撃。
ドラッグすると連続砲撃できます。
連続砲撃でまとめて焼き払うと高得点!!

[Unity]:[エディタ拡張]:SceneにGameObjectを大量に配置する

使用バージョン:Unity 4.5.2f1

製作中のゲームで海面に大量のブイを並べて配置したいのですが、手作業ではとても無理なのでエディタ拡張を利用して、一定間隔でGameObjectを複製して配置する機能を作ってみました。

WaterBoat画面イメージ

初めてのエディタ拡張で間違ってる部分もありそうなので、ご意見など頂けたら幸いです。(@mokusan)

先ず完成イメージは以下。

CreateBuoys完成イメージ01

このように必要なパラメータを設定して「Create」を押下すると、先に挙げたゲーム画面のようにブイが一定間隔で配置される。

・エディタ拡張用のスクリプト
「Editor」という名前のフォルダ配下に置く必要がある。
Resourcesと同様で他のフォルダを掘って、その下に「Editor」フォルダを配置しても大丈夫。
「Editor」フォルダ
(スクリプト”CreateBuoys.cs”の内容全文はこの投稿の最後に掲載しておきます。)

・UnityEditor名前空間の型の使用を許可
通常のUnityEngine以外にUnityEditorを指定しておく。

using UnityEngine;
using UnityEditor;
using System.Collections;

・メニューアイテムの追加
メニューアイテム01 メニューアイテム02

[MenuItem("GameObject/Create Other/Create Buoys")]
static void Init() {
	EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys");
}

“GameObject/Create Other/”配下を指定することで、Hierarchyビューの「Create」のメニューにも追加される。

・ウィンドウの表示
メニューから「Create Buoys」が選択された時に専用のウィンドウを表示する。

[MenuItem("GameObject/Create Other/Create Buoys")]
static void Init() {
	EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys");
}

EditorWindow.GetWindow

・ウィンドウ内容の表示
必要なメンバ変数を定義し、OnGUI()内で記述する。

public class CreateBuoys : EditorWindow {
	private GameObject parent;
	private GameObject prefab;
	private int numX = 1;
	private int numY = 1;
	private int numZ = 1;
	private float intervalX = 1;
	private float intervalY = 1;
	private float intervalZ = 1;
void OnGUI() {
	try {
		parent = EditorGUILayout.ObjectField("Parent", parent, typeof(GameObject), true) as GameObject;
		prefab = EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), true) as GameObject;

		GUILayout.Label("X : ", EditorStyles.boldLabel);
		numX = int.Parse(EditorGUILayout.TextField("num", numX.ToString()));
		intervalX = int.Parse(EditorGUILayout.TextField("interval", intervalX.ToString()));

		GUILayout.Label("Y : ", EditorStyles.boldLabel);
		numY = int.Parse(EditorGUILayout.TextField("num", numY.ToString()));
		intervalY = int.Parse(EditorGUILayout.TextField("interval", intervalY.ToString()));

		GUILayout.Label("Z : ", EditorStyles.boldLabel);
		numZ = int.Parse(EditorGUILayout.TextField("num", numZ.ToString()));
		intervalZ = int.Parse(EditorGUILayout.TextField("interval", intervalZ.ToString()));

		GUILayout.Label("", EditorStyles.boldLabel);
		if (GUILayout.Button("Create")) Create();
	} catch (System.FormatException) {}
}

通常のOnGUI()でGUIを作るのと同じ要領だが、EditorGUILayoutのパーツが使える。
詳細はリファレンスを参照。
GUILayout
EditorGUILayout

・ウィンドウ表示開始時に選択していたオブジェクトを取得
オブジェクトを取得01

void OnEnable() {
	if (Selection.gameObjects.Length > 0) parent = Selection.gameObjects[0];
}

ウィンドウ表示時に自動で呼ばれるメソッド。
Selection.gameObjects

・ウィンドウ表示後に選択したオブジェクトを反映
オブジェクトを取得02

void OnSelectionChange() {
	if (Selection.gameObjects.Length > 0) prefab = Selection.gameObjects[0];
	Repaint();
}

選択内容の変更時に自動で呼ばれるメソッド。

・「Create」ボタン押下時に実際にブイを作成する

private void Create() {
	if (prefab == null) return;

	int count = 0;
	Vector3 pos;

	pos.x = -(numX - 1) * intervalX / 2;
	for (int x = 0; x < numX; x++) {
		pos.y = -(numY - 1) * intervalY / 2;
		for (int y = 0; y < numY; y++) {
			pos.z = -(numZ - 1) * intervalZ / 2;
			for (int z = 0; z < numZ; z++) {
				GameObject obj = Instantiate(prefab, pos, Quaternion.identity) as GameObject;
				obj.name = prefab.name + count++;
				if (parent) obj.transform.parent = parent.transform;
				Undo.RegisterCreatedObjectUndo(obj, "Create Buoys");

				pos.z += intervalZ;
			}
			pos.y += intervalY;
		}
		pos.x += intervalX;
	}
}

普通にInstantiate()すれば、Hierarchyに追加される。
この際、Undoを設定しておくことにより、後で取り消しができる。(66行目)

Editor拡張を利用したのは初めてだが、食わず嫌いだったと反省している。
Unityで新規にGameObjectを作った時にHierarchyのトップに作成されてしまうというのは、良く聞く不満だが、Editor拡張を使えばその辺りも何とかできそうだと言うのが分かった。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class CreateBuoys : EditorWindow {
	private GameObject parent;
	private GameObject prefab;
	private int numX = 1;
	private int numY = 1;
	private int numZ = 1;
	private float intervalX = 1;
	private float intervalY = 1;
	private float intervalZ = 1;

	[MenuItem("GameObject/Create Other/Create Buoys")]
	static void Init() {
		EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys");
	}

	void OnEnable() {
		if (Selection.gameObjects.Length > 0) parent = Selection.gameObjects[0];
	}

	void OnSelectionChange() {
		if (Selection.gameObjects.Length > 0) prefab = Selection.gameObjects[0];
		Repaint();
	}

	void OnGUI() {
		try {
			parent = EditorGUILayout.ObjectField("Parent", parent, typeof(GameObject), true) as GameObject;
			prefab = EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), true) as GameObject;

			GUILayout.Label("X : ", EditorStyles.boldLabel);
			numX = int.Parse(EditorGUILayout.TextField("num", numX.ToString()));
			intervalX = int.Parse(EditorGUILayout.TextField("interval", intervalX.ToString()));

			GUILayout.Label("Y : ", EditorStyles.boldLabel);
			numY = int.Parse(EditorGUILayout.TextField("num", numY.ToString()));
			intervalY = int.Parse(EditorGUILayout.TextField("interval", intervalY.ToString()));

			GUILayout.Label("Z : ", EditorStyles.boldLabel);
			numZ = int.Parse(EditorGUILayout.TextField("num", numZ.ToString()));
			intervalZ = int.Parse(EditorGUILayout.TextField("interval", intervalZ.ToString()));

			GUILayout.Label("", EditorStyles.boldLabel);
			if (GUILayout.Button("Create")) Create();
		} catch (System.FormatException) {}
	}

	private void Create() {
		if (prefab == null) return;

		int count = 0;
		Vector3 pos;

		pos.x = -(numX - 1) * intervalX / 2;
		for (int x = 0; x < numX; x++) {
			pos.y = -(numY - 1) * intervalY / 2;
			for (int y = 0; y < numY; y++) {
				pos.z = -(numZ - 1) * intervalZ / 2;
				for (int z = 0; z < numZ; z++) {
					GameObject obj = Instantiate(prefab, pos, Quaternion.identity) as GameObject;
					obj.name = prefab.name + count++;
					if (parent) obj.transform.parent = parent.transform;
					Undo.RegisterCreatedObjectUndo(obj, "Create Buoys");

					pos.z += intervalZ;
				}
				pos.y += intervalY;
			}
			pos.x += intervalX;
		}
	}
}

[Mac]:iPhoneのバックアップに失敗する→バックアップの削除方法

※ この方法で何らかの問題が発生しても当方では責任を負いかねますので、各自の責任の元に実行して頂きますようお願い致します。

数日前からiPhoneのバックアップが出来ずに、以下のエラーが表示されるようになりました。

iPhoneバックアップエラー

バックアップはiTunesの「環境設定…」を開き、「デバイス」タブから削除できます。

iPhoneバックアップ削除

バックアップを一旦削除した後は、正常にバックアップを取れるようになりましたが、削除する前に念のため元のバックアップファイルの退避を行っておいた方が良いでしょう。

[Unity]:スクリプトからシェーダの変数にアクセスする

先ほどのエントリで、ノーマルマップのオフセットにアクセスする方法でつまづいたので追記。

using UnityEngine;
using System.Collections;

public class Water : MonoBehaviour {
	void Update() {
		Vector2 offset = renderer.sharedMaterial.mainTextureOffset;
		offset.x = Mathf.Sin(Time.time * 2) * 0.01f;
		offset.y = Mathf.Repeat(offset.y - 0.001f, 1.0f);
		renderer.sharedMaterial.mainTextureOffset = offset;
		renderer.sharedMaterial.SetTextureOffset("_BumpMap", offset);
	}
}

マテリアルInspector

マテリアルのInspectorの「Shader」右側にある「Edit…」を押下。
※ ↑組み込みシェーダの場合。
それ意外の場合はエディタでシェーダのソースが表示される。
組み込みシェーダと同様にInspectorで一覧を確認したい場合は、Inspector右上の歯車アイコンから「Select Shader」を選択。

ShaderInspector

「Properties:」の中に「_BumpMap Texture: Normalmap」の表記があり、ノーマルマップにアクセスするためには「_BumpMap」という変数名を使用すれば良いことが判る。

[Unity]:テキスチャのオフセットを動かして水面の動きを表現する

Standard Assetsに入っているWater(Basic)ではしっくり来なかったので、以下の無料アセットに含まれている水のテキスチャ(ノーマルマップ付き)を利用。

Prototype Textures by Dexsoft Games

PlaneなりCubeなり、水面にしたいオブジェクトを用意して、それに上記アセットに含まれているマテリアル「proto_water」を適用。

proto_water

このままでは動きが無いので、テキスチャのオフセットをずらして動く水面を表現する。
以下のスクリプトを作成して水面オブジェクトにアタッチする。

using UnityEngine;
using System.Collections;

public class Water : MonoBehaviour {
	void Update() {
		Vector2 offset = renderer.sharedMaterial.mainTextureOffset;
		offset.x = Mathf.Sin(Time.time * 2) * 0.01f;
		offset.y = Mathf.Repeat(offset.y - 0.001f, 1.0f);
		renderer.sharedMaterial.mainTextureOffset = offset;
		renderer.sharedMaterial.SetTextureOffset("_BumpMap", offset);
	}
}

マテリアルのテキスチャのオフセットを取得して、x,yそれぞれずらして再セットする。
ノーマルマップの方にも同じオフセットをそのままセット。

xに関しては-0.01〜0.01の範囲を約3.14秒間で往復する。
周期を変えたい場合はMathf.Sin()に与える引数を調整すると良い。
また、揺れ幅を変えたい場合はMathf.Sin()に掛けている0.01fの値を調整すると良い。

yに関しては毎フレーム0.001fずつ手前にずれるようにしてある。
本来ならdeltaTimeを使って調整すべきかもしれない。
必要無いかもしれないが、Mathf.Repeat()を使って、0.0f〜1.0fの間に収まるようにしている。