Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Monday, February 25, 2013

Simple terrain smoothing

After creating or importing terrain you may find it to be too steep or jagged. Part of this depends on how your terrain system is written, but it also depends on the resolution of the terrain and terrain you're importing.

Let's image the grid below signifies sections of our terrain, with the numbers representing elevation. We can see there are two peaks that are 20 meters tall, surrounded immediately by flat terrain that is 0 meters tall. So we basically have two very steep and pointy peaks that are 20 meters high.



To smooth the section represented here in blue, we total up all of the heights in the red squares (which gives us 20), and we divide by the number of red squares (there are 8 squares, so 20 / 8 = 2.5), and we average that result and the height of the blue square together. (20 + 2.5) / 2 = 11.25. We now have our new smoothed value, and we replace the blue square with that value.

Here we can see the results after smoothing out just the one square in the example above.


Here's what we get when we run the algorithm against every square in the grid.


Here's the algorithm:
public void SmoothTerrain(int Passes)
{
   float[,] newHeightData;

   while (Passes > 0)
   {
       Passes--;

       // Note: MapWidth and MapHeight should be equal and power-of-two values 
       newHeightData = new float[MapWidth, MapHeight];

       for (int x = 0; x < MapWidth; x++)
       {
          for (int y = 0; y < MapHeight; y++)
          {
              int adjacentSections = 0;
              float sectionsTotal = 0.0f;

              if ((x - 1) > 0) // Check to left
              {
                 sectionsTotal += HeightData[x - 1, y];
                 adjacentSections++;

                 if ((y - 1) > 0) // Check up and to the left
                 {
                    sectionsTotal += HeightData[x - 1, y - 1];
                    adjacentSections++;
                 }

                 if ((y + 1) < MapHeight) // Check down and to the left
                 {
                    sectionsTotal += HeightData[x - 1, y + 1];
                    adjacentSections++;
                 }
              }

              if ((x + 1) < MapWidth) // Check to right
              {
                 sectionsTotal += HeightData[x + 1, y];
                 adjacentSections++;

                 if ((y - 1) > 0) // Check up and to the right
                 {
                     sectionsTotal += HeightData[x + 1, y - 1];
                     adjacentSections++;
                 }

                 if ((y + 1) < MapHeight) // Check down and to the right
                 {
                     sectionsTotal += HeightData[x + 1, y + 1];
                     adjacentSections++;
                 }
              }

              if ((y - 1) > 0) // Check above
              {
                 sectionsTotal += HeightData[x, y - 1];
                 adjacentSections++;
              }

              if ((y + 1) < MapHeight) // Check below
              {
                 sectionsTotal += HeightData[x, y + 1];
                 adjacentSections++;
              }

              newHeightData[x, y] = (HeightData[x, y] + (sectionsTotal / adjacentSections)) * 0.5f;
           }
       }

      // Overwrite the HeightData info with our new smoothed info
      for (int x = 0; x < MapWidth; x++)
      {
          for (int y = 0; y < MapHeight; y++)
          {
              HeightData[x, y] = newHeightData[x, y];
          }
      }
   }
}


Here's some purposely jagged terrain, without any smoothing



















Terrain after one smoothing pass

After five smoothing passes

After 50 smoothing passes. 20 passes would probably have been fine






Saturday, February 11, 2012

Game Engine Architecture, C#

Here's the same architecture as my last two posts, but this time in C# (the last two were in Scala and C++).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MessagingInCSharp
{
    class Program
    {
        static void Main( string[] args )
        {
            // Create a scene manager
            SceneManager sceneMgr = new SceneManager();

            // Have scene manager create an entity for us, which
            // automatically puts the object into the scene as well
            Entity myEntity = sceneMgr.CreateEntity();

            // Create a render component
            RenderComponent renderComp = new RenderComponent();

            // Attach render component to the entity we made
            myEntity.AddComponent(renderComp);

            // Set 'myEntity' position to (1, 2, 3)
            MsgSetPosition msgSetPos = new MsgSetPosition(myEntity.uniqueID, 1.0f, 2.0f, 3.0f);
            sceneMgr.SendMessage(msgSetPos);
            Console.WriteLine("Position set to (1, 2, 3) on entity with ID: " + myEntity.uniqueID);

            Console.WriteLine("Retreiving position from entity with ID: " + myEntity.uniqueID);

            // Get 'myEntity' position to verify it was set properly
            MsgGetPosition msgGetPos = new MsgGetPosition(myEntity.uniqueID);
            sceneMgr.SendMessage(msgGetPos);
            Console.WriteLine("X: " + msgGetPos.x);
            Console.WriteLine("Y: " + msgGetPos.y);
            Console.WriteLine("Z: " + msgGetPos.z);
        }
    }

    public enum MessageType
    {
        SetPosition,
        GetPosition
    }

    public class Vector3
    {
        public float x = 0.0f;
        public float y = 0.0f;
        public float z = 0.0f;
    }

    public class BaseMessage
    {
        public int destEntityID;
        public MessageType messageType;

        protected BaseMessage( int destinationEntityID, MessageType messageType )
        {
            this.destEntityID = destinationEntityID;
            this.messageType = messageType;
        }
    }

    public class PositionMessage : BaseMessage
    {
        public float x;
        public float y;
        public float z;

        protected PositionMessage( int destinationEntityID, MessageType messageType,
                                   float X = 0.0f, float Y = 0.0f, float Z = 0.0f) :
            base(destinationEntityID, messageType)
        {
            this.x = X;
            this.y = Y;
            this.z = Z;
        }
    }

    public class MsgSetPosition : PositionMessage
    {
        public MsgSetPosition( int destinationEntityID, float X, float Y, float Z ) :
            base(destinationEntityID, MessageType.SetPosition, X, Y, Z)
        {}
    }

    public class MsgGetPosition : PositionMessage
    {
        public MsgGetPosition( int destinationEntityID) :
            base(destinationEntityID, MessageType.GetPosition, 0.0f, 0.0f, 0.0f)
        {}
    }

    public abstract class BaseComponent
    {
        public virtual bool SendMessage( BaseMessage msg )
        {
            return false;
        }
    }

    public class RenderComponent : BaseComponent
    {
        public override bool SendMessage( BaseMessage msg )
        {
            // Entity has a switch for any messages it cares about
            switch (msg.messageType)
            {
                case MessageType.SetPosition:
                    {
                        // Update render mesh position/translation

                        Console.WriteLine("RenderComponent handling SetPosition");
                    }
                    break;
                default:
                    return base.SendMessage(msg);
            }

            return true;
        }
    }

    public class Entity
    {
        public int uniqueID;
        public int UniqueID
        {
            get { return this.uniqueID; }
            set { this.uniqueID = value; } 
        }
        
        private Vector3 position = new Vector3();        
        private List<BaseComponent> components = new List<BaseComponent>();

        public Entity( int uniqueID )
        {
            this.uniqueID = uniqueID;
        }

        public void AddComponent( BaseComponent component )
        {
            this.components.Add(component);
        }

        public bool SendMessage( BaseMessage msg )
        {
            bool messageHandled = false;

            // Entity has a switch for any messages it cares about
            switch (msg.messageType)
            {
                case MessageType.SetPosition:
                    {
                        MsgSetPosition msgSetPos = msg as MsgSetPosition;
                        position.x = msgSetPos.x;
                        position.y = msgSetPos.y;
                        position.z = msgSetPos.z;

                        messageHandled = true;
                        Console.WriteLine("Entity handled SetPosition");
                    }
                    break;
                case MessageType.GetPosition:
                    {
                        MsgGetPosition msgGetPos = msg as MsgGetPosition;
                        msgGetPos.x = position.x;
                        msgGetPos.y = position.y;
                        msgGetPos.z = position.z;

                        messageHandled = true;
                        Console.WriteLine("Entity handled GetPosition");
                    }
                    break;
                default:
                    return PassMessageToComponents(msg);
            }

            // If the entity didn't handle the message but the component
            // did, we return true to signify it was handled by something.
            messageHandled |= PassMessageToComponents(msg);

            return messageHandled;
        }

        private bool PassMessageToComponents( BaseMessage msg )
        {
            bool messageHandled = false;

            this.components.ForEach(c => messageHandled |= c.SendMessage(msg) );

            return messageHandled;
        }
    }

    public class SceneManager
    {
        private Dictionary<int, Entity> entities = new Dictionary<int,Entity>();
        private static int nextEntityID = 0;

        // Returns true if the entity or any components handled the message
        public bool SendMessage( BaseMessage msg )
        {
            // We look for the entity in the scene by its ID
            Entity entity;
            if ( entities.TryGetValue(msg.destEntityID, out entity) )
            {
                // Entity was found, so send it the message
                return entity.SendMessage(msg);
            }

            // Entity with the specified ID wasn't found
            return false;
        }

        public Entity CreateEntity()
        {
            Entity newEntity = new Entity(SceneManager.nextEntityID++);
            entities.Add(newEntity.UniqueID, newEntity);

            return newEntity;
        }
    }
}

Wednesday, November 16, 2011

Let's Make a Game Engine for XNA 4.0r, Prologue

I was in the middle of making a simple game engine framework so I could start writing blog posts about things like creating camera, input systems, terrain systems, spatial partitioning, etc., when I realized that I should document the actual creation of the framework.

I chose XNA for a few reasons:
1.) I'm already familiar with it after having developed a game engine for it: http://quickstartengine.codeplex.com/
2.) It's fast for prototyping
3.) It handles the back-end for a lot of mundane things like creating a Window, handling Windows messaging, handling DirectX. All of that means less I have to explain here on my blog, and that lets us get to the interesting parts more quickly.

You'll need some familiarity with Visual Studio 2010:
XNA 4.0r runs only in Visual Studio 2010. Luckily it works in the free version of VS2010 C# Express, which you can get here: http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-csharp-express

The upcoming blog posts assume you know C#:
XNA runs within C#. If you do not know C# then you may want to stop now and spend a couple of days getting a basic rundown of C#. When I first started with XNA I knew only C++, I picked up enough about C# in about 8 hours to start making simple games in XNA. If you know C++ or Java fairly well then you can probably pick up the basics of C# pretty quickly.

If you're not familiar with XNA:
I would recommend at least spending a few hours looking a few simple tutorials to learn about some of the main functionality, how to create a project, and how the content pipeline works.
These links should cover just enough for you to grasp what you'll need to make this game engine.:
http://www.xnadevelopment.com/tutorials/gettingstartedwithxnadevelopment/GettingStartedWithXNADevelopment.shtml
http://www.xnadevelopment.com/tutorials/creatinganewxnagameproject/CreatingANewXNAWindowsGameProject.shtml
http://www.xnadevelopment.com/tutorials/addinganimagetothegameproject/AddingAnImageToTheGameProject.shtml

Ok, if you made it this far you are reasonably familiar with Visual Studio 2010 Express, C# and XNA 4.0r. Now we can continue on to Part 1 of this series.