Making a map editor for our platformer

Published on July 29, 2011

Making a map editor for our platformer

    Many, I think, created, or at least tried to create some kind of toy. And very often, indie developers choose the genre of arcade, scrolling shooter, platform meter or something like that. Here I want to talk about creating the simplest level editor for such an arcade.
    As a platform, I chose C # + XNA 4.0, and WinForms for custom elements in the level editor.
    Create a Windows Game project in VisualStudio and go!



    0. Architecture


    For the level, I created a separate class, which is used both in the game itself and in the editor. For convenience, I put it in a separate project and added it to both solutions. The level object itself is created in the editor, serialized into a file, and then from that file it is deserialized into the game.

    1. Interface


    As I said, the interface I did on WindowsForms. We connect System.Windows.Forms to our project. Some WindowsForms class names may conflict with the names in Xna, so let's do this
    using WinForms = System.Windows.Forms;

    MSDN has an excellent description of WindowsForms and all of its widgets, so I won’t stop there. I had no problems with the interaction of WindowsForms and XNA. We create a procedure in the Game1 class (the standard name of the Xna game class) that creates a window, widgets and customizes them, and calls it in the Initialize () method. I got something like this:


    2. Level class


    Create a level class and indicate that it is serializable:
    [Serializable]
    public class Level
    {
    //...

    }

    This class contains lists of all objects (I made only one type of objects, but adding others will not be difficult)
    public List<Block> Blocks = new List<Block>();
    public Vector2 cameraPos;


    Block Class:
    [Serializable]
      public class Block
      {
        [NonSerialized]
        public Texture2D tex;

        public Rectangle rect;

        public Block(Texture2D texture, Rectangle rectangle)
        {
          this.tex = texture;
          this.rect = rectangle;
        }

      }

    Please note that it must also be serializable, but we cannot serialize the texture (otherwise an error), but add it directly when loading the level into the game itself (for this you can create an additional property String Texturename, int TextureType or whatever you want).

    CameraPos is a vector indicating the camera position needed to scroll through the level.
    In the same class, methods for transferring coordination from screen to world and vice versa:
    public Vector2 ScreenToWorld(Vector2 vector)
    {
          Vector2 rvect;
          rvect = cameraPos + vector;
          return rvect;
    }

    public Vector2 WorldToScreen(Vector2 vector)
    {
          Vector2 rvect;
          rvect = vector - cameraPos;
          return rvect;
    }




    3. The editor himself


    In the Game1 class, create a description of the current tool:
    enum CurrentTool
        {
          None,
          Brick,
          Delete
        }
    CurrentTool currTool = CurrentTool.None;


    And in the tool switch event handler in our auxiliary window, change the currTool variable to what the user selects.

    If a block is selected, then create it and drag it around the window, when you click with the mouse, add it to the list of blocks and it is fixed in level.
    The tool for creating a block has two modes of operation: free location and location at certain places, at a distance multiple of the size of the block, so that the level is more correct.

    Window Scrolling:
    if (Keyboard.GetState().IsKeyDown(Keys.Left) && lastKeyboardState.IsKeyUp(Keys.Left))
    {
              level.cameraPos += new Vector2(-10, 0);
    }
    //в остальные стороны аналогично



    Do not forget when making a translation from screen coordinates to world coordinates and vice versa when it is necessary (when drawing, adding an object, etc.).

    4. Saving / loading level.


    In the handler for clicking on the Save / Load buttons, we call the class methods for working with loading and saving.

    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;

    namespace LevelLib
    {
      public static class SaveOpenLevel
      {
        public static Level OpenLevel(string filename)
        {
          Level level;
          if (filename == "")
          {
            return null;
          }
          if (!File.Exists(filename))
          {
            return null;
          }
          BinaryFormatter bf = new BinaryFormatter();
          Stream fs = new FileStream(filename, FileMode.Open);
          level = (Level)bf.Deserialize(fs);
          fs.Close();
          return level;
        }

        public static int SaveLevel(string filename, Level level)
        {
          if (filename == "")
          {
            return 1;
          }
          BinaryFormatter bf = new BinaryFormatter();
          Stream fs = new FileStream(filename, FileMode.Create);
          bf.Serialize(fs, level);
          fs.Close();
          return 0;
        }
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    We have this class in the project for working with the level (in the same place as Level, Block, etc.).

    In the game we do this.level = OpenLevel ("foo.lvl"), but do not forget to add the texture to the blocks.
    Next, we process the player’s interaction with the level (for now, this is limited to this:
    foreach(Block block in level.blocks)
    {
      if(block.Intersets(player.Rectangle)
       return true;
    }


    )

    5. Afterword


    This is how I made the framework for the level editor. Now you can add other objects, expand it.
    Please do not swear for sloppy / ugly / crooked code.
    I post the screenshot, the project in which you can throw these blocks on the stage and the game itself, in which you can run by the level I can’t lay out, now it’s raw, but if it’s interesting, then “see” is enough. By default, it loads the evel with the name Test.lev from the folder with the binaric of the game, and the editor saves the file in the folder with the entire file, so the level file will need to be transferred. Otherwise, you need to correct the level name in the sources (all sources are attached).
    Link to skydrive (file in 7z format)
    On narod.ru (file in zip format) - for those who do not have 7zip UPD: added files to the article.