大家好,我是阿赵。
有时候用Unity引擎做项目的时候,会遇到这样的需求,美术做了一些模型或者特效,然后策划想在游戏运行的时候,读取一些游戏图标放在特效或者模型上面当做贴图使用。
这个需求实现起来很简单,比如这样:
我这里随便找了一些icon图片,在Unity里面设置成了Sprite的贴图类型。并且输入了Packing Tag,意思是这些图片将会作为一个图集来使用。
然后我建了一个面片,当做模型。
接下来写代码了,代码很简单:
private void SetTextureBySprite(Sprite sp)
{
render.material.SetTexture("_MainTex", sp.texture);
}
在编辑器里面执行代码,会发现贴图被赋予上去了:
不过,如果我们实际打包游戏运行之后,会发现效果是不对的:
为什么会出现编辑器和实际打包运行效果不一致的情况呢?
这是因为上面我设置了Packing Tag,这样,同一个Tag的图片,将会打包成一个图集。
由于打图集的时间比较长,所以一般来说在编辑器环境下,会把图集取消掉:
勾选Disabled或者Enabled For Builds,在编辑器都是不打图集就运行的。
如果想在编辑器里面就能看到图集效果,要选Always Enabled,这样编辑器里面运行的效果就和打包之后的效果一致了。不过这样每次图集里面的图片有改动时,Unity会先重新生成图集,会比较慢。
用SpritePacker看一下:
在实际运行中,这些icon的贴图不再是单张的Texture,而是组合成这样一张贴图了。
再查看一下Unity的API:
里面很明确的说明了,假如packed了,sprite.texture指向的是整个图集。
怎样解决这个问题呢?
再继续看Unity的API:
Unity提供了textureRect的API,可以把原来的Sprite在现在整张图集所占的位置通过Rect返回。于是我们就可以用这个Rect来计算,实际的Sprite占整张图集的位置,也就是求出了采样的UV坐标了:
private void SetTextureBySpriteRect(Sprite sp)
{
Texture2D tex = sp.texture;
float texWidth = tex.width;
float texHeight = tex.height;
Rect rect = sp.textureRect;
Vector4 uv =new Vector4(rect.width / texWidth, rect.height / texHeight, rect.x/tex.width, rect.y/tex.height);
render.material.SetTexture("_MainTex", sp.texture);
render.material.SetVector("_MainTex_ST", uv);
}
这里注意的是,求出的UV,是通过SetVector设置进材质球的,用的变量名_MainTex_ST是对应主贴图_MainTex的,熟悉写Shader的朋友应该都知道,你声明了一张贴图置换,贴图名称后面接_ST就是这张贴图的UV。
所以这里通过_MainTex_ST设置UV不是固定的,要根据你实际的贴图名称来改。
最后运行代码,这次显示就应该是正确的了,其实这里面的原理,和之前说的地形局部采样UV是一样的。