老規矩先上圖:

最近在做一個做一個游戲,繩子纏繞在一起然后需要把繩子解開方能贏得游戲,因為需要用到一個繩子的效果,網了查了不少資料一方面是用插件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);
變成這樣:
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; //與目標球球位置差距
}
點贊關注,謝謝,
有問題請與我取得聯系,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/189368.html
標籤:其他
上一篇:微信小程式筆記(1)
