kamihi のブログ

作成したゲームの情報や作り方を掲載します

UnityでMinecraft風ゲームを作る その1「スクリプトでキューブを作成」

私がUnityを始めた時、ブロックを積み重ねることのできるMinecraft風ゲームが作りたかったです。 初心者の私は自分なりに考えて作った結果、ブロックひとつひとつをGameObjectにしていたので、とてもパフォーマンスが悪かったのを覚えています。 そんな過去の自分に向けて記事にしていきます。

※おそらくもっとパフォーマンスが良いやり方があるかもしれないですが、それなりの結果を得る事を目標とします。

※難易度は①Unityの基礎的な操作をなんとなく理解、②Classがなんとなく扱える、③Listなどがなんとなく扱える。①②③があるとわかると思います。

今回はスクリプトでキューブを作成します。

①CriateCube.csを作成します

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

//UnityでMinecraft風ゲームを作る その1「スクリプトでキューブを作成」
namespace Sono1
{
    public class CriateCube : MonoBehaviour
    {
        //作成するキューブのマテリアル情報
        public Material cubeMaterial;
        //頂点を保存するリスト
        List<Vector3> verticeList = new List<Vector3>();
        //面の頂点情報のリスト
        List<int> faceList = new List<int>();
        //faceListに頂点情報登録するときの指標
        int faceCount;

        void Start()
        {
            //"Cube"という空オブジェクトを作成
            GameObject obj = new GameObject("Cube");
            //メッシュを登録するコンポーネント
            obj.AddComponent<MeshFilter>();
            //スクリプトより作成されたメッシュを登録
            obj.GetComponent<MeshFilter>().mesh = BuildVisualMesh();
            //MeshFilterのメッシュをレンダリングするコンポーネント
            obj.AddComponent<MeshRenderer>();
            //materialの設定
            obj.GetComponent<MeshRenderer>().material = cubeMaterial;

        }

        public Mesh BuildVisualMesh()
        {
            //座標0,0,0にキューブを作成する、verticeList、faceListに情報が登録される
            MeshCreater_Cube(0, 0, 0);
            //空のメッシュを作成
            Mesh mesh = new Mesh();
            //今回はやらなくて良いが、verticeList、faceListに情報が更新されたとき役にたつ
            mesh.Clear();
            //頂点情報を登録
            mesh.SetVertices(verticeList);
            //面情報を登録
            mesh.SetTriangles(faceList, 0);
            //メッシュの法線の情報を再計算、法線とは光の反射の向き
            mesh.RecalculateNormals();

            return mesh;
        }

        void MeshCreater_Cube(int x, int y, int z)
        {
            //Front
            verticeList.Add(new Vector3(x - 0.5f, y - 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y - 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y + 0.5f, z + 0.5f));
            AddFaceList();

            //Back
            verticeList.Add(new Vector3(x + 0.5f, y - 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y - 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y + 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y + 0.5f, z - 0.5f));
            AddFaceList();

            //Top
            verticeList.Add(new Vector3(x - 0.5f, y + 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y + 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y + 0.5f, z - 0.5f));
            AddFaceList();

            //Buttom
            verticeList.Add(new Vector3(x - 0.5f, y - 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y - 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y - 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y - 0.5f, z + 0.5f));
            AddFaceList();

            //Left
            verticeList.Add(new Vector3(x - 0.5f, y - 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y - 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y + 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x - 0.5f, y + 0.5f, z - 0.5f));
            AddFaceList();

            //Right
            verticeList.Add(new Vector3(x + 0.5f, y - 0.5f, z + 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y - 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y + 0.5f, z - 0.5f));
            verticeList.Add(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f));
            AddFaceList();
        }

        void AddFaceList()
        {
            //面情報を登録、面は三角が二つで四角になる
            faceList.Add(faceCount + 0);
            faceList.Add(faceCount + 1);
            faceList.Add(faceCount + 3);
            //二つめ三角
            faceList.Add(faceCount + 1);
            faceList.Add(faceCount + 2);
            faceList.Add(faceCount + 3);
            //次の頂点登録用に+4しておく
            faceCount += 4;
        }

        // Update is called once per frame
        void Update()
        {

        }
    }

}

②空のゲームオブジェクトをシーンに作成してCriateCube.csをアタッチしてください

③マテリアルが必要なので右クリック->Create->Materialを選択し、名前をCubeMaterialとしてください。Materialの設定はそのままで大丈夫です。

④CubeMaterialをCriateCube.csにアタッチしてください


ここまで作成したものをパッケージとし、Googleドライブに保存しておきました。 そこからダウンロードしてみてください。

UnityでMinecraft風ゲームを作る - Google ドライブ


⑤実行

f:id:zekutasiki:20190110203946p:plain
実行結果

0,0,0の位置にキューブが作成されました。

補足

Minecraft風ゲームはパフォーマンスが大切だと思ってます。

パフォーマンスの中でVerts(頂点数)は注意する項目のひとつだと思います。

作ったキューブを確認してみましょう。

①Statsボタンを押しましょう

②シーンに存在するMain CameraのClear FlagsをSolid Color、Directional LightのShadow TypeをNo Shadowsにしましょう

そうするとVertsがかなり減り、4になると思います。(残りの4はなにかは分かっていない(・v・;))

f:id:zekutasiki:20190101234923p:plain

実行すると・・・

f:id:zekutasiki:20190101235315p:plain

28になりましたね!

スクリプトのverticeList.Addが24回だったのでそれが足されたわけです。


f:id:zekutasiki:20190102190034p:plain
Backの面

上の図はBackの面のfaceListの順番です。 このように4角面は3角面二つで成り立っているのがわかります。 さらに面には表裏があり、時計回りに見えるほうが表です。 ついでですが、UnityのStandard Shaderは裏面が表示されません。


キューブの頂点が重なっている箇所が存在するので24ではなく8に抑えれると思った方もいると思います。キューブが単色の場合は大丈夫ですが、テクスチャをはる必要が出てきた場合、貼れなくなってしまうので、今回は24で作成しています。


スクリプトでキューブを作っている理由はこの後にキューブをたくさん置いて、見えない余計な面を削って、チャンク(塊)を作成するからです。 チャンクにすることによって複数のキューブが1つのゲームオブジェクトで済むわけです。 ゲームオブジェクトを分けてはいけない理由は

  • Transformなどの余計な情報がついてしまう
  • StatsのBatchesが増える
  • 重複面が存在してしまいVertsが増える

です。


次回はこのキューブをたくさん置いて、余計な面を削って、チャンクを作成してみようと思います(・v・)