ゾンビとUnity

ゾンビネタとUnityでのゲーム制作について綴るブログです。

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Unity ゲーム製作 8 ボクセルテレインのプロトタイプ

マインクラフトと同じようなボクセルテレインの上を歩き回るプロトタイプを作ります。
本来、ボクセルは等間隔に並べたセルの集合体なのですが、ボクセルの高さだけを不均一にします。

本来のボクセル


高さが不均一なボクセル

なんでこんなことをするのか?

1.少し変ったことがしたい。
2.地面の密度(硬さ)をより自然に表現できそう。


2.は例えば、スコップで穴を掘ることができ、一回の掘るアクションでボクセルひとつを消去できるシステムだとします。
自然界の地面は固いところもあれば、柔らかいところもあり、固いところはそれだけ力を入れて掘らないといけません。

普通のボクセルを積み上げたところは、サクサク掘り進めることができます。
普通のボクセルの高さが1mだとしたら、1回で1mの地面を掘ることができます。

でも、高さが低いボクセルを積み上げたところは、普通のボクセルと同じ回数掘っても、なかなか深く掘ることができません。
仮に高さが10cmのボクセルだとすると、1回で10cmしか掘ることができないことになります。
縦に圧縮されているから硬いのです。



せっかくだから、プロシージャル・ジェネレーションしてみる

前回、補足説明した Procedural Generation (プログラムでの自動生成) で高さが不均一なボクセルテレインを作ります。
その上を2Dゆにてぃちゃんに走ってもらいます。


抹茶パウダーをふんだんに振りかけたティラミスのようだ…。

ボクセルテレインを生成するコード
using UnityEngine;
public class VoxelTerrain : MonoBehaviour {

public int size_x = 500;
public int size_y = 5;
public Material rock;
public Material hard;
public Material medium;
public Material soft;
public Material water;
public Material grass;

bool created = false;
float[,] heightmap = null;

GameObject MakeVoxel(int x, int y, int z, float height, Material m) {
GameObject g = GameObject.CreatePrimitive(PrimitiveType.Cube);
if (g == null) return null;
g.name = "Voxel_" + x + "," + y + "," + z;
g.tag = "Terrain";
g.transform.parent = GameObject.Find("Voxel Terrain").transform;
g.transform.position = new Vector3(x, heightmap[x,z] + height / 2, z);
g.transform.localScale = new Vector3(1, height, 1);
g.GetComponent().material = m;
heightmap[x,z] += height;
return g;
}

public void Generate(int x, int y) {
GameObject parent = new GameObject("Voxel Terrain");
heightmap = new float[x,y];

//深        浅
//0123456789
//岩水岩硬硬硬中中柔草 基本パターン

//最も硬い岩盤の層
for (int i = 0; i < y; i++) {
for (int j = 0; j < x; j++) {
float height = Random.Range(0.1f, 0.8f);
if (!this.MakeVoxel(j, 0, i, height, this.rock))
throw new System.InvalidOperationException();
}
}
//地下水
for (int i = 0; i < y; i++) {
for (int j = 0; j < x; j++) {
float height = 1.0f;
GameObject g = this.MakeVoxel(j, 1, i, height, this.water);
if (g == null) throw new System.InvalidOperationException();
UnityEngine.Object.DestroyImmediate(g.GetComponent());
}
}
//地下水の上にある硬い岩盤の層
for (int i = 0; i < y; i++) {
for (int j = 0; j < x; j++) {
float s = 2.0f - heightmap[j,i];
float height = (s > 0) ? s : 0;
height += Random.Range(0.1f, 0.5f);
if (!this.MakeVoxel(j, 2, i, height, this.rock))
throw new System.InvalidOperationException();
}
}
//硬い土の層
for (int i = 0; i < 3; i++) {
for (int j = 0; j < y; j++) {
for (int k = 0; k < x; k++) {
float height = Random.Range(0.1f, 0.25f);
if (!this.MakeVoxel(k, 3 + i, j, height, this.hard))
throw new System.InvalidOperationException();
}
}
}
//中くらいに硬い土の層
for (int i = 0; i < 2; i++) {
for (int j = 0; j < y; j++) {
for (int k = 0; k < x; k++) {
float height = Random.Range(0.25f, 0.5f);
if (!this.MakeVoxel(k, 6 + i, j, height, this.medium))
throw new System.InvalidOperationException();
}
}
}
//柔らかい土の層
for (int i = 0; i < y; i++) {
for (int j = 0; j < x; j++) {
float height = Random.Range(0.5f, 0.75f);
if (!this.MakeVoxel(j, 8, i, height, this.soft))
throw new System.InvalidOperationException();
}
}
//草の層
for (int i = 0; i < y; i++) {
for (int j = 0; j < x; j++) {
float height = Random.Range(0.75f, 1.0f);
if (!this.MakeVoxel(j, 9, i, height, this.grass))
throw new System.InvalidOperationException();
}
}
}

void OnGUI() {
if (!this.created) {
if (GUI.Button(new Rect(0, 0, 150, 30), "Generate Terrain")) {
this.Generate(size_x, size_y);
this.created = true;
}
}
}
}

ゆにてぃちゃん2Dの処理は、前回のプロトタイプの流用です。
地形の自動生成は、シンプルな地層を定義して、一番深い層からキューブを積み上げていく処理をしています。
とりあえず、作ってみて2つの問題があると分かりました。


1.生成処理がとても重い。
2.思い通りの地形を作る方法を決めないといけない。



1.
このプロトタイプは、25,000(500 * 5 * 10)個のキューブを生成するのですが、私のPCだと作り終えるまでに数十秒かかります。
作り終えるまで「なうろーでぃんぐ」になるわけですが、ローディングの時間がバカになりません。何らかの方法で高速化が必要です。

全てのキューブにタグを付けているのですが、何故かタグを設定すると生成スピードが倍近く遅くなります。
ランタイムでタグ付けをするのは重いようです。
タグを付けている理由は、ゆにてぃちゃんが地面に接触したかどうかを判定するためです。
このままだと、別の方法で接地判定をする処理に変更する必要が出るかも知れません。

実測していないので分からないのですが、もしかしたら GameObject.CreatePrimitive() も重いのかも知れません。
もし、CreatePrimitive() よりも Instantiate() の方が速いのなら、最初の1つ目のキューブを CreatePrimitive() で作って、2つ目以降は Instantiate() で作るという処理が必要になります。
いずれにせよ、時間を計ってみないと分かりません…。
Unity free版ではプロファイラが使えないので、自分で処理を組まないといけません。


2.
今のところ、地形の断面図をドット絵で描いて、それをプログラムで読み込んで地形を作る方法だけ思いついてます。
この方法だと、地形に5mの奥行きがある場合、1mごとの断面図を5枚(5m分)作る必要があります。

AnisometricVoxelTerrain4.png

この断面図から地形を生成するような処理を作る必要がありそうです。
2万5千個(とは限りませんが)のキューブを手作業でチマチマ置くなんて作業はやってられないので、他に良い方法が思いついたら、その方法で地形を作ることにします。


とりあえず、今回はここで中断。
次回は地形生成処理の高速化と、記事の文章量が少ないようであれば、地形データの読み込み・保存もやってみたいです。
 
スポンサーサイト

- 0 Comments

Add your comment

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。