x01.Game.MapEditor: 地图编辑器

1.游戏编程,需要一个地图编辑器。站在前人肩上,自己写一个,倒也不难。其效果图如下:

           

  只需从左下的 Tiles 面板中选择一个 Tile,在右边的面板中绘制即可。保存为同名的两个文件 file.bmp, file.xml。打开时选择 file.xml 文件。

2.界面设计不详述,可在效果图上点击右键,将其另存为桌面上作为参照。其完整代码如下:

MainForm
    public partial class MainForm : Form, IMessageFilter
    {
        int paletteColumes = 5, paletteSelectedIndex = 0;
        readonly int mapSize = 100; //for map tiles
        int mouseX = 0, mouseY = 0, boxCol = 0, boxRow = 0;

        Tile[] tiles;
        Bitmap bmpMap, bmpTile;
        Graphics graphMap, graphTile;
        BoxInfo boxSelected;  // editing selected box in the map

        public MainForm()
        {
            InitializeComponent();

            Application.AddMessageFilter(this);

            tiles = new Tile[mapSize * mapSize];

            bmpMap = new Bitmap(picMap.Size.Width, picMap.Size.Height);
            picMap.Image = bmpMap;
            graphMap = Graphics.FromImage(bmpMap);

            bmpTile = new Bitmap(picSelected.Size.Width, picSelected.Size.Height);
            picSelected.Image = bmpTile;
            graphTile = Graphics.FromImage(bmpTile);

            // Init Events
            picMap.MouseClick += new MouseEventHandler(picMap_MouseClick);
            picMap.MouseMove += new MouseEventHandler(picMap_MouseMove);
            picPalette.MouseClick += new MouseEventHandler(picPalette_MouseClick);

            dlgOpen.Filter = "XML Files(*.xml)|*.xml|All Files(*.*)|*.*";
            miNew.Click += new EventHandler(miNew_Click);
            miOpen.Click += new EventHandler(miOpen_Click);
            miSave.Click += new EventHandler(miSave_Click);
            miExit.Click += (a, b) => Close();

            radioEditMode.CheckedChanged += new EventHandler(radioEditMode_CheckedChanged);
        }

        void radioEditMode_CheckedChanged(object sender, EventArgs e)
        {
            if (radioEditMode.Checked == false)
            {
                int i = boxSelected.Index;
                tiles[i].TileId = Convert.ToInt32(txtTileId.Text);
                tiles[i].Data1 = txtData1.Text;
                tiles[i].Data2 = txtData2.Text;
                tiles[i].Data3 = txtData3.Text;
                tiles[i].Data4 = txtData4.Text;
                tiles[i].Collision = chkCollision.Checked;
                tiles[i].Portal = chkPortal.Checked;
                tiles[i].PortalX = Convert.ToInt32(txtPortalX.Text);
                tiles[i].PortalY = Convert.ToInt32(txtPortalY.Text);
                tiles[i].PortalFile = txtPortalFile.Text;

                paletteSelectedIndex = tiles[i].TileId;
                SetSelectedTile();

                boxCol = boxSelected.Col;
                boxRow = boxSelected.Row;
                DrawSelectedTile();
            }
        }

        void picPalette_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                paletteSelectedIndex = e.X / 33 + e.Y / 33 * paletteColumes;
                SetSelectedTile();
            }
        }

        void miNew_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < mapSize * mapSize; i++)
            {
                tiles[i].TileId = 0;
                tiles[i].Data1 = string.Empty;
                tiles[i].Data2 = string.Empty;
                tiles[i].Data3 = string.Empty;
                tiles[i].Data4 = string.Empty;
                tiles[i].Collision = false;
                tiles[i].Portal = false;
                tiles[i].PortalFile = string.Empty;
                tiles[i].PortalX = 0;
                tiles[i].PortalY = 0;
            }
            RedrawMap();
        }
        void miSave_Click(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<?xml version=\"1.0\" standalone=\"yes\"?>");
            sb.AppendLine("<tiles>");
            for (int i = 0; i < mapSize * mapSize; i++)
            {

                sb.AppendLine(" <tile>");
                sb.AppendLine("  <tileId>" + tiles[i].TileId.ToString() + "</tileId>");
                sb.AppendLine("  <data1>" + tiles[i].Data1 + "</data1>");
                sb.AppendLine("  <data2>" + tiles[i].Data2 + "</data2>");
                sb.AppendLine("  <data3>" + tiles[i].Data3 + "</data3>");
                sb.AppendLine("  <data4>" + tiles[i].Data4 + "</data4>");
                sb.AppendLine("  <collision>" + (tiles[i].Collision ? "true" : "false") + "</collision>");
                sb.AppendLine("  <portal>" + (tiles[i].Portal ? "true" : "false") + "</portal>");
                sb.AppendLine("  <portalX>" + tiles[i].PortalX.ToString() + "</portalX>");
                sb.AppendLine("  <portalY>" + tiles[i].PortalY.ToString() + "</portalY>");
                sb.AppendLine("  <portalFile>" + tiles[i].PortalFile + "</portalFile>");
                sb.AppendLine(" </tile>");
            }
            sb.AppendLine("</tiles>");

            if (dlgSave.ShowDialog() == DialogResult.OK)
            {
                File.WriteAllText(dlgSave.FileName + ".xml", sb.ToString());
                bmpMap.Save(dlgSave.FileName + ".bmp", ImageFormat.Bmp);
            }
        }
        void miOpen_Click(object sender, EventArgs e)
        {
            string file = string.Empty;
            if (dlgOpen.ShowDialog() == DialogResult.OK)
            {
                file = dlgOpen.FileName;
            }

            try
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(file);
                XmlNodeList nodes = doc.GetElementsByTagName("tile");

                int i = 0;
                foreach (XmlElement node in nodes)
                {
                    tiles[i].TileId = Convert.ToInt32(node.GetElementsByTagName("tileId")[0].InnerText);
                    tiles[i].Data1 = node.GetElementsByTagName("data1")[0].InnerText;
                    tiles[i].Data2 = node.GetElementsByTagName("data2")[0].InnerText;
                    tiles[i].Data3 = node.GetElementsByTagName("data3")[0].InnerText;
                    tiles[i].Data4 = node.GetElementsByTagName("data4")[0].InnerText;
                    tiles[i].Collision = Convert.ToBoolean(node.GetElementsByTagName("collision")[0].InnerText);
                    tiles[i].Portal = Convert.ToBoolean(node.GetElementsByTagName("portal")[0].InnerText);
                    tiles[i].PortalFile = node.GetElementsByTagName("portalFile")[0].InnerText;
                    tiles[i].PortalX = Convert.ToInt32(node.GetElementsByTagName("portalX")[0].InnerText);
                    tiles[i].PortalY = Convert.ToInt32(node.GetElementsByTagName("portalY")[0].InnerText);
                    i++;
                }

                RedrawMap();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        void picMap_MouseMove(object sender, MouseEventArgs e)
        {
            boxCol = e.X / 32;
            boxRow = e.Y / 32;
            mouseX = e.X;
            mouseY = e.Y;

            lblCursor.Text = "Map Cursor: x,y = " + e.X.ToString() + "," + e.Y.ToString();
            if (radioDrawMode.Checked)
            {
                Tile tile = tiles[boxRow * mapSize + boxCol];
                txtTileId.Text = tile.TileId.ToString();
                txtData1.Text = tile.Data1;
                txtData2.Text = tile.Data2;
                txtData3.Text = tile.Data3;
                txtData4.Text = tile.Data4;
                chkCollision.Checked = tile.Collision;
                chkPortal.Checked = tile.Portal;
                txtPortalX.Text = tile.PortalX.ToString();
                txtPortalY.Text = tile.PortalY.ToString();
                txtPortalFile.Text = tile.PortalFile;

                picMap_MouseClick(sender, e);
            }
        }
        /// <summary>
        /// 如是 Draw Mode,鼠标左键绘制 Tile 到 map 上,右键清除;
        /// 否则,绘制 box 到 map 上。
        /// </summary>
        void picMap_MouseClick(object sender, MouseEventArgs e)
        {
            switch (e.Button)
            {
                case MouseButtons.Left:
                    if (radioDrawMode.Checked)
                    {
                        DrawSelectedTile();
                    }
                    else
                    {
                        //show selected tile for editing
                        boxSelected.Col = boxCol;
                        boxSelected.Row = boxRow;
                        boxSelected.Index = boxRow * mapSize + boxCol;

                        Tile tile = tiles[boxSelected.Index];
                        txtTileId.Text = tile.TileId.ToString();
                        txtData1.Text = tile.Data1;
                        txtData2.Text = tile.Data2;
                        txtData3.Text = tile.Data3;
                        txtData4.Text = tile.Data4;
                        chkPortal.Checked = tile.Portal;
                        chkCollision.Checked = tile.Collision;
                        txtPortalX.Text = tile.PortalX.ToString();
                        txtPortalY.Text = tile.PortalY.ToString();
                        txtPortalFile.Text = tile.PortalFile;

                        DrawSelectedBox();
                    }
                    break;
                case MouseButtons.Right:
                    if (radioDrawMode.Checked)
                    {
                        DrawTile(boxCol, boxRow, 0);    // erase
                    }
                    break;
            }
        }
        void DrawSelectedTile()
        {
            DrawTile(boxCol, boxRow, paletteSelectedIndex);
        }
        void DrawSelectedBox()
        {
            // Erase selected tile with old.
            //DrawTile(boxSelected.Col, boxSelected.Row, tiles[boxSelected.OldIndex].TileId);

            // Remember current tile
            //boxSelected.OldIndex = boxSelected.Index;

            int x = boxCol * 32;
            int y = boxRow * 32;
            Pen pen = new Pen(Color.DarkMagenta, 2);
            Rectangle rect = new Rectangle(x + 1, y + 1, 30, 30);
            graphMap.DrawRectangle(pen, rect);

            picMap.Image = bmpMap;
        }


        //[DllImport("user32.dll")]
        //public static extern IntPtr WindowFromPoint(Point point);
        //[DllImport("user32.dll")]
        //public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
        // Handle MouseWheel Message.
        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == WinMessages.WM_MOUSEWHEEL)  //WM_MOUSEWHEEL = 0x020A
            {
                Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
                IntPtr hWnd = User32.WindowFromPoint(pos);
                if (hWnd != IntPtr.Zero && hWnd != m.HWnd && Control.FromHandle(hWnd) != null)
                {
                    User32.SendMessage(hWnd, m.Msg, m.WParam, m.LParam);
                    return true;
                }
            }
            return false;
        }
        void SetSelectedTile()
        {
            int x = (paletteSelectedIndex % paletteColumes) * 33;
            int y = (paletteSelectedIndex / paletteColumes) * 33;
            var src = new Rectangle(x, y, 32, 32);
            var dest = new Rectangle(0, 0, 32, 32);
            graphTile.DrawImage(picPalette.Image, dest, src, GraphicsUnit.Pixel);
            picSelected.Image = bmpTile;
        }
        /// <summary>
        /// 在 picMap 中按指定的行、列绘制所选的 tile.
        /// </summary>
        void DrawTile(int col, int row, int tileId)
        {
            tiles[row * mapSize + col].TileId = tileId;

            int srcX = (tileId % paletteColumes) * 33;
            int srcY = (tileId / paletteColumes) * 33;
            int destX = col * 32;

            int destY = row * 32;
            var src = new Rectangle(srcX, srcY, 32, 32);
            var dest = new Rectangle(destX, destY, 32, 32);
            graphMap.DrawImage(picPalette.Image, dest, src, GraphicsUnit.Pixel);

            picMap.Image = bmpMap;
        }
        void RedrawMap()
        {
            for (int i = 0; i < mapSize * mapSize; i++)
            {
                int col = i % mapSize;
                int row = i / mapSize;
                int tileId = tiles[i].TileId;
                DrawTile(col, row, tileId);
            }
        }
    }

    struct Tile
    {
        public int TileId;  // equal palette index
        public string Data1, Data2, Data3, Data4;
        public bool Portal, Collision;
        public int PortalX, PortalY;
        public string PortalFile;
    }

    struct BoxInfo
    {
        public int Index, Col, Row; // all in the map
    }

3.Picture 放在 Panel 中,使其有滚动效果。为方便操作,添加了鼠标滚轮的消息处理。即代码中的 IMessageFilter 接口实现。其中,用到两个Win32 API,见注释。实际使用,需将注释及 User32 前缀去掉,WM_* 也需用实际值替换。
4.Tiles 选择面板用到的资源如下:

          

  同样可通过右键保存到桌面,以方便观看使用。

posted on 2012-05-14 13:48  x01  阅读(2089)  评论(0编辑  收藏  举报

导航