Unityの六角圢マップ保存ず読み蟌み、テクスチャ、距離

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

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

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

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

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

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

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

パヌト12保存ず読み蟌み



かなり興味深いマップを䜜成する方法はすでに知っおいたす。 今、あなたはそれらを保存する方法を孊ぶ必芁がありたす。


test.mapファむルからロヌドされたす。

地圢タむプ


地図を保存するずき、アプリケヌション䞭に远跡するすべおのデヌタを保存する必芁はありたせん。 たずえば、セルの高さレベルを芚えるだけです。 その垂盎䜍眮自䜓はこのデヌタから取埗されるため、保存する必芁はありたせん。 実際、これらの蚈算された指暙を保存しない方が良いです。 したがっお、埌で高さオフセットを倉曎するこずにした堎合でも、マップデヌタは正しいたたです。 デヌタはプレれンテヌションずは別のものです。

同様に、セルの正確な色を保存する必芁はありたせん。 セルが緑色であるこずを曞くこずができたす。 しかし、芖芚スタむルが倉わるず、正確な緑の色合いが倉わる可胜性がありたす。 これを行うために、色そのものではなく、色のむンデックスを保存できたす。 実際、実行時にセルに実際の色の代わりにこのむンデックスを保存すれば十分かもしれたせん。 これにより、埌でレリヌフのより耇雑な芖芚化に進むこずができたす。

色の配列を移動する


セルにカラヌデヌタがなくなった堎合は、別の堎所に保存する必芁がありたす。 HexMetricsに保存するのが最も䟿利HexMetrics 。 色の配列を远加しおみたしょう。

  public static Color[] colors; 

ノむズなどの他のすべおのグロヌバルデヌタず同様に、これらの色をHexGrid初期化できたす。

  public Color[] colors; 
 void Awake () { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); HexMetrics.colors = colors; 
 } 
 void OnEnable () { if (!HexMetrics.noiseSource) { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); HexMetrics.colors = colors; } } 

たた、セルに盎接色を割り圓おないので、デフォルトの色を取り陀きたす。

 // public Color defaultColor = Color.white; 
 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.Color = defaultColor; 
 } 

六角マップ゚ディタヌの䞀般的な配列に䞀臎するように新しい色を蚭定したす。


グリッドに远加された色。

セルリファクタリング


HexCellからカラヌフィヌルドを削陀したす。 代わりに、むンデックスを保存したす。 カラヌむンデックスの代わりに、より䞀般的なレリヌフタむプむンデックスを䜿甚したす。

 // Color color; int terrainTypeIndex; 

colorプロパティは、察応する色を取埗するためにのみこのむンデックスを䜿甚できたす。 珟圚は盎接蚭定されおいないため、この郚分を削陀したす。 この堎合、コンパむル゚ラヌが発生したすが、すぐに修正したす。

  public Color Color { get { return HexMetrics.colors[terrainTypeIndex]; } // set { // 
 // } } 

新しいプロパティを远加しお、新しい暙高タむプのむンデックスを取埗および蚭定したす。

  public int TerrainTypeIndex { get { return terrainTypeIndex; } set { if (terrainTypeIndex != value) { terrainTypeIndex = value; Refresh(); } } } 

゚ディタヌのリファクタリング


HexMapEditor内で、色に関するすべおのコヌドHexMapEditor削陀したす。 これにより、コンパむル゚ラヌが修正されたす。

 // public Color[] colors; 
 // Color activeColor; 
 // bool applyColor; 
 // public void SelectColor (int index) { // applyColor = index >= 0; // if (applyColor) { // activeColor = colors[index]; // } // } 
 // void Awake () { // SelectColor(0); // } 
 void EditCell (HexCell cell) { if (cell) { // if (applyColor) { // cell.Color = activeColor; // } 
 } } 

次に、アクティブな暙高タむプむンデックスを制埡するフィヌルドずメ゜ッドを远加したす。

  int activeTerrainTypeIndex; 
 public void SetTerrainTypeIndex (int index) { activeTerrainTypeIndex = index; } 

このメ゜ッドは、珟圚欠萜しおいるSelectColorメ゜ッドの代わりずしお䜿甚したす。 UIのカラヌりィゞェットをSetTerrainTypeIndexで接続し、他のすべおを倉曎しないたたにしたす。 これは、負のむンデックスがただ䜿甚䞭であり、色が倉わらないこずを意味したす。

線集セルを倉曎しお、線集䞭のセルに暙高タむプのむンデックスを割り圓おたす。

  void EditCell (HexCell cell) { if (cell) { if (activeTerrainTypeIndex >= 0) { cell.TerrainTypeIndex = activeTerrainTypeIndex; } 
 } } 

セルから色デヌタを削陀したしたが、マップは以前ず同じように機胜するはずです。 唯䞀の違いは、デフォルトの色が配列の最初になったこずです。 私の堎合は黄色です。


黄色が新しいデフォルトの色です。

ナニティパッケヌゞ

デヌタをファむルに保存する


マップの保存ず読み蟌みを制埡するには、 HexMapEditorを䜿甚しHexMapEditor 。 これを行う2぀のメ゜ッドを䜜成し、今は空のたたにしたす。

  public void Save () { } public void Load () { } 

UIに2぀のボタンを远加したす GameObject / UI / Button 。 それらをボタンに接続し、適切なラベルを付けたす。 それらを右パネルの䞋郚に配眮したした。


[保存]ボタンず[読み蟌み]ボタン。

ファむルの堎所


カヌドを保存するには、どこかに保存する必芁がありたす。 ほずんどのゲヌムで行われおいるように、ファむルにデヌタを保存したす。 しかし、このファむルをファむルシステムのどこに眮くのでしょうか 答えは、ゲヌムが実行されおいるオペレヌティングシステムによっお異なりたす。 各OSには、アプリケヌションに関連するファむルを保存するための独自の暙準がありたす。

これらの暙準を知る必芁はありたせん。 Unityは、 Application.persistentDataPath取埗できる正しいパスを知っおいApplication.persistentDataPath 。 Saveメ゜ッドで、コン゜ヌルに衚瀺し、Playモヌドでボタンを抌すず、どのようになるかを確認できたす。

  public void Save () { Debug.Log(Application.persistentDataPath); } 

デスクトップシステムでは、パスには䌚瀟ず補品の名前が含たれたす。 ゚ディタヌずアセンブリの䞡方がこのパスを䜿甚したす。 名前は、 線集/プロゞェクト蚭定/プレヌダヌで蚭定できたす。


䌚瀟ず補品の名前。

Macでラむブラリフォルダヌが芋぀からないのはなぜですか
倚くの堎合、 ラむブラリフォルダヌは非衚瀺です。 衚瀺方法はOS Xのバヌゞョンによっお異なりたす。叀いバヌゞョンがない堎合は、Finderでホヌムフォルダヌを遞択し、[ 衚瀺オプションを衚瀺]に進みたす 。 ラむブラリフォルダのチェックボックスがありたす。

WebGLはどうですか
WebGLゲヌムはナヌザヌのファむルシステムにアクセスできたせん。 代わりに、すべおのファむル操䜜がメモリ内にあるファむルシステムにリダむレクトされたす。 圌女は私たちに透明です。 ただし、デヌタを保存するには、Webペヌゞを手動で泚文しお、ブラりザストレヌゞにデヌタをダンプする必芁がありたす。

ファむル䜜成


ファむルを䜜成するには、 System.IO名前空間のクラスを䜿甚する必芁がありたす。 したがっお、 HexMapEditorクラスの䞊にusingステヌトメントを远加したす。

 using UnityEngine; using UnityEngine.EventSystems; using System.IO; public class HexMapEditor : MonoBehaviour { 
 } 

たず、ファむルぞのフルパスを䜜成する必芁がありたす。 ファむル名ずしおtest.mapを䜿甚したす。 保存されたデヌタのパスに远加する必芁がありたす。 スラッシュたたはバックスラッシュスラッシュたたはバックスラッシュを挿入する必芁があるかどうかは、プラットフォヌムによっお異なりたす。 Path.Combineメ゜ッドがPath.CombineをPath.Combine 。

  public void Save () { string path = Path.Combine(Application.persistentDataPath, "test.map"); } 

次に、この堎所にあるファむルにアクセスする必芁がありたす。 これはFile.Openメ゜ッドを䜿甚しおFile.Openたす。 このファむルにデヌタを曞き蟌むため、䜜成モヌドを䜿甚する必芁がありたす。 この堎合、指定したパスに新しいファむルが䜜成されるか、既存のファむルが眮き換えられたす。

  string path = Path.Combine(Application.persistentDataPath, "test.map"); File.Open(path, FileMode.Create); 

このメ゜ッドを呌び出した結果は、このファむルに関連付けられたオヌプンデヌタストリヌムになりたす。 これを䜿甚しお、デヌタをファむルに曞き蟌むこずができたす。 そしお、ストリヌムが䞍芁になったずきに閉じるこずを忘れおはなりたせん。

  string path = Path.Combine(Application.persistentDataPath, "test.map"); Stream fileStream = File.Open(path, FileMode.Create); fileStream.Close(); 

この段階で[ 保存 ]ボタンをクリックするず、 保存されたデヌタぞのパスずしお指定されたフォルダヌにtest.mapファむルが䜜成されたす。 このファむルを調べるず、空になり、サむズが0バむトになりたす。これたでのずころ䜕も曞き蟌んでいたせん。

ファむルに曞き蟌む


ファむルにデヌタを曞き蟌むには、デヌタをストリヌミングする方法が必芁です。 これを行う最も簡単な方法は、 BinaryWriterです。 これらのオブゞェクトを䜿甚するず、プリミティブデヌタを任意のストリヌムに曞き蟌むこずができたす。

新しいBinaryWriterオブゞェクトを䜜成するず、ファむルストリヌムが匕数になりたす。 ラむタヌを閉じるず、䜿甚するスレッドが閉じられたす。 したがっお、ストリヌムぞの盎接リンクを保存する必芁がなくなりたした。

  string path = Path.Combine(Application.persistentDataPath, "test.map"); BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)); writer.Close(); 

デヌタをストリヌムに転送するには、 BinaryWriter.Writeメ゜ッドを䜿甚できたす。 敎数や浮動小数点数など、すべおのプリミティブ型に察しおWriteメ゜ッドのバリアントがありたす。 行を蚘録するこずもできたす。 敎数123を曞きたしょう。

  BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)); writer.Write(123); writer.Close(); 

[ 保存 ]ボタンをクリックしお、 test.mapをもう䞀床調べたす 。 敎数サむズが4バむトであるため、サむズは4バむトになりたした。

ファむルマネヌゞャヌが、ファむルがより倚くのスペヌスを占有するこずを瀺すのはなぜですか
ファむルシステムはスペヌスをバむトブロックに分割するためです。 個々のバむトを远跡したせん。 test.mapはこれたで4バむトしか䜿甚しないため、1ブロックのストレヌゞスペヌスが必芁です。

人間が読めるテキストではなく、バむナリデヌタを栌玍するこずに泚意しおください。 したがっお、テキスト゚ディタヌでファむルを開くず、䞍明瞭な文字のセットが衚瀺されたす。 おそらくシンボル{に続いお、䜕もないプレヌスホルダヌがいく぀か衚瀺されたす。

16進゚ディタヌでファむルを開くこずができたす。 この堎合、 7b 00 00 00が衚瀺されたす。 これらは16進衚蚘でマッピングされた4バむトの敎数です。 通垞の10進数では、これは123 0 0 0です。 バむナリでは、最初のバむトは01111011のようになりたす。

{のASCIIコヌドは123であるため、この文字はテキスト゚ディタヌで衚瀺できたす。 ASCII 0は、衚瀺される文字ず䞀臎しないヌル文字です。

残りの3バむトは、256未満の数字を曞き蟌んだため、れロに等しくなりたす。256を曞き蟌んだ堎合、16進゚ディタヌに00 01 00 00が衚瀺されたす。

123は00 00 00 7bずしお保存されるべきではありたせんか
BinaryWriterは、リトル゚ンディアン圢匏を䜿甚しお数倀を保存したす。 ぀たり、最䞋䜍バむトが最初に曞き蟌たれたす。 この圢匏は、Microsoftが.Netフレヌムワヌクの開発に䜿甚したものです。 Intel CPUがリトル゚ンディアン圢匏を䜿甚しおいるため、おそらく遞択されたした。

それに代わるものはビッグ゚ンディアンで、最䞊䜍バむトが最初に保存されたす。 これは、数字の通垞の数字の順序に察応したす。 123はビッグ゚ンディアンレコヌドを意味するため、123です。 リトル゚ンディアンの堎合、123は312を意味したす。

リ゜ヌスを解攟する


䜜家を閉じるこずが重芁です。 ファむルシステムが開いおいる間、ファむルシステムはファむルをロックし、他のプロセスがファむルに曞き蟌みできないようにしたす。 それを閉じるのを忘れるず、自分自身もブロックしたす。 保存ボタンを2回抌すず、2回目はストリヌムを開くこずができたせん。

ラむタヌを手動で閉じる代わりに、このためにusingブロックを䜜成できたす。 ラむタヌが有効な範囲を定矩したす。 実行可胜コヌドがこの範囲を超えるず、ラむタヌが削陀され、スレッドが閉じられたす。

  using ( BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)) ) { writer.Write(123); } // writer.Close(); 

これは、ラむタヌクラスずファむルストリヌムクラスがIDisposableむンタヌフェむスを実装しおいるため機胜したす。 これらのオブゞェクトにはDisposeメ゜ッドがありたす。このメ゜ッドは、 usingの範囲倖になるず間接的に呌び出されたす。

䜿甚の倧きな利点は、プログラムがどのようにスコヌプ倖で実行されおも動䜜するこずです。 早期の返品、䟋倖、゚ラヌは圌を悩たせたせん。 さらに、圌は非垞に簡朔です。

デヌタ怜玢


以前に曞き蟌たれたデヌタを読み取るには、 Loadメ゜ッドにコヌドを挿入する必芁がありたす。 保存の堎合ず同様に、パスを䜜成しおファむルストリヌムを開く必芁がありたす。 違いは、ファむルを曞き蟌み甚ではなく読み取り甚に開くこずです。 そしお、ラむタヌの代わりにBinaryReaderが必芁です。

  public void Load () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using ( BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open)) ) { } } 

この堎合、 File.OpenReadメ゜ッドを䜿甚しお、読み取り甚にファむルを開くこずができたす。

  using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { } 

曞き蟌み時にFile.OpenWriteを䜿甚できないのはなぜですか
このメ゜ッドは、既存のファむルを眮き換えるのではなく、既存のファむルにデヌタを远加するストリヌムを䜜成したす。

読み取り時には、受信したデヌタのタむプを明瀺的に瀺す必芁がありたす。 ストリヌムから敎数を読み取るには、 BinaryReader.ReadInt32を䜿甚する必芁がBinaryReader.ReadInt32たす。 このメ゜ッドは、32ビット敎数、぀たり4バむトを読み取りたす。

  using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { Debug.Log(reader.ReadInt32()); } 

123を受信するず、 1バむトを読み取るだけで十分であるこずに泚意しおください。 ただし、同時に、この敎数に属する3バむトがストリヌムに残りたす。 たた、これは、0〜255の範囲倖の数倀では機胜したせん。 したがっお、そうしないでください。

ナニティパッケヌゞ

地図デヌタの曞き蟌みず読み取り


デヌタを保存する際の重芁な質問は、人間が読める圢匏を䜿甚するかどうかです。 通垞、人間が読める圢匏では、JSON、XML、および䜕らかの構造のプレヌンASCIIを䜿甚したす。 このようなファむルは、テキスト゚ディタヌで開いたり、解釈したり、線集したりできたす。 さらに、異なるアプリケヌション間のデヌタ亀換を簡玠化したす。

ただし、このような圢匏には独自の芁件がありたす。 ファむルは、バむナリデヌタを䜿甚するよりも倚くのスペヌス堎合によっおはそれ以䞊を占有したす。 たた、ランタむムずメモリフットプリントの䞡方の点で、デヌタの゚ンコヌドずデコヌドのコストを倧幅に増加させる可胜性がありたす。

察照的に、バむナリデヌタはコンパクトで高速です。 これは、倧量のデヌタを蚘録する堎合に重芁です。 たずえば、ゲヌムの各タヌンで倧きなマップを自動保存する堎合。 だから
バむナリ圢匏を䜿甚したす。 これを凊理できれば、より詳现な圢匏で䜜業できたす。

自動シリアル化はどうですか
Unityデヌタをシリアル化するプロセスの盎埌に、シリアル化されたクラスをストリヌムに盎接曞き蟌むこずができたす。 個々のフィヌルドの蚘録の詳现は非衚瀺になりたす。 ただし、セルを盎接シリアル化するこずはできたせん。 これらは保存する必芁のないデヌタを含むMonoBehaviourクラスです。 したがっお、オブゞェクトの別の階局を䜿甚する必芁がありたすが、これは自動シリアル化の単玔さを砎壊したす。 さらに、将来のコヌド倉曎をサポヌトするこずはより困難になりたす。 したがっお、手動のシリアル化で完党な制埡を維持したす。 さらに、䜕が起こっおいるのかを本圓に理解させおくれたす。

マップをシリアル化するには、各セルのデヌタを保存する必芁がありたす。 単䞀のセルを保存およびロヌドするには、 SaveおよびLoadメ゜ッドをHexCell远加しSave 。 ラむタヌたたはリヌダヌが必芁なため、パラメヌタヌずしお远加したす。

 using UnityEngine; using System.IO; public class HexCell : MonoBehaviour { 
 public void Save (BinaryWriter writer) { } public void Load (BinaryReader reader) { } } 

SaveおよびLoadメ゜ッドをHexGrid远加しSave 。 これらのメ゜ッドは、 Loadメ゜ッドずSaveメ゜ッドを呌び出すこずにより、すべおのセルを単玔にバむパスしSave 。

 using UnityEngine; using UnityEngine.UI; using System.IO; public class HexGrid : MonoBehaviour { 
 public void Save (BinaryWriter writer) { for (int i = 0; i < cells.Length; i++) { cells[i].Save(writer); } } public void Load (BinaryReader reader) { for (int i = 0; i < cells.Length; i++) { cells[i].Load(reader); } } } 

マップをダりンロヌドする堎合、セルデヌタが倉曎された埌にマップを曎新する必芁がありたす。 これを行うには、すべおのフラグメントを曎新するだけです。

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

最埌に、 HexMapEditorテストコヌドを、グリッドのSaveメ゜ッドずLoadメ゜ッドの呌び出しに眮き換え、ラむタヌたたはリヌダヌを枡したす。

  public void Save () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using ( BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)) ) { hexGrid.Save(writer); } } public void Load () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { hexGrid.Load(reader); } } 

地圢タむプの保存


珟圚の段階では、再保存は空のファむルを䜜成し、ダりンロヌドは䜕もしたせん。 HexCell゚レベヌションタむプむンデックスのみを蚘述およびロヌドするこずから始めたしょう。

倀をterrainTypeIndexフィヌルドに盎接割り圓おたす。 プロパティは䜿甚したせん。 すべおのフラグメントを明瀺的に曎新するため、 Refreshプロパティの呌び出しは必芁ありたせん。 さらに、正しいマップのみを保存するため、ダりンロヌドしたすべおのマップも正しいず想定したす。 したがっお、たずえば、川や道路が蚱可されおいるかどうかはチェックしたせん。

  public void Save (BinaryWriter writer) { writer.Write(terrainTypeIndex); } public void Load (BinaryReader reader) { terrainTypeIndex = reader.ReadInt32(); } 

このファむルに保存するず、すべおのセルの救枈タむプのむンデックスが次々ず曞き蟌たれたす。 むンデックスは敎数なので、サむズは4バむトです。 私のカヌドには300個のセルが含たれおいたす。぀たり、ファむルサむズは1200バむトになりたす。

ロヌドは、むンデックスが曞き蟌たれるのず同じ順序でむンデックスを読み取りたす。 保存埌にセルの色を倉曎した堎合、マップをロヌドするず色が保存時の状態に戻りたす。 もう䜕も保存しないため、残りのセルデヌタは同じたたです。 ぀たり、ロヌドによっお地圢のタむプは倉わりたすが、その高さ、氎䜍、地圢の特城などは倉わりたせん。

すべおの敎数を保存する


レリヌフタむプのむンデックスを保存するだけでは十分ではありたせん。 他のすべおのデヌタを保存する必芁がありたす。 すべおの敎数フィヌルドから始めたしょう。 これは、起䌏のタむプ、セルの高さ、氎䜍、郜垂レベル、蟲堎レベル、怍生レベル、および特別なオブゞェクトのむンデックスのむンデックスです。 それらは、蚘録されたのず同じ順序で読む必芁がありたす。

  public void Save (BinaryWriter writer) { writer.Write(terrainTypeIndex); writer.Write(elevation); writer.Write(waterLevel); writer.Write(urbanLevel); writer.Write(farmLevel); writer.Write(plantLevel); writer.Write(specialIndex); } public void Load (BinaryReader reader) { terrainTypeIndex = reader.ReadInt32(); elevation = reader.ReadInt32(); waterLevel = reader.ReadInt32(); urbanLevel = reader.ReadInt32(); farmLevel = reader.ReadInt32(); plantLevel = reader.ReadInt32(); specialIndex = reader.ReadInt32(); } 

これらの操䜜の間に倉曎を加えお、マップを保存しおロヌドしおみおください。 セルの高さを陀いお、保存されたデヌタに含たれるすべおのものが、可胜な限り埩元されたした。 これは、高さレベルを倉曎するずきに、セルの垂盎䜍眮を曎新する必芁があるために発生したした。 これは、フィヌルドではなく、ロヌドされた高さの倀にプロパティに割り圓おるこずで実行できたす。 ただし、このプロパティは必芁のない远加の䜜業を行いたす。 したがっお、セルの䜍眮を曎新するコヌドをElevationセッタヌから抜出し、別のRefreshPositionメ゜ッドに貌り付けたしょう。 ここでの唯䞀の倉曎は、 value elevationフィヌルドぞの参照に眮き換えるこずvalue 。

  void RefreshPosition () { Vector3 position = transform.localPosition; position.y = elevation * HexMetrics.elevationStep; position.y += (HexMetrics.SampleNoise(position).y * 2f - 1f) * HexMetrics.elevationPerturbStrength; transform.localPosition = position; Vector3 uiPosition = uiRect.localPosition; uiPosition.z = -position.y; uiRect.localPosition = uiPosition; } 

これで、プロパティを蚭定するずき、および高さデヌタを読み蟌んだ埌にメ゜ッドを呌び出すこずができたす。

  public int Elevation { 
 set { if (elevation == value) { return; } elevation = value; RefreshPosition(); ValidateRivers(); 
 } } 
 public void Load (BinaryReader reader) { terrainTypeIndex = reader.ReadInt32(); elevation = reader.ReadInt32(); RefreshPosition(); 
 } 

この倉曎埌、セルはロヌド時に芋かけの高さを正しく倉曎したす。

すべおのデヌタを保存する


セル内の壁ず流入/流出河川の存圚は、ブヌルフィヌルドに栌玍されたす。 単玔に敎数ずしお曞くこずができたす。 さらに、道路デヌタは、ルヌプで曞き蟌むこずができる6぀のブヌル倀の配列です。

  public void Save (BinaryWriter writer) { writer.Write(terrainTypeIndex); writer.Write(elevation); writer.Write(waterLevel); writer.Write(urbanLevel); writer.Write(farmLevel); writer.Write(plantLevel); writer.Write(specialIndex); writer.Write(walled); writer.Write(hasIncomingRiver); writer.Write(hasOutgoingRiver); for (int i = 0; i < roads.Length; i++) { writer.Write(roads[i]); } } 

流入河川ず流出河川の方向は、 HexDirectionフィヌルドに保存されたす。 HexDirection型は、耇数の敎数倀ずしお内郚的に保存される列挙です。 したがっお、明瀺的な倉換を䜿甚しお敎数ずしおシリアル化するこずもできたす。

  writer.Write(hasIncomingRiver); writer.Write((int)incomingRiver); writer.Write(hasOutgoingRiver); writer.Write((int)outgoingRiver); 

ブヌル倀はBinaryReader.ReadBooleanメ゜ッドを䜿甚しお読み取られたす。 川の方向は敎数です。これをHexDirection倉換し盎す必芁がありたす。

  public void Load (BinaryReader reader) { terrainTypeIndex = reader.ReadInt32(); elevation = reader.ReadInt32(); RefreshPosition(); waterLevel = reader.ReadInt32(); urbanLevel = reader.ReadInt32(); farmLevel = reader.ReadInt32(); plantLevel = reader.ReadInt32(); specialIndex = reader.ReadInt32(); walled = reader.ReadBoolean(); hasIncomingRiver = reader.ReadBoolean(); incomingRiver = (HexDirection)reader.ReadInt32(); hasOutgoingRiver = reader.ReadBoolean(); outgoingRiver = (HexDirection)reader.ReadInt32(); for (int i = 0; i < roads.Length; i++) { roads[i] = reader.ReadBoolean(); } } 

次に、マップの完党な保存ず埩元に必芁なすべおのセルデヌタを保存したす。これには、セルごずに9぀の敎数ず9぀のブヌル倀が必芁です。各ブヌル倀は1バむトを䜿甚するため、セルごずに合蚈45バむトを䜿甚したす。぀たり、300セルのカヌドには合蚈13,500バむトが必芁です。

ナニティパッケヌゞ

ファむルサむズを小さくする


300セルの堎合、13,500バむトはそれほど倚くないように芋えたすが、おそらくもっず少ない量で実行できたす。最終的に、デヌタのシリアル化方法を完党に制埡できたす。それらを保存するよりコンパクトな方法があるかどうかを芋おみたしょう。

数倀間隔の削枛


異なるセルレベルずむンデックスは敎数ずしお保存されたす。ただし、䜿甚する倀の範囲はごくわずかです。それぞれが0〜255の範囲に確実に留たりたす。これは、各敎数の最初のバむトのみが䜿甚されるこずを意味したす。他の3぀は垞にれロになりたす。これらの空のバむトを保存するこずは意味がありたせん。ストリヌムに曞き蟌む前に敎数をバむトに曞き蟌むこずにより、それらを砎棄できたす。

  writer.Write((byte)terrainTypeIndex); writer.Write((byte)elevation); writer.Write((byte)waterLevel); writer.Write((byte)urbanLevel); writer.Write((byte)farmLevel); writer.Write((byte)plantLevel); writer.Write((byte)specialIndex); writer.Write(walled); writer.Write(hasIncomingRiver); writer.Write((byte)incomingRiver); writer.Write(hasOutgoingRiver); writer.Write((byte)outgoingRiver); 

さお、これらの数倀を返すには、を䜿甚する必芁がありたすBinaryReader.ReadByte。バむトから敎数ぞの倉換は暗黙的に行われるため、明瀺的な倉換を远加する必芁はありたせん。

  terrainTypeIndex = reader.ReadByte(); elevation = reader.ReadByte(); RefreshPosition(); waterLevel = reader.ReadByte(); urbanLevel = reader.ReadByte(); farmLevel = reader.ReadByte(); plantLevel = reader.ReadByte(); specialIndex = reader.ReadByte(); walled = reader.ReadBoolean(); hasIncomingRiver = reader.ReadBoolean(); incomingRiver = (HexDirection)reader.ReadByte(); hasOutgoingRiver = reader.ReadBoolean(); outgoingRiver = (HexDirection)reader.ReadByte(); 

したがっお、敎数ごずに3バむトを取り陀き、セルごずに27バむトを節玄したす。セルあたり18バむト、300セルあたりわずか5,400バむトを䜿甚したす。

この段階で叀いカヌドのデヌタが無意味になるこずは泚目に倀したす。叀いセヌブをロヌドするず、デヌタが混圚しおいるこずがわかり、セルが混乱したす。これは、珟圚読み取り䞭のデヌタが少ないためです。以前よりも倚くのデヌタを読み取るず、ファむルの末尟を超えお読み取ろうずするず゚ラヌが発生したす。

叀いデヌタを凊理できないこずは、私たちに適しおいたす。なぜなら、私たちはフォヌマットを決定する過皋にあるからです。ただし、保存圢匏を決定するずきは、将来のコヌドで垞に保存圢匏を読み取れるようにする必芁がありたす。圢匏を倉曎しおも、理想的には叀い圢匏を読み取るこずができるはずです。

River Byte Union


この段階では、4バむトを䜿甚しお川のデヌタを保存したす方向ごずに2バむト。方向ごずに、川の存圚ず流れる

方向を保存したすが、川の方向を保存する必芁がないのは明らかです。これは、川のないセルに必芁なバむト数が2バむト少ないこずを意味したす。実際、その存圚に関係なく、川の方向に1バむトあれば十分です。

可胜な方向は6぀あり、0〜5の間隔で数倀ずしお保存されたす。これには3ビットで十分です。これは、バむナリ圢匏では0〜5の数倀が000、001、010、011、100、101、110のように芋えるためです。぀たり、1バむトでさらに5ビットが䜿甚されたせん。それらの1぀を䜿甚しお、川が存圚するかどうかを瀺すこずができたす。たずえば、数倀128に察応する8番目のビットを䜿甚でき

たす。これを行うには、方向をバむトに倉換する前に128を远加したす。぀たり、北西に流れる川がある堎合、133を曞き蟌みたす。川がない堎合は、れロバむトを曞き蟌むだけです。

同時に、さらに4ビットが未䜿甚のたたですが、これは正垞です。川の䞡方向を1バむトにたずめるこずができたすが、これは既に混乱しすぎたす。

 // writer.Write(hasIncomingRiver); // writer.Write((byte)incomingRiver); if (hasIncomingRiver) { writer.Write((byte)(incomingRiver + 128)); } else { writer.Write((byte)0); } // writer.Write(hasOutgoingRiver); // writer.Write((byte)outgoingRiver); if (hasOutgoingRiver) { writer.Write((byte)(outgoingRiver + 128)); } else { writer.Write((byte)0); } 

riverデヌタをデコヌドするには、最初にバむトを読み戻す必芁がありたす。倀が128以䞊の堎合、これは川があるこずを意味したす。方向を取埗するには、128を枛算しおからに倉換しHexDirectionたす。

 // hasIncomingRiver = reader.ReadBoolean(); // incomingRiver = (HexDirection)reader.ReadByte(); byte riverData = reader.ReadByte(); if (riverData >= 128) { hasIncomingRiver = true; incomingRiver = (HexDirection)(riverData - 128); } else { hasIncomingRiver = false; } // hasOutgoingRiver = reader.ReadBoolean(); // outgoingRiver = (HexDirection)reader.ReadByte(); riverData = reader.ReadByte(); if (riverData >= 128) { hasOutgoingRiver = true; outgoingRiver = (HexDirection)(riverData - 128); } else { hasOutgoingRiver = false; } 

その結果、セルごずに16バむトを埗たした。改善はそれほど倧きくないように芋えたすが、これはバむナリデヌタのサむズを小さくするために䜿甚されるトリックの1぀です。

道路を1バむトで保存する


同様の手法を䜿甚しお、道路デヌタを圧瞮できたす。バむトの最初の6ビットに栌玍できる6぀のブヌル倀がありたす。぀たり、道路の各方向は2の环乗の数倀で衚されたす。これらは、1、2、4、8、16、32、たたはバむナリ圢匏の1、10、100、1000、10000、100000です。

完成したバむトを䜜成するには、道路の䜿甚方向に察応するビットを蚭定する必芁がありたす。方向の正しい方向を取埗するには、挔算子を䜿甚できたす<<。次に、ビットごずのOR挔算子を䜿甚しおそれらを結合したす。たずえば、1番目、2番目、3番目、6番目の道路が䜿甚されおいる堎合、完成したバむトは100111になりたす。

  int roadFlags = 0; for (int i = 0; i < roads.Length; i++) { // writer.Write(roads[i]); if (roads[i]) { roadFlags |= 1 << i; } } writer.Write((byte)roadFlags); 

<<はどのように機胜したすか
これはビット単䜍の巊シフト挔算子です。巊偎に敎数を取り、合蚈ビットを巊偎にシフトしたす。オヌバヌフロヌは砎棄されたした。シフトステップの数は、右偎の敎数によっお決たりたす。数倀は2進数であるため、すべおのビットを1ステップ巊にシフトするず、数倀の倀が2倍になりたす。぀たり、1 << n2 nが埗られ、これが必芁です。

道路のブヌル倀を取埗するには、ビットが蚭定されおいるかどうかを確認する必芁がありたす。その堎合、ビット単䜍のAND挔算子を適切な数で䜿甚しお、他のすべおのビットをマスクしたす。結果がれロに等しくない堎合、ビットが蚭定され、道路が存圚したす。

  int roadFlags = reader.ReadByte(); for (int i = 0; i < roads.Length; i++) { roads[i] = (roadFlags & (1 << i)) != 0; } 

6バむトを1぀に圧瞮するず、セルごずに11バむトを受け取りたした。300セルの堎合、これはわずか3,300バむトです。぀たり、バむトを少し䜿甚しお、ファむルサむズを75削枛したした。

未来ぞの準備


保存圢匏の完了を宣蚀する前に、もう1぀の詳现を远加したす。マップデヌタを保存する前にHexMapEditor、敎数のれロを匷制的に曞き蟌みたす。

  public void Save () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using ( BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)) ) { writer.Write(0); hexGrid.Save(writer); } } 

これにより、デヌタの先頭に4぀の空のバむトが远加されたす。぀たり、カヌドをロヌドする前に、これらの4バむトを読み取る必芁がありたす。

  public void Load () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { reader.ReadInt32(); hexGrid.Load(reader); } } 

これらのバむトはこれたでのずころ圹に立たないが、将来的に埌方互換性を提䟛するヘッダヌずしお䜿甚されたす。これらのヌルバむトを远加しおいない堎合、最初の数バむトの内容はマップの最初のセルに䟝存しおいたした。したがっお、将来、どのバヌゞョンの保存圢匏を扱っおいるかを把握するこずはより困難になりたす。これで、最初の4バむトを確認できたす。それらが空の堎合、フォヌマット0のバヌゞョンを扱っおいたす。将来のバヌゞョンでは、そこに䜕か他のものを远加するこずが可胜になりたす。

぀たり、タむトルがれロ以倖の堎合、䞍明なバヌゞョンを凊理しおいたす。どのデヌタがあるのか​​わからないため、マップのダりンロヌドを拒吊する必芁がありたす。

  using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { int header = reader.ReadInt32(); if (header == 0) { hexGrid.Load(reader); } else { Debug.LogWarning("Unknown map format " + header); } } 


ナニティパッケヌゞ

パヌト13カヌド管理



このパヌトでは、さたざたなサむズのカヌドのサポヌトを远加し、さたざたなファむルを保存したす。

このパヌトから始めお、チュヌトリアルはUnity 5.5.0で䜜成されたす。


マップラむブラリの始たり。

新しいマップを䜜成する


ここたでで、六角圢のグリッドを䞀床だけ䜜成したした-シヌンをロヌドするずき。これで、い぀でも新しいマップを開始できるようになりたす。新しいカヌドは、単に珟圚のカヌドを眮き換えたす。

Awake HexGridでは、いく぀かのメトリックが初期化され、次にセルの数が決定され、必芁なフラグメントずセルが䜜成されたす。フラグメントずセルの新しいセットを䜜成しお、新しいマップを䜜成したす。HexGrid.Awake初期化゜ヌスコヌドず䞀般的なメ゜ッドの2぀の郚分に分けたしょうCreateMap。

  void Awake () { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); HexMetrics.colors = colors; CreateMap(); } public void CreateMap () { cellCountX = chunkCountX * HexMetrics.chunkSizeX; cellCountZ = chunkCountZ * HexMetrics.chunkSizeZ; CreateChunks(); CreateCells(); } 

UIにボタンを远加しお、新しいマップを䜜成したす。倧きくしお、保存ボタンず読み蟌みボタンの䞋に配眮したした。


新しいマップボタン。 このボタンのOn Click

むベントをobjectのメ゜ッドに接続したしょう。぀たり、Hex Map Editorを䜿甚するのではなく、Hex Gridオブゞェクトメ゜ッドを盎接呌び出したす。CreateMapHexGrid


クリックしお地図を䜜成したす。

叀いデヌタを消去する


新しいマップボタンをクリックするず、新しいフラグメントずセルのセットが䜜成されたす。ただし、叀いものは自動的に削陀されたせん。したがっお、結果ずしお、耇数のマップメッシュが盞互に重ねられたす。これを回避するには、最初に叀いオブゞェクトを削陀する必芁がありたす。これは、最初にすべおの珟圚のフラグメントを砎棄するこずで実行できたすCreateMap。

  public void CreateMap () { if (chunks != null) { for (int i = 0; i < chunks.Length; i++) { Destroy(chunks[i].gameObject); } } 
 } 

既存のオブゞェクトを再利甚できたすか
可胜ですが、新しいフラグメントずセルから始める最も簡単な方法です。これは、さたざたなサむズのカヌドのサポヌトを远加する堎合に特に圓おはたりたす。さらに、新しいマップの䜜成は比范的たれなアクションであり、最適化はここではあたり重芁ではありたせん。

このような子芁玠をルヌプで砎棄するこずは可胜ですか
もちろん。 .

フラグメントではなくセルでサむズを指定したす


フィヌルドchunkCountXずchunkCountZオブゞェクトを䜿甚しおマップのサむズを蚭定したすHexGrid。ただし、セルのマップのサむズを瀺す方がはるかに䟿利です。同時に、カヌドのサむズを倉曎せずに、フラグメントのサむズを将来倉曎するこずもできたす。したがっお、セル数ずフラグメントフィヌルド数の圹割を亀換したしょう。

 // public int chunkCountX = 4, chunkCountZ = 3; public int cellCountX = 20, cellCountZ = 15; 
 // int cellCountX, cellCountZ; int chunkCountX, chunkCountZ; 
 public void CreateMap () { 
 // cellCountX = chunkCountX * HexMetrics.chunkSizeX; // cellCountZ = chunkCountZ * HexMetrics.chunkSizeZ; chunkCountX = cellCountX / HexMetrics.chunkSizeX; chunkCountZ = cellCountZ / HexMetrics.chunkSizeZ; CreateChunks(); CreateCells(); } 

これは、HexMapCameraフラグメントサむズを䜿甚しお䜍眮を制限するため、コンパむル゚ラヌが発生したす。HexMapCamera.ClampPosition圌がただ必芁な数のセルを盎接䜿甚するように倉曎したす。

  Vector3 ClampPosition (Vector3 position) { float xMax = (grid.cellCountX - 0.5f) * (2f * HexMetrics.innerRadius); position.x = Mathf.Clamp(position.x, 0f, xMax); float zMax = (grid.cellCountZ - 1) * (1.5f * HexMetrics.outerRadius); position.z = Mathf.Clamp(position.z, 0f, zMax); return position; } 

フラグメントのサむズは5 x 5セルで、デフォルトのマップのサむズは4 x 3フラグメントです。したがっお、カヌドを同じたたにするには、20 x 15セルのサむズを䜿甚する必芁がありたす。たた、コヌド内でデフォルト倀を割り圓おたしたが、フィヌルドはすでに存圚し、デフォルトで0に蚭定されおいるため、グリッドオブゞェクトはただそれらを自動的に䜿甚したせん。


デフォルトでは、カヌドのサむズは20 x 15です。

カスタムカヌドサむズ


次のステップは、デフォルトのサむズだけでなく、あらゆるサむズのカヌドの䜜成をサポヌトするこずです。これを行うには、HexGrid.CreateMapパラメヌタヌにXずZ を远加するず、既存のセル数が眮き換えられたす。内郚でAwakeは、珟圚のセル数で呌び出したす。

  void Awake () { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); HexMetrics.colors = colors; CreateMap(cellCountX, cellCountZ); } public void CreateMap (int x, int z) { 
 cellCountX = x; cellCountZ = z; chunkCountX = cellCountX / HexMetrics.chunkSizeX; chunkCountZ = cellCountZ / HexMetrics.chunkSizeZ; CreateChunks(); CreateCells(); } 

ただし、これは、フラグメントサむズの倍数であるセルの数でのみ正しく機胜したす。そうしないず、敎数陀算で䜜成されるフラグメントが少なすぎたす。セルで郚分的に満たされたフラグメントのサポヌトを远加できたすが、フラグメントに察応しないサむズの䜿甚を犁止しおみたしょう。

挔算子%を䜿甚しお、セルの数をフラグメントの数で割った䜙りを蚈算できたす。れロに等しくない堎合、矛盟があり、新しいマップを䜜成したせん。そしお、これを行っおいる間に、れロおよび負のサむズに察する保護を远加したしょう。

  public void CreateMap (int x, int z) { if ( x <= 0 || x % HexMetrics.chunkSizeX != 0 || z <= 0 || z % HexMetrics.chunkSizeZ != 0 ) { Debug.LogError("Unsupported map size."); return; } 
 } 

新しいカヌドメニュヌ


珟圚の段階では、メ゜ッドには2぀のパラメヌタヌがあるため、[ 新しいマップ ]ボタンは機胜しなくなりたしたHexGrid.CreateMap。 Unityむベントをそのようなメ゜ッドに盎接接続するこずはできたせん。さらに、さたざたなサむズのカヌドをサポヌトするには、いく぀かのボタンが必芁です。これらすべおのボタンをメむンUIに远加する代わりに、個別のポップアップメニュヌを䜜成したしょう。

シヌンに新しいキャンバスを远加したすGameObject / UI / Canvas。既存のキャンバスず同じ蚭定を䜿甚したすが、䞊べ替え順序は1にする必芁がありたす。これにより、メむン゚ディタヌのUIの䞊郚に衚瀺されたす。新しいUIオブゞェクトのキャンバスずむベントシステムの䞡方の子を䜜成しお、シヌンの階局がきれいに保たれるようにしたした。



キャンバスメニュヌの新しいマップ。 画面党䜓を閉じるパネル

を新しいマップメニュヌに远加したす。背景を暗くしお、メニュヌが開いおいるずきにカヌ゜ルが他のすべおず察話できないようにする必芁がありたす。Source Imageをクリアしお均䞀な色を䞎え、Colorずしお0、0、0、200を蚭定したした。


背景画像の蚭定。 16進マップ゚ディタヌの

パネルず同様に、キャンバスの䞭倮にメニュヌバヌを远加したす。圌女の小、䞭、倧のカヌド甚の明確なラベルずボタンを䜜成したしょう。プレむダヌが気が倉わった堎合に備えお、キャンセルボタンも远加したす。デザむンの䜜成が完了したら、新しいマップメニュヌ党䜓を無効にしたす。



新しいマップメニュヌ。

メニュヌを管理するには、コンポヌネントNewMapMenuを䜜成し、キャンバスの新しいマップメニュヌオブゞェクトに远加したす。新しいマップを䜜成するには、Hex Gridオブゞェクトにアクセスする必芁がありたす。したがっお、共通フィヌルドを远加しお接続したす。

 using UnityEngine; public class NewMapMenu : MonoBehaviour { public HexGrid hexGrid; } 


新しいマップメニュヌのコンポヌネント。

開閉


キャンバスオブゞェクトをアクティブ化および非アクティブ化するだけで、ポップアップメニュヌを開いたり閉じたりできたす。NewMapMenuこれを行うための2぀の䞀般的なメ゜ッドを远加したしょう。

  public void Open () { gameObject.SetActive(true); } public void Close () { gameObject.SetActive(false); } 

次に、゚ディタヌの[ 新しいマップ UI ]ボタンを[新しいマップメニュヌ]Openオブゞェクトのメ゜ッドに接続したす。


を抌しおメニュヌを開きたす。

たた、[ キャンセル ]ボタンをメ゜ッドに接続したすClose。これにより、ポップアップメニュヌを開いたり閉じたりできたす。

新しいマップを䜜成する


新しいマップを䜜成するには、Hex Gridオブゞェクトのメ゜ッドを呌び出す必芁がありたすCreateMap。さらに、その埌、ポップアップメニュヌを閉じる必芁がありたす。NewMapMenu任意のサむズを考慮しお、これに察凊するメ゜ッドに远加したす。

  void CreateMap (int x, int z) { hexGrid.CreateMap(x, z); Close(); } 

このメ゜ッドは、ボタンむベントに盎接接続できないため、䞀般的ではありたせん。代わりに、CreateMap指定されたサむズで呌び出すボタンごずに1぀のメ゜ッドを䜜成したす。小さなマップの堎合、マップのデフォルトサむズに察応する20 x 15のサむズを䜿甚したした。䞭倮のカヌドに぀いおは、このサむズを2倍にしお40 x 30にし、倧きなカヌドの堎合は再び2倍にするこずにしたした。適切な方法でボタンを接続したす。

  public void CreateSmallMap () { CreateMap(20, 15); } public void CreateMediumMap () { CreateMap(40, 30); } public void CreateLargeMap () { CreateMap(80, 60); } 

カメラロック


これで、ポップアップメニュヌを䜿甚しお、3぀の異なるサむズの新しいカヌドを䜜成できたす。すべおうたくいきたすが、少し詳现に泚意する必芁がありたす。ずきに新芏マップメニュヌがアクティブになっお、私たちはUI゚ディタおよび線集现胞ず、もはや察話するこずができたす。ただし、カメラを制埡するこずはできたす。理想的には、メニュヌを開いた状態でカメラをロックする必芁がありたす。

カメラは1台しかないため、簡単で実甚的な解決策は、静的プロパティを远加するこずLockedです。広範囲に䜿甚する堎合、この゜リュヌションはあたり適しおいたせんが、シンプルなむンタヌフェむスでは十分です。これには、内郚の静的むンスタンスを远跡する必芁がありたすHexMapCamera。これは、Awakeカメラが蚭定されたずきに蚭定されたす。

  static HexMapCamera instance; 
 void Awake () { instance = this; swivel = transform.GetChild(0); stick = swivel.GetChild(0); } 

プロパティLockedは、セッタヌのみを䜿甚した単玔な静的ブヌルプロパティにするこずができたす。HexMapCameraロックされるずむンスタンスがオフになり、ロックが解陀されるずオンになりたす。

  public static bool Locked { set { instance.enabled = !value; } } 

これでNewMapMenu.Openカメラをブロックでき、NewMapMenu.Closeロックを解陀できたす。

  public void Open () { gameObject.SetActive(true); HexMapCamera.Locked = true; } public void Close () { gameObject.SetActive(false); HexMapCamera.Locked = false; } 

正しいカメラ䜍眮を維持する


カメラに別の問題がある可胜性がありたす。珟圚のマップよりも小さい新しいマップを䜜成するず、カメラがマップの境界の倖偎に衚瀺される堎合がありたす。プレヌダヌがカメラを動かそうずするたで、圌女はそこに残りたす。そしお、新しいマップの制限によっお制限されたす。

この問題を解決するために、HexMapCamera静的メ゜ッドに远加できValidatePositionたす。AdjustPositionれロオフセットでむンスタンスメ゜ッドを呌び出すず、カメラがマップの境界に匷制的に移動したす。カメラが既に新しいマップの境界内にある堎合、カメラは所定の䜍眮に残りたす。

  public static void ValidatePosition () { instance.AdjustPosition(0f, 0f); } 

NewMapMenu.CreateMap新しいマップを䜜成した埌、内郚でメ゜ッドを呌び出したす。

  void CreateMap (int x, int z) { hexGrid.CreateMap(x, z); HexMapCamera.ValidatePosition(); Close(); } 

ナニティパッケヌゞ

マップサむズの保存


さたざたなサむズのカヌドを䜜成できたすが、保存や読み蟌みの際には考慮されたせん。これは、珟圚のマップのサむズがロヌドされたマップのサむズず䞀臎しない堎合、マップをロヌドするず゚ラヌたたは誀ったマップが発生するこずを意味したす。

この問題を解決するには、セルデヌタを読み蟌む前に、適切なサむズの新しいマップを䜜成する必芁がありたす。小さな地図が保存されおいるずしたす。この堎合、最初にHexGrid.Load20 x 15のマップを䜜成すれば、すべおうたくいきたす。

  public void Load (BinaryReader reader) { CreateMap(20, 15); for (int i = 0; i < cells.Length; i++) { cells[i].Load(reader); } for (int i = 0; i < chunks.Length; i++) { chunks[i].Refresh(); } } 

カヌドサむズの保管


もちろん、どんなサむズのカヌドでも保存できたす。したがっお、䞀般的な解決策は、これらのセルの前にマップのサむズを保存するこずです。

  public void Save (BinaryWriter writer) { writer.Write(cellCountX); writer.Write(cellCountZ); for (int i = 0; i < cells.Length; i++) { cells[i].Save(writer); } } 

次に、実際のサむズを取埗し、それを䜿甚しお正しいサむズのマップを䜜成できたす。

  public void Load (BinaryReader reader) { CreateMap(reader.ReadInt32(), reader.ReadInt32()); 
 } 

さたざたなサむズのマップをロヌドできるようになったため、カメラの䜍眮の問題に再び盎面しおいたす。HexMapEditor.Load地図を読み蟌んだ埌、その䜍眮をチェックむンするこずで解決したす。

  public void Load () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { int header = reader.ReadInt32(); if (header == 0) { hexGrid.Load(reader, header); HexMapCamera.ValidatePosition(); } else { Debug.LogWarning("Unknown map format " + header); } } } 

新しいファむル圢匏


このアプロヌチは、今埌保持するカヌドで機胜したすが、叀いカヌドでは機胜したせん。たたその逆-チュヌトリアルの前の郚分のコヌドは、新しいマップファむルを正しくロヌドできたせん。叀い圢匏ず新しい圢匏を区別するために、ヘッダヌの敎数倀を増やしたす。マップサむズのない叀い保存圢匏はバヌゞョン0でした。マップサむズの新しい圢匏はバヌゞョン1になりたす。したがっお、蚘録するずきHexMapEditor.Save、0ではなく1を曞き蟌む必芁がありたす。

  public void Save () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using ( BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)) ) { writer.Write(1); hexGrid.Save(writer); } } 

これ以降、マップはバヌゞョン1ずしお保存されたす。前のチュヌトリアルのアセンブリでマップを開こうずするず、䞍明なマップ圢匏のロヌドずレポヌトが拒吊されたす。実際、このようなカヌドを既にロヌドしようずするず、これが発生したす。HexMapEditor.Load新しいバヌゞョンを受け入れるようにメ゜ッドを倉曎する必芁がありたす。

  public void Load () { string path = Path.Combine(Application.persistentDataPath, "test.map"); using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { int header = reader.ReadInt32(); if (header == 1) { hexGrid.Load(reader); HexMapCamera.ValidatePosition(); } else { Debug.LogWarning("Unknown map format " + header); } } } 

䞋䜍互換性


実際、必芁に応じお、バヌゞョン0のマップをダりンロヌドできたす。マップのサむズはすべお20 x 15であるず仮定したす。぀たり、タむトルは1である必芁はなく、れロでもかたいたせん。各バヌゞョンには独自のアプロヌチが必芁であるためHexMapEditor.Load、ヘッダヌをmethodに枡す必芁がありHexGrid.Loadたす。

  if (header <= 1) { hexGrid.Load(reader, header); HexMapCamera.ValidatePosition(); } 

HexGrid.Loadパラメヌタにタむトルを远加し、それを䜿甚しお、さらにアクションを決定したす。ヘッダヌが1以䞊の堎合、カヌドサむズデヌタを読み取る必芁がありたす。それ以倖の堎合、20 x 15の叀い固定カヌドサむズを䜿甚し、サむズデヌタの読み取りをスキップしたす。

  public void Load (BinaryReader reader, int header) { int x = 20, z = 15; if (header >= 1) { x = reader.ReadInt32(); z = reader.ReadInt32(); } CreateMap(x, z); 
 } 

マップファむルバヌゞョン0

カヌドサむズの確認


新しいマップの䜜成ず同様に、フラグメントのサむズず互換性のないマップをロヌドする必芁がある可胜性が理論的にありたす。この堎合、カヌドのダりンロヌドを䞭断する必芁がありたす。HexGrid.CreateMapすでにマップの䜜成を拒吊し、コン゜ヌルに゚ラヌを衚瀺したす。これをメ゜ッドの呌び出し元に䌝えるために、マップが䜜成されたかどうかを䌝えるブヌル倀を返したしょう。

  public bool CreateMap (int x, int z) { if ( x <= 0 || x % HexMetrics.chunkSizeX != 0 || z <= 0 || z % HexMetrics.chunkSizeZ != 0 ) { Debug.LogError("Unsupported map size."); return false; } 
 return true; } 

さおHexGrid.Load、あたりにも、マップを䜜成するには、倱敗した堎合に実行を停止するこずができたす。

  public void Load (BinaryReader reader, int header) { int x = 20, z = 15; if (header >= 1) { x = reader.ReadInt32(); z = reader.ReadInt32(); } if (!CreateMap(x, z)) { return; } 
 } 

ロヌドするず既存のセルのすべおのデヌタが䞊曞きされるため、同じサむズのマップがロヌドされる堎合、新しいマップを䜜成する必芁はありたせん。したがっお、この手順はスキップできたす。

  if (x != cellCountX || z != cellCountZ) { if (!CreateMap(x, z)) { return; } } 

ナニティパッケヌゞ

ファむル管理


サむズの異なるカヌドを保存およびロヌドできたすが、垞にtest.mapを読み曞きしたす。次に、さたざたなファむルのサポヌトを远加したす。

マップを盎接保存たたはロヌドする代わりに、高床なファむル管理を提䟛する別のポップアップメニュヌを䜿甚したす。New Map Menuのように別のキャンバスを䜜成したすが、今回はSave Load Menuず呌びたす。このメニュヌは、マップを開いお抌すボタンに応じお、マップを保存およびロヌドしたす。Save Load Menu

デザむンを䜜成したす。保存メニュヌのように。埌でブヌトメニュヌに動的に倉曎したす。別のメニュヌず同様に、背景ずメニュヌバヌ、メニュヌラベル、キャンセルボタンが必芁です。次に、メニュヌにスクロヌルビュヌGameObject / UI / Scroll Viewを远加しお、ファむルのリストを衚瀺したす。以䞋に入力フィヌルドGameObject / UI / Input Fieldを挿入しお、新しいカヌドの名前を瀺したす。マップを保存するためのアクションボタンも必芁です。そしお最埌に。削陀ボタンを远加しお、䞍芁なカヌドを削陀したす。



[保存メニュヌの保存]を蚭蚈したす。

デフォルトでは、スクロヌルビュヌでは氎平スクロヌルず垂盎スクロヌルの䞡方が可胜ですが、必芁なのは垂盎スクロヌルのリストのみです。そのため、無効にスクロヌル氎平および氎平スクロヌルバヌ抜いおください。たた、Movement Typeをclampedに蚭定し、Inertiaを無効にしおリストの制限を匷化したす。


ファむルリストオプション。 File ListオブゞェクトからScrollbar Horizo​​ntal

子を削陀したす。これは必芁ないためです。次に、Scrollbar Verticalのサむズを倉曎しお、リストの䞋郚に到達したす。Name Inputオブゞェクトのプレヌスホルダヌテキストは、その子Placeholderで倉曎できたす。説明テキストを䜿甚したしたが、空癜のたたにしおプレヌスホルダヌを削陀できたす。




メニュヌのデザむンを倉曎したした。

デザむンが完了し、デフォルトでメニュヌが非衚瀺になるようにメニュヌを無効にしたす。

メニュヌ管理


メニュヌを機胜させるには、別のスクリプト、この堎合-が必芁SaveLoadMenuです。のようNewMapMenuに、グリッドぞのリンク、メ゜ッドOpen、およびが必芁Closeです。

 using UnityEngine; public class SaveLoadMenu : MonoBehaviour { public HexGrid hexGrid; public void Open () { gameObject.SetActive(true); HexMapCamera.Locked = true; } public void Close () { gameObject.SetActive(false); HexMapCamera.Locked = false; } } 

このコンポヌネントをSaveLoadMenuに远加し、グリッドオブゞェクトぞのリンクを䞎えたす。


コンポヌネントSaveLoadMenu。

保存たたはロヌドするメニュヌが開きたす。䜜業を簡玠化するには、Openブヌルパラメヌタをmethodに远加したす。メニュヌを保存モヌドにするかどうかを決定したす。このモヌドをフィヌルドで远跡しお、埌で実行するアクションを確認したす。

  bool saveMode; public void Open (bool saveMode) { this.saveMode = saveMode; gameObject.SetActive(true); HexMapCamera.Locked = true; } 

今すぐボタン組み合わせる保存ず読み蟌みオブゞェクトヘックスマップ゚ディタをメ゜ッドでOpenオブゞェクトの保存は、メニュヌをロヌドしたす。[ 保存 ]ボタンのみのブヌルパラメヌタを確認したす。


保存モヌドでメニュヌを開きたす。

ただ行っおいない堎合は、キャンセルボタンむベントをメ゜ッドに接続したすClose。今セヌブロヌドメニュヌを開閉するこずができたす。

倖芳の倉化


メニュヌを保存メニュヌずしお䜜成したしたが、モヌドはボタンを抌しお開くこずで決定されたす。モヌドに応じおメニュヌの倖芳を倉曎する必芁がありたす。特に、メニュヌラベルずアクションボタンラベルを倉曎する必芁がありたす。これは、これらのタグぞのリンクが必芁になるこずを意味したす。

 using UnityEngine; using UnityEngine.UI; public class SaveLoadMenu : MonoBehaviour { public Text menuLabel, actionButtonLabel; 
 } 


タグずの接続。

メニュヌが保存モヌドで開くず、既存のラベル、぀たり、メニュヌのマップの保存ずアクションボタンの保存が䜿甚されたす。それ以倖の堎合は、ロヌドモヌドになりたす。぀たり、Load MapずLoadを䜿甚したす。

  public void Open (bool saveMode) { this.saveMode = saveMode; if (saveMode) { menuLabel.text = "Save Map"; actionButtonLabel.text = "Save"; } else { menuLabel.text = "Load Map"; actionButtonLabel.text = "Load"; } gameObject.SetActive(true); HexMapCamera.Locked = true; } 

カヌド名を入力しおください


ファむルのリストは今のずころ残しおおきたしょう。ナヌザヌは、入力フィヌルドにカヌドの名前を入力しお、保存たたはダりンロヌドしたファむルを指定できたす。このデヌタを取埗するにInputFieldは、Name Inputオブゞェクトのコンポヌネントぞの参照が必芁です。

  public InputField nameInput; 


入力フィヌルドぞの接続。

ナヌザヌは、マップファむルぞのフルパスを入力する必芁はありたせん。.map拡匵子のないマップ名のみで十分です。ナヌザヌ入力を受け取り、その正しいパスを䜜成するメ゜ッドを远加したしょう。入力が空の堎合、これは䞍可胜なので、この堎合はを返しnullたす。

 using UnityEngine; using UnityEngine.UI; using System.IO; public class SaveLoadMenu : MonoBehaviour { 
 string GetSelectedPath () { string mapName = nameInput.text; if (mapName.Length == 0) { return null; } return Path.Combine(Application.persistentDataPath, mapName + ".map"); } } 

ナヌザヌが無効な文字を入力するずどうなりたすか
, . , , .

Content Type . , - , . , , .

保存ず読み蟌み


これで、保存ず読み蟌みが行われSaveLoadMenuたす。したがっお、我々は、メ゜ッドを移動SaveしおLoadのHexMapEditorの䞭でSaveLoadMenu。それらはもはや共有される必芁がなく、固定パスの代わりにパスパラメヌタで動䜜したす。

  void Save (string path) { // string path = Path.Combine(Application.persistentDataPath, "test.map"); using ( BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)) ) { writer.Write(1); hexGrid.Save(writer); } } void Load (string path) { // string path = Path.Combine(Application.persistentDataPath, "test.map"); using (BinaryReader reader = new BinaryReader(File.OpenRead(path))) { int header = reader.ReadInt32(); if (header <= 1) { hexGrid.Load(reader, header); HexMapCamera.ValidatePosition(); } else { Debug.LogWarning("Unknown map format " + header); } } } 

珟圚、任意のファむルをアップロヌドしおいるので、ファむルが実際に存圚するこずを確認しおから、それを読み取ろうずするずよいでしょう。そうでない堎合は、゚ラヌをスロヌしお操䜜を終了したす。

  void Load (string path) { if (!File.Exists(path)) { Debug.LogError("File does not exist " + path); return; } 
 } 

次に、䞀般的なメ゜ッドを远加しActionたす。ナヌザヌが遞択したパスを取埗するこずから始たりたす。パスがある堎合は、保存たたはロヌドしたす。次にメニュヌを閉じたす。

  public void Action () { string path = GetSelectedPath(); if (path == null) { return; } if (saveMode) { Save(path); } else { Load(path); } Close(); } 

このメ゜ッドにAction Buttonむベントをアタッチするこずにより、任意のマップ名を䜿甚しお保存およびロヌドできたす。入力フィヌルドをリセットしないため、遞択した名前は次の保存たたはロヌドたで残りたす。これは、1぀のファむルから連続しお耇数回保存たたはロヌドするのに䟿利なので、䜕も倉曎したせん。

マップリストアむテム


次に、ファむルのリストに、デヌタストレヌゞパス䞊にあるすべおのカヌドを入力したす。リスト内のいずれかのアむテムをクリックするず、名前入力のテキストずしお䜿甚されたす。SaveLoadMenuこのための䞀般的な方法を远加したす。

  public void SelectItem (string name) { nameInput.text = name; } 

リストアむテムが必芁です。通垞のボタンでできたす。䜜成し、高さを20単䜍に枛らしお、垂盎方向に倚くのスペヌスを占有しないようにしたす。ボタンのように芋えるべきではないので、ImageコンポヌネントのSource Imageリンクをクリアしたす。この堎合、完党に癜になりたす。さらに、ラベルが巊に揃えられ、テキストずボタンの巊偎の間にスペヌスがあるこずを確認したす。ボタンの蚭蚈が完了したら、それをプレハブに倉えたす。



ボタンはリスト項目です。

ボタンむベントを新しいマップメニュヌに盎接接続するこずはできたせん。これは、プレハブであり、ただシヌンに存圚しないためです。したがっお、クリックされSelectItemたずきにメ゜ッドを呌び出すこずができるように、メニュヌ項目にはメニュヌぞのリンクが必芁です。圌はたた、圌が代衚するカヌドの名前を远跡し、テキストを蚭定する必芁がありたす。このための小さなコンポヌネントを䜜成したしょうSaveLoadItem。

 using UnityEngine; using UnityEngine.UI; public class SaveLoadItem : MonoBehaviour { public SaveLoadMenu menu; public string MapName { get { return mapName; } set { mapName = value; transform.GetChild(0).GetComponent<Text>().text = value; } } string mapName; public void Select () { menu.SelectItem(mapName); } } 

コンポヌネントをメニュヌ項目に远加し、ボタンがそのメ゜ッドを呌び出すようにしたすSelect。


アむテムコンポヌネント。

リスト塗り぀ぶし


リストを䜜成SaveLoadMenuするには、ファむルリストオブゞェクトのビュヌポヌト内のコンテンツぞのリンクが必芁です。たた、アむテムのプレハブぞのリンクも必芁です。

  public RectTransform listContent; public SaveLoadItem itemPrefab; 


リストのコンテンツずプレハブをミックスしたす。

新しいメ゜ッドを䜿甚しお、このリストを䜜成したす。最初のステップは、既存のマップファむルを識別するこずです。ディレクトリ内のすべおのファむルパスの配列を取埗するには、メ゜ッドを䜿甚できたすDirectory.GetFiles。このメ゜ッドには、ファむルをフィルタヌできる2番目のパラメヌタヌがありたす。この堎合、* .mapマスクに䞀臎するファむルのみが必芁です。

  void FillList () { string[] paths = Directory.GetFiles(Application.persistentDataPath, "*.map"); } 

残念ながら、ファむルの順序は保蚌されおいたせん。それらをアルファベット順に衚瀺するには、配列をで゜ヌトする必芁がありたすSystem.Array.Sort。

 using UnityEngine; using UnityEngine.UI; using System; using System.IO; public class SaveLoadMenu : MonoBehaviour { 
 void FillList () { string[] paths = Directory.GetFiles(Application.persistentDataPath, "*.map"); Array.Sort(paths); } 
 } 

次に、配列の各芁玠に察しおプレハブむンスタンスを䜜成したす。アむテムをメニュヌにバむンドし、マップ名を蚭定しお、リストのコンテンツの子にしたす。

  Array.Sort(paths); for (int i = 0; i < paths.Length; i++) { SaveLoadItem item = Instantiate(itemPrefab); item.menu = this; item.MapName = paths[i]; item.transform.SetParent(listContent, false); } 

以来Directory.GetFiles、ファむルぞのフルパス戻り、私たちはそれらをきれいにする必芁がありたす。幞いなこずに、これはたさに䟿利なメ゜ッドを䜜るものPath.GetFileNameWithoutExtensionです。

  item.MapName = Path.GetFileNameWithoutExtension(paths[i]); 

メニュヌを衚瀺する前に、リストに蚘入する必芁がありたす。たた、ファむルは倉曎される可胜性が高いため、メニュヌを開くたびにこれを行う必芁がありたす。

  public void Open (bool saveMode) { 
 FillList(); gameObject.SetActive(true); HexMapCamera.Locked = true; } 

リストを再入力する堎合、新しいアむテムを远加する前に叀いものをすべお削陀する必芁がありたす。

  void FillList () { for (int i = 0; i < listContent.childCount; i++) { Destroy(listContent.GetChild(i).gameObject); } 
 } 


アレンゞなしのアむテム。

ポむントの配眮


これでリストに項目が衚瀺されたすが、それらは重なっお䞍適切な䜍眮にありたす。それらを垂盎リストに倉換するには、垂盎レむアりトグルヌプコンポヌネントコンポヌネント/レむアりト/垂盎レむアりトグルヌプをリストのコンテンツオブゞェクトに远加したす。配眮が正しく機胜するようにするには、「子コントロヌルサむズ」ず「子匷制拡匵」の䞡方の幅を有効にしたす。䞡方の高さオプションを無効にする必芁がありたす。





垂盎レむアりトグルヌプを䜿甚したす。

矎しいアむテムのリストを入手したした。ただし、リストのコンテンツのサむズはアむテムの実際の数に調敎されたせん。したがっお、スクロヌルバヌのサむズは倉曎されたせん。私たちは、䜜るこずができるコンテンツが自動的にコンポヌネント加えるこずにより、サむズ倉曎、コンテンツのサむズフィッタヌコンポヌネント/レむアりト/コンテンツサむズフィッタを。その垂盎フィットモヌドをPreferred Sizeに蚭定する必芁がありたす。



コンテンツサむズフィッタヌを䜿甚したす。

少数のポむントで、スクロヌルバヌが消えたす。たた、リスト内のビュヌポヌトに収たらないアむテムが倚すぎる堎合、スクロヌルバヌが衚瀺され、適切なサむズになりたす。


スクロヌルバヌが衚瀺されたす。

カヌドの削陀


これで、倚くのマップファむルを簡単に操䜜できたす。ただし、堎合によっおは䞀郚のカヌドを取り陀く必芁がありたす。これを行うには、削陀ボタンを䜿甚できたす。このためのメ゜ッドを䜜成し、ボタンから呌び出しおみたしょう。遞択したパスがある堎合は、単にで削陀しFile.Deleteたす。

  public void Delete () { string path = GetSelectedPath(); if (path == null) { return; } File.Delete(path); } 

ここでは、実際に存圚するファむルで䜜業しおいるこずも確認する必芁がありたす。そうでない堎合は、削陀しようずするべきではありたせんが、゚ラヌにはなりたせん。

  if (File.Exists(path)) { File.Delete(path); } 

カヌドを取り倖した埌、メニュヌを閉じる必芁はありたせん。これにより、䞀床に耇数のファむルを簡単に削陀できたす。ただし、削陀埌は、名前の入力をクリアするずずもに、ファむルのリストを曎新する必芁がありたす。

  if (File.Exists(path)) { File.Delete(path); } nameInput.text = ""; FillList(); 

ナニティパッケヌゞ

パヌト14レリヌフテクスチャ



この瞬間たで、私たちはカヌドを着色するために単色を䜿甚しおいたした。次に、テクスチャを適甚したす。


テクスチャの描画。

3皮類の混合物


均䞀な色は明らかに識別可胜で、タスクに完党に察応しおいたすが、あたり面癜く芋えたせん。テクスチャを䜿甚するず、マップの魅力が倧幅に向䞊したす。もちろん、このためには色だけでなくテクスチャを混ぜる必芁がありたす。Rendering 3チュヌトリアル、テクスチャの結合で、スプラットマップを䜿甚しお耇数のテクスチャを混合する方法に぀いお説明したした。六角圢のマップでは、同様のアプロヌチを䜿甚できたす。Rendering 3

チュヌトリアル4぀のテクスチャのみが混合され、1぀のスプラットマップで最倧5぀のテクスチャをサポヌトできたす。珟時点では5぀の異なる色を䜿甚しおいるため、これは非垞に適しおいたす。ただし、埌で他のタむプを远加できたす。したがっお、任意の数の救枈タむプのサポヌトが必芁です。テクスチャプロパティを明瀺的に蚭定しお䜿甚する堎合、これは䞍可胜であるため、テクスチャの配列を䜿甚する必芁がありたす。埌で䜜成したす。

テクスチャ配列を䜿甚する堎合、シェヌダヌに混合するテクスチャを䜕らかの方法で䌝える必芁がありたす。最も難しいミキシングは、䞉角圢の䞉角圢に必芁です。䞉角圢は、独自のタむプの地圢を持぀3぀のセルの間にある堎合がありたす。したがっお、䞉角圢ごずに3぀のタむプの混合サポヌトが必芁です。

スプラットマップずしお頂点カラヌを䜿甚する


どのテクスチャを混合するかを䌝えるこずができるず仮定するず、頂点カラヌを䜿甚しお各䞉角圢のスプラットマップを䜜成できたす。いずれの堎合も最倧3぀のテクスチャが䜿甚されるため、必芁なカラヌチャネルは3぀だけです。赀は最初のテクスチャ、緑は2番目、青は3番目のテクスチャを衚したす。


䞉角圢のスプラットマップ。

䞉角圢のスプラットマップの合蚈は垞に1に等しいですか
はい . . , (1, 0, 0) , (œ, œ, 0) (&frac13;, &frac13;, &frac13;) .

䞉角圢に必芁なテクスチャが1぀だけの堎合、最初のチャネルのみを䜿甚したす。぀たり、その色は完党に赀になりたす。2぀の異なるタむプを混合する堎合、最初ず2番目のチャネルを䜿甚したす。぀たり、䞉角圢の色は赀ず緑の混合になりたす。そしお、3぀のタむプすべおが出䌚うず、赀、緑、青の混合物になりたす。


3぀のスプラットマップ構成。

どのテクスチャが実際にミックスされるかに関係なく、これらのスプラットマップ構成を䜿甚したす。぀たり、スプラットマップは垞に同じになりたす。テクスチャのみが倉曎されたす。これを行う方法は、埌で確認したす。セルの色を䜿甚するのではなく、これらのスプラットマップを䜜成

するHexGridChunkように倉曎する必芁がありたす。しばしば3色を䜿甚するため、それらの静的フィヌルドを䜜成したす。

  static Color color1 = new Color(1f, 0f, 0f); static Color color2 = new Color(0f, 1f, 0f); static Color color3 = new Color(0f, 0f, 1f); 

セルセンタヌ


デフォルトでセルの䞭心の色を眮き換えるこずから始めたしょう。ここではブレンドは行われないため、最初の色、぀たり赀を䜿甚したす。

  void TriangulateWithoutRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { TriangulateEdgeFan(center, e, color1); 
 } 


セルの赀い䞭心。

セルの䞭心が赀に倉わりたす。これらはすべお、テクスチャが䜕であるかに関係なく、3぀のテクスチャの最初のものを䜿甚したす。スプラットマップは、セルを色付けする色に関係なく同じです。

川の近所


セルが流れる川のないセル内でのみセグメントを倉曎したした。川に隣接するセグメントに぀いおも同様にする必芁がありたす。私たちの堎合、これはリブストリップずリブの䞉角圢の扇の䞡方です。ここでも、赀だけで十分です。

  void TriangulateAdjacentToRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 TriangulateEdgeStrip(m, color1, e, color1); TriangulateEdgeFan(center, m, color1); 
 } 


川に隣接する赀いセグメント。

川


次に、セル内の川の圢状に泚意する必芁がありたす。それらもすべお赀に倉わりたす。たず、川の始たりず終わりを芋おみたしょう。

  void TriangulateWithRiverBeginOrEnd ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 TriangulateEdgeStrip(m, color1, e, color1); TriangulateEdgeFan(center, m, color1); 
 } 

そしお、土手ず川底を構成するゞオメトリ。コヌドを読みやすくするために、カラヌメ゜ッド呌び出しをグルヌプ化したした。

  void TriangulateWithRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 TriangulateEdgeStrip(m, color1, e, color1); terrain.AddTriangle(centerL, m.v1, m.v2); // terrain.AddTriangleColor(cell.Color); terrain.AddQuad(centerL, center, m.v2, m.v3); // terrain.AddQuadColor(cell.Color); terrain.AddQuad(center, centerR, m.v3, m.v4); // terrain.AddQuadColor(cell.Color); terrain.AddTriangle(centerR, m.v4, m.v5); // terrain.AddTriangleColor(cell.Color); terrain.AddTriangleColor(color1); terrain.AddQuadColor(color1); terrain.AddQuadColor(color1); terrain.AddTriangleColor(color1); 
 } 


セルに沿ったレッドリバヌ。

リブ


異なるタむプの地圢を持぀こずができるセルの間にあるため、すべおの゚ッゞは異なりたす。珟圚のセルタむプには最初の色を䜿甚し、隣接セルタむプには2番目の色を䜿甚したす。その結果、䞡方のセルが同じタむプであっおも、スプラットマップは赀緑のグラデヌションになりたす。䞡方のセルが同じテクスチャを䜿甚する堎合、それは䞡偎の同じテクスチャの混合になりたす。

  void TriangulateConnection ( HexDirection direction, HexCell cell, EdgeVertices e1 ) { 
 if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(e1, cell, e2, neighbor, hasRoad); } else { TriangulateEdgeStrip(e1, color1, e2, color2, hasRoad); } 
 } 


棚を陀く赀緑のリブ。

赀ず緑の急激な移行は問題を匕き起こしたせんか
, , . . splat map, . .

, .

出っ匵りのある゚ッゞは、頂点が远加されおいるため、もう少し耇雑です。幞いなこずに、既存の補間コヌドは、スプラットマップの色に最適です。最初ず最埌のセルの色ではなく、最初ず2番目の色を䜿甚しおください。

  void TriangulateEdgeTerraces ( EdgeVertices begin, HexCell beginCell, EdgeVertices end, HexCell endCell, bool hasRoad ) { EdgeVertices e2 = EdgeVertices.TerraceLerp(begin, end, 1); Color c2 = HexMetrics.TerraceLerp(color1, color2, 1); TriangulateEdgeStrip(begin, color1, e2, c2, hasRoad); for (int i = 2; i < HexMetrics.terraceSteps; i++) { EdgeVertices e1 = e2; Color c1 = c2; e2 = EdgeVertices.TerraceLerp(begin, end, i); c2 = HexMetrics.TerraceLerp(color1, color2, i); TriangulateEdgeStrip(e1, c1, e2, c2, hasRoad); } TriangulateEdgeStrip(e2, c2, end, color2, hasRoad); } 


green骚の赀緑の棚。

角床


セル角床は、3぀の異なるテクスチャを混合する必芁があるため、最も困難です。䞋のピヌクに赀、巊に緑、右に青を䜿甚したす。1぀の䞉角圢の角から始めたしょう。

  void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { 
 else { terrain.AddTriangle(bottom, left, right); terrain.AddTriangleColor(color1, color2, color3); } features.AddWall(bottom, bottomCell, left, leftCell, right, rightCell); } 


棚を陀く赀、緑、青の角。

ここでも、出っ匵りのあるコヌナヌに既存のカラヌ補間コヌドを䜿甚できたす。補間は2色ではなく3色の間で行われたす。最初に、厖の近くにない棚を考えおください。

  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 c3 = HexMetrics.TerraceLerp(color1, color2, 1); Color c4 = HexMetrics.TerraceLerp(color1, color3, 1); terrain.AddTriangle(begin, v3, v4); terrain.AddTriangleColor(color1, c3, c4); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color c1 = c3; Color c2 = c4; v3 = HexMetrics.TerraceLerp(begin, left, i); v4 = HexMetrics.TerraceLerp(begin, right, i); c3 = HexMetrics.TerraceLerp(color1, color2, i); c4 = HexMetrics.TerraceLerp(color1, color3, i); terrain.AddQuad(v1, v2, v3, v4); terrain.AddQuadColor(c1, c2, c3, c4); } terrain.AddQuad(v3, v4, left, right); terrain.AddQuadColor(c3, c4, color2, color3); } 


厖に沿った棚を陀く、赀緑青のコヌナヌ棚。

厖に関しおは、メ゜ッドを䜿甚する必芁がありたすTriangulateBoundaryTriangle。このメ゜ッドは、開始セルず巊セルをパラメヌタヌずしお受け取りたした。ただし、適切なスプラットカラヌが必芁になりたした。これは、トポロゞによっお異なる堎合がありたす。したがっお、これらのパラメヌタヌを色に眮き換えたす。

  void TriangulateBoundaryTriangle ( Vector3 begin, Color beginColor, Vector3 left, Color leftColor, Vector3 boundary, Color boundaryColor ) { Vector3 v2 = HexMetrics.Perturb(HexMetrics.TerraceLerp(begin, left, 1)); Color c2 = HexMetrics.TerraceLerp(beginColor, leftColor, 1); terrain.AddTriangleUnperturbed(HexMetrics.Perturb(begin), v2, boundary); terrain.AddTriangleColor(beginColor, c2, boundaryColor); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v2; Color c1 = c2; v2 = HexMetrics.Perturb(HexMetrics.TerraceLerp(begin, left, i)); c2 = HexMetrics.TerraceLerp(beginColor, leftColor, i); terrain.AddTriangleUnperturbed(v1, v2, boundary); terrain.AddTriangleColor(c1, c2, boundaryColor); } terrain.AddTriangleUnperturbed(v2, HexMetrics.Perturb(left), boundary); terrain.AddTriangleColor(c2, leftColor, boundaryColor); } 

TriangulateCornerTerracesCliff正しい色を䜿甚するように倉曎しおください。

  void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { 
 Color boundaryColor = Color.Lerp(color1, color3, b); TriangulateBoundaryTriangle( begin, color1, left, color2, boundary, boundaryColor ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, color2, right, color3, boundary, boundaryColor ); } else { terrain.AddTriangleUnperturbed( HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary ); terrain.AddTriangleColor(color2, color3, boundaryColor); } } 

そしお、同じこずを行いTriangulateCornerCliffTerracesたす。

  void TriangulateCornerCliffTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { 
 Color boundaryColor = Color.Lerp(color1, color2, b); TriangulateBoundaryTriangle( right, color3, begin, color1, boundary, boundaryColor ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, color2, right, color3, boundary, boundaryColor ); } else { terrain.AddTriangleUnperturbed( HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary ); terrain.AddTriangleColor(color2, color3, boundaryColor); } } 


完党なスプラットレリヌフマップ。

ナニティパッケヌゞ

テクスチャ配列


地圢にスプラットマップがあるので、テクスチャコレクションをシェヌダヌに枡すこずができたす。配列は単䞀の゚ンティティずしおGPUメモリに存圚する必芁があるため、シェヌダヌをCテクスチャの配列に割り圓おるこずはできたせん。Texture2DArrayバヌゞョン5.4以降、Unityでサポヌトされおいる特別なオブゞェクトを䜿甚する必芁がありたす。

すべおのGPUはテクスチャ配列をサポヌトしおいたすか
GPU , . Unity .
  • Direct3D 11/12 (Windows, Xbox One)
  • OpenGL Core (Mac OS X, Linux)
  • Metal (iOS, Mac OS X)
  • OpenGL ES 3.0 (Android, iOS, WebGL 2.0)
  • PlayStation 4


マスタヌ


残念ながら、バヌゞョン5.5でのテクスチャ配列のUnityのサポヌトは最小限です。テクスチャ配列アセットを䜜成しおテクスチャを割り圓おるだけではできたせん。手動で行う必芁がありたす。プレむモヌドでテクスチャの配列を䜜成するか、゚ディタヌでアセットを䜜成できたす。アセットを䜜成したしょう。

資産を䜜成する理由
, Play . , .

, . Unity . , . , .

テクスチャの配列を䜜成するには、独自のマスタヌを組み立おたす。スクリプトTextureArrayWizardを䜜成し、Editorフォルダヌ内に配眮したす。代わりに、名前空間からMonoBehaviour型を拡匵する必芁がScriptableWizardありたすUnityEditor。

 using UnityEditor; using UnityEngine; public class TextureArrayWizard : ScriptableWizard { } 

䞀般的な静的メ゜ッドを䜿甚しおりィザヌドを開くこずができたすScriptableWizard.DisplayWizard。そのパラメヌタヌは、りィザヌドりィンドりずその䜜成ボタンの名前です。このメ゜ッドを静的メ゜ッドで呌び出したすCreateWizard。

  static void CreateWizard () { ScriptableWizard.DisplayWizard<TextureArrayWizard>( "Create Texture Array", "Create" ); } 

゚ディタヌからりィザヌドにアクセスするには、このメ゜ッドをUnityメニュヌに远加する必芁がありたす。これは、メ゜ッドに属性を远加するこずで実行できたすMenuItem。それをAssetsメニュヌに、具䜓的にはAssets / Create / Texture Arrayに远加したしょう。

  [MenuItem("Assets/Create/Texture Array")] static void CreateWizard () { 
 } 


カスタムりィザヌド。

新しいメニュヌ項目を䜿甚しお、カスタムりィザヌドのポップアップメニュヌを開くこずができたす。あたり矎しくありたせんが、問題の解決に適しおいたす。ただし、ただ空です。テクスチャの配列を䜜成するには、テクスチャの配列が必芁です。マスタヌの䞀般フィヌルドを远加したす。りィザヌドの暙準GUIには、暙準むンスペクタヌのように衚瀺されたす。

  public Texture2D[] textures; 


テクスチャのマスタヌ。

䜕かを䜜ろう


りィザヌドの[ 䜜成 ]ボタンをクリックするず、りィザヌドが消えたす。さらに、Unityはメ゜ッドがないず文句を蚀いOnWizardCreateたす。これは、䜜成ボタンがクリックされたずきに呌び出されるメ゜ッドなので、りィザヌドに远加する必芁がありたす。

  void OnWizardCreate () { } 

ここで、テクスチャの配列を䜜成したす。少なくずも、ナヌザヌがマスタヌにテクスチャを远加した堎合。そうでない堎合、䜜成するものは䜕もないので、䜜業を停止する必芁がありたす。

  void OnWizardCreate () { if (textures.Length == 0) { return; } } 

次のステップは、テクスチャ配列アセットを保存する堎所をリク゚ストするこずです。メ゜ッドを䜿甚しお、ファむル保存パネルを開くこずができたすEditorUtility.SaveFilePanelInProject。そのパラメヌタヌは、パネル名、デフォルトのファむル名、ファむル拡匵子、および説明を定矩したす。テクスチャ配列は、䞀般的なアセットファむル拡匵子を䜿甚したす。

  if (textures.Length == 0) { return; } EditorUtility.SaveFilePanelInProject( "Save Texture Array", "Texture Array", "asset", "Save Texture Array" ); 

SaveFilePanelInProjectナヌザヌが遞択したファむルパスを返したす。ナヌザヌがこのパネルでキャンセルをクリックした堎合、パスは空の文字列になりたす。したがっお、この堎合、䜜業を䞭断する必芁がありたす。

  string path = EditorUtility.SaveFilePanelInProject( "Save Texture Array", "Texture Array", "asset", "Save Texture Array" ); if (path.Length == 0) { return; } 

テクスチャの配列を䜜成する


正しいパスがあれば、次に進み、新しいオブゞェクトを䜜成できたすTexture2DArray。圌のコンストラクタヌメ゜ッドでは、テクスチャの幅ず高さ、配列の長さ、テクスチャの圢匏、ミップテクスチャリングの必芁性を指定する必芁がありたす。これらのパラメヌタヌは、配列内のすべおのテクスチャで同じでなければなりたせん。オブゞェクトを蚭定するには、最初のテクスチャを䜿甚したす。ナヌザヌは、すべおのテクスチャが同じ圢匏であるこずを確認する必芁がありたす。

  if (path.Length == 0) { return; } Texture2D t = textures[0]; Texture2DArray textureArray = new Texture2DArray( t.width, t.height, textures.Length, t.format, t.mipmapCount > 1 ); 

テクスチャ配列は単䞀のGPUリ゜​​ヌスであるため、すべおのテクスチャに同じフィルタリングモヌドず折りたたみモヌドを䜿甚したす。ここでも、最初のテクスチャを䜿甚しおすべおを蚭定したす。

  Texture2DArray textureArray = new Texture2DArray( t.width, t.height, textures.Length, t.format, t.mipmapCount > 1 ); textureArray.anisoLevel = t.anisoLevel; textureArray.filterMode = t.filterMode; textureArray.wrapMode = t.wrapMode; 

メ゜ッドを䜿甚しお、テクスチャを配列にコピヌできたすGraphics.CopyTexture。このメ゜ッドは、䞀床に1ミップレベルの生のテクスチャデヌタをコピヌしたす。したがっお、すべおのテクスチャずそれらのミップレベルをルヌプする必芁がありたす。メ゜ッドパラメヌタは、テクスチャリ゜ヌス、むンデックス、およびミップレベルで構成される2぀のセットです。゜ヌステクスチャは配列ではないため、むンデックスは垞にれロです。

  textureArray.wrapMode = t.wrapMode; for (int i = 0; i < textures.Length; i++) { for (int m = 0; m < t.mipmapCount; m++) { Graphics.CopyTexture(textures[i], 0, m, textureArray, i, m); } } 

この段階で、メモリには正しいテクスチャの配列がありたすが、ただ資産ではありたせん。最埌の手順はAssetDatabase.CreateAsset、配列ずそのパスを䜿甚しお呌び出すこずです。この堎合、デヌタはプロゞェクトのファむルに曞き蟌たれ、プロゞェクトりィンドりに衚瀺されたす。

  for (int i = 0; i < textures.Length; i++) { 
 } AssetDatabase.CreateAsset(textureArray, path); 


テクスチャヌ


テクスチャの実際の配列を䜜成するには、元のテクスチャが必芁です。以䞋は、これたで䜿甚しおいた色に䞀臎する5぀のテクスチャです。黄色が砂になり、緑が草になり、青が土になり、オレンゞが石になり、癜が雪になりたす。






砂、草、地球、石、雪のテクスチャ。

これらのテクスチャは、このレリヌフの写真ではないこずに泚意しおください。これらは、NumberFlowを䜿甚しお䜜成した軜量の擬䌌ランダムパタヌンです。私は、抜象的な倚角圢のレリヌフず矛盟しない、認識可胜なレリヌフのタむプず詳现を䜜成するよう努めたした。フォトリアリズムはこれに適さないこずが刀明したした。さらに、パタヌンは倉動性を远加したすが、繰り返しがすぐに目立぀ようにするパタヌンはほずんどありたせん。

これらのテクスチャをマスタヌ配列に远加し、その順序が色ず䞀臎するこずを確認したす。぀たり、最初に砂、次に草、土、石、そしお最埌に雪です。



テクスチャの配列を䜜成したす。

テクスチャ配列アセットを䜜成したら、それを遞択しおむンスペクタヌで調べたす。


テクスチャ配列むンスペクタヌ。

これは、テクスチャ配列のデヌタの最も単玔な衚瀺です。最初にオンになっおいるIs Readableスむッチがあるこずに泚意しおください。配列からピクセルデヌタを読み取る必芁がないため、オフにしたす。Texture2DArrayこのパラメヌタヌにアクセスするメ゜ッドたたはプロパティがないため、りィザヌドでこれを行うこずはできたせん。

ではナニティ5.6は、呚りには切断をせずに、可胜である。耇数のプラットフォヌム間でのアセンブリにおけるテクスチャのバグその腐敗のアレむを有する読み取り可胜である。

これは、フィヌルドがあるこずが泚目にも䟡倀があるカラヌスペヌスは、倀1が割り圓おられたす。これは、テクスチャがガンマ空間にあるず想定されるこずを意味したす。それらが線圢空間にあるこずになっおいる堎合、フィヌルドに倀0を割り圓おる必芁がありたした。実際、デザむナヌにTexture2DArrayは色空間を蚭定するための远加のパラメヌタヌがありたすTexture2Dが、線圢空間にあるかどうかは衚瀺されないため、いずれにしおも、蚭定する必芁がありたす手動で倀。

シェヌダヌ


テクスチャの配列ができたので、シェヌダヌにその操䜜方法を教える必芁がありたす。ここでは、VertexColorsシェヌダヌを䜿甚しお地圢をレンダリングしたす。今から色の代わりにテクスチャを䜿甚するので、名前をTerrainに倉曎したす。次に、_MainTexパラメヌタヌをテクスチャの配列に倉換し、アセットに割り圓おたす。

 Shader "Custom/Terrain" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Terrain Texture Array", 2DArray) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } 
 } 


テクスチャの配列を含むレリヌフ玠材。

テクスチャアレむをサポヌトするすべおのプラットフォヌムでテクスチャアレむを有効にするには、シェヌダヌのタヌゲットレベルを3.0から3.5に䞊げる必芁がありたす。

  #pragma target 3.5 

倉数_MainTexはテクスチャの配列を参照するようになったため、タむプを倉曎する必芁がありたす。タむプはタヌゲットプラットフォヌムに䟝存し、マクロがこれを凊理しUNITY_DECLARE_TEX2DARRAYたす。

 // sampler2D _MainTex; UNITY_DECLARE_TEX2DARRAY(_MainTex); 

他のシェヌダヌず同様に、地圢テクスチャをサンプリングするには、䞖界のXZ座暙が必芁です。したがっお、サヌフェスシェヌダヌの入力構造にワヌルド内の䜍眮を远加したす。たた、デフォルトのUV座暙も必芁ないため、削陀したす。

  struct Input { // float2 uv_MainTex; float4 color : COLOR; float3 worldPos; }; 

テクスチャの配列をサンプリングするには、マクロを䜿甚する必芁がありたすUNITY_SAMPLE_TEX2DARRAY。配列をサンプリングするには、3぀の座暙が必芁です。最初の2぀は通垞のUV座暙です。0.02にスケヌリングされたXZワヌルド座暙を䜿甚したす。そのため、フル倍率で良奜なテクスチャ解像床が埗られたす。テクスチャは玄4セルごずに繰り返されたす。

3番目の座暙は、通垞の配列ず同様に、テクスチャ配列のむンデックスずしお䜿甚されたす。座暙は浮動小数点なので、GPU配列にむンデックスを付ける前に座暙を䞞めたす。どのテクスチャが必芁かがわかるたで、垞に最初のテクスチャを䜿甚したしょう。たた、頂点の色はスプラットマップであるため、最終結果に圱響したせん。

  void surf (Input IN, inout SurfaceOutputStandard o) { float2 uv = IN.worldPos.xz * 0.02; fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(uv, 0)); Albedo = c.rgb * _Color; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 


すべおが砂になりたした。

ナニティパッケヌゞ

テクスチャ遞択


䞉角圢ごずに3぀のタむプを混圚させるレリヌフスプラットマップが必芁です。テクスチャの配列があり、地圢のタむプごずにテクスチャがありたす。テクスチャ配列をサンプリングするシェヌダヌがありたす。ただし、珟時点では、各䞉角圢に遞択するテクスチャをシェヌダヌに䌝える方法はありたせん。

各䞉角圢には最倧3぀のタむプが混圚しおいるため、各䞉角圢に3぀のむンデックスを関連付ける必芁がありたす。䞉角圢の情報を保存できないため、頂点のむンデックスを保存する必芁がありたす。䞉角圢の3぀の頂点はすべお、単色の堎合ず同じむンデックスを単玔に栌玍したす。

メッシュデヌタ


むンデックスを保存するために、UVメッシュのセットの1぀を䜿甚できたす。各頂点に3぀のむンデックスが栌玍されおいるため、既存の2D UVセットでは十分ではありたせん。幞いなこずに、UVセットには最倧4぀の座暙を含めるこずができたす。したがっお、HexMesh2番目のリストに远加し、Vector3これをレリヌフタむプず呌びたす。

  public bool useCollider, useColors, useUVCoordinates, useUV2Coordinates; public bool useTerrainTypes; [NonSerialized] List<Vector3> vertices, terrainTypes; 

プレハブのHex Grid ChunkのTerrain子オブゞェクトの地圢タむプを有効にしたす。


リリヌフタむプを䜿甚しおいたす。

必芁に応じお、Vector3メッシュクリヌニング䞭にレリヌフタむプの別のリストを取埗したす。

  public void Clear () { 
 if (useTerrainTypes) { terrainTypes = ListPool<Vector3>.Get(); } triangles = ListPool<int>.Get(); } 

メッシュデヌタを適甚するプロセスでは、3番目のUVセットにレリヌフタむプを保存したす。このため、䞀緒に䜿甚するこずに決めた堎合、他の2぀のセットず競合するこずはありたせん。

  public void Apply () { 
 if (useTerrainTypes) { hexMesh.SetUVs(2, terrainTypes); ListPool<Vector3>.Add(terrainTypes); } hexMesh.SetTriangles(triangles, 0); 
 } 

䞉角圢のレリヌフタむプを蚭定するには、を䜿甚したすVector3。これらは䞉角圢党䜓で同じであるため、同じデヌタを3回远加するだけです。

  public void AddTriangleTerrainTypes (Vector3 types) { terrainTypes.Add(types); terrainTypes.Add(types); terrainTypes.Add(types); } 

クワッドでの混合も同じように機胜したす。4぀の頂点はすべお同じタむプです。

  public void AddQuadTerrainTypes (Vector3 types) { terrainTypes.Add(types); terrainTypes.Add(types); terrainTypes.Add(types); terrainTypes.Add(types); } 

リブの䞉角圢のファン


次に、のメッシュデヌタに型を远加する必芁がありHexGridChunkたす。から始めたしょうTriangulateEdgeFan。たず、読みやすくするために、頂点メ゜ッドずカラヌメ゜ッドの呌び出しを分離したす。このメ゜ッドを呌び出すたびにそれを圌color1に枡すので、この色を盎接䜿甚でき、パラメヌタヌを適甚しないこずを思い出しおください。

  void TriangulateEdgeFan (Vector3 center, EdgeVertices edge, Color color) { terrain.AddTriangle(center, edge.v1, edge.v2); // terrain.AddTriangleColor(color); terrain.AddTriangle(center, edge.v2, edge.v3); // terrain.AddTriangleColor(color); terrain.AddTriangle(center, edge.v3, edge.v4); // terrain.AddTriangleColor(color); terrain.AddTriangle(center, edge.v4, edge.v5); // terrain.AddTriangleColor(color); terrain.AddTriangleColor(color1); terrain.AddTriangleColor(color1); terrain.AddTriangleColor(color1); terrain.AddTriangleColor(color1); } 

色の埌に、レリヌフタむプを远加したす。䞉角圢のタむプは異なる堎合があるため、これは色を眮き換えるパラメヌタヌである必芁がありたす。この単玔なタむプを䜿甚しおを䜜成したすVector3。この堎合、スプラットマップは垞に赀であるため、最初の4぀のチャネルのみが重芁です。ベクトルの3぀のコンポヌネントすべおを割り圓おる必芁があるため、1぀のタむプを割り圓おたしょう。

  void TriangulateEdgeFan (Vector3 center, EdgeVertices edge, float type) { 
 Vector3 types; types.x = types.y = types.z = type; terrain.AddTriangleTerrainTypes(types); terrain.AddTriangleTerrainTypes(types); terrain.AddTriangleTerrainTypes(types); terrain.AddTriangleTerrainTypes(types); } 

次に、このメ゜ッドのすべおの呌び出しを倉曎しお、色の匕数をセルの地圢のタむプのむンデックスに眮き換える必芁がありたす。TriangulateWithoutRiver、TriangulateAdjacentToRiverおよびでこの倉曎を行いTriangulateWithRiverBeginOrEndたす。

 // TriangulateEdgeFan(center, e, color1); TriangulateEdgeFan(center, e, cell.TerrainTypeIndex); 

この時点で、再生モヌドを開始するず、UVメッシュの3番目のセットが範囲倖であるこずを通知する゚ラヌが衚瀺されたす。これは、各䞉角圢ず四角圢にただレリヌフタむプを远加しおいないために発生したした。それで、倉曎を続けたしょうHexGridChunk。

リブストラむプ


ここで、゚ッゞストリップを䜜成するずき、䞡偎にどのような地圢があるかを知る必芁がありたす。したがっお、それらをパラメヌタヌずしお远加し、2぀のチャネルにこれらのタむプが割り圓おられおいるタむプのベクトルを䜜成したす。3番目のチャネルは重芁ではありたせん。したがっお、最初のチャネルず同䞀芖しおください。色を远加したら、クワッドにタむプを远加したす。

  void TriangulateEdgeStrip ( EdgeVertices e1, Color c1, float type1, EdgeVertices e2, Color c2, float type2, 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); 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たす。たずTriangulateAdjacentToRiver、TriangulateWithRiverBeginOrEndそしおTriangulateWithRiverあなたは、フィンストリップの䞡偎のための现胞型を䜿甚する必芁がありたす。

 // TriangulateEdgeStrip(m, color1, e, color1); TriangulateEdgeStrip( m, color1, cell.TerrainTypeIndex, e, color1, cell.TerrainTypeIndex ); 

次に、゚ッゞの最も単玔なケヌスではTriangulateConnection、最も近い゚ッゞにはセルタむプを䜿甚し、遠い゚ッゞには隣接タむプを䜿甚する必芁がありたす。それらは同じでも異なっおいおもかたいたせん。

  void TriangulateConnection ( HexDirection direction, HexCell cell, EdgeVertices e1 ) { 
 if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(e1, cell, e2, neighbor, hasRoad); } else { // TriangulateEdgeStrip(e1, color1, e2, color2, hasRoad); TriangulateEdgeStrip( e1, color1, cell.TerrainTypeIndex, e2, color2, neighbor.TerrainTypeIndex, hasRoad ); } 
 } 

同じTriangulateEdgeTerracesこずが3回トリガヌされるものにも圓おはたりたすTriangulateEdgeStrip。棚のタむプは同じです。

  void TriangulateEdgeTerraces ( EdgeVertices begin, HexCell beginCell, EdgeVertices end, HexCell endCell, bool hasRoad ) { EdgeVertices e2 = EdgeVertices.TerraceLerp(begin, end, 1); Color c2 = HexMetrics.TerraceLerp(color1, color2, 1); float t1 = beginCell.TerrainTypeIndex; float t2 = endCell.TerrainTypeIndex; TriangulateEdgeStrip(begin, color1, t1, e2, c2, t2, hasRoad); for (int i = 2; i < HexMetrics.terraceSteps; i++) { EdgeVertices e1 = e2; Color c1 = c2; e2 = EdgeVertices.TerraceLerp(begin, end, i); c2 = HexMetrics.TerraceLerp(color1, color2, i); TriangulateEdgeStrip(e1, c1, t1, e2, c2, t2, hasRoad); } TriangulateEdgeStrip(e2, c2, t1, end, color2, t2, hasRoad); } 

角床


角床の最も単玔な堎合は、単玔な䞉角圢です。䞋のセルは最初のタむプ、巊は2番目、右は3番目のタむプを送信したす。それらを䜿甚しお、タむプのベクトルを䜜成し、䞉角圢に远加したす。

  void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { 
 else { terrain.AddTriangle(bottom, left, right); terrain.AddTriangleColor(color1, color2, color3); 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 c3 = HexMetrics.TerraceLerp(color1, color2, 1); Color c4 = HexMetrics.TerraceLerp(color1, color3, 1); Vector3 types; types.x = beginCell.TerrainTypeIndex; types.y = leftCell.TerrainTypeIndex; types.z = rightCell.TerrainTypeIndex; terrain.AddTriangle(begin, v3, v4); terrain.AddTriangleColor(color1, c3, c4); terrain.AddTriangleTerrainTypes(types); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color c1 = c3; Color c2 = c4; v3 = HexMetrics.TerraceLerp(begin, left, i); v4 = HexMetrics.TerraceLerp(begin, right, i); c3 = HexMetrics.TerraceLerp(color1, color2, i); c4 = HexMetrics.TerraceLerp(color1, color3, i); terrain.AddQuad(v1, v2, v3, v4); terrain.AddQuadColor(c1, c2, c3, c4); terrain.AddQuadTerrainTypes(types); } terrain.AddQuad(v3, v4, left, right); terrain.AddQuadColor(c3, c4, color2, color3); terrain.AddQuadTerrainTypes(types); } 

棚ず厖を混ぜるずきは、を䜿甚する必芁がありたすTriangulateBoundaryTriangle。型ベクトルパラメヌタヌを指定しお、すべおの䞉角圢に远加するだけです。

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

TriangulateCornerTerracesCliff透過现胞タむプに基づいおベクトルを䜜成したす。次に、それを1぀の䞉角圢に远加しお枡しTriangulateBoundaryTriangleたす。

  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 boundaryColor = Color.Lerp(color1, color3, b); Vector3 types; types.x = beginCell.TerrainTypeIndex; types.y = leftCell.TerrainTypeIndex; types.z = rightCell.TerrainTypeIndex; TriangulateBoundaryTriangle( begin, color1, left, color2, boundary, boundaryColor, types ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, color2, right, color3, boundary, boundaryColor, types ); } else { terrain.AddTriangleUnperturbed( HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary ); terrain.AddTriangleColor(color2, color3, boundaryColor); terrain.AddTriangleTerrainTypes(types); } } 

同じこずが圓おはたり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 boundaryColor = Color.Lerp(color1, color2, b); Vector3 types; types.x = beginCell.TerrainTypeIndex; types.y = leftCell.TerrainTypeIndex; types.z = rightCell.TerrainTypeIndex; TriangulateBoundaryTriangle( right, color3, begin, color1, boundary, boundaryColor, types ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, color2, right, color3, boundary, boundaryColor, types ); } else { terrain.AddTriangleUnperturbed( HexMetrics.Perturb(left), HexMetrics.Perturb(right), boundary ); terrain.AddTriangleColor(color2, color3, boundaryColor); terrain.AddTriangleTerrainTypes(types); } } 

川


倉曎する最埌の方法はこれTriangulateWithRiverです。ここではセルの䞭心にいるので、珟圚のセルのタむプのみを扱っおいたす。したがっお、そのためのベクトルを䜜成し、䞉角圢ず四角圢に远加したす。

  void TriangulateWithRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { 
 terrain.AddTriangleColor(color1); terrain.AddQuadColor(color1); terrain.AddQuadColor(color1); terrain.AddTriangleColor(color1); Vector3 types; types.x = types.y = types.z = cell.TerrainTypeIndex; terrain.AddTriangleTerrainTypes(types); terrain.AddQuadTerrainTypes(types); terrain.AddQuadTerrainTypes(types); terrain.AddTriangleTerrainTypes(types); 
 } 

タむプミックス


この段階で、メッシュには必芁な暙高むンデックスが含たれおいたす。残っおいるのは、Terrainシェヌダヌに匷制的にそれらを䜿甚させるこずだけです。フラグメントシェヌダヌでむンデックスを取埗するには、最初にむンデックスを頂点シェヌダヌに枡す必芁がありたす。河口シェヌダヌで行ったように、独自の頂点関数でこれを行うこずができたす。この堎合、入力構造にフィヌルドを远加し、その構造float3 terrainにコピヌしたすv.texcoord2.xyz。

  #pragma surface surf Standard fullforwardshadows vertex:vert #pragma target 3.5 
 struct Input { float4 color : COLOR; float3 worldPos; float3 terrain; }; void vert (inout appdata_full v, out Input data) { UNITY_INITIALIZE_OUTPUT(Input, data); data.terrain = v.texcoord2.xyz; } 

テクスチャ配列をフラグメントごずに3回サンプリングする必芁がありたす。したがっお、テクスチャ座暙の䜜成、配列のサンプリング、1぀のむンデックスのスプラットマップを䜿甚したサンプルの倉調に䟿利な関数を䜜成したしょう。

  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]; } void surf (Input IN, inout SurfaceOutputStandard o) { 
 } 

ベクトルを配列ずしお䜿甚できたすか
はい - color[0] color.r . color[1] color.g , .

この関数を䜿甚するず、単玔にテクスチャ配列を3回サンプリングし、結果を組み合わせるこずができたす。

  void surf (Input IN, inout SurfaceOutputStandard o) { // float2 uv = IN.worldPos.xz * 0.02; fixed4 c = GetTerrainColor(IN, 0) + GetTerrainColor(IN, 1) + GetTerrainColor(IN, 2); o.Albedo = c.rgb * _Color; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 


テクスチャレリヌフ。

これで、テクスチャでレリヌフをペむントできたす。単色のように混ざりたす。ワヌルド座暙はUV座暙ずしお䜿甚されるため、高さによっお倉化するこずはありたせん。その結果、鋭い厖に沿っお、テクスチャが匕き䌞ばされたす。テクスチャがかなりニュヌトラルで非垞に倉化しやすい堎合、結果は受け入れられたす。それ以倖の堎合、倧きなbigいストレッチマヌクが衚瀺されたす。远加のゞオメトリたたは厖のテクスチャで非衚瀺にするこずもできたすが、チュヌトリアルではこれを行いたせん。

スむヌプ


ここで、色の代わりにテクスチャを䜿甚する堎合、゚ディタヌパネルを倉曎するのが論理的です。レリヌフテクスチャを衚瀺するこずもできる矎しいむンタヌフェむスを䜜成できたすが、既存のスキヌムのスタむルに察応する略語に焊点を圓おたす。


救枈オプション。

たた、HexCellcolorプロパティは䞍芁になったため、削陀したす。

 // public Color Color { // get { // return HexMetrics.colors[terrainTypeIndex]; // } // } 

たたのでHexGrid、あなたは色の配列ずそれに関連するコヌドを削陀するこずができたす。

 // public Color[] colors; 
 void Awake () { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); // HexMetrics.colors = colors; CreateMap(cellCountX, cellCountZ); } 
 
 void OnEnable () { if (!HexMetrics.noiseSource) { HexMetrics.noiseSource = noiseSource; HexMetrics.InitializeHashGrid(seed); // HexMetrics.colors = colors; } } 

最埌に、色の配列も必芁ありたせんHexMetrics。

 // public static Color[] colors; 

ナニティパッケヌゞ

パヌト15距離



高品質のマップを䜜成したら、ナビゲヌションを開始したす。


最短経路は必ずしも盎線ではありたせん。

グリッド衚瀺


マップ䞊のナビゲヌションは、セルからセルに移動するこずにより実行されたす。どこかに到達するには、䞀連のセルを通過する必芁がありたす。距離の掚定を簡単にするために、マップのベヌスずなる六角圢のグリッドを衚瀺するオプションを远加したしょう。

メッシュテクスチャ


マップメッシュの䞍芏則性にもかかわらず、基瀎ずなるメッシュは完党に平らです。これを瀺すには、グリッドパタヌンを地図に投圱したす。これは、繰り返しメッシュテクスチャを䜿甚しお実珟できたす。


メッシュテクスチャを繰り返したす。

䞊蚘のテクスチャには、2 x 2セルをカバヌする六角圢グリッドの小さな郚分が含たれおいたす。この領域は正方圢ではなく長方圢です。テクスチャ自䜓は正方圢なので、パタヌンは匕き䌞ばされお芋えたす。サンプリング時には、これを補正する必芁がありたす。

グリッド投圱


メッシュパタヌンを投圱するには、Terrainシェヌダヌにテクスチャプロパティを远加する必芁がありたす。

  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 } 


メッシュテクスチャ付きのレリヌフ玠材。

ワヌルドのXZ座暙を䜿甚しおテクスチャをサンプリングし、アルベドを掛けたす。テクスチャのグリッド線は灰色であるため、これによりパタヌンが浮き圫りに織り蟌たれたす。

  sampler2D _GridTex; 
 void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = GetTerrainColor(IN, 0) + GetTerrainColor(IN, 1) + GetTerrainColor(IN, 2); fixed4 grid = tex2D(_GridTex, IN.worldPos.xz); o.Albedo = c.rgb * grid * _Color; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 


アルベドに现かいメッシュを掛けたす。

マップ内のセルに䞀臎するようにパタヌンをスケヌリングする必芁がありたす。隣接するセルの䞭心間の距離は15で、2぀のセルを䞊に移動するには2倍にする必芁がありたす。぀たり、Vグリッドの座暙を30で割る必芁がありたす。セルの内郚半埄は5√3であり、2぀のセルを右に移動するには、4倍必芁です。したがっお、グリッドのU座暙を20√3で陀算する必芁がありたす。

  float2 gridUV = IN.worldPos.xz; gridUV.x *= 1 / (4 * 8.66025404); gridUV.y *= 1 / (2 * 15.0); fixed4 grid = tex2D(_GridTex, gridUV); 


正しいメッシュサむズ。

これで、グリッド線はマップのセルに察応したす。レリヌフテクスチャず同様に、高さは無芖されるため、厖に沿っお線が匕き䌞ばされたす。


高さのあるセルぞの投圱。

メッシュの倉圢は、特にマップを遠くから芋た堎合、特にそれほど悪くはありたせん。


遠くのメッシュ。

グリッド有効化


グリッドの衚瀺は䟿利ですが、垞に必芁なわけではありたせん。たずえば、スクリヌンショットを撮るずきはオフにする必芁がありたす。さらに、誰もが垞にグリッドを芋るこずを奜むわけではありたせん。それでは、オプションにしたしょう。multi_compileディレクティブをシェヌダヌに远加しお、グリッドの有無にかかわらずオプションを䜜成したす。これを行うには、キヌワヌドを䜿甚しGRID_ONたす。条件付きシェヌダヌのコンパむルに぀いおは、Rendering 5チュヌトリアル、Multiple Lightsで説明されおいたす。

  #pragma surface surf Standard fullforwardshadows vertex:vert #pragma target 3.5 #pragma multi_compile _ GRID_ON 

倉数を宣蚀するずき、grid最初に倀1を割り圓おたす。その結果、グリッドは無効になりたす。次に、特定のキヌワヌドを持぀バリアントのグリッドテクスチャのみをサンプリングしGRID_ONたす。

  fixed4 grid = 1; #if defined(GRID_ON) float2 gridUV = IN.worldPos.xz; gridUV.x *= 1 / (4 * 8.66025404); gridUV.y *= 1 / (2 * 15.0); grid = tex2D(_GridTex, gridUV); #endif o.Albedo = c.rgb * grid * _Color; 

キヌワヌドはGRID_ONテレむンシェヌダヌに含たれおいないため、グリッドは消えたす。再び有効にするには、マップ゚ディタヌUIにスむッチを远加したす。これを可胜にするにHexMapEditorは、テレむンマテリアルぞのリンクず、キヌワヌドを有効たたは無効にするメ゜ッドを取埗する必芁がありたすGRID_ON。

  public Material terrainMaterial; 
 public void ShowGrid (bool visible) { if (visible) { terrainMaterial.EnableKeyword("GRID_ON"); } else { terrainMaterial.DisableKeyword("GRID_ON"); } } 


材料に関する3月の線集者の六角圢。 グリッド

スむッチをUIに远加し、それをメ゜ッドに接続したす。ShowGrid


グリッドスむッチ。

状態を保存


これで、プレむモヌドで、グリッドの衚瀺を切り替えるこずができたす。最初のテストでは、グリッドは最初はオフになっおおり、スむッチをオンにするず衚瀺されたす。オフにするず、グリッドは再び衚瀺されなくなりたす。ただし、グリッドが衚瀺されおいるずきに再生モヌドを終了するず、次に再生モヌドを開始するず、スむッチはオフになりたすが、再びオンになりたす。

これは、䞀般的なテレむンマテリアルのキヌワヌドを倉曎しおいるためです。マテリアルアセットを線集しおいるため、倉曎はUnity゚ディタヌに保存されたす。アセンブリには保存されたせん。

垞にグリッドなしでゲヌムを開始するにはGRID_ON、Awakeでキヌワヌドを無効にしHexMapEditorたす。

  void Awake () { terrainMaterial.DisableKeyword("GRID_ON"); } 

ナニティパッケヌゞ

線集モヌド


マップ䞊の動きを制埡したい堎合、それず察話する必芁がありたす。少なくずも、パスの開始点ずしおセルを遞択する必芁がありたす。しかし、セルをクリックするず、線集されたす。すべおの線集オプションを手動で無効にできたすが、これは䞍䟿です。さらに、マップの線集䞭にディスプレむスメントの蚈算を実行したくありたせん。それでは、線集モヌドかどうかを決定するスむッチを远加したしょう。

線集スむッチ


HexMapEditorブヌル型フィヌルドeditMode、およびそれを定矩するメ゜ッドに远加したす。次に、UIに別のスむッチを远加しお制埡したす。ナビゲヌションモヌドから始めたしょう。぀たり、線集モヌドはデフォルトで無効になりたす。

  bool editMode; 
 public void SetEditMode (bool toggle) { editMode = toggle; } 


線集モヌドの切り替え。

線集を実際に無効にするには、呌び出しをにEditCells䟝存させeditModeたす。

  void HandleInput () { Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(inputRay, out hit)) { HexCell currentCell = hexGrid.GetCell(hit.point); if (previousCell && previousCell != currentCell) { ValidateDrag(currentCell); } else { isDrag = false; } if (editMode) { EditCells(currentCell); } previousCell = currentCell; } else { previousCell = null; } } 

ラベルのデバッグ


これたでのずころ、マップ内を移動するナニットはありたせん。代わりに、移動距離を芖芚化したす。これを行うには、既存のセルラベルを䜿甚できたす。したがっお、線集モヌドが無効になっおいるずきにそれらを衚瀺したす。

  public void SetEditMode (bool toggle) { editMode = toggle; hexGrid.ShowUI(!toggle); } 

ナビゲヌションモヌドで開始するため、デフォルトのラベルを有効にする必芁がありたす。珟圚HexGridChunk.Awake、それらを無効にしたすが、圌はこれを行うべきではありたせん。

  void Awake () { gridCanvas = GetComponentInChildren<Canvas>(); cells = new HexCell[HexMetrics.chunkSizeX * HexMetrics.chunkSizeZ]; // ShowUI(false); } 


ラベルを調敎したす。

プレむモヌドを起動するず、セル座暙がすぐに衚瀺されるようになりたした。ただし、座暙は必芁ありたせん。ラベルを䜿甚しお距離を衚瀺したす。これにはセルごずに1぀の数倀しか必芁ないため、フォントサむズを倧きくしお読みやすくするこずができたす。Hex Cell Labelのプレハブを倉曎しお、サむズ8の倪字フォントを䜿甚するようにしたす。


倪字のフォントサむズ8の

タグ。再生モヌドを起動するず、倧きなタグが衚瀺されたす。セルの最初の座暙のみが衚瀺され、残りはラベルに配眮されたせん。


倧きなタグ。

座暙はもう必芁ないので、HexGrid.CreateCell割り圓おの倀を削陀したすlabel.text。

  void CreateCell (int x, int z, int i) { 
 Text label = Instantiate<Text>(cellLabelPrefab); label.rectTransform.anchoredPosition = new Vector2(position.x, position.z); // label.text = cell.coordinates.ToStringOnSeparateLines(); cell.uiRect = label.rectTransform; 
 } 

Labelsスむッチずその関連メ゜ッドをUIから削陀するこずもできHexMapEditor.ShowUIたす。

 // public void ShowUI (bool visible) { // hexGrid.ShowUI(visible); // } 


メ゜ッドの切り替えはもうありたせん。

ナニティパッケヌゞ

距離を芋぀ける


タグ付きナビゲヌションモヌドができたので、距離の衚瀺を開始できたす。セルを遞択し、このセルからマップ䞊のすべおのセルたでの距離を衚瀺したす。

距離衚瀺


セルたでの距離を远跡するには、HexCell敎数フィヌルドに远加したすdistance。このセルず遞択したセルの間の距離を瀺したす。したがっお、遞択されたセル自䜓の堎合はれロになり、すぐ隣のセルの堎合は1になりたす。

  int distance; 

距離が蚭定されたら、セルラベルを曎新しおその倀を衚瀺する必芁がありたす。UIオブゞェクトHexCellぞの参照がRectTransformありたす。私たちは圌に電話しGetComponent<Text>お、独房に着く必芁がありたす。Text名前空間にあるものを考慮しおUnityEngine.UI、スクリプトの最初で䜿甚しおください。

  void UpdateDistanceLabel () { Text label = uiRect.GetComponent<Text>(); label.text = distance.ToString(); } 

テキストコンポヌネントぞの盎接リンクを保持する必芁はありたせんか
, . , , , . , .

䞀般的なプロパティを蚭定しお、セルたでの距離を受け取っお蚭定し、ラベルを曎新したす。

  public int Distance { get { return distance; } set { distance = value; UpdateDistanceLabel(); } } 

cellパラメヌタヌを䜿甚しおHexGrid䞀般的なメ゜ッドに远加しFindDistancesToたす。ここでは、各セルにれロ距離を蚭定するだけです。

  public void FindDistancesTo (HexCell cell) { for (int i = 0; i < cells.Length; i++) { cells[i].Distance = 0; } } 

線集モヌドが有効になっおいない堎合HexMapEditor.HandleInput、珟圚のセルで新しいメ゜ッドを呌び出したす。

  if (editMode) { EditCells(currentCell); } else { hexGrid.FindDistancesTo(currentCell); } 

座暙間の距離


ナビゲヌションモヌドでは、そのうちの1぀に觊れるず、すべおのセルにれロが衚瀺されたす。ただし、もちろん、セルたでの実際の距離を衚瀺する必芁がありたす。それらたでの距離を蚈算するには、セルの座暙を䜿甚できたす。したがっお、HexCoordinatesメ゜ッドがあるず仮定しDistanceTo、で䜿甚しHexGrid.FindDistancesToたす。

  public void FindDistancesTo (HexCell cell) { for (int i = 0; i < cells.Length; i++) { cells[i].Distance = cell.coordinates.DistanceTo(cells[i].coordinates); } } 

HexCoordinatesメ゜ッドに远加しDistanceToたす。圌は自分の座暙を別のセットの座暙ず比范しなければなりたせん。Xを枬定するこずから始めたしょう。X座暙を互いに枛算したす。

  public int DistanceTo (HexCoordinates other) { return x - other.x; } 

その結果、遞択したセルに察しおXに沿ったオフセットを取埗したす。ただし、距離を負にするこずはできないため、座暙差Xモゞュロを返す必芁がありたす。

  return x < other.x ? other.x - x : x - other.x; 


Xに沿った距離。

したがっお、1぀の次元のみを考慮した堎合にのみ正しい距離が埗られたす。しかし、六角圢のグリッドには3぀の次元がありたす。それでは、3぀の次元すべおの距離を合蚈しお、それが䜕をもたらすかを芋おみたしょう。

  return (x < other.x ? other.x - x : x - other.x) + (Y < other.Y ? other.Y - Y : Y - other.Y) + (z < other.z ? other.z - z : z - other.z); 


XYZによる距離の合蚈。

距離が2倍になるこずがわかりたした。぀たり、正しい距離を取埗するには、この量を半分に分割する必芁がありたす。

  return ((x < other.x ? other.x - x : x - other.x) + (Y < other.Y ? other.Y - Y : Y - other.Y) + (z < other.z ? other.z - z : z - other.z)) / 2; 


実際の距離。

合蚈が距離の2倍に等しいのはなぜですか
, . , (1, −3, 2). . , . . , . .


.

ナニティパッケヌゞ

障害物を扱う


私たちが蚈算した距離は、遞択したセルから他の各セルたでの最短経路に察応しおいたす。もっず短い方法を芋぀けるこずはできたせん。ただし、ルヌトが䜕もブロックしない堎合、これらのパスは正しいこずが保蚌されたす。厖、氎、その他の障害物があるず、私たちは歩き回るこずができたす。おそらく、いく぀かのセルにたったく到達できたせん。

障害物を回避する方法を芋぀けるには、単に座暙間の距離を蚈算するのではなく、別のアプロヌチを䜿甚する必芁がありたす。各セルを個別に調べるこずはできなくなりたした。到達可胜なすべおのセルが芋぀かるたで、マップを怜玢する必芁がありたす。

怜玢の芖芚化


マップ怜玢は反埩プロセスです。私たちが䜕をしおいるかを理解するには、怜玢の各段階を確認するず圹立ちたす。これを行うには、怜玢アルゎリズムをコルヌチンに倉換したすSystem.Collections。これには怜玢スペヌスが必芁です。1秒間に60回の曎新のリフレッシュレヌトは、䜕が起こっおいるのかを確認するには十分に小さく、小さなマップでの怜玢にはそれほど時間がかかりたせんでした。

  public void FindDistancesTo (HexCell cell) { StartCoroutine(Search(cell)); } IEnumerator Search (HexCell cell) { WaitForSeconds delay = new WaitForSeconds(1 / 60f); for (int i = 0; i < cells.Length; i++) { yield return delay; cells[i].Distance = cell.coordinates.DistanceTo(cells[i].coordinates); } } 

垞に1぀の怜玢のみがアクティブであるこずを確認する必芁がありたす。したがっお、新しい怜玢を開始する前に、すべおのコルヌチンを停止したす。

  public void FindDistancesTo (HexCell cell) { StopAllCoroutines(); StartCoroutine(Search(cell)); } 

さらに、新しいマップを読み蟌むずきに怜玢を完了する必芁がありたす。

  public void Load (BinaryReader reader, int header) { StopAllCoroutines(); 
 } 

幅優先怜玢


怜玢を開始する前でも、遞択したセルたでの距離はれロであるこずがわかりたす。そしお、もちろん、それらに到達できる堎合、そのすべおの近隣ぞの距離は1です。次に、これらの隣人の1぀を芋おみたしょう。このセルには、到達可胜な独自の近傍があり、距離はただ蚈算されおいたせん。その堎合、これらの隣人たでの距離は2である必芁がありたす。距離1のすべおの隣人に察しおこのプロセスを繰り返すこずができたす。その埌、距離2のすべおの隣人に察しおこのプロセスを繰り返したす。

぀たり、最初に距離1にあるすべおのセルを芋぀け、次に2の距離にあるすべおのセルを芋぀け、次に3の距離にあるなど、終了するたで芋぀けたす。これにより、到達可胜な各セルたでの最短距離を芋぀けるこずができたす。このアルゎリズムは、幅優先怜玢ず呌ばれたす。

それが機胜するためには、セルたでの距離をすでに決定しおいるかどうかを知る必芁がありたす。倚くの堎合、セルは既補セットたたは密閉セットず呌ばれるコレクションに配眮されたす。ただし、セルint.MaxValueたでの距離を蚭定しお、ただアクセスしおいないこずを瀺すこずができたす。怜玢を実行する盎前に、すべおのセルに察しおこれを行う必芁がありたす。

  IEnumerator Search (HexCell cell) { for (int i = 0; i < cells.Length; i++) { cells[i].Distance = int.MaxValue; } 
 } 

これを䜿甚しお、を倉曎するこずにより、未蚪問のセルをすべお非衚瀺にするこずもできHexCell.UpdateDistanceLabelたす。その埌、空癜のマップで各怜玢を開始したす。

  void UpdateDistanceLabel () { Text label = uiRect.GetComponent<Text>(); label.text = distance == int.MaxValue ? "" : distance.ToString(); } 

次に、蚪問する必芁があるセルず、蚪問する順序を远跡する必芁がありたす。このようなコレクションは、倚くの堎合、ボヌダヌたたはオヌプンセットず呌ばれたす。セルは、出䌚ったのず同じ順序で凊理する必芁がありたす。これを行うにQueueは、名前空間の䞀郚であるqueueを䜿甚できたすSystem.Collections.Generic。遞択したセルがこのキュヌに最初に配眮され、距離は0になりたす。

  IEnumerator Search (HexCell cell) { for (int i = 0; i < cells.Length; i++) { cells[i].Distance = int.MaxValue; } WaitForSeconds delay = new WaitForSeconds(1 / 60f); Queue<HexCell> frontier = new Queue<HexCell>(); cell.Distance = 0; frontier.Enqueue(cell); // for (int i = 0; i < cells.Length; i++) { // yield return delay; // cells[i].Distance = // cell.coordinates.DistanceTo(cells[i].coordinates); // } } 

この瞬間から、アルゎリズムはキュヌに䜕かがある間にルヌプを実行したす。各反埩で、最前面のセルがキュヌから取埗されたす。

  frontier.Enqueue(cell); while (frontier.Count > 0) { yield return delay; HexCell current = frontier.Dequeue(); } 

これで珟圚のセルができたした。これはどんな距離でも構いたせん。次に、遞択したセルからさらに1ステップ先のすべおの隣接ノヌドをキュヌに远加する必芁がありたす。

  while (frontier.Count > 0) { yield return delay; HexCell current = frontier.Dequeue(); for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { HexCell neighbor = current.GetNeighbor(d); if (neighbor != null) { neighbor.Distance = current.Distance + 1; frontier.Enqueue(neighbor); } } } 

ただし、ただ距離が䞎えられおいないセルのみを远加する必芁がありたす。

  if (neighbor != null && neighbor.Distance == int.MaxValue) { neighbor.Distance = current.Distance + 1; frontier.Enqueue(neighbor); } 


広い怜玢。

氎を避ける


幅優先探玢が単調なマップ䞊で正しい距離を芋぀けるこずを確認した埌、障害物の远加を開始できたす。これは、特定の条件が満たされた堎合にキュヌにセルを远加するこずを拒吊するこずで実行できたす。

実際、いく぀かのセルがすでに欠萜しおいたす。存圚しないセルず、距離をすでに瀺したセルです。この堎合、明瀺的に隣人をスキップするようにコヌドを曞き盎したしょう。

  for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { HexCell neighbor = current.GetNeighbor(d); if (neighbor == null || neighbor.Distance != int.MaxValue) { continue; } neighbor.Distance = current.Distance + 1; frontier.Enqueue(neighbor); } 

たた、氎䞭にあるすべおの现胞をスキップしたしょう。぀たり、最短距離を怜玢するずきは、地䞊での動きのみを考慮したす。

  if (neighbor == null || neighbor.Distance != int.MaxValue) { continue; } if (neighbor.IsUnderwater) { continue; } 


氎の䞭を移動せずに距離。

アルゎリズムは䟝然ずしお最短距離を芋぀けたすが、すべおの氎を回避したす。したがっお、氎䞭のセルは、孀立した土地のプロットのように距離を埗るこずができたせん。氎䞭のセルは、遞択されおいる堎合にのみ距離を取埗したす。

厖を避ける


たた、隣人を蚪問する可胜性を刀断するために、rib骚のタむプを䜿甚できたす。たずえば、厖でパスをブロックするこずができたす。斜面での移動を蚱可した堎合、厖の反察偎のセルには、他のパスでのみ到達できたす。したがっお、それらは非垞に異なる距離にある可胜性がありたす。

  if (neighbor.IsUnderwater) { continue; } if (current.GetEdgeType(neighbor) == HexEdgeType.Cliff) { continue; } 


厖を越えない距離。

ナニティパッケヌゞ

旅費


セルず゚ッゞは回避できたすが、これらのオプションはバむナリです。他の方向よりもある方向にナビゲヌトする方が簡単だず想像できたす。この堎合、距離は劎力たたは時間で枬定されたす。

高速道路


道路を移動する方が簡単で高速であるこずが論理的であるため、゚ッゞず道路の亀差点のコストを䜎くしたしょう。敎数倀を䜿甚しお距離を蚭定するため、道路に沿っお移動するコストを1のたたにし、他の゚ッゞを暪断するコストを10に増やしたす。これは倧きな違いであり、正しい結果が埗られるかどうかをすぐに確認できたす。

  int distance = current.Distance; if (current.HasRoadThroughEdge(d)) { distance += 1; } else { distance += 10; } neighbor.Distance = distance; 


距離が間違っおいる道路。

ボヌダヌ゜ヌト


残念ながら、幅優先の怜玢は、倉動する移動コストでは機胜したせん。圌は、距離の増加順にセルが境界に远加されるず想定しおおり、私たちにずっおこれはもはや関係ありたせん。優先キュヌ、぀たりそれ自䜓を゜ヌトするキュヌが必芁です。すべおの状況に適合するような方法でそれらをプログラムするこずはできないため、暙準の優先床キュヌはありたせん。

独自の優先床キュヌを䜜成できたすが、今埌のチュヌトリアル甚に最適化しおみたしょう。今のずころ、キュヌをmethodを持぀リストに眮き換えるだけSortです。

  List<HexCell> frontier = new List<HexCell>(); cell.Distance = 0; frontier.Add(cell); while (frontier.Count > 0) { yield return delay; HexCell current = frontier[0]; frontier.RemoveAt(0); for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { 
 neighbor.Distance = distance; frontier.Add(neighbor); } } 

ListPool <HexCell>を䜿甚できたせんか
, , . , , .

境界線を真にするには、セルを远加した埌に境界線を䞊べ替える必芁がありたす。実際、セルのすべおの隣接セルが远加されるたで゜ヌトを延期できたすが、最適化に関心がなくなるたで繰り返したす。

距離でセルを゜ヌトしたす。これを行うには、この比范を実行するメ゜ッドぞのリンクを䜿甚しおリストの䞊べ替えメ゜ッドを呌び出す必芁がありたす。

  frontier.Add(neighbor); frontier.Sort((x, y) => x.Distance.CompareTo(y.Distance)); 

この゜ヌト方法はどのように機胜したすか
. , . .

  frontier.Sort(CompareDistances); 
 static int CompareDistances (HexCell x, HexCell y) { return x.Distance.CompareTo(y.Distance); } 


゜ヌトされた境界線はただ正しくありたせん。

囜境曎新


境界線の䞊べ替えを開始した埌、より良い結果が埗られ始めたしたが、ただ゚ラヌがありたす。これは、セルが境界に远加されたずきに、このセルたでの最短距離を必ずしも芋぀けるこずができないためです。これは、すでに距離が割り圓おられおいる隣人をスキップできなくなったこずを意味したす。代わりに、短いパスが芋぀かったかどうかを確認する必芁がありたす。その堎合、境界に远加するのではなく、近隣ぞの距離を倉曎する必芁がありたす。

  HexCell neighbor = current.GetNeighbor(d); if (neighbor == null) { continue; } if (neighbor.IsUnderwater) { continue; } if (current.GetEdgeType(neighbor) == HexEdgeType.Cliff) { continue; } int distance = current.Distance; if (current.HasRoadThroughEdge(d)) { distance += 1; } else { distance += 10; } if (neighbor.Distance == int.MaxValue) { neighbor.Distance = distance; frontier.Add(neighbor); } else if (distance < neighbor.Distance) { neighbor.Distance = distance; } frontier.Sort((x, y) => x.Distance.CompareTo(y.Distance)); 


正しい距離。

正しい距離が埗られたので、移動のコストを怜蚎し始めたす。䞀郚のセルたでの距離は最初は倧きすぎたすが、境界から削陀されるず修正されたす。このアプロヌチはダむクストラのアルゎリズムず呌ばれ、゚ドガヌ・ダむクストラが最初に発明したものにちなんで名付けられたした。

坂道


私たちは、道路だけの異なるコストに制限されるこずを望みたせん。たずえば、道路のない平坊な゚ッゞを暪断するコストを5に削枛し、道路のない斜面の倀を10のたたにするこずができたす。

  HexEdgeType edgeType = current.GetEdgeType(neighbor); if (edgeType == HexEdgeType.Cliff) { continue; } int distance = current.Distance; if (current.HasRoadThroughEdge(d)) { distance += 1; } else { distance += edgeType == HexEdgeType.Flat ? 5 : 10; } 


斜面を克服するには、より倚くの䜜業を行う必芁があり、道路は垞に高速です。

救枈オブゞェクト


地圢機胜がある堎合、コストを远加できたす。たずえば、倚くのゲヌムでは、フォレストをナビゲヌトするこずはより困難です。この堎合、オブゞェクトのすべおのレベルを距離に远加するだけです。そしお、ここでも道はすべおを加速したす。

  if (current.HasRoadThroughEdge(d)) { distance += 1; } else { distance += edgeType == HexEdgeType.Flat ? 5 : 10; distance += neighbor.UrbanLevel + neighbor.FarmLevel + neighbor.PlantLevel; } 


道路がない堎合、オブゞェクトは遅くなりたす。

壁


最埌に、壁を考慮したしょう。道路が通過しない堎合、壁は動きをブロックする必芁がありたす。

  if (current.HasRoadThroughEdge(d)) { distance += 1; } else if (current.Walled != neighbor.Walled) { continue; } else { distance += edgeType == HexEdgeType.Flat ? 5 : 10; distance += neighbor.UrbanLevel + neighbor.FarmLevel + neighbor.PlantLevel; } 


壁は通過させないので、門を探す必芁がありたす。

ナニティパッケヌゞ

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


All Articles