altebute.hatenablog.com

犬も歩けば規格にあたる

Unity プロジェクト作成

手順

  1. Dropbox 上にディレクトproject-name を作成.
  2. そのディレクトリに移動.
  3. 以下のコマンドを実行.
    git init --bare --shared=true project-name.git
  4. 同期の完了を待つ.
  5. 作業用ディレクトリにプル. ディレクトリ名は project-name/dev.git にした.
  6. dev.git に移動 Unity のプロジェクト project-name.unity を作成.
  7. Assets/ProjectName/Games/Game.cs を作成, Visual Studio で開ける事を確認, シーン Game に空のオブジェクト Game を作成し, コンポーネント Game を追加. 実行確認.
  8. .gitignore を作成. 内容は後述.
  9. コミット, プッシュ.

.gitignore

/project-name.unity/[Aa]ssets/AssetStoreTools*
/project-name.unity/[Bb]uild/
/project-name.unity/[Bb]uilds/
/project-name.unity/[Ll]ibrary/
/project-name.unity/[Oo]bj/
/project-name.unity/[Tt]emp/

# Autogenerated VS/MD solution and project files
ExportedObj/
*.booproj
*.csproj
*.pidb
*.sln
*.suo
*.svd
*.tmp
*.unityproj
*.user
*.userprefs

# Unity3D generated meta files
*.pidb.meta

# Unity3D Generated File On Crash Reports
sysinfo.txt

# Builds
*.apk
*.unitypackage

ディレクトリ構成

リモート

  • project-name
    • project-name.git

ローカル

  • project-name
    • dev.git
      • .gitignore
      • project-name.unity

dev.git については, ブランチを切る時はまた別のディレクトリを作成してそこで作業をする.

参考文献

Unityを三日坊主して分かった事

最初から目標を大きくするな

それ一番言われてるから.

使用する機能を最初から隅から隅まで理解しようとするな

機能が必要になったら調べればいい. そもそも全体を把握しないと何のためにあるのか分からない事もある.

郷に入らば郷に従え

エンジンの設計に逆らうのはエンジンをある程度理解してからでも遅くない.

案ずるより生むが易し

簡単な小さい目標を立てて, 何でもいいからまず動かす.

とりあえず

週末なんかしたいな.

Unity 5 日記 6 ショットを発射

もう適当にさっさと書かないと飽きるので雑に書いていく. さっさとショット発射してみたい. 敵にあたるとかそんなんどうでもいい.

Unity 5 日記 2 スクリプトの作成Unity 5 日記 3 deltaTime の適用 で作成したオブジェクトにショットの発射機能を持たせる.

作成してあった移動処理をメソッド Move に分離し, メソッド Update を以下のように書き換える.

private void Update()
{
    Move();
}

変数 float shootFreezeSystem.Collections.Generic.List<GameObject> shots を追加する.

private QuantizedTime shootFreeze;
private System.Collections.Generic.List<GameObject> shots;

ついでにメソッド Start に各々の初期化処理を追加しておく.

void Start()
{
    ...
    shootFreeze = 0;
    shots = new System.Collections.Generic.List<GameObject>();
}

はっきり言ってこの書き方は良くない. クソコードである. 関心の分離もへったくれもない. 本当なら別途ショットを管理するためのクラスを用意すべきだが, ここではとりあえず後回しにする.

Unity 5 日記 5 Prefab で書いたような感じで Prefab を作成し, Resouces フォルダに Shot という名前で適当にいれる.

そしてショットにスクリプトを追加する.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShotBehaviorScript : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        var x = transform.position.x;
        var y = transform.position.y;
        var z = transform.position.z;
        z += Time.deltaTime * 100;
        transform.position = new Vector3(x, y, z);
    }

    public bool IsOut()
    {
        return 32 < transform.position.z;
    }
}

メソッド Update で移動を行う. IsOut は消去判定を行うためのメソッドだ. Unity では画面外に出たことを判定するための関数があるが, ショットは画面外に出た時に消えてしまうのは困るので自分で色々判定する. ここでは本当に適当な値の比較にした.

そして自機側のスクリプトにメソッド Shoot を追加する. この名前もかなり悪い.

private void Shoot()
{
    if (0 < shootFreeze.Value)
    {
        var deltaTime = Time.deltaTime;
        shootFreeze -= deltaTime.Value;
        return;
    }

    var fire = UnityEngine.Input.GetButton("Fire1");
    if (fire)
    {
        GameObject shotPrefab = (GameObject)Resources.Load("Shot");
        var shot = Instantiate(shotPrefab, new Vector3(0,(float)_position.Y, (float)_position.X), Quaternion.identity);
        shots.Add(shot);
        shootFreeze.Value = 128;
    }
}

手元のスクリプトとブログに書いてあるスクリプトが一致していないので座標周りがおかしかったり変数名があっていなかったりするが心の目で読んで欲しい. Resouces ディレクトリに入れた Prefab は上記のような感じでインスタンス化することが出来る. 生成したショットは shots に追加する.

最後に画面外に出たショットを削除するためのメソッド RemoveShots を追加する.

void RemoveShots()
{
    for (int i = 0; i < shots.Count;)
    {
        var shot = shots[i];
        var script = shot.GetComponent<ShotBehaviorScript>();
        if (script.IsOut())
        {
            Destroy(shot);
            shots.Remove(shot);
            continue;
        }
        ++i;
    }
}

GetComponent でアタッチしたコンポーネントを取得し, メソッド IsOut で判定を行い, Destroy 関数で GameObject を削除する.

ShotBehaviorScript の中で Destroy(this) するとオブジェクト全体ではなくコンポーネント単体を削除してしまうらしい.

なんかもう記事書くのも面倒になってきた. この雑さ極まるコードを GitHub に丸上げして, コミットログにリンク張って日記にする程度でいいんじゃなかろうか.

Unity 5 日記 5 Prefab

Hierarchy 内に生成したオブジェクトを Project にドラッグ & ドロップするとなんか青くなる. 以後は Project 側からドラッグ & ドロップすると簡単にオブジェクトを複製出来る. Inspector からオブジェクトのパラメータを変更すると一括で変更出来る. オブジェクトのどの要素が個別に扱われ, どの要素が一括で変更されるのかはよく分からない. ぐぐって.

参考文献

Unity 5 日記 4 InputManager の調整 キーボードの入力で慣性がつく問題の修正

デフォルトの設定だと UnityEngine.Input.GetAxis("Horizontal")UnityEngine.Input.GetAxis("Vertical") で入力をいい感じに受け取れるが, ジョイパッドで操作する分には問題ないものの, キーボードで操作する場合に妙な慣性がついてしまう. 今回は入力系の設定変更でこのあたりの調整を行う.

Edit->Project Settings->Input から Unity の通常の入力系の設定を行うことが出来る. InspectorHorizontal とかかれた項目が 2 つあると思うので, それを開く. 片方はキーボードの, 片方はジョイパッドの設定だ. Gravity はボタンを離した時の加速度, Sensityvity はボタンを押した時の減速度である.

大体 64 程度にしておけば FPS 60 の環境で 1 フレームで最大値になる. 高フレームレートでも同様になってくれる事を期待するのであれば 255 等にしておけば良いだろう.

欲を言えば 0 にした場合は必ず 1 フレームで最大値になる等の設定が欲しかった所だが残念ながらそういった機能はない. それどころか負数の指定すら出来てしまい, 入力方向に対して明後日の方向に加速していってしまう. それでよかったのか, Unity......

Unity 5 日記 3 deltaTime の適用

なんだか 前回 まで大真面目に記事を書いてたせいで新しく記事を書くのが億劫になってきてしまっていたので, 今回からもっと雑に書いていくことにする.

UnityEngine.MonoBehaviour を継承したクラスのメンバ関数 Update は毎フレーム呼ばれるが, 毎フレーム呼ばれるということはフレームレートの高い環境ではそのままではゲームの動作まで早くなってしまう. そこで, Time.deltaTime を乗算する事でこの問題を解決する.

void Update()
{
    var x_axis = Input.GetAxis("Horizontal");
    var y_axis = Input.GetAxis("Vertical");
    var vx     = x_axis * 60 * Time.deltaTime;
    var vy     = y_axis * 60 * Time.deltaTime;
    var v      = new Vector3(vx, vy, 0);
    this.transform.position += v;
}

Time.deltaTime には前回のフレームから今回のフレームまでに経過した時間が秒単位で保持されている. 型は float である.

FPS が 60 の環境である事を前提にフレームカウンタを回して任意の動作を実行するようなアルゴリズムを実行した場合は, Time.time の差分を取るか, Time.deltaTime を毎フレーム加算して, 大小の比較演算によって実行する必用がああるだろう. 浮動小数点であるから, 同値比較を行うと危険である事に注意.

Unity 5 日記 2 スクリプトの作成

前回は GameObject の追加とギズモを使った操作までを行った。 Unity入門 (全26回) - プログラミングならドットインストール ではこの後 Transform 、 Material の操作、 Rigitbody 、 Directional Light 、 GameObject の階層化、 Prefab が紹介されているが、それらを全てすっ飛ばして Script の追加に入っていく。 Visual Studio がインストールされている前提で話を進める。

プログラマ諸氏におかれては、さっさとプログラムに入って一先ず入力を受け取ってオブジェクトを動かしたくて仕方ないと思われるので、まずは Hierarchy ペインの Create ボタン 3D Object -> Cube で適当に GameObject を追加する。

次に、作成した GameObject を Hierarchy ペインから選択し、 Inspector ペインの最下部の Add Component をクリックしてコンポーネントを追加する。上部メニューの Component から追加してもよい。今回は New Script を選択し、名前を設定、 C Sharpスクリプトを追加する。

スクリプトを追加すると Project ペインの Assets の中にファイルが追加される。ファイルをダブルクリックすると自動的に Visual Studio が立ち上がり、プロジェクトが生成され、ファイルが開かれる。自動生成したクラスは MonoBehaviour を継承しており、メンバ関数 StartUpdate 関数が最初から定義されている。

不思議な事に、ファイル名とクラス名は一致していなければ Unity 側でうまく動かない一方で、クラスを名前空間に入れる分には特に問題なく動く。謎である。関数 Start はオブジェクトの生成時に一度だけ呼ばれる。一方 Update は毎フレーム呼ばれる関数である。

この時、すでに存在するクラス名を指定しようとすると、新しくファイルを追加出来ない場合がある。 Assets 内を適当に右クリックして新規ファイルを作成し、ドラッグ & ドロップで GameObject に Attach した方が良いのかも知れない。 そもそも名前空間で区切りながら同じ名前の複数のクラスを作成したいと考えていないが出来ないらしい。無念。

以下のように記述する事で、入力に応じてオブジェクトを移動する事が出来る。

public class NewBehaviourScript : MonoBehaviour
{
    // Use this for initialization
    void Start() {}

    // Update is called once per frame
    void Update()
    {
        var x_axis = Input.GetAxis("Horizontal");
        var y_axis = Input.GetAxis("Vertical");
        var velocity = new Vector3(x_axis, y_axis, 0);
        this.transform.position += velocity;
    }
}

InputUnityEngine.Input クラスである。このクラスは静的メンバ関数 GetAxis を持っており、引数 axisName によって識別される Unity 側で仮想的に定義された軸の入力値を取得する事が出来る。

取得した値をベクトルに変換し、 MonoBehaviour クラスのデータメンバである transform を操作して実際の移動を行う。丁度、このデータメンバは Inspector ペインに表示される Transform の内容そのものである。

あとは画面上部の再生ボタンを押せば、ゲームが動作を開始する。ちなみに Visual Studio 側から Unity にアタッチすれば、簡単にデバッガも動いてしまう。

これで晴れて C# を使用して、スクリプトによるオブジェクトの操作が出来た事になる。カメラやライトの制御をきちんとしないと Game 内での実際の見え方は奇妙になるが、そこは適宜調整してやればよいだろう。あとは、オブジェクト間で互いに情報を取得するための手段と、オブジェクトの生成と破棄を行う仕組みがあれば最低限のゲームを作成する事が出来る。