实现unity内2D物品延迟跟随鼠标移动

先摆出效果示意图

不知道有没有实现了你想要的效果,反正我是挺满意的,图里没演示它停下来的模样,只要它的坐标和鼠标的坐标一致就不会动了。

其实我还去试过其他人的办法,但大部分文章都是一样的,而且是给3D用的,是这样写的。

this.transform.LookAt(mousePosition);
this.transform.Translate(Vector3.forward * 1f * Time.deltaTime,Space.Self); 

如果用百度找的方法来做,那这个2D物品就会进行翻转,原因在于那个LookAt的函数,它会让物品看向鼠标的方向,unity好像没给2D物品用的LookAt函数,毕竟重写一个函数还是很浪费时间的,能用现成的就用现成的。不过我找资料时发现了另一个更好的解决方法,后面会提到。

那再说说看代码实现吧

一开始我是自己做的,我认为它的实现逻辑应该就是gameobject的position往鼠标的position靠拢,我没能找到unity中有现成的函数可用,所以只能自己写一个了。

我开始写的时候遇到了不小困难,仅仅只是让gameobject的位置和鼠标的位置一致的话还比较简单,但依然会遇到一个问题。

那就是屏幕的鼠标坐标和游戏里物件里面的坐标的坐标轴是不同的,它们的单位不同,所以最开始运行的时候它直接飞出了屏幕外。所幸的是它没一直往一个方向飞,通过它飞到某个区域后开始绕点打转的这一行为让我发觉了这个问题,随后百度解决了这个问题。

用一段代码即可解决

mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);

这段代码的意思是将鼠标的vector3的屏幕坐标转化游戏内的坐标

而后的困难就是计算它的路程,那是初中知识了,但要现在的我重新捡起来还是费了十几分钟。

具体的数学原理是这样的

 先声明变量,mouse为鼠标的坐标,item为gameobject的坐标,以item为二维坐标轴的原点,L是mouse离item的直线距离,X是mouse离X坐标轴的直线距离,Y是mouse离Y坐标轴的直线距离,l是item在一帧内可移动的距离,x是移动后item离X坐标轴的直线距离,y是移动后item里y坐标轴的直线距离,现知道mouse的坐标,item的坐标和l的大小,求x和y的大小

嘛,写成数学题就是这样表述的,题目很长,但问题不算难。我的解决方案是这样的:

 X和Y都可以通过两个坐标求出来,再通过相似三角形的公式就能算出x和y。

数学上的问题解决了,但我们都知道做游戏要远比这复杂多了,实际上我们还得考虑mouse到底位于item的哪个位置,左上?右下?那又该怎么解决?

我的代码如下:

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

public class player : MonoBehaviour
{
    [Header("变量")]
    public Collider2D playerCollider;
    public Vector3 mousePosition;
    public Vector3 emtpyVector2;
    public Vector3 emtpyPosition;
    public float totalLength;
    public float playerSpeed;

    // Start is called before the first frame update
    void Start()
    {
        playerCollider = GetComponent<Collider2D>();
    }

    // Update is called once per frame
    void Update()
    {
        Move();
    }

    void Move()
    {
        mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        emtpyPosition = playerCollider.gameObject.transform.position;
        totalLength = Mathf.Pow(Mathf.Pow(mousePosition.x - emtpyPosition.x,2) + Mathf.Pow(mousePosition.y - emtpyPosition.y,2), 0.5f);

        if (playerSpeed > totalLength)
            emtpyVector2 = Cal(totalLength, mousePosition.x - emtpyPosition.x, mousePosition.y - emtpyPosition.y, totalLength, emtpyVector2, mousePosition, emtpyPosition);
        else
            emtpyVector2 = Cal(playerSpeed, mousePosition.x - emtpyPosition.x, mousePosition.y - emtpyPosition.y, totalLength, emtpyVector2, mousePosition, emtpyPosition);

        if(mousePosition.x != emtpyPosition.x || mousePosition.y != emtpyPosition.y)
        {
            playerCollider.gameObject.transform.position = new Vector3(emtpyPosition.x + emtpyVector2.x, emtpyPosition.y + emtpyVector2.y, emtpyPosition.z);
        }
    }

    Vector2 Cal(float passLength, float x, float y, float totalLength, Vector2 vector2, Vector2 mousePosition, Vector2 playerPosition)
    {
        vector2.x = passLength / totalLength * x;
        vector2.y = passLength / totalLength * y;
        
        return vector2;
    }
}

请忽略我代码基础差的事实,变量全部声明为public是因为我要看看它们之间的关系,现在让我们摘取核心部分看看

void Move()
    {
        mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        emtpyPosition = playerCollider.gameObject.transform.position;
        totalLength = Mathf.Pow(Mathf.Pow(mousePosition.x - emtpyPosition.x,2) + Mathf.Pow(mousePosition.y - emtpyPosition.y,2), 0.5f);

        if (playerSpeed > totalLength)
            emtpyVector2 = Cal(totalLength, mousePosition.x - emtpyPosition.x, mousePosition.y - emtpyPosition.y, totalLength, emtpyVector2, mousePosition, emtpyPosition);
        else
            emtpyVector2 = Cal(playerSpeed, mousePosition.x - emtpyPosition.x, mousePosition.y - emtpyPosition.y, totalLength, emtpyVector2, mousePosition, emtpyPosition);

        if(mousePosition.x != emtpyPosition.x || mousePosition.y != emtpyPosition.y)
        {
            playerCollider.gameObject.transform.position = new Vector3(emtpyPosition.x + emtpyVector2.x, emtpyPosition.y + emtpyVector2.y, emtpyPosition.z);
        }
    }

    Vector2 Cal(float passLength, float x, float y, float totalLength, Vector2 vector2, Vector2 mousePosition, Vector2 playerPosition)
    {
        vector2.x = passLength / totalLength * x;
        vector2.y = passLength / totalLength * y;
        
        return vector2;
    }

鼠标的坐标如何获取并转换前面以及提过了,然后是通过直角三角形两边平方之和等于斜边平方的公式算出两者间的距离,即totalLength

在Cal函数内我实现了算出x和y这个功能,用一个vector2来返回了这两个值。

我在调用这个计算函数时有两种情况,一个是可移动的距离小于物品和鼠标之间的距离,即l<L,如果是这种情况的话可移动距离,即第一个参数传的是l,物品可移动的距离,否则传L,两者之间的距离,通过这种方式实现了物品的坐标完全和鼠标坐标重合并停止运动。数学上的原理不用再说明了吧?

另外有一点我在写代码时踩过的坑是关于x和y的正负,这个需要注意,如果mouse在item的左下角,而你的x和y算出来都是正数,那它就会往mouse相反的方向直直飞走,再见了您嘞。

我这边因为totalLength开方后是正数,passLength也只会是正数,所以取决于传进来的x和y的正负,解决了这个问题。

其次是如何判断物品的坐标和鼠标的坐标是否重合,不能直接用transform.position来进行比较,我当时虽然游戏有在运行,但后台不停报错,似乎两者不会因为坐标相等就完全相等,效果不变,但它会试图给物品赋一个空值,所以后台一直报错。然后我改了一下,就解决了。

这是一个笨办法,本着绝不误人子弟的想法,我再次查了一遍资料,发现了一个更好的方法。

Vector2.Lerp函数可以实现我以上的相同功能,我就不重写一遍了,有兴趣的可以到这个链接直接看官方文档关于Vector2.Lerp函数的描述

学无止境,好了,所有要说明的技术难题都结束了,这次是我第一次分享有关unity的相关技术。要是你有什么问题请尽管留言,虽然很少上来看。要是你想和我一起做独立游戏,那请务必联系我(我在想pitch)

猜你喜欢

转载自blog.csdn.net/weixin_49779414/article/details/122442439