マイコン研究会の日記

大阪市立大学マイコン研究会のブログです。

【Unity】3Dゲームを作る その7 スイッチでドアの開閉・カメラの切り替え

注:修正の話以外は修正後データでやっています。

こんばんは、自分のプログラムが汚すぎて何とかしたいzankです。

 

今回は

という形で紹介していこうと思います

 

また今回のように修正は結構な頻度で入ると考えられますが、大目に見てくれると助かります。

 

後、今回から話小分けにするのめんどくさいので、毎回長くなります。

 

 

それでは始めて行きましょう。

 ________________

1.スイッチでドアの開閉

 ________________

まず、スイッチでドアの開閉をするには、当たり前ですけどドアとスイッチが必要です。

 

なのでとりあえず困ったらアセットストアにないかな?と探してみます。

 

するとドアのモデルが無料でありました

 

f:id:ocumcr:20180618114350p:plain

 

 いい感じのモデルですね。

自分が望んでたイメージとは少し違いますが、十分です。(上から目線)

 

次にスイッチを探していきましょう。

 

f:id:ocumcr:20180618153832p:plain

 

 

 なん…だと…

スイッチとかめっちゃ使いそうなのにないのか。

 

まぁスイッチとか簡単に作れますね。

有料の方ではセット売りされてるけど 金払いたくない。

 

というわけで、スイッチを作っていきましょう。

今回作るのはボタン型のスイッチです

 

まず、ProBuilderからf:id:ocumcr:20180618115810p:plainを選択して、Shape SelectorのCylinderを選択します。

(Hierarchyでも作れますが形をいじりたいのでProBuilderを使用)

 

f:id:ocumcr:20180618115832p:plain

 

 

Build Cylinderはまだ押さずに設定を決めて、いい感じになったらBuildしましょう。

(位置も調整できるので、ユニティちゃんのそばでやると個人的にやりやすい!)

 

f:id:ocumcr:20180618120225p:plain

 

次に変形作業をしていきましょう。

 

個人的に土台上部分が斜めになっていて、そこにボタンがついているのが良いのでそうなるように、f:id:ocumcr:20180614185828p:plainを使って変形していきます。

 

f:id:ocumcr:20180618121422p:plain

 

こんな感じでいいかな?

 

とりあえず、これで土台ができたのでスイッチのボタンを作っていきます。

 

とはいっても同じようにするだけなので省略。

 

f:id:ocumcr:20180618121826p:plain

 

 

とりあえずスイッチ完成。

と言いたいのですが、ボタンの色がスイッチって感じがしないですね。

 

なのでマテリアルを使って色をつけていきましょう。

 

この記事では、マテリアルは初めての紹介になるので説明していきます。

マテリアルというのは簡単に言うと、

 

オブジェクトに色を付けるものです。

 

いわゆるテクスチャと呼ばれるものですね。

これは単色でも行けますし、画像データなどを張り付けることも可能です。

これがあるか無いかでは、大きく違ってきます。

 

 

では、作っていきましょう。

 

まず初めにマテリアルを管理するファイルを作りましょう(重要)

 

ProjectのCreateからFolderをクリックしMaterialsとつけておきましょう。

 

次にProjectのCreateからMaterialを選択します。

 

f:id:ocumcr:20180618122807p:plain

 

次に作ったマテリアルにSwitch_Bottonと名前を付けてMaterialsフォルダーに突っ込みます。

 

そしてInspectorのAlbedoを選択するとColorというものが出てくるので、それで色をいじっていきましょう。

 

f:id:ocumcr:20180618123358p:plain

 

色を変更した後はマテリアルを色を付けたいオブジェクトにドラッグアンドドロップをするだけで適応できます。

 

f:id:ocumcr:20180618124235p:plain

 

一応スイッチっぽいですね。

 

では、次にドアを設置していきましょう。

 

インポートしたデータのファイルの中からPrefabsを探しその中のデータをシーンにぶち込んで、位置を合わしていきましょう。

 

f:id:ocumcr:20180618124645p:plain

 

このドアはちゃんと開閉機能がついているので試してみましょう。

 

f:id:ocumcr:20180618124843p:plain

 

ちゃんと開きますね。 

 

これはどのようにして開いているのでしょうか。

 

スクリプトを見てみましょう。

 

using UnityEngine;
using System.Collections;

public class door : MonoBehaviour {
 GameObject thedoor; //オブジェクトの取得のための変数

 //どちらかのIs TriggerがオンになっているCollider同士が侵入したときに呼び出し
 void OnTriggerEnter(Collider obj) {                  
      thedoor = GameObject.FindWithTag("SF_Door"); //"SF_Door"のタグが付いたオブジェクトを取得
        thedoor.GetComponent<Animation>().Play("open"); //"open"のアニメーションを再生
 }
 //どちらかのIs TriggerがオンになっているCollider同士が離れたときに呼び出し
    void OnTriggerExit(Collider obj) {
        thedoor = GameObject.FindWithTag("SF_Door");
        thedoor.GetComponent<Animation>().Play("close"); //”close”のアニメーションを再生
    }
}

 

(コメントは後付けです)

これを見る限りコライダーに侵入すると開いて、離れると閉まるといった仕組みになっているようです。

なのでこれにスイッチがオンになっているときにドアを開く、というように変更すれば大丈夫そうです。

 

 

なので、まず先にスイッチのオンオフを設定できるスクリプトを書いていきましょう。

 

新しくSwitch_Flagというスクリプトを作っていきます。

 

スイッチのオンオフも簡単に書くことができて、スイッチの近くであるボタンを押すとスイッチが入る、というプログラムを書けばいいだけです。

 

実際に書いていきましょう。

 

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

public class Switch_Flag : MonoBehaviour {
    public bool is_Open; //ドアが開いたかどうかの変数
    public KeyCode keyCode; //どのキーを入力するかの変数
 //どちらかのIs TriggerがオンになっているCollider同士が接触している間常に呼び出し
    private void OnTriggerStay(Collider obj) {
  //特定のボタンを押してかつis_Openがfalseの時is_Openをtrueにする
        if (Input.GetKey(keyCode) && !is_Open)  is_Open = true;   
    }
}

 

非常に簡単ですね。

 

では、今度はdoor.csのプログラムを書き換えていきましょう。

 

この時の問題はSwitch_Flagのis_Openをdoorが取得しなければならない点です。

この技術は非常によく使うので私のやり方が正しいかわかりませんが、知っておいて損はありません。

(多分探したらもっといい方法ありそう)

 

とりあえず書いていきましょう。

 

using UnityEngine;
using System.Collections;

public class door : MonoBehaviour {

    GameObject thedoor;
    GameObject switch_Base; //Switch_Baseを入れる変数
    private Switch_Flag switch_Flag; //Switch_Flagを参照する変数
    private bool is_Open; 

    private void Start() {
       //Switch_Baseの情報を取得
        switch_Base = GameObject.Find("Switch_Base"); 
       //Switch_BaseのSwitch_Flagを取得
        switch_Flag = switch_Base.GetComponent<Switch_Flag>(); 
    }
    void OnTriggerEnter(Collider obj) {
        is_Open = switch_Flag.is_Open; //Switch_Flagからis_Openを取得
        if (is_Open) {
            thedoor = GameObject.FindWithTag("SF_Door");
            thedoor.GetComponent<Animation>().Play("open");
        }
    }

    void OnTriggerExit(Collider obj) {
        if (is_Open) {
            thedoor = GameObject.FindWithTag("SF_Door");
            thedoor.GetComponent<Animation>().Play("close");
        }
    }
}

 

 (コメントめんどくさい)

 

これでスイッチを使ってドアの開閉はできるようになりました。

 

ですがまだやることがあります。

それはスイッチの判定範囲の設定です

 

まず、Swtch_BaseのInspector下部のAdd ComponentからPhysicsを選びBox Colliderを選択します

 

そしてその中身をいじっていきます。

 

f:id:ocumcr:20180618143144p:plain

 

忘れてはいけないのがOnTrigger関数をつかっているのでIs Triggerにチェックしましょう。

あとSwitch_FlagのKey Code設定しておきましょう。 

 

f:id:ocumcr:20180618143435p:plain

 

 この緑の範囲が判定になります。

 

では実際に動くのか試してみましょう。

 

 

youtu.be

 
ちゃんと動いてますね。
 
しかし動画を見てもらえばわかりますが、スイッチが押されたのかが全く分からないです。
 
なので次にモーションを追加していきましょう。
 
まずモーションを探すところからですが、
 
アセットストアに非常に優秀なものがありました。
 

f:id:ocumcr:20180618145202p:plain

 
しかしこのモーション集1~3すべて入れるととんでもない大きさになるので、ひとまず別のプロジェクトにインポートして、必要なものだけこちらのプロジェクトでインポートするのがいいと思います。
 
ひとまずざっくりいいのがないかとモーションをあさっていると19_01が良い感じだったのでこれを使っていきます
 
とはいってもモーションの追加については説明したのでバッサリカットしてプログラムだけ載せます。
 
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class anime : MonoBehaviour {

    [SerializeField] private KeyCode actionKey;
    private Animator animator;

    void Start () {
        animator = GetComponent<Animator>();
    }

    void OnTriggerStay(Collider other) {
  //タグがスイッチのコライダー内にいるとき
        if (other.gameObject.tag == "Switch") {
     //あるキーを押すとスイッチモーションの再生
            if (Input.GetKey(actionKey)) {
                animator.SetBool("Switch", true);
            }
            else {
                animator.SetBool("Switch", false);
            }
        }
    }
}

 

door.csの時には説明しませんでしたがタグについて説明します。
 
これはInspectorの上の方にあるTagからAdd Tagを選びそこから追加することができます。
 

f:id:ocumcr:20180618150331p:plain

ocumcr  
タグ付けもTriggerを使う際に非常に重要だと私は思っています。 
ただタグはわかりやすい名前にしておかないと後でわかんなくなるので気を付けましょう。 

それではモーションをつけた場合を見てみましょう。 

youtu.be 
 ちょっと届いてない感じはありますが、ちゃんとスイッチを押している感じは出ています。
 
 これでゲーム機能はひとまず完成です。
 
なのでタイトルとリザルト、ランキングが実装すれば、後は箱を押したりワープゲートでワープしたりその他の機能を実装してステージを増やすだけです。
________________

2.現状の問題と修正

 ________________

前回までのパートではカメラとモーションがいろいろとおかしな状況でした。

なので何とかして直そうと前回頑張ったのですがうまくいかず。

 

そこで私は考えました。

 

一回全部最初から設定していこう。

そしてもっとちゃんと調べてからやろうと。

 

そしたらいろいろ有用そうな情報が載ってまして、ちゃんと調べてからやればよかったと後悔しました、

 

なので今から、多分実装するなら一般的な、ほかの方もよく説明している方法を実装していきたいと思います。

 

まず以前まで設定したユニティちゃんとカメラは削除しちゃいます。

(Locomotionなんてなかったんや)

 

そして右上のAssetからImportpackageのCameraとCharactersをインポートします。

 

f:id:ocumcr:20180618152500p:plain

 

そしてCameraのPrefabsの中にMultipurposeCameraRigというものがあるのでそれをHierarchy内にぶち込みます。

 

そのあとユニティちゃんのInspectorにAdd ComponentのScriptからUnityStandard Assets.Charactor.ThirdPersonCharacterを見つけてその中のThird Person User Controlを選択します。

 

f:id:ocumcr:20180618153142p:plain

 

この後MultipurposeCameraRigのTargetをユニティちゃんにすればちゃんと機能します。

 

これでひとまず大丈夫です。

 

こんなに簡単なことだったのにいちいちめんどくさく問題の多い方を使っていたのってホント馬鹿。

 

作業時のBakedがうざかったのでついでにライトも排除しときました。

 

_________________________

3.TPS、注視モード、クリア時のカメラ固定などのカメラ切り替え

 _________________________

( シューティングじゃないけどわかりやすいのでTPSと表記していきます。)

 

前回までのパートでカメラの問題が解決したら、こんどはFPS のカメラの操作もできるようにしようと考えていていました。

 

そして謎に、

これらも私はプログラムで実装しなきゃ(使命感) 

とかいう謎の使命感に駆られて頑張っていましたが、実際には注視用のプログラムと切り替え用のプログラムしかいりませんでした。

 

というわけで記事も長くなってきたので早速説明していきます。

 

 まずカメラの修正である程度回り込むようになりましたが、よくあるFPSやTPSではマウスでカメラ操作をしていることが多いので、このゲームでもマウスでカメラを操作できるようにしたいです。

 

でもプログラムをいじったらまた変になるんじゃ・・・

 

そんな心配をしていましたが調べたところunityの機能でできるようになっているらしいです(すごい!)

 

ではその機能を入れていきましょう。

 

ProBuilderの時と同様にPackage Managerをクリックし、Cinemachineをインストールします

 

f:id:ocumcr:20180618155125p:plain

 

次に右上に追加されたCinemachineのCreate FreeLook Cameraを選択します。

 

f:id:ocumcr:20180618155312p:plain

 

するとCM FreeLookというものがHierarchyに追加されます。

 

つぎにCM FreeLookのInspectorのFollowにカメラをLook Atにユニティちゃんの頭を選択します。

 

f:id:ocumcr:20180618155840p:plain

 

各パラメータについては全然わかりませんが、とりあえずField Of Viewでユニティちゃんとの距離をいじれるのでいい感じにしておきます。

 

これでマウスで操作できるようになりました。

 

次に注視モードです。

 

あるボタンを押すと主観視点になり周りを見渡す事ができるようにしていきます。

 

まずは主観視点のカメラを作っていきます。

普通にHierarchyからCameraを作ります。

 

その後Add ComponentからUnityStandardAssets.CamerasのFree Look Camを選択します。

 

f:id:ocumcr:20180618160634p:plain

 

このカメラは子オブジェクトにするのでAuto Target Playerは外してもいいと思います。

 

 ここでユニティちゃんの子オブジェクトにしたら非常にやばいバグが発生したので、適当に空のオブジェクトを作って、そこの子オブジェクトにしていきます。

 

しかしそうすると、ユニティちゃんの視点にならなくなってしまうため、スクリプトで顔あたりに追従するようにします。

 

というわけでスクリプトがこちら

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

public class FPCamera : MonoBehaviour {

    public Transform target;    // ターゲットへの参照

    void Update() {
        // 自分の座標にtargetの座標を代入する		
        GetComponent<Transform>().position = target.position;
    }
}

 

非常に簡単なプログラムで済みました。

 

これでターゲットを指定するとそのターゲットの座標に移動し続けます。

 

 次にクリア後、視点をドアを映したまま固定し、リザルト表記としたいため、新しくHierarchyからCameraを作りいい感じの場所に置きます。

 

次に空のオブジェクトにBox Colliderをつけて、ドアの向こうに置きます。

ついでにタグをexitとしておきます。

 

これでカメラの設置が完了したので次にカメラを切り替えるプログラムを書いていきます。

 

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

public class Camera_Switch : MonoBehaviour {

    [SerializeField] private KeyCode cameraKey;
    [SerializeField] private GameObject tpsCamera;
    [SerializeField] private GameObject fpsCamera;
    [SerializeField] private GameObject endCamera;
    private int count = 0;
    private Animator animator;
    private int pressKeyFrames = 0; //キーを押した時間
    private int longPress = 2; //長押し最大値

    void Start() {
        animator = GetComponent<Animator>();
    }
    void Update() {
     /*注視モード用の処理*/

      //キーを押していたら+1する
        pressKeyFrames += (Input.GetKey(cameraKey)) ? 1 : 0;
       //もしキーが離されたらPressKeyFranesを0にする
        if (Input.GetKeyUp(cameraKey)) pressKeyFrames = 0;
       //切り替え用キーが押されるかつキーを押して2フレーム以内なら
        if (Input.GetKey(cameraKey) && (pressKeyFrames < longPress)) {
         //TPS用のカメラと注視用カメラのオンオフを反対にする
            tpsCamera.SetActive(!tpsCamera.activeSelf);
            fpsCamera.SetActive(!fpsCamera.activeSelf);
         //アニメーターを停止して動けなくする
            animator.enabled = false;
        }
       //TPS用カメラがオンなら
        if (tpsCamera.activeSelf) {
          //アニメーター動かせるようにする
            animator.enabled = true;
        }
        /*ここまで*/
    }
   /*クリア時のカメラ変更の処理*/
    void OnTriggerEnter(Collider other) {
        if (other.gameObject.tag == "exit") {
            count++; //なぜかこれがないと動かなかった
            if (count > 1) {
                tpsCamera.SetActive(!tpsCamera.activeSelf);
                endCamera.SetActive(!endCamera.activeSelf);
            }
        }
    }
  /*ここまで*/
}

 

こんな短いコードでも汚くなるのか。(困惑)

 

プログラムできる人、もっときれいにできる方法教えてください。

 

(countは全然カメラ変更しなかったので、デバッグログ出してデバッグしてる時に、遊び半分で突っ込んだら動いたから一応入れてます。正直boolでいいと思うけどめんどいのでそのままで。)

というわけで動かしてみます。

 

youtu.be

 

完成です。

 

思ったより簡単に終わってうれしいです。

 

 

以上で今回の内容は終わりとさせていただきます。

 

なんと今回まさかの9000字越えという大ボリュームになってしまいました。

プログラムがある程度あるとはいってもそんなに文字書いた覚えはないです。

 

多分ですが今後はこれぐらいが平均になっていく可能性がありますのでご了承ください。

 

やることを小分けにすればいいのかもしれませんが、私は一度書くととりあえず今終わってる分までは全て載せたくなってしまう性格でして、あと私は細かい記事がたくさんよりは、長い記事で少ない数の記事の方が好きなのでこんなことになってしまっています。

 

これに関しては賛否あるかもしれませんが、まずこんな記事誰も見ないと思いますので気にせずにやっていきたいと思います。

 

次回はタイトルとシーンの変更の予定です。

 

ではまた次の記事でお会いしましょう。