You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中实现Image灰度精灵竖旗分色及斜向分割需求

解决Unity Image竖旗分色问题及斜向分割实现

原代码问题分析

你当前的代码在同一个Image对象上循环设置颜色、填充量和位置,单个Image只能显示一种状态,之前的循环操作会被后续覆盖,最终只会保留最后一次循环的颜色设置,自然无法实现多段分色。

基础竖旗分色实现方案

正确的做法是为每个颜色段创建独立的Image子对象,每个子对象负责显示一段颜色,代码实现如下:

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public static class ImageFlagExtensions
{
    public static void ColorizeImageAsFlag(Image parentImage, List<Color> colors)
    {
        if (colors.Count < 2)
        {
            parentImage.color = colors.Count == 1 ? colors[0] : Color.white;
            ClearFlagSegments(parentImage);
            return;
        }

        // 清空旧的分段子对象
        ClearFlagSegments(parentImage);

        RectTransform parentRect = parentImage.rectTransform;
        float totalHeight = parentRect.rect.height;
        float segmentHeight = totalHeight / colors.Count;

        // 隐藏原Image,用子段显示分色效果
        parentImage.enabled = false;

        for (int i = 0; i < colors.Count; i++)
        {
            // 创建子Image对象
            Image segmentImage = new GameObject($"FlagSegment_{i}").AddComponent<Image>();
            segmentImage.transform.SetParent(parentRect, false);

            // 复制原Image的核心属性,保证显示效果一致
            segmentImage.sprite = parentImage.sprite;
            segmentImage.type = parentImage.type;
            segmentImage.preserveAspect = parentImage.preserveAspect;
            segmentImage.material = parentImage.material;
            segmentImage.raycastTarget = parentImage.raycastTarget;

            // 设置当前段的颜色
            segmentImage.color = colors[i];

            // 通过锚点设置段的位置和尺寸,自动适配父容器
            RectTransform segmentRect = segmentImage.rectTransform;
            segmentRect.anchorMin = new Vector2(0, 1 - (i + 1) * segmentHeight / totalHeight);
            segmentRect.anchorMax = new Vector2(1, 1 - i * segmentHeight / totalHeight);
            segmentRect.offsetMin = Vector2.zero;
            segmentRect.offsetMax = Vector2.zero;
        }
    }

    private static void ClearFlagSegments(Image parentImage)
    {
        // 销毁所有子段对象
        for (int i = parentImage.transform.childCount - 1; i >= 0; i--)
        {
            Object.Destroy(parentImage.transform.GetChild(i).gameObject);
        }
        parentImage.enabled = true;
    }
}

代码说明

  1. 清空旧子段:每次调用先销毁之前创建的分段子对象,避免重复叠加。
  2. 容器复用:原Image作为父容器隐藏,用子Image承载各段颜色,保证布局结构统一。
  3. 锚点适配:通过anchorMin和anchorMax让每个子段自动适配父容器高度,实现均等垂直分色,父容器尺寸变化时也能自动调整。

斜向分割实现(带倾斜系数控制)

要实现斜向分割,我们需要自定义Image子类,通过修改顶点位置实现倾斜效果,代码如下:

public static void ColorizeImageAsSlantedFlag(Image parentImage, List<Color> colors, float slantFactor = 0.1f)
{
    if (colors.Count < 2)
    {
        ColorizeImageAsFlag(parentImage, colors);
        return;
    }

    ClearFlagSegments(parentImage);
    parentImage.enabled = false;

    RectTransform parentRect = parentImage.rectTransform;
    float totalHeight = parentRect.rect.height;
    float totalWidth = parentRect.rect.width;
    float segmentHeight = totalHeight / colors.Count;

    for (int i = 0; i < colors.Count; i++)
    {
        SlantedImage segmentImage = new GameObject($"SlantedFlagSegment_{i}").AddComponent<SlantedImage>();
        segmentImage.transform.SetParent(parentRect, false);

        // 复制原Image属性
        segmentImage.sprite = parentImage.sprite;
        segmentImage.type = parentImage.type;
        segmentImage.preserveAspect = parentImage.preserveAspect;
        segmentImage.material = parentImage.material;
        segmentImage.raycastTarget = parentImage.raycastTarget;
        segmentImage.color = colors[i];

        // 设置段的位置和尺寸
        RectTransform segmentRect = segmentImage.rectTransform;
        segmentRect.anchorMin = new Vector2(0, 1 - (i + 1) * segmentHeight / totalHeight);
        segmentRect.anchorMax = new Vector2(1, 1 - i * segmentHeight / totalHeight);
        segmentRect.offsetMin = Vector2.zero;
        segmentRect.offsetMax = Vector2.zero;

        // 设置倾斜参数:正数向右斜,负数向左斜
        segmentImage.slantFactor = slantFactor;
        // 标记是否为底部段,保证相邻段倾斜线连贯
        segmentImage.isBottomSegment = i != colors.Count - 1;
    }
}

// 自定义斜向Image类
public class SlantedImage : Image
{
    [Tooltip("倾斜系数,0为垂直,绝对值越大倾斜越明显")]
    public float slantFactor = 0.1f;
    [Tooltip("是否为底部段,用于调整顶点倾斜方向")]
    public bool isBottomSegment = true;

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        base.OnPopulateMesh(vh);

        if (Mathf.Approximately(slantFactor, 0)) return;

        Rect rect = GetPixelAdjustedRect();
        float slantOffset = rect.height * slantFactor;

        List<UIVertex> vertices = new List<UIVertex>();
        vh.GetUIVertexStream(vertices);

        // 修改顶点位置实现倾斜效果
        if (isBottomSegment)
        {
            // 底部段:调整右下角两个顶点的X坐标
            vertices[2].position += new Vector3(slantOffset, 0, 0);
            vertices[3].position += new Vector3(slantOffset, 0, 0);
        }
        else
        {
            // 顶部段:调整右上角两个顶点的X坐标
            vertices[0].position += new Vector3(slantOffset, 0, 0);
            vertices[1].position += new Vector3(slantOffset, 0, 0);
        }

        vh.Clear();
        vh.AddUIVertexTriangleStream(vertices);
    }
}

斜向分割说明

  1. 自定义顶点修改:重写OnPopulateMesh方法调整顶点位置,实现非垂直分割线。
  2. 倾斜系数控制:slantFactor参数控制倾斜程度,正数向右倾斜,负数向左倾斜。
  3. 连贯分割线:通过isBottomSegment标记区分上下段,让相邻段的倾斜顶点对齐,保证分割线连贯无错位。

使用示例

在任意脚本中调用方法即可:

public Image flagImage;
public List<Color> flagColors = new List<Color> { Color.red, Color.white, Color.blue };

void Start()
{
    // 基础竖旗分色
    ImageFlagExtensions.ColorizeImageAsFlag(flagImage, flagColors);

    // 斜向分色(倾斜系数0.1)
    // ImageFlagExtensions.ColorizeImageAsSlantedFlag(flagImage, flagColors, 0.1f);
}

内容的提问来源于stack exchange,提问作者SaltyGiampy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 15:10:29