13 Replies - 2291 Views - Last Post: 09 May 2012 - 06:54 AM

#1 ArchColossus  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 22-February 12

Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 06:37 AM

I've been having trouble with this for some time now, and my programming knowledge is all from sample programs, reverse engineering, and some basic 2D things. And I mean VERY basic. I'm looking for a tutorial on generating a 3D grid-based map from a bitmap file. I have reading the bitmap down, and I understand it, but getting the map to generate in the correct places is evading me. And before telling me to take a class (Which I understand is probably most helpful.), I'm in one right now, but I'm the one writing the classes tutorials, and I won't be able to find a better class until I graduate high-school. I also lack funds to purchase any books, so anything free is preferred. Thank you in advance, as this community has been very helpful so far in learning XNA.

Is This A Good Question/Topic? 0
  • +

Replies To: Does anyone know of a tutorial teaching grid-based map generation?

#2 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 07:40 AM

I'm actually kind of working on writing a tutorial on this subject and then going beyond it. I'm still working on building my examples. Although, I'm probably at a place where I could write the part on terrain generation.

A really good tutorial on basic 3D terrain generation is Riemer's tutorial. I'm sure several other people here would recommend it to you.

http://www.riemers.n...arp/series1.php

One of the things I don't like about Riemer's tutorial, and most of the one's out there, is that they use HLSL for even the most simple things, such as drawing a terrain. Now granted, Riemer goes into some things such as water that may require HLSL. But the terrain itself can be done with BasicEffect with texture and lighting included.

Maybe I can post some of my code for you. My current code has billboard trees and a lot of stuff that goes beyond just the terrain. Maybe I can include some of my older code. The problem with my older code is it was written in XNA 3.0 and you have to make some pretty major changes in the Draw method to port it over to 4.0. Now that I think about it, I think Riemer's examples are partially written for 3.0 and partially written for 4.0. That may cause problems for you too.

I don't want to give you my 3.0 code if you're working in 4.0, because I think it will just confuse you unless you have ported over a fair amount of 3.0 code to 4.0.

Looking at my current 4.0 code, there's quite a bit of code considering all it does is a 3D terrain, skybox, trees, and some basic collision detection. And I am currently rewritting the forest building routine to place the trees according to a bitmap rather then their current random positions. (I'm looking closely at Riemer's example for this, by the way.)


Maybe I'll just post the sections of my code responsible for drawing the terrain and making it "solid" through collision detection. I hate to post all of it, because there's quite a bit of code that has little to do with the terrain, and I use a quite a few files for textures and things that I don't think I can include here.

My goal is to eventually put the whole thing, along with the files on a website as a tutorial, but I don't have the website yet, and I kind of wanted to get a lot more into my tutorial than I'm currently ready to teach. Anyway, I'll try and paste some code for you here.
Was This Post Helpful? 0
  • +
  • -

#3 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 07:51 AM

Here's some of the code from my current program that uses a 3D terrain from a 1024 heightmap. I wasn't really ready to go public with this code; it's not all that well commented and needs to be cleaned up quite a bit. But it's what I have at the moment.



Declarations
        GraphicsDeviceManager graphics;
        GraphicsDevice GraphicsCard;
        BasicEffect Shader;
        AlphaTestEffect ATE;
        VertexPositionNormalTexture[] TerrainVertices;
        int[] TerrainIndices;
        private int TerrainXSize = 512;   //Size must be an odd number to divide evenly and balance
        private int TerrainZSize = 512;   //Size must be an odd number to divide evenly and balance
        private float[,] TerrainHeightData; //Two dimensional array of x,z where the float value is height(y)
        private float[,] ForestTreeData;
        private int ForestMapResolution = 1024;
        List<TreeModel> TreeList;

        VertexPositionTexture[] SkyVertices;
        int[] SkyIndices;
        Model SkyBox;
        Matrix SkyBoxWorldMatrix;
        Matrix WorldMatrix = Matrix.Identity;
        BasicCamera Camera;
        Texture2D TerrainHeightMap;
        Texture2D ForestMap;
        int GridScale = 6;
        float HeightScale = 300.0f;     //Scale the height of the terrain. This number should be the max height.
        Random RandomNumberGenerator;
        Texture2D GridTexture;
        Texture2D SkyTexture;




Load method

            LoadTerrainHeightData();
            TerrainVertices = SetTerrainVertices();
            SetTerrainIndices();
            TerrainVertices = CalculateNormals(TerrainVertices, TerrainIndices);

                   
            Camera = new BasicCamera(new Vector3(0.0f, TerrainHeightBelow(0, 0), 0.0f), GraphicsDevice.Viewport.AspectRatio);
            Camera.Ground(TerrainHeightBelow(0, 0));




Methods that get the work done

        private void LoadTerrainHeightData()
        {
            float HeightValue = 0.0f;
            Color[] HeightData;

            TerrainHeightMap = Content.Load<Texture2D>("TemperateHeight");
            TerrainHeightData = new float[TerrainXSize, TerrainZSize];
            HeightData = new Color[TerrainXSize * TerrainZSize];
            TerrainHeightMap.GetData<Color>(HeightData);


            for (int z = 0; z < TerrainZSize; z++)
            {
                for (int x = 0; x < TerrainXSize; x++)
                {
                    HeightValue = HeightData[z * TerrainXSize + x].R;   //Get gray scale value from Height Map as a value 0-255
                    HeightValue = HeightValue / 255.0f;                 //Normalize Height to value between zero and one
                    TerrainHeightData[x, z] = HeightValue * HeightScale;
                }
            }
        }


        private VertexPositionNormalTexture[] SetTerrainVertices()
        {
            VertexPositionNormalTexture[] TerrainVertices = new VertexPositionNormalTexture[TerrainXSize * TerrainZSize];   //The vertex array index is ALWAYS positive.
            float XMidPoint = TerrainXSize / 2;     //Find the vertex in the middle
            float ZMidPoint = TerrainZSize / 2;     //Find the vertex in the middle


            for (int x = 0; x < TerrainXSize; x++)
            {
                for (int z = 0; z < TerrainZSize; z++)
                {
                    TerrainVertices[x + z * TerrainXSize].Position = new Vector3((x - XMidPoint) * GridScale, TerrainHeightData[x, z], (z - ZMidPoint) * GridScale);
                    TerrainVertices[x + z * TerrainXSize].TextureCoordinate.X = (float)x / 33.2f * GridScale;
                    TerrainVertices[x + z * TerrainXSize].TextureCoordinate.Y = (float)z / 25.4f * GridScale;
                }
            }
            return TerrainVertices;
        }


        private void SetTerrainIndices()
        {
            //Indices need to be wound in clockwise order in XNA.
            TerrainIndices = new int[(TerrainXSize - 1) * (TerrainZSize - 1) * 6];  //-1 because arrays number from 0,0
            int counter = 0;
            //Look at every vertex left to right and then every row back to front and set indices
            for (int z = 0; z < TerrainZSize - 1; z++)      //Go through the vertices from back to front
            {
                for (int x = 0; x < TerrainXSize - 1; x++)  //Go through the vertices from left to right
                {
                    int UpperLeft = x + z * TerrainXSize;
                    int UpperRight = (x + 1) + z * TerrainXSize;
                    int LowerLeft = x + (z + 1) * TerrainXSize;
                    int LowerRight = (x + 1) + (z + 1) * TerrainXSize;

                    //Top Triangle
                    TerrainIndices[counter++] = LowerLeft;
                    TerrainIndices[counter++] = UpperLeft;
                    TerrainIndices[counter++] = UpperRight;
                    //Bottom Triangle
                    TerrainIndices[counter++] = UpperRight;
                    TerrainIndices[counter++] = LowerRight;
                    TerrainIndices[counter++] = LowerLeft;

                    //The shared edge between the two triangles is the one "implied".
                }
            }
        }

        private VertexPositionNormalTexture[] CalculateNormals(VertexPositionNormalTexture[] Vertices, int[] Indices)
        {
            for (int i = 0; i < Vertices.Length; i++)
                Vertices[i].Normal = new Vector3(0, 0, 0);

            for (int i = 0; i < Indices.Length / 3; i++)
            {
                int Index1 = Indices[i * 3];
                int Index2 = Indices[i * 3 + 1];
                int Index3 = Indices[i * 3 + 2];

                Vector3 Side1 = Vertices[Index1].Position - Vertices[Index3].Position;
                Vector3 Side2 = Vertices[Index1].Position - Vertices[Index2].Position;
                Vector3 Normal = Vector3.Cross(Side1, Side2);

                Vertices[Index1].Normal += Normal;
                Vertices[Index2].Normal += Normal;
                Vertices[Index3].Normal += Normal;
            }

            for (int i = 0; i < Vertices.Length; i++)
                Vertices[i].Normal.Normalize();

            return Vertices;
        }




Collision Detection

        private float TerrainHeightBelow(float x, float z)
        {
            float HeightOfTerrain = 0.0f;
            //Quad has edge between LowerLeft and UpperRight... I think...
            float QuadUpperLeft;
            float QuadLowerLeft;
            float QuadUpperRight;
            float QuadLowerRight;
            float XInQuad = 0.0f;
            float ZInQuad = 0.0f;
            int X = 0;
            int Z = 0;
            float HeightBetweenULandLL = 0.0f;
            float HeightBetweenURandLR = 0.0f;


            //We set the starting point as "center of the grid" and have to offset for that
            x = (x / GridScale) + TerrainXSize / 2;
            z = (z / GridScale) + TerrainZSize / 2;


            X = (int)Math.Floor(x);
            Z = (int)Math.Floor(z);

            XInQuad = x - X;
            ZInQuad = z - Z;

            //Get the height at every corner of the quad (square)
            QuadUpperLeft = TerrainHeightData[X, Z];
            QuadLowerLeft = TerrainHeightData[X, Z + 1];
            QuadUpperRight = TerrainHeightData[X + 1, Z];
            QuadLowerRight = TerrainHeightData[X + 1, Z + 1];

            //If the coordinate is out of bounds (off the grid) just set height to max
            if ((x < 1) || (z < 1) || (x > TerrainXSize - 2) || (z > TerrainZSize - 2))
            {
                HeightOfTerrain = HeightScale;
            }
            else
            {
                //This next formula is the key even though it may be confusing at first glance.
                //Get the height between the two left corners. Then get the height between the right two corners.
                //Then get the height between those two heights. XInQuad is the percentage of the way between
                //the left and right side of the quad. ZInQuad is the percentage of the way between the top
                //of the quad and the bottom. Weighting the average of the four points based on how close the 
                //point being analyzed is to each of the four corners and the height at that corner.
                HeightBetweenULandLL = QuadUpperLeft * (1 - ZInQuad) + QuadLowerLeft * ZInQuad;
                HeightBetweenURandLR = QuadUpperRight * (1 - ZInQuad) + QuadLowerRight * ZInQuad;
                HeightOfTerrain = HeightBetweenULandLL * (1 - XInQuad) + HeightBetweenURandLR * XInQuad;

            }

            return HeightOfTerrain;
        }

        private bool CausesCollision(Vector3 CameraMovement)
        {
            bool CollisionCaused = false;
            int BarrierFromEdge = 120 * GridScale;

            if (CameraMovement.X <= -(((TerrainXSize * GridScale) - BarrierFromEdge) / 2)
                || CameraMovement.X >= (((TerrainXSize * GridScale) - BarrierFromEdge) / 2)
                || CameraMovement.Z <= -(((TerrainZSize * GridScale) - BarrierFromEdge) / 2)
                || CameraMovement.Z >= (((TerrainZSize * GridScale) - BarrierFromEdge) / 2))
            {
                CollisionCaused = true;
            }

            return CollisionCaused;
        }



Update Method
        protected override void Update(GameTime gameTime)
        {
            KeyboardState KBState;

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            KBState = Keyboard.GetState();
            if (KBState.IsKeyDown(Keys.Escape)) this.Exit();


            if (KBState.IsKeyDown(Keys.Up) || KBState.IsKeyDown(Keys.W))
            {

                if (!CausesCollision(Camera.TestForward2D()))
                {
                    Camera.Forward2D();
                    Camera.Ground(TerrainHeightBelow(Camera.XPosition(), Camera.ZPosition()));
                }

            }
            else
            {
                //Play footstep sound effect code removed here for berevity
            };

            if (KBState.IsKeyDown(Keys.Down) || KBState.IsKeyDown(Keys.S))
            {
                Camera.Back2D();
                Camera.Ground(TerrainHeightBelow(Camera.XPosition(), Camera.ZPosition()));

            };

            if (KBState.IsKeyDown(Keys.Left) || KBState.IsKeyDown(Keys.A))
            {
                Camera.Rotate2D(0.035f);
            };

            if (KBState.IsKeyDown(Keys.Right) || KBState.IsKeyDown(Keys.D))
            {
                Camera.Rotate2D(-0.035f);
            };

            if (KBState.IsKeyDown(Keys.Q) || KBState.IsKeyDown(Keys.PageDown))
            {
                Camera.PitchView(-0.01f);
            }

            if (KBState.IsKeyDown(Keys.E) || KBState.IsKeyDown(Keys.PageUp))
            {
                Camera.PitchView(0.01f);
            }

            base.Update(gameTime);
        }




Draw Method
            //Render Terrain
            RS = new RasterizerState();
            RS.CullMode = CullMode.CullCounterClockwiseFace; //Do not show the back side of the mesh
            RS.FillMode = FillMode.Solid;
            GraphicsCard.RasterizerState = RS;

            Shader.View = Camera.View();
            Shader.Projection = Camera.Projection();
            Shader.World = WorldMatrix;

            Shader.LightingEnabled = true;
            Shader.TextureEnabled = true;
            Shader.AmbientLightColor = Color.DarkSlateBlue.ToVector3();     //Dark Cloud color
            Shader.DiffuseColor = Color.White.ToVector3();
            Shader.SpecularColor = Color.Black.ToVector3();
            Shader.FogEnabled = true;
            Shader.Alpha = 1f;
            Shader.FogColor = Color.SlateGray.ToVector3();
            //Very good fog setting but you can see the edge of the terrain
            //Shader.FogStart = 45.0f;    //Fog start distance
            //Shader.FogEnd = 450.0f;     //Fog begins max, distance

            Shader.FogStart = 45.0f;    //Fog start distance
            Shader.FogEnd = 250.0f;     //Fog begins max, distance
            Shader.PreferPerPixelLighting = true;
            Shader.DirectionalLight0.Enabled = true;
            Shader.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-700f, -4000f, -3700f));
            Shader.DirectionalLight0.DiffuseColor = Color.Goldenrod.ToVector3();
            Shader.DirectionalLight0.SpecularColor = Color.Black.ToVector3();
            Shader.SpecularPower = 0.6f;
            Shader.Texture = GridTexture;

            foreach (EffectPass pass in Shader.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsCard.DepthStencilState = DepthStencilState.Default;
                GraphicsCard.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, TerrainVertices, 0, TerrainVertices.Length,
                    TerrainIndices, 0, TerrainIndices.Length / 3);
            }

            base.Draw(gameTime);



Was This Post Helpful? 0
  • +
  • -

#4 ArchColossus  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 22-February 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 07:56 AM

I've actually done riemers tutorials, and have tried to edit the flight simulator's code to generate a dungeon rather than a city, but ran into a problem with changing cameras and getting the pieces to fit together correctly. I'm actually trying to make something similar to Markus Persson (Notch)'s Prelude of the Chambered, if that clears things a bit.

Oh, wow. You posted that while I was posting so I didn't see it. But from reading through your code, that looks very helpful. I'll take a look in Visual Studio.
Was This Post Helpful? 0
  • +
  • -

#5 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 07:57 AM

Here's the code for my camera class "BasicCamera". I'm including the whole thing. It's commented reasonably well, because I was finished with it for the moment and had considered using it as an example. But, I'm already realizing there are some major improvements that can be made. The main one I thought of was to make an abstract camera class and this class should be inherited from that class. The idea being that you should have different camera types inheriting basic camera attributes from the abstract camera class.

Anyway, this code works, and works pretty well. CameraImpulse controls your movement speed and you may want to adjust that, since it's set to something close to what feels like a normal walking pace.

This whole thing was in a BasicCamera.cs file that's included in the project. As long as it's in the same namespace as the game code, it should work together with it pretty easily.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace TreeModels
{
    class BasicCamera
    {
        Matrix ViewMatrix;
        Matrix ProjectionMatrix;
        Vector3 CameraFacing;           //3D direction the camera is facing
        Vector3 CameraPosition;         //3D position of camera in world
        float FieldOfView;              //Camera Field of View (view width) in Radians
        //float AspectRatio;              // 4:3 or 16:9 are typical aspect ratios for a screen
        float NearPlaneDistance;        //Clip view image starting here
        float FarPlaneDistance;         //Clip image further than this distance
        float CameraHeight;
        float PitchPercentage;          //Percentage of the max angle that the camera is currently pitched.
        float MaxPitchRadians;          //Maximum angle that the camera can pitch up or down.
        float CameraImpulse;            //Distance camera will move forward when told to

        public BasicCamera(Vector3 CameraStartPosition, float AspectRatio)
        //===================================================================================
        // BasicCamera()
        //
        // Purpose: Constructor for the class initializes the class on creation.
        // Parameters:  CameraStartPosition = The x,y,z coordinates in world space to place the camera at.
        //              AspectRation = The ratio between screen width & height.
        // Returns: void
        //===================================================================================
        {
            FieldOfView = MathHelper.ToRadians(45);     //Width the camera can see. Camparable to Focal Length of the Lens.
            NearPlaneDistance = 0.1f;                   //Don't draw closer than this distance.
            FarPlaneDistance = 10000.0f;                 //Don't draw further than this distance.
            CameraPosition = CameraStartPosition;
            CameraImpulse = 2.8f/60;        //Camera is moved every frame at 60 frames a second.
            CameraHeight = 1.8f;            //Eye height of the average man.
            MaxPitchRadians = MathHelper.ToRadians(45.0f);  //Angle from level to max angle that camera can look upwards.
            PitchPercentage = 0.0f;         //Start out looking straight and level.
            ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(FieldOfView, 
                AspectRatio, NearPlaneDistance, FarPlaneDistance);
            CameraStartPosition += new Vector3(0, CameraHeight, 0);     //Camera position at initilization
            CameraFacing = new Vector3(0.0f, 0.0f, -1.0f); //Start by looking North (towards -Z).
            ViewMatrix = Matrix.CreateLookAt(CameraPosition, CameraPosition + CameraFacing, Vector3.Up);
        }
        //===================================================================================


        public void Forward2D()
        //===================================================================================
        // Forward2D()
        //
        // Purpose: Move the camera towards the direction it is facing but keep it at terrain level.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            //Matrix Translation;
            Vector2 CameraDirection;    //Direction camera is facing ignoring any upward or downward facing.
            Vector3 ImpulseVector;      //Speed & Direction to move the camera every frame (60th of a second).

            CameraDirection = new Vector2(CameraFacing.X, CameraFacing.Z);  //Remove any up/down info.
            CameraDirection.Normalize();            //Vector length changed so we want to reset the length to 1.
            CameraDirection *= CameraImpulse;       //Scale the facing vector to get an impulse vector (multiplying by Impulse with increase the length of the vector that amount.)
            ImpulseVector = new Vector3(CameraDirection.X, 0.0f, CameraDirection.Y);    //Now that we have the proper force and direction to apply to the camera turn it back into 3D for permenant storage.
            CameraPosition += ImpulseVector;        //Apply movement force.
        }
        //===================================================================================
        
        
        public Vector3 TestForward2D()
        //===================================================================================
        // TestForward2D()
        //
        // Purpose: Get a vector that is the path of the camera in the next movement to test for collisions.
        // Parameters: 
        // Returns: Vector3 with a vector of where the camera is going if told to go forward.
        //===================================================================================
        {
            //Matrix Translation;
            Vector2 CameraDirection;    //Direction camera is facing ignoring any upward or downward facing.
            Vector3 ImpulseVector;      //Speed & Direction to move the camera every frame (60th of a second).

            CameraDirection = new Vector2(CameraFacing.X, CameraFacing.Z);  //Remove any up/down info.
            CameraDirection.Normalize();            //Vector length changed so we want to reset the length to 1.
            CameraDirection *= CameraImpulse;       //Scale the facing vector to get an impulse vector (multiplying by Impulse with increase the length of the vector that amount.)
            ImpulseVector = new Vector3(CameraDirection.X, 0.0f, CameraDirection.Y);    //Now that we have the proper force and direction to apply to the camera turn it back into 3D for permenant storage.

            return CameraPosition + ImpulseVector;
        }
        //===================================================================================


        public void Back2D()
        //===================================================================================
        // Back2D()
        //
        // Purpose: Move the camera away from the direction it is facing but keep it at terrain level.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            //Matrix Translation;
            Vector2 CameraDirection;
            Vector3 ImpulseVector;

            CameraDirection = new Vector2(CameraFacing.X, CameraFacing.Z);
            CameraDirection.Normalize();
            CameraDirection *= -CameraImpulse;            //Scale the facing vector to get an impulse vector
            ImpulseVector = new Vector3(CameraDirection.X, 0.0f, CameraDirection.Y);
            CameraPosition += ImpulseVector;
        }
        //===================================================================================


        public void Rotate2D(float RotationAngleRadians)
        //===================================================================================
        // Rotate2D()
        //
        // Purpose: Change the direction the camera is facing by a specific degree, but keep it at terrain level.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            Matrix RotationMatrix;
            Vector3 TransformedFacing;

            RotationMatrix = Matrix.CreateRotationY(RotationAngleRadians);  //Rotate horizontally.
            TransformedFacing = Vector3.Transform(CameraFacing, RotationMatrix);    //Rotate.
            CameraFacing = new Vector3(TransformedFacing.X, 0.0f, TransformedFacing.Z); //Ignore any pitch change.
            CameraFacing.Normalize();          //CameraFacing is a Unit vector that should always have a length of 1.
        }
        //===================================================================================


        public void Ground(float TerrainHeight)
        //===================================================================================
        // Ground()
        //
        // Purpose: Reset the elevation of the camera to be standing on the terrain whether it's above or below the terrain.
        // Parameters: TerrainHeight = Elevation of the terrain where the camera is currently positioned. 
        // Notes: TerrainHeight has to be determined using the current camera position. So, this class has
        //          to somehow tell the program what it's position is before the TerrainHeight can be determined, asuming the terrain is not 100% level.
        // Returns: void
        //===================================================================================
        {
            CameraPosition = new Vector3(CameraPosition.X, TerrainHeight + CameraHeight, CameraPosition.Z);
        }
        //===================================================================================


        public void SetCameraVelocity(float CameraVelocity)
        //===================================================================================
        // SetCameraVelocity()
        //
        // Purpose: Allows changing of how fast the camera moves forward/backward.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            CameraImpulse = CameraVelocity;
        }
        //===================================================================================


        public void PitchView(float IncreaseInPitchPercentage)
        //===================================================================================
        // PitchView()
        //
        // Purpose: Causes the camera to look more upwards or downwards
        // Parameters: IncreaseInPitchPercentage
        // Returns: void
        //===================================================================================
        {
            PitchPercentage += IncreaseInPitchPercentage;
            if (PitchPercentage > 1.0f) PitchPercentage = 1.0f;
            if (PitchPercentage < -1.0f) PitchPercentage = -1.0f;
        }
        //===================================================================================


        public Matrix View()
        //===================================================================================
        // View()
        //
        // Purpose: Converts CameraFacing and CameraPostion to a ViewMatrix that can be sent to the shader.
        // Parameters: none
        // Notes: This was created to make it easy to pass a ViewMatrix to the shader. I intentionally
        //          store the camera position and facing seperately and consider them the "true" state.
        //          This allows the data to be converted to a ViewMatrix because that's what the shader
        //          is going to want and it will need to be done often.
        // Returns: ViewMatrix 
        //===================================================================================
        {
            Vector3 CameraLookTowards;

            CameraLookTowards = new Vector3(CameraFacing.X, PitchPercentage, CameraFacing.Z);
            CameraLookTowards += CameraPosition;    //CameraLookTowards is on the ground and must be raised to camera height or the camera will just look at it's "toes".
            ViewMatrix = Matrix.CreateLookAt(CameraPosition, CameraLookTowards, Vector3.Up);    

            return ViewMatrix;
        }
        //===================================================================================


        public Matrix Projection()
        //===================================================================================
        // Projection()
        //
        // Purpose: Makes the primary storage for the projection matrix here in the class.
        // Parameters: none
        // Notes: This should just be passed straight to the shader as the ProjectionMatrix.
        // Returns: ProjectionMatrix
        //===================================================================================
        {
            return ProjectionMatrix;
        }
        //===================================================================================

        public float XPosition()
        {
            return CameraPosition.X;
        }

        public float YPosition()
        {
            return CameraPosition.Y;
        }

        public float ZPosition()
        {
            return CameraPosition.Z;
        }

        public Vector3 Facing()
        {
            return CameraFacing;
        }
    }
}


Was This Post Helpful? 1
  • +
  • -

#6 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 08:03 AM

And I'm open to answering questions about my code. I think I would answer just about any question other than "Why is your code written so poorly?" ;-)

There are quite a few improvements that could be made to this code, even as a "basic" example. One thing is that I use DrawUserPrimitives instead of DrawPrimitives, so my terrain is not stored in the graphics card. It's such a large mesh that it probably should, although it runs fine on my computer.

Eventually, you want to do a Level of Detail optimization to the terrain. ROAM is one of the main algorithms that I'm hearing about for doing this sort of thing. Viewport Frustrum Culling and Quad Trees come up a lot in discussions about the subject.

This code has really no optimizations for performance of the terrain, but it's a pretty good example of how to do the terrain.

Also note, that XNA 4.0 does not support a terrain size greater than basically 512 in a single Draw call. But, the terrain this code produces is fairly large since it allows for horizontal scaling (stretching of the terrain).
Was This Post Helpful? 0
  • +
  • -

#7 ArchColossus  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 22-February 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 08:08 AM

Your Forward2D/Back2D methods are exactly what kind of movement I've been trying to achieve, although I may add strafing if you don't mind me playing around with your code. Now I just need to find a way to get the walls and ceiling of a dungeon crawler environment to cooperate. Thank you for the assist, been stuck on this for a good while now. :D
Was This Post Helpful? 0
  • +
  • -

#8 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 09:00 AM

View PostArchColossus, on 07 May 2012 - 08:08 AM, said:

Your Forward2D/Back2D methods are exactly what kind of movement I've been trying to achieve, although I may add strafing if you don't mind me playing around with your code. Now I just need to find a way to get the walls and ceiling of a dungeon crawler environment to cooperate. Thank you for the assist, been stuck on this for a good while now. :D


Glad you found something that you could use. Feel free to modify and use it. I've been meaning to add strafing myself; I find myself wanting to move to the side all the time, and keep thinking I should add it.

I had started some collision detection with Forward2D, but had not yet added anything for Back2D. So, right now you can walk backwards in places that you can't walk forwards. Adding the backwards collision is just a matter of reversing the vector by making it negative. Also, the collision detection prevents you from walking off the edge of the map, right now with enough extra space that it's hard to see that there is an edge of the map. But the collision vector just tests; it shouldprobably return a shorter vector that can be substitued. What I mean is that if you are moving very fast and your next step will take you through the barrier it won't let you take that next step. But at high speed that means that it won't let you get as close to the barrier as you can at low speed. Instead, it should probably say "this vector won't work, but let me hand you one that will work". That's once of my next improvements to the collision, I think.

I've been thinking about putting a dungeon crawler program and tutorial together eventually too. I'm really disappointed that I have never seen a dungeon crawler tutorial anywhere for XNA. And that type of thing is for a lot more than just dungeons. Space stations would be an example that would almost be the exact same thing with different graphics. But a lot of games have houses and things that you can go inside, that are done in almost the same way. A lot of games have houses that you "zone" into. Never Winter Nights had you click on the front door, as a trigger, then it zoned you into a "house zone" that you could use tools to build. Insidently, Never Winter Nights has a tool set that allows you to modify the game and can teach you a WHOLE lot about game development. That's where I learned a significant amount of what I know about how 3D games are designed.

There are several ways you could put a dungeon program together. I have been thinking about writing a fairly straight forward "dungeon" engine/game. I would probably not use a 3D terrain like the one in the code that I posted. Instead, I would probably define the terrain as Y=0. In other words, I wouldn't bother with a 3D mesh like in my code posted, but instead I would just have my terrain collision to tell the camera that if it ever goes lower than eye level, to reset it to eye level. That's what I do in the code that I posted except the terrain changes elevation across the mesh so it's eye level + current terrain elevation. For a dungeon crawler I would just say terrain height is always zero and forget about the mesh.

But what I would do instead is terrain tiles, just like a 2D game, but in 3D for the floor. I was thinking I might make the grid size 0.5 units and say to myself that the units are meters. I would do a bitmap like a heightmap, but instead of having the grayscale colors be the terrain height, I would have them be a certain type of tile. That would allow me about 255 different tiles to chose from. I could make each tile a seperate texture, and I would probably use two triangles for every tile. The 3D terrain I posted code for is one big mesh. But for my tiles I would make every tile a seperate mesh to make it easy to texture and to make it easy for two tiles next to each other to have entirely different textures. I might use zero as a "no tile" and leave it black; I might not even draw the triangles for that square. And I might write my collision code to prevent the camera from walking into a tile that has "no tile"; you could just look at the bitmap and calculate whether that square is set to zero to determine the collision. So, then you would have a "floor" defined for collision with invisible walls.

Of course you want something that looks like "real walls". What I would probably do is build wall "models" with Blender or Sketchup of some other tool. And I would assign them a bounding box for collision. I would make them the same length as one tile and the same height as the ceiling. The bounding box would be pretty close to the exact shape of the wall. This would allow me to place walls either along the lines of my "invisible" wall defined by the tiles. Or I could just put a wall in the center of the room. The walls would be solid because of collision detection with the bounding box. And you would also have the "no tile" regions as a backup to make them solid.

The only time I would worry about the ceiling being solid is if you can get to it. If you can't fly, climb, or levitate, I probably wouldn't worry about collisions with it. If you needed to you could define a certain height to be an imaginary "ceiling" such as Y=12 and not let the camera go higher than that.

What I would probably do instead is define no cieling. Then I would use models, just like for the walls, with bounding boxes. That would allow me to put in a second floor or additional higher floors. The next floor would create a ceiling for the lower floor, and this would allow for holes in between the floors for things like stairs.

Of course for a basic dungeon crawler, you don't have to have multiple floors.

Another thing I would add would be "zones". I would have a doorway or something, as a model with a bounding box, and if you "collide" with it, I would load up a different zone map and zone you into it. I would have a doorway on the other map that zones you back to the original zone.

Anyway, that's how I would handle a dungeon. I'm hoping to actually build that as a tutorial, but I'm working on outdoor stuff at the moment.
Was This Post Helpful? 0
  • +
  • -

#9 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 09:07 AM

View PostArchColossus, on 07 May 2012 - 08:08 AM, said:

Your Forward2D/Back2D methods are exactly what kind of movement I've been trying to achieve, although I may add strafing if you don't mind me playing around with your code. Now I just need to find a way to get the walls and ceiling of a dungeon crawler environment to cooperate. Thank you for the assist, been stuck on this for a good while now. :D



Oh. I also wanted to add that I wrote Forward2D for a 3D environment that allows "flying". I'm tring to write a vehicle simulation that allows you to jump off cliffs and what not. I wanted a "walking" camera in a 3D environment that allowed for jumping and what not while mostly keeping you on the ground, but I was also intending on expanding my camera to allow the vehicle to fly up into the air and then have gravity pull it back down. I wrote the "Ground" method, as an easy way to keep the camera on the ground, but that shouldn't be necessary if you did camera movement as 2D Vectors, I just wanted to allow for the possibility of leaping into the air at odd angles and such for my eventual vehicle camera.

There's probably a better way to write Forward2D if you plan on always keeping the camera at ground level. Even allowing for jumping and jumping on top of things probably conforms better to a 2D Vector than a 3D Vector. But, it works for moving essentially along 2 dimensions.
Was This Post Helpful? 0
  • +
  • -

#10 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 07 May 2012 - 09:39 AM

I should also point out that my camera "PitchView" is broken and not working the way it was "intended" to work. I haven't bothered to go in and fix it because it works "close enough". But if you go through it line by line you may figure out that it's not working exactly as intended. Essentially, what I wanted was it to allow you to look upwards or downwards a certain number of defined degrees. It does that, but I think there's something wrong to where it's not giving you exactly what's defined. Again, it's so close that I haven't bothered to take the time to try and fix it.
Was This Post Helpful? 0
  • +
  • -

#11 ArchColossus  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 22-February 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 08 May 2012 - 07:39 AM

So what would be the best way to set up a "level" that's just a flat representation of the bitmap? Say a floor tile is a red square, and a wall is blue. Maybe even green for the spawn tile and yellow "treasure." My only problem with this is getting the tiles the right size. I sort of understand setting up the vertices and indices, although I'll probably have to look at a reference.
Was This Post Helpful? 0
  • +
  • -

#12 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 08 May 2012 - 11:53 AM

View PostArchColossus, on 08 May 2012 - 07:39 AM, said:

So what would be the best way to set up a "level" that's just a flat representation of the bitmap? Say a floor tile is a red square, and a wall is blue. Maybe even green for the spawn tile and yellow "treasure." My only problem with this is getting the tiles the right size. I sort of understand setting up the vertices and indices, although I'll probably have to look at a reference.


Your post got me real interested in this. I'm looking at getting started working on a tutorial for this. I was thinking it through last night.

I'll probably start with an array, rather than a bitmap. Once I get it going pretty well, I'll then switch to a storing and loading the data in a text file. It will still basically be a "bitmap" (or more accurately a byte map. But an actual bitmap file has header info and what not. The reason I don't want to use a standard .bmp file is that I don't want to "draw" large areas the way I would in a heightmap. I want to have pretty accurate controll over each byte in the "map". So, I'll just make my own file with nothing but raw byte values that can be loaded directly into the array that I started with.

So, let's say I'm working towards a 512 by 512 map or array. Every value is going to be one byte in size or values between 0 and 255.

I've decided that I want the map to contain the type of tile in that spot on the grid along with it's "rotation". I could store 4 different textures for each 90 degree rotation of the tile, but it's much more memory efficent to have one tile texture and just rotate it to whatever facing I want it rotated to. For a plain tile this won't make a difference, but for a tile with a design that's not symetrical each rotation will look different visually. I might be able to use this to make more complex tiled floor designs in some cases.

So, I've decided to use the first 4 numbers as control values. For now 0 through 3 all mean that spot has no tile and it's a "solid" area that the player should never be allowed to stand on. It's an invisible wall essentially. I'm imagining a dungeon under ground and the 0 values are "earth" that you can't walk through even if there's no wall there. Because I've decided only to use every 4 values that means 1 through 3 are basically the same as zero for now. But later I might want to assign them some special meaning. I don't know what that would be but it just makes sense to leave them open for future changes.

So, values 4 though 255 will be tiles and their direction that they "face". I'll divide by 4 to get the tile number. So value 4 divided by four will be one, and one will be my first tile positioned with the top towards the -z direction. 5 will be the exact same tile but rotated 90 degrees clockwise, 6 will be 180 degrees rotation, and 7 will be the same tile rotated 270 degrees clockwise.

To determine the tile type the formula is Floor(Byte Value/4) because I always want to drop the remainder. To get the rotation I want to find the remainder. I think you could use the modulus division to get just the remainder. I think that's (Byte Value%4). The remainder it is probably going to be 0.25, 0.50, 0.75, or 0.00. So multiplying by four would give me 0 for no rotation, 1 for 90 deg clockwise, 2 for 180, and 3 for 270.

You may notice this is a WHOLE lot like doing a 2D tile game. And it really is until we start moving into the height dimension with actual walls.

Since I'm storing 4 possible tile directions in each value, that gives me 64 possible different tiles for my level. Keep in mind that I could load in another level later with 64 completely different tiles, if 64 isn't enough. Actually, I'm using the first one to represent no tile, so its really 63 possible tiles to chose from with each of them being able to be rotated to face different directions for visual/artistic purposes.

As for the grid. I have to decide how I want to lay this out. I'll make my grid size perfectly match my array/map. So, it will be 512 positions by 512 positions. I could make every position 1 meter wide (in my mind), but I find that to be too wide and may not give me enough control. For example, if a meter is too narrow for a hallway - and every grid unit is a meter - then I would have to expand it by a full meter to increase the width of the hallway. Making each grid unit be half a meter, keeps me pretty close to using meters for my measurements, but gives me twice as much control over width. Now I can widen the hallway from 1 meter to 1.5 meters, which is probably going to be more reasonable. It's a much smaller jump than from 1 to 2 meters, since meters themselves are pretty large for this.

So, the first thing I'm going to do is start building a "design tool" program. This won't be my actual game, but instead a "level designer" that will help me lay things out for the game. The end result will be stored in a file that will be loaded into my game program, once I get to that point.

I'm going to start, by building my level editor in XNA. At for starters I'm going to put the camera on the "roof" pointed perfectly straight down. So, it will be at a certain Y height (that later will be changable for zooming in and out. And it will only move along the X and Z axis. It will point straight down and will therefore be pointing to a specific grid cell.

I'll start by using line primitives to draw the grid out. Probably I'll start with white lines on a black background and
maybe draw the cell I'm currently pointing to in yellow lines. The lines that make up the grid will be 0.5 units apart with each unit representing a meter just to keep sizes straight and consistent in my head. The grid will be 512 cells across and 512 cells down. Every cell will perfectly match a value in my array/map/file. And every cell will have a value stored in that array/map between 0 and 255 that represents how that cell is to be tiled.

For every cell, I will draw a textured quad. In other words, I'll draw two triangle to form that grid square. Each quad(two triangles) will be a seperate object, which will allow me to texture every one of them differently if I want. If you made the whole grid one mesh you would have a problem trying to texture every grid cell with a different texture. By doing a seperate two triangle mesh for every grid cell you have full control over how that grid cell is textured and can easily use a different texture for each.

Since a grid cell is 0.5 units wide, the texture will be stretched (or compressed - which would be bad) to match that size. This is sounding to me like we could keep each tile texture pretty low res, like maybe 128 by 128 resolution. That would give you 256 res across a full unit/meter of space.

I just went back and reread your post. As an answer, I might use my control values 1 through 3 for things like player starting point and spawn points. Personally, I had planned on doing the spawn points through a totally different mechanism, but this isn't a bad idea either. See! I was right about it being a good idea to leave a couple of those open for future ideas.

Oh wait. That won't work. The reason is that I want spawn tiles to be textured. So, it would have to hold a value for the texture as well as the spawn tile status.

So, what I will probably do instead is define a second array, that will be stored as another map, in another file. I will call this my "control array" for lack of a better name.

I will make a rule, at least for now, that says if my texture map says the value is 0 through 3 then it doesn't matter what my control map says because it means it's solid area (wall that nothing can be positioned in).

Otherwise, my controll array/map will perfectly match my texture array/map in size and you could imagine laying one perfectly on top of the other, perfectly matching up to a grid square/quad/cell. Then values in my control array/map could be stored for status of that grid cell being a spawn point, or a player starting point, or switch. (A switch would be a tile that you step on that makes something happen. Like when someone steps into a tile placed under a door they are zoned into another zone. Actually, I want to handle switches as invisible objects with bounding boxes. Then when the player "collides" with that invisible bounding box the switch is activated. I could place the switch in a doorway or hall and have it zone them into another zone or something. But still, this control array/map could be good for spawn points, etc.

Starting out I would just make a camera that points perfectly downward at a grid cell. Later, I would build a new camera (or two) that would allow for more 3D movement. I would build a walking camera that allow you to walk around the area like a player and I would forbid that camera from walking over a grid cell that has a value <4. It would be the start of building an actual camera for my game, but also useful to see things as a player at design time.

Then I might build a swing camera that just orbits around the selected grid cell at a given height and distance that the user can control.

Keep in mind that all this gives us at this point is a 2D floor that can be viewed 3 dimensionally and a player camera that obeys the rule of acting as if there is an invisible wall preventing walking over blank grid cells.

Later, I would allow 3D models to be put into place for walls and such. They would have bounding boxes for collision that would allow me to not only put them at the edges for walls, but would also allow me to put a wall in the "center of the room". I would probably make the wall units a standard size such as 1 meter long and 3 meters high, just to have them match up well with the grid.

Well, if you read through this "novel" you've probably got quite a bit to think about. So, I guess that's enough for now. ;-)

View PostArchColossus, on 08 May 2012 - 07:39 AM, said:

So what would be the best way to set up a "level" that's just a flat representation of the bitmap? Say a floor tile is a red square, and a wall is blue. Maybe even green for the spawn tile and yellow "treasure." My only problem with this is getting the tiles the right size. I sort of understand setting up the vertices and indices, although I'll probably have to look at a reference.


Maybe I should also mention that I would store my textures (or at least their file name or something) in a 63 element array (0 though 62 and you have to add one to make it match up with my grid tile values). Then I would draw each grid cell by defining a two triangle mesh, texturing it with the correct texture for that cell, and then rotating the mesh to the correct facing.
Was This Post Helpful? 0
  • +
  • -

#13 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 08 May 2012 - 12:15 PM

As I go back and think through my design for my level editor. I'm realizing that it may not be that important to use different textures on my floor. Do I really need complex floor textures for this? Can't I just use the same floor texture all over the dungeon? Does every room need seperate flooring? I mean I suppose it allows for some things like having a wooden floor in one room. And it definately allows for complex mosaic tile designs on a temple floor or something. But for a first effort at creating a game, maybe I don't need more than one floor tile, or the ability to rotate it in different directions.

If I don't need that I can go to using a true bitmap. In that case each grid cell is assigned a bit value in an array. 0 (off) means an area that can't be walked into and 1 (on) means you can walk there and we need to draw a tile on the floor.

Of course, maybe that's even more of an advanced subject just because you have to know C# well enough to manipulate and store bits. Off the top of my head, I'm not sure how to do that in C#, although I've done it in other languages.

Maybe I'm over thinking it and I should just stick with the original idea.
Was This Post Helpful? 0
  • +
  • -

#14 ArchColossus  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 22-February 12

Re: Does anyone know of a tutorial teaching grid-based map generation?

Posted 09 May 2012 - 06:54 AM

You're idea seems great and I understand the concepts of what you're thinking of, but unfortunately, I'm nowhere near knowing how to program all of the off the top of my head. Looks like I'll be pulling up my old primitive drawing projects to reference. And I'll have to brush up on writing arrays, since I've never been formally taught, and have only just looked at them. I definitely will be working with this though. And it would probably be good experience for future game developing escapades. Also, you explained what would have to be done to have this sort of game work very well. So thank you.
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1