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

如何在Unity中利用XML提取的矩阵值实现图像倾斜?

在Unity中应用Flash风格的倾斜(Skew)变换

核心原理回顾

你提到的XML矩阵是Flash标准的2D变换矩阵,结构如下:

[ a  c  tx ]
[ b  d  ty ]
[ 0  0  1  ]

其中:

  • a/d:X/Y轴缩放(你已完成这部分应用)
  • b:Y轴沿X方向的倾斜量(对应Y轴倾斜角度的正切值)
  • c:X轴沿Y方向的倾斜量(对应X轴倾斜角度的正切值)

Unity的Transform组件没有原生倾斜参数,以下是两种支持动态动画的实现方案:


方案1:针对Sprite/2D物体(修改Mesh顶点)

适合非UI的2D精灵,直接修改网格顶点实现倾斜,支持实时更新:

实现代码

创建SkewSprite.cs脚本挂载到Sprite物体上:

using UnityEngine;

[RequireComponent(typeof(SpriteRenderer))]
public class SkewSprite : MonoBehaviour
{
    private SpriteRenderer _spriteRenderer;
    private Mesh _mesh;
    private Vector3[] _originalVertices;

    void Awake()
    {
        _spriteRenderer = GetComponent<SpriteRenderer>();
        _mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = _mesh;
        UpdateOriginalVertices();
    }

    void UpdateOriginalVertices()
    {
        Sprite sprite = _spriteRenderer.sprite;
        if (sprite == null) return;

        _originalVertices = new Vector3[]
        {
            new Vector3(-sprite.bounds.extents.x, -sprite.bounds.extents.y, 0),
            new Vector3(sprite.bounds.extents.x, -sprite.bounds.extents.y, 0),
            new Vector3(sprite.bounds.extents.x, sprite.bounds.extents.y, 0),
            new Vector3(-sprite.bounds.extents.x, sprite.bounds.extents.y, 0)
        };
    }

    // 传入XML中的c(skewX)和b(skewY)值
    public void SetSkew(float c, float b)
    {
        Vector3[] skewedVertices = new Vector3[_originalVertices.Length];
        for (int i = 0; i < _originalVertices.Length; i++)
        {
            skewedVertices[i] = new Vector3(
                _originalVertices[i].x + c * _originalVertices[i].y,
                _originalVertices[i].y + b * _originalVertices[i].x,
                _originalVertices[i].z
            );
        }

        _mesh.vertices = skewedVertices;
        _mesh.triangles = new int[] { 0, 1, 2, 0, 2, 3 };
        _mesh.RecalculateBounds();
        _mesh.RecalculateNormals();
    }
}

使用说明

  1. 给Sprite物体添加MeshFilter和MeshRenderer组件
  2. 挂载脚本后,调用SetSkew(-1.66069f, 1.66069f)即可应用你提供的XML参数,动画时可逐帧调用更新

方案2:针对UGUI元素(顶点修改/Shader)

如果是UGUI的Image/Text,推荐以下两种实现方式:

方式A:修改UI顶点

创建SkewUI.cs脚本挂载到UGUI元素上:

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Image))]
public class SkewUI : MonoBehaviour
{
    private Image _image;
    private Vector3[] _originalVertices;

    void Awake()
    {
        _image = GetComponent<Image>();
        UpdateOriginalVertices();
    }

    void UpdateOriginalVertices()
    {
        RectTransform rect = GetComponent<RectTransform>();
        _originalVertices = new Vector3[4];
        rect.GetLocalCorners(_originalVertices);
    }

    public void SetSkew(float c, float b)
    {
        Vector3[] skewedVertices = new Vector3[4];
        Vector2 center = _image.rectTransform.rect.center;

        for (int i = 0; i < 4; i++)
        {
            Vector3 localPos = _originalVertices[i] - center;
            skewedVertices[i] = new Vector3(
                localPos.x + c * localPos.y,
                localPos.y + b * localPos.x,
                localPos.z
            ) + center;
        }

        _image.canvasRenderer.SetVertices(skewedVertices, 4);
    }
}

方式B:自定义Shader(高效批量处理)

适合大量UI元素倾斜场景,创建UI/SkewableUI Shader:

Shader "UI/SkewableUI"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        _SkewX ("Skew X", Float) = 0
        _SkewY ("Skew Y", Float) = 0

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
            };

            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float _SkewX;
            float _SkewY;

            v2f vert(appdata_t v)
            {
                v2f OUT;
                float4 localPos = v.vertex;
                localPos.x += _SkewX * localPos.y;
                localPos.y += _SkewY * localPos.x;
                
                OUT.worldPosition = localPos;
                OUT.vertex = UnityObjectToClipPos(localPos);
                OUT.texcoord = v.texcoord;

                #ifdef UNITY_HALF_TEXEL_OFFSET
                OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
                #endif

                OUT.color = v.color * _Color;
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                return color;
            }
            ENDCG
        }
    }
}

使用时给UI元素替换该Shader,通过脚本动态修改_SkewX和_SkewY参数(对应XML的c和b)。


方案3:直接使用Matrix4x4控制变换

如果你想直接通过完整矩阵覆盖物体变换,可构建4x4矩阵赋值:

public void SetTransformFromMatrix(float a, float b, float c, float d, float tx, float ty)
{
    Matrix4x4 matrix = new Matrix4x4(
        new Vector4(a, b, 0, 0),
        new Vector4(c, d, 0, 0),
        new Vector4(0, 0, 1, 0),
        new Vector4(tx, ty, 0, 1)
    );
    transform.localToWorldMatrix = matrix;
}

注意:该方式会覆盖Transform的缩放、旋转、平移,若已单独设置缩放,需将a/d与现有缩放值结合计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 08:10:25