雪千渔Blog

  • 首页
  • 写写代码
    • dotNet
    • C++
    • Lua
    • Visual Basic
    • Java
    • Android
    • Web
  • DCC
    • Maya
    • Maya-Plug
    • AfterEffect
    • AfterEffect-Plug
    • PhotoShop-Plug
  • 游戏制作
    • Unity3D
    • UnrealEngine
    • 经验杂谈
    • 游戏设计
    • 自研引擎
    • 效果实现
  • 其他
    • 乱七八糟
    • 软件工具
    • 留言板
    • 自制素材
    • 关于我
雪千渔blog
唯有热爱,能抵漫长岁月
  1. 首页
  2. GameDev
  3. Unity3D
  4. 正文

ugui多层Sprite的混合合并

2022年4月17日 659点热度 2人点赞 0条评论

Sprite是以Texture2d为基础的,所以我们应该首先获取Sprite的texture2d对象,合并,然后生成新的Sprite对象。

可以用画画的方式一层一层的混合,Unity3d新建的Texture2d对象并不是一张RGB为0的图,而是0.8的灰色缓冲区,所以我们需要清空这个颜色,只需要用最底层的texture去填充覆盖,上面的texture在绘制即可。

分别为两个指令,Fill和Draw。

public static void Fill(Texture2D src, Texture2D brush)
{
    src.SetPixels(brush.GetPixels());
}

Draw绘制函数

public static void Draw(Texture2D src, Texture2D brush, Vector2Int pos)
{
    int width = brush.width;
    int height = brush.height;

    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++)
        {
            var destColor = brush.GetPixel(i, j);
            var srcColor = src.GetPixel(pos.x + i, pos.y + j);
            if (srcColor.a != 0)
            {
                float alpha = srcColor.a + destColor.a;
                destColor = Color.Lerp(srcColor, destColor, destColor.a);
                if (alpha >= 1)
                {
                    destColor.a = 1;
                }
            }
            src.SetPixel(pos.x + i, pos.y + j, destColor);
        }
    }
}

该函数对旧颜色进行判断,如果旧颜色的的透明度不为0,则使用新颜色的透明度对两个颜色插值,这是为了处理两层交界边缘的半透明区域。但是Lerp同时也会插值alpha,会在旧不透明与新半透明时混合错误,所以这里做了一个判断,如果两个颜色透明度相加大于等于1,那就将Lerp插值错误的alpha补偿回来。

现在已经可以合并多个Texture了,例:

Texture2D t1 = sprite1.texture as Texture2D;
Texture2D t2 = sprite2.texutre as Texture2D;
Texture2D tex = new Texture2D(t1.width, t1.height, t1.format, false);
Fill(tex, t1);
Draw(tex, t2, Vector2Int.zero);
tex.Apply();

到这里我们已经可以手动设置多张texture合成了。

public static Texture2D ComposeNew(Texture2D[] objs, Vector2Int[] offset)
{
    Texture2D t1 = objs[0];
    Texture2D tex = new Texture2D(t1.width, t1.height, t1.format, false);
    Fill(tex, t1);
    for (int i = 1; i < objs.Length; i++)
    {
        Draw(tex, objs[i], offset[i]);
    }
    tex.Apply();
    return tex;
}

但是对于sprite来说还需要计算它们的偏移。

在ugui中的sprite以Image显示,而在ugui的Canvas中一个单位即一个像素。在绘制的过程中我们要注意一点:Unity的颜色缓冲信息是从图像的左下角开始的,所以我们要算出Image在Canvas中的相对位置以及和填充层的相对位置。

一般情况下ugui的控件中心点pivot为(0.5,0.5),使用fillRect.sizeDelta * fillRect.pivot就可以求出填充层的中心位置,但是从填充层中心开始画就错了,因为是从左下角开始画的,所以这里还需要减掉需要绘制图像的一半:

fillRect.sizeDelta * fillRect.pivot - transRect.sizeDelta / 2

然后我们加上两个对象在Canvas上的相对距离,首先编写控件获取在Canvas上的位置:

private static Vector2 TransformToCanvasLocalPosition(Transform current, Canvas canvas)
{
    var screenPos = canvas.worldCamera.WorldToScreenPoint(current.transform.position);
    Vector2 localPos;
    RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.GetComponent<RectTransform>(), screenPos,
        canvas.worldCamera, out localPos);
    return localPos;
}

最后将上述表达式整合,编写Sprite合成函数:

public static Sprite ComposeNewSpriteFromUImage(Transform[] transforms, Canvas canvas)
{
    Texture2D[] texArr = new Texture2D[transforms.Length];
    Vector2Int[] offsetArr = new Vector2Int[transforms.Length];
    var first = transforms[0];
    for (int i = 0; i < transforms.Length; i++)
    {
        var transform = transforms[i];
        texArr[i] = transform.GetComponent<Image>().mainTexture as Texture2D;
        if (i == 0)
        {
            offsetArr[i] = new Vector2Int(0, 0);
        }
        else
        {
            var fillRect = first.GetComponent<RectTransform>();
            var transRect = transform.GetComponent<RectTransform>();

            var offset = fillRect.sizeDelta * fillRect.pivot - transRect.sizeDelta / 2;
            offset += TransformToCanvasLocalPosition(transform, canvas) - TransformToCanvasLocalPosition(first, canvas);

            offsetArr[i] = new Vector2Int((int)offset.x, (int)offset.y);
        }
    }
    var tex = ComposeNew(texArr, offsetArr);
    return Texture2DUtility.Texture2dToSprite(tex);
}

 

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: Sprite Texture Unity
最后更新:2022年4月19日

JomiXedYu

IndieGame Developer & Netease Games TechArt

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

JomiXedYu

IndieGame Developer & Netease Games TechArt

最新 热点 随机
最新 热点 随机
UE材质Custom节点与HLSL的HACK操作 UE5出现D3D12崩溃报错解决方案 游戏引擎脚本绑定的三种写法与利弊 一种解决不支持中文与空格文件名的编码 Vulkan坐标系通用方案以及反转viewport UE编辑器开发之蓝图篇
一种解决不支持中文与空格文件名的编码游戏引擎脚本绑定的三种写法与利弊UE5出现D3D12崩溃报错解决方案UE材质Custom节点与HLSL的HACK操作
Unity之Galgame引擎框架制作思路 游戏引擎脚本绑定的三种写法与利弊 C++中的事件分发器与委托 UE编辑器开发之UToolMenus扩展位置 xLua无法判断Unity对象为nil的问题 VB6读取32/64位系统注册表检测Java安装路径
友情链接
  • DorinXL
  • 小博博客
  • 秋橘斋

COPYRIGHT © 2014-2023 雪千渔Blog. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

辽ICP备20006894号-1