メインコンテンツまでスキップ

カスタムタイルタイプ

Source / Studio エディション限定

このページは、Source / Studio エディションのライセンシーだけが利用できる、エンジンの内部コードを説明しています。

World Gen タブ

タイルもデータです。下のジェネレーターは、手でペイントするのと同じタイルマップのアセットを 書き込みます。

MCE は、Unity の Tilemap システムを、すべてのタイルアセットを移動タイプとバトルシナリオに対応づける独自のデータレイヤーとともに使います。このページでは、タイルシステムのアーキテクチャと、カスタムのタイル挙動で拡張する方法を扱います。

アーキテクチャの概要​

タイルシステムには 3 つのレイヤーがあります。

  1. TileType enum -- GridController のための移動の意味づけを定義します。
  2. TileData / SingleTileData -- 各 TileBase アセットを TileType と BattleScenario に対応づける ScriptableObject のデータベース。
  3. タイルの挙動 -- タイルの GameObject に置かれ、ICharacterInteractingTile または ITileTypeOverride を実装して実行時のロジックを加える MonoBehaviour コンポーネント。
TileBase (Unity asset)
└─ registered in TileData ─► SingleTileData { Tile, TileType, BattleScenario }
└─ instantiated object ─► MonoBehaviour(s) implementing ICharacterInteractingTile

TileType enum​

Assets/OpenMon/Core/Runtime/World/TileType.cs にあります。

public enum TileType
{
NonExistent, // Tile has no data -- treated as void
Walkable, // Normal ground the player can traverse
NonWalkable, // Solid obstacle (walls, trees, rocks)
Jumpable, // Ledge the player can jump over (one-way)
Bridge, // Bridge surface (layered above water)
BridgeEntrance, // Transition tile between ground and bridge
Water, // Swimmable/surfable water
Waterfall, // Vertical water the player can climb
Slippery, // Ice -- forces movement until hitting an obstacle
WalkableNotBikable // Walkable on foot but not on a bicycle
}

GridController.GetTypeOfTileDirectlyBelowSortOrder() は、ソート順の高いものから低いものへタイルマップを走査し、まず ITileTypeOverride コンポーネントを確認し、次に TileData の検索へフォールバックすることで、任意のワールド座標の実効タイプを解決します。

新しい TileType の値を追加する​

  1. TileType enum に値を追加します(例:Quicksand)。
  2. MCECharacterController の移動ロジック(タイルタイプで分岐する switch/if のブロック)で処理します。
  3. そのタイプのタイルを、Inspector で TileData アセットに登録します。

TileData システム​

TileData(Assets/OpenMon/Core/Runtime/World/TileData.cs)は、プロジェクト内のすべてのタイルを保持する ScriptableObject です。

[CreateAssetMenu(menuName = "MCE/Maps/TileData", fileName = "TileData")]
public class TileData : OpenMonScriptable<TileData>
{
[SerializeField]
private List<SingleTileData> TileDataCollection = new();
}

各エントリは SingleTileData です。

[Serializable]
public class SingleTileData
{
public TileBase Tile; // Reference to the Unity tile asset
public TileType Type; // Movement semantics
public BattleScenario BattleScenario; // Visual theme for battles on this tile
}

エディターユーティリティ​

TileData は、(NovaInspector の [Button] を通じて)いくつかの Inspector ボタンを公開しています。

ボタン目的
AddGroup同じタイプとバトルシナリオを持つタイルのリストを一括追加する
Populateプロジェクト内のすべての TileBase アセットを走査し、足りないものを追加する(NonExistent としてマーク)
CheckInvalidまだ NonExistent のままか、BattleScenario が欠けているタイルについてエラーをログに出す
RemoveCollidersすべての RuleTile エントリから物理コライダーを取り除く(MCE は独自のグリッド衝突を使う)
RemoveNullsタイルアセットが削除されたエントリを整理する

検索のパフォーマンス​

TileData は、初回アクセス時に構築される Dictionary<TileBase, SingleTileData> のキャッシュを使います。未知のタイルは(missingTileCache を通じて)一度だけ警告をログに出し、歩行可能として扱われます。

SiblingTile(隣接タイルと接続するオートタイル)​

SiblingTile(Assets/OpenMon/Core/Runtime/World/Tiles/SiblingTile.cs)は、Unity の RuleTile を拡張して、他のタイルタイプと視覚的に接続するタイル -- たとえば、土に溶け込む草のタイルや、道のタイルと合流する小道 -- をサポートします。

[CreateAssetMenu(menuName = "2D/Tiles/SiblingTile", fileName = "SiblingTile")]
public class SiblingTile : RuleTile<SiblingTile.Neighbor>
{
[SerializeField]
private List<TileBase> Siblings = new();

[SerializeField]
private bool DefaultIncludesSibling = true;

public class Neighbor : RuleTile.TilingRule.Neighbor
{
public const int Sibling = 3; // Custom neighbor constant
}
}

仕組み​

SiblingTile は、Unity 組み込みの「This」と「Not This」に加えて、3 つ目の隣接マッチングモードを追加します。

隣接の値意味
Sibling (3)隣のタイルが Siblings リストにあればマッチする
Not This (2)DefaultIncludesSibling が true の場合、兄弟も除外する
デフォルト (1 / その他)DefaultIncludesSibling が true の場合、兄弟も「This」としてマッチする

RuleMatch のオーバーライド:

public override bool RuleMatch(int neighbor, TileBase tile) =>
neighbor switch
{
Neighbor.Sibling => Siblings.Contains(tile),
2 => DefaultIncludesSibling && !Siblings.Contains(tile) && base.RuleMatch(neighbor, tile),
_ => (DefaultIncludesSibling && Siblings.Contains(tile)) || base.RuleMatch(neighbor, tile)
};

SiblingTile を作成する​

  1. Project ウィンドウで右クリックして、2D > Tiles > SiblingTile を選びます。
  2. Inspector で、標準の RuleTile とまったく同じようにタイリングルールを設定します。
  3. Siblings リストに、このタイルが接続すべきタイルアセットをドラッグします。
  4. デフォルトの緑の矢印(隣接 = 「This」)が兄弟のタイルにもマッチすべき場合は、DefaultIncludesSibling を有効にします。これは、地形の溶け込みで最も一般的な設定です。
  5. 兄弟の隣接に対してだけ発動するルールが必要なときは、タイリングルールで Sibling の隣接定数を使います。

タイルの挙動インターフェース​

ICharacterInteractingTile​

キャラクターと実行時にやり取りする必要があるタイルは、ICharacterInteractingTile を実装します。MCECharacterController は、移動中にこれらのフックを呼び出します。

public interface ICharacterInteractingTile
{
bool HasBlockingInteraction();

// Async (coroutine) hooks -- called during movement animation
IEnumerator CharacterAboutToEnterTileAsync(MCECharacterController controller);
IEnumerator CharacterEnterTileAsync(MCECharacterController controller);
IEnumerator CharacterAboutToLeaveTileAsync(MCECharacterController controller);
IEnumerator CharacterLeftTileAsync(MCECharacterController controller);

// Callback hooks -- can cancel movement or suppress wild encounters
IEnumerator CharacterAboutToEnterTile(MCECharacterController controller, Action<bool, bool> finished);
IEnumerator CharacterEnterTile(MCECharacterController controller, Action<bool> finished);
IEnumerator CharacterAboutToLeaveTile(MCECharacterController controller, Action<bool> finished);
IEnumerator CharacterLeftTile(MCECharacterController controller, Action<bool> finished);
}

コールバック版は、Action<bool>(または Action<bool, bool>)を受け取り、キャラクターが移動を続けられるか、野生エンカウントを発生させるかを、タイルが制御できるようにします。

ITileTypeOverride​

このインターフェースを実装すると、状況(位置、ソート順、ゲーム状態)に応じて、実行時に TileType を上書きできます。

public interface ITileTypeOverride
{
TileType GetOverride(GridController gridController,
Tilemap tilemap,
TileBase tile,
Vector3Int tilePosition,
int sortOrder);
}

最も単純な実装は、固定のタイプを返す TileTypeOverride です。

public class TileTypeOverride : OpenMonBehaviour<TileTypeOverride>, ITileTypeOverride
{
[SerializeField]
private TileType Override;

public TileType GetOverride(...) => Override;
}

組み込みのタイル挙動​

MCE には、リファレンス実装として使える、いくつかのタイル挙動が同梱されています。

クラス目的
TallGrassキャラクターが通り抜けるときにスプライトのソート順を調整し、進入時に VFX を再生し、キャラクターの影を切り替える
ForceCharacterToKeepMovingある方向への連続した移動を強制する(ベルトコンベア、海流)。HasBlockingInteraction() => true を持つ
OneWayJumpableTile段差のジャンプを 1 方向に制限する
BlockMovementDirectionsタイルから特定の方向への移動を妨げる
LadderUp縦方向のはしごの昇降のために、スプライトのソート順を調整する
EncounterTile特定の EncounterType を持つ野生エンカウントゾーンとしてタイルをマークする
TileTypeOverride実行時に TileType を上書きする
TileTypeOfTileBelowソート順で真下にあるタイルからタイプを継承する
WaterfallEdge滝の端の挙動を扱う
WhirlwindInDirectionキャラクターを風の向きに押す

カスタムのタイル挙動を作成する​

プレイヤーが踏み込むとしばらく足止めし、野生エンカウントを抑制する「流砂」タイルを作る、ステップごとの例を示します。

ステップ 1:MonoBehaviour を作成する​

using System;
using System.Collections;
using UnityEngine;
using OpenMon.Foundation.Behaviours;
using MCECharacterController = OpenMon.MCE.Characters.MCECharacterController;

namespace OpenMon.MCE.World.Tiles
{
public class QuicksandTile : OpenMonBehaviour<QuicksandTile>, ICharacterInteractingTile
{
// The hook below waits, so the tile has to say it has a blocking interaction:
// the character controller only calls the callback hooks of such tiles.
public bool HasBlockingInteraction() => true;

// The character has just stepped in: hold it for half a second, then tell the
// controller not to trigger wild encounters on this tile (false = no encounter).
public IEnumerator CharacterEnterTile(MCECharacterController controller, Action<bool> finished)
{
yield return new WaitForSeconds(0.5f);
finished.Invoke(false);
}

// Remaining hooks -- no-op
public IEnumerator CharacterAboutToEnterTileAsync(MCECharacterController c) { yield break; }
public IEnumerator CharacterEnterTileAsync(MCECharacterController c) { yield break; }
public IEnumerator CharacterAboutToLeaveTileAsync(MCECharacterController c) { yield break; }
public IEnumerator CharacterLeftTileAsync(MCECharacterController c) { yield break; }
public IEnumerator CharacterAboutToEnterTile(MCECharacterController c, Action<bool, bool> f) { yield break; }
public IEnumerator CharacterAboutToLeaveTile(MCECharacterController c, Action<bool> f) { yield break; }
public IEnumerator CharacterLeftTile(MCECharacterController c, Action<bool> f) { yield break; }
}
}

ステップ 2:タイルのプレハブにアタッチする​

  1. 流砂にするタイルアセットを作成または編集します。
  2. タイルの Game Object のインスタンス化が有効になっていることを確認します(RuleTile > Default Game Object、またはルールごと)。
  3. そのプレハブに QuicksandTile コンポーネントをアタッチします。

ステップ 3:TileData に登録する​

  1. Inspector で TileData アセットを開きます。
  2. Populate ボタンを使って、新しいタイルアセットを検出します。
  3. タイルの TileType を Walkable に設定します(特別な移動ロジックが必要なら、カスタムの enum 値を追加します)。
  4. ビジュアルテーマに適した BattleScenario を割り当てます。

ステップ 4:Tilemap にペイントする​

標準の Unity の Tile Palette を使って、適切なタイルマップのレイヤーにタイルをペイントします。GridController が自動的にタイルデータを拾い、ゲームプレイ中にあなたの挙動のフックを呼び出します。