Правильная отрисовка поля 10х10 для игры типа Морского боя
Игра, которую я хочу создать, – Лабиринт. Кратко суть: 2 поля от морского боя 10х10, игроки ходят по очереди: могут передвигаться из одной клетки поля в любую соседнюю, строя "маршрут" всех посещенных клеток.
Проблема заключается в следующем: как правильно реализовать именно само поле? Идея в лоб, которую я вижу: спрайт поля Field, в нем 100 дочерних объектов FieldCell с коллайдерами и соответствующим скриптом. Однако еще нужно, чтобы игрок мог расставлять стены лабиринта, нажимая на его стенки. На поле 10х10 всего 81 стенка (не считая крайних). Получается всего: 2*(1+100+81)=364 объекта, что, на мой взгляд, очень много => подход неверен.
Как же тогда реализовать подобное? Может быть, можно как-то получать точное место нажатия на картинку относительно самой картинки, а не экрана/мира...
Ответы (1 шт):
Если все картинки тайлов поля в одном атласе, то они рендерится за 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 тупо реагирует на ивенты скрипта логики и обновляется сам.