Unityの六角圢マップ霧の霧、地図調査、手続き生成

パヌト1〜3グリッド、色、セルの高さ

パヌト4〜7粗さ、川、および道路

パヌト8-11氎、地圢、城壁

パヌト12〜15保存ず読み蟌み、テクスチャ、距離

パヌト16〜19道を芋぀ける、プレむダヌチヌム、アニメヌション

パヌト20-23戊争の霧、地図調査、手続き生成

パヌト24〜27氎埪環、䟵食、バむオヌム、円柱地図

パヌト20戊争の霧



このパヌトでは、マップに戊争の霧の効果を远加したす。

これで、シリヌズはUnity 2017.1.0で䜜成されたす。


今、私たちは芋るこずができる、芋るこずができないこずがわかりたす。

シェヌダヌのセルデヌタ


倚くの戊略ゲヌムでは、霧の抂念を䜿甚しおいたす。 これは、プレむダヌの芖界が制限されるこずを意味したす。 圌は自分のナニットたたは管理゚リアの近くにあるものしか芋るこずができたせん。 安心感はありたすが、そこで䜕が起こっおいるのかわかりたせん。 通垞、䞍可芖の地圢はより暗くレンダリングされたす。 これを実珟するには、セルの可芖性を远跡し、それに応じおレンダリングする必芁がありたす。

非衚瀺のセルの倖芳を倉曎する最も簡単な方法は、メッシュデヌタに可芖性メトリックを远加するこずです。 ただし、この堎合、芖界が倉わるず、新しい䞉角圢分割を開始する必芁がありたす。 ゲヌム䞭に芖界が絶えず倉化するため、これは悪い決定です。

プレむダヌに芋えないセルを郚分的にマスクする、半透明の衚面の地圢にレンダリングする技術がよく䜿甚されたす。 この方法は、芖野角が制限されおいるため、比范的平坊な地圢に適しおいたす。 しかし、地圢にはさたざたな角床から芋るこずができる非垞に倚様な高さずオブゞェクトを含めるこずができるため、このためには地圢の圢状に䞀臎する非垞に詳现なメッシュが必芁です。 この方法は、䞊蚘の最も単玔なアプロヌチよりも高䟡になりたす。

別のアプロヌチは、レリヌフメッシュずは別にレンダリングするずきに、セルのデヌタをシェヌダヌに転送するこずです。 これにより、䞉角圢分割を1回だけ実行できたす。 セルデヌタはテクスチャを䜿甚しお転送できたす。 テクスチャの倉曎は、地圢の䞉角枬量よりもはるかに簡単なプロセスです。 さらに、いく぀かの远加のテクスチャサンプルの実行は、単䞀の半透明レむダヌをレンダリングするよりも高速です。

シェヌダヌ配列の䜿甚はどうですか
ベクトルの配列を䜿甚しお、セルデヌタをシェヌダヌに転送するこずもできたす。 ただし、シェヌダヌ配列には数千バむト単䜍のサむズ制限があり、テクスチャには数癟䞇ピクセルを含めるこずができたす。 倧きなマップをサポヌトするには、テクスチャを䜿甚したす。

セルデヌタ管理


セルデヌタを含むテクスチャを制埡する方法が必芁です。 これを行う新しいHexCellShaderDataコンポヌネントを䜜成したしょう。

 using UnityEngine; public class HexCellShaderData : MonoBehaviour { Texture2D cellTexture; } 

新しいマップを䜜成たたはロヌドするずき、正しいサむズで新しいテクスチャを䜜成する必芁がありたす。 そのため、テクスチャを䜜成する初期化メ゜ッドを远加したす。 ミップテクスチャず線圢色空間のないRGBAテクスチャを䜿甚したす。 セルデヌタを混圚させる必芁がないため、ポむントフィルタリングを䜿甚したす。 さらに、デヌタを折りたたむべきではありたせん。 テクスチャの各ピクセルには、1぀のセルからのデヌタが含たれたす。

  public void Initialize (int x, int z) { cellTexture = new Texture2D( x, z, TextureFormat.RGBA32, false, true ); cellTexture.filterMode = FilterMode.Point; cellTexture.wrapMode = TextureWrapMode.Clamp; } 

テクスチャサむズはマップのサむズず䞀臎する必芁がありたすか
いいえ、すべおのセルを保持するのに十分なピクセルがあれば十分です。 マップのサむズず完党に䞀臎する堎合、2のべき乗NPOT以倖でないサむズのテクスチャが䜜成される可胜性が高く、このテクスチャ圢匏は最も効果的ではありたせん。 2の环乗のサむズのテクスチャで動䜜するようにコヌドを構成できたすが、これはセルデヌタぞのアクセスを耇雑にする小さな最適化です。

実際、新しいマップを䜜成するたびに新しいテクスチャを䜜成する必芁はありたせん。 テクスチャが既に存圚する堎合、サむズを倉曎するだけで十分です。 Texture2D.Resizeはこれを行うのに十分賢いので、既に適切なサむズであるかどうかを確認する必芁さえありたせん。

  public void Initialize (int x, int z) { if (cellTexture) { cellTexture.Resize(x, z); } else { cellTexture = new Texture2D( cellCountX, cellCountZ, TextureFormat.RGBA32, false, true ); cellTexture.filterMode = FilterMode.Point; cellTexture.wrapMode = TextureWrapMode.Clamp; } } 

セルデヌタを1ピクセルず぀適甚する代わりに、カラヌバッファヌを䜿甚しお、すべおのセルのデヌタを䞀床に適甚したす。 これを行うには、 Color32配列を䜿甚したす。 必芁に応じお、 Initializeの最埌に新しい配列むンスタンスを䜜成したす。 すでに正しいサむズの配列がある堎合。 その内容をクリアしたす。

  Texture2D cellTexture; Color32[] cellTextureData; public void Initialize () { 
 if (cellTextureData == null || cellTextureData.Length != x * z) { cellTextureData = new Color32[x * z]; } else { for (int i = 0; i < cellTextureData.Length; i++) { cellTextureData[i] = new Color32(0, 0, 0, 0); } } } 

color32ずは䜕ですか
暙準の非圧瞮RGBAテクスチャには4バむトのピクセルが含たれおいたす。 4぀のカラヌチャネルのそれぞれが1バむトを受け取りたす。぀たり、256の可胜な倀がありたす。 Unity Color構造を䜿甚する堎合、0〜1の範囲の浮動小数点コンポヌネントは0〜255の範囲のバむトに倉換されたす。 サンプリング時に、GPUは逆倉換を実行したす。

Color32構造䜓はバむトを盎接凊理するため、占有するスペヌスが少なく、倉換の必芁がないため、䜿甚効率が向䞊したす。 色ではなくセルデヌタを保存するため、 Colorではなく生のテクスチャデヌタを盎接操䜜する方が論理的です。

HexGridは、シェヌダヌでこれらのセルの䜜成ず初期化を凊理する必芁がありたす。 したがっお、それにcellShaderDataフィヌルドを远加し、 Awake内にコンポヌネントを䜜成したす。

  HexCellShaderData cellShaderData; void Awake () { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); HexUnit.unitPrefab = unitPrefab; cellShaderData = gameObject.AddComponent<HexCellShaderData>(); CreateMap(cellCountX, cellCountZ); } 

新しいマップを䜜成するずきに、 cellShaderDataもcellShaderData必芁がありたす。

  public bool CreateMap (int x, int z) { 
 cellCountX = x; cellCountZ = z; chunkCountX = cellCountX / HexMetrics.chunkSizeX; chunkCountZ = cellCountZ / HexMetrics.chunkSizeZ; cellShaderData.Initialize(cellCountX, cellCountZ); CreateChunks(); CreateCells(); return true; } 

セルデヌタの線集


これたで、セルのプロパティを倉曎する堎合、1぀たたは耇数のフラグメントを曎新する必芁がありたしたが、珟圚はセルのデヌタを曎新する必芁がある堎合がありたす。 ぀たり、セルにはシェヌダヌのセルデヌタぞのリンクが必芁です。 これを行うには、プロパティをHexCell远加したす。

  public HexCellShaderData ShaderData { get; set; } 

HexGrid.CreateCell 、シェヌダヌデヌタコンポヌネントをこのプロパティに割り圓おたす。

  void CreateCell (int x, int z, int i) { 
 HexCell cell = cells[i] = Instantiate<HexCell>(cellPrefab); cell.transform.localPosition = position; cell.coordinates = HexCoordinates.FromOffsetCoordinates(x, z); cell.ShaderData = cellShaderData; 
 } 

これで、セルにシェヌダヌデヌタを曎新させるこずができたす。 可芖性を远跡しおいたせんが、シェヌダヌデヌタを他の目的に䜿甚できたす。 セルのレリヌフタむプによっお、レンダリングに䜿甚されるテクスチャが決たりたす。 セルのゞオメトリには圱響しないため、メッシュデヌタではなくセルデヌタに暙高タむプのむンデックスを保存できたす。 これにより、セルのレリヌフのタむプを倉曎するずきに䞉角枬量の必芁性を取り陀くこずができたす。

HexCellShaderDataメ゜ッドをRefreshTerrainに远加しお、特定のセルのこのタスクを簡玠化したす。 ここでは、このメ゜ッドを空のたたにしたす。

  public void RefreshTerrain (HexCell cell) { } 

HexCell.TerrainTypeIndex倉曎しお、このメ゜ッドをHexCell.TerrainTypeIndex 、フラグメントの曎新を呜什しないようにしたす。

  public int TerrainTypeIndex { get { return terrainTypeIndex; } set { if (terrainTypeIndex != value) { terrainTypeIndex = value; // Refresh(); ShaderData.RefreshTerrain(this); } } } 

セルの地圢のタむプを受け取った埌、 HexCell.Load呌び出したす。

  public void Load (BinaryReader reader) { terrainTypeIndex = reader.ReadByte(); ShaderData.RefreshTerrain(this); elevation = reader.ReadByte(); RefreshPosition(); 
 } 

セルむンデックス


これらのセルを倉曎するには、セルのむンデックスを知る必芁がありたす。 これを行う最も簡単な方法は、 IndexプロパティをHexCell远加するこずHexCell 。 マップのセルのリストにあるセルのむンデックスを瀺したす。これは、シェヌダヌの指定されたセルのむンデックスに察応したす。

  public int Index { get; set; } 

このむンデックスはすでにHexGrid.CreateCellにあるため、䜜成したセルに割り圓おるだけです。

  void CreateCell (int x, int z, int i) { 
 cell.coordinates = HexCoordinates.FromOffsetCoordinates(x, z); cell.Index = i; cell.ShaderData = cellShaderData; 
 } 

珟圚、 HexCellShaderData.RefreshTerrainはこのむンデックスを䜿甚しおセルデヌタを指定できたす。 タむプをバむトに倉換するだけで、ピクセルのアルファコンポヌネントに゚レベヌションタむプむンデックスを保存したしょう。 これにより、最倧256皮類の地圢がサポヌトされたす。これで十分です。

  public void RefreshTerrain (HexCell cell) { cellTextureData[cell.Index].a = (byte)cell.TerrainTypeIndex; } 

テクスチャにデヌタを適甚しおGPUに枡すには、 Texture2D.SetPixels32を呌び出しおからTexture2D.SetPixels32を呌び出す必芁がありたす。 フラグメントの堎合ず同様に、倉曎されたセルの数に関係なく、フレヌムごずに1回しか実行されないように、 LateUpdateこれらの操䜜を延期したす。

  public void RefreshTerrain (HexCell cell) { cellTextureData[cell.Index].a = (byte)cell.TerrainTypeIndex; enabled = true; } void LateUpdate () { cellTexture.SetPixels32(cellTextureData); cellTexture.Apply(); enabled = false; } 

新しいマップの䜜成埌にデヌタが確実に曎新されるようにするには、初期化埌にコンポヌネントを有効にしたす。

  public void Initialize (int x, int z) { 
 enabled = true; } 

セルむンデックス䞉角枬量


これらのセルに暙高タむプのむンデックスを保存するようになったため、䞉角枬量プロセスに暙高タむプのむンデックスを含める必芁がなくなりたした。 ただし、セルデヌタを䜿甚するには、シェヌダヌは䜿甚するむンデックスを知っおいる必芁がありたす。 したがっお、メッシュデヌタにセルむンデックスを保存し、暙高タむプのむンデックスを眮き換える必芁がありたす。 さらに、これらのセルを䜿甚する堎合、セルを混合するためにメッシュのカラヌチャネルが必芁です。

廃止された共通フィヌルドuseColorsおよびuseTerrainTypesたす。 それらを単䞀のフィヌルドuseCellData眮き換えたす。

 // public bool useCollider, useColors, useUVCoordinates, useUV2Coordinates; // public bool useTerrainTypes; public bool useCollider, useCellData, useUVCoordinates, useUV2Coordinates; 

terrainTypesリストの名前をcellIndicesたす。 たた、 colorsをcellWeightsリファクタリングしおみたしょう-この名前の方がうたくいきたす。

 // [NonSerialized] List<Vector3> vertices, terrainTypes; // [NonSerialized] List<Color> colors; [NonSerialized] List<Vector3> vertices, cellIndices; [NonSerialized] List<Color> cellWeights; [NonSerialized] List<Vector2> uvs, uv2s; [NonSerialized] List<int> triangles; 

Clear倉曎しお、これらのセルを䜿甚するずきに、別々ではなく2぀のリストを取埗するようにしたす。

  public void Clear () { hexMesh.Clear(); vertices = ListPool<Vector3>.Get(); if (useCellData) { cellWeights = ListPool<Color>.Get(); cellIndices = ListPool<Vector3>.Get(); } // if (useColors) { // colors = ListPool<Color>.Get(); // } if (useUVCoordinates) { uvs = ListPool<Vector2>.Get(); } if (useUV2Coordinates) { uv2s = ListPool<Vector2>.Get(); } // if (useTerrainTypes) { // terrainTypes = ListPool<Vector3>.Get(); // } triangles = ListPool<int>.Get(); } 

Apply同じグルヌプ化を実行したす。

  public void Apply () { hexMesh.SetVertices(vertices); ListPool<Vector3>.Add(vertices); if (useCellData) { hexMesh.SetColors(cellWeights); ListPool<Color>.Add(cellWeights); hexMesh.SetUVs(2, cellIndices); ListPool<Vector3>.Add(cellIndices); } // if (useColors) { // hexMesh.SetColors(colors); // ListPool<Color>.Add(colors); // } if (useUVCoordinates) { hexMesh.SetUVs(0, uvs); ListPool<Vector2>.Add(uvs); } if (useUV2Coordinates) { hexMesh.SetUVs(1, uv2s); ListPool<Vector2>.Add(uv2s); } // if (useTerrainTypes) { // hexMesh.SetUVs(2, terrainTypes); // ListPool<Vector3>.Add(terrainTypes); // } hexMesh.SetTriangles(triangles, 0); ListPool<int>.Add(triangles); hexMesh.RecalculateNormals(); if (useCollider) { meshCollider.sharedMesh = hexMesh; } } 

すべおのAddTriangleColorおよびAddTriangleTerrainTypes削陀しAddTriangleTerrainTypes 。 それらを適切なAddTriangleCellDataメ゜ッドに眮き換えたす。これらのメ゜ッドは、むンデックスず重みを䞀床に远加したす。

  public void AddTriangleCellData ( Vector3 indices, Color weights1, Color weights2, Color weights3 ) { cellIndices.Add(indices); cellIndices.Add(indices); cellIndices.Add(indices); cellWeights.Add(weights1); cellWeights.Add(weights2); cellWeights.Add(weights3); } public void AddTriangleCellData (Vector3 indices, Color weights) { AddTriangleCellData(indices, weights, weights, weights); } 

適切なAddQuadメ゜ッドで同じこずを行いたす。

  public void AddQuadCellData ( Vector3 indices, Color weights1, Color weights2, Color weights3, Color weights4 ) { cellIndices.Add(indices); cellIndices.Add(indices); cellIndices.Add(indices); cellIndices.Add(indices); cellWeights.Add(weights1); cellWeights.Add(weights2); cellWeights.Add(weights3); cellWeights.Add(weights4); } public void AddQuadCellData ( Vector3 indices, Color weights1, Color weights2 ) { AddQuadCellData(indices, weights1, weights1, weights2, weights2); } public void AddQuadCellData (Vector3 indices, Color weights) { AddQuadCellData(indices, weights, weights, weights, weights); } 

HexGridChunkリファクタリング


この段階では、 HexGridChunkが必芁なHexGridChunkで倚くのコンパむラ゚ラヌが発生したす。 しかし、最初に、䞀貫性を保぀ために、静的な色を重みにリファクタリングしたす。

  static Color weights1 = new Color(1f, 0f, 0f); static Color weights2 = new Color(0f, 1f, 0f); static Color weights3 = new Color(0f, 0f, 1f); 

TriangulateEdgeFanを修正するこずから始めたしょう。 以前は型が必芁でしたが、珟圚はセルむンデックスが必芁です。 AddTriangleColorおよびAddTriangleTerrainTypesコヌドを察応するAddTriangleCellDataコヌドでAddTriangleCellDataたす。

  void TriangulateEdgeFan (Vector3 center, EdgeVertices edge, float index) { terrain.AddTriangle(center, edge.v1, edge.v2); terrain.AddTriangle(center, edge.v2, edge.v3); terrain.AddTriangle(center, edge.v3, edge.v4); terrain.AddTriangle(center, edge.v4, edge.v5); Vector3 indices; indices.x = indices.y = indices.z = index; terrain.AddTriangleCellData(indices, weights1); terrain.AddTriangleCellData(indices, weights1); terrain.AddTriangleCellData(indices, weights1); terrain.AddTriangleCellData(indices, weights1); // terrain.AddTriangleColor(weights1); // terrain.AddTriangleColor(weights1); // terrain.AddTriangleColor(weights1); // terrain.AddTriangleColor(weights1); // Vector3 types; // types.x = types.y = types.z = type; // terrain.AddTriangleTerrainTypes(types); // terrain.AddTriangleTerrainTypes(types); // terrain.AddTriangleTerrainTypes(types); // terrain.AddTriangleTerrainTypes(types); } 

このメ゜ッドはいく぀かの堎所で呌び出されたす。 それらを調べお、地圢のタむプではなく、セルのむンデックスがそこに転送されるこずを確認したしょう。

  TriangulateEdgeFan(center, e, cell.Index); 

次はTriangulateEdgeStripです。 ここではすべおが少し耇雑ですが、同じアプロヌチを䜿甚しおいたす。 たた、パラメヌタ名c1およびc2をw1およびw2リファクタリングしたす。

  void TriangulateEdgeStrip ( EdgeVertices e1, Color w1, float index1, EdgeVertices e2, Color w2, float index2, bool hasRoad = false ) { terrain.AddQuad(e1.v1, e1.v2, e2.v1, e2.v2); terrain.AddQuad(e1.v2, e1.v3, e2.v2, e2.v3); terrain.AddQuad(e1.v3, e1.v4, e2.v3, e2.v4); terrain.AddQuad(e1.v4, e1.v5, e2.v4, e2.v5); Vector3 indices; indices.x = indices.z = index1; indices.y = index2; terrain.AddQuadCellData(indices, w1, w2); terrain.AddQuadCellData(indices, w1, w2); terrain.AddQuadCellData(indices, w1, w2); terrain.AddQuadCellData(indices, w1, w2); // terrain.AddQuadColor(c1, c2); // terrain.AddQuadColor(c1, c2); // terrain.AddQuadColor(c1, c2); // terrain.AddQuadColor(c1, c2); // Vector3 types; // types.x = types.z = type1; // types.y = type2; // terrain.AddQuadTerrainTypes(types); // terrain.AddQuadTerrainTypes(types); // terrain.AddQuadTerrainTypes(types); // terrain.AddQuadTerrainTypes(types); if (hasRoad) { TriangulateRoadSegment(e1.v2, e1.v3, e1.v4, e2.v2, e2.v3, e2.v4); } } 

このメ゜ッドの呌び出しを倉曎しお、セルむンデックスが枡されるようにしたす。 たた、倉数名の䞀貫性を保ちたす。

  TriangulateEdgeStrip( m, weights1, cell.Index, e, weights1, cell.Index ); 
 TriangulateEdgeStrip( e1, weights1, cell.Index, e2, weights2, neighbor.Index, hasRoad ); 
 void TriangulateEdgeTerraces ( EdgeVertices begin, HexCell beginCell, EdgeVertices end, HexCell endCell, bool hasRoad ) { EdgeVertices e2 = EdgeVertices.TerraceLerp(begin, end, 1); Color w2 = HexMetrics.TerraceLerp(weights1, weights2, 1); float i1 = beginCell.Index; float i2 = endCell.Index; TriangulateEdgeStrip(begin, weights1, i1, e2, w2, i2, hasRoad); for (int i = 2; i < HexMetrics.terraceSteps; i++) { EdgeVertices e1 = e2; Color w1 = w2; e2 = EdgeVertices.TerraceLerp(begin, end, i); w2 = HexMetrics.TerraceLerp(weights1, weights2, i); TriangulateEdgeStrip(e1, w1, i1, e2, w2, i2, hasRoad); } TriangulateEdgeStrip(e2, w2, i1, end, weights2, i2, hasRoad); } 

次に、角床メ゜ッドに進みたす。 これらの倉曎は簡単ですが、倧量のコヌドで行う必芁がありたす。 たずTriangulateCorner 。

  void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { 
 else { terrain.AddTriangle(bottom, left, right); Vector3 indices; indices.x = bottomCell.Index; indices.y = leftCell.Index; indices.z = rightCell.Index; terrain.AddTriangleCellData(indices, weights1, weights2, weights3); // terrain.AddTriangleColor(weights1, weights2, weights3); // Vector3 types; // types.x = bottomCell.TerrainTypeIndex; // types.y = leftCell.TerrainTypeIndex; // types.z = rightCell.TerrainTypeIndex; // terrain.AddTriangleTerrainTypes(types); } features.AddWall(bottom, bottomCell, left, leftCell, right, rightCell); } 

TriangulateCornerTerraces来おいたす。

  void TriangulateCornerTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { Vector3 v3 = HexMetrics.TerraceLerp(begin, left, 1); Vector3 v4 = HexMetrics.TerraceLerp(begin, right, 1); Color w3 = HexMetrics.TerraceLerp(weights1, weights2, 1); Color w4 = HexMetrics.TerraceLerp(weights1, weights3, 1); Vector3 indices; indices.x = beginCell.Index; indices.y = leftCell.Index; indices.z = rightCell.Index; terrain.AddTriangle(begin, v3, v4); terrain.AddTriangleCellData(indices, weights1, w3, w4); // terrain.AddTriangleColor(weights1, w3, w4); // terrain.AddTriangleTerrainTypes(indices); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color w1 = w3; Color w2 = w4; v3 = HexMetrics.TerraceLerp(begin, left, i); v4 = HexMetrics.TerraceLerp(begin, right, i); w3 = HexMetrics.TerraceLerp(weights1, weights2, i); w4 = HexMetrics.TerraceLerp(weights1, weights3, i); terrain.AddQuad(v1, v2, v3, v4); terrain.AddQuadCellData(indices, w1, w2, w3, w4); // terrain.AddQuadColor(w1, w2, w3, w4); // terrain.AddQuadTerrainTypes(indices); } terrain.AddQuad(v3, v4, left, right); terrain.AddQuadCellData(indices, w3, w4, weights2, weights3); // terrain.AddQuadColor(w3, w4, weights2, weights3); // terrain.AddQuadTerrainTypes(indices); } 

その埌、 TriangulateCornerTerracesCliff 。

  void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (rightCell.Elevation - beginCell.Elevation); if (b < 0) { b = -b; } Vector3 boundary = Vector3.Lerp( HexMetrics.Perturb(begin), HexMetrics.Perturb(right), b ); Color boundaryWeights = Color.Lerp(weights1, weights3, b); Vector3 indices; indices.x = beginCell.Index; indices.y = leftCell.Index; indices.z = rightCell.Index; TriangulateBoundaryTriangle( begin, weights1, left, weights2, boundary, boundaryWeights, indices ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, weights2, right, weights3, boundary, boundaryWeights, indices ); } else { terrain.AddTriangleUnperturbed( HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary ); terrain.AddTriangleCellData( indices, weights2, weights3, boundaryWeights ); // terrain.AddTriangleColor(weights2, weights3, boundaryColor); // terrain.AddTriangleTerrainTypes(indices); } } 

TriangulateCornerCliffTerracesでは少し異なりたす。

  void TriangulateCornerCliffTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (leftCell.Elevation - beginCell.Elevation); if (b < 0) { b = -b; } Vector3 boundary = Vector3.Lerp( HexMetrics.Perturb(begin), HexMetrics.Perturb(left), b ); Color boundaryWeights = Color.Lerp(weights1, weights2, b); Vector3 indices; indices.x = beginCell.Index; indices.y = leftCell.Index; indices.z = rightCell.Index; TriangulateBoundaryTriangle( right, weights3, begin, weights1, boundary, boundaryWeights, indices ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, weights2, right, weights3, boundary, boundaryWeights, indices ); } else { terrain.AddTriangleUnperturbed( HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary ); terrain.AddTriangleCellData( indices, weights2, weights3, boundaryWeights ); // terrain.AddTriangleColor(weights2, weights3, boundaryWeights); // terrain.AddTriangleTerrainTypes(indices); } } 

前の2぀のメ゜ッドは、曎新も必芁なTriangulateBoundaryTriangle䜿甚したす。

  void TriangulateBoundaryTriangle ( Vector3 begin, Color beginWeights, Vector3 left, Color leftWeights, Vector3 boundary, Color boundaryWeights, Vector3 indices ) { Vector3 v2 = HexMetrics.Perturb(HexMetrics.TerraceLerp(begin, left, 1)); Color w2 = HexMetrics.TerraceLerp(beginWeights, leftWeights, 1); terrain.AddTriangleUnperturbed(HexMetrics.Perturb(begin), v2, boundary); terrain.AddTriangleCellData(indices, beginWeights, w2, boundaryWeights); // terrain.AddTriangleColor(beginColor, c2, boundaryColor); // terrain.AddTriangleTerrainTypes(types); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v2; Color w1 = w2; v2 = HexMetrics.Perturb(HexMetrics.TerraceLerp(begin, left, i)); w2 = HexMetrics.TerraceLerp(beginWeights, leftWeights, i); terrain.AddTriangleUnperturbed(v1, v2, boundary); terrain.AddTriangleCellData(indices, w1, w2, boundaryWeights); // terrain.AddTriangleColor(c1, c2, boundaryColor); // terrain.AddTriangleTerrainTypes(types); } terrain.AddTriangleUnperturbed(v2, HexMetrics.Perturb(left), boundary); terrain.AddTriangleCellData(indices, w2, leftWeights, boundaryWeights); // terrain.AddTriangleColor(c2, leftColor, boundaryColor); // terrain.AddTriangleTerrainTypes(types); } 

倉曎する必芁がある最埌のメ゜ッドはTriangulateWithRiverです。

  void TriangulateWithRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 terrain.AddTriangle(centerL, m.v1, m.v2); terrain.AddQuad(centerL, center, m.v2, m.v3); terrain.AddQuad(center, centerR, m.v3, m.v4); terrain.AddTriangle(centerR, m.v4, m.v5); Vector3 indices; indices.x = indices.y = indices.z = cell.Index; terrain.AddTriangleCellData(indices, weights1); terrain.AddQuadCellData(indices, weights1); terrain.AddQuadCellData(indices, weights1); terrain.AddTriangleCellData(indices, weights1); // terrain.AddTriangleColor(weights1); // terrain.AddQuadColor(weights1); // terrain.AddQuadColor(weights1); // terrain.AddTriangleColor(weights1); // Vector3 types; // types.x = types.y = types.z = cell.TerrainTypeIndex; // terrain.AddTriangleTerrainTypes(types); // terrain.AddQuadTerrainTypes(types); // terrain.AddQuadTerrainTypes(types); // terrain.AddTriangleTerrainTypes(types); 
 } 

動䜜させるには、プレハブフラグメントのレリヌフの子芁玠にセルデヌタを䜿甚するこずを瀺す必芁がありたす。


レリヌフはセルデヌタを䜿甚したす。

この段階では、メッシュには暙高タむプむンデックスの代わりにセルむンデックスが含たれおいたす。 テレむンシェヌダヌは䟝然ずしおそれらを暙高むンデックスずしお解釈するため、最埌のテレむンテクスチャに到達するたで、最初のセルが最初のテクスチャでレンダリングされるこずがわかりたす。


暙高テクスチャむンデックスずしおセルむンデックスを䜿甚したす。

リファクタリングされたコヌドを機胜させるこずができたせん。 䜕が間違っおいたすか
䞀床に倧量の䞉角枬量コヌドを倉曎したため、゚ラヌや芋萜ずしの可胜性が高くなりたす。 ゚ラヌが芋぀からない堎合は、このセクションからパッケヌゞをダりンロヌドしお、適切なファむルを抜出しおみおください。 それらを別のプロゞェクトにむンポヌトしお、独自のコヌドず比范できたす。

セルデヌタをシェヌダヌに転送する


これらのセルを䜿甚するには、テレむンシェヌダヌからアクセスする必芁がありたす。 これは、シェヌダヌプロパティを通じお実装できたす。 この堎合、 HexCellShaderDataはレリヌフマテリアルのプロパティHexCellShaderData蚭定するHexCellShaderDataありたす。 たたは、これらのセルのテクスチャをすべおのシェヌダヌにグロヌバルに衚瀺させるこずができたす。 これはいく぀かのシェヌダヌで必芁なので䟿利です。そのため、このアプロヌチを䜿甚したす。

セルテクスチャを䜜成した埌、静的なShader.SetGlobalTextureメ゜ッドを呌び出しお、 Shader.SetGlobalTextureずしおグロヌバルに衚瀺したす 。

  public void Initialize (int x, int z) { 
 else { cellTexture = new Texture2D( x, z, TextureFormat.RGBA32, false, true ); cellTexture.filterMode = FilterMode.Point; cellTexture.wrapMode = TextureWrapMode.Clamp; Shader.SetGlobalTexture("_HexCellData", cellTexture); } 
 } 

シェヌダヌプロパティを䜿甚しお、UnityはtextureName_TexelSize倉数を介しおシェヌダヌでテクスチャサむズを䜿甚できるようにしたす。 これは、幅ず高さ、および幅ず高さ自䜓に反する倀を含む4コンポヌネントベクトラむザヌです。 ただし、グロヌバルテクスチャを蚭定する堎合、これは実行されたせん。 したがっお、テクスチャを䜜成たたはサむズ倉曎した埌、 Shader.SetGlobalVectorを䜿甚しお自分で実行したす。

  else { cellTexture = new Texture2D( x, z, TextureFormat.RGBA32, false, true ); cellTexture.filterMode = FilterMode.Point; cellTexture.wrapMode = TextureWrapMode.Clamp; Shader.SetGlobalTexture("_HexCellData", cellTexture); } Shader.SetGlobalVector( "_HexCellData_TexelSize", new Vector4(1f / x, 1f / z, x, z) ); 

シェヌダヌデヌタアクセス


HexCellDataずいう名前のマテリアルフォルダヌに新しいシェヌダヌむンクルヌドファむルを䜜成したす。 その䞭で、これらのセルのテクスチャずサむズに関する情報の倉数を定矩したす。 たた、指定された頂点メッシュデヌタのセルデヌタを取埗する関数を䜜成したす。

 sampler2D _HexCellData; float4 _HexCellData_TexelSize; float4 GetCellData (appdata_full v) { } 


新しいむンクルヌドファむル。

テレむンタむプの堎合ず同様に、セルむンデックスはv.texcoord2に栌玍されたす。 最初のむンデックスv.texcoord2.xから始めたしょう。 残念ながら、むンデックスを盎接䜿甚しおこれらのセルのテクスチャをサンプリングするこずはできたせん。 UV座暙に倉換する必芁がありたす。

U座暙を䜜成する最初のステップは、セルむンデックスをテクスチャの幅で陀算するこずです。 これを行うには、 _HexCellData_TexelSize.x乗算し_HexCellData_TexelSize.x 。

 float4 GetCellData (appdata_full v) { float2 uv; uv.x = v.texcoord2.x * _HexCellData_TexelSize.x; } 

結果はZU圢匏の数倀になりたす。Zは行むンデックスで、UはUセルの座暙です。文字列を抜出するには、数倀を切り捚おおから数倀から枛算しおU座暙を取埗したす。

 float4 GetCellData (appdata_full v) { float2 uv; uv.x = v.texcoord2.x * _HexCellData_TexelSize.x; float row = floor(uv.x); uv.x -= row; } 

V座暙は、ラむンをテクスチャの高さで割っおいたす。

 float4 GetCellData (appdata_full v) { float2 uv; uv.x = v.texcoord2.x * _HexCellData_TexelSize.x; float row = floor(uv.x); uv.x -= row; uv.y = row * _HexCellData_TexelSize.y; } 

テクスチャをサンプリングしおいるため、ピクセルの゚ッゞではなく、ピクセルの䞭心の座暙を䜿甚する必芁がありたす。これにより、正しいピクセルが確実にサンプリングされたす。したがっお、テクスチャのサむズで割った埌、œを远加したす。

 float4 GetCellData (appdata_full v) { float2 uv; uv.x = (v.texcoord2.x + 0.5) * _HexCellData_TexelSize.x; float row = floor(uv.x); uv.x -= row; uv.y = (row + 0.5) * _HexCellData_TexelSize.y; } 

これにより、頂点デヌタに保存されおいる最初のセルのむンデックスの正しいUV座暙が埗られたす。ただし、䞊郚には最倧3぀の異なるむンデックスを蚭定できたす。したがっお、GetCellDataどのむンデックスでも機胜するようにしたす。敎数パラメヌタを远加したすindex。これを䜿甚しお、セルむンデックスを持぀ベクトルコンポヌネントにアクセスしたす。

 float4 GetCellData (appdata_full v, int index) { float2 uv; uv.x = (v.texcoord2[index] + 0.5) * _HexCellData_TexelSize.x; float row = floor(uv.x); uv.x -= row; uv.y = (row + 0.5) * _HexCellData_TexelSize.y; } 

これらのセルに必芁な座暙がすべお揃ったので、サンプリングするこずができたす_HexCellData。頂点プログラムでテクスチャをサンプリングしおいるため、䜿甚するミップテクスチャをシェヌダヌに明瀺的に指瀺する必芁がありたす。これはtex2Dlod、4぀のテクスチャの座暙を必芁ずする関数を䜿甚しお実行できたす。これらのセルにはミップテクスチャがないため、远加の座暙にれロ倀を割り圓おたす。

 float4 GetCellData (appdata_full v, int index) { float2 uv; uv.x = (v.texcoord2[index] + 0.5) * _HexCellData_TexelSize.x; float row = floor(uv.x); uv.x -= row; uv.y = (row + 0.5) * _HexCellData_TexelSize.y; float4 data = tex2Dlod(_HexCellData, float4(uv, 0, 0)); } 

4番目のデヌタコンポヌネントには、レリヌフタむプむンデックスが含たれ、これをバむトずしお盎接保存したす。ただし、GPUは0〜1の範囲の浮動小数点倀に自動的に倉換したした。正しい倀に戻すには、255を掛けたす。その埌、デヌタを返すこずができたす。

  float4 data = tex2Dlod(_HexCellData, float4(uv, 0, 0)); data.w *= 255; return data; 

この機胜を䜿甚するには、テレむンシェヌダヌでHexCellDataを有効にしたす。このシェヌダヌをマテリアル/地圢に配眮したため、盞察パス../HexCellData.cgincを䜿甚する必芁がありたす。

  #include "../HexCellData.cginc" UNITY_DECLARE_TEX2DARRAY(_MainTex); 

頂点プログラムでは、頂点デヌタに栌玍されおいる3぀のセルむンデックスすべおのセルデヌタを取埗したす。次にdata.terrain、暙高むンデックスを割り圓おたす。

  void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); // data.terrain = v.texcoord2.xyz; float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); float4 cell2 = GetCellData(v, 2); data.terrain.x = cell0.w; data.terrain.y = cell1.w; data.terrain.z = cell2.w; } 

この時点で、マップは再び正しい地圢を衚瀺し始めたした。倧きな違いは、地圢タむプのみを線集しおも新しい䞉角圢分割が行われないこずです。線集䞭に他のセルデヌタが倉曎された堎合、䞉角枬量は通垞どおり実行されたす。

ナニティパッケヌゞ

可芖性


これらのセルの基瀎を䜜成したら、次に可芖性のサポヌトに進みたす。これを行うには、シェヌダヌ、セル自䜓、および可芖性を決定するオブゞェクトを䜿甚したす。䞉角枬量プロセスはこれに぀いおたったく䜕も知らないこずに泚意しおください。

シェヌダヌ


たず、テレむンシェヌダヌに可芖性を䌝えるこずから始めたしょう。頂点プログラムから可芖性デヌタを受け取り、構造䜓を䜿甚しおフラグメントプログラムに枡しInputたす。3぀の個別の暙高むンデックスを枡すため、3぀の可芖性倀も枡したす。

  struct Input { float4 color : COLOR; float3 worldPos; float3 terrain; float3 visibility; }; 

可芖性を保存するには、これらのセルの最初のコンポヌネントを䜿甚したす。

  void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); float4 cell2 = GetCellData(v, 2); data.terrain.x = cell0.w; data.terrain.y = cell1.w; data.terrain.z = cell2.w; data.visibility.x = cell0.x; data.visibility.y = cell1.x; data.visibility.z = cell2.x; } 

0の可芖性は、セルが珟圚非衚瀺であるこずを意味したす。可芖であれば、可芖性1の倀になりたす。したがっお、結果GetTerrainColorに可芖性の察応するベクトルを掛けるこずで、地圢を暗くするこずができたす。したがっお、各混合セルのレリヌフ色を個別に調敎したす。

  float4 GetTerrainColor (Input IN, int index) { float3 uvw = float3(IN.worldPos.xz * 0.02, IN.terrain[index]); float4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, uvw); return c * (IN.color[index] * IN.visibility[index]); } 


现胞が黒くなった。

代わりに、頂点プログラムで可芖性を組み合わせるこずはできたせんか
, . . . , . , .

完党な暗闇は、䞀時的に芋えない现胞にずっおはやり過ぎです。浮き圫りが芋えるようにするには、非衚瀺のセルに䜿甚するむンゞケヌタヌを増やす必芁がありたす。0–1からŒ– 1に移動したしょうlerp。これは、頂点プログラムの最埌にある関数を䜿甚しお実行できたす。

  void vert (inout appdata_full v, out Input data) { 
 data.visibility.x = cell0.x; data.visibility.y = cell1.x; data.visibility.z = cell2.x; data.visibility = lerp(0.25, 1, data.visibility); } 


圱付きセル。

セル可芖性远跡


可芖性が機胜するには、セルが可芖性を远跡する必芁がありたす。しかし、セルはそれが芋えるかどうかをどのように決定したすかこれを行う゚ンティティの数を远跡するこずでこれを行うこずができたす。誰かがセルを芋始めたら、このセルを報告しなければなりたせん。そしお、誰かがセルを芋るこずをやめたら、それに぀いおも圌女に通知しなければなりたせん。セルは、゚ンティティが䜕であれ、りォッチャヌの数を単玔に远跡したす。セルの可芖性の倀が少なくずも1の堎合、セルは衚瀺され、そうでない堎合は非衚瀺になりたす。この動䜜を実装するには、HexCell2぀のメ゜ッドずプロパティを倉数に远加したす。

  public bool IsVisible { get { return visibility > 0; } } 
 int visibility; 
 public void IncreaseVisibility () { visibility += 1; } public void DecreaseVisibility () { visibility -= 1; } 

次に、HexCellShaderDataメ゜ッドRefreshVisibilityにを远加したす。これは、RefreshTerrain可芖性のためだけず同じこずを行いたす。デヌタをデヌタセルのコンポヌネントRに保存したす。倀0〜1に倉換されるバむトを凊理するため、可芖性を瀺すために䜿甚したす(byte)255。

  public void RefreshVisibility (HexCell cell) { cellTextureData[cell.Index].r = cell.IsVisible ? (byte)255 : (byte)0; enabled = true; } 

0から1の間で倀を倉曎しお、可芖性を増枛しおこのメ​​゜ッドを呌び出したす。

  public void IncreaseVisibility () { visibility += 1; if (visibility == 1) { ShaderData.RefreshVisibility(this); } } public void DecreaseVisibility () { visibility -= 1; if (visibility == 0) { ShaderData.RefreshVisibility(this); } } 

分隊の可芖性の䜜成


ナニットが占有しおいるセルを芋るこずができるようにしたしょう。これはIncreaseVisibility、タスク䞭のナニットの新しい堎所ぞの呌び出しを䜿甚しお達成されたすHexUnit.Location。たた、叀い堎所存圚する堎合も呌び出したすDecreaseVisibility。

  public HexCell Location { get { return location; } set { if (location) { location.DecreaseVisibility(); location.Unit = null; } location = value; value.Unit = this; value.IncreaseVisibility(); transform.localPosition = value.Position; } } 


ナニットは珟圚地を確認できたす。

最埌に、可芖性を䜿甚したしたナニットをマップに远加するず、セルが衚瀺されたす。さらに、新しい堎所に移動するず、スコヌプがテレポヌトされたす。ただし、マップからナニットを削陀しおも、それらのスコヌプはアクティブのたたです。これを修正するために、ナニットを砎壊するずきの䜍眮の可芖性を枛らしたす。

  public void Die () { if (location) { location.DecreaseVisibility(); } location.Unit = null; Destroy(gameObject); } 

可芖範囲


これたでのずころ、デタッチメントが配眮されおいるセルのみが衚瀺されおおり、これにより可胜性が制限されおいたす。少なくずも隣接するセルを確認する必芁がありたす。䞀般的な堎合、ナニットは、ナニットに䟝存する特定の距離内のすべおのセルを芋るこずができたす。メ゜ッドに

远加しおHexGrid、範囲を考慮しお1぀のセルから芋えるすべおのセルを芋぀けたしょう。を耇補しお倉曎するこずで、このメ゜ッドを䜜成できたすSearch。パラメヌタを倉曎し、リストプヌルを䜿甚できるセルのリストを返すようにしたす。

各反埩で、珟圚のセルがリストに远加されたす。最終的なセルはないため、このポむントに到達しおも怜玢は終了したせん。たた、移動のロゞックず移動のコストも取り陀きたす。プロパティを䜜成するPathFrom私たちはそれらを必芁ずせず、グリッドに沿ったパスに干枉したくないので、もはや尋ねられたせんでした。

各ステップで、距離は1だけ増加したす。範囲を超える堎合、このセルはスキップされたす。たた、怜玢ヒュヌリスティックは必芁ないため、倀0で初期化したす。぀たり、実際にはダむクストラアルゎリズムに戻りたした。

  List<HexCell> GetVisibleCells (HexCell fromCell, int range) { List<HexCell> visibleCells = ListPool<HexCell>.Get(); searchFrontierPhase += 2; if (searchFrontier == null) { searchFrontier = new HexCellPriorityQueue(); } else { searchFrontier.Clear(); } fromCell.SearchPhase = searchFrontierPhase; fromCell.Distance = 0; searchFrontier.Enqueue(fromCell); while (searchFrontier.Count > 0) { HexCell current = searchFrontier.Dequeue(); current.SearchPhase += 1; visibleCells.Add(current); // if (current == toCell) { // return true; // } // int currentTurn = (current.Distance - 1) / speed; for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { HexCell neighbor = current.GetNeighbor(d); if ( neighbor == null || neighbor.SearchPhase > searchFrontierPhase ) { continue; } // 
 // int moveCost; // 
 int distance = current.Distance + 1; if (distance > range) { continue; } // int turn = (distance - 1) / speed; // if (turn > currentTurn) { // distance = turn * speed + moveCost; // } if (neighbor.SearchPhase < searchFrontierPhase) { neighbor.SearchPhase = searchFrontierPhase; neighbor.Distance = distance; // neighbor.PathFrom = current; neighbor.SearchHeuristic = 0; searchFrontier.Enqueue(neighbor); } else if (distance < neighbor.Distance) { int oldPriority = neighbor.SearchPriority; neighbor.Distance = distance; // neighbor.PathFrom = current; searchFrontier.Change(neighbor, oldPriority); } } } return visibleCells; } 

より簡単なアルゎリズムを䜿甚しお、範囲内のすべおのセルを芋぀けるこずはできたせんか
, , .

HexGridメ゜ッドIncreaseVisibilityずを远加したすDecreaseVisibility。圌らはセルず範囲を取埗し、察応するセルのリストを取埗し、それらの可芖性を増枛したす。完了したら、リストをプヌルに戻す必芁がありたす。

  public void IncreaseVisibility (HexCell fromCell, int range) { List<HexCell> cells = GetVisibleCells(fromCell, range); for (int i = 0; i < cells.Count; i++) { cells[i].IncreaseVisibility(); } ListPool<HexCell>.Add(cells); } public void DecreaseVisibility (HexCell fromCell, int range) { List<HexCell> cells = GetVisibleCells(fromCell, range); for (int i = 0; i < cells.Count; i++) { cells[i].DecreaseVisibility(); } ListPool<HexCell>.Add(cells); } 

これらのメ゜ッドを䜿甚HexUnitするには、グリッドにアクセスする必芁があるため、それにプロパティを远加したすGrid。

  public HexGrid Grid { get; set; } 

グリッドにチヌムを远加するず、グリッドがこのプロパティに割り圓おられたすHexGrid.AddUnit。

  public void AddUnit (HexUnit unit, HexCell location, float orientation) { units.Add(unit); unit.Grid = this; unit.transform.SetParent(transform, false); unit.Location = location; unit.Orientation = orientation; } 

たず、3぀のセルの可芖性の範囲で十分です。これを行うには、HexUnit定数に远加したす。これは将来、垞に倉数になりたす。次に、分隊がグリッドIncreaseVisibilityずのメ゜ッドを呌び出すようにし、DecreaseVisibilityこの堎所に移動するだけでなく、その可芖範囲も送信したす。

  const int visionRange = 3; 
 public HexCell Location { get { return location; } set { if (location) { // location.DecreaseVisibility(); Grid.DecreaseVisibility(location, visionRange); location.Unit = null; } location = value; value.Unit = this; // value.IncreaseVisibility(); Grid.IncreaseVisibility(value, visionRange); transform.localPosition = value.Position; } } 
 public void Die () { if (location) { // location.DecreaseVisibility(); Grid.DecreaseVisibility(location, visionRange); } location.Unit = null; Destroy(gameObject); } 


オヌバヌラップ可胜な可芖範囲を持぀ナニット。

移動時の可芖性


珟時点では、移動コマンド埌のナニットの芖認範囲は、すぐに終点にテレポヌトされたす。デタッチメントずその芖界が䞀緒に移動するず、芋た目が良くなりたす。これの最初のステップは、プロパティLocationcを蚭定しないこずHexUnit.Travelです。代わりに、locationプロパティコヌドを回避しお、フィヌルドを盎接倉曎したす。したがっお、叀い堎所を手動でクリアし、新しい堎所を構成したす。可芖性は倉曎されたせん。

  public void Travel (List<HexCell> path) { // Location = path[path.Count - 1]; location.Unit = null; location = path[path.Count - 1]; location.Unit = this; pathToTravel = path; StopAllCoroutines(); StartCoroutine(TravelPath()); } 

コルヌチン内でTravelPathは、完了埌にのみ最初のセルの可芖性を䜎䞋させLookAtたす。その埌、新しいセルに移動する前に、このセルからの可芖性を高めたす。これを終えるず、再び可芖性が䜎䞋したす。最埌に、最埌のセルの可芖性を高めたす。

  IEnumerator TravelPath () { Vector3 a, b, c = pathToTravel[0].Position; // transform.localPosition = c; yield return LookAt(pathToTravel[1].Position); Grid.DecreaseVisibility(pathToTravel[0], visionRange); float t = Time.deltaTime * travelSpeed; for (int i = 1; i < pathToTravel.Count; i++) { a = c; b = pathToTravel[i - 1].Position; c = (b + pathToTravel[i].Position) * 0.5f; Grid.IncreaseVisibility(pathToTravel[i], visionRange); for (; t < 1f; t += Time.deltaTime * travelSpeed) { 
 } Grid.DecreaseVisibility(pathToTravel[i], visionRange); t -= 1f; } a = c; b = location.Position; // We can simply use the destination here. c = b; Grid.IncreaseVisibility(location, visionRange); for (; t < 1f; t += Time.deltaTime * travelSpeed) { 
 } 
 } 


運転時の芖界。

デタッチメントが移動した時点で新しい泚文が発行された堎合を陀き、これはすべお機胜したす。これはテレポヌテヌションに぀ながり、可芖性にも適甚されるはずです。これを実珟するには、ナニットの珟圚䜍眮の動きを远跡する必芁がありたす。

  HexCell location, currentTravelLocation; 

分隊が最終セルに到達するたで、移動䞭に新しいセルにヒットするたびにこの䜍眮を曎新したす。その埌、リセットする必芁がありたす。

  IEnumerator TravelPath () { 
 for (int i = 1; i < pathToTravel.Count; i++) { currentTravelLocation = pathToTravel[i]; a = c; b = pathToTravel[i - 1].Position; c = (b + currentTravelLocation.Position) * 0.5f; Grid.IncreaseVisibility(pathToTravel[i], visionRange); for (; t < 1f; t += Time.deltaTime * travelSpeed) { transform.localPosition = Bezier.GetPoint(a, b, c, t); Vector3 d = Bezier.GetDerivative(a, b, c, t); dy = 0f; transform.localRotation = Quaternion.LookRotation(d); yield return null; } Grid.DecreaseVisibility(pathToTravel[i], visionRange); t -= 1f; } currentTravelLocation = null; 
 } 

これで、電源を入れた埌TravelPath、パスの叀い䞭間䜍眮がわかっおいるかどうかを確認できたす。はいの堎合は、パスの先頭ではなく、このセルの可芖性を䞋げる必芁がありたす。

  IEnumerator TravelPath () { Vector3 a, b, c = pathToTravel[0].Position; yield return LookAt(pathToTravel[1].Position); Grid.DecreaseVisibility( currentTravelLocation ? currentTravelLocation : pathToTravel[0], visionRange ); 
 } 

たた、チヌムの移動䞭に発生した再コンパむル埌の可芖性を修正する必芁がありたす。䞭間䜍眮がただわかっおいる堎合は、䞭間䜍眮の可芖性を䞋げお、゚ンドポむントの可芖性を高めおから、䞭間䜍眮をリセットしたす。

  void OnEnable () { if (location) { transform.localPosition = location.Position; if (currentTravelLocation) { Grid.IncreaseVisibility(location, visionRange); Grid.DecreaseVisibility(currentTravelLocation, visionRange); currentTravelLocation = null; } } } 

ナニティパッケヌゞ

道路ず氎の可芖性


レリヌフの色の倉化は芖認性に基づいおいたすが、これは道路や氎には圱響したせん。芋えない现胞には明るすぎたす。道路ず氎に可芖性を適甚するには、セルむンデックスを远加し、メッシュデヌタにりェむトをブレンドする必芁がありたす。したがっお、プレハブフラグメントの川、道路、氎、氎蟺、河口のセルデヌタの䜿甚の子をチェックしたす。

道路


道路から始めたす。この方法はHexGridChunk.TriangulateRoadEdge、セルの䞭心に道路の小さな郚分を䜜成するために䜿甚されるため、1぀のセルむンデックスが必芁です。パラメヌタヌを远加しお、䞉角圢のセルデヌタを生成したす。

  void TriangulateRoadEdge ( Vector3 center, Vector3 mL, Vector3 mR, float index ) { roads.AddTriangle(center, mL, mR); roads.AddTriangleUV( new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(0f, 0f) ); Vector3 indices; indices.x = indices.y = indices.z = index; roads.AddTriangleCellData(indices, weights1); } 

道路を䜜成するもう1぀の簡単な方法はTriangulateRoadSegmentです。セルの内郚ずセルの䞡方で䜿甚されるため、2぀の異なるむンデックスで動䜜するはずです。このためには、むンデックスベクトルパラメヌタヌを䜿甚するず䟿利です。道路セグメントは棚の䞀郚である可胜性があるため、重みもパラメヌタに枡す必芁がありたす。

  void TriangulateRoadSegment ( Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Vector3 v5, Vector3 v6, Color w1, Color w2, Vector3 indices ) { roads.AddQuad(v1, v2, v4, v5); roads.AddQuad(v2, v3, v5, v6); roads.AddQuadUV(0f, 1f, 0f, 0f); roads.AddQuadUV(1f, 0f, 0f, 0f); roads.AddQuadCellData(indices, w1, w2); roads.AddQuadCellData(indices, w1, w2); } 

TriangulateRoadでは、セル内に道路を䜜成するに進みたしょう。むンデックスパラメヌタも必芁です。圌は、このデヌタを呌び出した道路メ゜ッドに枡し、䜜成した䞉角圢に远加したす。

  void TriangulateRoad ( Vector3 center, Vector3 mL, Vector3 mR, EdgeVertices e, bool hasRoadThroughCellEdge, float index ) { if (hasRoadThroughCellEdge) { Vector3 indices; indices.x = indices.y = indices.z = index; Vector3 mC = Vector3.Lerp(mL, mR, 0.5f); TriangulateRoadSegment( mL, mC, mR, e.v2, e.v3, e.v4, weights1, weights1, indices ); roads.AddTriangle(center, mL, mC); roads.AddTriangle(center, mC, mR); roads.AddTriangleUV( new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(1f, 0f) ); roads.AddTriangleUV( new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(0f, 0f) ); roads.AddTriangleCellData(indices, weights1); roads.AddTriangleCellData(indices, weights1); } else { TriangulateRoadEdge(center, mL, mR, index); } } 

必芁なメ゜ッド匕数をTriangulateRoad、TriangulateRoadEdgeおよびに远加しお、TriangulateRoadSegmentすべおのコンパむラ゚ラヌを修正したす。

  void TriangulateWithoutRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { TriangulateEdgeFan(center, e, cell.Index); if (cell.HasRoads) { Vector2 interpolators = GetRoadInterpolators(direction, cell); TriangulateRoad( center, Vector3.Lerp(center, e.v1, interpolators.x), Vector3.Lerp(center, e.v5, interpolators.y), e, cell.HasRoadThroughEdge(direction), cell.Index ); } } 
 void TriangulateRoadAdjacentToRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 TriangulateRoad(roadCenter, mL, mR, e, hasRoadThroughEdge, cell.Index); if (previousHasRiver) { TriangulateRoadEdge(roadCenter, center, mL, cell.Index); } if (nextHasRiver) { TriangulateRoadEdge(roadCenter, mR, center, cell.Index); } } 
 void TriangulateEdgeStrip ( 
 ) { 
 if (hasRoad) { TriangulateRoadSegment( e1.v2, e1.v3, e1.v4, e2.v2, e2.v3, e2.v4, w1, w2, indices ); } } 

これでメッシュデヌタが正しいので、Roadシェヌダヌに進みたす。頂点プログラムが必芁で、HexCellDataが含たれおいる必芁がありたす。

  #pragma surface surf Standard fullforwardshadows decal:blend vertex:vert #pragma target 3.0 #include "HexCellData.cginc" 

耇数のマテリアルを混圚させおいないため、フラグメントプログラムに可芖性のむンゞケヌタを1぀枡すだけで十分です。

  struct Input { float2 uv_MainTex; float3 worldPos; float visibility; }; 

新しい頂点プログラムが2぀のセルからデヌタを受信するのに十分です。すぐに可芖性をミックスし、調敎しお出力に远加したす。

  void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); data.visibility = cell0.x * v.color.x + cell1.x * v.color.y; data.visibility = lerp(0.25, 1, data.visibility); } 

フラグメントプログラムでは、色に可芖性を远加するだけです。

  void surf (Input IN, inout SurfaceOutputStandard o) { float4 noise = tex2D(_MainTex, IN.worldPos.xz * 0.025); fixed4 c = _Color * ((noise.y * 0.75 + 0.25) * IN.visibility); 
 } 


可芖性のある道路。

オヌプンりォヌタヌ


芖界はすでに氎に圱響を䞎えおいるように芋えるかもしれたせんが、これは氎に浞された地圢の衚面にすぎたせん。たず、オヌプンりォヌタヌに可芖性を適甚するこずから始めたしょう。このために倉曎する必芁がありたすHexGridChunk.TriangulateOpenWater。

  void TriangulateOpenWater ( HexDirection direction, HexCell cell, HexCell neighbor, Vector3 center ) { 
 water.AddTriangle(center, c1, c2); Vector3 indices; indices.x = indices.y = indices.z = cell.Index; water.AddTriangleCellData(indices, weights1); if (direction <= HexDirection.SE && neighbor != null) { 
 water.AddQuad(c1, c2, e1, e2); indices.y = neighbor.Index; water.AddQuadCellData(indices, weights1, weights2); if (direction <= HexDirection.E) { 
 water.AddTriangle( c2, e2, c2 + HexMetrics.GetWaterBridge(direction.Next()) ); indices.z = nextNeighbor.Index; water.AddTriangleCellData( indices, weights1, weights2, weights3 ); } } } 

たた、海岞近くの䞉角圢のファンにセルデヌタを远加する必芁がありたす。

  void TriangulateWaterShore ( HexDirection direction, HexCell cell, HexCell neighbor, Vector3 center ) { 
 water.AddTriangle(center, e1.v1, e1.v2); water.AddTriangle(center, e1.v2, e1.v3); water.AddTriangle(center, e1.v3, e1.v4); water.AddTriangle(center, e1.v4, e1.v5); Vector3 indices; indices.x = indices.y = indices.z = cell.Index; water.AddTriangleCellData(indices, weights1); water.AddTriangleCellData(indices, weights1); water.AddTriangleCellData(indices, weights1); water.AddTriangleCellData(indices, weights1); 
 } 

Waterシェヌダヌは、Roadシェヌダヌず同じ方法で倉曎する必芁がありたすが、2぀ではなく3぀のセルの可芖性を組み合わせる必芁がありたす。

  #pragma surface surf Standard alpha vertex:vert #pragma target 3.0 #include "Water.cginc" #include "HexCellData.cginc" sampler2D _MainTex; struct Input { float2 uv_MainTex; float3 worldPos; float visibility; }; 
 void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); float4 cell2 = GetCellData(v, 2); data.visibility = cell0.x * v.color.x + cell1.x * v.color.y + cell2.x * v.color.z; data.visibility = lerp(0.25, 1, data.visibility); } void surf (Input IN, inout SurfaceOutputStandard o) { float waves = Waves(IN.worldPos.xz, _MainTex); fixed4 c = saturate(_Color + waves); o.Albedo = c.rgb * IN.visibility; 
 } 


可芖性のあるオヌプンりォヌタヌ。

海岞ず河口


海岞をサポヌトするには、再床倉曎する必芁がありHexGridChunk.TriangulateWaterShoreたす。既にむンデックスベクトルを䜜成したしたが、オヌプンりォヌタヌには1぀のセルむンデックスのみを䜿甚したした。コヌストにはネむバヌむンデックスも必芁なので、コヌドを倉曎したす。

  Vector3 indices; // indices.x = indices.y = indices.z = cell.Index; indices.x = indices.z = cell.Index; indices.y = neighbor.Index; 

セルデヌタを海岞の四角圢ず䞉角圢に远加したす。たた、呌び出しでむンデックスを枡したすTriangulateEstuary。

  if (cell.HasRiverThroughEdge(direction)) { TriangulateEstuary( e1, e2, cell.IncomingRiver == direction, indices ); } else { 
 waterShore.AddQuadUV(0f, 0f, 0f, 1f); waterShore.AddQuadCellData(indices, weights1, weights2); waterShore.AddQuadCellData(indices, weights1, weights2); waterShore.AddQuadCellData(indices, weights1, weights2); waterShore.AddQuadCellData(indices, weights1, weights2); } HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (nextNeighbor != null) { 
 waterShore.AddTriangleUV( 
 ); indices.z = nextNeighbor.Index; waterShore.AddTriangleCellData( indices, weights1, weights2, weights3 ); } 

必芁なパラメヌタヌを远加TriangulateEstuaryし、海岞ず口のこれらのセルを凊理したす。口が台圢でできおおり、䞡偎に海岞の2぀の䞉角圢があるこずを忘れないでください。重量が正しい順序で転送されるこずを確認しおください。

  void TriangulateEstuary ( EdgeVertices e1, EdgeVertices e2, bool incomingRiver, Vector3 indices ) { waterShore.AddTriangle(e2.v1, e1.v2, e1.v1); waterShore.AddTriangle(e2.v5, e1.v5, e1.v4); waterShore.AddTriangleUV( new Vector2(0f, 1f), new Vector2(0f, 0f), new Vector2(0f, 0f) ); waterShore.AddTriangleUV( new Vector2(0f, 1f), new Vector2(0f, 0f), new Vector2(0f, 0f) ); waterShore.AddTriangleCellData(indices, weights2, weights1, weights1); waterShore.AddTriangleCellData(indices, weights2, weights1, weights1); estuaries.AddQuad(e2.v1, e1.v2, e2.v2, e1.v3); estuaries.AddTriangle(e1.v3, e2.v2, e2.v4); estuaries.AddQuad(e1.v3, e1.v4, e2.v4, e2.v5); estuaries.AddQuadUV( new Vector2(0f, 1f), new Vector2(0f, 0f), new Vector2(1f, 1f), new Vector2(0f, 0f) ); estuaries.AddTriangleUV( new Vector2(0f, 0f), new Vector2(1f, 1f), new Vector2(1f, 1f) ); estuaries.AddQuadUV( new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(1f, 1f), new Vector2(0f, 1f) ); estuaries.AddQuadCellData( indices, weights2, weights1, weights2, weights1 ); estuaries.AddTriangleCellData(indices, weights1, weights2, weights2); estuaries.AddQuadCellData(indices, weights1, weights2); 
 } 

3぀のセルの可芖性を混合しお、WaterShoreシェヌダヌずWaterシェヌダヌに同じ倉曎を加える必芁がありたす。

  #pragma surface surf Standard alpha vertex:vert #pragma target 3.0 #include "Water.cginc" #include "HexCellData.cginc" sampler2D _MainTex; struct Input { float2 uv_MainTex; float3 worldPos; float visibility; }; 
 void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); float4 cell2 = GetCellData(v, 2); data.visibility = cell0.x * v.color.x + cell1.x * v.color.y + cell2.x * v.color.z; data.visibility = lerp(0.25, 1, data.visibility); } void surf (Input IN, inout SurfaceOutputStandard o) { 
 fixed4 c = saturate(_Color + max(foam, waves)); o.Albedo = c.rgb * IN.visibility; 
 } 

シャむダヌ河口ミックス2぀のセルの可芖性だけでなく、シェヌダ道路䞀般的な説明。川のUV座暙を送信する必芁があるため、圌にはすでに頂点プログラムがありたす。

  #include "Water.cginc" #include "HexCellData.cginc" sampler2D _MainTex; struct Input { float2 uv_MainTex; float2 riverUV; float3 worldPos; float visibility; }; half _Glossiness; half _Metallic; fixed4 _Color; void vert (inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input, o); o.riverUV = v.texcoord1.xy; float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); o.visibility = cell0.x * v.color.x + cell1.x * v.color.y; o.visibility = lerp(0.25, 1, o.visibility); } void surf (Input IN, inout SurfaceOutputStandard o) { 
 fixed4 c = saturate(_Color + water); o.Albedo = c.rgb * IN.visibility; 
 } 


芖認性のある海岞ず河口。

川


最埌に䜿甚する氎域は川です。HexGridChunk.TriangulateRiverQuad2぀のセルの可芖性を維持できるように、むンデックスベクトルをパラメヌタヌに远加し、メッシュに远加したす。

  void TriangulateRiverQuad ( Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, float y, float v, bool reversed, Vector3 indices ) { TriangulateRiverQuad(v1, v2, v3, v4, y, y, v, reversed, indices); } void TriangulateRiverQuad ( Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, float y1, float y2, float v, bool reversed, Vector3 indices ) { 
 rivers.AddQuadCellData(indices, weights1, weights2); } 

TriangulateWithRiverBeginOrEndセルの䞭心に四角圢ず䞉角圢のある川の終点を䜜成したす。これに必芁なセルデヌタを远加したす。

  void TriangulateWithRiverBeginOrEnd ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 if (!cell.IsUnderwater) { bool reversed = cell.HasIncomingRiver; Vector3 indices; indices.x = indices.y = indices.z = cell.Index; TriangulateRiverQuad( m.v2, m.v4, e.v2, e.v4, cell.RiverSurfaceY, 0.6f, reversed, indices ); center.y = m.v2.y = m.v4.y = cell.RiverSurfaceY; rivers.AddTriangle(center, m.v2, m.v4); 
 rivers.AddTriangleCellData(indices, weights1); } } 

これらのセルむンデックスはすでに入っおいるTriangulateWithRiverので、呌び出しでそれらを枡すだけTriangulateRiverQuadです。

  void TriangulateWithRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 if (!cell.IsUnderwater) { bool reversed = cell.IncomingRiver == direction; TriangulateRiverQuad( centerL, centerR, m.v2, m.v4, cell.RiverSurfaceY, 0.4f, reversed, indices ); TriangulateRiverQuad( m.v2, m.v4, e.v2, e.v4, cell.RiverSurfaceY, 0.6f, reversed, indices ); } } 

たた、深海に泚ぐ滝にむンデックスサポヌトを远加したす。

  void TriangulateWaterfallInWater ( Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, float y1, float y2, float waterY, Vector3 indices ) { 
 rivers.AddQuadCellData(indices, weights1, weights2); } 

そしお最埌に、TriangulateConnection必芁な指暙を川や滝の方法に枡すように倉曎したす。

  void TriangulateConnection ( HexDirection direction, HexCell cell, EdgeVertices e1 ) { 
 if (hasRiver) { e2.v3.y = neighbor.StreamBedY; Vector3 indices; indices.x = indices.z = cell.Index; indices.y = neighbor.Index; if (!cell.IsUnderwater) { if (!neighbor.IsUnderwater) { TriangulateRiverQuad( e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbor.RiverSurfaceY, 0.8f, cell.HasIncomingRiver && cell.IncomingRiver == direction, indices ); } else if (cell.Elevation > neighbor.WaterLevel) { TriangulateWaterfallInWater( e1.v2, e1.v4, e2.v2, e2.v4, cell.RiverSurfaceY, neighbor.RiverSurfaceY, neighbor.WaterSurfaceY, indices ); } } else if ( !neighbor.IsUnderwater && neighbor.Elevation > cell.WaterLevel ) { TriangulateWaterfallInWater( e2.v4, e2.v2, e1.v4, e1.v2, neighbor.RiverSurfaceY, cell.RiverSurfaceY, cell.WaterSurfaceY, indices ); } } 
 } 

Riverシェヌダヌは、Roadシェヌダヌず同じ倉曎を行う必芁がありたす。

  #pragma surface surf Standard alpha vertex:vert #pragma target 3.0 #include "Water.cginc" #include "HexCellData.cginc" sampler2D _MainTex; struct Input { float2 uv_MainTex; float visibility; }; 
 void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float4 cell0 = GetCellData(v, 0); float4 cell1 = GetCellData(v, 1); data.visibility = cell0.x * v.color.x + cell1.x * v.color.y; data.visibility = lerp(0.25, 1, data.visibility); } void surf (Input IN, inout SurfaceOutputStandard o) { float river = River(IN.uv_MainTex, _MainTex); fixed4 c = saturate(_Color + river); o.Albedo = c.rgb * IN.visibility; 
 } 


可芖性のある川。

ナニティパッケヌゞ

オブゞェクトず可芖性


珟圚、可芖性は手順で生成されたテレむン党䜓で機胜したすが、これたでのずころテレむンフィヌチャには圱響したせん。建物、蟲堎、朚は、手続き型ゞオメトリからではなく、プレハブから䜜成されるため、セルむンデックスを远加したり、頂点に重みを混ぜるこずはできたせん。これらのオブゞェクトはそれぞれ1぀のセルにのみ属しおいるため、どのセルに属しおいるかを刀断する必芁がありたす。これを実行できる堎合、察応するセルのデヌタにアクセスし、可芖性を適甚したす。

すでに䞖界のXZ䜍眮をセルむンデックスに倉換できたす。この倉換は、地圢の線集ず分隊の管理に䜿甚されたした。ただし、察応するコヌドは重芁です。敎数挔算を䜿甚し、゚ッゞを操䜜するロゞックが必芁です。これはシェヌダヌには実甚的ではないため、ロゞックの倧郚分をテクスチャでベむク凊理しお䜿甚できたす。

すでに六角圢パタヌンのテクスチャを䜿甚しお、地圢にメッシュを投圱しおいたす。このテクスチャは、2×2のセル領域を定矩したす。したがっお、どの領域にいるかを簡単に蚈算できたす。その埌、この領域のセルにXオフセットずZオフセットを含むテクスチャを適甚し、このデヌタを䜿甚しお自分がいるセルを蚈算できたす。

これは同様のテクスチャです。 Xオフセットは赀チャンネルに保存され、Zオフセットは緑チャンネルに保存されたす。 2×2セルの領域をカバヌするため、0ず2からのオフセットが必芁です。そのようなデヌタはカラヌチャンネルに保存できないため、オフセットは半分に削枛されたす。セルの明確な゚ッゞは必芁ないため、小さなテクスチャで十分です。


グリッド座暙のテクスチャ。

プロゞェクトにテクスチャを远加したす。他のメッシュテクスチャず同様に、Wrap ModeをRepeatに蚭定したす。ミキシングは必芁ないので、ブレンドモヌドでは倀Pointを遞択したす。たた、デヌタが歪たないように、圧瞮をオフにしたす。sRGBモヌドをオフにしお、線圢モヌドでレンダリングするずきに色空間倉換が実行されないようにしたす。最埌に、ミップテクスチャは必芁ありたせん。


テクスチャのむンポヌトオプション。

可芖性のあるオブゞェクトシェヌダヌ


新しい機胜シェヌダヌを䜜成しお、オブゞェクトに可芖性サポヌトを远加したす。これは、頂点プログラムを備えたシンプルなサヌフェスシェヌダヌです。HexCellDataを远加しお、可芖性むンゞケヌタをフラグメントプログラムに枡し、通垞どおり、色で怜蚎したす。ここでの違いはGetCellData、必芁なメッシュデヌタが存圚しないため䜿甚できないこずです。代わりに、私たちには䞖界での地䜍がありたす。ただし、珟時点では、可芖性を1のたたにしおおきたす。

 Shader "Custom/Feature" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 [NoTilingOffset] _GridCoordinates ("Grid Coordinates", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows vertex:vert #pragma target 3.0 #include "../HexCellData.cginc" sampler2D _MainTex, _GridCoordinates; half _Glossiness; half _Metallic; fixed4 _Color; struct Input { float2 uv_MainTex; float visibility; }; void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float3 pos = mul(unity_ObjectToWorld, v.vertex); data.visibility = 1; } void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb * IN.visibility; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } ENDCG } FallBack "Diffuse" } 

新しいシェヌダヌを䜿甚するようにオブゞェクトのすべおのマテリアルを倉曎し、グリッド座暙のテクスチャを割り圓おたす。


メッシュテクスチャず郜垂。

セルデヌタにアクセスする


頂点プログラムでグリッド座暙のテクスチャをサンプリングするにはtex2Dlod、4コンポヌネントのテクスチャ座暙ベクトルが再び必芁です。最初の2぀の座暙は、䞖界のXZ䜍眮です。他の2぀は以前ず同様にれロです。

  void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); float3 pos = mul(unity_ObjectToWorld, v.vertex); float4 gridUV = float4(pos.xz, 0, 0); data.visibility = 1; } 

テレむンシェヌダヌのように、UV座暙をストレッチしお、テクスチャが六角圢のグリッドに察応する正しいアスペクト比になるようにしたす。

  float4 gridUV = float4(pos.xz, 0, 0); gridUV.x *= 1 / (4 * 8.66025404); gridUV.y *= 1 / (2 * 15.0); 

UV座暙倀を切り捚おるこずにより、2×2セルのどの郚分にいるかがわかりたす。これは、セル座暙の基瀎を圢成したす。

  float4 gridUV = float4(pos.xz, 0, 0); gridUV.x *= 1 / (4 * 8.66025404); gridUV.y *= 1 / (2 * 15.0); float2 cellDataCoordinates = floor(gridUV.xy); 

珟圚のセルの座暙を芋぀けるには、テクスチャに保存されおいる倉䜍を远加したす。

  float2 cellDataCoordinates = floor(gridUV.xy) + tex2Dlod(_GridCoordinates, gridUV).rg; 

グリッドの䞀郚のサむズは2×2であり、オフセットは半分になるため、結果を2倍にしお最終的な座暙を取埗する必芁がありたす。

  float2 cellDataCoordinates = floor(gridUV.xy) + tex2Dlod(_GridCoordinates, gridUV).rg; cellDataCoordinates *= 2; 

これで、これらのセルのUV座暙に倉換する必芁があるセルグリッドのXZ座暙が埗られたした。これは、ピクセルの䞭心に移動し、それらをテクスチャサむズに分割するだけで実行できたす。したがっお、サンプリングを凊理するHexCellDataむンクルヌドファむルにこのための関数を远加したしょう。

 float4 GetCellData (float2 cellDataCoordinates) { float2 uv = cellDataCoordinates + 0.5; uv.x *= _HexCellData_TexelSize.x; uv.y *= _HexCellData_TexelSize.y; return tex2Dlod(_HexCellData, float4(uv, 0, 0)); } 

今、私たちは、頂点シェヌダプログラムでこれを䜿甚するこずができる機胜。

  cellDataCoordinates *= 2; data.visibility = GetCellData(cellDataCoordinates).x; data.visibility = lerp(0.25, 1, data.visibility); 


可芖性を持぀オブゞェクト。

最埌に、可芖性は垞に衚瀺されるナニットを陀き、マップ党䜓に圱響したす。各頂点のオブゞェクトの可芖性を決定するため、セルの境界を暪切るオブゞェクトの堎合、それが閉じるセルの可芖性は混合されたす。しかし、オブゞェクトは非垞に小さいため、䜍眮の歪みを考慮しおも、垞にセル内に残りたす。ただし、䞀郚は別のセルの頂点の䞀郚である堎合がありたす。したがっお、私たちのアプロヌチは安䟡ですが、䞍完党です。これは、壁の堎合に最も顕著であり、その芖認性は隣接するセルの可芖性の間で異なりたす。


芖界が倉化する壁。

壁セグメントは手続き的に生成されるため、セルデヌタをメッシュに远加し、レリヌフに䜿甚したアプロヌチを䜿甚できたす。残念ながら、タワヌはプレハブであるため、䟝然ずしお矛盟が生じたす。䞀般的に蚀えば、既存のアプロヌチは単玔なゞオメトリには十分に芋えたす。将来的には、より詳现なモデルず壁を怜蚎するため、それらの可芖性を混合する方法を改善したす。

ナニティパッケヌゞ

パヌト21地図調査



前のパヌトでは、戊争の霧を远加したした。これを改良しお、地図研究を実装したす。


私たちは䞖界を探玢する準備ができおいたす。

線集モヌドでマップ党䜓を衚瀺したす


この研究の意味は、これたで芋られなかった现胞は未知であるず芋なされ、したがっお目に芋えないずいうこずです。それらを䞍明瞭にするこずはできたせんが、衚瀺するこずはできたせん。したがっお、調査サポヌトを远加する前に、線集モヌドでの可芖性を有効にしたす。

可芖性の切り替え


グリッド䞊のオヌバヌレむで行われたように、キヌワヌドを䜿甚しおシェヌダヌが可芖性を䜿甚するかどうかを制埡できたす。HEX_MAP_EDIT_MODEキヌワヌドを䜿甚しお、線集モヌドの状態を瀺したしょう。いく぀かのシェヌダヌはこのキヌワヌドを知っおいる必芁があるため、静的メ゜ッドShader.EnableKeyWordずを䜿甚しおグロヌバルに定矩したすShader.DisableKeyword。HexGameUI.SetEditMode線集モヌドを倉曎するずきに適切なメ゜ッドを呌び出したす。

  public void SetEditMode (bool toggle) { enabled = !toggle; grid.ShowUI(!toggle); grid.ClearPath(); if (toggle) { Shader.EnableKeyword("HEX_MAP_EDIT_MODE"); } else { Shader.DisableKeyword("HEX_MAP_EDIT_MODE"); } } 

線集モヌドシェヌダヌ


HEX_MAP_EDIT_MODEが定矩されおいる堎合、シェヌダヌは可芖性を無芖したす。これは、セルの可芖性が垞に1ず芋なされるずいう事実に芁玄されたす。HexCellData include-fileの先頭にあるキヌワヌドに応じお、セルのデヌタをフィルタヌする関数を远加したしょう。

 sampler2D _HexCellData; float4 _HexCellData_TexelSize; float4 FilterCellData (float4 data) { #if defined(HEX_MAP_EDIT_MODE) data.x = 1; #endif return data; } 

䞡方の関数の結果をGetCellData返す前に、この関数を通過させたす。

 float4 GetCellData (appdata_full v, int index) { 
 return FilterCellData(data); } float4 GetCellData (float2 cellDataCoordinates) { 
 return FilterCellData(tex2Dlod(_HexCellData, float4(uv, 0, 0))); } 

すべおが機胜するためには、HEX_MAP_EDIT_MODEキヌワヌドが定矩されおいる堎合に、関連するすべおのシェヌダヌがmulti_compileディレクティブを受け取っおオプションを䜜成する必芁がありたす。タヌゲットディレクティブず最初のincludeディレクティブの間のシェヌダヌ河口、フィヌチャヌ、川、道路、地圢、氎、および氎蟺に適切なラむンを远加したす。

  #pragma multi_compile _ HEX_MAP_EDIT_MODE 

これで、マップ線集モヌドに切り替えるず、戊争の霧が消えたす。

ナニティパッケヌゞ

现胞研究


デフォルトでは、セルは未探玢ず芋なされる必芁がありたす。郚隊が圌らを芋るず、圌らは探怜されたす。その埌、分遣隊がそれらを芋るこずができる堎合、圌らは調査され続けたす。

远跡調査ステヌタス


研究のステヌタスを監芖するためのサポヌトを远加するために、HexCell䞀般プロパティに远加したすIsExplored。

  public bool IsExplored { get; set; } 

研究の状態は、セル自䜓によっお決定されたす。したがっお、このプロパティはのみ蚭定する必芁がありたすHexCell。このような制限を远加するには、セッタヌをプラむベヌトに蚭定したす。

  public bool IsExplored { get; private set; } 

セルの可芖性が初めおれロより倧きくなるず、セルは調査察象ず芋なされるようになるためIsExplored、倀を割り圓おる必芁がありたすtrue。実際、可芖性が1に増加したずきに、セルを怜査枈みずしおマヌクするだけで十分です。これは、呌び出しの前に行う必芁がありたすRefreshVisibility。

  public void IncreaseVisibility () { visibility += 1; if (visibility == 1) { IsExplored = true; ShaderData.RefreshVisibility(this); } } 

研究状態をシェヌダヌに転送する


セルの可芖性の堎合ず同様に、シェヌダヌデヌタを通じおシェヌダヌに研究状態を枡したす。結局、これは単なる別のタむプの可芖性です。HexCellShaderData.RefreshVisibilityデヌタチャネルRに可芖性状態を保存したす。チャンネルGデヌタで調査の状態を保持したしょう。

  public void RefreshVisibility (HexCell cell) { int index = cell.Index; cellTextureData[index].r = cell.IsVisible ? (byte)255 : (byte)0; cellTextureData[index].g = cell.IsExplored ? (byte)255 : (byte)0; enabled = true; } 

黒の未螏の救枈


これで、シェヌダヌを䜿甚しお现胞研究の状態を芖芚化できたす。すべおが正垞に機胜するこずを確認するために、未探玢の地圢を黒にしたす。しかし、最初に、線集モヌドを機胜させるFilterCellDataために、研究デヌタを陀倖するように倉曎したす。

 float4 FilterCellData (float4 data) { #if defined(HEX_MAP_EDIT_MODE) data.xy = 1; #endif return data; } 

テレむンシェヌダヌは、3぀のセルすべおの可芖性デヌタをフラグメントプログラムに枡したす。研究状態の堎合、頂点プログラムでそれらを組み合わせお、唯䞀の倀をフラグメントプログラムに転送したす。visibilityこの堎所を確保するために、4番目のコンポヌネントを入力に远加したす。

  struct Input { float4 color : COLOR; float3 worldPos; float3 terrain; float4 visibility; }; 

頂点プログラムでは、可芖性むンデックスを倉曎するずきに、明瀺的にアクセスする必芁がありたすdata.visibility.xyz。

  void vert (inout appdata_full v, out Input data) { 
 data.visibility.xyz = lerp(0.25, 1, data.visibility.xyz); } 

その埌、調査の状態を組み合わせお、結果をに曞き蟌みdata.visibility.wたす。これは、他のシェヌダヌで可芖性を組み合わせるこずず同様ですが、これらのセルのコンポヌネントYを䜿甚したす。

  data.visibility.xyz = lerp(0.25, 1, data.visibility.xyz); data.visibility.w = cell0.y * v.color.x + cell1.y * v.color.y + cell2.y * v.color.z; 

珟圚、フラグメントプログラムで研究ステヌタスを利甚できたすIN.visibility.w。アルベドの蚈算で考慮しおください。

  void surf (Input IN, inout SurfaceOutputStandard o) { 
 float explored = IN.visibility.w; o.Albedo = c.rgb * grid * _Color * explored; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 


未探玢の地圢は黒になりたした。

未探玢の现胞の浮き圫りは黒色になりたした。しかし、これはただオブゞェクト、道路、氎に圱響しおいたせん。ただし、これは調査が機胜するこずを確認するのに十分です。

研究ステヌタスの保存ず読み蟌み


調査サポヌトを远加したので、マップを保存およびロヌドするずきに調査ステヌタスが考慮されるようにする必芁がありたす。したがっお、マップファむルのバヌゞョンを3に増やす必芁がありたす。これらの倉曎をより䟿利にするSaveLoadMenuために、この定数を远加したしょう。

  const int mapFileVersion = 3; 

にファむルバヌゞョンを曞き蟌むずき、Saveおよびファむルサポヌトをチェックするずきに、この定数を䜿甚しLoadたす。

  void Save (string path) { using ( BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)) ) { writer.Write(mapFileVersion); hexGrid.Save(writer); } } void Load (string path) { if (!File.Exists(path)) { Debug.LogError("File does not exist " + path); return; } using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { int header = reader.ReadInt32(); if (header <= mapFileVersion) { hexGrid.Load(reader, header); HexMapCamera.ValidatePosition(); } else { Debug.LogWarning("Unknown map format " + header); } } } 

最埌のステップずしおHexCell.Save、調査のステヌタスを蚘録したす。

  public void Save (BinaryWriter writer) { 
 writer.Write(IsExplored); } 

そしお、最埌にそれを読みたすLoad。その埌RefreshVisibility、調査の状態が前のものず異なる堎合に呌び出したす。

  public void Load (BinaryReader reader) { 
 IsExplored = reader.ReadBoolean(); ShaderData.RefreshVisibility(this); } 

叀い保存ファむルずの埌方互換性を維持するために、ファむルバヌゞョンが3未満の堎合、保存状態の読み取りをスキップする必芁がありたす。この堎合、デフォルトでは、セルの状態は「未探玢」になりたす。これを行うには、パラメヌタヌずしおLoadヘッダヌデヌタを远加する必芁がありたす。

  public void Load (BinaryReader reader, int header) { 
 IsExplored = header >= 3 ? reader.ReadBoolean() : false; ShaderData.RefreshVisibility(this); } 

次にHexGrid.Load、HexCell.Loadヘッダヌデヌタを枡す必芁がありたす。

  public void Load (BinaryReader reader, int header) { 
 for (int i = 0; i < cells.Length; i++) { cells[i].Load(reader, header); } 
 } 

これで、マップを保存およびロヌドするずきに、セルの探玢状態が考慮されたす。

ナニティパッケヌゞ

䞍明なセルを非衚瀺


珟圚の段階では、未探玢のセルは黒い浮き圫りで芖芚的に瀺されたす。しかし、実際には、これらのセルは䞍明であるため目に芋えないようにしたす。䞍透明なゞオメトリを透明にしお、衚瀺されないようにするこずができたす。ただし、Unityサヌフェスシェヌダヌフレヌムワヌクは、この可胜性を考慮せずに蚭蚈されたした。真の透明床を䜿甚する代わりに、シェヌダヌを背景に合わせお倉曎したす。これにより、シェヌダヌも非衚瀺になりたす。

レリヌフを本圓に黒にする


調査したレリヌフは黒ですが、ただ鏡面反射光があるため、認識できたす。照明を取り陀くには、完党にマットな黒にする必芁がありたす。他の衚面特性に圱響を䞎えないために、鏡面反射色を黒に倉曎するのが最も簡単です。これは、鏡面反射で動䜜するサヌフェスシェヌダヌを䜿甚する堎合に可胜ですが、珟圚は暙準のメタリックを䜿甚しおいたす。それでは、テレむンシェヌダヌをスペキュラヌに切り替えおみたしょう。

カラヌプロパティに眮き換え_Metallicをプロパティに_Specular。デフォルトでは、色の倀は0.2、0.2、0.2に等しくなければなりたせん。したがっお、メタリックバヌゞョンの倖芳ず䞀臎するこずを保蚌したす。

  Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Terrain Texture Array", 2DArray) = "white" {} _GridTex ("Grid Texture", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 // _Metallic ("Metallic", Range(0,1)) = 0.0 _Specular ("Specular", Color) = (0.2, 0.2, 0.2) } 

たた、察応するシェヌダヌ倉数も倉曎したす。鏡面サヌフェスシェヌダの色はずしお定矩されおいるfixed3ので、それを䜿甚したしょう。

  half _Glossiness; // half _Metallic; fixed3 _Specular; fixed4 _Color; 

倉化プラグマ衚面サヌフ暙準のStandardSpecular。これにより、Unityはスペキュラヌを䜿甚しおシェヌダヌを生成したす。

  #pragma surface surf StandardSpecular fullforwardshadows vertex:vert 

ここで、関数surfは2番目のパラメヌタヌの型が必芁SurfaceOutputStandardSpecularです。さらに、ではなくo.Metallic、ではなく倀を割り圓おる必芁がありo.Specularたす。

  void surf (Input IN, inout SurfaceOutputStandardSpecular o) { 
 float explored = IN.visibility.w; o.Albedo = c.rgb * grid * _Color * explored; // o.Metallic = _Metallic; o.Specular = _Specular; o.Smoothness = _Glossiness; o.Alpha = ca; } 

explored鏡面反射色を考慮するこずにより、ハむラむトを䞍明瞭にするこずができたす。

  o.Specular = _Specular * explored; 


反射照明のない未螏の地圢。

写真でわかるように、未探玢のレリヌフはくすんだ黒に芋えたす。ただし、接線角床で芋るず、衚面は鏡に倉わりたす。そのため、レリヌフは環境、぀たりスカむボックスを反映し始めたす。

衚面が鏡になるのはなぜですか
. . Rendering .


未開拓の地域はただ環境を反映しおいたす。

これらの反射を取り陀くために、未探玢の浮き圫りが完党に陰圱付けされおいるこずを考慮したす。これはexplored、反射マスクずしお䜿甚するオクルヌゞョンパラメヌタヌに倀を割り圓おるこずで実珟されたす。

  float explored = IN.visibility.w; o.Albedo = c.rgb * grid * _Color * explored; o.Specular = _Specular * explored; o.Smoothness = _Glossiness; o.Occlusion = explored; o.Alpha = ca; 


反射のない未螏。

䞀臎する背景


未探玢の地圢はすべおの照明を無芖するようになったので、背景ず䞀臎させる必芁がありたす。カメラは垞に䞊から芋えるため、背景は垞に灰色です。䜿甚する色をテレむンシェヌダヌに指瀺するには、_BackgroundColorプロパティを远加したす。デフォルトは黒です。

  Properties { 
 _BackgroundColor ("Background Color", Color) = (0,0,0) } 
 half _Glossiness; fixed3 _Specular; fixed4 _Color; half3 _BackgroundColor; 

この色を䜿甚するには、発光色ずしお远加したす。これはo.Emission、探玢されたマむナス1を掛けた背景色の倀を割り圓おるこずによっお実珟されたす。

  o.Occlusion = explored; o.Emission = _BackgroundColor * (1 - explored); 

デフォルトのスカむボックスを䜿甚しおいるため、衚瀺される背景色は実際には同じではありたせん。䞀般に、わずかに赀みがかった灰色が最適な色になりたす。レリヌフマテリアルを蚭定するずきは、Hex Colorにコヌド68615BFF を䜿甚できたす。


灰色の背景色のレリヌフ玠材。

䞀般的には動䜜したすが、どこを芋るべきか知っおいれば、非垞に匱いシル゚ットに気づくでしょう。プレヌダヌがそれらを芋るこずができないように、スカむボックスの代わりに68615BFFの均䞀な背景色をカメラに割り圓おるこずができたす。


均䞀な背景色のカメラ。

スカむボックスを削陀しおみたせんか
, , environmental lighting . , .

これで、背景のセルず未探玢のセルを区別できなくなりたした。高い未探査の地圢は、䜎いカメラアングルでの䜎い調査地圢を䟝然ずしお䞍明瞭にする可胜性がありたす。さらに、未探玢のパヌツはただ探玢された郚分に圱を萜ずしたす。しかし、これらの最小限の手がかりは無芖できたす。


未探玢のセルは衚瀺されなくなりたした。

均䞀な背景色を䜿甚しない堎合はどうなりたすか
, , . . , . , , , UV- .

レリヌフオブゞェクトを隠す


これで、レリヌフのメッシュのみが非衚瀺になりたした。研究の残りの状態はただ圱響を受けおいたせん。


これたでのずころ、安reliefだけが隠されおいたす。 Terrainのような䞍透明なシェヌダヌであるFeature

シェヌダヌを倉曎したしょう。それを鏡面シェヌダヌに倉えお、背景色を远加したす。プロパティから始めたしょう。

  Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 // _Metallic ("Metallic", Range(0,1)) = 0.0 _Specular ("Specular", Color) = (0.2, 0.2, 0.2) _BackgroundColor ("Background Color", Color) = (0,0,0) [NoScaleOffset] _GridCoordinates ("Grid Coordinates", 2D) = "white" {} } 

前ず同じように、次のプラグマサヌフェスず倉数。

  #pragma surface surf StandardSpecular fullforwardshadows vertex:vert 
 half _Glossiness; // half _Metallic; fixed3 _Specular; fixed4 _Color; half3 _BackgroundColor; 

visibilityもう1぀のコンポヌネントも必芁です。ので機胜は各頂点のための可芖性を兌ね備えた、圌は䞀぀の倀だけフロヌトを必芁ずしおいたした。ここで2぀必芁です。

  struct Input { float2 uv_MainTex; float2 visibility; }; 

vert可芖性デヌタを明瀺的に䜿甚するように倉曎しおからdata.visibility.x、data.visibility.y倀をスタディデヌタに割り圓おたす。

  void vert (inout appdata_full v, out Input data) { 
 float4 cellData = GetCellData(cellDataCoordinates); data.visibility.x = cellData.x; data.visibility.x = lerp(0.25, 1, data.visibility.x); data.visibility.y = cellData.y; } 

Terrainのsurfような新しいデヌタを䜿甚するように倉曎したす。

  void surf (Input IN, inout SurfaceOutputStandardSpecular o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; float explored = IN.visibility.y; o.Albedo = c.rgb * (IN.visibility.x * explored); // o.Metallic = _Metallic; o.Specular = _Specular * explored; o.Smoothness = _Glossiness; o.Occlusion = explored; o.Emission = _BackgroundColor * (1 - explored); o.Alpha = ca; } 


非衚瀺のレリヌフオブゞェクト。

氎を隠す


次はりォヌタヌシェヌダヌずりォヌタヌショアシェヌダヌです。それらをスペキュラヌシェヌダヌに倉換するこずから始めたしょう。ただし、透明なシェヌダヌであるため、背景色は必芁ありたせん。

倉換埌、visibilityもう1぀のコンポヌネントを远加し、それに応じお倉曎しvertたす。䞡方のシェヌダヌは、3぀のセルからのデヌタを結合したす。

  struct Input { 
 float2 visibility; }; 
 void vert (inout appdata_full v, out Input data) { 
 data.visibility.x = cell0.x * v.color.x + cell1.x * v.color.y + cell2.x * v.color.z; data.visibility.x = lerp(0.25, 1, data.visibility.x); data.visibility.y = cell0.y * v.color.x + cell1.y * v.color.y + cell2.y * v.color.z; } 

WaterずWater Shoreはsurf異なる操䜜を実行したすが、衚面のプロパティを同じように蚭定したす。透明なのでexplore、アルファチャンネルを考慮し、攟出を蚭定したせん。

  void surf (Input IN, inout SurfaceOutputStandardSpecular o) { 
 float explored = IN.visibility.y; o.Albedo = c.rgb * IN.visibility.x; o.Specular = _Specular * explored; o.Smoothness = _Glossiness; o.Occlusion = explored; o.Alpha = ca * explored; } 


隠された氎。

河口、川、道路を隠す


私たちは、シェヌダ滞圚河口、河川ず道路の䞀般的な説明を。3぀はすべお透過的であり、2぀のセルのデヌタを結合したす。それらをすべお鏡面反射に切り替えおから、visibility研究デヌタに远加したす。

  struct Input { 
 float2 visibility; }; 
 void vert (inout appdata_full v, out Input data) { 
 data.visibility.x = cell0.x * v.color.x + cell1.x * v.color.y; data.visibility.x = lerp(0.25, 1, data.visibility.x); data.visibility.y = cell0.y * v.color.x + cell1.y * v.color.y; } 

新しいデヌタを䜿甚するように、河口および川surfシェヌダヌの機胜を倉曎したす。どちらも同じ倉曎を行う必芁がありたす。

  void surf (Input IN, inout SurfaceOutputStandardSpecular o) { 
 float explored = IN.visibility.y; fixed4 c = saturate(_Color + water); o.Albedo = c.rgb * IN.visibility.x; o.Specular = _Specular * explored; o.Smoothness = _Glossiness; o.Occlusion = explored; o.Alpha = ca * explored; } 

Shader Roadは、远加の混合メトリックを䜿甚するため、少し異なりたす。

  void surf (Input IN, inout SurfaceOutputStandardSpecular o) { float4 noise = tex2D(_MainTex, IN.worldPos.xz * 0.025); fixed4 c = _Color * ((noise.y * 0.75 + 0.25) * IN.visibility.x); float blend = IN.uv_MainTex.x; blend *= noise.x + 0.5; blend = smoothstep(0.4, 0.7, blend); float explored = IN.visibility.y; o.Albedo = c.rgb; o.Specular = _Specular * explored; o.Smoothness = _Glossiness; o.Occlusion = explored; o.Alpha = blend * explored; } 


すべおが隠されおいたす。

ナニティパッケヌゞ

未探玢のセルの回避


未知のものはすべお芖芚的に隠されおいたすが、パスを怜玢する際に調査の状態は考慮されたせん。その結果、ナニットは未探玢のセルを移動するように呜什され、魔法のように移動する方法を決定できたす。未探玢のセルを避けるためにナニットを匷制する必芁がありたす。


未探玢のセルをナビゲヌトしたす。

分隊が移動コストを決定する


未螏の现胞内に入る前に、のは、コストの動きに転送するためのコヌドを手盎ししたしょうHexGridではHexUnit。これにより、異なる移動ルヌルを持぀ナニットのサポヌトが簡玠化されたす。䞀般的な方法に

远加しお、移動のコストを決定したす。圌はどの现胞が现胞間を移動しおいるか、たた方向を知る必芁がありたす。このメ゜ッドに移行するコストの察応するコヌドをコピヌし、倉数の名前を倉曎したす。HexUnitGetMoveCostHexGrid.Search

  public int GetMoveCost ( HexCell fromCell, HexCell toCell, HexDirection direction) { HexEdgeType edgeType = fromCell.GetEdgeType(toCell); if (edgeType == HexEdgeType.Cliff) { continue; } int moveCost; if (fromCell.HasRoadThroughEdge(direction)) { moveCost = 1; } else if (fromCell.Walled != toCell.Walled) { continue; } else { moveCost = edgeType == HexEdgeType.Flat ? 5 : 10; moveCost += toCell.UrbanLevel + toCell.FarmLevel + toCell.PlantLevel; } } 

メ゜ッドは移動のコストを返す必芁がありたす。叀いコヌドを䜿甚しお無効な移動をスキップしたしたがcontinue、このアプロヌチはここでは機胜したせん。移動が䞍可胜な堎合は、移動の負のコストを返したす。

  public int GetMoveCost ( HexCell fromCell, HexCell toCell, HexDirection direction) { HexEdgeType edgeType = fromCell.GetEdgeType(toCell); if (edgeType == HexEdgeType.Cliff) { return -1; } int moveCost; if (fromCell.HasRoadThroughEdge(direction)) { moveCost = 1; } else if (fromCell.Walled != toCell.Walled) { return -1; } else { moveCost = edgeType == HexEdgeType.Flat ? 5 : 10; moveCost += toCell.UrbanLevel + toCell.FarmLevel + toCell.PlantLevel; } return moveCost; } 

ここで、速床だけでなく、遞択したナニットもパスを芋぀けるずきに知る必芁がありたす。それに応じお倉曎しHexGameUI.DoPathFindingたす。

  void DoPathfinding () { if (UpdateCurrentCell()) { if (currentCell && selectedUnit.IsValidDestination(currentCell)) { grid.FindPath(selectedUnit.Location, currentCell, selectedUnit); } else { grid.ClearPath(); } } } 

チヌムのスピヌドにアクセスする必芁があるため、HexUnitプロパティに远加したすSpeed。䞀方、24の定数倀を返したす。

  public int Speed { get { return 24; } } 

HexGrid倉曎FindPath、そしおSearch、圌らは私たちの新しいアプロヌチで䜜業できるようにしたす。

  public void FindPath (HexCell fromCell, HexCell toCell, HexUnit unit) { ClearPath(); currentPathFrom = fromCell; currentPathTo = toCell; currentPathExists = Search(fromCell, toCell, unit); ShowPath(unit.Speed); } bool Search (HexCell fromCell, HexCell toCell, HexUnit unit) { int speed = unit.Speed; 
 } 

次にSearch、次のセルに移動できるかどうか、および移動のコストを決定した叀いコヌドから削陀したす。代わりに、それが呌び出されたすHexUnit.IsValidDestinationずHexUnit.GetMoveCost。移動コストが負の堎合、セルをスキップしたす。

  for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { HexCell neighbor = current.GetNeighbor(d); if ( neighbor == null || neighbor.SearchPhase > searchFrontierPhase ) { continue; } // if (neighbor.IsUnderwater || neighbor.Unit) { // continue; // } // HexEdgeType edgeType = current.GetEdgeType(neighbor); // if (edgeType == HexEdgeType.Cliff) { // continue; // } // int moveCost; // if (current.HasRoadThroughEdge(d)) { // moveCost = 1; // } // else if (current.Walled != neighbor.Walled) { // continue; // } // else { // moveCost = edgeType == HexEdgeType.Flat ? 5 : 10; // moveCost += neighbor.UrbanLevel + neighbor.FarmLevel + // neighbor.PlantLevel; // } if (!unit.IsValidDestination(neighbor)) { continue; } int moveCost = unit.GetMoveCost(current, neighbor, d); if (moveCost < 0) { continue; } int distance = current.Distance + moveCost; int turn = (distance - 1) / speed; if (turn > currentTurn) { distance = turn * speed + moveCost; } 
 } 

未探玢の゚リアをバむパスする


未探玢のセルを避けるために必芁なこずはHexUnit.IsValidDestination、セルが怜査されたかどうかを確認するこずだけです。

  public bool IsValidDestination (HexCell cell) { return cell.IsExplored && !cell.IsUnderwater && !cell.Unit; } 


より倚くのナニットが未探玢のセルに到達するこずはできたせん。

未探玢のセルは有効な゚ンドポむントではないため、分隊ぱンドポむントに移動するずきにそれらを回避したす。぀たり、未開拓の゚リアは障壁ずしお機胜し、経路を延長したり、䞍可胜にしたりするこずさえありたす。最初に゚リアを探玢するには、ナニットを未知の地圢に近づける必芁がありたす。

移動䞭に短いパスが衚瀺されたらどうなりたすか
. , . .

, , . , .

ナニティパッケヌゞ

パヌト22可芖性の匷化



地図探玢のサポヌトを远加するこずにより、蚈算ず可芖性の遷移を改善したす。


さらに芋るには、高く登りたす。

可芖性の遷移


セルは、切り離しの範囲内にあるかどうかによっお、衚瀺たたは非衚瀺になりたす。ナニットがセル間を移動するのに時間が必芁ず思われる堎合でも、その芖野はセルからセルぞ瞬時にゞャンプしたす。その結果、呚囲のセルの可芖性が劇的に倉化したす。分隊の動きはスムヌズに芋えたすが、芖界の倉化は突然です。

理想的には、可芖性もスムヌズに倉化するはずです。いったん芖野に入るず、现胞は埐々に照らされ、それを残しお、埐々に暗くなりたす。それずも、むンスタントトランゞションを奜むでしょうかHexCellShaderDataむンスタントトランゞションを切り替えるプロパティに远加したしょう。デフォルトでは、移行はスムヌズになりたす。

  public bool ImmediateMode { get; set; } 

遷移セルの远跡


スムヌズな遷移を衚瀺する堎合でも、真の可芖性デヌタは䟝然ずしおバむナリのたたです。぀たり、効果は芖芚的なものにすぎたせん。これは、可芖性の遷移を凊理する必芁があるこずを意味しHexCellShaderDataたす。遷移が実行されるセルのリストを提䟛したす。各初期化で空であるこずを確認しおください。

 using System.Collections.Generic; using UnityEngine; public class HexCellShaderData : MonoBehaviour { Texture2D cellTexture; Color32[] cellTextureData; List<HexCell> transitioningCells = new List<HexCell>(); public bool ImmediateMode { get; set; } public void Initialize (int x, int z) { 
 transitioningCells.Clear(); enabled = true; } 
 } 

珟時点では、セルデヌタをRefreshVisibility盎接蚭定しおいたす。これは、むンスタントトランゞションでは䟝然ずしお正しいですが、無効になっおいる堎合は、トランゞションセルのリストにセルを远加する必芁がありたす。

  public void RefreshVisibility (HexCell cell) { int index = cell.Index; if (ImmediateMode) { cellTextureData[index].r = cell.IsVisible ? (byte)255 : (byte)0; cellTextureData[index].g = cell.IsExplored ? (byte)255 : (byte)0; } else { transitioningCells.Add(cell); } enabled = true; } 

珟時点では、リスト内のセルに察しお䜕も実行しおいないため、可芖性はもう機胜しおいないようです。

ルヌプ内のセルをルヌプする


察応する倀を即座に255たたは0に蚭定する代わりに、これらの倀を埐々に増枛したす。遷移の滑らかさは、倉化率に䟝存したす。非垞に高速でも䜎速でもないはずです。矎しいトランゞションずゲヌムの利䟿性の間の適切な劥協点は、1秒以内に倉曎するこずです。倉曎しやすくするために、これに定数を蚭定したしょう。

  const float transitionSpeed = 255f; 

これでLateUpdate、倀に適甚されるデルタを定矩できたす。これを行うには、時間差に速床を掛けたす。敎数である必芁がありたす。どのくらいの倧きさになるかわからないからです。フレヌムレヌトが急激に䜎䞋するず、デルタが255を超える可胜

性がありたす。さらに、遷移セルがある間に曎新する必芁がありたす。そのため、リストに䜕かがある間はコヌドを含める必芁がありたす。

  void LateUpdate () { int delta = (int)(Time.deltaTime * transitionSpeed); cellTexture.SetPixels32(cellTextureData); cellTexture.Apply(); enabled = transitioningCells.Count > 0; } 

非垞に理論的に可胜な非垞に高いフレヌムレヌト。䜎い遷移速床ず組み合わせお、これは0のデルタを䞎えるこずができたす。倉曎を行うために、デルタの最小倀を匷制的に1にしたす。

  int delta = (int)(Time.deltaTime * transitionSpeed); if (delta == 0) { delta = 1; } 

デルタを受け取ったら、すべおの遷移セルをルヌプしおデヌタを曎新できたす。このためのメ゜ッドがありUpdateCellData、そのパラメヌタヌは察応するセルずデルタであるずしたす。

  int delta = (int)(Time.deltaTime * transitionSpeed); if (delta == 0) { delta = 1; } for (int i = 0; i < transitioningCells.Count; i++) { UpdateCellData(transitioningCells[i], delta); } 

ある時点で、セルの移行が完了するはずです。メ゜ッドが、遷移がただ進行䞭かどうかに関する情報を返すず仮定したす。凊理が停止したら、リストからセルを削陀できたす。その埌、セルをスキップしないように反埩子をデクリメントする必芁がありたす。

  for (int i = 0; i < transitioningCells.Count; i++) { if (!UpdateCellData(transitioningCells[i], delta)) { transitioningCells.RemoveAt(i--); } } 

遷移セルが凊理される順序は重芁ではありたせん。したがっお、珟圚のむンデックスのセルを削陀する必芁はありたせん。削陀するず、RemoveAtすべおのセルが匷制的に移動されたす。代わりに、最埌のセルを珟圚のむンデックスに移動しおから、最埌のセルを削陀したす。

  if (!UpdateCellData(transitioningCells[i], delta)) { transitioningCells[i--] = transitioningCells[transitioningCells.Count - 1]; transitioningCells.RemoveAt(transitioningCells.Count - 1); } 

次に、メ゜ッドを䜜成する必芁がありたすUpdateCellData。圌の仕事をするために、圌はむンデックスずセルデヌタを必芁ずするので、それらを取埗するこずから始めたしょう。たた、セルの曎新を続行するかどうかも決定する必芁がありたす。デフォルトでは、それは必芁ないず仮定したす。䜜業の完了埌、倉曎されたデヌタを適甚し、ステヌタス「曎新が継続䞭」を返す必芁がありたす。

  bool UpdateCellData (HexCell cell, int delta) { int index = cell.Index; Color32 data = cellTextureData[index]; bool stillUpdating = false; cellTextureData[index] = data; return stillUpdating; } 

セルデヌタの曎新


この段階で、移行䞭たたは既に完了したセルがありたす。たず、セルプロヌブの状態を確認したしょう。セルが調べられたが、そのG倀がただ255に等しくない堎合、遷移のプロセスにあるため、これを監芖したす。

  bool stillUpdating = false; if (cell.IsExplored && data.g < 255) { stillUpdating = true; } cellTextureData[index] = data; 

遷移を実行するには、セルのG倀にデルタを远加したす。算術挔算はバむトでは機胜せず、最初に敎数に倉換されたす。したがっお、合蚈は敎数圢匏になり、バむトに倉換する必芁がありたす。

  if (cell.IsExplored && data.g < 255) { stillUpdating = true; int t = data.g + delta; data.g = (byte)t; } 

ただし、倉換の前に、倀が255を超えないようにする必芁がありたす。

  int t = data.g + delta; data.g = t >= 255 ? (byte)255 : (byte)t; 

次に、Rの倀を䜿甚する可芖性に぀いおも同じこずを行う必芁がありたす。

  if (cell.IsExplored && data.g < 255) { 
 } if (cell.IsVisible && data.r < 255) { stillUpdating = true; int t = data.r + delta; data.r = t >= 255 ? (byte)255 : (byte)t; } 

セルが再び䞍可芖になる可胜性があるため、Rの倀を枛らす必芁があるかどうかを確認する必芁がありたす。これは、セルが䞍可芖であるがRがれロより倧きい堎合に発生したす。

  if (cell.IsVisible) { if (data.r < 255) { stillUpdating = true; int t = data.r + delta; data.r = t >= 255 ? (byte)255 : (byte)t; } } else if (data.r > 0) { stillUpdating = true; int t = data.r - delta; data.r = t < 0 ? (byte)0 : (byte)t; } 

これでUpdateCellData準備が敎い、可芖性の移行が正しく実行されたす。


可芖性の遷移。

重耇する遷移芁玠に察する保護


トランゞションは機胜したすが、重耇したアむテムがリストに衚瀺される堎合がありたす。これは、移行䞭のセルの衚瀺状態が倉化した堎合に発生したす。たずえば、分隊の移動䞭に短時間だけセルが衚瀺される堎合。

重耇した芁玠の出珟の結果ずしお、セル遷移はフレヌムごずに数回曎新され、遷移の高速化ず䜙分な䜜業に぀ながりたす。これを防ぐには、セルを远加する前に、すでにリストにあるかどうかを確認したす。ただし、すべおの呌び出しでリスト怜玢RefreshVisibility特に、耇数のセル遷移が実行される堎合、コストがかかりたす。代わりに、セルが移行䞭かどうかを瀺すためにただ䜿甚されおいない別のチャネル、たずえば倀Bを䜿甚したす。セルをリストに远加するずき、倀255を割り圓お、倀が255に等しくないセルのみを远加したす。

  public void RefreshVisibility (HexCell cell) { int index = cell.Index; if (ImmediateMode) { cellTextureData[index].r = cell.IsVisible ? (byte)255 : (byte)0; cellTextureData[index].g = cell.IsExplored ? (byte)255 : (byte)0; } else if (cellTextureData[index].b != 255) { cellTextureData[index].b = 255; transitioningCells.Add(cell); } enabled = true; } 

これが機胜するには、セル遷移の完了埌にBの倀をリセットする必芁がありたす。

  bool UpdateCellData (HexCell cell, int delta) { 
 if (!stillUpdating) { data.b = 0; } cellTextureData[index] = data; return stillUpdating; } 


重耇のない遷移。

可芖性の即時読み蟌み


可芖性の倉曎は、マップをロヌドしおいるずきでも垞に緩やかです。マップはセルがすでに衚瀺されおいる状態を衚しおいるため、これは非論理的です。したがっお、遷移はここでは䞍適切です。さらに、倧きなマップの倚くの可芖セルに察しおトランゞションを実行するず、ロヌド埌にゲヌムが遅くなる可胜性がありたす。したがっお、セルず分隊をロヌドする前HexGrid.Loadに、むンスタント遷移モヌドに切り替えたしょう。

  public void Load (BinaryReader reader, int header) { 
 cellShaderData.ImmediateMode = true; for (int i = 0; i < cells.Length; i++) { cells[i].Load(reader, header); } 
 } 

したがっお、むンスタント遷移モヌドの初期蚭定を、それが䜕であれ再定矩したす。おそらく既に無効化されおいるか、構成オプションが䜜成されおいるため、初期モヌドを蚘憶し、䜜業の完了埌に切り替えたす。

  public void Load (BinaryReader reader, int header) { 
 bool originalImmediateMode = cellShaderData.ImmediateMode; cellShaderData.ImmediateMode = true; 
 cellShaderData.ImmediateMode = originalImmediateMode; } 

ナニティパッケヌゞ

高さ䟝存スコヌプ


これたで、すべおのナニットに3぀の䞀定のスコヌプを䜿甚したしたが、実際にはもっず耇雑です。䞀般的な堎合、次の2぀の理由でオブゞェクトを芋るこずができたせん。䜕らかの障害によりオブゞェクトが芋えないか、オブゞェクトが小さすぎるか遠すぎたす。このゲヌムでは、スコヌプ制限のみを実装したす。

地球は私たちの芖野を閉じおいるため、地球の反察偎にあるものを芋るこずができたせん。地平線しか芋えたせん。惑星はほが球䜓ず芋なすこずができるため、芖点が高いほど、より倚くの衚面を芋るこずができたす。぀たり、地平線は高さに䟝存したす。


地平線は芖点の高さに䟝存したす。

私たちのナニットの限られた可芖性は、地球の曲率によっお䜜成された地平線効果を暡倣しおいたす。レビュヌの範囲は、惑星のサむズず地図の瞮尺によっお異なりたす。少なくずもそれは論理的な説明です。しかし、範囲を瞮小する䞻な理由はゲヌムプレむであり、これは戊争の霧ず呌ばれる制限です。ただし、芖野の基瀎ずなる物理孊を理解するず、地平線から遠ざかり、より䜎い障害物を芋るこずができるため、高い芖点には戊略的䟡倀があるず結論付けるこずができたす。しかし、これたでのずころ、私たちはそれを実珟しおいたせん。

レビュヌの高さ


スコヌプを決定するずきに高さを考慮するには、高さを知る必芁がありたす。これは、陞䞊のセルか氎かによっお、通垞の高さたたは氎のレベルになりたす。これをHexCellプロパティに远加したしょう。

  public int ViewElevation { get { return elevation >= waterLevel ? elevation : waterLevel; } } 

ただし、高さがスコヌプに圱響する堎合、セルの衚瀺高さが倉わるず、可芖性の状況も倉わる可胜性がありたす。セルが耇数のナニットの範囲をブロックしおいるか、珟圚ブロックしおいるため、倉曎が必芁なものを刀断するのはそれほど簡単ではありたせん。セル自䜓はこの問題を解決できないため、状況の倉化を報告させHexCellShaderDataたす。HexCellShaderDataこのためのメ゜ッドがあるずしたすViewElevationChanged。HexCell.Elevation必芁に応じお、割り圓お時に呌び出したす。

  public int Elevation { get { return elevation; } set { if (elevation == value) { return; } int originalViewElevation = ViewElevation; elevation = value; if (ViewElevation != originalViewElevation) { ShaderData.ViewElevationChanged(); } 
 } } 

同じこずが圓おはたりWaterLevelたす。

  public int WaterLevel { get { return waterLevel; } set { if (waterLevel == value) { return; } int originalViewElevation = ViewElevation; waterLevel = value; if (ViewElevation != originalViewElevation) { ShaderData.ViewElevationChanged(); } ValidateRivers(); Refresh(); } } 

可芖性をリセット


次に、メ゜ッドを䜜成する必芁がありたすHexCellShaderData.ViewElevationChanged。䞀般的な可芖性の状況の倉化を刀断するこずは、特に耇数のセルを同時に倉曎する堎合は難しい䜜業です。したがっお、トリックを思い付くのではなく、すべおのセルの可芖性をリセットするこずを蚈画したす。これを行う必芁があるかどうかを远跡するブヌルフィヌルドを远加したす。メ゜ッド内では、単玔にtrueに蚭定し、コンポヌネントを含めたす。同時に倉曎されるセルの数に関係なく、これは単䞀のリセットに぀ながりたす。

  bool needsVisibilityReset; 
 public void ViewElevationChanged () { needsVisibilityReset = true; enabled = true; } 

すべおのセルの可芖性の倀をリセットするには、それらにアクセスできる必芁がありたすがHexCellShaderData、持っおいたせん。それでは、この責任を委任したしょうHexGrid。これを行うには、HexCellShaderDataプロパティを远加する必芁がありたす。これにより、グリッドを参照できたす。次に、それを䜿甚しおLateUpdateリセットを芁求できたす。

  public HexGrid Grid { get; set; } 
 void LateUpdate () { if (needsVisibilityReset) { needsVisibilityReset = false; Grid.ResetVisibility(); } 
 } 

次に進みたしょう。シェヌダヌデヌタを䜜成した埌HexGrid、グリッドぞのリンクを蚭定しHexGrid.Awakeたす。

  void Awake () { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); HexUnit.unitPrefab = unitPrefab; cellShaderData = gameObject.AddComponent<HexCellShaderData>(); cellShaderData.Grid = this; CreateMap(cellCountX, cellCountZ); } 

HexGridResetVisibilityすべおのセルをリセットするメ゜ッドも取埗する必芁がありたす。ルヌプ内のすべおのセルを巡回させ、リセットを自分自身に委任するだけです。

  public void ResetVisibility () { for (int i = 0; i < cells.Length; i++) { cells[i].ResetVisibility(); } } 

次に、HexCellメ゜ッドに远加する必芁がありResetVisibiltyたす。単に可芖性をれロにし、可芖性の曎新をトリガヌしたす。これは、セルの可芖性がれロより倧きい堎合に実行する必芁がありたす。

  public void ResetVisibility () { if (visibility > 0) { visibility = 0; ShaderData.RefreshVisibility(this); } } 

すべおの可芖性デヌタをリセットした埌HexGrid.ResetVisibility、すべおのナニットに可芖性を再床適甚する必芁がありたす。そのためには、各ナニットのスコヌプを知る必芁がありたす。プロパティを䜿甚しお取埗できるずしたすVisionRange。

  public void ResetVisibility () { for (int i = 0; i < cells.Length; i++) { cells[i].ResetVisibility(); } for (int i = 0; i < units.Count; i++) { HexUnit unit = units[i]; IncreaseVisibility(unit.Location, unit.VisionRange); } } 

これが機胜するように、名前の倉曎HexUnit.visionRangeをリファクタリングHexUnit.VisionRangeしおプロパティに倉換したす。3の定数倀を受け取りたすが、将来的には倉曎されたす。

  public int VisionRange { get { return 3; } } 

このため、セルの衚瀺高さを倉曎しおも、可芖性デヌタはリセットされ、正しいたたになりたす。ただし、スコヌプを決定するルヌルを倉曎し、再生モヌドで再コンパむルを実行する可胜性がありたす。スコヌプを独立しお倉曎HexGrid.OnEnableするには、再コンパむルが怜出されたずきにリセットを開始したしょう。

  void OnEnable () { if (!HexMetrics.noiseSource) { 
 ResetVisibility(); } } 

これで、プレむモヌドのたたで、スコヌプコヌドを倉曎しお結果を確認できたす。

地平線を広げる


スコヌプの蚈算が決定されHexGrid.GetVisibleCellsたす。高さがスコヌプに圱響するようにfromCell、透過領域を䞀時的に再定矩するこずにより、単に衚瀺の高さを䜿甚できたす。したがっお、これが機胜するかどうかを簡単に確認できたす。

  List<HexCell> GetVisibleCells (HexCell fromCell, int range) { 
 range = fromCell.ViewElevation; fromCell.SearchPhase = searchFrontierPhase; fromCell.Distance = 0; searchFrontier.Enqueue(fromCell); 
 } 


スコヌプずしお高さを䜿甚したす。

可芖性の障害


衚瀺高さをスコヌプずしお適甚するこずは、他のすべおのセルの高さがれロの堎合にのみ正しく機胜したす。ただし、すべおのセルの高さが芖点ず同じ堎合、スコヌプはれロになりたす。さらに、高さが高いセルは、背埌の䜎いセルの可芖性をブロックする必芁がありたす。これたでのずころ、これは実装されおいたせん。


スコヌプは䜕も劚げたせん。

スコヌプを決定する最も正しい方法は、光線の攟射で確認するこずですが、すぐにコストがかかり、それでも奇劙な結果が生成されたす。完璧である必芁のない、十分な結果を生み出す迅速な゜リュヌションが必芁です。さらに、スコヌプを決定するためのルヌルは、プレヌダヌにずっおシンプルで盎感的で予枬可胜なものであるこずが重芁です。

解決策は次のずおりです。セルの可芖性を刀断するずき、隣接するセルの衚瀺高さを察象距離に远加したす。実際、これにより、これらのセルを芋たずきにスコヌプが瞮小され、それらがスキップされた堎合、その背埌のセルに到達できなくなりたす。

  int distance = current.Distance + 1; if (distance + neighbor.ViewElevation > range) { continue; } 


高いセルはビュヌをブロックしたす。

私たちは遠くに高い现胞を芋るべきではありたせんか
, , , . , .

角を芋回さないでください


珟圚、高いセルはビュヌを䜎い䜍眮にブロックしおいるように芋えたすが、スコヌプが貫通するこずもありたすが、そうではないようです。これは、怜玢アルゎリズムがブロッキングセルをバむパスしおこれらのセルぞのパスを怜出するためです。その結果、芖界が障害物を回避できるかのように芋えたす。これを回避するには、セルの可芖性を刀断するずきに最短パスのみが考慮されるようにする必芁がありたす。これは、必芁以䞊に長くなるパスをドロップするこずで実行できたす。

  HexCoordinates fromCoordinates = fromCell.coordinates; while (searchFrontier.Count > 0) { 
 for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { 
 int distance = current.Distance + 1; if (distance + neighbor.ViewElevation > range || distance > fromCoordinates.DistanceTo(neighbor.coordinates) ) { continue; } 
 } } 


最短パスのみを䜿甚したす。

そこで、明らかに間違いのあるケヌスのほずんどを修正したした。近くのセルでは、最短のパスしか存圚しないため、これはうたく機胜したす。セルが遠いほどパスのオプションが増えるため、遠距離でも可芖性゚ンベロヌプが発生する可胜性がありたす。可芖領域が小さいたたで、隣接する高さの差が倧きすぎない堎合、これは問題になりたせん。

最埌に、送信されたビュヌのフィヌルドを眮き換える代わりに、ビュヌの高さを远加したす。チヌムの芖界は、その高さ、飛行高床、偵察胜力を瀺しおいたす。

  range += fromCell.ViewElevation; 


䜎い芖点での完党な芖野での衚瀺。

぀たり、芖界に察するセルの高さの違いを考慮しお、芖野ぞの最短経路に沿っお移動する堎合、芖界の最終芏則が芖芚に適甚されたす。セルがスコヌプ倖にある堎合、セルはすべおのパスをブロックしたす。その結果、ビュヌを劚げるものがない高い芳枬ポむントは、戊略的に䟡倀がありたす。

オブゞェクトの可芖性を劚げるこずはどうですか
, , . , , . .

ナニティパッケヌゞ

探玢できない现胞


可芖性に関する最埌の問題は、マップの端に関するものです。゚ッゞのセルに隣接セルがないため、トランゞションなしで突然レリヌフが終了したす。


マップのマヌクされた゚ッゞ。

理想的には、未探玢の領域ず地図の端の芖芚的な衚瀺は同じでなければなりたせん。゚ッゞを䞉角圢分割する堎合、隣接する゚ッゞがない堎合に特殊なケヌスを远加するこずでこれを実珟できたすが、これには远加のロゞックが必芁であり、欠損セルを凊理する必芁がありたす。したがっお、このような解決策は重芁です。別の方法は、マップの境界セルが分隊の範囲内にある堎合でも、匷制的に未探玢にするこずです。このアプロヌチははるかに単玔なので、䜿甚したしょう。たた、未探玢のセルや他のセルずしおマヌクするこずもできるため、マップの䞍均䞀な゚ッゞを簡単に䜜成できたす。さらに、端にある非衚瀺のセルを䜿甚するず、川ず道路のマップに出入りする道路ず川を䜜成できたす。これらの終点は範囲倖になるためです。たた、この゜リュヌションの助けを借りお、カヌドに出入りするナニットを远加できたす。

セルを調査枈みずしおマヌクしたす


セルを怜査できるこずを瀺すには、HexCellプロパティに远加したすExplorable。

  public bool Explorable { get; set; } 

これで、調査察象のセルが衚瀺されるようになるため、IsVisibleこれを考慮しおプロパティを倉曎したす。

  public bool IsVisible { get { return visibility > 0 && Explorable; } } 

同じこずが適甚されIsExploredたす。ただし、このために暙準プロパティを調査したした。ゲッタヌのロゞックを倉曎できるように、明瀺的なプロパティに倉換する必芁がありたす。

  public bool IsExplored { get { return explored && Explorable; } private set { explored = value; } } 
 bool explored; 

地図の端を隠す


メ゜ッドで長方圢マップの゚ッゞを非衚瀺にできHexGrid.CreateCellたす。端にないセルは調査され、残りはすべお未探玢です。

  void CreateCell (int x, int z, int i) { 
 HexCell cell = cells[i] = Instantiate<HexCell>(cellPrefab); cell.transform.localPosition = position; cell.coordinates = HexCoordinates.FromOffsetCoordinates(x, z); cell.Index = i; cell.ShaderData = cellShaderData; cell.Explorable = x > 0 && z > 0 && x < cellCountX - 1 && z < cellCountZ - 1; 
 } 

今、カヌドは端の呚りで暗くなり、それらの埌ろに巚倧な未螏のスペヌスを隠したす。その結果、マップの調査察象゚リアのサむズは、各次元で2ず぀枛少したす。


マップの未探玢の゚ッゞ。

研究状態を線集可胜にするこずは可胜ですか
, , . .

未探玢のセルは可芖性を劚げたす


最埌に、セルを怜査できない堎合は、可芖性を劚げるはずです。HexGrid.GetVisibleCellsこれを考慮しお倉曎しおください。

  if ( neighbor == null || neighbor.SearchPhase > searchFrontierPhase || !neighbor.Explorable ) { continue; } 

ナニティパッケヌゞ

パヌト23土地の生成



チュヌトリアルのこの郚分は、手順マップ生成のシリヌズの始たりです。

この郚分はUnity 2017.1.0で䜜成されたした。


生成された倚数のマップの1぀。

カヌド生成


任意のマップを䜜成できたすが、時間がかかりたす。アプリケヌションがデザむナヌのためにカヌドを生成し、奜みに合わせお倉曎できるようにしおおくず䟿利です。別の手順を実行しお、蚭蚈を手動で完党に䜜成し、完成したマップを生成する責任をアプリケヌションに完党に移すこずができたす。このため、ゲヌムは毎回新しいマップでプレむでき、各ゲヌムセッションは異なりたす。これらすべおを可胜にするには、マップ生成アルゎリズムを䜜成する必芁がありたす。

必芁な生成アルゎリズムのタむプは、必芁なカヌドのタむプによっお異なりたす。正しいアプロヌチはありたせん。信頌性ずプレむアビリティの間の劥協点を垞に探す必芁がありたす。

カヌドが信じられるためには、プレむダヌにずっお非垞に可胜性があり珟実的である必芁がありたす。これは、地図が地球の䞀郚のように芋えるこずを意味するものではありたせん。それは、異なる惑星たたは完党に異なる珟実かもしれたせん。しかし、もしそれが地球の安reliefを瀺すなら、それは少なくずも郚分的にそれに䌌おいなければなりたせん。

プレむアビリティは、カヌドがゲヌムプレむにどのように察応するかに関連しおいたす。信念ず矛盟するこずもありたす。たずえば、山脈は矎しいように芋えたすが、同時にナニットの移動ずビュヌを倧きく制限したす。これが望たしくない堎合は、山なしで行う必芁があり、信頌性が䜎䞋し、ゲヌムの衚珟力が制限されたす。たたは、山を救うこずができたすが、ゲヌムプレむぞの圱響を枛らすこずができ、信頌性も䜎䞋したす。

さらに、実珟可胜性を考慮する必芁がありたす。たずえば、構造プレヌト、䟵食、雚、火山噎火、met石や月の圱響などをシミュレヌトするこずにより、非垞にリアルな地球のような惑星を䜜成できたす。しかし、そのようなシステムの開発には倚くの時間が必芁です。さらに、そのような惑星を生成するのに長い時間がかかる可胜性があり、プレむダヌは新しいゲヌムを開始する前に数分埅぀必芁はありたせん。぀たり、シミュレヌションは匷力なツヌルですが、䟡栌がかかりたす。

ゲヌムはしばしば、真正性、プレむ可胜性、および実珟可胜性の間のトレヌドオフを䜿甚したす。そのような劥協は目に芋えず、完党に正垞に芋えるこずもあれば、開発プロセス䞭に䞋された決定に応じお、ランダム、䞀貫性のない、たたは無秩序に芋えるこずもありたす。これは、カヌド生成だけでなく、手続き型カヌドゞェネレヌタヌを開発する際には、これに特別な泚意を払う必芁がありたす。あなたが䜜成しおいるゲヌムにずっお圹に立たないこずが刀明した矎しいカヌドを生成するアルゎリズムを䜜成するのに倚くの時間を費やすこずができたす。

このチュヌトリアルシリヌズでは、土地のようなレリヌフを䜜成したす。面癜く、倧きな倉動性ず倧きな均䞀な領域の欠劂が芋られるはずです。レリヌフスケヌルは倧きく、マップは1぀以䞊の倧陞、海掋の領域、たたは惑星党䜓をカバヌしたす。土地の質量、気候、地域の数、地圢の隆起など、地理を管理する必芁がありたす。このパヌトでは、寿叞䜜りの基瀎を築きたす。

線集モヌドで開始する


ゲヌムプレむではなくマップに焊点を圓おるので、線集モヌドでアプリケヌションを起動する方が䟿利です。これにより、すぐにカヌドを芋るこずができたす。したがっお、HexMapEditor.Awake線集モヌドをtrueに蚭定し、このモヌドのシェヌダヌキヌワヌドをオンにしお倉曎したす。

  void Awake () { terrainMaterial.DisableKeyword("GRID_ON"); Shader.EnableKeyword("HEX_MAP_EDIT_MODE"); SetEditMode(true); } 

カヌドゞェネレヌタヌ


手続き型マップを生成するには非垞に倚くのコヌドが必芁なため、に盎接远加したせんHexGrid。代わりに、私たちは、新しいコンポヌネントを䜜成しHexMapGenerator、そしおHexGridそれはないだろう知っおいたす。これにより、必芁に応じお別のアルゎリズムぞの移行が簡単になりたす。

ゞェネレヌタヌにはグリッドぞのリンクが必芁なので、䞀般的なフィヌルドを远加したす。さらにGenerateMap、アルゎリズムを凊理する䞀般的なメ゜ッドを远加したす。マップの寞法をパラメヌタヌずしお指定し、それを䜿甚しお新しい空のマップを䜜成したす。

 using System.Collections.Generic; using UnityEngine; public class HexMapGenerator : MonoBehaviour { public HexGrid grid; public void GenerateMap (int x, int z) { grid.CreateMap(x, z); } } 

コンポヌネントを持぀オブゞェクトをシヌンに远加し、HexMapGeneratorそれをグリッドに接続したす。


マップゞェネレヌタヌオブゞェクト。

新しいマップのメニュヌを倉曎する


NewMapMenu空のカヌドを䜜成するだけでなく、カヌドを生成できるように倉曎したす。generateMapsデフォルトで倀を持぀ブヌル型フィヌルドを介しお機胜を制埡したすtrue。optionsを切り替えるために行ったように、このフィヌルドを蚭定する䞀般的な方法を䜜成したしょうHexMapEditor。適切なスむッチをメニュヌに远加し、メ゜ッドに接続したす。

  bool generateMaps = true; public void ToggleMapGeneration (bool toggle) { generateMaps = toggle; } 


スむッチ付きの新しいカヌドのメニュヌ。

メニュヌにマップゞェネレヌタヌぞのリンクを指定したす。次に、必芁に応じお、グリッドGenerateMapを実行するだけでなく、ゞェネレヌタヌメ゜ッドを呌び出したすCreateMap。

  public HexMapGenerator mapGenerator; 
 void CreateMap (int x, int z) { if (generateMaps) { mapGenerator.GenerateMap(x, z); } else { hexGrid.CreateMap(x, z); } HexMapCamera.ValidatePosition(); Close(); } 


ゞェネレヌタヌぞの接続。

セルアクセス


ゞェネレヌタヌが機胜するためには、セルにアクセスする必芁がありたす。私たちはHexGrid、すでに䞀般的な方法持っおいるGetCell必芁や䜍眮ベクトル、たたは六角圢の座暙を。ゞェネレヌタはどちらか䞀方を䜿甚する必芁がないためHexGrid.GetCell、セルのオフセットたたはむンデックスの座暙を䜿甚する2぀の䟿利なメ゜ッドを远加したす。

  public HexCell GetCell (int xOffset, int zOffset) { return cells[xOffset + zOffset * cellCountX]; } public HexCell GetCell (int cellIndex) { return cells[cellIndex]; } 

これでHexMapGenerator、セルを盎接受信できたす。たずえば、新しいマップを䜜成した埌、草の座暙を䜿甚しお、セルの䞭倮の列の起䌏ずしお草を蚭定できたす。

  public void GenerateMap (int x, int z) { grid.CreateMap(x, z); for (int i = 0; i < z; i++) { grid.GetCell(x / 2, i).TerrainTypeIndex = 1; } } 


小さな地図䞊の草の列。

ナニティパッケヌゞ

寿叞䜜り


マップを生成するずき、土地なしで完党に開始したす。党䞖界が1぀の巚倧な海であふれおいるず想像できたす。陞地は、海底の䞀郚が抌し䞊げられお氎面䞊に浮かんだずきに䜜成されたす。この方法でどのくらいの土地を䜜成するか、どこに衚瀺するか、どのような圢状にするかを決定する必芁がありたす。

レリヌフを䞊げる


小さく始めたしょう-私たちは氎の䞊に䞀枚の土地を䞊げたす。このためRaiseTerrainに、プロットのサむズを制埡するパラメヌタヌを持぀メ゜ッドを䜜成したす。このメ゜ッドをで呌び出しGenerateMap、以前のテストコヌドを眮き換えたす。7぀のセルで構成される小さな土地から始めたしょう。

  public void GenerateMap (int x, int z) { grid.CreateMap(x, z); // for (int i = 0; i < z; i++) { // grid.GetCell(x / 2, i).TerrainTypeIndex = 1; // } RaiseTerrain(7); } void RaiseTerrain (int chunkSize) {} 

これたでのずころ、「草」タむプのレリヌフを䜿甚しお高地を瀺し、最初の「砂」レリヌフは海を指したす。匷芁RaiseTerrain我々は土地の適切な量を埗るたで、ランダムなセルを取り、その救枈の皮類を倉曎したす。

ランダムセルを取埗するにはGetRandomCell、ランダムセルむンデックスを決定し、グリッドから察応するセルを取埗するメ゜ッドを远加したす。

  void RaiseTerrain (int chunkSize) { for (int i = 0; i < chunkSize; i++) { GetRandomCell().TerrainTypeIndex = 1; } } HexCell GetRandomCell () { return grid.GetCell(Random.Range(0, grid.cellCountX * grid.cellCountZ)); } 


7぀のランダムな寿叞セル。

最終的には倚くのランダムなセルが必芁になるか、すべおのセルを数回ルヌプする必芁があるため、セル自䜓のセルの数を远跡したしょうHexMapGenerator。

  int cellCount; public void GenerateMap (int x, int z) { cellCount = x * z; 
 } 
 HexCell GetRandomCell () { return grid.GetCell(Random.Range(0, cellCount)); } 

1぀のサむトの䜜成


これたでのずころ、7぀のランダムなセルを土地に倉えおいたすが、どこにでも配眮できたす。ほずんどの堎合、単䞀の土地゚リアを圢成しおいたせん。さらに、同じセルを耇数回遞択できるため、土地が少なくなりたす。䞡方の問題を無制限に解決するには、最初のセルのみを遞択したす。その埌、以前に遞択したセルの隣にあるセルのみを遞択する必芁がありたす。これらの制限はパス怜玢の制限に䌌おいるため、ここでも同じアプロヌチを䜿甚したす。

にあるようにHexMapGenerator、独自のプロパティず怜玢境界のフェヌズのカりンタヌを远加したすHexGrid。

  HexCellPriorityQueue searchFrontier; int searchFrontierPhase; 

優先キュヌが必芁になる前に存圚するこずを確認したす。

  public void GenerateMap (int x, int z) { cellCount = x * z; grid.CreateMap(x, z); if (searchFrontier == null) { searchFrontier = new HexCellPriorityQueue(); } RaiseTerrain(7); } 

新しいマップを䜜成した埌、すべおのセルの怜玢境界はれロになりたす。ただし、マップ生成のプロセスでセルを怜玢する堎合、このプロセスで怜玢境界を増やしたす。倚数の怜玢操䜜を実行する堎合、それらは蚘録された怜玢境界のフェヌズの前にある可胜性がありたすHexGrid。これは、ナニットパスの怜玢を劚げる可胜性がありたす。これを回避するために、マップ生成プロセスの最埌に、すべおのセルの怜玢フェヌズをれロにリセットしたす。

  RaiseTerrain(7); for (int i = 0; i < cellCount; i++) { grid.GetCell(i).SearchPhase = 0; } 

次にRaiseTerrain、適切なセルを探しお、ランダムに遞択する必芁はありたせん。このプロセスは、の怜玢方法に非垞に䌌おいたすHexGrid。ただし、セルに耇数回アクセスするこずはないため、怜玢境界の䜍盞を2ではなく1増やすだけで十分です。その埌、ランダムに遞択された最初のセルで境界を初期化したす。通垞どおり、怜玢フェヌズの指定に加えお、距離ずヒュヌリスティックにれロの倀を割り圓おたす。

  void RaiseTerrain (int chunkSize) { // for (int i = 0; i < chunkSize; i++) { // GetRandomCell().TerrainTypeIndex = 1; // } searchFrontierPhase += 1; HexCell firstCell = GetRandomCell(); firstCell.SearchPhase = searchFrontierPhase; firstCell.Distance = 0; firstCell.SearchHeuristic = 0; searchFrontier.Enqueue(firstCell); } 

その埌、怜玢ルヌプはほずんどおなじみになりたす。さらに、境界が空になるたで怜玢を続行するには、フラグメントが目的のサむズに達したずきに停止する必芁があるため、远跡したす。各反埩で、キュヌから次のセルを抜出し、救枈のタむプを蚭定し、サむズを倧きくしおから、このセルの隣接セルをバむパスしたす。すべおの隣人は、ただ远加されおいない堎合、単に囜境に远加されたす。倉曎や比范を行う必芁はありたせん。完了したら、境界線をクリアする必芁がありたす。

  searchFrontier.Enqueue(firstCell); int size = 0; while (size < chunkSize && searchFrontier.Count > 0) { HexCell current = searchFrontier.Dequeue(); current.TerrainTypeIndex = 1; size += 1; for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { HexCell neighbor = current.GetNeighbor(d); if (neighbor && neighbor.SearchPhase < searchFrontierPhase) { neighbor.SearchPhase = searchFrontierPhase; neighbor.Distance = 0; neighbor.SearchHeuristic = 0; searchFrontier.Enqueue(neighbor); } } } searchFrontier.Clear(); 


セルのラむン。

適切なサむズの単䞀のプロットを埗たした。セルの数が十分でない堎合にのみ小さくなりたす。境界線が塗り぀ぶされるため、プロットは垞に北西に䌞びる線で構成されたす。マップの端に到達したずきにのみ方向を倉曎したす。

セルを぀なぐ


陞地が線に䌌おいるこずはめったにありたせんが、線が䌌おいる堎合、垞に同じ方向に向いおいるずは限りたせん。サむトの圢状を倉曎するには、セルの優先順䜍を倉曎する必芁がありたす。最初のランダムなセルは、プロットの䞭心ずしお䜿甚できたす。そうするず、他のすべおのセルたでの距離は、この点を基準にしおいたす。そのため、䞭心に近いセルに高い優先順䜍を䞎えるため、サむトは線ずしおではなく䞭心の呚りに成長したす。

  searchFrontier.Enqueue(firstCell); HexCoordinates center = firstCell.coordinates; int size = 0; while (size < chunkSize && searchFrontier.Count > 0) { HexCell current = searchFrontier.Dequeue(); current.TerrainTypeIndex = 1; size += 1; for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { HexCell neighbor = current.GetNeighbor(d); if (neighbor && neighbor.SearchPhase < searchFrontierPhase) { neighbor.SearchPhase = searchFrontierPhase; neighbor.Distance = neighbor.coordinates.DistanceTo(center); neighbor.SearchHeuristic = 0; searchFrontier.Enqueue(neighbor); } } } 


现胞の蓄積。

実際、䞭倮のセルがマップの端に衚瀺されない堎合、7぀のセルはコンパクトな六角圢の領域に矎しく詰め蟌たれおいたす。30のプロットサむズを䜿甚しおみたしょう。

  RaiseTerrain(30); 


30セルの寿叞マス。

正しい六角圢を埗るのに十分なセルがありたせんでしたが、再び同じ圢状になりたした。プロットの半埄が倧きいため、マップの端に近くなる可胜性が高くなり、マップを別の圢状に匷制したす。

寿叞のランダム化


すべおの領域が同じように芋えるこずを望たないため、セルの優先床をわずかに倉曎したす。境界に隣接するセルを远加するたびに、次の数倀がRandom.value特定のしきい倀よりも小さい堎合、このセルのヒュヌリスティックは0ではなく1になりたす。しきい倀ずしお倀0.5を䜿甚したす。

  neighbor.Distance = neighbor.coordinates.DistanceTo(center); neighbor.SearchHeuristic = Random.value < 0.5f ? 1: 0; searchFrontier.Enqueue(neighbor); 


歪んだ領域。

セルの怜玢ヒュヌリスティックを増やすこずで、予想よりも遅くアクセスしたした。同時に、ヒュヌリスティックを増加させない限り、䞭心から1ステップ離れた堎所にある他のセルも早くアクセスされたす。぀たり、すべおのセルのヒュヌリスティックを1぀の倀だけ増やしおも、マップにはたったく圱響したせん。぀たり、しきい倀0のようなしきい倀1の効果はありたせん。たた、しきい倀0.8は0.2に盞圓したす。぀たり、確率が0.5の堎合、怜玢プロセスが最も「震えおいる」こずになりたす。

振動の適切な量は、垌望する地圢のタむプによっお異なりたすので、カスタマむズ可胜にしたしょう。jitterProbability属性を持぀䞀般的なフロヌトフィヌルドをゞェネレヌタヌに远加したすRange0〜0.5の範囲で制限されたす。この間隔の平均、぀たり0.25に等しいデフォルト倀を蚭定したしょう。これにより、Unityむンスペクタヌりィンドりでゞェネレヌタヌを構成できたす。

  [Range(0f, 0.5f)] public float jitterProbability = 0.25f; 


倉動の確率。

ゲヌムUIでカスタマむズ可胜にできたすか
, . UI, . , UI. , . , .

ここで、ヒュヌリスティックが1になるタむミングを決定するために、定数倀の代わりに確率を䜿甚したす。

  neighbor.SearchHeuristic = Random.value < jitterProbability ? 1: 0; 

ヒュヌリスティック倀0ず1を䜿甚したす。より倧きな倀を䜿甚できたすが、これによりセクションの倉圢が倧幅に悪化し、ほずんどの堎合、ストラむプの束になりたす。

いく぀かの土地を䞊げる


1぀の土地の生成に限定されたせん。たずえばRaiseTerrain、ルヌプ内に呌び出しを配眮し​​お、5぀のセクションを取埗したす。

  for (int i = 0; i < 5; i++) { RaiseTerrain(30); } 


土地の5぀の区画。

珟圚はそれぞれ30セルの5぀のプロットを生成しおいたすが、必ずしも150セルの土地を取埗しおいるわけではありたせん。各サむトは個別に䜜成されるため、互いを認識しおいないため、亀差する可胜性がありたす。これは通垞のこずです。孀立したセクションのセットよりも興味深いランドスケヌプを䜜成できるためです。

土地の倉動性を高めるために、各プロットのサむズを倉曎するこずもできたす。2぀の敎数フィヌルドを远加しお、プロットの最小サむズず最倧サむズを制埡したす。それらに十分な間隔、たずえば20〜200を割り圓おたす。暙準の最小倀を30、暙準の最倧倀を100にしたす。

  [Range(20, 200)] public int chunkSizeMin = 30; [Range(20, 200)] public int chunkSizeMax = 100; 


サむゞング間隔。

これらのフィヌルドを䜿甚しお、呌び出されRaiseTerrainたずきに領域のサむズをランダムに決定したす。

  RaiseTerrain(Random.Range(chunkSizeMin, chunkSizeMax + 1)); 


䞭倮のマップ䞊のランダムにサむズ蚭定された5぀のセクション。

十分な寿叞を䜜る


生成される土地の量を特に制埡するこずはできたせんが。プロット数の構成オプションを远加できたすが、プロット自䜓のサむズはランダムであり、わずかにたたは匷く重なる堎合がありたす。したがっお、サむトの数は、必芁な土地の地図䞊の領収曞を保蚌するものではありたせん。敎数ずしお衚される土地の割合を盎接制埡するオプションを远加したしょう。100の土地や氎はあたりおもしろくないので、5〜95の間隔に制限したす。デフォルトでは50です。

  [Range(5, 95)] public int landPercentage = 50; 


寿叞の割合。

適切な量​​の土地の䜜成を保蚌するために、十分な量が埗られるたで地圢の゚リアを䞊げ続ける必芁がありたす。これを行うには、土地の生成を耇雑にするプロセスを制埡する必芁がありたす。したがっお、新しいメ゜ッドを呌び出しお、サむトを䞊げる既存のサむクルを眮き換えたしょうCreateLand。このメ゜ッドが最初に行うこずは、土地になるセルの数を蚈算するこずです。この量は、寿叞现胞の合蚈になりたす。

  public void GenerateMap (int x, int z) { 
 // for (int i = 0; i < 5; i++) { // RaiseTerrain(Random.Range(chunkSizeMin, chunkSizeMax + 1)); // } CreateLand(); for (int i = 0; i < cellCount; i++) { grid.GetCell(i).SearchPhase = 0; } } void CreateLand () { int landBudget = Mathf.RoundToInt(cellCount * landPercentage * 0.01f); } 

CreateLandRaiseTerrain现胞党䜓を䜿い果たすたで 金額を超えないようにするためRaiseTerrain、远加のパラメヌタずしお金額を受け取るように倉曎したす。仕事を終えた埌、圌は残りの金額を返さなければなりたせん。

 // void RaiseTerrain (int chunkSize) { int RaiseTerrain (int chunkSize, int budget) { 
 return budget; } 

セルが境界から削陀され、土地に倉換されるたびに、量は枛少するはずです。この埌、党額が䜿甚された堎合、怜玢を停止しおサむトを完了する必芁がありたす。たた、これは、珟圚のセルがただ土地でない堎合にのみ実行する必芁がありたす。

  while (size < chunkSize && searchFrontier.Count > 0) { HexCell current = searchFrontier.Dequeue(); if (current.TerrainTypeIndex == 0) { current.TerrainTypeIndex = 1; if (--budget == 0) { break; } } size += 1; 
 } 

今でCreateLandは、现胞の党量を䜿うたで土地を䞊げるこずができたす。

  void CreateLand () { int landBudget = Mathf.RoundToInt(cellCount * landPercentage * 0.01f); while (landBudget > 0) { landBudget = RaiseTerrain( Random.Range(chunkSizeMin, chunkSizeMax + 1), landBudget ); } } 


マップのちょうど半分が土地になりたした。

ナニティパッケヌゞ

高さを考慮に入れる


土地は単なる海岞線ではなく、海岞線によっお制限されおいたす。圌女は、䞘、山、谷、湖などを含む、倉化する高さを持っおいたす。ゆっくりず移動する構造プレヌトの盞互䜜甚により、高さに倧きな違いがありたす。シミュレヌトしたせんが、私たちの土地は䜕らかの圢でそのようなプレヌトに䌌おいるはずです。サむトは移動したせんが、亀差する堎合がありたす。そしお、これを利甚できたす。

土地を抌し䞊げる


各セクションは、海底から抌し出された土地の䞀郚を瀺しおいたす。したがっお、珟圚のセルの高さを垞に増やしお、RaiseTerrain䜕が起こるかを芋おみたしょう。

  HexCell current = searchFrontier.Dequeue(); current.Elevation += 1; if (current.TerrainTypeIndex == 0) { 
 } 


高さのある土地。

高さはわかりたしたが、芋にくいです。地理的な階局化など、各高さレベルに独自の地圢タむプを䜿甚するず、それらをより読みやすくするこずができたす。これは、高さをより目立たせるためにのみ行うので、単に高さレベルを暙高むンデックスずしお䜿甚できたす。

高さが地圢タむプの数を超えるずどうなりたすか
. , .

高さを倉曎するたびにセルの地圢タむプを曎新する代わりに、SetTerrainTypeすべおの地圢タむプを1回だけ蚭定する別のメ゜ッドを䜜成したしょう。

  void SetTerrainType () { for (int i = 0; i < cellCount; i++) { HexCell cell = grid.GetCell(i); cell.TerrainTypeIndex = cell.Elevation; } } 

寿叞を䜜成した埌にこのメ゜ッドを呌び出したす。

  public void GenerateMap (int x, int z) { 
 CreateLand(); SetTerrainType(); 
 } 

今、圌RaiseTerrainはタむプの救枈に察凊するこずができず、高さに集䞭するこずができたせん。これを行うには、そのロゞックを倉曎する必芁がありたす。珟圚のセルの新しい高さが1である堎合、そのセルはより也燥しおいるため、セルの合蚈が枛少し、サむトの成長の完了に぀ながる可胜性がありたす。

  HexCell current = searchFrontier.Dequeue(); current.Elevation += 1; if (current.Elevation == 1 && --budget == 0) { break; } // if (current.TerrainTypeIndex == 0) { // current.TerrainTypeIndex = 1; // if (--budget == 0) { // break; // } // } 


局の局化。

氎を加える


すべおのセルの氎䜍を1に蚭定しお、どのセルが氎たたは土地であるかを明瀺的に瀺したしょうGenerateMap。土地を䜜成する前にこれを実行したす。

  public void GenerateMap (int x, int z) { cellCount = x * z; grid.CreateMap(x, z); if (searchFrontier == null) { searchFrontier = new HexCellPriorityQueue(); } for (int i = 0; i < cellCount; i++) { grid.GetCell(i).WaterLevel = 1; } CreateLand(); 
 } 

土地レむダヌの指定には、あらゆるタむプの地圢を䜿甚できたす。最䞋局のセルず同様に、すべおの海底セルは砂のたたです。これは、高さから氎䜍を枛算し、その倀をレリヌフタむプのむンデックスずしお䜿甚するこずで実行できたす。

  void SetTerrainType () { for (int i = 0; i < cellCount; i++) { HexCell cell = grid.GetCell(i); if (!cell.IsUnderwater) { cell.TerrainTypeIndex = cell.Elevation - cell.WaterLevel; } } } 


土地ず氎。

氎䜍を䞊げる


1぀の氎䜍に限定されたせん。間隔が1〜5でデフォルト倀が3の共通フィヌルドを䜿甚しお、カスタマむズ可胜にしたしょう。このレベルを䜿甚しお、セルを初期化したす。

  [Range(1, 5)] public int waterLevel = 3; 
 public void GenerateMap (int x, int z) { 
 for (int i = 0; i < cellCount; i++) { grid.GetCell(i).WaterLevel = waterLevel; } 
 } 



氎䜍3。氎䜍が3の

堎合、予想よりも少ない土地が埗られたす。これは、RaiseTerrain氎䜍が1であるずただ信じおいるためです。それを修正したしょう。

  HexCell current = searchFrontier.Dequeue(); current.Elevation += 1; if (current.Elevation == waterLevel && --budget == 0) { break; } 

より高い氎䜍を䜿甚するず、それに぀ながりたす。现胞がすぐに土地にならないこず。氎䜍が2の堎合、最初のセクションは氎面䞋に残りたす。海の底は䞊昇しおいたすが、ただ氎面䞋にありたす。土地は、少なくずも2぀のセクションの亀差点でのみ圢成されたす。氎䜍が高いほど、土地を䜜成するためにより倚くのサむトを暪断する必芁がありたす。したがっお、氎䜍が䞊昇するず、土地はより混oticずしたす。さらに、より倚くのプロットが必芁な堎合、既存の土地で亀差する可胜性が高くなりたす。そのため、より小さなプロットを䜿甚する堎合のように、山がより䞀般的であり、平坊な土地はあたりありたせん。





氎䜍は2〜5で、寿叞は垞に50です。

ナニティパッケヌゞ

䞊䞋動


ここたでで、プロットを䞀床に1レベル䞊げたしたが、これに限定する必芁はありたせん。

高いサむト


各セクションはセルの高さを1レベル増やしたすが、クリッピングが発生する堎合がありたす。これは、2぀のセクションの゚ッゞが接觊したずきに発生したす。これにより孀立した厖ができたすが、長い厖線はたれです。プロットの高さを1ステップ以䞊増やすこずで、出珟頻床を増やすこずができたす。ただし、これは特定の割合のサむトに察しおのみ行う必芁がありたす。すべおの゚リアが高くなるず、地圢に沿っお移動するこずが非垞に困難になりたす。0.25のデフォルト倀を持぀確率フィヌルドを䜿甚しお、このパラメヌタヌをカスタマむズ可胜にしたしょう。

  [Range(0f, 1f)] public float highRiseProbability = 0.25f; 


现胞の匷い䞊昇の可胜性。

高い領域では高さの増加を䜿甚できたすが、これはすぐに手に負えなくなりたす。高さの差2はすでに厖を䜜っおいるので、これで十分です。氎䜍に等しい高さをスキップできるため、セルが陞地になったかどうかを刀断する方法を倉曎する必芁がありたす。氎䜍を䞋回り、同じレベル以䞊になった堎合、新しい土地セルを䜜成したした。

  int rise = Random.value < highRiseProbability ? 2 : 1; int size = 0; while (size < chunkSize && searchFrontier.Count > 0) { HexCell current = searchFrontier.Dequeue(); int originalElevation = current.Elevation; current.Elevation = originalElevation + rise; if ( originalElevation < waterLevel && current.Elevation >= waterLevel && --budget == 0 ) { break; } size += 1; 
 } 





高さが倧幅に増加する確率は、0.25、0.50、0.75、および1です。

土地を䞋げる


土地は垞に䞊昇するわけではなく、時には䞋降するこずもありたす。土地が十分に䜎くなるず、氎がいっぱいになり、倱われたす。これたでのずころ、これを行っおいたせん。私たちぱリアを抌し䞊げるだけなので、通垞、土地はたずたりのない䞞い゚リアのセットのように芋えたす。時々゚リアを䞋げるず、より倚様なフォヌムが埗られたす。


沈んだ寿叞のない倧きな地図。

別の確率フィヌルドを䜿甚しお、地盀沈䞋の頻床を制埡できたす。䜎䞋は土地を砎壊する可胜性があるため、䜎䞋の確率は垞に䞊昇の確率より䜎くなければなりたせん。そうしないず、適切な割合の土地を取埗するのに非垞に時間がかかる堎合がありたす。したがっお、0.4の最倧䜎䞋確率ず0.2のデフォルト倀を䜿甚したしょう。

  [Range(0f, 0.4f)] public float sinkProbability = 0.2f; 


䜎䞋の確率。

サむトを䞋げるこずは䞊げるこずず䌌おいたすが、いく぀かの違いがありたす。したがっお、メ゜ッドを耇補し、RaiseTerrainその名前をに倉曎しSinkTerrainたす。䞊昇の倧きさを刀断する代わりに、同じロゞックを䜿甚できる䜎䞋倀が必芁です。同時に、氎面を通過したかどうかを確認するための比范を裏返す必芁がありたす。さらに、レリヌフを䞋げるずき、セルの合蚈に限定されたせん。代わりに、倱われた寿叞のセルごずに消費した金額が返されるため、それを増やしお䜜業を続けたす。

  int SinkTerrain (int chunkSize, int budget) { 
 int sink = Random.value < highRiseProbability ? 2 : 1; int size = 0; while (size < chunkSize && searchFrontier.Count > 0) { HexCell current = searchFrontier.Dequeue(); int originalElevation = current.Elevation; current.Elevation = originalElevation - sink; if ( originalElevation >= waterLevel && current.Elevation < waterLevel // && --budget == 0 ) { // break; budget += 1; } size += 1; 
 } searchFrontier.Clear(); return budget; } 

これで、内郚の各反埩で、CreateLand䜎䞋する確率に応じお、土地を䜎䞋たたは䞊昇させる必芁がありたす。

  void CreateLand () { int landBudget = Mathf.RoundToInt(cellCount * landPercentage * 0.01f); while (landBudget > 0) { int chunkSize = Random.Range(chunkSizeMin, chunkSizeMax - 1); if (Random.value < sinkProbability) { landBudget = SinkTerrain(chunkSize, landBudget); } else { landBudget = RaiseTerrain(chunkSize, landBudget); } } } 





ドロップの確率は、0.1、0.2、0.3、および0.4です。

高さを制限する


珟圚の段階では、朜圚的に倚くのセクションをオヌバヌラップさせるこずができたす。堎合によっおは、いく぀かの高さの増加があり、䞀郚は䞋がっおから再び䞊昇するこずがありたす。同時に、特に高い割合の土地が必芁な堎合には、非垞に高い、堎合によっおは非垞に䜎い高さを䜜成できたす。


90の土地での巚倧な高さ。

高さを制限するには、カスタムの最小倀ず最倧倀を远加したしょう。合理的な最小倀は-4〜0の間で、蚱容される最倧倀は6〜10の範囲です。デフォルト倀を-2ず8にしたす。マップを手動で線集する堎合、蚱容範囲倖になるため、UI゚ディタヌのスラむダヌを倉曎するか、そのたたにしおおくこずができたす。

  [Range(-4, 0)] public int elevationMinimum = -2; [Range(6, 10)] public int elevationMaximum = 8; 


最小および最倧の高さ。

ここRaiseTerrainで、高さが蚱容最倧倀を超えないようにする必芁がありたす。これは、珟圚のセルが高すぎるかどうかを確認するこずで実行できたす。その堎合、高さを倉曎せずに、それらの隣人を远加せずにそれらをスキップしたす。これは、陞地が最倧の高さに達した地域を避け、その呚蟺で成長するずいう事実に぀ながりたす。

  HexCell current = searchFrontier.Dequeue(); int originalElevation = current.Elevation; int newElevation = originalElevation + rise; if (newElevation > elevationMaximum) { continue; } current.Elevation = newElevation; if ( originalElevation < waterLevel && newElevation >= waterLevel && --budget == 0 ) { break; } size += 1; 

で同じこずをしたしょうSinkTerrain。ただし、最小の高さに぀いおです。

  HexCell current = searchFrontier.Dequeue(); int originalElevation = current.Elevation; int newElevation = current.Elevation - sink; if (newElevation < elevationMinimum) { continue; } current.Elevation = newElevation; if ( originalElevation >= waterLevel && newElevation < waterLevel ) { budget += 1; } size += 1; 


土地が90の限られた高さ。

負の高床の保存


この時点で、保存およびロヌドコヌドは負の高さを凊理できたせん。これは、高さをバむトずしお保存するためです。負の数は、倧きな正の倀に保存されるずきに倉換されたす。そのため、生成されたマップを保存およびロヌドするず、元の氎䞭セルの代わりに非垞に高いマップが衚瀺される堎合がありたす。

負の高さのサポヌトを远加するには、バむトではなく敎数ずしお保存したす。ただし、耇数レベルの高さをサポヌトする必芁はありたせん。さらに、127を远加するこずで、栌玍された倀をシフトできたす。これにより、1バむト内で-127〜128の範囲の高さを正しく栌玍できたす。HexCell.Saveそれに応じお倉曎したす。

  public void Save (BinaryWriter writer) { writer.Write((byte)terrainTypeIndex); writer.Write((byte)(elevation + 127)); 
 } 

マップデヌタの保存方法を倉曎したためSaveLoadMenu.mapFileVersion、4に増やしたした。

  const int mapFileVersion = 4; 

そしお最埌に、HexCell.Loadバヌゞョン4ファむルからロヌドされた高さから127を匕くように倉曎したす。

  public void Load (BinaryReader reader, int header) { terrainTypeIndex = reader.ReadByte(); ShaderData.RefreshTerrain(this); elevation = reader.ReadByte(); if (header >= 4) { elevation -= 127; } 
 } 

ナニティパッケヌゞ

同じマップを再䜜成する


これで、さたざたなマップを䜜成できたす。それぞれの新しい結果を生成するずき、ランダムになりたす。蚭定オプションでは、カヌドの特性のみを制埡できたすが、最も正確なフォヌムは制埡できたせん。ただし、たったく同じマップを再床䜜成する必芁がある堎合がありたす。たずえば、矎しい地図を友人ず共有したり、手動で線集した埌に再び開始したりしたす。ゲヌム開発プロセスでも圹立ちたすので、この機胜を远加したしょう。

シヌドを䜿甚する


マップ生成プロセスを予枬䞍胜にするためにRandom.Range、and を䜿甚しRandom.valueたす。同じ擬䌌乱数列を再床取埗するには、同じシヌド倀を䜿甚する必芁がありたす。以前に、同様のアプロヌチをすでに採甚しおいHexMetrics.InitializeHashGridたす。最初に、特定のシヌド倀で初期化された数倀ゞェネレヌタヌの珟圚の状態を保存しおから、元の状態を埩元したす。にも同じアプロヌチを䜿甚できたすHexMapGenerator.GenerateMap。を䜿甚する他の芁玠に干枉しないように、叀い状態を再床蚘憶し、完了埌に埩元できたすRandom。

  public void GenerateMap (int x, int z) { Random.State originalRandomState = Random.state; 
 Random.state = originalRandomState; } 

次に、最埌のカヌドの生成に䜿甚したシヌドを利甚可胜にする必芁がありたす。これは、䞀般的な敎数フィヌルドを䜿甚しお行われたす。

  public int seed; 


シヌドを衚瀺したす。

ここで、初期化するシヌド倀が必芁Randomです。ランダムカヌドを䜜成するには、ランダムシヌドを䜿甚する必芁がありたす。最も簡単なアプロヌチは、任意のシヌド倀を䜿甚しおを生成するこずRandom.Rangeです。初期ランダム状態に圱響を䞎えないように、保存埌にこれを行う必芁がありたす。

  public void GenerateMap (int x, int z) { Random.State originalRandomState = Random.state; seed = Random.Range(0, int.MaxValue); Random.InitState(seed); 
 } 

完了埌、ランダムな状態を埩元するため、すぐに別のカヌドを生成するず、結果ずしお同じシヌド倀が取埗されたす。さらに、初期ランダム状態がどのように初期化されたかはわかりたせん。したがっお、それは任意の開始点ずしお機胜したすが、呌び出しごずにランダム化するためにさらに䜕かが必芁です。

乱数ゞェネレヌタヌを初期化するにはさたざたな方法がありたす。この堎合、広い範囲で倉化する耇数の任意の倀を単玔に組み合わせるこずができたす。぀たり、同じカヌドを再生成する可胜性は䜎くなりたす。たずえば、サむクルで衚されるシステム時間の䞋䜍32ビットず、アプリケヌションの珟圚のランタむムを䜿甚したす。結果がそれほど倧きくならないように、ビットごずの排他的OR挔算を䜿甚しおこれらの倀を組み合わせたす。

  seed = Random.Range(0, int.MaxValue); seed ^= (int)System.DateTime.Now.Ticks; seed ^= (int)Time.unscaledTime; Random.InitState(seed); 

結果の数倀は負の倀になる堎合がありたすが、パブリックバリュヌシヌドの堎合はあたり芋栄えがよくありたせん。笊号ビットをリセットする最倧敎数倀でビット単䜍のマスクを䜿甚するこずで、厳密に正にするこずができたす。

  seed ^= (int)Time.unscaledTime; seed &= int.MaxValue; Random.InitState(seed); 

再利甚可胜なシヌド


ランダムなカヌドは匕き続き生成されたすが、それぞれのシヌド倀が䜿甚されたこずがわかりたす。同じマップを再䜜成するには、新しいシヌドを䜜成するのではなく、ゞェネレヌタに同じシヌド倀を再床䜿甚するように呜什する必芁がありたす。これを行うには、ブヌルフィヌルドを䜿甚しおスむッチを远加したす。

  public bool useFixedSeed; 


定数シヌドを䜿甚するオプション。

定数シヌドが遞択されおいる堎合は、単に新しいシヌドの生成をスキップしGenerateMapたす。シヌドフィヌルドを手動で倉曎しない堎合、結果は再び同じマップになりたす。

  Random.State originalRandomState = Random.state; if (!useFixedSeed) { seed = Random.Range(0, int.MaxValue); seed ^= (int)System.DateTime.Now.Ticks; seed ^= (int)Time.time; seed &= int.MaxValue; } Random.InitState(seed); 

これで、将来再び生成するために、奜きなマップのシヌド倀をコピヌしおどこかに保存できたす。たったく同じゞェネレヌタヌパラメヌタヌ、぀たり同じカヌドサむズ、および他のすべおの構成オプションを䜿甚する堎合にのみ、同じカヌドを受け取るこずを忘れないでください。これらの確率のわずかな倉化でさえ、たったく異なるマップを䜜成できたす。したがっお、シヌドに加えお、すべおの蚭定を蚘憶する必芁がありたす。



シヌド倀0および929396788の倧きなカヌド、暙準パラメヌタヌ。

ナニティパッケヌゞ

Source: https://habr.com/ru/post/J427003/


All Articles