Unity绳子/绳索效果

老规矩先上图:
在这里插入图片描述

最近在做一个做一个游戏,绳子缠绕在一起然后需要把绳子解开方能赢得游戏。因为需要用到一个绳子的效果,网了查了不少资料一方面是用插件Obi Rope 或 Megafiers 都可以实现,另一方面比较硬核的可以使用自己的算法也可以用关节什么的。

但由于IOS14以上版本对代码审核非常严格,很多插件是无法使用的,只能自己写算法了,网上很多方法都是使用关节实现的绳子效果,绳子并非一条连贯的绳子,以下分享一实现的方式:
一、使用铰链关节(Hinge Joint)把球体串起,保留Sphere Collider和Rigidbody。
在这里插入图片描述
二、随便找下物体挂上下面脚本,使物体可以被拖拽。

using UnityEngine;

public class MousePosHandle : MonoBehaviour
{
    #region 公有变量
    //------------------------------------------------------------------------------------

    //------------------------------------------------------------------------------------
    #endregion

    #region 私有变量
    //------------------------------------------------------------------------------------
    private Transform dragGameObject;
    private Vector3 offset;
    private bool isPicking;
    private Vector3 targetScreenPoint;
    //------------------------------------------------------------------------------------
    #endregion

    #region 公有方法
    #endregion

    #region 私有方法
    //------------------------------------------------------------------------------------
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (CheckGameObject())
            {
                offset = dragGameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetScreenPoint.z));
            }
        }

        if (isPicking)
        {
            //当前鼠标所在的屏幕坐标
            Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetScreenPoint.z);
            //把当前鼠标的屏幕坐标转换成世界坐标
            Vector3 curWorldPoint = Camera.main.ScreenToWorldPoint(curScreenPoint);
            Vector3 targetPos = curWorldPoint + offset;

            dragGameObject.position = targetPos;
        }

        if (Input.GetMouseButtonUp(0))
        {
            isPicking = false;
            if (dragGameObject != null)
            {
                dragGameObject = null;
            }
        }

    }
    //------------------------------------------------------------------------------------
    /// <summary>
    /// 检查是否点击到cbue
    /// </summary>
    /// <returns></returns>
    bool CheckGameObject()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hitInfo;
        if (Physics.Raycast(ray, out hitInfo, 1000f))
        {
            isPicking = true;
            //得到射线碰撞到的物体
            dragGameObject = hitInfo.collider.gameObject.transform;

            targetScreenPoint = Camera.main.WorldToScreenPoint(dragGameObject.position);
            return true;
        }
        return false;
    }
    //------------------------------------------------------------------------------------
    #endregion
}

三、 建模一个圆柱体网格大概是酱子,节不能太少因为绳子会不柔顺,也不能太多因为 性能会伤不起。
在这里插入图片描述
四、加了一个可爱的贴图
在这里插入图片描述
五、然后把球和网格放在一起。
在这里插入图片描述
六、创建MonoBehaviour,添加脚本并挂上对应物体,通过先找到所有球

public MeshFilter TargetMesh;               //网格
public Transform BallGroup;                 //球体集合(关节上所有球放这)
private List<Transform> m_listBall;         //存放所有球体
private List<MeshData> m_listMeshData;      //节点数据

void Start()
 {
        m_listBall = new List<Transform>();
        foreach (Transform tran in BallGroup)
        {
            m_listBall.Add(tran);
        }
}

在这里插入图片描述

七、提供一个方法,帮所有网格节点找到对应的球节点,网格自动寻找离自己最近的球为目标。

private Transform __FindNearest(Vector3 v3)
    {
        if (m_listBall != null)
        {
            float MaxDis = 999999;
            Transform MaxTran = null;
            for (int i = 0; i < m_listBall.Count; i++)
            {
                float curDis = Vector3.Distance(m_listBall[i].localPosition, v3);
                if (curDis < MaxDis)
                {
                    MaxDis = curDis;
                    MaxTran = m_listBall[i];
                }
            }
            return MaxTran;
        }
        return null;
    }

八、提供记录节点数据的数据结构

public class MeshData
{
    public int Index;               //索引
    public Transform target;        //目标球球
    public Vector3 offset;          //与目标球球位置差距
}

九、在Start中调用以下代码找到各节点的目标与位差信息

m_listMeshData = new List<MeshData>();
        int totleMeshPoint = TargetMesh.mesh.vertices.Length;
        for (int i = 0; i < totleMeshPoint; i++)
        {
            MeshData data = new MeshData();

            data.Index = i;
            data.target = __FindNearest(TargetMesh.mesh.vertices[i]);
            if (data.target == null) Debug.Log("有空的");
            data.offset = TargetMesh.mesh.vertices[i] - data.target.localPosition;
            m_listMeshData.Add(data);
        }

十、然后在Update调用以下方法,并拖动。你会得到下面的效果:

	private void MoveMeshPoint()
    {
        Vector3[] v3 = TargetMesh.mesh.vertices;
        for (int i = 0; i < m_listMeshData.Count; i++)
        {
            MeshData curData = m_listMeshData[i];
            v3[i] = curData.target.localPosition+ curData.offset;
        }
        TargetMesh.mesh.vertices = v3;
    }

在这里插入图片描述
十一、跟着动了对吗?显然这还不是我们要的最终效果,优化一下加上这句,
让位置着跟着球球旋转一下:

Vector3 dir = curData.target.transform.TransformDirection(curData.offset);

变成这样:

扫描二维码关注公众号,回复: 11978577 查看本文章
private void MoveMeshPoint()
    {
        Vector3[] v3 = TargetMesh.mesh.vertices;
        for (int i = 0; i < m_listMeshData.Count; i++)
        {
            MeshData curData = m_listMeshData[i];
            Vector3 dir = curData.target.transform.TransformDirection(curData.offset);
            v3[i] = curData.target.localPosition + dir;
        }
        TargetMesh.mesh.vertices = v3;
    }

在这里插入图片描述
十二、完整代码:

using System.Collections.Generic;
using UnityEngine;

public class TestMono : MonoBehaviour
{

    public MeshFilter TargetMesh;               //网格
    public Transform BallGroup;                 //球体集合(关节上所有球放这)
    private List<Transform> m_listBall;         //存放所有球体
    private List<MeshData> m_listMeshData;      //节点数据

    void Start()
    {
        m_listBall = new List<Transform>();
        foreach (Transform tran in BallGroup)
        {
            m_listBall.Add(tran);
        }


        m_listMeshData = new List<MeshData>();
        int totleMeshPoint = TargetMesh.mesh.vertices.Length;
        for (int i = 0; i < totleMeshPoint; i++)
        {
            MeshData data = new MeshData();

            data.Index = i;
            data.target = __FindNearest(TargetMesh.mesh.vertices[i]);
            if (data.target == null) Debug.Log("有空的");
            data.offset = TargetMesh.mesh.vertices[i] - data.target.localPosition;
            m_listMeshData.Add(data);
        }
    }

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

    private void MoveMeshPoint()
    {
        Vector3[] v3 = TargetMesh.mesh.vertices;
        for (int i = 0; i < m_listMeshData.Count; i++)
        {
            MeshData curData = m_listMeshData[i];
            Vector3 dir = curData.target.transform.TransformDirection(curData.offset);
            v3[i] = curData.target.localPosition + dir;
        }
        TargetMesh.mesh.vertices = v3;
    }



    private Transform __FindNearest(Vector3 v3)
    {
        if (m_listBall != null)
        {
            float MaxDis = 999999;
            Transform MaxTran = null;
            for (int i = 0; i < m_listBall.Count; i++)
            {
                float curDis = Vector3.Distance(m_listBall[i].localPosition, v3);
                if (curDis < MaxDis)
                {
                    MaxDis = curDis;
                    MaxTran = m_listBall[i];
                }
            }
            return MaxTran;
        }
        return null;
    }

}

public class MeshData
{
    public int Index;               //索引
    public Transform target;        //目标球球
    public Vector3 offset;          //与目标球球位置差距
}

点赞关注,谢谢。
有问题请与我取得联系。

猜你喜欢

转载自blog.csdn.net/ww1351646544/article/details/109235955