【Unity3d基础】Unity3d 中如何旋转一张2D图片到指定角度

本文介绍了一种在Unity中让2D对象指向特定方向的简便方法,包括使用Vector3.Angle计算角度、向量点乘判断方向以及通过Quaternion插值实现平滑旋转。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

近日在开发项目中,遇到一个这样的需求:将炮弹旋转至开火方到受击方所成的角度上,简言之就是将一枚炮弹旋转到开或者到敌人的方向上。

如何旋转2D贴图?

查阅了很多资料,都是关于四元数,欧拉角的问题。有点杀鸡焉用牛刀的意味,于是自己摸索着利用已有的接口用一种简单的方法实现。(注意,我的需求存在于2D场景内)

用到的接口

Vector3.Angle

public static float  Angle( Vector3  fromVector3  to);

Parameters

fromThe angle extends round from this vector.
toThe angle extends round to this vector.

Description

Returns the angle in degrees between from and to.

The angle returned is always the non reflex angle between the two vectors - ie the smaller of the two possible angles between them and never greater than 180 degrees.

using UnityEngine;

public class AngleExample : MonoBehaviour
{
	public Transform     target;

	// prints "close" if the z-axis of this transform looks
	// almost towards the target

	void Update ()
	{
		Vector3 targetDir = target.position - transform.position;
		float angle = Vector3.Angle( targetDir, transform.forward );

		if( angle < 5.0f )
			print( "close" );
	}
}

Vector3.Angle()代表的实际含义其实是,原点到from点与原点到to点的夹角,即OA与OB的夹角。俗话说不会美术的策划不是好程序:


然而当我们需要AB与水平坐标的夹角该怎么办呢?这时候就需要引入传说中的单位向量了。

解决方案

using UnityEngine;
using System.Collections;

public class ArrowTest : MonoBehaviour {

    public UISprite arrow;
	// Use this for initialization
	void Start () {
        TestForRotation(); 
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

    void TestForRotation()
    {
        GameObject pointA = GameObject.Find("PointA");
        GameObject pointB = GameObject.Find("PointB");
        Vector3 vecA = pointA.GetComponent<Transform>().localPosition;
        Vector3 vecB = pointB.GetComponent<Transform>().localPosition;
        Vector3 direction = vecB - vecA;                                    ///< 终点减去起点
        float angle = Vector3.Angle(direction, Vector3.right);              ///< 计算旋转角度
        arrow.GetComponent<Transform>().Rotate(0, 0, angle);
    }
}
运行结果:

箭头指向AB方向,垂直结果:



任意“角度”运行结果:



为什么“角度”要加引号?我们来看如果需要旋转到BA所成的夹角会出现什么情况:


显然,箭头的指向于BA所成的角度并不一致。

这是由于Vector3.Angle()的定义中有这样一句话:The angle returned is always the non reflex angle between the two vectors - ie the smaller of the two possible angles between them and never greater than 180 degrees.

返回的角度总是两个角度中较小的一个,且不会超过180°,因此当所需旋转的角度大于90°时,需要进行适当的变换。如何界定是否超过90°呢?这时候Vector3.Dot,向量点乘就派上用场了,当旋转角度与Vector3.up相反时,需要对角度进行适当的变换。

修正后的代码如下:

using UnityEngine;
using System.Collections;

public class ArrowTest : MonoBehaviour {

    public UISprite arrow;
	// Use this for initialization
	void Start () {
        TestForRotation(); 
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

    void TestForRotation()
    {
        GameObject pointA = GameObject.Find("PointA");
        GameObject pointB = GameObject.Find("PointB");
        Vector3 vecA = pointA.GetComponent<Transform>().localPosition;
        Vector3 vecB = pointB.GetComponent<Transform>().localPosition;
        //Vector3 direction = vecB - vecA;                                    ///< 终点减去起点(AB方向与X轴的夹角)
        Vector3 direction = vecA - vecB;                                  ///< (BA方向与X轴的夹角)
        float angle = Vector3.Angle(direction, Vector3.right);              ///< 计算旋转角度
        direction = Vector3.Normalize(direction);                           ///< 向量规范化
        float dot = Vector3.Dot(direction, Vector3.up);                  ///< 判断是否Vector3.right在同一方向
        if (dot < 0)
            angle = 360 - angle;
        Debug.LogWarning("vecA:" + vecA.ToString() + ", vecB:" + vecB.ToString() + ", angle: " + angle.ToString());
        arrow.GetComponent<Transform>().Rotate(0, 0, angle);
    }
}
箭头转向BA向量的结果如图:


7.11补充:

1)更新一种计算角度的方法,通过Atan()接口

2)通过欧拉角来旋转对象

3) 通过四元数的插值函数来平滑过渡旋转过程

using UnityEngine;
using System.Collections;

public class ArrowTest : MonoBehaviour
{

    public UISprite arrow;

    private Vector3 targetVec;
    private float targetAngle, AtanTarget;
    // Use this for initialization
    void Start()
    {
        TestForRotation();

    }

    // Update is called once per frame
    void Update()
    {
        ///< 补充点3: 通过插值使箭头平滑的转向指定的方向,在游戏中常用于人物转头,转移视角等操作,当然是在3D空间中,这里就抛块砖了。0.1f只是我偷懒写的一个单位值,正式的项目中一般会deltaTime * speed来控制转向的速度。
        arrow.GetComponent<Transform>().rotation = Quaternion.Slerp(arrow.GetComponent<Transform>().rotation, Quaternion.Euler(0, 0, AtanTarget), 0.1f);
    }

    void TestForRotation()
    {
        GameObject pointA = GameObject.Find("PointA");
        GameObject pointB = GameObject.Find("PointB");
        Vector3 vecA = pointA.GetComponent<Transform>().localPosition;
        Vector3 vecB = pointB.GetComponent<Transform>().localPosition;
        //Vector3 direction = vecB - vecA;                                    ///< 终点减去起点(AB方向与X轴的夹角)
        Vector3 direction = vecA - vecB;                                  ///< (BA方向与X轴的夹角)
        float angle = Vector3.Angle(direction, Vector3.right);              ///< 计算旋转角度
        direction = Vector3.Normalize(direction);                           ///< 向量规范化
        float dot = Vector3.Dot(direction, Vector3.up);                  ///< 判断是否Vector3.right在同一方向
        if (dot < 0)
            angle = 360 - angle;
        
        targetAngle = angle;
        targetVec = new Vector3(0, 0, angle);

        ///< 补充点1: 通过Atan2与方向向量的两条边可以计算出转向的角度,通过计算结果可以看到targetAngle与-AtanTarget相加正好是360°,即二者都指向同一方向。具体使用场景需要根据具体需求分析。
        AtanTarget = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        Debug.LogWarning("vecA:" + vecA.ToString() + ", vecB:" + vecB.ToString() + ", targetAngle: " + targetAngle.ToString() + ", AtanTarget: " + AtanTarget.ToString());
        //arrow.GetComponent<Transform>().Rotate(0, 0, angle);

        ///< 补充点2: 使用欧拉角来控制物体的旋转
        //arrow.GetComponent<Transform>().eulerAngles = new Vector3(0, 0, angle);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值