[Unity]UnityとC#を学ぶ問題:002

二日目の問題を出題しました。
Unity本をやったことがある人が居たので後半は難易度が高いです。

第一問

マウスで画面をクリックすると、マウスでクリックした座標をConsoleに出力するようにしてください。

ヒント

  • Inputクラスを使ってマウスのクリックを取得します。
  • Debug.Log()でコンソールへの出力ができます。

第二問

マウスでクリックしたら、クリックした位置からワールド座標系における、カメラから10m離れたところにキューブを出現させてください。

仕様

当たり判定を取って、キューブの位置を決定します。

ヒント

  • Camera.main でカメラを取得できます。
  • ScreenPointToRayで画面座標からワールド座標へ座標変換されたRayを飛ばすことが出来ます。Rayとは光線みたいなものです。
  • 当たり判定は Collider 継承コンポーネントで取ることが出来ます。
  • PlaneやCubeなどのPrimitiveでCollider付きのGameObjectを予めゲーム空間に配置して、Rayの当たり判定を取れるようにしておきましょう。
Ad

答え

コリジョンの当たり判定まわりは難易度が高いのと、予め用意する内容が最低限必要なのでまんま答えを載せつつ解説をします。
以降の問題でこの内容を踏まえることになります。

ゲーム空間(Hierarchyペイン。Sceneとも言う)上にあらかじめColliderを持つオブジェクトを配置しておきます。
Planeのx軸を-90fにしたものを配置し、Scaleをそれぞ10くらいにしておくと良いと思います。
さらに、Rendererコンポーネントをオフにしておけば画面に表示がされなくなります。

    void Update () {
        if ( Input.GetMouseButton(0) )
        {
            // マウスのクリック位置を取得
            var screenpos = Input.mousePosition;
            // スクリーン座標からワールド座標空間で飛ばす 線 を生成
            var ray = Camera.main.ScreenPointToRay(screenpos);
            // 当たり判定の情報を以って帰ってくれる変数を宣言。宣言さえしておけばスコープ内で参照可能
            RaycastHit hit;

            // Physics.Raycastで当たり判定お行う。
            // out が付くと、関数終了後に変数の中身が変わる可能性ああることを理解すべし
            if ( Physics.Raycast(ray,out hit,float.MaxValue) ){
                // このブロックあヒットした場合のみ駆動する。

                // キューブを生成
                var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                // ヒット位置は読み取り専用なのでコピー
                var p = hit.point;
                // カメラから10m離れた位置を代入(厳密ではないですがとりあえずこれで仕様にある距離について全うしたってことで。)
                p.z = Camera.main.transform.position.z + 10f;
                // キューブの位置い渡す
                cube.transform.position = p;

                // おまけ。5秒後に消滅
                Destroy(cube,5f);
            }
        }
    }

第三問

第二問で出現したキューブは自由落下し、 画面から外れたら 消滅するようにしてください。

仕様

  • 自由落下はRigidbodyでおこないます。
  • 消滅はDestory関数を使います。

ヒント

  • Rigidbodyを使って自由落下をさせることができます。
  • Rendererを持つコンポーネントと一緒にアタッチしたMonobehaviour継承クラスには
  • Renderer系のComponentからはカメラから外れた時に OnBecameInvisible が発行されます。
  • このメッセージはMonoBehaviour継承クラスで受信できます。
  • Renderer.OnBecameInvisible()

答え

  • 画面外判定用コンポーネントを別途用意する場合
  • 画面外判定も含めて1ファイルにする場合

前者の方がUnityらしい作り方ですが、画面外判定をするためだけに独立したスクリプトを作成することに抵抗がある場合は後者のように管理クラスが対応するやりかたも良いと思います。

第四問

キューブを出現させずに線を引いてください

仕様

クリックした場所からクリックを離す場所まで直線を引を引きます。
ドラッグ中はマウスの位置を終点扱いにしてください。
線の色は、出現する度にランダムで決定するようにします。

ヒント

  • LineRendererを使って線を引くことが出来ます。
  • LineRenderer
  • UnityEngine.Random.Range(float,float)を使って乱数を生成できます。

答え

  • ラインの開始点、終着点、ドラッグ時の挙動を認識するとこうなります。

LineMaker from s2kw on Vimeo.

第五問

第四問で使用した線をマウスの軌道に合わせて描画する線として表示してください。

仕様

クリックした位置から開始し、毎フレームマウス座標を取得して線を伸ばしていきます。
次にクリックを開始したらこれまで書いた線を消して新しい線を引き始めます。

ヒント

  • LineRendererには複数の位置を保持してその間をポリゴンで埋めてくれます。
  • この位置の羅列は LineRenderer.positions で取得できます。
  • 位置を更新するには、 SetPositions() で行います。
  • LineRenderer.SetVertexCountで頂点数を更新してあげる必要があります。

答え

gist

マウスダウン中はずっとマウスの位置がアップデートされます。
その座標を全部拾ってLineRendererの各頂点をつなげていくように処理しています。
フレーム単位で取得するのと、これまで取得した部分は変更しないようにするために、何番目まで取得したのかを保持しなければならない点に注意が必要です。

DrawLine from s2kw on Vimeo.

解説

今回は「もっと意地悪な問題をくれ」ということだったので、途中からかなりレベルを上げました。
第二問の時点でもPhysicsを使って当たり判定を取るような処理をするようにしています。
これはUnity関連の入門書をやると必ずでてくるので、やったことがある人も多いかと思います。なので分かる人はさくっと分かる内容ですね。
わからない人でも先にすすめるように、答えをそのまま貼りました。

ただ、Hierarchy上に予め当たり判定を取る用のGameObjectを用意しろ、と言われてすぐできるわけではないのでいずれにせよ難易度は高いと思います。
なのでここで記載する時点でヒントを大量投入しておきました。

Debug.DrawLine()

RayをScene中に描画することができます。ポリゴンの法線を描画したり応用がいろいろできます。

    Debug.DrawLine(<開始位置>,<終了位置>,<色>,<描画時間>);

描画時間をfloatで指定できます。ここを指定しないと1フレームしか描画されないので目視で確認するのが困難になったりします。
たとえば Input.GetMouseButtonDown() などはクリックしたフレームだけなので次のフレームではfalseが返ってきます。
このInputでif文を書いていると1フレームしかDebug.DrawLine()は処理されないので観測できない、という事態に陥ります。

そこでこの描画時間を1fとかに指定しておけば1秒間はDrawLineの線が描かれています。
覚えておくと便利です。

GetComponent と AddComponent と Generic について

Getの反対はSetですが、SetではなくAddなのは、現在無いものを追加するからです。
二日目にしてが登場してしまいました。文字列で取得、追加する方法もかつてはありましたがnamespace導入から使えなくなりました。

それはさておき、GetComponentとAddComponentは最初はよく使うので覚えておきましょう。

Tには型を直接記述します。

これらの関数で渡す型はComponent継承クラスでないと怒られます。
独自に定義したMonoBehaviour継承クラスか、Unity提供のコンポーネントでないと取得できないということです。
Unityの画面にあるInspectorペイン上から、現在付いているコンポーネントを確認することができます。

型とは、現実世界でいうところのカテゴリみたいなものです。
りんごやバナナはフルーツカテゴリにおけるコンパチである、という意味です。
猫とウサギは動物カテゴリのパラメータ違いです。

という表現ですが、GenericといってListと同様に型を指定するのですが、プログラミング初心者だとイメージしにくいと思います。

    // ウサギ
    var usagi = new 動物();
    usagi.name = "うさぎ";
    usagi.runSpeed = 5f;

    // 猫
    var neko = new 動物();
    neko.name = "猫";
    nekoc.runSpeed = 4f;

    // 動物図鑑の作成
    var doubutuZukan = List<動物>();
    doubutuZukan.Add(usagi);
    doubutuZukan.Add(neko);

プログラムで書くとこんな感じでしょうか。
List に動物を指定してます。このListは動物のリストであることを宣言してます。

マリオで例えると

    var nokonoko = new Turtle();
    nokonoko.isFly = false;
    nokonoko.fireDamage = true;

    var patapata = new Turtle();
    patapata.isFly = true;
    patapata.fireDamage = true;

    var met = new Turtle();
    met.isFly = false;
    met.fireDamage = true;

    var kame = List<Turtle>();
    kame.Add(nokonoko);
    kame.Add(patapata);
    kame.Add(met);

と言った感じです。
なんとなくイメージが湧いたでしょうか?

Renderer

ポリゴンを描画するためのコンポーネントです。
SkinedMeshrendererだとインポートしたポリゴンモデルのためのレンダラですし、今回使ったLineRendererも線を描画するための機能を色々備えたコンポーネントです。ポリゴンを操るにあたって必ず登場します。

マテリアルを渡してあげないと描画するための情報が足らずエラー(ピンク!)になるということを覚えておきましょう。
ピンクになったらMaterial内の情報がおかしいということです。

LineRenderer

LineRendererは頂点を複数持つポリゴンの板を生成するレンダラです。
マウスの位置から取得した座標をLineRendererの頂点の位置として渡すことで、絵かきをしてるかのような線を表示することが出来ます。
予めポリゴン数を指定しておかなければならないですが、必要であれば増やすこともできます。

第五問目まで解けた人はLineRendererの使い方がよくわかってるかと思います。
また、LineRendererはカメラの向きに応じてポリゴンを動かすので立体的な線を表現しようとするとおかしなことになる点は注意しておきましょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です