/// <summary> /// Recursivly follows the drawpath instructions in order to render the tiles /// </summary> /// <param name="dpi"></param> /// <param name="oldPosition"></param> /// <param name="oldNode"></param> private void HandleDPI(DrawPathInstruction dpi, Vector2Int oldPosition, Node oldNode, bool grayout) { //If our map does not have a node in the instruction dir, we cannot render in that dir //if (oldNode.GetConnectionFromDir(dpi.dir) == null) { if (oldNode[(int)dpi.dir] < 0) // if this does not point to a node { return; } Vector2Int position = oldPosition + dpi.dir.offset(); RenderTile tile = renderLayers[dpi.dir.renderLayer()][position.x, position.y]; grayout = grayout || GameManager.gameplay.map.disjoint(oldNode, dpi.dir); // gray-out if disjoint, or previos node was grayed-out Node node = GameManager.gameplay.map[oldNode[(int)dpi.dir]]; //Draw the node, regarless of if it is null (null node is handled by the drawer) tile.DrawFullNode(node, dpi.dir, position, grayout); //If the node is not null, then we continue on with the instructions if (node != null) { //Add it to the list of drawn nodes if (visibleNodes.Contains(node) == false) { visibleNodes.Add(node); } //Follow instructions on the next node foreach (DrawPathInstruction nDpi in dpi.nextInstructions) { HandleDPI(nDpi, position, node, grayout); } } }
public override Map GenerateLevel() { //Delete all old tiles GameObject[] oldRender = GameObject.FindGameObjectsWithTag("RenderTile"); for (int i = oldRender.Length - 1; i >= 0; i--) { DestroyImmediate(oldRender[i].gameObject); } //5555 handler.renderMap = new RenderingHandler.RenderMap(dim * dim); handler.altRenderMap = new RenderingHandler.RenderMap(dim * dim); handler.renderMap.dim = dim; handler.altRenderMap.dim = dim; for (int x = 0; x < dim; x++) { for (int y = 0; y < dim; y++) { handler.renderMap[x, y] = GetPrefab(x, y).GetComponent <RenderTile>(); handler.altRenderMap[x, y] = GetPrefab(x, y, "alt").GetComponent <RenderTile>(); //Change the masking on the alt render map RenderTile t = handler.altRenderMap[x, y]; foreach (SpriteRenderer spr in t.GetAllSprites) { spr.maskInteraction = SpriteMaskInteraction.VisibleInsideMask; spr.sortingOrder += 10; } } } return(null); }
private void HandleDPI(DrawPathInstruction dpi, Vector2Int oldPosition, Node oldNode) { //If our map does not have a node in the instruction dir, we cannot render in that dir if (oldNode.GetConnectionFromDir(dpi.dir) == null) { return; } Vector2Int position = oldPosition + dpi.dir.offset(); RenderTile tile = renderLayers[dpi.dir.renderLayer()][position.x, position.y]; Node node = GameManager.instance.map[(int)oldNode.GetConnectionFromDir(dpi.dir)]; //Draw the node, regarless of if it is null (null node is handled by the drawer) tile.DrawFullNode(node); //If the node is not null, then we continue on with the instructions if (node != null) { //Add it to the list of drawn nodes if (visibleNodes.Contains(node) == false) { visibleNodes.Add(node); } //Follow instructions on the next node foreach (DrawPathInstruction nDpi in dpi.nextInstructions) { HandleDPI(nDpi, position, node); } } }
public void CopyState(RenderTile other) { this.gameObject.SetActive(other.gameObject.activeSelf); northWall.gameObject.SetActive(other.northWall.gameObject.activeSelf); eastWall.gameObject.SetActive(other.eastWall.gameObject.activeSelf); southWall.gameObject.SetActive(other.southWall.gameObject.activeSelf); westWall.gameObject.SetActive(other.westWall.gameObject.activeSelf); }
//a==dirt public static void DirtToGrass(DynamicMesh mesh, RenderTile a, RenderTile b, RenderTile c) { Vector3 abMid = (a.position + b.position) * 0.5f; Vector3 acMid = (a.position + c.position) * 0.5f; Vector3 abGround = new Vector3(abMid.x, a.position.y, abMid.z); Vector3 acGround = new Vector3(acMid.x, a.position.y, acMid.z); mesh.SimpleTriangle(a.position, abGround, acGround, a.color, a.color, a.color, a.normal, a.normal, a.normal); mesh.SimpleTriangle(acGround, abGround, abMid, a.color, a.color, b.color, c.normal, b.normal, b.normal); mesh.SimpleTriangle(acGround, abMid, acMid, a.color, b.color, c.color, c.normal, b.normal, c.normal); mesh.SimpleTriangle(acMid, abMid, b.position, c.color, b.color, b.color, c.normal, b.normal, b.normal); mesh.SimpleTriangle(acMid, b.position, c.position, c.color, b.color, c.color, c.normal, b.normal, c.normal); }
/// <summary> /// Calls DeleteRenderMap(), then re-generates the render-tiles and masks, and applies settings to the tiles and masks based on position /// </summary> /// <returns></returns> public override LevelMap GenerateLevel() { // only works correctly if these prefabs/references are set if ((tilePrefab == null) || (cornerMaskPrefab == null) || (tileMaskPrefab == null) || (universe == null) || (cornerHalfMask == null) || (tile18Mask == null) || (tile72Mask == null) || (lineMaskLow == null)) { Debug.Log("Error: Prefabs, Universe, or mask Sprites not set"); return(new LevelMap()); } // get the layer ids, so don't have to call NameToID() numerous times int[] layerID = new int[4]; layerID[0] = SortingLayer.NameToID("Layer_0"); layerID[1] = SortingLayer.NameToID("Layer_1"); layerID[2] = SortingLayer.NameToID("Layer_2"); layerID[3] = SortingLayer.NameToID("Layer_3"); //Delete all old tiles and masks DeleteRenderMap(); // create render & altrender tile maps handler.renderMap = new RenderingHandler.RenderMap(dim); handler.altRenderMap = new RenderingHandler.RenderMap(dim); for (int x = 0; x < dim; x++) { for (int y = 0; y < dim; y++) { handler.renderMap[x, y] = GetPrefab(x, y).GetComponent <RenderTile>(); handler.altRenderMap[x, y] = GetPrefab(x, y, "alt").GetComponent <RenderTile>(); handler.renderMap[x, y].gameObject.SetActive(true); handler.altRenderMap[x, y].gameObject.SetActive(true); RenderTile t = handler.renderMap[x, y]; foreach (SpriteRenderer spr in t.GetAllSprites) { spr.sortingLayerID = layerID[(x % 2) + (2 * (y % 2))]; // set tile to be on layer Layer_0/1/2/3 based on (x,y), so that masks on neigboring tiles do not interfere } t = handler.altRenderMap[x, y]; foreach (SpriteRenderer spr in t.GetAllSprites) { spr.sortingOrder += 20; // set altRenderMap to have higher height, so that masks do not interfere between render and altrender maps spr.sortingLayerID = layerID[(x % 2) + (2 * (y % 2))]; // set tile to be on layer Layer_0/1/2/3 based on (x,y), so that masks on neigboring tiles do not interfere } } } // only generate these if regenerateMasks is set if (this.regenerateMasks) { handler.cornerMaskMap = new RenderingHandler.MaskMap(dim + 1); // Generate corner masks #region Generate corner masks for (int x = 0; x <= dim; x++) { for (int y = 0; y <= dim; y++) { handler.cornerMaskMap[x, y] = GetPrefabCornerMask(x, y).GetComponent <GenericMask>(); //handler.altCornerMaskMap[x, y] = GetPrefabCornerMask(x, y, "alt").GetComponent<GenericMask>(); handler.cornerMaskMap[x, y].gameObject.SetActive(true); // certain corner masks are only neededd based on which direction a tile is visited/rendered from // only tiles on the diagonal can have corner masks be rendered from both directions if (((y < x) && ((y + x) > dim)) || ((y > x) && ((y + x) < dim))) { // only need horizontal masks foreach (SpriteMask mask in handler.cornerMaskMap[x, y].maskVertical) { if (mask != null) { DestroyImmediate(mask.gameObject); } } handler.cornerMaskMap[x, y].maskVertical = new SpriteMask[0]; handler.cornerMaskMap[x, y].maskLineOfSight.sprite = lineMaskLow; handler.cornerMaskMap[x, y].maskLineOfSight.transform.localPosition = new Vector3(-0.5f, -0.5f, 0.0f); float xScale = handler.cornerMaskMap[x, y].maskLineOfSight.transform.localScale.x; float yScale = handler.cornerMaskMap[x, y].maskLineOfSight.transform.localScale.y; float zScale = handler.cornerMaskMap[x, y].maskLineOfSight.transform.localScale.z; handler.cornerMaskMap[x, y].maskLineOfSight.transform.localScale = new Vector3(xScale, yScale * -1, zScale); handler.cornerMaskMap[x, y].maskLineOfSight.transform.eulerAngles = new Vector3(0.0f, 0.0f, 90.0f); } else if (((y < x) && ((y + x) < dim)) || ((y > x) && ((y + x) > dim))) { // only need vertical masks foreach (SpriteMask mask in handler.cornerMaskMap[x, y].maskHorizontal) { if (mask != null) { DestroyImmediate(mask.gameObject); } } handler.cornerMaskMap[x, y].maskHorizontal = new SpriteMask[0]; handler.cornerMaskMap[x, y].maskLineOfSight.sprite = lineMaskLow; handler.cornerMaskMap[x, y].maskLineOfSight.transform.localPosition = new Vector3(-0.5f, -0.5f, 0.0f); } else if (((x == (dim / 2)) || (x == (dim / 2) + 1)) && ((y == (dim / 2)) || (y == (dim / 2) + 1))) { //the corners right around the center tile do not need their horizontal masks foreach (SpriteMask mask in handler.cornerMaskMap[x, y].maskHorizontal) { if (mask != null) { DestroyImmediate(mask.gameObject); } } handler.cornerMaskMap[x, y].maskHorizontal = new SpriteMask[0]; } //outer-edge corner masks do not need line mask if ((x == 0) || (y == 0) || (x == dim) || (y == dim)) { SpriteMask line = handler.cornerMaskMap[x, y].maskLineOfSight; if (line != null) { DestroyImmediate(line.gameObject); } line = null; } // set 45 angle corner SpriteMasks to use CornerMask_Half // do not do this for corners immediately around the center tile if (((x == y) || ((x + y) == dim)) && !((x == (dim / 2)) || (x == (dim / 2) + 1))) { foreach (SpriteMask mask in handler.cornerMaskMap[x, y].maskVertical) { mask.sprite = cornerHalfMask; } foreach (SpriteMask mask in handler.cornerMaskMap[x, y].maskHorizontal) { mask.sprite = cornerHalfMask; } } if ((x > (dim / 2)) && (y > (dim / 2))) { handler.cornerMaskMap[x, y].gameObject.transform.localScale = new Vector3(-1.0f, -1.0f, 1.0f); } else if (x > (dim / 2)) { handler.cornerMaskMap[x, y].gameObject.transform.localScale = new Vector3(-1.0f, 1.0f, 1.0f); } else if (y > (dim / 2)) { handler.cornerMaskMap[x, y].gameObject.transform.localScale = new Vector3(1.0f, -1.0f, 1.0f); } } } #endregion handler.tileMaskMap = new RenderingHandler.MaskMap(dim); //handler.altTileMaskMap = new RenderingHandler.MaskMap(dim); // Generate Tile masks #region Generate Tile masks for (int x = 0; x < dim; x++) { for (int y = 0; y < dim; y++) { if ((x != (dim / 2)) && (y != (dim / 2))) { handler.tileMaskMap[x, y] = GetPrefabTileMask(x, y).GetComponent <GenericMask>(); //handler.altTileMaskMap[x, y] = GetPrefabTileMask(x, y, "alt").GetComponent<GenericMask>(); handler.tileMaskMap[x, y].gameObject.SetActive(true); SpriteMask mask = handler.tileMaskMap[x, y].GetTileMask; SpriteMask maskAlt = handler.tileMaskMap[x, y].GetAltTileMask; mask.frontSortingLayerID = layerID[(x % 2) + (2 * (y % 2))]; // set mask to be on layer Layer_0/1/2/3 based on (x,y), so that does not interfere with neighboring tiles mask.backSortingLayerID = layerID[(x % 2) + (2 * (y % 2))]; maskAlt.frontSortingLayerID = layerID[(x % 2) + (2 * (y % 2))]; // set mask to be on layer Layer_0/1/2/3 based on (x,y), so that does not interfere with neighboring tiles maskAlt.backSortingLayerID = layerID[(x % 2) + (2 * (y % 2))]; // alt-tile-masks along the diagonals need to be rotated by 180 degrees, so only 1 sprite needs to be used if ((x == y) || (x + y == (dim - 1))) { maskAlt.transform.eulerAngles = new Vector3(0.0f, 0.0f, 180.0f); } //set tiles (0, 1), (0, 3), (4, 1), (4, 3) to be rotated & flipped, and to use different sprites if (((y < x) && ((y + x) > (dim - 1))) || ((y > x) && ((y + x) < (dim - 1)))) { float xScale = mask.transform.localScale.x; float yScale = mask.transform.localScale.y; float zScale = mask.transform.localScale.z; mask.transform.localScale = new Vector3(xScale, yScale * -1, zScale); mask.transform.eulerAngles = new Vector3(0.0f, 0.0f, 90.0f); maskAlt.transform.localScale = new Vector3(xScale, yScale * -1, zScale); maskAlt.transform.eulerAngles = new Vector3(0.0f, 0.0f, 90.0f); mask.sprite = tile72Mask; maskAlt.sprite = tile18Mask; } if (((y < x) && ((y + x) < (dim - 1))) || ((y > x) && ((y + x) > (dim - 1)))) { mask.sprite = tile18Mask; maskAlt.sprite = tile72Mask; } if ((x > (dim / 2)) && (y > (dim / 2))) { handler.tileMaskMap[x, y].gameObject.transform.localScale = new Vector3(-1.0f, -1.0f, 1.0f); } else if (x > (dim / 2)) { handler.tileMaskMap[x, y].gameObject.transform.localScale = new Vector3(-1.0f, 1.0f, 1.0f); } else if (y > (dim / 2)) { handler.tileMaskMap[x, y].gameObject.transform.localScale = new Vector3(1.0f, -1.0f, 1.0f); } } } } #endregion } return(null); }
public static void RenderTriangle(DynamicMesh mesh, RenderTile a, RenderTile b, RenderTile c) { TerrainType tA = a.GetTerrain(); TerrainType tB = b.GetTerrain(); TerrainType tC = c.GetTerrain(); if (tA == tB && tA == tC) { //No terrain transition mesh.SimpleTriangle(a.position, b.position, c.position, a.color, b.color, c.color, a.normal, b.normal, c.normal); } else { //NOTE: Currently I assume height goes: sand, dirt, grass. Height transitions could also be added //Terrain transition. TODO: cache all combination if (tB == tC) { //A unique if (tA == TerrainType.Grass && tB == TerrainType.Dirt) { GrassToDirt(mesh, a, b, c); return; } else if (tA == TerrainType.Dirt && tB == TerrainType.Grass) { DirtToGrass(mesh, a, b, c); return; } } else if (tA == tC) { //B unique if (tB == TerrainType.Grass && tC == TerrainType.Dirt) { GrassToDirt(mesh, b, c, a); return; } else if (tB == TerrainType.Dirt && tC == TerrainType.Grass) { DirtToGrass(mesh, b, c, a); return; } } else if (tA == tB) { //C unique if (tC == TerrainType.Grass && tA == TerrainType.Dirt) { GrassToDirt(mesh, c, a, b); return; } else if (tC == TerrainType.Dirt && tA == TerrainType.Grass) { DirtToGrass(mesh, c, a, b); return; } } else { //All unique } mesh.SimpleTriangle(a.position, b.position, c.position, a.color, b.color, c.color, a.normal, b.normal, c.normal); } }
void Render() { LogicArray2 <int> tiles = world.map; LogicArray2 <RenderTile> renderTiles = new LogicArray2 <RenderTile>(tiles.width, tiles.height, null); { //Vertex creation int index = 0; for (int y = 0; y < tiles.height; ++y) { for (int x = 0; x < tiles.width; ++x) { int tile = tiles.Get(x, y); Vector3 pos = HexToWorldPos(new LogicHex(x, y)); Color32 color = TerrainTypeToColor(LogicTile.GetTerrain(tile)); renderTiles.Set(x, y, new RenderTile(tile, pos, Vector3.zero, color)); ++index; } } } { //Smooth normal calculations int index = 0; for (int y = 1; y < renderTiles.height - 1; ++y) //Skip edge tiles { for (int x = 1; x < renderTiles.width - 1; ++x) { RenderTile tile = renderTiles.Get(x, y); Vector3 p0 = tile.position; for (int h = 0; h < 6; ++h) { LogicHex dir1 = LogicHex.Direction(h); LogicHex dir2 = LogicHex.Direction(h + 1); Vector3 p1 = renderTiles.Get(x + dir1.q, y + dir1.r).position; Vector3 p2 = renderTiles.Get(x + dir2.q, y + dir2.r).position; Vector3 side1 = p1 - p0; Vector3 side2 = p2 - p0; Vector3 perp = Vector3.Cross(side1, side2); //float perpLength = perp.magnitude; //perp /= perpLength; tile.normal += perp; } float normLength = tile.normal.magnitude; tile.normal /= normLength; ++index; } } } { //Mesh creation (splitting in chunks, triangulation etc.) int widthInChunks = renderTiles.width / CHUNK_SIZE; int heightInChunks = renderTiles.height / CHUNK_SIZE; if (chunks == null) { chunks = new LogicArray2 <Chunk>(widthInChunks, heightInChunks, null); } for (int cy = 0; cy < heightInChunks; ++cy) { for (int cx = 0; cx < widthInChunks; ++cx) { DynamicMesh dynamicMesh = new DynamicMesh(); //Chunk bounds int startY = cy * CHUNK_SIZE; int endY = startY + CHUNK_SIZE; int startX = cx * CHUNK_SIZE; int endX = startX + CHUNK_SIZE; if (cx == widthInChunks - 1) { endX--; } if (cy == widthInChunks - 1) { endY--; } for (int y = startY; y < endY; ++y) { for (int x = startX; x < endX; ++x) { RenderTile tile0 = renderTiles.Get(x, y); RenderTile tile1 = renderTiles.Get(x + 1, y); RenderTile tile2 = renderTiles.Get(x, y + 1); RenderTile tile3 = renderTiles.Get(x + 1, y + 1); RenderTriangle(dynamicMesh, tile0, tile1, tile2); RenderTriangle(dynamicMesh, tile2, tile1, tile3); } } Mesh mesh = dynamicMesh.Create(); Chunk chunk = GetChunk(cy, cx); chunk.SetMesh(mesh); } } } //Debug.Log("Triangles:" + dynamicMesh.triangles.Count + "Vertices:" + dynamicMesh.vertices.Count); //Mesh mesh = dynamicMesh.Create(); //meshFilter.mesh = mesh; //meshCollider.sharedMesh = mesh; //Debug.Log("NavigationBlob::MeshUpdate duration=" + (Time.realtimeSinceStartup - timer)); }
public override bool HasUpdate(uint gameTick) { if (_update || _centerChanged) { RenderTile[] renderTiles = RenderTiles.ToArray(); if (Game.MovingUnit != null && !_centerChanged) { IUnit unit = Game.MovingUnit; ITile[] tiles = Map.QueryMapPart(unit.X - 1, unit.Y - 1, 3, 3).ToArray(); renderTiles = renderTiles.Where(t => tiles.Any(x => x != null && x.X == t.Tile.X && x.Y == t.Tile.Y)).ToArray(); } else { _centerChanged = false; _canvas = new Picture(_canvas.Width, _canvas.Height, _palette); } foreach (RenderTile t in renderTiles) { if (!Settings.RevealWorld && !t.Visible) { _canvas.FillRectangle(5, t.X * 16, t.Y * 16, 16, 16); continue; } AddLayer(t.Image, t.Position); if (Settings.RevealWorld) { continue; } if (!Human.Visible(t.Tile, Direction.West)) { AddLayer(Resources.Instance.GetFog(Direction.West), t.Position); } if (!Human.Visible(t.Tile, Direction.North)) { AddLayer(Resources.Instance.GetFog(Direction.North), t.Position); } if (!Human.Visible(t.Tile, Direction.East)) { AddLayer(Resources.Instance.GetFog(Direction.East), t.Position); } if (!Human.Visible(t.Tile, Direction.South)) { AddLayer(Resources.Instance.GetFog(Direction.South), t.Position); } } foreach (RenderTile t in renderTiles) { if (!Settings.RevealWorld && !t.Visible) { continue; } if (t.Tile.City != null) { continue; } IUnit[] units = t.Tile.Units.Where(u => !u.Moving).ToArray(); if (t.Tile.Type == Terrain.Ocean) { // Always show naval units first at sea units = units.OrderBy(u => (u.Class == UnitClass.Water) ? 1 : 0).ToArray(); } if (units.Length == 0) { continue; } IUnit drawUnit = units.FirstOrDefault(u => u == Game.ActiveUnit); if (drawUnit == null) { // No active unit on this tile, show top unit if (t.Tile.IsOcean) { drawUnit = units.OrderBy(x => x.Class == UnitClass.Land ? 1 : 0).FirstOrDefault(); } else { drawUnit = units[0]; } } else if (!Common.HasScreenType <Input>() && ((gameTick % 4) >= 2 || drawUnit.Moving)) { // Active unit on this tile or unit is currently moving. Drawing happens later. continue; } if (t.Tile.IsOcean && drawUnit.Class != UnitClass.Water && drawUnit.Sentry) { // Do not draw sentried land units at sea continue; } AddLayer(drawUnit.GetUnit(drawUnit.Owner), t.Position); if (units.Length == 1) { continue; } AddLayer(drawUnit.GetUnit(drawUnit.Owner), t.Position.X - 1, t.Position.Y - 1); } foreach (RenderTile t in renderTiles.Reverse()) { if (!Settings.RevealWorld && !t.Visible) { continue; } City city = t.Tile.City; if (city == null) { continue; } AddLayer(Icons.City(city), t.Position); if (t.Y == 11) { continue; } int labelX = (t.X == 0) ? t.Position.X : t.Position.X - 8; int labelY = t.Position.Y + 16; _canvas.DrawText(city.Name, 0, 5, labelX, labelY + 1, TextAlign.Left); _canvas.DrawText(city.Name, 0, 11, labelX, labelY, TextAlign.Left); } foreach (RenderTile t in renderTiles) { if (!Settings.RevealWorld && !t.Visible) { continue; } IUnit[] units = t.Tile.Units.Where(u => !u.Moving).ToArray(); if (units.Length == 0) { continue; } IUnit drawUnit = units.FirstOrDefault(u => u == Game.ActiveUnit); if (drawUnit == null) { continue; } // Active unit on this tile if (drawUnit.Moving) { // Unit is currently moving, do not draw the unit here. continue; } if (Human == drawUnit.Owner && (gameTick % 4) >= 2 && !GameTask.Any()) { // Unit is owned by human player, blink status is off and no tasks are running. Do not draw unit. continue; } if (t.Tile.City != null && units.Length == 1 && !GameTask.Any()) { AddLayer(drawUnit.GetUnit(units[0].Owner), t.Position.X - 1, t.Position.Y - 1); continue; } AddLayer(drawUnit.GetUnit(units[0].Owner), t.Position); if (units.Length == 1) { continue; } AddLayer(drawUnit.GetUnit(units[0].Owner), t.Position.X - 1, t.Position.Y - 1); } if (Game.MovingUnit != null && (Settings.RevealWorld || Game.Human == Game.MovingUnit.Owner || Game.Human.Visible(Game.MovingUnit.Tile))) { IUnit unit = Game.MovingUnit; if (renderTiles.Any(t => (t.Tile.X == unit.X && t.Tile.Y == unit.Y))) { RenderTile tile = renderTiles.First(t => (t.Tile.X == unit.X && t.Tile.Y == unit.Y)); AddLayer(unit.GetUnit(unit.Owner), tile.Position.X + unit.Movement.X, tile.Position.Y + unit.Movement.Y); if (unit is IBoardable && tile.Tile.Units.Any(u => u.Class == UnitClass.Land && (tile.Tile.City == null || (tile.Tile.City != null && unit.Sentry)))) { // If there are units on the ship, draw a stack AddLayer(unit.GetUnit(unit.Owner), tile.Position.X + unit.Movement.X - 1, tile.Position.Y + unit.Movement.Y - 1); } } return(true); } _update = false; return(true); } return(false); }