Unity 2D角色纹理的动态加载和自动分割
发表于2017-01-22
背景
本篇研究的内容是“2D角色纹理的动态加载和自动分割”,估计很多人会有疑问Unity 已经提供了很强大的功能为什么还要研究这部分的内容,首先我们回顾下Unity2d 已经提供的工具和功能
A、Sprite Editor 工具,可以将合并的Texture2d图集进行sprite分割,功能很强大自己可以动态分割,也可以使用Grid自定义分割,还能设置Pivot中心点。
B、Sprite Packer 工具,可以将多个独立的sprite的Texture2d纹理进行打包,提高渲染效率,功能也很强大。
既然Unity已经提供了很么强大的工具,为什么还要研究呢,这里有句话叫“优点及缺点”,这两个工具有一个共同的特点就是可视化的,需要借助Unity编辑器可视化进行操作,也就是说其实他们是手动的而不是自动的,而现实中的程序是需要动态进行配置和自动化分割的。
说了这么多说下我的需求吧,以下两点
1、由于本人目前研究2d的相关技术,所以经常需要做帧动画,结果每写一个例子都需要绑定下精灵数组,有点重复劳动的感觉(代码的坏味道),就是像下图这样
2、实际游戏中各种精灵对象,主角,NPC,怪物等等,每个都手动绑定?是不是有点“so young so simple”(具体如何很好的实现是后话了)。
实现
精灵图片打包
首先由于我自己的角色资源是散装的,首先需要进行Sprite Packer ,当然这也不是必须的比如传统网页游戏和单机游戏就是散装的原理是一样的。这里第一步我需要将散装的图片打包成一张纹理图片。这里我推荐一个小工具CssSprite,文后提供附件下载,当然你也可以用PS这样的重器。具体三张图,聪明的你一看就能明白了。
使用PS打开散装图片
使用CssSprite工具打开的散装图片
最终生成的合成图片
动态加载图片
这里由于是初探,所以就用Resources资源这个比较简单,这里其实没有别的教程写的那么故弄玄虚了,中的Resources中保存的是什么?一句话就是静态资源,也就是一起打包到发布程序的资源。如何使用很多文章讲过。核心函数是
Resources.Load()
主要这个函数有泛型重载使用很方便
固定大小自动分割纹理
如何做纹理的自动分割,这里我找到方法是使用Sprite的静态方法Create,当然我这里做的定宽定长的Grid分割,Create方法有很多重载其实就是对应着Sprite Editor 工具,根据需求可以自行选择
具体代码实现
这里我对资源获取和自动分割做了封装
/// /// 根据合成图片实现sprites(传说中的sprite技术) /// /// 合成图片的具体信息 /// public static Sprite[,] SampleEquipPart(SpriteFrameMixImageInfo spriteFrameMixImageInfo) { Sprite[,] bitmap = new Sprite[spriteFrameMixImageInfo.RowNum, spriteFrameMixImageInfo.ColNum]; //加载角色衣服(身体)大图 Texture2D texture = Resources.Load(spriteFrameMixImageInfo.ResourceFilePath, typeof (Texture2D)) as Texture2D; for ( int i = 0; i < spriteFrameMixImageInfo.RowNum; i++) { for ( int j = 0; j < spriteFrameMixImageInfo.ColNum; j++) { bitmap[i, j] = Sprite.Create(texture, new Rect(j * spriteFrameMixImageInfo.SingleWidth, (spriteFrameMixImageInfo.RowNum - 1 - i) * spriteFrameMixImageInfo.SingleHeight, spriteFrameMixImageInfo.SingleWidth, spriteFrameMixImageInfo.SingleHeight), spriteFrameMixImageInfo.CenterPoint); } } return bitmap; } |
分割函数参数封装
using UnityEngine; /// /// sprite精灵图片合集信息 /// public class SpriteFrameMixImageInfo { #region Texture2d 相关 private int _rowNum; private int _colNum; private int _totalWidth; private int _totalHeight; private int _singleWidth; private int _singleHeight; private string _resourceFilePath; private Vector2 _centerPoint; #endregion private int _bodyIndex; private int _weaponIndex; public SpriteFrameMixImageInfo( string resourceFilePath, int rowNum, int colNum, int singleWidth, int singleHeight, Vector2 centerPoint, int bodyIndex, int weaponIndex) { _rowNum = rowNum; _colNum = colNum; _totalWidth = singleWidth * colNum; _totalHeight = singleHeight*rowNum; _singleWidth = singleWidth; _singleHeight = singleHeight; _resourceFilePath = resourceFilePath; _centerPoint = centerPoint; _bodyIndex = bodyIndex; _weaponIndex = weaponIndex; } public int RowNum { get { return _rowNum; } } public int ColNum { get { return _colNum; } } public int TotalWidth { get { return _totalWidth; } } public int TotalHeight { get { return _totalHeight; } } public int SingleWidth { get { return _singleWidth; } } public int SingleHeight { get { return _singleHeight; } } public string ResourceFilePath { get { return _resourceFilePath; } } public Vector2 CenterPoint { get { return _centerPoint; } } public int BodyIndex { get { return _bodyIndex; } set { _bodyIndex = value; } } public int WeaponIndex { get { return _weaponIndex; } set { _weaponIndex = value; } } } |
具体使用
这里我用的定长二维数组装的纹理集合,由于栗子比较简单做了一次转成一维纹理数组(实际不可能这么简单的)
void Awake() { //初始化精灵跑动动画sprite数组 InitPlayerSprites(); //定义障碍物 ResetMatrix(); //鼠标点击控制 Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0)).Subscribe(LeftMouseClick2); } /// /// 初始化精灵跑动动画sprite数组 /// private void InitPlayerSprites() { SpriteFrameMixImageInfo spriteFrameMixImageInfo = new SpriteFrameMixImageInfo(resourceFilePath: "Play/PlayerRun",rowNum: 1,colNum: 8,singleWidth: 150,singleHeight: 150,centerPoint: new Vector2(0.5f,0.26f), bodyIndex: 0, weaponIndex: 0); //规则数组 Sprite[,] spriteTable = Super.SampleEquipPart(spriteFrameMixImageInfo); textureArray = new Sprite[spriteFrameMixImageInfo.ColNum]; for ( int i = 0; i < textureArray.Length; i++) { textureArray[i] = spriteTable[0, i]; } } |
总结
本文所提到的技术虽然简单,但却是程序从Hello world到复杂程序的一块基石,当然如果大家有更方便的方法,请多多指点!