つよく、やさしく、泥臭く生きていくブログ

日常とポエムと、ときどき技術

Unityでゲーム作ったのともろもろ

ちょっと暇があったので、Unityを触ってみてゲーム作った。

まず普通の2Dのブロック崩しチュートリアル的なものをやった後、それを改造して3Dなものにした。 2Dから3Dにすることで、2Dではほとんど気にしなかったことを追加で学習できてよい

2Dのチュートリアルはこのページを見ながらやった。
【Unity】ブロック崩し(3D)の制作手順を解説!反射角度・速度調整・クリア判定まで – XR-Hub

3Dは適宜ググりながらいろいろ見てやった。

あと、Unityのすごいところは、簡単に、ブラウザで動くようにビルドできること。しかもそれをgithubページでそのまま公開できちゃうってこと。
ブラウザで遊べるってのは強くて、ツイッターで公開してすぐに遊んでもらえる。感動。

ビルドとgithubページでの公開はこれを参考にそのまんまできた。
【Unity】作成したゲームをWebで公開する方法 - Qiita

2Dで学べる事

  • 物体の作り方
  • 剛体が勝手に動くようにすること
  • キー入力で物体を動かせること
  • 当たり判定、当たった時の処理
  • プレハブ
  • etc.

3Dで学べる事

  • 光と影
  • 物体の回転
  • カメラの回転
  • カメラに合わせた物体の移動
  • Ray ( 邪魔になる壁を透過するとかに使う )
  • etc.

困ったことと解決した方法を書いておく

箱の面の、光源と逆側が真っ暗

逆向きの光源を一つ追加して解決。多分ほかにやりようがある気がする。例えば箱の内側の面がそれぞれ発光するとか。
また、光源にはこういう種類がある。

  • めちゃ遠い位置から均一に降り注ぐ太陽みたいな光源 (デフォルトの光源がこれ。向きはあるが位置には意味ない。)
  • ランプのようにある位置から全方向に広がる光源
  • スポットライトのようにある位置からある方向へ円錐型に広がる光源
  • 物体そのものが発光する光源

影が出るけど離れると出ない

編集中は台の影が床に出てるのに、ゲーム画面になると影がでない。編集中でもカメラを遠くすると出ないということがあった。 これは負荷軽減のために遠い影を描画しないからだそうだ。
Edit > Project Settings で、Quality の Shadow Distance の値を大きくすることで解決 f:id:yukinea:20200906153129p:plain

あと、物体はそれぞれに

  • 光をどう遮るか: Inspectorタブ> Mesh Renderer > Lighting > のCast Shadows
  • 影を受けるか: Inspectorタブ> Mesh Renderer > Lighting > のReceive Shadows

という設定がある。そもそも影が出ないとかってときはこの辺をちゃんと設定する。

プレハブで作成したブロックが、ボールはぶつかるのに消えない

プレハブ化したブロックを、ゲームオブジェクトに設定したスクリプトで出現させるようにしたところ、ぶつかっても消えない最強のブロックができてしまった。 当たればボールは跳ね返るので当たり判定はあるっぽい。なぜだといろいろ悩んだ結果、 原因は、プレハブ側にスクリプトを設定していなかったこと。2つのスクリプトが必要。

  • 「プレハブから作るという動き」はあるゲームオブジェクトにスクリプトを設定する
  • 「当たったら消えるという動き」はそのプレハブにスクリプトを設定する

プレハブで作るボールが無限増殖する

プレハブの方に、「プレハブから作るという動き」のスクリプトを設定してしまっていたため。 これのせいで、プレハブから作られたオブジェクトがプレハブから作りそのオブジェクトがプレハブからオブジェクトを作り...という無限増殖になってしまっていた。

バーの移動とカメラ移動

これを主に参考にした
Unityで3D空間内をTPSっぽくWASD移動するキャラをつくる - ささみ雑記帳

メインカメラが取得できない

メインカメラオブジェクトは、 Camera.main.transform みたいにして簡単に取得できるはずなのに、The name 'Camera' does not exist in the current context みたいなエラーがでて困ってた。 原因は、Camera.csっていうスクリプトを作ってて Camera っていうクラスを自分で作ってしまっていてそっちを参照されていたからっぽい。 名前を変えるか、 UnityEngine.Camera.main みたいにするとよいらしい。

手前の壁を透過したい

舞台が壁に囲まれた箱の中なので、カメラがその外に居るので中が見えない。 なので普通の考えとして、手前側になる壁は消えてほしい。 カメラを横移動して視点を変えることもできるようにしているので、その時その時で手前の壁が消えてほしいし奥の壁は表示されてほしい。 Unity カメラ 壁 透過 とかでいろいろググった結果こうなった。

    void Update()
    {
        // 一旦、壁を全部表示する
        GameObject[] transparentWalls = GameObject.FindGameObjectsWithTag("transparentWall"); // 透過したい側面の横壁にはタグをつけてある
        foreach( var wall in transparentWalls)
        {
            wall.GetComponent<Collider>().gameObject.GetComponent<Renderer>().enabled = true;
        }

        // 床面のオブジェクトを取得
        GameObject underWall = GameObject.Find("UnderWall");
        // 床面の4隅の座標を取得
        Vector3 underWall_pos = underWall.transform.position;
        Vector3 underWall_size = underWall.GetComponent<Renderer>().bounds.size;
        //Debug.Log("Ray Hit");

        // 中心点とサイズから四隅を計算
        ArrayList vectors = new ArrayList();
        vectors.Add( new Vector3(
            underWall_pos.x - (underWall_size / 2).x,
            underWall_pos.y,
            underWall_pos.z + (underWall_size / 2).z
        ));
        vectors.Add( new Vector3(
            underWall_pos.x + (underWall_size / 2).x,
            underWall_pos.y,
            underWall_pos.z + (underWall_size / 2).z
        ));
        vectors.Add( new Vector3(
            underWall_pos.x - (underWall_size / 2).x,
            underWall_pos.y,
            underWall_pos.z - (underWall_size / 2).z
        ));
        vectors.Add( new Vector3(
            underWall_pos.x + (underWall_size / 2).x,
            underWall_pos.y,
            underWall_pos.z - (underWall_size / 2).z
        ));

        // 四隅それぞれの座標とカメラを結ぶRayを作成
        RaycastHit hit;
        foreach (Vector3 vector in vectors)
        {
            Vector3 difference = (vector - this.transform.position);
            Vector3 direction = difference.normalized;
            Ray ray = new Ray(this.transform.position, direction);

            // 横壁とぶつかった場合表示を消す
            if (Physics.Raycast(ray, out hit))
            {
                GameObject hitObject = hit.collider.gameObject;
                Renderer renderer = hitObject.GetComponent<Renderer>();
                if (hitObject.tag == "transparentWall" && _renderer != null)
                {
                    renderer.enabled = false;
                }

            }
        }
    }

参考にした: Unity でカメラと被写体の間の遮蔽物を非表示にするスクリプトを作ってみた - Qiita

Rayというのは、ある座標からある座標に仮想的なレーザービームを打って、途中で遮られたかどうかと遮った物体が何かを取得できる機能。 通常はカメラからある座標へRayを飛ばすらしいが、座標から座標へ飛ばせる模様。
これを使って、床面の四隅とカメラを結ぶ線の間に壁があればそれを消すようにした。
四隅が見えているかどうか、とすることで思ってる通りの見せ方ができるようになった。
もっといいやり方ないのかな。

BGMのループ

ちなみにBGMは自作です。
で、ループ仕様なのだけどイントロ+ループ部分なBGMなのでどうしたらいいのかなってところは
https://tsumikiseisaku.com/blog/how-to-make-loop-sound/
をそのまま参考にしてできた。 waveファイルにはsmplチャンクという、ループ部分を指定するものがある。

効果音がちっさい

ボールが壁などにぶつかったらボールから音が出るのだけど、カメラから遠くて音が小さい。 通常、メインカメラにマイクがある想定となっており、これによりカメラと物体の距離に応じて遠くから聞こえたり左右から聞こえたりができる。
でも今回は距離感いらないので、ボールの座標から音を出すのではなく、カメラの座標から音を出すようにした。
これは簡単で、鳴らすコードの第2引数が鳴る座標なのでこれをカメラの座標を指定する。 AudioSource.PlayClipAtPoint(sound, m_camera.transform.position);

WebGLにしたら日本語が表示されない

デフォルトが日本語を含まないフォントで、かつ、表示できないときに別のフォントを使おうとしないため。
参考にした: 【Unity】WebGLで日本語テキストが表示されない問題について | ちりつもぶろぐ