void ComputeBounds() { Vector2 min = Misc.Vector2max; Vector2 max = Misc.Vector2min; for (int k = 0; k < points.Length; k++) { float x = points [k].x; float y = points [k].y; if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } } rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); rect2DArea = rect2D.width * rect2D.height; FastVector.Average(ref min, ref max, ref center); // center = (min + max) * 0.5f; }
/// <summary> /// Returns the registered Game Object near a given position /// </summary> public GameObjectAnimator VGOGet(Vector2 mapPos, float distance) { distance *= distance; CheckVGOsArrayDirty(); for (int k = 0; k < vgosCount; k++) { GameObjectAnimator go = vgos[k]; float d = FastVector.SqrDistanceByValue(go.currentMap2DLocation, mapPos); // Vector2.SqrMagnitude (go.currentMap2DLocation - mapPos); if (d <= distance) { return(go); } } return(null); }
/// <summary> /// Updates the region rect2D. Needed if points is updated manually. /// </summary> /// <param name="newPoints">New points.</param> /// <param name="inflate">If set to <c>true</c> points will be slightly displaced to prevent polygon clipping floating point issues.</param> public void UpdatePointsAndRect(Vector2[] newPoints, bool inflate = false) { sanitized = false; points = newPoints; ComputeBounds(); if (inflate) { Vector2 tmp = Misc.Vector2zero; for (int k = 0; k < points.Length; k++) { FastVector.NormalizedDirection(ref center, ref points [k], ref tmp); FastVector.Add(ref points [k], ref tmp, 0.00001f); } } }
// Snap positions to country frontiers? void SnapToCountryFrontiers(Region region) { WMSK map = WMSK.instance; int positionsCount = region.points.Length; for (int k = 0; k < positionsCount; k++) { Vector2 p = region.points [k]; float minDist = float.MaxValue; Vector2 nearest = Misc.Vector2zero; bool found = false; for (int c = 0; c < map.countries.Length; c++) { Country country = map.countries [c]; // if country's bounding box does not contain point, skip it if (!country.regionsRect2D.Contains(p)) { continue; } int regionCount = country.regions.Count; for (int cr = 0; cr < regionCount; cr++) { Region countryRegion = country.regions [cr]; // if region's bounding box does not contain point, skip it if (!countryRegion.rect2D.Contains(p)) { continue; } for (int q = 0; q < countryRegion.points.Length; q++) { float dist = FastVector.SqrDistance(ref countryRegion.points [q], ref p); // (countryRegion.points [q] - p).sqrMagnitude; if (dist < minDist) { minDist = dist; nearest = region.points [q]; found = true; } } } } if (found) { region.points [k] = nearest; } } region.RemoveDuplicatePoints(); }
/// <summary> /// Returns the registered Game Object near a given position /// </summary> public GameObjectAnimator VGOGet(Vector2 mapPos, float distance) { distance *= distance; List <GameObjectAnimator> gos = new List <GameObjectAnimator> (vgos.Values); int gosCount = gos.Count; for (int k = 0; k < gosCount; k++) { GameObjectAnimator go = gos [k]; float d = FastVector.SqrDistanceByValue(go.currentMap2DLocation, mapPos); // Vector2.SqrMagnitude (go.currentMap2DLocation - mapPos); if (d <= distance) { return(go); } } return(null); }
public void ScaleMountPoints() { if (map == null || map.currentCamera == null || map.currentCamera.pixelWidth == 0) { return; // Camera pending setup } map.GetLocalHitFromScreenPos(Input.mousePosition, out lastPos, false); lastPos = transform.TransformPoint(lastPos); lastCamPos = map.cameraMain.transform.position; lastIconSize = map.cityIconSize; lastOrtographicSize = map.currentCamera.orthographicSize; Vector3 a = map.currentCamera.WorldToScreenPoint(transform.position); Vector3 b = new Vector3(a.x, a.y + MOUNTPOINT_SIZE_ON_SCREEN, a.z); if (map.currentCamera.pixelWidth == 0) { return; // Camera pending setup } Vector3 aa = map.currentCamera.ScreenToWorldPoint(a); Vector3 bb = map.currentCamera.ScreenToWorldPoint(b); float scale = (aa - bb).magnitude * map.cityIconSize; if (map.currentCamera.orthographic) { scale /= 1 + (map.currentCamera.orthographicSize * map.currentCamera.orthographicSize) * (0.1f / map.transform.localScale.x); } else { scale /= 1 + FastVector.SqrDistance(ref lastCamPos, ref lastPos) * (0.1f / map.transform.localScale.x); } Vector3 newScale = new Vector3(scale / WMSK.mapWidth, scale / WMSK.mapHeight, 1.0f); newScale *= 2.0f; foreach (Transform t in transform) { t.localScale = newScale; } }
/// <summary> /// Returns any mount point near the point specified in local coordinates. /// </summary> /// <param name="separation">Distance threshold (minimum should be MOUNT_POINT_HIT_PRECISION constant).</param> public int GetMountPointNearPoint(Vector2 localPoint, float separation) { if (mountPoints == null) { return(-1); } if (separation < MOUNT_POINT_HIT_PRECISION) { separation = MOUNT_POINT_HIT_PRECISION; } float separationSqr = separation * separation; int count = mountPoints.Count; for (int c = 0; c < count; c++) { Vector2 mpLoc = mountPoints [c].unity2DLocation; float distSqr = FastVector.SqrDistance(ref mpLoc, ref localPoint); // (mpLoc - localPoint).sqrMagnitude; if (distSqr < separationSqr) { return(c); } } return(-1); }
public void ScaleCities() { if (map == null || map.currentCamera == null || map.currentCamera.pixelWidth == 0 || gameObject == null) { return; // Camera pending setup } // annotate current values lastPos = transform.position; lastCamPos = map.currentCamera.transform.position; lastIconSize = map.cityIconSize; lastOrtographicSize = map.currentCamera.orthographicSize; // get mouse position relative to map in world coordinates Vector3 worldPos; map.GetLocalHitFromScreenPos(Input.mousePosition, out worldPos, false); worldPos = transform.TransformPoint(worldPos); // calculates optimal city icon size - deals with WorldToScreenPoint lack of precision Vector3 a = map.currentCamera.WorldToScreenPoint(transform.position); if (a.z < 0) { return; } Vector3 b = new Vector3(a.x, a.y + CITY_SIZE_ON_SCREEN, a.z); Vector3 c = new Vector3(a.x - CITY_SIZE_ON_SCREEN, a.y, a.z); Vector3 aa = map.currentCamera.ScreenToWorldPoint(a); Vector3 bb = map.currentCamera.ScreenToWorldPoint(b); Vector3 cc = map.currentCamera.ScreenToWorldPoint(c); float dist = ((aa - bb).magnitude + (aa - cc).magnitude) * 0.5f; float scale = dist * map.cityIconSize; if (map.currentCamera.orthographic) { scale /= 1.0f + (map.currentCamera.orthographicSize * map.currentCamera.orthographicSize) * (0.1f / map.transform.localScale.x); } else { scale /= 1.0f + FastVector.SqrDistance(ref lastCamPos, ref worldPos) * (0.1f / map.transform.localScale.x); } Vector3 newScale = new Vector3(scale / WMSK.mapWidth, scale / WMSK.mapHeight, 1.0f); map.currentCityScale = newScale; // check if scale has changed Transform tNormalCities = transform.Find("Normal Cities"); bool needRescale = false; Transform tChild; if (tNormalCities != null && tNormalCities.childCount > 0) { tChild = tNormalCities.GetChild(0); if (tChild != null) { if (tChild.localScale != newScale) { needRescale = true; } } } Transform tRegionCapitals = transform.Find("Region Capitals"); if (!needRescale && tRegionCapitals != null && tRegionCapitals.childCount > 0) { tChild = tRegionCapitals.GetChild(0); if (tChild != null) { if (tChild.localScale != newScale) { needRescale = true; } } } Transform tCountryCapitals = transform.Find("Country Capitals"); if (!needRescale && tCountryCapitals != null && tCountryCapitals.childCount > 0) { tChild = tCountryCapitals.GetChild(0); if (tChild != null) { if (tChild.localScale != newScale) { needRescale = true; } } } if (!needRescale) { return; } // apply scale to all cities children foreach (Transform t in tNormalCities) { t.localScale = newScale; } foreach (Transform t in tRegionCapitals) { t.localScale = newScale * 1.75f; } foreach (Transform t in tCountryCapitals) { t.localScale = newScale * 2.0f; } }
void CheckTiles(TileInfo parent, int currentZoomLevel, int xTile, int yTile, int zoomLevel, int subquadIndex) { // Is this tile visible? TileInfo ti; int tileCode = GetTileHashCode(xTile, yTile, zoomLevel); if (!cachedTiles.TryGetValue(tileCode, out ti)) { ti = new TileInfo(xTile, yTile, zoomLevel, subquadIndex, currentEarthTexture); ti.parent = parent; if (parent != null) { if (parent.children == null) { parent.children = new List <TileInfo> (); } parent.children.Add(ti); } for (int k = 0; k < 4; k++) { Vector2 latlon = Conversion.GetLatLonFromTile(xTile + offsets [k].x, yTile + offsets [k].y, zoomLevel); ti.latlons [k] = latlon; ti.cornerLocalPos [k] = Conversion.GetLocalPositionFromLatLon(latlon); } cachedTiles [tileCode] = ti; } // Check if tile is within restricted area if (_tileRestrictToArea) { if (ti.latlons[0].x <_tileMinMaxLatLon.x || ti.latlons[2].x> _tileMinMaxLatLon.z || ti.latlons[0].y > _tileMinMaxLatLon.w || ti.latlons[2].y < _tileMinMaxLatLon.y) { return; } } // Check if any tile corner is visible // Phase I Vector3 minWorldPos = Misc.Vector3max; Vector3 maxWorldPos = Misc.Vector3min; Vector3 tmp = Misc.Vector3zero; for (int c = 0; c < 4; c++) { Vector3 wpos = transform.TransformPoint(ti.cornerLocalPos [c]); ti.cornerWorldPos [c] = wpos; if (wpos.x < minWorldPos.x) { minWorldPos.x = wpos.x; } if (wpos.y < minWorldPos.y) { minWorldPos.y = wpos.y; } if (wpos.z < minWorldPos.z) { minWorldPos.z = wpos.z; } if (wpos.x > maxWorldPos.x) { maxWorldPos.x = wpos.x; } if (wpos.y > maxWorldPos.y) { maxWorldPos.y = wpos.y; } if (wpos.z > maxWorldPos.z) { maxWorldPos.z = wpos.z; } } FastVector.Average(ref minWorldPos, ref maxWorldPos, ref tmp); Bounds bounds = new Bounds(tmp, maxWorldPos - minWorldPos); Vector3 tileMidPoint = bounds.center; #if DEBUG_TILES if (root == null) { root = new GameObject(); root.transform.SetParent(transform); root.transform.localPosition = Vector3.zero; root.transform.localRotation = Misc.QuaternionZero; //Quaternion.Euler (0, 0, 0); } #endif bool insideViewport = false; float minX = currentCamera.pixelWidth * 2f, minY = currentCamera.pixelHeight * 2f; float maxX = -minX, maxY = -minY; for (int c = 0; c < 4; c++) { Vector3 scrPos = currentCamera.WorldToScreenPoint(ti.cornerWorldPos [c]); insideViewport = insideViewport || (scrPos.z > 0 && scrPos.x >= 0 && scrPos.x < currentCamera.pixelWidth && scrPos.y >= 0 && scrPos.y < currentCamera.pixelHeight); if (scrPos.x < minX) { minX = scrPos.x; } if (scrPos.x > maxX) { maxX = scrPos.x; } if (scrPos.y < minY) { minY = scrPos.y; } if (scrPos.y > maxY) { maxY = scrPos.y; } } if (!insideViewport) { insideViewport = GeometryUtility.TestPlanesAABB(cameraPlanes, bounds); if (!insideViewport && _wrapHorizontally && _wrapCamera.enabled) { insideViewport = GeometryUtility.TestPlanesAABB(wrapCameraPlanes, bounds); } } ti.insideViewport = insideViewport; ti.visible = false; if (insideViewport) { if (!ti.created) { CreateTile(ti); } if (!ti.gameObject.activeSelf) { ti.gameObject.SetActive(true); } // Manage hierarchy of tiles float aparentSize = maxY - minY; bool tileIsBig = aparentSize > currentTileSize; #if DEBUG_TILES if (ti.gameObject != null) { ti.gameObject.GetComponent <TileInfoEx> ().bigTile = tileIsBig; ti.gameObject.GetComponent <TileInfoEx> ().zoomLevel = ti.zoomLevel; } #endif if ((tileIsBig || zoomLevel < TILE_MIN_ZOOM_LEVEL) && zoomLevel < _tileMaxZoomLevel) { // Load nested tiles CheckTiles(ti, currentZoomLevel, xTile * 2, yTile * 2, zoomLevel + 1, 0); CheckTiles(ti, currentZoomLevel, xTile * 2 + 1, yTile * 2, zoomLevel + 1, 1); CheckTiles(ti, currentZoomLevel, xTile * 2, yTile * 2 + 1, zoomLevel + 1, 2); CheckTiles(ti, currentZoomLevel, xTile * 2 + 1, yTile * 2 + 1, zoomLevel + 1, 3); ti.renderer.enabled = false; } else { ti.visible = true; // Show tile renderer if (!ti.renderer.enabled) { ti.renderer.enabled = true; } // If parent tile is loaded then use that as placeholder texture if (ti.zoomLevel > TILE_MIN_ZOOM_LEVEL && ti.parent.loadStatus == TILE_LOAD_STATUS.Loaded && !ti.placeholderImageSet) { ti.placeholderImageSet = true; ti.parentTextureCoords = placeHolderUV [ti.subquadIndex]; ti.SetPlaceholderImage(ti.parent.texture); } if (ti.loadStatus == TILE_LOAD_STATUS.Loaded) { if (!ti.hasAnimated) { ti.hasAnimated = true; ti.Animate(1f, AnimationEnded); } } else if (ti.loadStatus == TILE_LOAD_STATUS.Inactive) { ti.distToCamera = FastVector.SqrDistance(ref ti.cornerWorldPos [0], ref currentCameraPosition) * ti.zoomLevel; ti.loadStatus = TILE_LOAD_STATUS.InQueue; ti.queueTime = Time.time; loadQueue.Add(ti); } if (ti.children != null) { for (int k = 0; k < 4; k++) { TileInfo tiChild = ti.children [k]; HideTile(tiChild); } } } } else { HideTile(ti); } }
/// <summary> /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of provinces array /// </summary> public void RefreshProvinceGeometry(IAdminEntity province) { lastProvinceLookupCount = -1; float maxVol = 0; if (province.regions == null) { ReadProvincePackedString(province); } if (province.regions == null) { return; } int regionCount = province.regions.Count; Vector2 minProvince = Misc.Vector2one * 10; Vector2 maxProvince = -minProvince; Vector2 min = Misc.Vector2one * 10f; Vector2 max = -min; for (int r = 0; r < regionCount; r++) { Region provinceRegion = province.regions [r]; provinceRegion.entity = province; // just in case one country has been deleted provinceRegion.regionIndex = r; // just in case a region has been deleted int coorCount = provinceRegion.points.Length; min.x = min.y = 10f; max.x = max.y = -10f; for (int c = 0; c < coorCount; c++) { float x = provinceRegion.points [c].x; float y = provinceRegion.points [c].y; if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } } FastVector.Average(ref min, ref max, ref provinceRegion.center); if (min.x < minProvince.x) { minProvince.x = min.x; } if (min.y < minProvince.y) { minProvince.y = min.y; } if (max.x > maxProvince.x) { maxProvince.x = max.x; } if (max.y > maxProvince.y) { maxProvince.y = max.y; } provinceRegion.rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); provinceRegion.rect2DArea = provinceRegion.rect2D.width * provinceRegion.rect2D.height; float vol = FastVector.SqrDistance(ref max, ref min); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.center = provinceRegion.center; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y)); }
/// <summary> /// Unpacks province geodata information. Used by Map Editor. /// </summary> /// <param name="province">Province.</param> public void ReadProvincePackedString(IAdminEntity entity) { Province province = (Province)entity; if (province == null || province.packedRegions == null) { return; } string[] regions = province.packedRegions.Split(SPLIT_SEP_ASTERISK, StringSplitOptions.RemoveEmptyEntries); int regionCount = regions.Length; province.regions = new List <Region> (regionCount); float maxVol = float.MinValue; Vector2 minProvince = Misc.Vector2one * 10; Vector2 maxProvince = -minProvince; for (int r = 0; r < regionCount; r++) { string[] coordinates = regions [r].Split(SPLIT_SEP_SEMICOLON, StringSplitOptions.RemoveEmptyEntries); int coorCount = coordinates.Length; Vector2 min = Misc.Vector2one * 10; Vector2 max = -min; Region provinceRegion = new Region(province, province.regions.Count); provinceRegion.points = new Vector2[coorCount]; for (int c = 0; c < coorCount; c++) { float x, y; GetPointFromPackedString(ref coordinates [c], out x, out y); if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } provinceRegion.points [c].x = x; provinceRegion.points [c].y = y; } FastVector.Average(ref min, ref max, ref provinceRegion.center); provinceRegion.sanitized = true; province.regions.Add(provinceRegion); // Calculate province bounding rect if (min.x < minProvince.x) { minProvince.x = min.x; } if (min.y < minProvince.y) { minProvince.y = min.y; } if (max.x > maxProvince.x) { maxProvince.x = max.x; } if (max.y > maxProvince.y) { maxProvince.y = max.y; } provinceRegion.rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); provinceRegion.rect2DArea = provinceRegion.rect2D.width * provinceRegion.rect2D.height; float vol = FastVector.SqrDistance(ref min, ref max); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.center = provinceRegion.center; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y)); }
/// <summary> /// Returns an optimal route from startPosition to endPosition with options. /// </summary> /// <returns>The route.</returns> /// <param name="startPosition">Start position in map coordinates (-0.5...0.5)</param> /// <param name="endPosition">End position in map coordinates (-0.5...0.5)</param> /// <param name="totalCost">The total cost of traversing the path</param> /// <param name="terrainCapability">Type of terrain that the unit can pass through</param> /// <param name="minAltitude">Minimum altitude (0..1)</param> /// <param name="maxAltitude">Maximum altutude (0..1)</param> /// <param name="maxSearchCost">Maximum search cost for the path finding algorithm. A value of -1 will use the global default defined by pathFindingMaxCost</param> public List <Vector2> FindRoute(Vector2 startPosition, Vector2 endPosition, out int totalCost, TERRAIN_CAPABILITY terrainCapability = TERRAIN_CAPABILITY.Any, float minAltitude = 0, float maxAltitude = 1f, int maxSearchCost = -1, int maxSearchSteps = -1) { ComputeRouteMatrix(terrainCapability, minAltitude, maxAltitude); totalCost = 0; Point startingPoint = new Point((int)((startPosition.x + 0.5f) * EARTH_ROUTE_SPACE_WIDTH), (int)((startPosition.y + 0.5f) * EARTH_ROUTE_SPACE_HEIGHT)); Point endingPoint = new Point((int)((endPosition.x + 0.5f + 0.5f / EARTH_ROUTE_SPACE_WIDTH) * EARTH_ROUTE_SPACE_WIDTH), (int)((endPosition.y + 0.5f + 0.5f / EARTH_ROUTE_SPACE_HEIGHT) * EARTH_ROUTE_SPACE_HEIGHT)); endingPoint.X = Mathf.Clamp(endingPoint.X, 0, EARTH_ROUTE_SPACE_WIDTH - 1); endingPoint.Y = Mathf.Clamp(endingPoint.Y, 0, EARTH_ROUTE_SPACE_HEIGHT - 1); // Helper to find a minimum path in case the destination position is on a different terrain type if (terrainCapability == TERRAIN_CAPABILITY.OnlyWater) { int arrayIndex = endingPoint.Y * EARTH_ROUTE_SPACE_WIDTH + endingPoint.X; if ((earthRouteMatrix [arrayIndex] & 4) == 0) { int regionIndex = -1; int countryIndex = GetCountryIndex(endPosition, out regionIndex); if (countryIndex >= 0 && regionIndex >= 0) { List <Vector2> coastPositions = GetCountryCoastalPoints(countryIndex, regionIndex, 0.001f); float minDist = float.MaxValue; Vector2 bestPosition = Misc.Vector2zero; int coastPositionsCount = coastPositions.Count; // Get nearest position to the ship which is on water for (int k = 0; k < coastPositionsCount; k++) { Vector2 waterPosition; if (ContainsWater(coastPositions [k], 0.001f, out waterPosition)) { float dist = FastVector.SqrDistance(ref endPosition, ref waterPosition); // (endPosition - waterPosition).sqrMagnitude; if (dist < minDist) { minDist = dist; bestPosition = waterPosition; } } } if (minDist < float.MaxValue) { endPosition = bestPosition; } endingPoint = new Point((int)((endPosition.x + 0.5f + 0.5f / EARTH_ROUTE_SPACE_WIDTH) * EARTH_ROUTE_SPACE_WIDTH), (int)((endPosition.y + 0.5f + 0.5f / EARTH_ROUTE_SPACE_HEIGHT) * EARTH_ROUTE_SPACE_HEIGHT)); arrayIndex = endingPoint.Y * EARTH_ROUTE_SPACE_WIDTH + endingPoint.X; if ((earthRouteMatrix [arrayIndex] & 4) == 0) { Vector2 direction = Misc.Vector2zero; for (int k = 1; k <= 10; k++) { if (k == 10 || startPosition == endPosition) { return(null); } FastVector.NormalizedDirection(ref endPosition, ref startPosition, ref direction); Vector2 p = endPosition + direction * (float)k / EARTH_ROUTE_SPACE_WIDTH; endingPoint = new Point((int)((p.x + 0.5f + 0.5f / EARTH_ROUTE_SPACE_WIDTH) * EARTH_ROUTE_SPACE_WIDTH), (int)((p.y + 0.5f + 0.5f / EARTH_ROUTE_SPACE_HEIGHT) * EARTH_ROUTE_SPACE_HEIGHT)); arrayIndex = endingPoint.Y * EARTH_ROUTE_SPACE_WIDTH + endingPoint.X; if ((earthRouteMatrix [arrayIndex] & 4) > 0) { break; } } } } } } List <Vector2> routePoints = null; // Minimum distance for routing? if (Mathf.Abs(endingPoint.X - startingPoint.X) > 0 || Mathf.Abs(endingPoint.Y - startingPoint.Y) > 0) { finder.Formula = _pathFindingHeuristicFormula; finder.MaxSearchCost = maxSearchCost < 0 ? _pathFindingMaxCost : maxSearchCost; finder.MaxSteps = maxSearchSteps < 0 ? _pathFindingMaxSteps : maxSearchSteps; if (_pathFindingEnableCustomRouteMatrix) { finder.OnCellCross = FindRoutePositionValidator; } else { finder.OnCellCross = null; } List <PathFinderNode> route = finder.FindPath(startingPoint, endingPoint, out totalCost); if (route != null) { routePoints = new List <Vector2> (route.Count); routePoints.Add(startPosition); for (int r = route.Count - 1; r >= 0; r--) { float x = (float)route [r].X / EARTH_ROUTE_SPACE_WIDTH - 0.5f; float y = (float)route [r].Y / EARTH_ROUTE_SPACE_HEIGHT - 0.5f; Vector2 stepPos = new Vector2(x, y); // due to grid effect the first step may be farther than the current position, so we skip it in that case. if (r == route.Count - 1 && (endPosition - startPosition).sqrMagnitude < (endPosition - stepPos).sqrMagnitude) { continue; } routePoints.Add(stepPos); } } else { return(null); // no route available } } // Add final step if it's appropiate bool hasWater = ContainsWater(endPosition); if (terrainCapability == TERRAIN_CAPABILITY.Any || (terrainCapability == TERRAIN_CAPABILITY.OnlyWater && hasWater) || (terrainCapability == TERRAIN_CAPABILITY.OnlyGround && !hasWater)) { if (routePoints == null) { routePoints = new List <Vector2> (); routePoints.Add(startPosition); routePoints.Add(endPosition); } else { routePoints [routePoints.Count - 1] = endPosition; } } // Check that ground units ends in a position where GetCountryIndex returns a valid index if (terrainCapability == TERRAIN_CAPABILITY.OnlyGround) { int rr = routePoints.Count - 1; Vector2 dd = routePoints [rr - 1] - routePoints [rr]; dd *= 0.1f; while (GetCountryIndex(routePoints [rr]) < 0) { routePoints [rr] += dd; } } return(routePoints); }
void DrawTextMeshProLabels() { if (labelsFontTMPro == null) { return; } float mw = mapWidth; float mh = mapHeight; Vector2 center = Misc.Vector2zero; for (int countryIndex = 0; countryIndex < _countries.Length; countryIndex++) { Country country = _countries[countryIndex]; if (country.hidden || !country.labelVisible || country.mainRegionIndex < 0 || country.mainRegionIndex >= country.regions.Count) { continue; } Region region = country.regions[country.mainRegionIndex]; CurvedLabelInfo curvedLabel; if (!ComputeCurvedLabelData(region, out curvedLabel)) { continue; } if (country.labelOffset.x != 0 || country.labelOffset.y != 0) { center = country.center + country.labelOffset; } else { FastVector.Average(ref curvedLabel.axisStart, ref curvedLabel.axisEnd, ref center); // (curvedLabel.axisStart + curvedLabel.axisEnd) * 0.5f; } center.x *= mw; center.y *= mh; // Adjusts country name length string countryName = country.customLabel != null ? country.customLabel : country.name.ToUpper(); if (countryName.Length == 0) { continue; } if (countryName.Length > 15) { countryName = BreakOneLineString(countryName); } // add caption GameObject textObj; TextMeshPro tm; Color labelColor = country.labelColorOverride ? country.labelColor : _countryLabelsColor; if (country.labelTextMeshGO == null) { // create base text textObj = new GameObject(countryName); textObj.hideFlags = HideFlags.DontSave; textObj.hideFlags |= HideFlags.HideInHierarchy; tm = textObj.AddComponent <TextMeshPro>(); tm.alignment = TextAlignmentOptions.Center; tm.enableWordWrapping = false; country.labelTextMeshPro = tm; country.labelTextMeshGO = tm.gameObject; textObj.transform.SetParent(textRoot.transform, false); textObj.layer = textRoot.gameObject.layer; } else { tm = (TextMeshPro)country.labelTextMeshPro; textObj = tm.gameObject; textObj.transform.localPosition = center; } tm.font = (TMP_FontAsset)labelsFontTMPro; tm.color = labelColor; if (_countryLabelsEnableAutomaticFade) { // By using fontMaterial we're forcing to instantiate the material which will enable individual colors and alpha Material mat = tm.fontMaterial; mat.SetColor("_OutlineColor", _countryLabelsOutlineColor); mat.SetFloat("_OutlineWidth", _countryLabelsOutlineWidth); mat.hideFlags = HideFlags.DontSave; } else { tm.fontSharedMaterial.SetColor("_OutlineColor", _countryLabelsOutlineColor); tm.fontSharedMaterial.SetFloat("_OutlineWidth", _countryLabelsOutlineWidth); } tm.text = countryName; textObj.transform.localPosition = center; country.labelMeshWidth = tm.preferredWidth; country.labelMeshHeight = tm.preferredHeight; country.labelMeshCenter = center; float meshWidth = country.labelMeshWidth; float meshHeight = country.labelMeshHeight; // adjusts scale to fit in region Vector2 axis = curvedLabel.axisEnd - curvedLabel.axisStart; float scale; if (country.labelFontSizeOverride) { scale = country.labelFontSize; } else { // axisWidth represents the length of the label along the longest axis float axisWidth = new Vector2(axis.x * mw, axis.y * mh).magnitude; // axisAveragedWidth represents the average length of the region (used as a maximum height for the label) float axisAveragedThickness = new Vector2(curvedLabel.axisAveragedThickness.x * mw, curvedLabel.axisAveragedThickness.y * mh).magnitude; float scaleheight = axisAveragedThickness / meshHeight; float scaleWidth = axisWidth / meshWidth; scale = Mathf.Min(scaleWidth, scaleheight); if (meshHeight * scale < _countryLabelsAbsoluteMinimumSize) { scale = _countryLabelsAbsoluteMinimumSize / meshHeight; } scale *= _countryLabelsSize * 2f; } // apply scale textObj.transform.localScale = new Vector3(scale, scale, 1); // Apply axis rotation or user defined rotation if (country.labelRotation > 0) { textObj.transform.localRotation = Quaternion.Euler(0, 0, country.labelRotation); } else { textObj.transform.localRotation = Quaternion.Euler(0, 0, curvedLabel.axisAngle); } if (_countryLabelsCurvature > 0) { // Compute fitting curve if (labelsCurve == null || labelsCurve.length == 0) { ComputeLabelsCurve(); } tm.havePropertiesChanged = true; // Need to force the TextMeshPro Object to be updated. tm.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate. TMP_TextInfo textInfo = tm.textInfo; int characterCount = textInfo.characterCount; float boundsMinX = tm.bounds.min.x; float boundsMaxX = tm.bounds.max.x; // map bounds to axis length float axisLengthWS = new Vector2(axis.x * mw / scale, axis.y * mh / scale).magnitude; float boundsLength = boundsMaxX - boundsMinX; float boundsMid = (boundsMaxX + boundsMinX) * 0.5f; boundsMinX = boundsMid - (boundsMid - boundsMinX) * axisLengthWS / boundsLength; boundsMaxX = boundsMid + (boundsMaxX - boundsMid) * axisLengthWS / boundsLength; float curveMultiplier = new Vector2(curvedLabel.axisMidDisplacement.x * mw / scale, curvedLabel.axisMidDisplacement.y * mh / scale).magnitude *_countryLabelsCurvature; // check if axisAveragedThickness is above or below axis Vector2 a = axis * 0.5f + curvedLabel.axisMidDisplacement; float dot = a.x * -axis.y + a.y * axis.x; if (dot < 0) { curveMultiplier *= -1f; } float boundsWidth = boundsMaxX - boundsMinX; // Get the index of the mesh used by this character. int materialIndex = textInfo.characterInfo[0].materialReferenceIndex; Vector3[] vertices = textInfo.meshInfo[materialIndex].vertices; for (int i = 0; i < characterCount; i++) { if (!textInfo.characterInfo[i].isVisible) { continue; } int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Compute the baseline mid point for each character Vector2 offsetToMidBaseline = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine); // Apply offset to adjust our pivot point. vertices[vertexIndex + 0].x -= offsetToMidBaseline.x; vertices[vertexIndex + 0].y -= offsetToMidBaseline.y; vertices[vertexIndex + 1].x -= offsetToMidBaseline.x; vertices[vertexIndex + 1].y -= offsetToMidBaseline.y; vertices[vertexIndex + 2].x -= offsetToMidBaseline.x; vertices[vertexIndex + 2].y -= offsetToMidBaseline.y; vertices[vertexIndex + 3].x -= offsetToMidBaseline.x; vertices[vertexIndex + 3].y -= offsetToMidBaseline.y; // Compute the angle of rotation for each character based on the animation curve float x0 = (offsetToMidBaseline.x - boundsMinX) / boundsWidth; // Character's position relative to the bounds of the mesh. float x1 = x0 + 0.01f; float y0 = labelsCurve.Evaluate(x0) * curveMultiplier; float y1 = labelsCurve.Evaluate(x1) * curveMultiplier; Vector2 tangent = new Vector2(x1 * boundsWidth + boundsMinX - offsetToMidBaseline.x, y1 - y0); // - new Vector3 (offsetToMidBaseline.x, y0); dot = Mathf.Acos(tangent.normalized.x) * Mathf.Rad2Deg; float angle = tangent.y > 0 ? dot : 360 - dot; Matrix4x4 matrix = Matrix4x4.TRS(new Vector3(offsetToMidBaseline.x, y0 + offsetToMidBaseline.y, 0), Quaternion.Euler(0, 0, angle), Misc.Vector3one); vertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 0]); vertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 1]); vertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 2]); vertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 3]); // vertices [vertexIndex + 0] += offsetToMidBaseline; // vertices [vertexIndex + 1] += offsetToMidBaseline; // vertices [vertexIndex + 2] += offsetToMidBaseline; // vertices [vertexIndex + 3] += offsetToMidBaseline; } // Upload the mesh with the revised information tm.UpdateVertexData(); } country.labelMeshWidth = tm.bounds.size.x; country.labelMeshHeight = tm.bounds.size.y; // GameObject sphere3 = GameObject.CreatePrimitive (PrimitiveType.Sphere); // sphere3.name = "Axis Middle"; // sphere3.transform.SetParent (o.transform, true); // sphere3.transform.localPosition = (curvedLabel.axisStart + curvedLabel.axisEnd) * 0.5f; // sphere3.transform.localScale *= 0.3f; // // GameObject sphere4 = GameObject.CreatePrimitive (PrimitiveType.Sphere); // sphere4.name = "avgAxisPoint"; // sphere4.transform.SetParent (o.transform, true); // sphere4.transform.localPosition = (curvedLabel.axisStart + curvedLabel.axisEnd) * 0.5f + curvedLabel.axisMidDisplacement; // sphere4.GetComponent<Renderer> ().material.color = Color.red; // sphere4.transform.localScale *= 0.2f; StartCoroutine(RepositionTexts()); } }
void UpdateArrowCap(ref GameObject placeholder, ref GameObject obj, GameObject cap, bool flipDirection, Vector3 arrowBasePos, Vector3 arrowTipPos, Vector3 scale, float offset, Material capMaterial) { if (obj == null) { placeholder = new GameObject("CapPlaceholder"); placeholder.transform.SetParent(transform, false); if (!usesViewport) { placeholder.transform.localScale = new Vector3(1f / transform.lossyScale.x, 1f / transform.lossyScale.y, 1f); } obj = Instantiate <GameObject>(cap); capSpriteRenderer = obj.GetComponent <SpriteRenderer>(); if (capSpriteRenderer != null) { capSpriteRenderer.color = lineMaterial.color; capStartColor = capSpriteRenderer.color; } capMeshRenderer = obj.GetComponent <MeshRenderer>(); if (capMeshRenderer != null) { capMeshRenderer.sharedMaterial = capMaterial != null ? capMaterial : lineMaterial; capStartColor = capMeshRenderer.sharedMaterial.color; } obj.transform.SetParent(placeholder.transform); obj.SetActive(false); } // set position Vector3 pos = Misc.Vector3zero; if (!usesViewport) { arrowBasePos = map.transform.TransformPoint(arrowBasePos); arrowTipPos = map.transform.TransformPoint(arrowTipPos); } // Length of cap based on size if (arrowBasePos != arrowTipPos) { Vector3 dir = Misc.Vector3zero; FastVector.NormalizedDirection(ref arrowBasePos, ref arrowTipPos, ref dir); pos = arrowTipPos - dir * offset; if (!obj.activeSelf) { obj.SetActive(true); } obj.transform.position = pos; // look to camera if (usesViewport) { Vector3 camDir = pos - map.cameraMain.transform.position; obj.transform.LookAt(arrowTipPos + dir * 100f, camDir); } else { Vector3 prdir = Vector3.ProjectOnPlane(dir, map.transform.forward); obj.transform.LookAt(pos + map.transform.forward, prdir); obj.transform.Rotate(obj.transform.forward, 90, Space.Self); } if (flipDirection) { obj.transform.Rotate(180, 0, 0, Space.Self); } } obj.transform.localScale = scale; }
/// <summary> /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of countries array /// </summary> public void RefreshCountryGeometry(IAdminEntity country) { float maxVol = 0; if (country.regions == null) { return; } int regionCount = country.regions.Count; Vector2 min = Misc.Vector2one * 10; Vector2 max = -min; Vector2 minCountry = Misc.Vector2one * 10; Vector2 maxCountry = -minCountry; for (int r = 0; r < regionCount; r++) { Region countryRegion = country.regions [r]; if (countryRegion.points == null) { continue; } countryRegion.entity = country; // just in case one country has been deleted countryRegion.regionIndex = r; // just in case a region has been deleted int coorCount = countryRegion.points.Length; min.x = min.y = 10f; max.x = max.y = -10; for (int c = 0; c < coorCount; c++) { float x = countryRegion.points [c].x; float y = countryRegion.points [c].y; if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } } FastVector.Average(ref min, ref max, ref countryRegion.center); // countryRegion.center = (min + max) * 0.5f; // Calculate country bounding rect if (min.x < minCountry.x) { minCountry.x = min.x; } if (min.y < minCountry.y) { minCountry.y = min.y; } if (max.x > maxCountry.x) { maxCountry.x = max.x; } if (max.y > maxCountry.y) { maxCountry.y = max.y; } // Calculate bounding rect countryRegion.rect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); countryRegion.rect2DArea = countryRegion.rect2D.width * countryRegion.rect2D.height; float vol = FastVector.SqrDistance(ref max, ref min); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; country.mainRegionIndex = r; country.center = countryRegion.center; } } country.regionsRect2D = new Rect(minCountry.x, minCountry.y, Math.Abs(maxCountry.x - minCountry.x), Mathf.Abs(maxCountry.y - minCountry.y)); }
/// <summary> /// Returns true if the unit is near given map coordinate (optionally pass a max distance value). /// </summary> /// <returns><c>true</c> if this instance is near the specified mapPosition maxDistance; otherwise, <c>false</c>.</returns> /// <param name="mapPosition">Map position.</param> /// <param name="maxDistance">Max distance.</param> public bool isNear(Vector2 mapPosition, float maxDistance = 0.01f) { return(FastVector.SqrDistance(ref _currentMap2DLocation, ref mapPosition) < maxDistance * maxDistance); }
/// <summary> /// Calculates correct province for cities /// </summary> public void FixOrphanCities() { if (_map.provinces == null) { return; } for (int c = 0; c < _map.cities.Length; c++) { City city = _map.cities[c]; if (city.countryIndex == -1) { for (int k = 0; k < _map.countries.Length; k++) { Country co = _map.countries[k]; for (int kr = 0; kr < co.regions.Count; kr++) { if (co.regions[kr].Contains(city.unity2DLocation)) { city.countryIndex = k; cityChanges = true; k = 100000; break; } } } } if (city.countryIndex == -1) { float minDist = float.MaxValue; for (int k = 0; k < _map.countries.Length; k++) { Country co = _map.countries[k]; for (int kr = 0; kr < co.regions.Count; kr++) { float dist = FastVector.SqrDistance(ref co.regions[kr].center, ref city.unity2DLocation); if (dist < minDist) { minDist = dist; city.countryIndex = k; cityChanges = true; } } } } if (city.province.Length == 0) { Country country = _map.countries[city.countryIndex]; if (country.provinces == null) { continue; } for (int p = 0; p < country.provinces.Length; p++) { Province province = country.provinces[p]; if (province.regions == null) { _map.ReadProvincePackedString(province); } for (int pr = 0; pr < province.regions.Count; pr++) { Region reg = province.regions[pr]; if (reg.Contains(city.unity2DLocation)) { city.province = province.name; cityChanges = true; p = 100000; break; } } } } } for (int c = 0; c < _map.cities.Length; c++) { City city = _map.cities[c]; if (city.province.Length == 0) { float minDist = float.MaxValue; int pg = -1; for (int p = 0; p < _map.provinces.Length; p++) { Province province = _map.provinces[p]; if (province.regions == null) { _map.ReadProvincePackedString(province); } for (int pr = 0; pr < province.regions.Count; pr++) { Region pregion = province.regions[pr]; for (int prp = 0; prp < pregion.points.Length; prp++) { float dist = FastVector.SqrDistance(ref city.unity2DLocation, ref pregion.points[prp]); if (dist < minDist) { minDist = dist; pg = p; } } } } if (pg >= 0) { city.province = _map.provinces[pg].name; cityChanges = true; } } } }
public static void UpdateDashedLine(MeshFilter meshFilter, List <Vector3> points, float thickness, bool worldSpace, ref Vector3[] meshPoints, ref int[] triPoints, ref Vector2[] uv) { int max = (points.Count / 2) * 2; if (max == 0) { return; } int numPoints = 8 * 3 * max / 2; if (numPoints > 65000) { return; } Mesh mesh = meshFilter.sharedMesh; bool reassignMesh = false; if (mesh == null || meshPoints == null || mesh.vertexCount != numPoints) { meshPoints = new Vector3[numPoints]; triPoints = new int[numPoints]; uv = new Vector2[numPoints]; reassignMesh = true; } int mp = 0; thickness *= 0.5f; float y0 = 0f; //Mathf.Sin (0.0f * Mathf.Deg2Rad); float x0 = 1f; //Mathf.Cos (0.0f * Mathf.Deg2Rad); float y1 = 0.8660254f; //Mathf.Sin (120.0f * Mathf.Deg2Rad); float x1 = -0.5f; //Mathf.Cos (120.0f * Mathf.Deg2Rad); float y2 = -0.8660254f; //Mathf.Sin (240.0f * Mathf.Deg2Rad); float x2 = -0.5f; //Mathf.Cos (240.0f * Mathf.Deg2Rad); Vector3 up = WMSK.instance.currentCamera.transform.forward; Vector3 axis = Misc.Vector3zero; float scaleX = worldSpace ? 1f : 0.5f; Vector3 tmp = Misc.Vector3zero; for (int p = 0; p < max; p += 2) { Vector3 p0 = points [p]; Vector3 p1 = points [p + 1]; FastVector.NormalizedDirection(ref p0, ref p1, ref tmp); // Front triangle Vector3 right = Vector3.Cross(up, tmp); // Vector3 axis = (up * y0 + right * x0).normalized; FastVector.CombineAndNormalize(ref up, y0, ref right, x0, ref axis); axis.x *= scaleX; meshPoints [mp + 0] = p0 + axis * thickness; // axis = (up * y2 + right * x2).normalized; FastVector.CombineAndNormalize(ref up, y2, ref right, x2, ref axis); axis.x *= scaleX; meshPoints [mp + 1] = p0 + axis * thickness; // axis = (up * y1 + right * x1).normalized; FastVector.CombineAndNormalize(ref up, y1, ref right, x1, ref axis); axis.x *= scaleX; meshPoints [mp + 2] = p0 + axis * thickness; triPoints [mp + 0] = mp + 0; triPoints [mp + 1] = mp + 1; triPoints [mp + 2] = mp + 2; uv [mp + 0] = Misc.Vector2zero; uv [mp + 1] = Misc.Vector2right; uv [mp + 2] = Misc.Vector2up; // Back triangle // axis = (up * y0 + right * x0).normalized; FastVector.CombineAndNormalize(ref up, y0, ref right, x0, ref axis); axis.x *= scaleX; meshPoints [mp + 3] = p1 + axis * thickness; // axis = (up * y1 + right * x1).normalized; FastVector.CombineAndNormalize(ref up, y1, ref right, x1, ref axis); axis.x *= scaleX; meshPoints [mp + 4] = p1 + axis * thickness; // axis = (up * y2 + right * x2).normalized; FastVector.CombineAndNormalize(ref up, y2, ref right, x2, ref axis); axis.x *= scaleX; meshPoints [mp + 5] = p1 + axis * thickness; triPoints [mp + 3] = mp + 3; triPoints [mp + 4] = mp + 4; triPoints [mp + 5] = mp + 5; uv [mp + 3] = Misc.Vector2zero; uv [mp + 4] = Misc.Vector2one; uv [mp + 5] = Misc.Vector2right; // One side meshPoints [mp + 6] = meshPoints [mp + 0]; triPoints [mp + 6] = mp + 6; uv [mp + 6] = Misc.Vector2up; meshPoints [mp + 7] = meshPoints [mp + 3]; triPoints [mp + 7] = mp + 7; uv [mp + 7] = Misc.Vector2one; meshPoints [mp + 8] = meshPoints [mp + 1]; triPoints [mp + 8] = mp + 8; uv [mp + 8] = Misc.Vector2zero; meshPoints [mp + 9] = meshPoints [mp + 1]; triPoints [mp + 9] = mp + 9; uv [mp + 9] = Misc.Vector2zero; meshPoints [mp + 10] = meshPoints [mp + 3]; triPoints [mp + 10] = mp + 10; uv [mp + 10] = Misc.Vector2one; meshPoints [mp + 11] = meshPoints [mp + 5]; triPoints [mp + 11] = mp + 11; uv [mp + 11] = Misc.Vector2zero; // Second side meshPoints [mp + 12] = meshPoints [mp + 1]; triPoints [mp + 12] = mp + 12; uv [mp + 12] = Misc.Vector2zero; meshPoints [mp + 13] = meshPoints [mp + 5]; triPoints [mp + 13] = mp + 13; uv [mp + 13] = Misc.Vector2right; meshPoints [mp + 14] = meshPoints [mp + 2]; triPoints [mp + 14] = mp + 14; uv [mp + 14] = Misc.Vector2up; meshPoints [mp + 15] = meshPoints [mp + 2]; triPoints [mp + 15] = mp + 15; uv [mp + 15] = Misc.Vector2up; meshPoints [mp + 16] = meshPoints [mp + 5]; triPoints [mp + 16] = mp + 16; uv [mp + 16] = Misc.Vector2right; meshPoints [mp + 17] = meshPoints [mp + 4]; triPoints [mp + 17] = mp + 17; uv [mp + 17] = Misc.Vector2up; // Third side meshPoints [mp + 18] = meshPoints [mp + 0]; triPoints [mp + 18] = mp + 18; uv [mp + 18] = Misc.Vector2right; meshPoints [mp + 19] = meshPoints [mp + 4]; triPoints [mp + 19] = mp + 19; uv [mp + 19] = Misc.Vector2up; meshPoints [mp + 20] = meshPoints [mp + 3]; triPoints [mp + 20] = mp + 20; uv [mp + 20] = Misc.Vector2zero; meshPoints [mp + 21] = meshPoints [mp + 0]; triPoints [mp + 21] = mp + 21; uv [mp + 21] = Misc.Vector2zero; meshPoints [mp + 22] = meshPoints [mp + 2]; triPoints [mp + 22] = mp + 22; uv [mp + 22] = Misc.Vector2one; meshPoints [mp + 23] = meshPoints [mp + 4]; triPoints [mp + 23] = mp + 23; uv [mp + 23] = Misc.Vector2up; mp += 24; } if (mesh == null) { mesh = new Mesh(); mesh.hideFlags = HideFlags.DontSave; } if (reassignMesh) { mesh.Clear(); } mesh.vertices = meshPoints; mesh.uv = uv; mesh.triangles = triPoints; mesh.RecalculateNormals(); mesh.RecalculateBounds(); if (reassignMesh) { meshFilter.sharedMesh = mesh; } }