Exemple #1
0
    /// <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);
    }
Exemple #3
0
    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);
            }
        }
    }
Exemple #4
0
 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);
 }
Exemple #5
0
    //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);
    }
Exemple #6
0
    /// <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);
    }
Exemple #7
0
    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);
        }
    }
Exemple #8
0
    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));
    }
Exemple #9
0
        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);
        }