Sign in to follow this  
Jouei

Problem with IsoMetric Render (C# XNA) (Solved)

Recommended Posts

Hello i am quite new at Isometric style rendering and i was reading through a tutorial at this location http://www.ziggyware.com/readarticle.php?article_id=244 it seems fairly complex and half of the tutorial is really in the source that was available for download. See the source and everything runs fine i am just getting some unexpected behavior i wish to fix and i can not seem to figure out where i need to fix it. 1 Particular issue i would like to fix but have been unable to do so is when you use the arrow keys to move the map around and the map goes outside the bounds of the rendering window instead of just one square or GRID being removed from display the whole row it belong to stops being displayed as well witch is really not good IMO. The second issue arises with the arrow keys movements now the particular behavior it has is understandable because when you can no longer go in one direction say down for instance the grid moves on and angle in the only available direction so to speak.. this one is hard to explain but needless to say i would like a suggestion at how to fix this problem a solution is not so much required :p I am going to post my render code and then associated functions as well but separately i do not expect anyone wants to read through it but i am hoping someone will spare the time to help me out. Rendering Funtion
public void Render(SpriteBatch sb, ref Point CenterGrid, int DisplayAreaWidth,
 int DisplayAreaHeight, int ScreenWidth, int ScreenHeight)
        {
            int CurX, CurY;			// this tell us which grid coordinate we're dealing with
            int LimitX, LimitY;		// these tell us which coordinate limits on each axis we are currently processing
            int OriginX, OriginY;	// these mark our starting grid for the entire area
            int StartX, StartY;		// these mark our starting grid for the line
            int EndX, EndY;			// these mark our ending grid for the line
            int SpriteOriginX, SpriteOriginY;	// these mark the rendering location of the first grid, serves as a rendering origin for all grids
            Vector2 Sprite;			// this is used to store the current grid's rendering location
            bool bEdgeX, bEdgeY;	// these get used to know when we finish rendering the desired area
            bool bProcessNRA;		// this tells us to process NoRenderAreas even if we added one immediately
            MapGrid CurGrid;		// this tells us which grid we are currently processing

            // this is how we know whether or not we've processed a grid without having to touch every grid an extra time
            ProcessID++;

            // clear out our deferred grids list so it's fresh for this frame
            DeferredGrids.Clear();
            NoRenderAreas.Clear();

            // initialize our values to the first grid we'll process
            OriginX = StartX = LimitX = CenterGrid.X - DisplayAreaWidth / 2;
            OriginY = StartY = LimitY = CenterGrid.Y - DisplayAreaHeight / 2;

            // ensure we aren't starting outside the bounds of our grid array
            if (LimitX < 0) OriginX = StartX = LimitX = 0;
            if (LimitY < 0) OriginY = StartY = LimitY = 0;

            // initialize our ending grid
            EndX = StartX + DisplayAreaWidth - 1;
            EndY = StartY + DisplayAreaHeight - 1;

            // ensure we aren't ending outside the bounds of our grid array
            if (EndX >= Width) EndX = Width - 1;
            if (EndY >= Height) EndY = Height - 1;

            // initialize the sprite rendering origin coordinates
            SpriteOriginX = (ScreenWidth / 2) + (ScreenWidth % 2) -
 FloorTileWidthHalf;
            SpriteOriginY = (ScreenHeight / 2) + (ScreenHeight % 2) + 
FloorTileHeightHalf;
            SpriteOriginX -= (CenterGrid.X - StartX) * FloorTileWidthHalf - 
(CenterGrid.Y - StartY) * FloorTileWidthHalf;
            SpriteOriginY -= (CenterGrid.X - StartX) * FloorTileHeightHalf + 
(CenterGrid.Y - StartY) * FloorTileHeightHalf;


            // start our SpriteBatch
            sb.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate,
 SaveStateMode.None);

            // we're rendering diagonally across the array
            // this means we don't have to precompute how many iterations we're
            //doing, as that gets tested later on
            while (true)
            {
                // start a new line
                CurX = StartX;
                CurY = LimitY;

                
                // reset our edge values
                bEdgeX = bEdgeY = false;

                // iterate across the line of grids
                while ((CurX <= LimitX) && (CurY >= StartY))
                {
                    CurGrid = Grids[CurX, CurY];
                    if ((CurGrid != null) && (CurGrid.ProcessID != ProcessID)
                    {
                        // every grid gets checked to see if it's in a 
                        //NoRenderArea's shadow
                        if (ShouldDeferRender(CurGrid))
                        {
                            DeferredGrids.Add(CurGrid);
                            CurX++;
                            CurY--;
                            continue;
                        }
                        
                        // this is how we flag the grid as being processed
                        CurGrid.ProcessID = ProcessID;
                        // if there's no sprite associated with this grid, 
                        //continue on
                        if (CurGrid.Texture == null)
                        {
                            CurX++;
                            CurY--;
                            continue;
                        }
                        // set our sprite rendering coordinates
                        Sprite.X = SpriteOriginX + (CurX - OriginX) * 
FloorTileWidthHalf - (CurY - OriginY) * FloorTileWidthHalf +
 FloorTileWidthHalf - CurGrid.Texture.Width / 2;
                        Sprite.Y = SpriteOriginY + (CurX - OriginX) * 
FloorTileHeightHalf + (CurY - OriginY) * FloorTileHeightHalf -
 CurGrid.Texture.Height;
                        // draw this grid
                        sb.Draw(CurGrid.Texture, Sprite, Color.White);

                        bProcessNRA = false;

                        // check if there's an object to render on this grid
                        if ((CurGrid.Object != null) && (CurGrid.Object.Texture
 != null))
                        {
                            // always immediately render single grid objects
                            if ((CurGrid.Object.Width == 1) && (CurGrid.Object.Height == 1))
                            {
                                Sprite = GetGridRenderLocation(CurGrid.Coordinate.X, CurGrid.Coordinate.Y,
 ref CenterGrid, DisplayAreaWidth, DisplayAreaHeight, ScreenWidth, 
ScreenHeight);
                                Sprite.X += FloorTileWidthHalf - CurGrid.Object.Texture.Width / 2;
                                Sprite.Y -= CurGrid.Object.Texture.Height;
                                sb.Draw(CurGrid.Object.Texture, Sprite, Color.White);
                            }
                            else if (!IsInNoRenderAreaList(CurGrid.Object))
                            {
                                // create a NoRenderArea that covers this object
                                AddToNoRenderAreaList(CurGrid.Object, 
ref CenterGrid, DisplayAreaWidth, DisplayAreaHeight);

                                // it's possible that this object's edges have
                                // already been processed, so we flag for an 
                                //immediate processing of NoRenderAreas
                                if (NoRenderAreas[NoRenderAreas.Count - 1].bLeftEdge 
&& NoRenderAreas[NoRenderAreas.Count - 1].bRightEdge)
 bProcessNRA = true;
                            }
                        }
                        
                        if (bProcessNRA || (CurGrid.Object == null))
                        {
                            // check to see if this grid enables us to render a deferred object
                            for (int i = 0; i < NoRenderAreas.Count; i++)
                            {
                                // when both edges are flagged, we want to render the object immediately
                                if (NoRenderAreas[i].bLeftEdge && NoRenderAreas[i].bRightEdge)
                                {
                                    // first render all grids the object is on
                                    RenderUnderObject(NoRenderAreas[i].Object, sb, ref CenterGrid, DisplayAreaWidth, DisplayAreaHeight, ScreenWidth, ScreenHeight);
                                    // render object now
                                    Sprite = GetGridRenderLocation(NoRenderAreas[i].Object.Location.X, 
NoRenderAreas[i].Object.Location.Y, ref CenterGrid, DisplayAreaWidth, 
DisplayAreaHeight, ScreenWidth, ScreenHeight);

                                    Sprite.X += FloorTileWidthHalf + 
FloorTileWidthHalf * (NoRenderAreas[i].Object.Width - 
NoRenderAreas[i].Object.Height) / 2 - NoRenderAreas[i].Object.Texture.Width / 2;

                                    Sprite.Y += FloorTileHeightHalf * 
(NoRenderAreas[i].Object.Width + NoRenderAreas[i].Object.Height - 2) - 
NoRenderAreas[i].Object.Texture.Height;
                                    sb.Draw(NoRenderAreas[i].Object.Texture,
 Sprite, Color.White);
                                    // remove this NoRenderArea
                                    RemoveFromNoRenderAreaList(NoRenderAreas[i--].Object);
                                    // we may have made some deferred grids 
                                    //renderable now
                                    RenderDeferred(sb, ref CenterGrid, 
DisplayAreaWidth, DisplayAreaHeight,
ScreenWidth, ScreenHeight);
                                }
                            }
                        }
                    }

                    // move to the next grid on the line
                    CurX++;
                    CurY--;
                }

                // increment our X axis limit
                if (++LimitX > EndX)
                {
                    // this means our diagonal traversals are hitting the right most edge of the area
                    LimitX = EndX;
                    // we've also hit the bottom most edge of the area
                    if (++StartY > EndY)
                    {
                        StartY = EndY;
                        bEdgeY = true;
                    }
                }

                // increment our Y axis limit
                if (++LimitY > EndY)
                {
                    // this means our diagonal traversals are hitting the bottom most edge of the area
                    LimitY = EndY;
                    // we've also hit the right most edge of the area
                    if (++StartX > EndX)
                    {
                        StartX = EndX;
                        bEdgeX = true;
                    }
                }

                // hitting both edges at the same time means we've run out of diagonals
                if (bEdgeX && bEdgeY) break;
            }

            sb.End();

            return;
        }
    }




Associated funtions
       public void ReplaceGridWith(int x, int y, MapGrid nmg)
        {
            nmg.Coordinate.X = x;
            nmg.Coordinate.Y = y;
            Grids[x, y] = nmg;

            return;
        }

        public void SetObjectLocation(MapObject mo, Point p)
        {
            if (mo.Map != this)
            {
                if (mo.Map != null) mo.Map.ClearObjectLocation(mo);
                mo.Map = this;
            }
            else ClearObjectLocation(mo);

            MapGrid mg;
            mo.Location = p;
            for (int y = p.Y; y < p.Y + mo.Height; y++)
                for (int x = p.X; x < p.X + mo.Width; x++)
                {
                    mg = GetGrid(x, y);
                    if (mg != null) mg.Object = mo;
                }

            return;
        }

        public void ClearObjectLocation(MapObject mo)
        {
            if (mo == null) return;

            MapGrid mg;
            for (int y = mo.Location.Y; y < mo.Location.Y + mo.Height; y++)
                for (int x = mo.Location.X; x < mo.Location.X + mo.Width; x++)
                {
                    mg = GetGrid(x, y);
                    if (mg != null) mg.Object = null;
                }

            return;
        }

        public Vector2 GetGridRenderLocation(int x, int y, ref Point CenterGrid, int DisplayAreaWidth, int DisplayAreaHeight, int ScreenWidth, int ScreenHeight)
        {
            Vector2 v;

            int OriginX = CenterGrid.X - DisplayAreaWidth / 2;
            int OriginY = CenterGrid.Y - DisplayAreaHeight / 2;

            if (OriginX < 0) OriginX = 0;
            if (OriginY < 0) OriginY = 0;

            // initialize v to the base render point of a grid at the center of the screen
            v.X = (ScreenWidth / 2) + (ScreenWidth % 2) - FloorTileWidthHalf;
            v.Y = (ScreenHeight / 2) + (ScreenHeight % 2) + FloorTileHeightHalf;
            // offset v to the base render point of CenterGrid
            v.X -= (CenterGrid.X - OriginX - CenterGrid.Y + OriginY) * FloorTileWidthHalf;
            v.Y -= (CenterGrid.X - OriginX + CenterGrid.Y - OriginY) * FloorTileHeightHalf;
            // offset v to the final base render point
            v.X += (x - OriginX - y + OriginY) * FloorTileWidthHalf;
            v.Y += (x - OriginX + y - OriginY) * FloorTileHeightHalf;

            return v;
        }

        private void AddToNoRenderAreaList(MapObject mo, ref Point CenterGrid, int DisplayAreaWidth, int DisplayAreaHeight)
        {
            if (mo == null) return;

            NoRenderArea nra = NRAPool.GetObject();

            int StartX = CenterGrid.X - DisplayAreaWidth / 2;
            int StartY = CenterGrid.Y - DisplayAreaHeight / 2;
            if (StartX < 0) StartX = 0;
            if (StartY < 0) StartY = 0;

            int EndX = StartX + DisplayAreaWidth - 1;
            int EndY = StartY + DisplayAreaHeight - 1;
            if (EndX > Width - 1) EndX = Width - 1;
            if (EndY > Height - 1) EndY = Height - 1;

            nra.Object = mo;

            // if the left edge is outside the display area, cue the edge as already good
            if (mo.Location.X <= StartX)
            {
                //nra.x = StartX;
                nra.x = mo.Location.X;
                nra.bLeftEdge = true;
            }
            else
            {
                nra.x = mo.Location.X;
                nra.bLeftEdge = false;
            }

            // if the right edge is outside the display area, cue the edge as already good
            if (mo.Location.Y <= StartY)
            {
                //nra.y = StartY;
                nra.y = mo.Location.Y;
                nra.bRightEdge = true;
            }
            else
            {
                nra.y = mo.Location.Y;
                nra.bRightEdge = false;
            }

            // double check that the right edge is inside the display area and set the width
            if (nra.x + mo.Width - 1 >= EndX)
            {
                nra.w = EndX - nra.x + 1;
                nra.bRightEdge = true;
            }
            else nra.w = mo.Width;

            // double check that the left edge is inside the display area and set the height
            if (nra.y + mo.Height - 1 >= EndY)
            {
                nra.h = EndY - nra.y + 1;
                nra.bLeftEdge = true;
            }
            else nra.h = mo.Height;

            MapGrid mg;
            // double check that the left edge is a grid that hasn't been processed yet
            mg = GetGrid(nra.x - 1, nra.y + nra.h);
            if ((mg == null) || (mg.ProcessID == ProcessID)) nra.bLeftEdge = true;
            // double check that the right edge is a grid that hasn't been processed yet
            mg = GetGrid(nra.x + nra.w, nra.y - 1);
            if ((mg == null) || (mg.ProcessID == ProcessID)) nra.bRightEdge = true;

            NoRenderAreas.Add(nra);

            return;
        }

        private void RemoveFromNoRenderAreaList(MapObject mo)
        {
            for (int i = 0; i < NoRenderAreas.Count; i++)
            {
                if (NoRenderAreas[i].Object == mo)
                {
                    NoRenderAreas[i].Object = null;
                    NRAPool.ReturnObject(NoRenderAreas[i]);
                    NoRenderAreas.RemoveAt(i);
                    return;
                }
            }

            return;
        }

        private bool IsInNoRenderAreaList(MapObject mo)
        {
            return (NoRenderAreas.Find(n => n.Object == mo) != null);
        }

        private void RenderAreaCornerCheck(MapGrid mg)
        {
            if (mg == null) return;

            for (int i = 0; i < NoRenderAreas.Count; i++)
            {
                // check for mg being a corner of a NoRenderArea
                if ((mg.Coordinate.X == NoRenderAreas[i].x - 1) && (mg.Coordinate.Y == NoRenderAreas[i].y + NoRenderAreas[i].h))
                    NoRenderAreas[i].bLeftEdge = true;

                if ((mg.Coordinate.X == NoRenderAreas[i].x + NoRenderAreas[i].w) && (mg.Coordinate.Y == NoRenderAreas[i].y - 1))
                    NoRenderAreas[i].bRightEdge = true;
            }

            return;
        }

        private bool ShouldDeferRender(MapGrid mg)
        {
            if (mg == null) return false;

            RenderAreaCornerCheck(mg);

            for (int i = 0; i < NoRenderAreas.Count; i++)
            {
                if (mg.Object == NoRenderAreas[i].Object) continue;

                if ((mg.Coordinate.X >= NoRenderAreas[i].x) && (mg.Coordinate.Y >= NoRenderAreas[i].y)) return true;
            }

            return false;
        }

        private void RenderUnderObject(MapObject mo, SpriteBatch sb, ref Point CenterGrid, int DisplayAreaWidth, int DisplayAreaHeight, int ScreenWidth, int ScreenHeight)
        {
            MapGrid mg;
            Vector2 Sprite;

            // we render by rows instead of diagonals to gain a bit of speed boost
            // only the grids along the edge of the object will be seen, and the very large odds are nobody will ever be able to visually tell the difference
            for (int y = mo.Location.Y; y < mo.Location.Y + mo.Height; y++)
                for (int x = mo.Location.X; x < mo.Location.X + mo.Width; x++)
                {
                    mg = GetGrid(x, y);
                    if (mg == null) continue;
                    // need to make sure we give this grid the chance to flag an edge of another NoRenderArea
                    RenderAreaCornerCheck(mg);
                    // don't process a grid more than once
                    if (mg.ProcessID != ProcessID)
                    {
                        mg.ProcessID = ProcessID;
                        if (mg.Texture == null) continue;
                        Sprite = GetGridRenderLocation(x, y, ref CenterGrid, DisplayAreaWidth, DisplayAreaHeight, ScreenWidth, ScreenHeight);
                        Sprite.X += FloorTileWidthHalf - mg.Texture.Width / 2;
                        Sprite.Y -= mg.Texture.Height;
                        sb.Draw(mg.Texture, Sprite, Color.White);
                    }
                }

            return;
        }

        private void RenderDeferred(SpriteBatch sb, ref Point CenterGrid, int DisplayAreaWidth, int DisplayAreaHeight, int ScreenWidth, int ScreenHeight)
        {
            Vector2 Sprite;

            for (int i = 0; i < DeferredGrids.Count; i++)
            {
                if (!ShouldDeferRender(DeferredGrids[i]))
                {
                    // ensure we process this grid only once
                    DeferredGrids[i].ProcessID = ProcessID;

                    Sprite = GetGridRenderLocation(DeferredGrids[i].Coordinate.X, DeferredGrids[i].Coordinate.Y, ref CenterGrid, DisplayAreaWidth, DisplayAreaHeight, ScreenWidth, ScreenHeight);

                    if (DeferredGrids[i].Texture != null)
                    {
                        Sprite.X += FloorTileWidthHalf - DeferredGrids[i].Texture.Width / 2;
                        Sprite.Y -= DeferredGrids[i].Texture.Height;
                        sb.Draw(DeferredGrids[i].Texture, Sprite, Color.White);
                    }

                    DeferredGrids.RemoveAt(i--);
                }
            }

            return;
        }





I know it is a rather large amount of code to go through and i am sure to be flamed for not figuring it on my own but believe me i have tried and am just not getting anywhere. Here is hoping to not get flamed to much. Regards Jouei. [Edited by - Jouei on August 19, 2009 1:36:33 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this