Перейти к основному содержимому

Пользовательские типы тайлов

Только редакции Source и Studio

Эта страница описывает внутренний код движка, доступный исключительно лицензиатам редакций Source и Studio.

Вкладка World Gen

Тайлы — тоже данные: генератор ниже записывает те же ассеты тайлмапа, которые вы нарисовали бы вручную.

MCE использует систему Tilemap Unity с собственным слоем данных, сопоставляющим каждый ассет тайла с типом движения и боевым сценарием. Эта страница охватывает архитектуру системы тайлов и способы её расширения пользовательским поведением тайлов.

Обзор архитектуры​

У системы тайлов три слоя:

  1. Перечисление TileType — задаёт семантику движения для GridController.
  2. TileData / SingleTileData — база данных ScriptableObject, сопоставляющая каждый ассет TileBase с TileType и BattleScenario.
  3. Поведения тайлов — компоненты MonoBehaviour на GameObject тайлов, реализующие ICharacterInteractingTile или ITileTypeOverride для добавления логики времени выполнения.
TileBase (Unity asset)
└─ registered in TileData ─► SingleTileData { Tile, TileType, BattleScenario }
└─ instantiated object ─► MonoBehaviour(s) implementing ICharacterInteractingTile

Перечисление TileType​

Находится в 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 (например, Quicksand).
  2. Обработайте его в логике движения MCECharacterController (блоки switch/if, ветвящиеся по типу тайла).
  3. Зарегистрируйте тайлы этого типа в ассете TileData через Inspector.

Система 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 предоставляет несколько кнопок Inspector (через [Button] из NovaInspector):

КнопкаНазначение
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) расширяет RuleTile Unity, чтобы поддерживать тайлы, визуально соединяющиеся с другими типами тайлов, — например, тайл травы, переходящий в землю, или дорожку, сливающуюся с тайлами дороги.

[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»:

Значение соседаЗначение
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. Включите DefaultIncludesSibling, если зелёная стрелка по умолчанию (сосед = «This») также должна совпадать с «сестринскими» тайлами. Это самая распространённая настройка для плавного перехода местности.
  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Ограничивает прыжок через уступ одним направлением
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. Откройте ассет TileData в Inspector.
  2. Нажмите кнопку Populate, чтобы обнаружить новые ассеты тайлов.
  3. Задайте тайлу TileType равным Walkable (или добавьте собственное значение перечисления, если нужна особая логика движения).
  4. Назначьте подходящий BattleScenario для визуальной темы.

Шаг 4: Нарисуйте на Tilemap​

Используйте стандартную Tile Palette Unity, чтобы нарисовать тайл на нужном слое тайлмапа. GridController автоматически подхватит данные тайла и будет вызывать хуки вашего поведения во время игры.