2014年1月1日水曜日

Unity 試してみた

マルチプラットホームのゲームエンジン、Unity を使ってみました。最近は Unity を使ってゲーム開発をしている人が非常〜に多いです。

というわけでインストールしてチュートリアルをやってみるも、画面の操作が思い通りにいかない。。。ので。

ドットインストールの Unity入門 (全26回) をやってみる。Shift + drag とかcommand + drag とかalt + drag とかcontrol + drag とか Align with view とかみやすくするための技を先に教えてくれるのでありがたい。

Script Reference

スクリプトの簡単な使い方
#pragma strict

// How to use var. 

var x : int = 5;

// display message. Not Console.log but "Debug.Log".

function Start () {
  Debug.Log("Hello World ->" + x);
}

function Update () {
  // 3 ways to transform:
  // transform.position.z += 0.1;
  // transform.position += Vector3(0,0,0.1);
  // transform.Translate(0,0,0.1);
}

プレイヤーを動かす
#pragma strict

function Update () {

//controling objects
 if (Input.GetButtonUp("Jump")){
  Debug.Log("Jumped!");
 }

//moving objects
var x : float = Input.GetAxis("Horizontal");
transform.Translate(x * 0.2, 0,0);
}

// checking collision
function OnCollisionEnter(obj : Collision){
  if (obj.gameObject.name == "RightWall"){
      Debug.Log("Hit R!");
  }
}

スペースキークリックでボールを出し、10個超えたらゲームオーバー
#pragma strict

var ball : Transform;
var n : int = 0;

function Update () {

//controlling objects
 if (Input.GetButtonUp("Jump")){
  Instantiate(ball,transform.position, transform.rotation);
  n++;
 }

//display game over message after throwing 10 balls
 if (n > 10){
  Application.LoadLevel("GameOver");
 }
}

で、ゲームを作る。

プレイヤーを左右に動かすだけのサンプルゲームをそのまま作ってみる。 Player のスクリプト。敵を捕まえる・敵を捕まえると小さくなっていく(難易度が上がっていく)。
#pragma strict

// catch the enemy
function Update () {
  var x : float = Input.GetAxis("Horizontal");
  transform.Translate(x * 0.2,0,0);
}

// player becomes smaller after catching enemy
function OnCollisionEnter(obj : Collision){
  if (obj.gameObject.name == "Enemy(Clone)"){
    transform.localScale.x -= Random.Range(0.1,0.5);
    if (transform.localScale.x < 1.0) transform.localScale.x = 1.0;
  }
}

敵を回転しながら発射させる。敵はプレイヤーが捕まえたら消える。
#pragma strict

// Enemy flies toward the player rotating
function Update () {
  transform.position.z -= 0.1;
  transform.Rotate(1,1,1);
  // Game over when enemy is not caught
  if (transform.position.z < -12.0){
    Application.LoadLevel("GameOver");
  }
}
// Delete enemy when player catches it
function OnCollisionEnter(){
  Destroy(gameObject);
}


敵を60フレームごとに発射させる。
#pragma strict

var enemy : Transform;

//Enemy every 60 frame

function Update(){
 if (Time.frameCount % 60 == 0){
  Instantiate(enemy,Vector3(Random.Range(-5,5.0),1,8), transform.rotation);
 }

}


四角い箱(敵)が回転しながら降ってくるのを食い止めるだけのゲーム(チュートリアルそのまま)。テクスチャはたまたまその日に撮ったバラの花でした(適当)当たり判定してゲームオーバーまで出すようになっています。Mac用のアプリとしてエクスポートしてみた。



はじめての Unity: 第一回 世界の「骨格」をつくろう

ゆっくりがコロコロ転がってくるのを左右に移動しながらブロックするだけのゲーム



ボールをコントロール
#pragma strict

var Speed = 20.0;
function Start () {
  rigidbody.AddForce(
    (transform.forward + transform.right ) * Speed, ForceMode.VelocityChange );
}



ラケットをコントロール
#pragma strict

var Accel = 1000.0;
function Update () {
  rigidbody.AddForce(
    transform.right * Input.GetAxisRaw("Horizontal") * Accel, 
    ForceMode.Impulse
    );
}
NaCLとMac用とWeb Player 用とエクスポートしてみた。それほどゆっくりを転がす需要があるとも思えないけど、とりあえず公開しておきます。

Tutorial ブロック崩しをやってみた@ゆっくりしていってね!:

WebApp: https://googledrive.com/host/0B39ZuZLxj12aTXIyU2FzbWVJQ1E/yukkuriweb.html

Asset Store を使ってみる・キャラクターを設定してみる

次は何をやろうかなーと思って、Unity Asset Store を見にいってみる。無料のキャラクターのところでこんなのを発見。韓国の Unity Asset Store Contest 2012 で入賞している作品らしい。

Dokebimusa - Ghost Warrior


とりあえず入れてみる。"Includes animation (idle, run)" と書いてあるので、多分走ったり止まったりは何も作らなくても使えるのではないかしら。

。。。

おおー細かく作ってある!さすが。idle 状態のアニメスクリプトも書かれているので、立っているだけでもゲームとかでキャラクターがうにうに動いているあの状態になる。



。。。と思ったら10ドルぐらいでもっとすごい色々なキャラクターとアニメーションが手に入っちゃうことが判明。なるほどー。
【Unity 入門】ダンジョンゲーを作ってみたい 第二回「キャラクターをダンジョン内で動かす」デモサイト

とりあえず初期設定。スクリプトを全部設定して、カメラとライトを設定。

プレビューしてみると。。。落ちる。そりゃそうだ、重力つけてるし。床を作って、カプセルの長さを床の上に来るように設定して解決。



キャラクターを走らせる、ジャンプさせる、回転させるなどの動きはできるようになったのですが、結構速いスピードで動かすので思わず床からはみ出て落ちちゃう!ので、壁を作ってみた。これでグィングィン走らせても落ちなくなりました。


デバイスオリエンテーションを入力に使った Android アプリを作ってみる

もう一つ、こんなの見つけてしまいました。

NASA Space Flight Assets

宇宙を遊泳するだけの空間が作れますな。透明な壁で金魚鉢みたいに囲って、その中をぷよぷよ遊泳していくと、色々なシャトルとか星とかがあって、触ると解説の音声が流れる的な。(音声データもパッケージされてる)さっそくダウンロード&インストール!なんかめちゃめちゃ重くて時間がかかる。。。

様々な宇宙関連の物体(スペースシャトルやISSなど)をばら撒き、宇宙飛行士の一人にカメラとライトを子供でつけて、マウス入力で宇宙飛行士を動かせるようにし、動くと周りに色々な物体が見えるようにしました。遠くまで行って戻ってこれなくなっても困るので、透明なsphereで壁を作りました。宇宙のテクスチャでキレイにやれたらもっといいですねえ。

ちなみに今回使ったのは、宇宙飛行士・アポロソユーズ・EMU(Extravehicular Mobility Unit、船外機動ユニット)・freedom7・Gemini・グリースガン(宇宙飛行士用の器具)・ISS・Jupiterc(ロケット)・Lunarlander(月面着陸船)・SaturnV(ロケット)・SkyLabなどです。

Unity

で、いざ入力。あれ?設定が Horizontal と Vertical しか出ない。。せっかく前後左右上下にオブジェクトを置いて泳げるようにしたのに!/(^o^)\まあでもマウスは上下左右しか動けないからな。。。

というわけでちょこっと調べたら Android アプリにしちゃえば、加速度センサーで3軸のデータを取れることに気づいたので今回は Android アプリにすることに。

加速度センサーでの3軸の取り方

#pragma strict

var speed = 10.0;
function Update () {
  var dir : Vector3 = Vector3.zero;

  // デバイスが地面に平行と仮定
  // し、ホームボタンが右側

  // デバイスの加速度軸をゲームの座標にあわせて再マッピング:
  //  1) デバイスの XY 平面をXZ平面にマッピング
  //  2) Y軸の周りに90度回転
  dir.x = -Input.acceleration.y;
  dir.z = Input.acceleration.x;

  // 球の単位に加速度ベクトルの値を Clamp
  if (dir.sqrMagnitude > 1)
    dir.Normalize();

  // 10メートル毎秒で移動(1フレームあたりでなく)
  dir *= Time.deltaTime;

  // オブジェクトを移動
  transform.Translate (dir * speed);
}


Nexus4 では開発したことがなかったので USB デバッグモードが表示されない。ビルド番号を連打して開発者モードに変更し、USB デバックを ON にする。 Android SDK はインストールしてあったので接続して "adb devices" と打ってみたらデバイスが一個表示されたので大丈夫そう。

Platform を Android に変更。(Assetが多すぎるのでめっちゃ時間がかかる。。。) ビルドしてみたら、来た!画面が FumiNASA アプリに。ただ、オブジェクトをもうちょっと密集させないと孤独に宇宙を漂いまくってやっとオブジェクトに辿り着くっていう寂しいアプリに。いずれにせよ加速度センサーで orientation を取るのはできたみたい。

Sphere を小さくして、オブジェクトを全部密集させて、再度ビルドしてみる。あれ、エラーが出る。"Error building Player: UnityException: Unable to install APK!"うーん、Nexus4 は今ストレージがパンパンなのでそのせいかもしれない。

Nexus5 につなぎかえてビルドしてみる。お、今度はちゃんと来た。 Android を傾けると、宇宙飛行士とか ISS とかスペースシャトルとかがふよふよ浮いてくる。うはは。満足!音声はまた今度でいいや。

Unity

Unity

Unity

MikuMikuDance を躍らせる

そういえば主婦ゆにで Miku Miku Dance を踊らせてたなーと思って、それもやってみることにしました。

・モデルLat 式ミクさんをダウンロードし、Unityで読み込む。Latさん、ありがとうございます!!ミクのモデルデータリスト
変換ソフトMMD4Mecanim をダウンロードし、Unityで読み込む。Noraさん、ありがとうございます!!
モーションデータこちらの動画WAVEFILE モーションデータをダウンロードし、Unityで読み込む。Hinoさん、ありがとうございます!!モーションデータリスト

MMD4Mecanimファイルのvmdの項目にモーションデータを入れて、Processを実行。
できた!ライト設置&カメラの位置調整。


Animator Controller にダンスを設定。再生!Unityの中でミクさんが踊りました!


音楽の設定。Animタブのaudio clipの設定に歌を入れる。音源はラマーズPさんからお借りしました。ありがとうございます! ミクさんが歌って踊れるようになりました!

次は舞台&背景ですね。。。Unity Asset Store を物色。ステージじゃないけどちょっとかっこいいステージ風の背景を発見。"Sci-Fi Level" Mixed Dimensions さん、ありがとうございます!お借りします!借りてばっかだな、俺!


ついにミクさんがステージに!!


こうして皆さんから素材をお借りして、ついにノンプログラマーな私の初めての Unity のプロジェクトを公開してみました!

作品その1: WAVEFILE/初音ミク fullver.をLat式ミクさんに踊ってもらったのを Unity で見られるようにしてみた。

WebGame: https://googledrive.com/host/0B39ZuZLxj12aLXd2dlZ1UjQ3bWc/build.html

矢印キーで前後左右に動き、ミクさんに近づいたり遠のいたりすることができます。音声はぜひステレオで聞いてみてください。マウスでも動けますが、動けすぎます(笑)でも自由に動きたい方はマウスでもジョイスティックでもどうぞ :)


作品その2: WAVEFILE/初音ミク fullver.をLat式ミクさんに踊ってもらったのを Android アプリにしてみた。

Google Play: https://play.google.com/store/apps/details?id=com.fumi.MMDWavefile

作品その1 で公開したのと同じ歌って踊れるミクさんですが、ステージとか動きとかを設定する前の物を Android アプリとしてエクスポートしてみました。いつでもどこにいてもミクさんが歌って踊ってくれます。こちらのアプリはカメラを動かせませんのであしからず。Skybox を使って青空を取り込み、健康的なミクさんのステージにしてみました。きゃわわ!

miku

あと、Unity で Android アプリを作ると終了ボタンが存在しない問題が発生するので、こちらのブログを参照して戻るボタンで終了できるように下記スクリプトを追加しました。


function Update () {
 if (Application.platform == RuntimePlatform.Android)
 {
  if (Input.GetKey(KeyCode.Home) || Input.GetKey(KeyCode.Escape) || Input.GetKey(KeyCode.Menu))
  {
   Application.Quit();
   return;
  }
 }
}



Leap Flying!

次は何を作ろうかなーと思ってウロウロしてたら、Leap Flying というのを見つけてしまいました。Leap Motion をコントローラとして、両手を動かすことでフライトコントロールをするゲームを Unity で作れちゃうらしい。正直、Leap Motion はテルミンを作って以来一度も使っていないので、これは埃を払うチャンス。

というわけでダウンロードして、Leap Motion をつないで、アプリを動かしてみる。おー、両手で動かして飛んでる飛んでる!さっそくプロジェクトファイルとアセットをUnity で読み込んでみる。ただこれ、ビルの間を飛ぶだけなんですよね。。。どうせなら戦いたい。

というわけで戦闘機を飛ばして、敵が飛んでくるのを撃ち落とすとかそういう感じにしようかと。挙動的には DotInstall で書いたスクリプトが使えるはず。

ちょうどいい感じのネタが Asset Store にありました。Duane's Mindさん、ありがとうございます!

戦闘機:Space Ship Shooter


敵:Alien and Cocoon

今度のステージも前回と同じMixed Dimensions さんの、今度はもうちょっと通路が多いので戦闘機を飛ばしやすそうな "Sci-Fi level 2" をお借りしてみたいと思います。



ステージはこんな感じで、Camera は通路を捉えるとして、そこに spaceship を配置。エイリアンを迎撃する。



ここで一旦 Leap Flying に戻り、Unity 側で動かしてみようとするが動かない。Read me には飛んでる画面のスクリーンショットしかない(><)どうやって入力すんのーと思って色々調べてみる。

スクリプトの中に "LeapUnityExtensions"が。これか

あ、動かすやつあった。LeapFly.

using UnityEngine;
using System.Collections;
using Leap;

public class LeapFly : MonoBehaviour {
  
  Controller m_leapController;
  
  // Use this for initialization
  void Start () {
    m_leapController = new Controller();
    if (transform.parent == null) {
      Debug.LogError("LeapFly must have a parent object to control"); 
    }
  }
  
  Hand GetLeftMostHand(Frame f) {
    float smallestVal = float.MaxValue;
    Hand h = null;
    for(int i = 0; i < f.Hands.Count; ++i) {
      if (f.Hands[i].PalmPosition.ToUnity().x < smallestVal) {
        smallestVal = f.Hands[i].PalmPosition.ToUnity().x;
        h = f.Hands[i];
      }
    }
    return h;
  }
  
  Hand GetRightMostHand(Frame f) {
    float largestVal = -float.MaxValue;
    Hand h = null;
    for(int i = 0; i < f.Hands.Count; ++i) {
      if (f.Hands[i].PalmPosition.ToUnity().x > largestVal) {
        largestVal = f.Hands[i].PalmPosition.ToUnity().x;
        h = f.Hands[i];
      }
    }
    return h;
  }
  
  void FixedUpdate () {
    
    Frame frame = m_leapController.Frame();
  
    if (frame.Hands.Count >= 2) {
      Hand leftHand = GetLeftMostHand(frame);
      Hand rightHand = GetRightMostHand(frame);
      
      // takes the average vector of the forward vector of the hands, used for the
      // pitch of the plane.
      Vector3 avgPalmForward = (frame.Hands[0].Direction.ToUnity() + frame.Hands[1].Direction.ToUnity()) * 0.5f;
      
      Vector3 handDiff = leftHand.PalmPosition.ToUnityScaled() - rightHand.PalmPosition.ToUnityScaled();
      
      Vector3 newRot = transform.parent.localRotation.eulerAngles;
      newRot.z = -handDiff.y * 20.0f;
      
      // adding the rot.z as a way to use banking (rolling) to turn.
      newRot.y += handDiff.z * 3.0f - newRot.z * 0.03f * transform.parent.rigidbody.velocity.magnitude;
      newRot.x = -(avgPalmForward.y - 0.1f) * 100.0f;

      float forceMult = 10.0f;
      
      // if closed fist, then stop the plane and slowly go backwards.
      if (frame.Fingers.Count < 3) {
        forceMult = -3.0f;
      }
      
      transform.parent.localRotation = Quaternion.Slerp(transform.parent.localRotation, Quaternion.Euler(newRot), 0.1f);
      transform.parent.rigidbody.velocity = transform.parent.forward * forceMult;
    }
  } 
}

これは飛ぶだけだから両手を飛ぶのに使っていいけど、私が作りたいのは戦闘シーンだから、片手は飛ぶ操作でもう片手はスペースキーあたりで敵を撃たないといけないなあ。

とか考えつつ色々やってみるのですが動きません。これでは眠れません。目が真っ赤です。とりあえず寝よう。

一旦地に足を戻して Leap Motion で車を動かすチュートリアルをやってみる。

Leap Enabling the Unity3D Car Tutorial

必要アイテム:
Car Tutorial
Leap Motion Starter Kit Manual for Unity
Pierre Semaan 先生が書いたチュートリアル用スクリプト"LeapEnabledBootCamp-master"
Leap SDK

このチュートリアルで最終的に出来上がるはずのものはこちら。




Leap の SDK から色々なファイルをコピペして Unity にもってくる。そもそもこの作業が必要だったのね。そりゃそうか。。詳細は上記チュートリアルをご参照。長いので。

"GetHandAxisPrivate" これか!
private static float GetHandAxisPrivate(string axisName, bool scaled)
{
// Call Update so you can get the latest frame and hand
Update();
float ret = 0.0F;
if (m_Hand != null)
{
  Vector3 PalmPosition = new Vector3(0,0,0);
  Vector3 PalmNormal = new Vector3(0,0,0);
  Vector3 PalmDirection = new Vector3(0,0,0);
  if (scaled == true)
  {
    PalmPosition = m_Hand.PalmPosition.ToUnityTranslated();
    PalmNormal = m_Hand.PalmNormal.ToUnity();
    PalmDirection = m_Hand.Direction.ToUnity();
  }
  else
  {
    PalmPosition = m_Hand.PalmPosition.ToUnity();
    PalmNormal = m_Hand.PalmPosition.ToUnity();
    PalmDirection = m_Hand.Direction.ToUnity();
  }
  switch (axisName)
  {
  case "Horizontal":
    ret = PalmPosition.x ;
    break;
  case "Vertical":
    ret = PalmPosition.y;
    break;
  case "Depth":
    ret = PalmPosition.z ;
    break;
  case "Rotation":
    ret = -2 * PalmNormal.x ;
    break;
  case "Tilt":
    ret = PalmNormal.z ;
    break;
  case "HorizontalDirection":
    ret = PalmDirection.x ;
    break;
  case "VericalDirection":
    ret = PalmDirection.y ;
    break;
  default:
    break;
  }
}
if (scaled == true)
{
  if (ret > 1) {ret = 1;}
  if (ret < -1) {ret = -1;}
}
return ret;
}


LeapEnabled はデフォルト falseに。

// pxs modify for Leap
// store a variable to indicate whether we are using Leap as the input here
public var LeapEnabled : boolean = false;

Input がキーボードからの入力だと前後左右しかコントロール出来ないのでこうなっているのを:

throttle = Input.GetAxis("Vertical");
steer = Input.GetAxis("Horizontal");

Leap Motion ではこう書き換える。

// pxs Modify for Leap
if (LeapEnabled == true)
{
  throttle = pxsLeapInput.GetHandAxis("Depth");
  print("Throttle: " + throttle.ToString());
  steer = pxsLeapInput.GetHandAxis("Rotation");
  print("Steer: " + steer.ToString());
}
else
{
  throttle = Input.GetAxis("Vertical");
  steer = Input.GetAxis("Horizontal");
}

これで設定に Leap Enabled のチェックボックスが表示されるようになったのですかさずチェック。


これで無事、Leap Motion で車をコントロールすることができるようになりました!
なお、WebPlayer だとうまく動かないので、今回はオフラインアプリにしました。Leap Motion を持っている人は落として遊んでみてください。

Tutorial LeapEnabledCar をやってみた:

Packaged App: https://drive.google.com/file/d/0B39ZuZLxj12aLUlrd2FzXzNmaDA/edit?usp=sharing

再び Leap Fly

LeapEnabledCar のを参考に Leap Fly の設定を見てみるんだけど、必要なファイルは全部入っているし、LeapFlyController にはちゃんと LeapFly.js が設定されているし、それが ThrusterPlane に紐付けられている。→←↑↓ではちゃんと動く。Leap が反映されない。。。そして困ったことにエラーが出ないのでどこでこけているのかちっともわからない/(^o^)\

とりあえず LeapFly は一旦忘れて、エイリアンを倒すことに集中しよう。

エイリアン迎撃ゲーム再び

まずはステージを作り直す。前のはなんか色々いじっていたら床が剥がれたり壁が分離したり、大変なことになってしまったので。。。


エイリアンが近づいてきた時のビュー。怖い。。。



カメラの前に Space Shooter 設置完了。ピシュピシュ!


エイリアンが近づくとマジで怖いです!


ただ、ここでひとつ問題が。この Space Shooter、主砲とか機関銃とか火力があるものがついてなさそうに見える。うーん、じゃあ戦闘じゃなくてエイリアンを避ける避けゲーにした方がいいのか。もしくは戦闘機に変える。


ちなみに Leap Motion を使った Unity で作られたゲームはここに色々あります。

Unity-chan 待機

Unity-chan はまだダウンロードできないらしい。。。春からだそうです。ぜひアニメーション素材多めでお願いします!動きを作るの、素人には難しいので。。。m(__)m
http://unity-chan.com/


Disclaimer このブログは山崎富美の個人的なものです。ここで述べられていることは私の個人的な意見に基づくものであり、私の雇用者には一切の関係はありません。