Правильная отрисовка поля 10х10 для игры типа Морского боя

Игра, которую я хочу создать, – Лабиринт. Кратко суть: 2 поля от морского боя 10х10, игроки ходят по очереди: могут передвигаться из одной клетки поля в любую соседнюю, строя "маршрут" всех посещенных клеток.

Проблема заключается в следующем: как правильно реализовать именно само поле? Идея в лоб, которую я вижу: спрайт поля Field, в нем 100 дочерних объектов FieldCell с коллайдерами и соответствующим скриптом. Однако еще нужно, чтобы игрок мог расставлять стены лабиринта, нажимая на его стенки. На поле 10х10 всего 81 стенка (не считая крайних). Получается всего: 2*(1+100+81)=364 объекта, что, на мой взгляд, очень много => подход неверен.

Как же тогда реализовать подобное? Может быть, можно как-то получать точное место нажатия на картинку относительно самой картинки, а не экрана/мира...


Ответы (1 шт):

Автор решения: Yaroslav

Если все картинки тайлов поля в одном атласе, то они рендерится за 1 drawcall и по идее сотня не проблема. Для чего тайлам нужны скрипты не понимаю, сотни коллайдеров (не понимаю для чего) и возможно сотни Update это действительно грузно.

Получаем координаты клика:

private Plane _planeXY = new Plane(Vector3.back, 0);

private Vector3 GetPlanePosition (Vector3 mousePosition)
{
    Ray Ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    float enter = 0.0f;
    if (_planeXY.Raycast(Ray, out enter))
        return Ray.GetPoint(enter);
    return Vector3.zero;
}

Field спокойно может определить кликнули ли по нему и по какому тайлу:

[DisallowMultipleComponent]

public class FieldLogic : MonoBehaviour
{
    public event Action<Vector2Int> TileChanged;
    [SerializeField] private Vector2Int _size = new Vector2Int(10, 10);

    public Vector2Int Size => _size;

    public void OnTileClick (Vector2Int index)
    {
        TileChanged?.Invoke(index);
    }

}

[DisallowMultipleComponent]
[RequireComponent(typeof(FieldLogic))]

public class FieldView : MonoBehaviour
{
    [SerializeField] private int _tileSize = 32;
    private SpriteRenderer[,] _tileSprites;

    public int TileSize => _tileSize;

    private void Start ()
    {
        FieldLogic Logic = GetComponent<FieldLogic>();
        Logic.TileChanged += OnTileChange;
        SpawnSprites(Logic.Size);
    }

    private void SpawnSprites (Vector2Int size)
    {
        Transform Parent = transform;
        _tileSprites = new SpriteRenderer[size.x, size.y];
        Vector3 Ancore = size*0.5f;
        for (int x = 0; x < size.x; x++)
            for (int y = 0; y < size.y; y++)
            {
                GameObject NewTile = new GameObject();
                Transform TileTransform = NewTile.transform;
                TileTransform.parent = Parent;
                TileTransform.localPosition = Ancore+new Vector3(_tileSize*x, _tileSize*y);
                _tileSprites[x, y] = NewTile.AddComponent<SpriteRenderer>();
            }
    }

    private void OnTileChange (Vector2Int index)
    {

    }
}

[DisallowMultipleComponent]
[RequireComponent(typeof(FieldView))]
[RequireComponent(typeof(FieldLogic))]

public class FieldInteractive : MonoBehaviour
{
    private Transform _transform;
    private FieldView _view;
    private FieldLogic _logic;

    private void Start ()
    {
        _transform = transform;
        _view = GetComponent<FieldView>();
        _logic = GetComponent<FieldLogic>();
    }

    public void OnClick (Vector2 position)
    {
        if (HitField(position))
        {
            Vector2Int TileIndex = GetHitTileIndex(position);
            _logic.OnTileClick(TileIndex);
        }
    }

    private Vector2Int GetHitTileIndex (Vector2 position)
    {
        float TileSize = _view.TileSize;
        position -= (Vector2)_transform.position;
        return new Vector2(Mathf.FloorToInt(position.x/TileSize), Mathf.FloorToInt(position.y/TileSize));
    }

    private bool HitField (Vector2 position)
    {
        Vector2Int FieldSize = _logic.Size;
        float TileSize = _view.TileSize;
        Rect FieldRect = new Rect(_transform.position, FieldSize*TileSize);
        return FieldRect.Contains(position);
    }
}

FieldView отвечает только за визуал, FieldInteractive за тачи. Бизнес логикой занимается FieldLogic. interactive посылает события клика по клетке, а view тупо реагирует на ивенты скрипта логики и обновляется сам.

→ Ссылка