Unity 2D 如何在运行时替换 Sprite 的辅助纹理?
可以实现,但要区分材质属性和 Sprite 辅助纹理
Material.SetTexture("_Shoes", texture2D) 修改的是材质的纹理属性,不会直接修改 Sprite 关联的 Secondary Textures 数据。EnableKeyword("_SHOES") 也不会自动启用同名纹理槽。
运行时换装时,可以把鞋子纹理改为由脚本控制的普通 Shader Graph 纹理属性,再通过 MaterialPropertyBlock 为当前角色赋值,避开与 Sprite 中同名辅助纹理的绑定冲突。
目前没有 Unity、渲染管线和 Shader Graph 的版本信息,也没有实际的节点连接图,暂时无法确定失败的唯一原因。
为什么能够识别属性,画面却没有变化?
Unity 的 SecondarySpriteTexture 将纹理与 Shader 属性名关联,供 Sprite 渲染时使用。因此,即使材质中存在 _Shoes,最终绘制时使用的也不一定是材质中保存的纹理。Unity:SecondarySpriteTexture
可以从三个位置排查:
- Sprite 是否仍然通过
_Shoes提供凉鞋纹理,导致材质赋值没有成为最终使用的值。 - Shader Graph 是否采样了该属性,并将采样结果连接到最终颜色和透明度的合成路径。
- 脚本使用的名称是否与属性的
Reference Name完全一致,包括大小写。显示名称相同并不够。Unity:Shader Graph 属性参数
纹理属性和 Shader Keyword 是两套机制。只有 Shader 声明并使用了 _SHOES 关键字,它才可能影响渲染分支。普通纹理替换不需要根据属性名额外调用 EnableKeyword。Unity 的 SetTexture 文档中,启用关键字的示例针对特定 Shader 功能,不能直接推广到任意自定义纹理。Unity:Material.SetTexture
方案一:将鞋子纹理交给脚本控制
调整 Shader Graph
- 在 Blackboard 中添加一个
Texture2D属性,将Reference Name设为_RuntimeShoes,并勾选Exposed,方便检查。 - 将该属性连接到鞋子对应的
Sample Texture 2D节点,替换原先使用_Shoes的输入。 - 保留原来的鞋子颜色和透明度合成逻辑。
- 不要在 Sprite 的 Secondary Textures 中添加
_RuntimeShoes同名条目。 - 保存 Shader Graph,确认 SpriteRenderer 使用的材质引用了它。
_RuntimeShoes 是这里新建的自定义属性名,需要同步修改 Shader Graph 和脚本,仅修改代码中的字符串不会生效。
为当前 SpriteRenderer 设置纹理
将下面的脚本挂到拥有 SpriteRenderer 的对象上,在 Inspector 中为 initialShoes 指定初始凉鞋纹理。换装时调用 SetShoes(bootsTexture)。
using UnityEngine;
[RequireComponent(typeof(SpriteRenderer))]
public class ShoesTextureSwitcher : MonoBehaviour
{
[SerializeField] private Texture2D initialShoes;
private static readonly int ShoesId =
Shader.PropertyToID("_RuntimeShoes");
private SpriteRenderer spriteRenderer;
private MaterialPropertyBlock propertyBlock;
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
propertyBlock = new MaterialPropertyBlock();
}
private void Start()
{
SetShoes(initialShoes);
}
public void SetShoes(Texture2D texture)
{
if (texture == null)
return;
Material material = spriteRenderer.sharedMaterial;
if (material == null || !material.HasProperty(ShoesId))
{
Debug.LogError("当前材质缺少 _RuntimeShoes 属性。", this);
return;
}
spriteRenderer.GetPropertyBlock(propertyBlock);
propertyBlock.SetTexture(ShoesId, texture);
spriteRenderer.SetPropertyBlock(propertyBlock);
}
}
MaterialPropertyBlock.SetTexture 设置覆盖值,Renderer.SetPropertyBlock 将覆盖值应用到渲染器。先读取已有的 Property Block,可以保留其他代码已经设置的属性。如果其他脚本随后重新写入整个 Property Block,还需要协调它们的写入逻辑。Unity:SetTexture、Unity:SetPropertyBlock
这种方式控制的是实际显示效果,不会将 Sprite Editor 中原有的 _Shoes 条目改为靴子纹理。
方案二:保留 Secondary Textures 工作方式
如果鞋子必须继续作为 Sprite 自身的辅助纹理,可以预先准备关联不同鞋子纹理的 Sprite,再切换 SpriteRenderer.sprite。如果 Animator 也在驱动该属性,需要同步调整动画使用的 Sprite,否则下一帧可能切回原来的资源。
对于支持相应 API 的 Unity 版本,也可以使用带 secondaryTextures 参数的 Sprite.Create 创建新 Sprite,传入包含 _Shirt、_Pants、_Shoes 等条目的 SecondarySpriteTexture[],然后赋给 SpriteRenderer。这会创建一个新 Sprite,不会原地修改现有 Sprite;使用前应核对项目版本中的 API。Unity:Sprite.Create
采用运行时创建方式时,还需要处理原 Sprite 的矩形区域、pivot、pixelsPerUnit,以及网格、图集或其他附加数据,不能假设简单重建一次就能完整保留所有导入结果。
替换后仍然显示异常时检查什么?
先临时将鞋子采样结果直接接到最终输出,确认新纹理是否进入 Shader,再恢复衣服、裤子和鞋子的合成逻辑,以区分纹理赋值问题和合成问题。
还要检查 UV 和纹理布局。Secondary Textures 使用与主 Sprite 相同的 UV,靴子纹理需要与角色对应区域对齐。如果主 Sprite 使用整张角色图布局,而靴子纹理只裁出了鞋子,沿用同一套 UV 就可能采样到错误区域。使用 Sprite Atlas 时,也需要核对实际图集坐标。Unity:Sprite Editor 的 Secondary Textures
最后检查鞋子区域的 Alpha 和原 Sprite 的网格范围。即使纹理已切换,如果最终透明度仍排除了靴子区域,或靴子超出实际绘制的网格,画面也不会完整显示。
备注:内容仅供参考。
