The old rules are first pictured:
I'm doing a game recently. The ropes are twisted together and then the ropes need to be untied to win the game. Because of the need to use a rope effect, I checked a lot of information on the Internet. On the one hand, it can be achieved with plug-ins Obi Rope or Megafiers. On the other hand, the hard-core ones can use their own algorithms or joints.
However, since IOS14 and above versions are very strict on code review, many plug-ins cannot be used, and you can only write your own algorithms. Many methods on the Internet use joints to achieve the rope effect. The rope is not a coherent rope. The following is an implementation method. :
1. Use Hinge Joint to string the spheres together, and keep Sphere Collider and Rigidbody.
2. Find any object and hang the script below, so that the object can be dragged.
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
}
3. Modeling a cylindrical mesh is probably a sauce. The knots should not be too few because the rope will not be compliant, nor too much because the performance will hurt.
Fourth, add a cute texture.
Fifth, put the ball and the grid together.
6. Create MonoBehaviour, add scripts and hang corresponding objects, find all balls first
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);
}
}
7. Provide a method to help all grid nodes find the corresponding ball nodes, and the grid will automatically find the ball closest to itself as the target.
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;
}
8. Provide a data structure for recording node data
public class MeshData
{
public int Index; //索引
public Transform target; //目标球球
public Vector3 offset; //与目标球球位置差距
}
9. Call the following code in Start to find the target and position difference information of each node
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);
}
10. Then call the following method in Update and drag. You will get the following effect:
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;
}
Eleven, did it follow along, right? Obviously this is not the final result we want, optimize and add this sentence,
let the position follow the ball to rotate:
Vector3 dir = curData.target.transform.TransformDirection(curData.offset);
Becomes like this:
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;
}
12. Complete code:
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; //与目标球球位置差距
}
Like and follow, thank you.
If you have any questions, please contact me.