找回密码
 注册帐号

扫一扫,访问微社区

暂存 轻松导入2D作品到Unity里的工作流程(二)

5
回复
4575
查看
[ 复制链接 ]
排名
1
昨日变化

1592

主题

3671

帖子

1万

积分

Rank: 16

UID
2
好友
123
蛮牛币
3425
威望
121
注册时间
2013-5-19
在线时间
2333 小时
最后登录
2019-9-20

原创先锋

2014-6-4 18:52:36 显示全部楼层 阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
第二步,汇入Unity

作者: Brett Bibby - 资深Unity原厂讲师

本篇我们要来介绍如何汇入上次的成果到Unity,文章最下面是整个范例项目打包
接下来继续衔接上一章


Unity用正常方式汇入上次完成的的图形和中介数据是挺容易,但是汇入的图形会默认为一般的贴图,XML档案会被当成一个文字Asset汇入,所以我们要写一个汇入功能让Unity来处理这些档案,或许你会想说我会写一个AssetPostprocessor来解决,那你就误会了,AssetPostprocessor的问题是它限制我们只能处里Asset内容但不允许我们在里面建立我们要的新材质,所以解决方法就是写一个编辑器脚本。


我们的输入器需要下列流程:

  • 加载和逆排序输出XML中介数据
  • 改变贴图输入设定,我们才能用程序处里图形
  • 建立贴图Atlas,并将所有的贴图放入
  • 为每一个图形建立mesh asset
  • 为每一个mesh建立Object
  • 建立一个Scene来放这些Object

编写编辑器脚本

和输出脚本一样,你可以在范例里找到输入的脚本,要让它可以运作,把它放在Editor的目录里,如果你以前没有编写过Unity编辑器脚本,那代表你没有了解Unity最强大的功能之一,编辑器脚本是一般脚本的集合,所以如果你已经会写游戏脚本,代表你一定也有基本的编辑器脚本知识。


编辑器脚本有有三点和普通游戏脚本不同,首先,你必须放在一个名为Editor的目录或子目录里,再来,脚本最上面必须要有这一行

  1. using UnityEditor;
复制代码

最后,为了呼叫这个脚本编辑器,它必须被归类在一个编辑器的子类像是 Editor,ScriptableWizard等等,虽然你不一定要这样归类,但如果你想要接收Event事件或是和编辑器互动,这些类别运作方式才会像是MonoBehaviour。对于我们本章的目的而言我会使用基本的Editor类别。

  1. public class HogImporter : Editor
  2. {
  3.         ...
  4. }
复制代码

跟上一篇一样,我不会逐一介绍每一行程序,因为脚本文件里面会有批注,但我会介绍一些值得注意的地方
我们需要一些管道来呼叫输入流程,所以我用下面这个语法来让选单加在Unity的Assets选单里

  1. [MenuItem("Assets/Create/Import HOG Scene...")]
  2. static public void ImportHogSceneMenuItem()
  3. {
  4.         ...
  5. }
复制代码

现在我们会有个Import HOG Scene功能在Assets选单,选择之后可以执行脚本,接下来就是让这个脚本执行我们要的内容

首先要做的是转换XML档案到我们想要用的格式,例如数据类别(Data Class),之前我们写导出器时规划了数据的排列格式,有这样做就很容易放到一个数据类别,我们要的数据类别会符合我们输出的XML结构,如下所示。

  1. public class HogScene
  2. {
  3.         public Layer[] layers;
  4.         public enum LayerStatus { Active, Found };
  5.         public enum LayerType { Custom, Item, Scenery };
  6.         public class Layer
  7.         {
  8.                 public LayerType type;
  9.                 public string name;
  10.                 public LayerStatus layerStatus;
  11.                 public Rect bounds;
  12.                 public Image[] images;
  13.         }

  14.         public enum ImageType { Hotspot, Obscured, Whole, Shadow, Custom, Count };
  15.         public class Image
  16.         {
  17.                 public ImageType type;
  18.                 public string name;
  19.                 public float x;
  20.                 public float y;
  21.                 public float z;
  22.         }
  23. }
复制代码

要真正转换XML档案到数据类别,我们使用内建的.NET串行化方式

  1. HogScene hogScene = (HogScene)DeserializeXml(assetPath, typeof(HogScene));
  2. ...
  3. static private object DeserializeXml(string filePath, System.Type type)
  4. {
  5.         object instance = null;
  6.         StreamReader xmlFile = File.OpenText(filePath);
  7.         if(xmlFile != null)
  8.         {
  9.                 string xml = xmlFile.ReadToEnd();
  10.                 if((xml != null) && (xml.ToString() != ""))
  11.                 {
  12.                         XmlSerializer xs = new XmlSerializer(type);
  13.                         UTF8Encoding encoding = new UTF8Encoding();
  14.                         byte[] byteArray = encoding.GetBytes(xml);
  15.                         MemoryStream memoryStream = new MemoryStream(byteArray);
  16.                         XmlTextWriter xmlTextWriter =
  17.                                 new XmlTextWriter(memoryStream, Encoding.UTF8);
  18.                         if(xmlTextWriter != null)
  19.                         {
  20.                                 instance = xs.Deserialize(memoryStream);
  21.                         }
  22.                 }
  23.         }
  24.         xmlFile.Close();
  25.         return instance;
  26. }
复制代码

由于我们将使用程序来建立一个场景,要排除建立场景各种可能产生的问题确保它可以被存盘,我们呼叫下列语法

  1. if(EditorApplication.SaveCurrentSceneIfUserWantsTo() == false)
  2. {
  3.         return;
  4. }
复制代码

然后我们就可以删除已存在的旧场景并建立新场景

  1. string scenePath = baseDirectory + baseFilename + " Scene.unity";
  2. if(File.Exists(scenePath) == true)
  3. {
  4.         File.Delete(scenePath);
  5.         AssetDatabase.Refresh();
  6. }
  7. // now create a new scene
  8. EditorApplication.NewScene();
复制代码

接下来是修正texture import设定,预设贴图是无法读取的,所以我们需要改为可读取,才能用程序把图放入Atlas,这里同时也顺便改变一些其他设定

  1. List<Texture2D> textureList = new List<Texture2D>();
  2. for(int layerIndex = 0; layerIndex < hogScene.layers.Length; layerIndex++)
  3. {
  4.         for(int imageIndex = 0;
  5.                 imageIndex < hogScene.layers[layerIndex].images.Length;
  6.                 imageIndex++)
  7.         {
  8.                 // we need to fixup all images that were exported from PS
  9.                 string texturePathName = baseDirectory +
  10.                         hogScene.layers[layerIndex].images[imageIndex].name;
  11.                 Texture2D inputTexture =
  12.                         (Texture2D)AssetDatabase.LoadAssetAtPath(
  13.                         texturePathName, typeof(Texture2D));
  14.                 // modify the importer settings
  15.                 TextureImporter textureImporter =
  16.                         AssetImporter.GetAtPath(texturePathName) as TextureImporter;
  17.                 textureImporter.mipmapEnabled = false;
  18.                 textureImporter.isReadable = true;
  19.                 textureImporter.npotScale = TextureImporterNPOTScale.None;
  20.                 textureImporter.wrapMode = TextureWrapMode.Clamp;
  21.                 textureImporter.filterMode = FilterMode.Point;
  22.                 ...
  23.         }
  24. }
复制代码

现在我们已经可以建立新的Atlas材质,把贴图和其他需要放入的东西放进去

  1. // create material
  2. string materialPath = baseDirectory + baseFilename + " Material.mat";
  3. // remove previous one if it exists
  4. if(File.Exists(materialPath) == true)
  5. {
  6.         File.Delete(materialPath);
  7.         AssetDatabase.Refresh();
  8. }
  9. // make a material and link it to atlas, save that too
  10. Material material = new Material(Shader.Find("Transparent/Diffuse"));
  11. AssetDatabase.CreateAsset(material, materialPath);
  12. AssetDatabase.Refresh();
  13. // load it back
  14. material = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material));
  15. // make a new atlas texture
  16. Texture2D atlas = new Texture2D(2048, 2048);
  17. // to make an atlas we need an array instead of a list
  18. Texture2D[] textureArray = textureList.ToArray();
  19. // pack it with all the textures we have
  20. Rect[] atlasRects = atlas.PackTextures(textureArray, 0, 2048);
  21. // save it to disk
  22. byte[] atlasPng = atlas.EncodeToPNG();
  23. string atlasPath = baseDirectory + baseFilename + " Atlas.png";
  24. if(File.Exists(atlasPath) == true)
  25. {
  26.         File.Delete(atlasPath);
  27.         AssetDatabase.Refresh();
  28. }
  29. File.WriteAllBytes(atlasPath, atlasPng);
复制代码

我们已经完成了所有基本工作,现在来把图档一个一个放入建立好的网格(Mesh)然后放入对象(Object)然后对应坐标放入场景(Scene),这里有很多程序代码所以不在全部说明,如何在Unity建立2D的技术是最有趣的部份,你可能会讶异的是它其实很简单。我用一个功能来处理所有的2D对象,大概是长这样

  1. static private void ConfigureGo(GameObject go, Texture2D texture, Material material,
  2. Rect uvRect, string meshPath)
  3. {
  4.         // create meshFilter if new
  5.         MeshFilter meshFilter = (MeshFilter)go.GetComponent(typeof(MeshFilter));
  6.         if(meshFilter == null)
  7.         {
  8.                 meshFilter = (MeshFilter)go.AddComponent(typeof(MeshFilter));
  9.         }
  10.         // create mesh if new
  11.         Mesh mesh = meshFilter.sharedMesh;
  12.         if(mesh == null)
  13.         {
  14.                 mesh = new Mesh();
  15.         }
  16.         mesh.Clear();

  17.         // setup rendering
  18.         MeshRenderer meshRenderer =
  19.                 (MeshRenderer)go.GetComponent(typeof(MeshRenderer));
  20.         if(meshRenderer == null)
  21.         {
  22.                 meshRenderer =
  23.                         (MeshRenderer)go.AddComponent(typeof(MeshRenderer));
  24.         }
  25.         meshRenderer.renderer.material = material;
  26.         // create the mesh geometry
  27.         // Unity winding order is counter-clockwise when viewed
  28.         // from behind and facing forward (away)
  29.         // Unity winding order is clockwise when viewed
  30.         // from behind and facing behind
  31.         // 1---2
  32.         // | / |
  33.         // | / |
  34.         // 0---3
  35.         Vector3[] newVertices;
  36.         int[] newTriangles;
  37.         Vector2[] uvs;

  38.         float hExtent = texture.width * 0.5f;
  39.         float vExtent = texture.height * 0.5f;

  40.         newVertices = new Vector3[4];
  41.         newVertices[0] = new Vector3(-hExtent, -vExtent, 0);
  42.         newVertices[1] = new Vector3(-hExtent, vExtent, 0);
  43.         newVertices[2] = new Vector3(hExtent, vExtent, 0);
  44.         newVertices[3] = new Vector3(hExtent, -vExtent, 0);

  45.         newTriangles = new int[] { 0, 1, 2, 0, 2, 3 };
  46.         uvs = new Vector2[4];
  47.         uvs[0] = new Vector2(uvRect.x, uvRect.y);
  48.         uvs[1] = new Vector2(uvRect.x, uvRect.y + uvRect.height);
  49.         uvs[2] = new Vector2(uvRect.x + uvRect.width, uvRect.y + uvRect.height);
  50.         uvs[3] = new Vector2(uvRect.x + uvRect.width, uvRect.y);
  51.         Color[] vertColors = new Color[4];
  52.         vertColors[0] = Color.white;
  53.         vertColors[1] = Color.white;
  54.         vertColors[2] = Color.white;
  55.         vertColors[3] = Color.white;

  56.         // update the mesh
  57.         mesh.vertices = newVertices;
  58.         mesh.colors = vertColors;
  59.         mesh.uv = uvs;
  60.         mesh.triangles = newTriangles;
  61.         // generate some some normals for the mesh
  62.         mesh.normals = new Vector3[4];
  63.         mesh.RecalculateNormals();

  64.         if(File.Exists(meshPath) == true)
  65.         {
  66.                 File.Delete(meshPath);
  67.                 AssetDatabase.Refresh();
  68.         }
  69.         AssetDatabase.CreateAsset(mesh, meshPath);
  70.         AssetDatabase.Refresh();
  71.         meshFilter.sharedMesh = mesh;
  72.         // add collider
  73.         go.AddComponent(typeof(MeshCollider));
  74. }
复制代码

看起来很多程序代码,但是其实很简单
终于,在完成建立所有的2D Assets之后,我们只要改变相机的设定让它变成像在photoshop看到的一样完美即可

  1. // setup our game camera
  2. Camera.main.gameObject.AddComponent<HOGController>();
  3. position = Vector3.zero;
  4. position.z = -hogScene.layers.Length;
  5. Camera.main.transform.position = position;
  6. Camera.main.isOrthoGraphic = true;
  7. Camera.main.orthographicSize = (768.0f/2.0f);
  8. Camera.main.nearClipPlane = 1;
  9. Camera.main.farClipPlane = hogScene.layers.Length + 1;
  10. RenderSettings.ambientLight = Color.white;
复制代码

如果你好好的研究所有程序代码,对于本章你应该很容易理解
如果一切执行顺利,汇入脚本后你应该会看到整个场景被建立起来并顺利在Unity里面呈现


1.jpg


注一:meta-data(中介数据),意思是用来诠释补述数据本身的延伸数据,在本范例就是XML文件,用来补充描述2D图形的定位坐标、名称和群组分类,在汇入Unity可以直接被分析的额外数据
本范例项目文件可从这个连结下载:点击此处

  上一篇:轻松导入2D作品到Unity里的工作流程(一)












unity 5.3 ;unity怎么导入;unity工作要求;unity下载文件;order of unity;unity int转string;unity下载的shader;unity 默认的shader;unity怎么改变材质;unity如何导入assets;unity 简单游戏;unity场景下载;unity 场景 打包;unity 5 textures;unity 2d collider;unity中vector3;unity材质 贴图;unity删除项目;unity xml加载;unity;unity 5 2d;unity导入在哪;unity导入;unity导入后怎么看;unity刚工作;unity 文件下载;of order unity;unity string int;unity string转int;unity自带shader 下载;unity 默认shader;unity改变材质;unity 导入assets;unity最简单的游戏;unity在哪儿下载场景;unity打包场景;unity 5.3多场景打包;unity textures;unity collider 2d;vector3 unity;unity vector3什么意思;unity中材质和贴图;unity如何删除项目;unity怎么删除项目;unity 加载xml;unity find;unity asset editor;unity uv贴图;unity脚本打包后;unity find object;如何打包unitypackage;unity material 多个;unity怎么导入场景;unity mat;unity rect;unity 多场景编辑;unity 多场景 选择;unity url加载;unity 加载url;unity www 本地文件;unity什么时候用public;unity 5.0后 renderer;unity atlas 是什么;find gameobject unity;unity gameobject find;if unity editor;unity editor if;unitypackage导出;custom editor unity;unity url下载;unity bounds;unity导入项目;unity如何导入项目;unity项目导入打开;unity rect transform;unity场景贴图;unity meta文件;unity代码怎么写;unity 5 getcomponent;unity float转string;unity instance;unity transform position;unity 程序最小化;unity layer;unity shader目录;unity texture转texture2d
回复

使用道具 举报

7日久生情
2079/5000
排名
598
昨日变化

0

主题

458

帖子

2079

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
3744
好友
0
蛮牛币
4982
威望
0
注册时间
2013-9-12
在线时间
240 小时
最后登录
2016-5-20
2014-9-17 09:55:43 显示全部楼层

看看,学习学习
回复 支持 反对

使用道具 举报

4四处流浪
424/500
排名
7196
昨日变化

5

主题

175

帖子

424

积分

Rank: 4

UID
906
好友
5
蛮牛币
764
威望
20
注册时间
2013-7-16
在线时间
64 小时
最后登录
2018-12-12
2014-9-18 16:49:50 显示全部楼层
赞一个
回复 支持 反对

使用道具 举报

2初来乍到
114/150
排名
19949
昨日变化

0

主题

53

帖子

114

积分

Rank: 2Rank: 2

UID
6164
好友
0
蛮牛币
38
威望
0
注册时间
2013-10-22
在线时间
32 小时
最后登录
2017-7-14
2014-10-23 12:58:56 显示全部楼层
牛啊!很不错的教程
回复 支持 反对

使用道具 举报

2初来乍到
105/150
排名
28932
昨日变化

0

主题

51

帖子

105

积分

Rank: 2Rank: 2

UID
11472
好友
1
蛮牛币
89
威望
0
注册时间
2013-12-30
在线时间
42 小时
最后登录
2018-9-3
2015-2-10 23:37:53 显示全部楼层
看了之后,云里雾里,有点没看懂 ,不知道有没有视频演示
回复 支持 反对

使用道具 举报

9以坛为家
20420/50000
排名
463
昨日变化

236

主题

1万

帖子

2万

积分

Rank: 9Rank: 9Rank: 9

UID
78862
好友
19
蛮牛币
39894
威望
0
注册时间
2015-3-11
在线时间
3311 小时
最后登录
2019-9-12
QQ
2017-7-3 13:34:11 来自Mobile--- 显示全部楼层
难懂啊,额
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则