Unity:Wheel Colliderで車を組み立ててみる

UnityのWheel Colliderコンポーネントを使って車を組み立ててみる実験をしました。公式のCar Tutorialの車は動きが不自然で、実際、スクリプトを見ると妙なことをしているように見えたので(笑)自分で中身を把握できるものを作っておこうという感じで。まだToDoをたくさん残したまま間が開いてしまっていて、ちょっと投げっぱなしなんですが……。

Wheel Colliderは、タイヤのスリップに対する摩擦力と、サスペンションをシミュレートしてくれます。なら、車体に4つタイヤをつけてWheel Colliderくっつければとりあえず完成? と思ってしまうのですが、それだけでは駄目で、旋回すると遠心力でタイヤが浮いてすぐ横転してしまいます。重心をいじったり、ダウンフォースを与えてみたりとしばらく悩んだのですが、車体のロールを抑えるためにスタビライザーを組み込むのがポイントのようです。車に必要不可欠なパーツだったんですね。こうしたことを体験・実感できるのは、物理エンジンに触れる面白さのひとつだと思います。まあ、重要なヒントをもらったところでやっぱり調整は難しいんですけど。

ちなみにいろいろ調べているうちに知ったのですが、本格的な自動車のシミュレーションのアセットがあったりします。CPUカーのAIのアセットもあるようです。

Unityは、せっかく頑張っても「それアセットでできるよ」となって不貞寝してしまうようなことが結構……。

バックミラーの左右反転描画はこんな感じで投影行列とポリゴンを反転してます。

using UnityEngine;

public class MirrorCamera : MonoBehaviour
{
	void Start()
	{
		camera.projectionMatrix *= Matrix4x4.Scale(new Vector3(-1, 1, 1));
	}

	void OnPreRender()
	{
		GL.SetRevertBackfacing(true);
	}

	void OnPostRender()
	{
		GL.SetRevertBackfacing(false);
	}
}

コースの生成にはEasyRoads3Dを使用しています。Terrain上に引いたスプラインパスから道路や縁石、ガードレールなどのメッシュを生成し、Terrainをそれに合うようにならしてくれます。便利ですが、UIなどやや癖のあるアセットです。

道路とTerrainがZファイティングを起こすのを回避するため、EasyRoads3D付属の道路描画用のシェーダにはOffsetの設定が入っています。が、調整が難しいのでオフにして、手っ取り早く道路の高さを7.5cmほど上げて対処しています。そのため、段差で車体が跳ねてしまってたり……。

まだいろいろと課題が多そうですが、Unityのおかげでこういったことをわりと手軽に試せるようになったのは嬉しいですね。

2012年5月16日

Blenderの導入ガイドを書いてみた

Blender 2.60が出ましたね。最近更新ペースが速いです。12月のバージョン2.61では新しいレンダリングエンジンも入るとか。

で、つい勢いでBlender基礎最速マスターというものを書いてみました。タイトルは一時期人気になったプログラミング言語の基礎文法最速マスターシリーズにあやかっています。

Blenderはフルスペックのフリーの3DCGソフトウェアです。Unityとの連携で用いられることも増えていると思うのですが、癖がとても強いことでも知られています。できるだけ短時間でハマりポイントのうち大きなものをカバーできるようにしてみたつもりなんですが、どうでしょうねー。

ブログではなく通常のページにしたのは、また結構長文になってしまったのと、最近ウェブで検索したときに古い情報が出てくることが多い気がするので(数ヶ月前に書いたAIRのエントリももう時代遅れになってしまいましたし……)、固定ページとしてメンテナンスしたほうがいいんじゃないかと。

いざ久しぶりにページを作るとなると、URLを考えるのに困ったりするんですけどね。思い切ってunityというディレクトリを作ったので、そんな感じのものを上げていければいいなと思ってます。

近況としてはわりとUnity漬けですが、Flash 11も気になるところです。

2011年10月22日

Unityで1週間でミニゲームを作ってみた

最近なにかと話題のゲームエンジンUnity。ずっと前から気になっていてちょくちょくいじってはいたんですが、実際にゲームを作ってみたことはありませんでした。そんなところにIGDA日本のUnityのセミナーがありまして、面白そうな内容でぜひ行こうと思ったんですが、Unityをろくに触らないまま遠路出かけるのはもったいないということで、急遽ゲームを1本でっちあげてみました。

ゲームはこちらです。要求スペックはそこそこ高めです。

ちなみにセミナーの内容については「強火で進め」さんのブログにまとめられています。

以下、メイキング的なメモ書きです。

今回、時間がなかったこともあって、「リソースは自作しない」というのを絶対方針にしました。UnityにはAsset Storeという公式の素材マーケットがあるので、主にそこからフリーのものをいろいろ見つくろってきています。背景やトゲ付き鉄球など、全部既製の素材です。

また、プレイヤーキャラクターはMixamoを利用しました。有料ですが、3Dの人体モデルを作成したり、自作のモデルをアップロードしてモーションをつけたりできるサービスです。今回は、Mixamo本家で3Dモデルを作成して$10で購入し、Unityのプラグイン(Mixamo Animation Store)から歩きモーションと立ちモーションをそれぞれ$10で購入しました。しめて$30です。モデリングもモーション付けも自分でやるとものすごく大変な作業ですから、とりあえずこうして解決してしまえるのはとてもありがたいです。

ゲームデザインについてですが、作る直前にUnity上でラグドールの実験をしてまして、ラグドールが吹っ飛ぶのは面白いよねということで(ゲームのバグ動画などでよくありますよね)いろいろ物を投げ込んでぶつけて吹っ飛ばしてやろうと。物理エンジンも活用できるし、見栄えがするのでいいかなと思いました。

吹き飛ぶときの爆発はDetonator Explosion Frameworkを使用しています。Prefabをスクリプト1行で生成するだけで派手な爆発をしてくれるアセットです。ゲームの内容的にはなぜ爆発するんだって感じですが(笑)、一度使ってみたかったので。まあ、特撮などでも意味もなく爆発しますし。このキャラがそういう体質なんでしょう、きっと。

避けゲーということで安直にAvoiderとタイトル決定。たぶん主人公の名前か何かでしょう。タイトル画面は、Adobe Fireworksで適当にプリセットのスタイルを適用して字詰めしてロゴを作成、プレイヤーキャラを配置してライトを当ててできあがり。ちょっとでも動きがないと寂しいので、トゥイーンライブラリのiTweenを使って、ライトのフェードインとロゴの明滅をさせています。ちなみにこのトゥイーンライブラリというのはFlashの世界で生まれて発展してきたもので、簡単に言うと「動き」をライブラリ化したものです。コード1行でオブジェクトをアニメーションさせたりできます。UnityにもFlashのタイムラインのようなアニメーション機能があるのですが、Flash同様、トゥイーンライブラリのほうが楽に動きをつけられる場面が往々にしてあります。iTweenのサイトのExamplesに印象的なデモがたくさんあります。

ただ避けて生き延びるだけではゲームとして単純すぎると思ったので、宝石を取るとスコアが加算されるようにしました。ゲームはプレイヤーに2つ以上のことを同時にさせるといいそうです(参考:社長が訊く『New スーパーマリオブラザーズ Wii』)。というか、Asset StoreにGem Shaderというシェーダがあって、綺麗な宝石モデルも入っていたので使ってみたというのが本当のところで、順序が逆かもしれません。ちなみにシェーダの中身をまったく見てません。ブラックボックスのまま使って(使えて)しまっています。

宝石を取ったときにエフェクトがないと取った感が出ないのでどうしようかと思ったんですが、標準アセットのParticlesパッケージの中にあったFireworksをもうこれでいいやとそのまま使用。あとは、せっかくなのでコンボボーナスをつけたり、宝石を2種類にしてスコアを変えたりしてよりゲームっぽくしてみました。なお、時間が経つと宝石が小さくなって消えるのもiTweenを使ってます。

難易度調整、実はこれが一番時間がかかったかもしれないんですが、NORMALモードとHARDモードを用意するという逃げに走りました。あとカメラアングルは最初もっと低かったんですが、フィールドの上のほうがオブジェクトに隠れて見づらくなり、下のほうばかりをうろうろするプレイになってしまって、あまりに不自然だったので、ほとんどトップビューにしてしまいました。せっかくのキャラクターがよく見えなくなってしまうので躊躇したんですが仕方なしです。タイトル画面のアップでカバーする感じですかね(笑)。それと、プレイヤーキャラの当たり判定は、胴体より下だけにしてあります。

BGMは、一応DTMerなので本来なら自作するべきところなんですが、CubaseのLoopMashのプリセットをちょっといじって書き出してシーンに配置して終わり。宝石を取った効果音もCubase付属音源から適当な音色を探してきて使用。オブジェクトが跳ね返る音は効果音素材CDからです。

と、こんな感じで作ったんですが、ゲーム内容的にも制作工程的にも相当横着してますね。とりあえず見ていただいた方々に面白いと言っていただけたので(ありがとうございます!)いいのかな?と思います。既存のアセットを使いまくってるのはバレバレでしたけど(笑)。

*

さて、こうして短期間で1本でっちあげてみて、Unityのことが以前よりもずっとよくわかったというか、正直、過小評価してたような気がしてきました。すごいゲームエンジンだとは認識していたんですが、ゲームエンジン本体よりも、周辺の文化圏こそが真価だったんじゃないかと。今回、他人のアセットの力を素直に借りようと最初に決断していたとはいえ、素材を探してぽんぽん放りこんで、ちょこちょことスクリプトを書き足すだけで、あっという間にそれっぽいものが組み上がっていくのは、いいんだろうか本当にって感じでした。ほとんどチートのような気もするんですが、実はこれが本来の使い方(のひとつ)なのかもしれません。セミナーの講演の中でもレゴブロック的という話があったように記憶しています。

実のところ、現状のAsset Storeはサーバがかなり重かったりして、お世辞にも使いやすいとは言えないのですが(公式フォーラムのこのスレッドで、Unityの中の人いわくなんとかするそうです)、そのあたりが解決されてアセットの数が増えれば、さらにとんでもないことになりそうな予感がします(なお、ゲームに使用可能な3D素材については他にもTurboSquidなどがあります)。

あと、スクリプトについても、まだ慣れていなくてAPIがあまり身についていないのですが、何か引っかかったらUnity Answersフォーラムで検索すればすぐ答えが出てきましたし、そもそも今回自分で書いたJavaScriptコードが全部で600行ほどしかありません。流暢に書けなくてもどうにでもなる量です。

このスピードとこのコード量で、1人で、仮にも物理エンジンが走り、人体モデルが動くような3Dゲームをでっちあげられるというのは、今までは考えられなかったことなんじゃないでしょうか。3Dゲームを作れるのはナムコとセガくらいみたいな時代もあったと思うんですが、すごいカジュアル化です。Unityが言うには「民主化」とのことですが、ある意味、言い訳ができなくなりつつあるようで、ちょっと怖いなと……まあ、一方で、リソース作りが大変なのは基本的に変わらないとも思うんですけどね。

こうした至れり尽くせりのゲームエンジンに乗っかれるかというのは、特にプログラミングをする人にとって心情的に難しいところもあると思うんですが、自分の場合はどうもメリットのほうが大きそうなので、本気で乗っかってみようかなと考えはじめてます。

とりあえず、もう1本くらい実験作を作ってみたいですね。もうちょっとシーンやスクリプト的に凝った何か。

2011年7月19日

Flashでジョイスティックを使う実験

Flashはブラウザゲームのすぐれたプラットフォームですが、ジョイスティックが使えないのが欠点のひとつです。一方、新興のゲームプラットフォームにUnityがあります。こちらのプラグインはまだほとんど普及していませんが、ジョイスティックに対応しています。

FlashにもUnityにも、それぞれブラウザのJavaScriptとの連携手段が用意されています。それなら、Unityからブラウザを介してFlashにジョイスティックの状態を送れば、Flashでもジョイスティックが使えるんじゃ? と思いついたので、実際にやってみました。

ジョイスティックの状態を送る一連のプロセスについて。まずUnity側からです。Unityでは、物理デバイスの入力を整理・抽象化して、それぞれ名前をつけて扱うようになっています。メニューの Edit > Project Settings > Input にその設定があります。今回はひとまず生データを送りたいので、レバーの軸とボタンを一通り登録します。レバーのデッドゾーン(Dead)もゼロにしておきます。

気分の問題ですが、遅延を少なくするために、Unityの最大フレームレートを上げておきます。

function Awake()
{
	Application.targetFrameRate = 300;
}

次に、ジョイスティックの状態の変化をブラウザに毎フレーム送信します。ブラウザとの通信についてはUnity Web Player and browser communicationに説明があります。Application.ExternalCall()でHTML内のJavaScriptを呼び出すことができます。実際のスクリプトとしては以下のようになります。

var axes = new float[9];

function Update()
{
	for (var i = 1; i <9; i++) {
		var position = Input.GetAxis("Axis" + i);
		if (axes[i] != position) {
			axes[i] = position;
			turnOnLed();
			Application.ExternalCall("axisHandler", i, position);
		}
	}

	for (i = 0; i < 16; i++) {
		var buttonName = "Button" + i;
		if (Input.GetButtonDown(buttonName)) {
			turnOnLed();
			Application.ExternalCall("buttonHandler", i, 1);
		}
		if (Input.GetButtonUp(buttonName)) {
			turnOnLed();
			Application.ExternalCall("buttonHandler", i, 0);
		}
	}
}

ちょっとしたテスト兼演出として、関数を呼び出したときにLEDが点灯するようにしました。

function turnOnLed()
{
	GameObject.Find("Led").SendMessage("TurnOn");
}

次にHTMLです。Flashの埋め込みにはSWFObject 2.2を使用しています。Unityから呼び出されたJavaScriptの関数axisHandlerとbuttonHandlerが、ExternalInterfaceで登録したActionScriptの関数を呼び出します。

var attributes = {
	id:"Flash"
};
swfobject.embedSWF("Game.swf", ...);
...
<script type="text/javascript">
	function axisHandler(no, position) {
		document.getElementById("Flash").axisHandler(no, position);
	}

	function buttonHandler(no, state) {
		document.getElementById("Flash").buttonHandler(no, state);
	}
</script>

ExternalInterfaceは、ローカルファイルシステムではセキュリティのため動作しません。ローカルにApacheを立ててそこでテストするのが簡単です。また、ExternalInterfaceはいろいろ罠があって、ちょっとしたことで動かなくなることが多いようです。今回もそれでなぜかIEでだけ動かず、しばらくはまったんですが、教訓としては、SWFObjectのtest suiteから始めるのがいいと思います。

ここの”Browser communication test page”がブラウザとの通信のサンプルです。test suiteというだけあって動くことが保証されていますので、このサンプルがサーバ上できちんと動くことを確かめてから少しずつ書き換えていくのがいいんじゃないでしょうか。

Flash側では、以下のようなクラスを作ってコールバック関数を登録し、メッセージを受け付けます。

package
{
	import flash.external.ExternalInterface;

	public class Joystick
	{
		public static const AXIS_MAX:int = 9;
		public static const BUTTON_MAX:int = 16;

		private var axes:Vector.<Number> = new Vector.<Number>(AXIS_MAX);
		private var buttons:Vector.<int> = new Vector.<int>(BUTTON_MAX);

		function Joystick()
		{
			if (ExternalInterface.available) {
				ExternalInterface.addCallback("axisHandler", axisHandler);
				ExternalInterface.addCallback("buttonHandler", buttonHandler);
			}
		}

		public function axisHandler(no:int, position:Number):void
		{
			axes[no] = position;
		}

		public function buttonHandler(no:int, state:int):void
		{
			buttons[no] = state;
		}

		public function getAxis(no:int):Number
		{
			return axes[no];
		}

		public function isButtonPressed(no:int):Boolean
		{
			return (buttons[no] == 1);
		}
	}
}

これでUnityからFlashまでジョイスティックの状態が伝わるようになりました。

ちなみに、いったんここまで作った後で、逆の方法に思い当たりました。つまり、UnityからFlashにジョイスティックの状態を流し込むのではなく、FlashからUnityにジョイスティックの状態を問い合わせるやり方です。もしこちらができれば、通信量も間違いも少なく好ましいでしょう。が、ブラウザからUnityの関数をSendMessageで呼び出したときに、戻り値が取得できないので断念しました。

さて、これで、「Unityのプラグインがインストールされていればジョイスティックでも操作できるFlash」ができました。アクロバティックなやり方ですが、信じがたいことにIE8、Firefox 3.6、Google Chrome 4それぞれでXbox360コントローラなどを使って普通に操作できるようです。特にアナログレバーを動かすとかなり頻繁にメッセージが送られるのでもつだろうかと心配だったんですが、レスポンスは悪くないし、CPU使用率もたいして上がりません。作る前はデータの間引きなども考えていたんですが、必要なさそうなのでやっていません。

で、実用性はどうかというと、微妙かなと思ってます。まず、ブラウザゲームでジョイスティックを使う文化がないですよね。それに、ジョイスティックを使いたい人はすでにJoyToKeyなどを使っているはずです。アナログレバーが使えるという違いはありますが、Flashゲームはまずマウスとキーボードで遊べるように作りますから、ゲームにアナログレバーの必要な操作を持ち込むわけにはいかないでしょう。そもそも、ジョイスティックを使うゲームなら最初からFlashではなくUnityで作れよという気もします。

あと、1つのウェブページに2つの標準でない技術を使うことになるので、それなりにリスクがありそうです。Unityの出力するHTMLファイルも結構複雑ですし(ちなみにコメントで説明が書かれているので読んでおくのがおすすめです)、環境によっては不具合が発生するかもしれません。導入には結構ためらうものがあります。

もしUnityのプラグインがある程度普及すれば(20%くらい?)メリットのほうが大きくなるかもしれません。また、いずれにせよFlashでジョイスティックを使う方法ができたということで、ウェブではなく、どこかへの展示用のFlashなどでしたら役に立つかもしれません。

……ということで、どうもひたすら微妙な感じ。こんな変態的なことをさせる前に、そもそもAdobeがFlashでジョイスティックをサポートしてくれたらいいんですけどね……。ゲーム志向にするとか考えてるならその辺検討してほしいなあ。

2010年2月10日

Unityでパーティクルの実験

最近Unityをいじくったりしてます。

Unityの簡単な説明をすると、米Unity Technologies社製のゲームエンジンで、PCやブラウザ、iPhone等で動く3Dアプリケーションを作ることができます。GUI環境に3Dモデル等の素材を放り込んで、JavaScriptやC#でスクリプトを書くだけで(割と)簡単に動かせます。Flashを触っている方は、Flash IDEの3D版だと考えればだいたい感覚的にあってるかも。ブラウザ上でGPUアクセラレーションつきの本格的な3Dが動くこと、iPhoneの3Dゲームが比較的手軽に作れること、開発環境の制限バージョンが無料になったことなどで人気が出ているようです(残念ながらまだ日本語化されていないのですが)。ブラウザのプラグインをインストールして公式のデモを見ると、かなりのものが動くのが分かると思います。

というところで、唐突ですが、ABAさんのパーティクルテストにつられて、似たような、オブジェクトをたくさん出す実験をしてみました。

UnityParticle500

プロジェクトファイルはこちら

おそらく個々のGameObjectがパーティクルを描画するのはGPU的に嫌だろうということで、GameObjectは座標を保持するだけにして、下のようなスクリプトでビルボード群のメッシュを毎フレーム作成してまとめて描画してみました。とりあえずCore 2 Duo/3.16GHz、RADEON 4550で10000個は出ているようです(上限を上げればもっと出るかもしれません)。UnityのJavaScriptは.NET(Mono)ベースで、C++の半分の速度は出るよとのことなんですが、確かに結構な速さはあるようです。

function LateUpdate()
{
	var particles = GameObject.FindGameObjectsWithTag("Particle");
	...

	var i = 0;
	var size = 0.5;
	for (var particle in particles) {
		var position = particle.transform.position;
		vertices[i++] = Vector3(position.x - size, position.y - size, position.z);
		vertices[i++] = Vector3(position.x - size, position.y + size, position.z);
		vertices[i++] = Vector3(position.x + size, position.y + size, position.z);
		vertices[i++] = Vector3(position.x + size, position.y - size, position.z);
	}
	...

	var mesh = GetComponent(MeshFilter).mesh;
	mesh.Clear();
	mesh.vertices = vertices;
	mesh.colors = colors;
	mesh.uv = uv;
	mesh.triangles = triangles;
}

まあ、パーティクルを多数出すだけならUnity組み込みのパーティクルシステムを使えばいいんですが、シューティングの敵弾などで自前でやる必要が出てくることもあるかなと。

ちなみにUnityのいいところは、開発環境がそのまま実行・デバッグ環境になることですね。下の画面のように、実行中に視点を動かして、描画が正しく行われているか確認したりというのが、何の苦労もなくできます。

UnityIDE500

Unityというプラットフォームがどんな感じかについてはまた後ほど(たぶん)。

2010年1月5日

トップページ
プロフィール

はてなブックマーク
wonderfl