Unity3d开发——保卫萝卜学习笔记一地图编辑器开发
来源:互联网 发布:深圳赛维网络招聘 编辑:程序博客网 时间:2024/04/29 17:42
一直在学习,几乎边学边忘,再也不想打王者农药了,现在打卡学习持续写博客,现在在开发学习保卫萝卜,这篇文章关于地图编辑器的开发,在项目中不能是无限个场景,用读取xml文件的方法可以说是能实现无限关卡,同时节省很多开发时间缩短周期
一.目录结构
二.创建数据类(放置在Data目录)
首先看一下XML文件内容
根据xml文件节点创建一些数据类,首先创建Point.cs脚本,放置在Data目录中
using UnityEngine;using System.Collections;//格子坐标public class Point{ public int X; public int Y; public Point(int x, int y) { this.X = x; this.Y = y; }}
3.创建Tile.cs脚本
using System;using System.Collections.Generic;using System.Linq;using System.Text;//格子信息public class Tile{ public int X; public int Y; public bool CanHold; //是否可以放置塔 public object Data; //格子所保存的数据 public Tile(int x, int y) { this.X = x; this.Y = y; } public override string ToString() { return string.Format("[X:{0},Y:{1},CanHold:{2}]", this.X, this.Y, this.CanHold ); }}
4.创建Round.cs脚本
using System;using System.Collections.Generic;using System.Linq;using System.Text;public class Round{ public int Monster; //怪物类型ID public int Count; //怪物数量 public Round(int monster, int count) { this.Monster = monster; this.Count = count; }}
5.根据xml文件信息创建关卡类Level.cs
using UnityEngine;using System.Collections;using System.Collections.Generic;public class Level{ //名字 public string Name; //背景 public string Background; //路径 public string Road; //金币 public int InitScore; //炮塔可放置的位置 public List<Point> Holder = new List<Point>(); //怪物行走的路径 public List<Point> Path = new List<Point>(); //出怪回合信息 public List<Round> Rounds = new List<Round>();}
三.创建读写XML工具类
using UnityEngine;using System.IO;using System.Collections;using System.Collections.Generic;using System.Text;using System.Xml;public class Tools { //读取关卡列表 public static List<FileInfo> GetLevelFiles() { string[] files = Directory.GetFiles(Consts.LevelDir, "*.xml"); List<FileInfo> list = new List<FileInfo>(); for (int i = 0; i < files.Length; i++) { FileInfo file = new FileInfo(files[i]); list.Add(file); } return list; } //填充Level类数据 public static void FillLevel(string fileName, ref Level level) { FileInfo file = new FileInfo(fileName); StreamReader sr = new StreamReader(file.OpenRead(), Encoding.UTF8); XmlDocument doc = new XmlDocument(); doc.Load(sr); level.Name = doc.SelectSingleNode("/Level/Name").InnerText; level.Background = doc.SelectSingleNode("/Level/Background").InnerText; level.Road = doc.SelectSingleNode("/Level/Road").InnerText; level.InitScore = int.Parse(doc.SelectSingleNode("/Level/InitScore").InnerText); XmlNodeList nodes; nodes = doc.SelectNodes("/Level/Holder/Point"); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; Point p = new Point( int.Parse(node.Attributes["X"].Value), int.Parse(node.Attributes["Y"].Value)); level.Holder.Add(p); } nodes = doc.SelectNodes("/Level/Path/Point"); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; Point p = new Point( int.Parse(node.Attributes["X"].Value), int.Parse(node.Attributes["Y"].Value)); level.Path.Add(p); } nodes = doc.SelectNodes("/Level/Rounds/Round"); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; Round r = new Round( int.Parse(node.Attributes["Monster"].Value), int.Parse(node.Attributes["Count"].Value) ); level.Rounds.Add(r); } sr.Close(); sr.Dispose(); } //保存关卡 public static void SaveLevel(string fileName, Level level) { StringBuilder sb = new StringBuilder(); sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); sb.AppendLine("<Level>"); sb.AppendLine(string.Format("<Name>{0}</Name>", level.Name)); sb.AppendLine(string.Format("<Background>{0}</Background>", level.Background)); sb.AppendLine(string.Format("<Road>{0}</Road>", level.Road)); sb.AppendLine(string.Format("<InitScore>{0}</InitScore>", level.InitScore)); sb.AppendLine("<Holder>"); for (int i = 0; i < level.Holder.Count; i++) { sb.AppendLine(string.Format("<Point X=\"{0}\" Y=\"{1}\"/>", level.Holder[i].X, level.Holder[i].Y)); } sb.AppendLine("</Holder>"); sb.AppendLine("<Path>"); for (int i = 0; i < level.Path.Count; i++) { sb.AppendLine(string.Format("<Point X=\"{0}\" Y=\"{1}\"/>", level.Path[i].X, level.Path[i].Y)); } sb.AppendLine("</Path>"); sb.AppendLine("<Rounds>"); for (int i = 0; i < level.Rounds.Count; i++) { sb.AppendLine(string.Format("<Round Monster=\"{0}\" Count=\"{1}\"/>", level.Rounds[i].Monster, level.Rounds[i].Count)); } sb.AppendLine("</Rounds>"); sb.AppendLine("</Level>"); string content = sb.ToString(); StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8); sw.Write(content); sw.Flush(); sw.Dispose(); } //加载图片 public static IEnumerator LoadImage(string url, SpriteRenderer render) { WWW www = new WWW(url); while (!www.isDone) yield return www; Texture2D texture = www.texture; Sprite sp = Sprite.Create( texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); render.sprite = sp; }}
三.创建编辑器Editor类
1.创建常量类Consts.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using UnityEngine;public static class Consts{ //目录 public static readonly string LevelDir = Application.dataPath + @"\Game\Res\Levels"; public static readonly string MapDir = Application.dataPath + @"\Game\Res\Maps";}
2.创建Map.cs,此脚本挂在层级试图hierarchy中的Map上
先定义一些字段,属性,和方法
using UnityEngine;using System;using System.Collections;using System.Collections.Generic;public class Map : MonoBehaviour{ #region 常量 public const int RowCount = 8; //行数 public const int ColumnCount = 12; //列数 #endregion #region 字段 float MapWidth;//地图宽 float MapHeight;//地图高 float TileWidth;//格子宽 float TileHeight;//格子高 List<Tile> m_grid = new List<Tile>(); //格子集合 List<Tile> m_road = new List<Tile>(); //路径集合 Level m_level; //关卡数据 public bool DrawGizmos = true; //是否绘制网格 #endregion #region 属性 public Level Level { get { return m_level; } } public string BackgroundImage { set { SpriteRenderer render = transform.Find("Background").GetComponent<SpriteRenderer>(); StartCoroutine(Tools.LoadImage(value, render)); } } public string RoadImage { set { SpriteRenderer render = transform.Find("Road").GetComponent<SpriteRenderer>(); StartCoroutine(Tools.LoadImage(value, render)); } } public List<Tile> Grid { get { return m_grid; } } public List<Tile> Road { get { return m_road; } } //怪物的寻路路径 public Vector3[] Path { get { List<Vector3> m_path = new List<Vector3>(); for (int i = 0; i < m_road.Count; i++) { Tile t = m_road[i]; Vector3 point = GetPosition(t); m_path.Add(point); } return m_path.ToArray(); } } #endregion #region 方法 public void LoadLevel(Level level) { //清除当前状态 Clear(); //保存 this.m_level = level; //加载图片 this.BackgroundImage = "file://" + Consts.MapDir + "/" + level.Background; this.RoadImage = "file://" + Consts.MapDir + "/" + level.Road; //寻路点 for (int i = 0; i < level.Path.Count; i++) { Point p = level.Path[i]; Tile t = GetTile(p.X, p.Y); m_road.Add(t); } //炮塔点 for (int i = 0; i < level.Holder.Count; i++) { Point p = level.Holder[i]; Tile t = GetTile(p.X, p.Y); t.CanHold = true; } } //清除塔位信息 public void ClearHolder() { foreach (Tile t in m_grid) { if(t.CanHold) t.CanHold = false; } } //清除寻路格子集合 public void ClearRoad() { m_road.Clear(); } //清除所有信息 public void Clear() { m_level = null; ClearHolder(); ClearRoad(); } #endregion #region Unity回调 //只在运行期起作用 void Awake() { //计算地图和格子大小 CalculateSize(); //创建所有的格子 for (int i = 0; i < RowCount; i++) for (int j = 0; j < ColumnCount; j++) m_grid.Add(new Tile(j, i)); } //只在编辑器里起作用 void OnDrawGizmos() { if (!DrawGizmos) return; //计算地图和格子大小 CalculateSize(); //绘制格子 Gizmos.color = Color.green; //绘制行 for (int row = 0; row <= RowCount; row++) { Vector2 from = new Vector2(-MapWidth / 2, -MapHeight / 2 + row * TileHeight); Vector2 to = new Vector2(-MapWidth / 2 + MapWidth, -MapHeight / 2 + row * TileHeight); Gizmos.DrawLine(from, to); } //绘制列 for (int col = 0; col <= ColumnCount; col++) { Vector2 from = new Vector2(-MapWidth / 2 + col * TileWidth, MapHeight / 2); Vector2 to = new Vector2(-MapWidth / 2 + col * TileWidth, -MapHeight / 2); Gizmos.DrawLine(from, to); } foreach (Tile t in m_grid) { if (t.CanHold) { Vector3 pos = GetPosition(t); Gizmos.DrawIcon(pos, "holder.png", true); } } Gizmos.color = Color.red; for (int i = 0; i < m_road.Count; i++) { //起点 if (i == 0) { Gizmos.DrawIcon(GetPosition(m_road[i]), "start.png", true); } //终点 if (m_road.Count > 1 && i == m_road.Count - 1) { Gizmos.DrawIcon(GetPosition(m_road[i]), "end.png", true); } //红色的连线 if (m_road.Count > 1 && i != 0) { Vector3 from = GetPosition(m_road[i - 1]); Vector3 to = GetPosition(m_road[i]); Gizmos.DrawLine(from, to); } } } #endregion #region 帮助方法 //计算地图大小,格子大小 void CalculateSize() { Vector3 leftDown = new Vector3(0, 0); Vector3 rightUp = new Vector3(1, 1); Vector3 p1 = Camera.main.ViewportToWorldPoint(leftDown); Vector3 p2 = Camera.main.ViewportToWorldPoint(rightUp); MapWidth = (p2.x - p1.x); MapHeight = (p2.y - p1.y); TileWidth = MapWidth / ColumnCount; TileHeight = MapHeight / RowCount; } //获取格子中心点所在的世界坐标 Vector3 GetPosition(Tile t) { return new Vector3( -MapWidth / 2 + (t.X + 0.5f) * TileWidth, -MapHeight / 2 + (t.Y + 0.5f) * TileHeight, 0 ); } //根据格子索引号获得格子 Tile GetTile(int tileX, int tileY) { int index = tileX + tileY * ColumnCount; if (index < 0 || index >= m_grid.Count) return null; return m_grid[index]; } //获取鼠标下面的格子 Tile GetTileUnderMouse() { Vector2 wordPos = GetWorldPosition(); int col = (int)((wordPos.x + MapWidth / 2) / TileWidth); int row = (int)((wordPos.y + MapHeight / 2) / TileHeight); return GetTile(col, row); } //获取鼠标所在位置的世界坐标 Vector3 GetWorldPosition() { Vector3 viewPos = Camera.main.ScreenToViewportPoint(Input.mousePosition); Vector3 worldPos = Camera.main.ViewportToWorldPoint(viewPos); return worldPos; } #endregion}
3.在Editor文件夹下创建编辑器类MapEditor.cs
using UnityEngine;using UnityEditor;using System.IO;using System.Collections;using System.Collections.Generic;[CustomEditor(typeof(Map))]public class MapEditor : Editor{ [HideInInspector] public Map Map = null; //关卡列表 List<FileInfo> m_files = new List<FileInfo>(); //当前编辑的关卡索引号 int m_selectIndex = -1; public override void OnInspectorGUI() { base.OnInspectorGUI(); if(Application.isPlaying) { //关联的Mono脚本组件 Map = target as Map; EditorGUILayout.BeginHorizontal(); int currentIndex = EditorGUILayout.Popup(m_selectIndex, GetNames(m_files)); if (currentIndex != m_selectIndex) { m_selectIndex = currentIndex; //加载关卡 LoadLevel(); } if (GUILayout.Button("读取列表")) { //读取关卡列表 LoadLevelFiles(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("清除塔点")) { Map.ClearHolder(); } if (GUILayout.Button("清除路径")) { Map.ClearRoad(); } EditorGUILayout.EndHorizontal(); if (GUILayout.Button("保存数据")) { //保存关卡 SaveLevel(); } } if (GUI.changed) EditorUtility.SetDirty(target); } void LoadLevelFiles() { //清除状态 Clear(); //加载列表 m_files = Tools.GetLevelFiles(); //默认加载第一个关卡 if (m_files.Count > 0) { m_selectIndex = 0; LoadLevel(); } } void LoadLevel() { FileInfo file = m_files[m_selectIndex]; Level level = new Level(); Tools.FillLevel(file.FullName, ref level); Map.LoadLevel(level); } void SaveLevel() { //获取当前加载的关卡 Level level = Map.Level; //临时索引点 List<Point> list = null; //收集放塔点 list = new List<Point>(); for (int i = 0; i < Map.Grid.Count; i++) { Tile t = Map.Grid[i]; if (t.CanHold) { Point p = new Point(t.X, t.Y); list.Add(p); } } level.Holder = list; //收集寻路点 list = new List<Point>(); for (int i = 0; i < Map.Road.Count; i++) { Tile t = Map.Road[i]; Point p = new Point(t.X, t.Y); list.Add(p); } level.Path = list; //路径 string fileName = m_files[m_selectIndex].FullName; //保存关卡 Tools.SaveLevel(fileName, level); //弹框提示 EditorUtility.DisplayDialog("保存关卡数据", "保存成功", "确定"); } void Clear() { m_files.Clear(); m_selectIndex = -1; } string[] GetNames(List<FileInfo> files) { List<string> names = new List<string>(); foreach (FileInfo file in files) { names.Add(file.Name); } return names.ToArray(); }}
4.自此完成了读取XML文件,显示地图和放塔点,接着要做的是完成在编辑器模式下鼠标左键添加或清除放塔点,右键添加或清除路径点
在map.cs脚本中添加一个事件类TileClickEventArgs,继承EventArgs
//鼠标点击参数类public class TileClickEventArgs : EventArgs{ public int MouseButton; //0左键,1右键 public Tile Tile; public TileClickEventArgs(int mouseButton, Tile tile) { this.MouseButton = mouseButton; this.Tile = tile; }}
定义事件
#region 事件 public event EventHandler<TileClickEventArgs> OnTileClick; #endregion
接着在map类中添加事件回调代码
#region 事件回调 void Map_OnTileClick(object sender, TileClickEventArgs e) { if (Level == null) return; //处理放塔操作 if (e.MouseButton == 0 && !m_road.Contains(e.Tile)) { e.Tile.CanHold = !e.Tile.CanHold; } //处理寻路点操作 if (e.MouseButton == 1 && !e.Tile.CanHold) { if (m_road.Contains(e.Tile)) m_road.Remove(e.Tile); else m_road.Add(e.Tile); } else { } } #endregion
在Awake()方法中监听鼠标点击事件
监听鼠标点击事件 OnTileClick += Map_OnTileClick;
然后在Update中检测触发
void Update() { //鼠标左键检测 if (Input.GetMouseButtonDown(0)) { Tile t = GetTileUnderMouse(); if (t != null) { //触发鼠标左键点击事件 TileClickEventArgs e = new TileClickEventArgs(0, t); if (OnTileClick != null) { OnTileClick(this, e); } } } //鼠标右键检测 if (Input.GetMouseButtonDown(1)) { Tile t = GetTileUnderMouse(); if (t != null) { //触发鼠标右键点击事件 TileClickEventArgs e = new TileClickEventArgs(1, t); if (OnTileClick != null) { OnTileClick(this, e); } } } }
地图编辑器到这里就完成了。第一次写博客完全不知道怎么写,只知道贴代码,就当是自己的学习笔记鼓励自己继续学下去,总比沉迷王者农药要好得多,农药伤身不益智 。mmp—-
阅读全文
7 0
- Unity3d开发——保卫萝卜学习笔记一地图编辑器开发
- 《保卫萝卜》分析续——地图构成
- cocos2dx保卫萝卜项目开发----第一天
- 【Unity3D游戏开发学习笔记】(二)Unity3D编辑器总览
- 地图开发笔记一
- 【Unity3D游戏开发学习笔记】(一)Unity3D初认识
- 【unity3d Editor开发】简单节点编辑器(一)
- <Unity3d学习笔记一>编辑器结构
- Photon + Unity3D 线上游戏开发 学习笔记(一)
- 游戏开发学习笔记——第二期:初识Unity3D
- 百度地图sdk Android开发学习笔记(一)
- 【Cocos2d-X开发学习笔记】开发工具之Tiled地图编辑器的使用
- 地理信息系统学习笔记——地图开发相关介绍
- 学习笔记 - 百度地图开发
- 【游戏客户端开发】Unity3D 学习笔记3——Unity3D资源目录及资源读取
- Unity3D学习笔记(2)——Unity3D与3DMax结合开发注意事项
- 开发文本编辑器学习笔记
- Unity3d——Android开发笔记
- thinkphp根据条件取得id值并更新个别字段的值
- 代理Proxy
- Django学习笔记02
- ASP.NET出现的shit问题总结
- 求逆序数
- Unity3d开发——保卫萝卜学习笔记一地图编辑器开发
- 排序(1)冒泡排序
- Hbase初探
- Ubuntu新增用户登录之后不能执行命令
- static字段初始化过程与静态内部类
- java多线程------实现多线程两种方式
- 309. Best Time to Buy and Sell Stock with Cooldown
- Spring 源码粘贴8-2
- 2016背包专题1005