/// <summary> /// Merge cell example. This function will make cell1 marge with a random cell from its neighbours. /// </summary> void MergeCell(Cell cell1) { int neighbourCount = cell1.region.neighbours.Count; if (neighbourCount==0) return; Cell cell2 = (Cell)cell1.region.neighbours[Random.Range(0, neighbourCount)].entity; tgs.CellMerge(cell1, cell2); tgs.Redraw(); }
void CellUpdateBounds(Cell cell) { cell.polygon = cell.region.polygon; List<Vector3> points = cell.polygon.contours[0].GetVector3Points(); cell.region.points = points; // Update bounding rect float minx, miny, maxx, maxy; minx = miny = float.MaxValue; maxx = maxy = float.MinValue; for (int p=0;p<points.Count;p++) { Vector3 point = points[p]; if (point.x<minx) minx = point.x; if (point.x>maxx) maxx = point.x; if (point.y<miny) miny = point.y; if (point.y>maxy) maxy = point.y; } cell.region.rect2D = new Rect(minx, miny, maxx-minx, maxy-miny); }
void SetupIrregularGrid() { Point[] centers = new Point[_numCells]; for (int k=0;k<centers.Length;k++) { centers[k] = new Point(UnityEngine.Random.Range (-0.49f, 0.49f), UnityEngine.Random.Range (-0.49f, 0.49f)); } VoronoiFortune voronoi = new VoronoiFortune (); for (int k=0;k<goodGridRelaxation;k++) { voronoi.AssignData (centers); voronoi.DoVoronoi (); if (k <goodGridRelaxation-1) { for (int j=0;j<_numCells;j++) { Point centroid = voronoi.cells[j].centroid; centers[j] = (centers[j] + centroid)/2; } } } // Make cell regions: we assume cells have only 1 region but that can change in the future float curvature = goodGridCurvature; for (int k=0; k<voronoi.cells.Length; k++) { VoronoiCell voronoiCell = voronoi.cells[k]; Cell cell = new Cell(k.ToString(), voronoiCell.center.vector3); Region cr = new Region (cell); if (curvature>0) { cr.polygon = voronoiCell.GetPolygon(3, curvature); } else { cr.polygon = voronoiCell.GetPolygon(1, 0); } if (cr.polygon!=null) { // Add segments for (int i=0;i<voronoiCell.segments.Count;i++) { Segment s = voronoiCell.segments[i]; if (!s.deleted) { if (curvature>0) { cr.segments.AddRange(s.subdivisions); } else { cr.segments.Add (s); } } } cell.polygon = cr.polygon.Clone(); cell.region = cr; cells.Add (cell); } } }
/// <summary> /// Must be called after changing one cell geometry. /// </summary> void UpdateCellGeometry(Cell cell, TGS.Geom.Polygon poly) { // Copy new polygon definition cell.region.polygon = poly; cell.polygon = cell.region.polygon.Clone(); // Update segments list cell.region.segments.Clear(); List<Segment>segmentCache = new List<Segment>(cellNeighbourHit.Keys); for (int k=0;k<poly.contours[0].points.Count;k++) { Segment s = poly.contours[0].GetSegment(k); bool found = false; // Search this segment in the segment cache for (int j=0;j<segmentCache.Count;j++) { Segment o = segmentCache[j]; if ((Point.EqualsBoth(o.start, s.start) && Point.EqualsBoth(o.end, s.end)) || (Point.EqualsBoth(o.end, s.start) && Point.EqualsBoth(o.start, s.end))) { cell.region.segments.Add (o); ((Cell)cellNeighbourHit[o].entity).territoryIndex = cell.territoryIndex; // updates the territory index of this segment in the cache found = true; break; } } if (!found) cell.region.segments.Add (s); } // Refresh neighbours CellsUpdateNeighbours(); // Refresh rect2D CellUpdateBounds(cell); // Refresh territories FindTerritoryFrontiers(); UpdateTerritoryBoundaries(); }
void SetupBoxGrid(bool strictQuads) { // Make cell regions: we assume cells have only 1 region but that can change in the future int l = _numCells; int qx, qy; int q = (int)(Math.Sqrt (l)); if (strictQuads) { qx=qy=q; } else { qx=l; qy=1; if (q<1) q=1; if ( (int)(q*q) != l) { // not squared if (!GetTwoFactors(l, out qx, out qy)) { // if number > 10 and it's prime, reduce by one so we can avoid ugly accordian grids if (l>10) GetTwoFactors(l-1, out qx, out qy); } } else { qx = qy = q; } } double stepX = (transform.localScale.y / transform.localScale.x) / qx; double stepY = (transform.localScale.x / transform.localScale.y) / qy; double halfStepX = stepX*0.5; double halfStepY = stepY*0.5; Segment [,,] sides = new Segment[qx,qy,4]; // 0 = left, 1 = top, 2 = right, 3 = bottom int c = -1; int subdivisions = goodGridCurvature > 0 ? 3: 1; for (int k=0;k<qx;k++) { for (int j=0;j<qy;j++) { Point center = new Point((double)k/qx-0.5+halfStepX,(double)j/qy-0.5+halfStepY); Cell cell = new Cell( (++c).ToString(), new Vector2((float)center.x, (float)center.y)); Segment left = k>0 ? sides[k-1, j, 2] : new Segment(center.Offset(-halfStepX, -halfStepY), center.Offset(-halfStepX, halfStepY), true); sides[k, j, 0] = left; Segment top = new Segment(center.Offset(-halfStepX, halfStepY), center.Offset(halfStepX, halfStepY), j==qy-1); sides[k, j, 1] = top; Segment right = new Segment(center.Offset(halfStepX, halfStepY), center.Offset(halfStepX, -halfStepY), k==qx-1); sides[k, j, 2] = right; Segment bottom = j>0 ? sides[k, j-1, 1] : new Segment(center.Offset(halfStepX, -halfStepY), center.Offset(-halfStepX, -halfStepY), true); sides[k, j, 3] = bottom; Region cr = new Region (cell); if (subdivisions>1) { cr.segments.AddRange (top.Subdivide(subdivisions, _gridCurvature)); cr.segments.AddRange (right.Subdivide(subdivisions, _gridCurvature)); cr.segments.AddRange (bottom.Subdivide(subdivisions, _gridCurvature)); cr.segments.AddRange (left.Subdivide(subdivisions, _gridCurvature)); } else { cr.segments.Add (top); cr.segments.Add (right); cr.segments.Add (bottom); cr.segments.Add (left); } Connector connector = new Connector(); connector.AddRange(cr.segments); cr.polygon = connector.ToPolygon(); // FromLargestLineStrip(); if (cr.polygon!=null) { cell.region = cr; cells.Add (cell); } } } }
void SetupHexagonalGrid() { // Make cell regions: we assume cells have only 1 region but that can change in the future int l = _numCells; int qx, qy; int q = (int)(Math.Sqrt (l)); q = q * 12 / 13; if (q<1) q= 1; qx=qy=q; int qx2 = qx * 4 / 3; double stepX = (transform.localScale.y / transform.localScale.x) / qx; double stepY = (transform.localScale.x / transform.localScale.y) / qy; double halfStepX = stepX*0.5; double halfStepY = stepY*0.5; Segment [,,] sides = new Segment[qx2,qy,6]; // 0 = left-up, 1 = top, 2 = right-up, 3 = right-down, 4 = down, 5 = left-down int c = -1; int subdivisions = goodGridCurvature > 0 ? 3: 1; for (int j=0;j<qy;j++) { for (int k=0;k<qx2;k++) { Point center = new Point((double)k/qx-0.5+halfStepX,(double)j/qy-0.5+halfStepY); center.x -= k * halfStepX/2; Cell cell = new Cell( (++c).ToString(), new Vector2((float)center.x, (float)center.y)); double offsetY = (k % 2==0) ? 0: -halfStepY; Segment leftUp = (k>0 && offsetY<0) ? sides[k-1, j, 3]: new Segment(center.Offset(-halfStepX, offsetY), center.Offset(-halfStepX/2, halfStepY + offsetY), k==0 || (j==qy-1 && offsetY==0)); sides[k, j, 0] = leftUp; Segment top = new Segment(center.Offset(-halfStepX/2, halfStepY + offsetY), center.Offset(halfStepX/2, halfStepY + offsetY), j==qy-1); sides[k, j, 1] = top; Segment rightUp = new Segment(center.Offset(halfStepX/2, halfStepY + offsetY), center.Offset(halfStepX, offsetY), k==qx2-1 || (j==qy-1 && offsetY==0)); sides[k, j, 2] = rightUp; Segment rightDown = (j > 0 && k<qx2-1 && offsetY<0) ? sides[k+1,j-1,0]: new Segment(center.Offset(halfStepX, offsetY), center.Offset(halfStepX/2, -halfStepY + offsetY), (j==0 && offsetY<0)|| k==qx2-1); sides[k, j, 3] = rightDown; Segment bottom = j>0 ? sides[k, j-1, 1] : new Segment(center.Offset(halfStepX/2, -halfStepY + offsetY), center.Offset(-halfStepX/2, -halfStepY +offsetY), true); sides[k, j, 4] = bottom; Segment leftDown; if (offsetY<0 && j>0) { leftDown = sides[k-1, j-1, 2]; } else if (offsetY==0 && k>0) { leftDown = sides[k-1, j, 2]; } else { leftDown = new Segment(center.Offset(-halfStepX/2, -halfStepY+offsetY), center.Offset(-halfStepX, offsetY), true); } sides[k, j, 5] = leftDown; if (j==0) { // leftDown.CropBottom(); // bottom.CropBottom(); // rightDown.CropBottom(); } if (k==qx2-1) { top.CropRight(); rightUp.CropRight(); rightDown.CropRight(); bottom.CropRight(); } Region cr = new Region (cell); if (subdivisions>1) { if (!top.deleted) cr.segments.AddRange (top.Subdivide(subdivisions, _gridCurvature)); if (!rightUp.deleted) cr.segments.AddRange (rightUp.Subdivide(subdivisions, _gridCurvature)); if (!rightDown.deleted) cr.segments.AddRange (rightDown.Subdivide(subdivisions, _gridCurvature)); if (!bottom.deleted) cr.segments.AddRange (bottom.Subdivide(subdivisions, _gridCurvature)); if (!leftDown.deleted) cr.segments.AddRange (leftDown.Subdivide(subdivisions, _gridCurvature)); if (!leftUp.deleted) cr.segments.AddRange (leftUp.Subdivide(subdivisions, _gridCurvature)); } else { if (!top.deleted) cr.segments.Add (top); if (!rightUp.deleted) cr.segments.Add (rightUp); if (!rightDown.deleted) cr.segments.Add (rightDown); if (!bottom.deleted) cr.segments.Add (bottom); if (!leftDown.deleted) cr.segments.Add (leftDown); if (!leftUp.deleted) cr.segments.Add (leftUp); } Connector connector = new Connector(); connector.AddRange(cr.segments); cr.polygon = connector.ToPolygon(); // FromLargestLineStrip(); if (cr.polygon!=null) { cell.region = cr; cells.Add (cell); } } } }
/// <summary> /// Highlights the cell region specified. Returns the generated highlight surface gameObject. /// Internally used by the Map UI and the Editor component, but you can use it as well to temporarily mark a territory region. /// </summary> /// <param name="refreshGeometry">Pass true only if you're sure you want to force refresh the geometry of the highlight (for instance, if the frontiers data has changed). If you're unsure, pass false.</param> void HighlightCellRegion(int cellIndex, bool refreshGeometry) { #if HIGHLIGHT_NEIGHBOURS DestroySurfaces(); #endif if (highlightedObj!=null) HideCellRegionHighlight(); if (cellIndex<0 || cellIndex>=cells.Count) return; int cacheIndex = GetCacheIndexForCellRegion (cellIndex); bool existsInCache = surfaces.ContainsKey (cacheIndex); if (refreshGeometry && existsInCache) { GameObject obj = surfaces [cacheIndex]; surfaces.Remove(cacheIndex); DestroyImmediate(obj); existsInCache = false; } if (existsInCache) { highlightedObj = surfaces [cacheIndex]; if (highlightedObj!=null) { highlightedObj.SetActive (true); highlightedObj.GetComponent<Renderer> ().sharedMaterial = hudMatCell; } else { surfaces.Remove(cacheIndex); } } else { highlightedObj = GenerateCellRegionSurface (cellIndex, hudMatCell); } _cellHighlighted = cells[cellIndex]; _cellHighlightedIndex = cellIndex; highlightFadeStart = Time.time; #if HIGHLIGHT_NEIGHBOURS for (int k=0;k<cellRegionHighlighted.neighbours.Count;k++) { int ni = GetCellIndex((Cell)cellRegionHighlighted.neighbours[k].entity); GenerateCellRegionSurface(ni, 0, hudMatTerritory); } #endif }
void HideCellRegionHighlight() { if (cellHighlighted == null) return; if (highlightedObj != null) { if (cellHighlighted.region.customMaterial!=null) { ApplyMaterialToSurface (highlightedObj, cellHighlighted.region.customMaterial); } else { highlightedObj.SetActive (false); } highlightedObj = null; } _cellHighlighted = null; _cellHighlightedIndex = -1; }
/// <summary> /// Returns the_numCellsrovince in the cells array by its reference. /// </summary> public int GetCellIndex(Cell cell) { // string searchToken = cell.territoryIndex + "|" + cell.name; if (cellLookup.ContainsKey(cell)) return _cellLookup[cell]; else return -1; }
/// <summary> /// Merges cell2 into cell1. Cell2 is removed. /// Only cells which are neighbours can be merged. /// </summary> public bool CellMerge(Cell cell1, Cell cell2) { if (cell1==null || cell2==null) return false; if (!cell1.region.neighbours.Contains(cell2.region)) return false; cell1.center = (cell2.center + cell1.center)/2.0f; // Polygon UNION operation between both regions PolygonClipper pc = new PolygonClipper(cell1.polygon, cell2.polygon); pc.Compute(PolygonOp.UNION); // Remove cell2 from lists int territoryIndex = cell2.territoryIndex; if (territories[territoryIndex].cells.Contains(cell2)) territories[territoryIndex].cells.Remove(cell2); if (cells.Contains(cell2)) cells.Remove(cell2); // Updates geometry data on cell1 UpdateCellGeometry(cell1, pc.subject); return true; }
/// <summary> /// Automatically generates territories based on the different colors included in the texture. /// </summary> /// <param name="neutral">This color won't generate any texture.</param> public void CreateTerritories(Texture2D texture, Color neutral, bool hideNeutralCells = false) { if (texture == null || cells == null) return; List<Color> dsColors = new List<Color>(); Dictionary<Color, int> dsColorDict = new Dictionary<Color, int>(); int cellCount = cells.Count; Color[] colors = null; try { colors = texture.GetPixels(); } catch { Debug.Log("Texture used to create territories is not readable. Check import settings."); return; } for (int k = 0; k < cellCount; k++) { if (!cells[k].visible) continue; Vector2 uv = cells[k].center; uv.x += 0.5f; uv.y += 0.5f; int x = (int)(uv.x * texture.width); int y = (int)(uv.y * texture.height); int pos = y * texture.width + x; if (pos < 0 || pos >= colors.Length) continue; Color pixelColor = colors[pos]; int territoryIndex; if (!dsColorDict.TryGetValue(pixelColor, out territoryIndex)) { dsColors.Add(pixelColor); territoryIndex = dsColors.Count - 1; dsColorDict[pixelColor] = territoryIndex; } cells[k].territoryIndex = territoryIndex; if (territoryIndex >= MAX_TERRITORIES - 1) break; } needUpdateTerritories = true; if (dsColors.Count > 0) { _numTerritories = dsColors.Count; _showTerritories = true; if (territories == null) { territories = new List<Territory>(_numTerritories); } else { territories.Clear(); } for (int c = 0; c < _numTerritories; c++) { Territory territory = new Territory(c.ToString()); Color territoryColor = dsColors[c]; if (territoryColor.r != neutral.r || territoryColor.g != neutral.g || territoryColor.b != neutral.b) { territory.fillColor = territoryColor; } else { territory.fillColor = new Color(0, 0, 0, 0); territory.visible = false; } // Add cells to territories for (int k = 0; k < cellCount; k++) { Cell cell = cells[k]; if (cell.territoryIndex == c) { territory.cells.Add(cell); territory.center += cell.center; } } if (territory.cells.Count > 0) { territory.center /= territory.cells.Count; // Ensure center belongs to territory Cell cellAtCenter = CellGetAtPosition(territory.center); if (cellAtCenter.territoryIndex != c) { territory.center = territory.cells[0].center; } } territories.Add(territory); } isDirty = true; issueRedraw = RedrawType.Full; Redraw(); } }