Создание 2D карты мира для глобальной стратегии UNITY

Не так давно я решился сделать игру-стратегию в unity и столкнулся с одной проблемой. Я не понимаю как красить карту и выделять границу особой линией. Допустим есть такая карта: Я хочу по нажатии на провинции прекрашивать их в определенный цвет и перемещать линию границы (можно просто линию другого цвета). И желательно не через делёжку картинки на сотни спрайтов. Буду очень благодарен если наведёте меня на верный путь по решению этой проблемы.
Отслеживать
задан 27 мая 2021 в 10:44
55 8 8 бронзовых знаков
Буду очень благодарен если наведёте меня на верный путь по решению этой проблемы. — не надо такое писать, если наводка на верный путь вам на самом деле не нужна или не устраивает. Я понял, что вы пришли сюда не за подсказкой, а наверное за решением, поэтому удалил ответ как бесполезный. Собственно вы его и не приняли.
27 мая 2021 в 14:49
@aepot Я вам и слова не сказал про то, что меня что-то не устраивает, и решения у вас НИ РАЗУ не просил. А про бесполезность соглашусь.
27 мая 2021 в 14:56
Оффтоп вопрос. А как ты сделал карту мира с разделением на страны? Процедурная генерация?
7 фев в 10:07
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Не много подумав и покопавшись я решил эту проблему путём создания множества Polygon Colider 2D и его преобразование в MESH, а сам mesh легко красится через материал и вышел такой скрипт:
using UnityEngine; using System.Collections; using System.Collections.Generic; /// /// /// /// Source: https://forum.unity3d.com/threads/trying-extrude-a-2d-polygon-to-create-a-mesh.102629/ [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(PolygonCollider2D))] public class color : MonoBehaviour < public Color extrudeColor = Color.white; public float frontDistance = -0.249f; public float backDistance = 0.249f; void Start() < PolygonCollider2D pol = GetComponent(); Mesh m = CreateMesh(pol.points, frontDistance, backDistance); GetComponent().sharedMesh = m; GetComponent().material.color = extrudeColor; pol.isTrigger = true; pol.enabled = false; > private static Mesh CreateMesh(Vector2[] poly, float frontDistance = -10, float backDistance = 10) < frontDistance = Mathf.Min(frontDistance, 0); backDistance = Mathf.Max(backDistance, 0); // convert polygon to triangles Triangulator triangulator = new Triangulator(poly); int[] tris = triangulator.Triangulate(); Mesh m = new Mesh(); Vector3[] vertices = new Vector3[poly.Length * 2]; for (int i = 0; i < poly.Length; i++) < vertices[i].x = poly[i].x; vertices[i].y = poly[i].y; vertices[i].z = frontDistance; // front vertex vertices[i + poly.Length].x = poly[i].x; vertices[i + poly.Length].y = poly[i].y; vertices[i + poly.Length].z = backDistance; // back vertex >int[] triangles = new int[tris.Length * 2 + poly.Length * 6]; int count_tris = 0; for (int i = 0; i < tris.Length; i += 3) < triangles[i] = tris[i]; triangles[i + 1] = tris[i + 1]; triangles[i + 2] = tris[i + 2]; >// front vertices count_tris += tris.Length; for (int i = 0; i < tris.Length; i += 3) < triangles[count_tris + i] = tris[i + 2] + poly.Length; triangles[count_tris + i + 1] = tris[i + 1] + poly.Length; triangles[count_tris + i + 2] = tris[i] + poly.Length; >// back vertices count_tris += tris.Length; for (int i = 0; i < poly.Length; i++) < // triangles around the perimeter of the object int n = (i + 1) % poly.Length; triangles[count_tris] = i; triangles[count_tris + 1] = n; triangles[count_tris + 2] = i + poly.Length; triangles[count_tris + 3] = n; triangles[count_tris + 4] = n + poly.Length; triangles[count_tris + 5] = i + poly.Length; count_tris += 6; >m.vertices = vertices; m.triangles = triangles; m.RecalculateNormals(); m.RecalculateBounds(); m.Optimize(); return m; > > /// /// /// /// Source: http://wiki.unity3d.com/index.php?title=Triangulator public class Triangulator < private Listm_points = new List(); public Triangulator(Vector2[] points) < m_points = new List(points); > public int[] Triangulate() < Listindices = new List(); int n = m_points.Count; if (n < 3) return indices.ToArray(); int[] V = new int[n]; if (Area() >0) < for (int v = 0; v < n; v++) V[v] = v; >else < for (int v = 0; v < n; v++) V[v] = (n - 1) - v; >int nv = n; int count = 2 * nv; for (int m = 0, v = nv - 1; nv > 2;) < if ((count--) > indices.Reverse(); return indices.ToArray(); > private float Area() < int n = m_points.Count; float A = 0.0f; for (int p = n - 1, q = 0; q < n; p = q++) < Vector2 pval = m_points[p]; Vector2 qval = m_points[q]; A += pval.x * qval.y - qval.x * pval.y; >return (A * 0.5f); > private bool Snip(int u, int v, int w, int n, int[] V) < int p; Vector2 A = m_points[V[u]]; Vector2 B = m_points[V[v]]; Vector2 C = m_points[V[w]]; if (Mathf.Epsilon >(((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x)))) return false; for (p = 0; p < n; p++) < if ((p == u) || (p == v) || (p == w)) continue; Vector2 P = m_points[V[p]]; if (InsideTriangle(A, B, C, P)) return false; >return true; > private bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) < float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; float cCROSSap, bCROSScp, aCROSSbp; ax = C.x - B.x; ay = C.y - B.y; bx = A.x - C.x; by = A.y - C.y; cx = B.x - A.x; cy = B.y - A.y; apx = P.x - A.x; apy = P.y - A.y; bpx = P.x - B.x; bpy = P.y - B.y; cpx = P.x - C.x; cpy = P.y - C.y; aCROSSbp = ax * bpy - ay * bpx; cCROSSap = cx * apy - cy * apx; bCROSScp = bx * cpy - by * cpx; return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); > >
UPD: Этот скрипт гораздо чувствительнее к polygon colider 2d, относительно прежнего.
Плюс нашёл скрипт для создания границ
using System.Collections; using System.Collections.Generic; using UnityEngine; public class border : MonoBehaviour < private void Start() < DrawPolygonCollider(gameObject.GetComponent()); > public static void DrawPolygonCollider(PolygonCollider2D collider) < LineRenderer _lr = collider.gameObject.AddComponent(); _lr.startWidth = 0.025f; _lr.endWidth = 0.025f; _lr.useWorldSpace = false; _lr.positionCount = collider.points.Length - 1; for (int i = 0; i < collider.points.Length -1; i++) < _lr.SetPosition(i, new Vector3(collider.points[i].x, collider.points[i].y)); >_lr.SetPosition(collider.points.Length, new Vector3(collider.points[0].x, collider.points[0].y)); > >

Получилось как-то так:
Создание пошаговой стратегии
Доброго времени суток, захотелось создать игру, пошаговую стратегию, но опыта в создании подобного нет.
Общая идея и базовая механика игры.
В игре есть 4 игрока у каждого из которых по 3+ юнита. В свой ход игрок может сделать ход каждым юнитом 1 раз.
Игровое поле представляет собой карту в форме острова. Вид изометрический. Каждая клетка скрыта туманом войны.
Юнит может ходить на 1-у клетку в любом направлении. Клеток несколько типов, те что с золотом(либо другой особый предмет), пустые, с ловушками, смертельные, специальные. Когда юнит ходит для него открывается клетка.
Смертельные 2-х видов, те, что убивают сразу, либо отбрасывают игрока на клетку назад. После того как смертельные клетки были открыты, то на них уже нельзя ходить.
Есть специальные клетки, став на которые игроку дается возможность ходить на определенные клетки. Есть клетки которые телепортируют игрока на определенную позицию. Есть ловушки, которые заставляют игрока пропустить несколько ходов, либо откупиться за спец. предмет и сделать ход.
Одна клетка может вмещать на себе всех юнитов одного игрока.
Юниты могут воевать между собой, например, если юнит занимает клетку, то при ходе другого юнита на нее, он отправляется на стартовую позицию.
Также юнит может взаимодействовать с предметами. Поднимать / сбрасывать.
Например подобрав монету, юнит не может исследовать новые клетки.
Другие, игроки это боты, управляемые компьютером.
Вообще цель игры — собрать как можно больше золота с острова и удрать. Собирать золото нужно для прокачки, чтобы потом заходить на карту, например с каким то снаряжением, которое позволяет нести не 1-у монету, а 2-е. Тут можно много чего придумать. Так же игрок может потерять предметы в игре. То что он теряет можно только купить за золото либо забрать у бота в игре.
Есть вопросы по технической реализации.
1. Туман войны. Когда юнит ходит на клетку, то она открывается и происходит какое то действие, в зависимости от типа клетки, описывал выше. Когда на карте открывают клетку с золотом, то она становится видна всем.
2. Генерация карты. Здесь хотелось бы сделать как процедурную генерацию, так и оставить возможность создавать карту вручную. Допустим есть сложность препятствий, легкие, средние, тяжелые. Все они должны расставляться по принципу, чем сложнее тем ближе к центру карты.
3. Передвижения юнита: как определять возможные ходы юнита, в зависимости от клетки, на которой он стоит. Например если юнит стал на клетку со стрелкой влево, то он может сделать ход еще раз, но только по направлению стрелки.
4. Как можно реализовать ботов в игре, они не должны быть сложными, типа нейросети, но должно быть интересно.
Вообще говоря хотелось бы сделать расширяемую систему, чтобы при введении чего либо нового в игру, например препятствия, не приходилось переписывать все целиком.
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:
Создание стратегии
Всем привет! Недавно задался мыслёй о создании стратегии на андроид. Сама стратегия по задумке.
Наставник по созданию пошаговой стратегии на WPF
Нужен наставник/ментор по написанию простой пошаговой игрушки на wpf, рассмотрю также исполнителей.
Теория ходов стрелка в пошаговой стратегии
Почему-то захотелось написать пошаговую стратегию, гибрид Dota и тому подобного и шахмат для одного.
Ищу программиста Unity 3D для пошаговой стратегии
Доброго времени суток! Меня зовут Владимир. На конструкторе Game Maker 8.1 я сделал пошаговую.
Создание стратегии
Кто нибудь знает каким способом можно сделать выделение объекта мышкой (в квадратик как в стратегии.
629 / 41 / 25
Регистрация: 25.04.2017
Сообщений: 499
Скачал тестовый проект с офф репозитория юнити. Там используются компоненты юнити из коробки — Grid, Tilemap. Нашел несколько примеров кода, переделал все это дело так, что игрок двигается туда, куда кликают, при этом обходя стены. Но движения на большие расстояния не относятся к механике игры, это я больше делал для понимания алгоритма.
На компоненте Grid, висят 2 Tilemap с тегами Floor и Blocking. После построения карты вручную строится абстрактная модель данных, которой уже осуществляются вычисления. Компоненты Grid и Tilemap нужны чисто для отрисовки визуала.
Подскажите, правильно ли я иду?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
//Представление всех Tilemap в модели public class NodeGrid { public Grid tileGrid; public string wallTag = "Blocking"; public string floorTag = "Floor"; public Vector2Int vGridWorldSize; public Node[,] nodeArray; public int xOffset; public int yOffset; public NodeGrid(Grid tileGrid, string wallTag, string floorTag) { this.tileGrid = tileGrid; this.wallTag = wallTag; this.floorTag = floorTag; if (tileGrid != null && wallTag != "") CreateGrid(); } public void CreateGrid() { // Get all tilemaps Tilemap[] allTileMaps = tileGrid.GetComponentsInChildrenTilemap>(); // Check for largest tilemap int largestX = 0, largestY = 0; int lowestX = int.MaxValue, lowestY = int.MaxValue; foreach (Tilemap map in allTileMaps) { map.CompressBounds(); BoundsInt bounds = map.cellBounds; if (bounds.size.x > largestX) largestX = bounds.size.x; if (bounds.size.y > largestY) largestY = bounds.size.y; if (map.cellBounds.xMin lowestX) lowestX = bounds.xMin; if (map.cellBounds.yMin lowestY) lowestY = bounds.yMin; } // Setup variables nodeArray = new Node[largestX, largestY]; vGridWorldSize.x = largestX; vGridWorldSize.y = largestY; xOffset = lowestX * -1; yOffset = lowestY * -1; // Add nodes foreach (Tilemap map in allTileMaps) { foreach (var pos in map.cellBounds.allPositionsWithin) { if (!map.HasTile(pos)) continue; int gridPosX = pos.x + xOffset; int gridPosY = pos.y + yOffset; // Make new node if (nodeArray[gridPosX, gridPosY] == null) { // Check tag if (map.tag == wallTag) { nodeArray[gridPosX, gridPosY] = new Node(gridPosX, gridPosY, false); } else if (map.tag == floorTag) { nodeArray[gridPosX, gridPosY] = new Node(gridPosX, gridPosY, true); } } // Check existing node else if (map.tag == wallTag || nodeArray[gridPosX, gridPosY].isWalkable == false || map.tag != floorTag) { nodeArray[gridPosX, gridPosY].isWalkable = false; } } } } public int GetWidth() { return vGridWorldSize.x; } public int GetHeight() { return vGridWorldSize.y; } public Node GetGridObject(int x, int y) { if (x >= 0 && y >= 0 && x vGridWorldSize.x && y vGridWorldSize.y) { return nodeArray[x, y]; } else { return default(Node); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
public class Node { public int x; public int y; public int gCost; public int hCost; public int fCost; public bool isWalkable; public Node parent; public Node(int x, int y, bool isWalkable) { this.x = x; this.y = y; this.isWalkable = isWalkable; } public void CalculateFCost() { fCost = gCost + hCost; } public override string ToString() { return x + "," + y; } }
Выделение объектов / юнитов, как в стратегии
Выделение нескольких юнитов, как в стратегии или похожей игре, с помощью рамки, которую рисуем мышкой. Как это сделать? Сегодня решим данную проблему. На самом деле, у нас несколько вопросов, требующих решения. Во-первых, рисование прямоугольника или квадрата, путем перетаскивания курсора по диагонали, т.е. между стартовой и конечной позицией. Затем, нужно трансформировать позиции объектов/юнитов в пространство экрана, так как, рамка рисуется в нем. Следующий шаг, определить — находится юнит в рамке или нет. И чтобы иметь возможность обращаться к объектам, давая какие-нибудь например, команды, необходимо все выделенные в данный момент объекты, добавить в массив.
Без лишней болтологии, начнем. В начале, создаем скин для GUI, выбираем какую нибудь папку проекта, правый клик > Create > GUI Skin. В нем мы сможем настроить элемент Box, а сам скин незабываем добавить в скрипт.
Теперь, создаем C# скрипт SelectObjects:
using UnityEngine; using System.Collections; using System.Collections.Generic; public class SelectObjects : MonoBehaviour < public static Listunit; // массив всех юнитов, которых мы можем выделить public static List unitSelected; // массив выделенных юнитов public GUISkin skin; private Rect rect; private bool draw; private Vector2 startPos; private Vector2 endPos; void Awake () < unit = new List(); unitSelected = new List(); > // проверка, добавлен объект или нет bool CheckUnit (GameObject unit) < bool result = false; foreach(GameObject u in unitSelected) < if(u == unit) result = true; >return result; > void Select() < if(unitSelected.Count >0) < for(int j = 0; j < unitSelected.Count; j++) < // делаем что-либо с выделенными объектами unitSelected[j].GetComponent().material.color = Color.red; > > > void Deselect() < if(unitSelected.Count >0) < for(int j = 0; j < unitSelected.Count; j++) < // отменяем то, что делали с объектоми unitSelected[j].GetComponent().material.color = Color.white; > > > void OnGUI () < GUI.skin = skin; GUI.depth = 99; if(Input.GetMouseButtonDown(0)) < Deselect(); startPos = Input.mousePosition; draw = true; >if (Input.GetMouseButtonUp(0)) < draw = false; Select(); >if(draw) < unitSelected.Clear(); endPos = Input.mousePosition; if(startPos == endPos) return; rect = new Rect(Mathf.Min(endPos.x, startPos.x), Screen.height - Mathf.Max(endPos.y, startPos.y), Mathf.Max(endPos.x, startPos.x) - Mathf.Min(endPos.x, startPos.x), Mathf.Max(endPos.y, startPos.y) - Mathf.Min(endPos.y, startPos.y) ); GUI.Box(rect, ""); for(int j = 0; j < unit.Count; j++) < // трансформируем позицию объекта из мирового пространства, в пространство экрана Vector2 tmp = new Vector2(Camera.main.WorldToScreenPoint(unit[j].transform.position).x, Screen.height - Camera.main.WorldToScreenPoint(unit[j].transform.position).y); if(rect.Contains(tmp)) // проверка, находится-ли текущий объект в рамке < if(unitSelected.Count == 0) < unitSelected.Add(unit[j]); >else if(!CheckUnit(unit[j])) < unitSelected.Add(unit[j]); >> > > > >
Как видно, в ключевых местах есть комментарий, поэтому разобраться что и как не должно составить особого труда.
Внимание! Чтобы всё работало, все активные объекты должны быть добавлены в массив unit. Соответственно в вашем скрипте, который есть на юнитах, в функцию Start необходимо добавить строчку:
SelectObjects.unit.Add(gameObject);
Для большей наглядности, вы можете скачать готовый проект по теме:
Где можно найти пример или инструкцию по написанию пошаговой стратегии в unity3d?
Здравствуйте , сейчас пишу для себя пошаговую стратегию на подобии герои меча и магии , может кто знает где можно найти пример или туториал по написанию подобной игры , в частности интересует организация сетки на поле, т.к. вокруг неё всё и вертится.
- Вопрос задан более трёх лет назад
- 17611 просмотров
Комментировать
Решения вопроса 2

Даниил Басманов @BasmanovDaniil
Геймдизайнер-телепат
Три минуты гугления дали эту ссылку: https://tbswithunity3d.wordpress.com/
Если вы задаётесь такими вопросами, может быть вам стоит выбрать игру попроще?
Ответ написан более трёх лет назад
Нравится 1 1 комментарий