c# - 我可以使用哪些方法来处理球上的软体物理
问题描述
我正在 Unity 中为其中一个对象制作一个带有橡皮球的游戏。我浏览了各种可用的软体物理资产,但没有一个看起来特别符合我的要求。我希望编写一个会弹跳的球,并在它落地时进行拉伸。这将是我的游戏的中心点,我希望它看起来尽可能好,但是我在网上找到的所有解决方案都是小补丁或写得不好。我也想研究解决方案,以便将来适应并从中学习,我不想只是购买资产并将其投入游戏中。
我首先被零世界及其网格变形视频所吸引。然而,这些对于我的需求来说似乎异常复杂,我被他的 Squishy Sphere 视频所吸引,在那里它们不能很好地工作。最终我发现了 Catlike Coding 及其基于向量等的网格变形。它在视觉上效果非常好,但是根据我的需要调整它比我希望的要复杂得多。
编辑2:我添加了这个类的其余部分以更好地展示功能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshDeformerInput : MonoBehaviour
{
public float force = 10f;
public float forceOffset = 0.01f;
void HandleInput(Ray inputRay)
{
RaycastHit hit;
Debug.DrawRay(inputRay.origin, inputRay.direction * 50000000, Color.red);
if (Physics.Raycast(inputRay, out hit))
{
MeshDeformer deformer = hit.collider.GetComponent<MeshDeformer>();
if (deformer)
{
Vector3 point = hit.point;
point += hit.normal * forceOffset;
deformer.AddDeformingForce(point, force);
}
}
}
void OnCollisionStay(Collision collision)
{
foreach (ContactPoint contact in collision.contacts)
{
Debug.Log("Hit!");
// Visualize the contact point
Debug.DrawRay(contact.point, contact.normal, Color.white);
Ray newRay = new Ray(contact.point, contact.normal);
HandleInput(newRay);
}
}
我遇到了 OnCollisionEnter 的问题,因为当球滚动时它不会继续正确地进行碰撞检测。我转而继续希望在球滚动时改变矢量,而不是在一个点上,它工作得更好一点。
在它调用 HandleInput 之后,将光线和静态力带入我的网格变形器,从这里开始它与本教程中的代码相同
https://catlikecoding.com/unity/tutorials/mesh-deformation/
我遇到的问题首先是球滚动时接触点移动太慢,导致错误放置的凹陷。弹簧物理响应太慢,增加速度会导致网格物理完全崩溃。其次,球在接触点上方盘旋,因为对撞机仍然没有移动,这会导致我的游戏出现重大问题。我想我要问的是这种方法是否有任何替代方法,或者如果这个方法很好,我只是在接近这个错误?我已经读到 Unity 没有附带标准的软体物理,但这是一个我永远无法完全正确的功能,因为它默认没有,还是我只是在错误的地方寻找?
编辑1:链接失效是一个公平点,所以这里是代码。我将尝试将其全部包含在内,因为它相当厚,而且我觉得描述它是不够的。
首先是我的实际网格变形器。该代码使用了一个圆角立方体,它是用我放在最后的代码构建的,因此我遇到了问题。每个顶点都被编码在一个循环中,以根据与碰撞点的距离进行缩进。最初它是从您从相机点击的地方绘制的光线,我调整了它以改为从与物体的接触点绘制光线。它似乎至少在正确的位置工作,只是不是马上就可以保持凹痕的时间比它应该的要长。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class MeshDeformer : MonoBehaviour
{
Mesh deformingMesh;
Vector3[] originalVertices, displacedVertices, vertexVelocities;
public float forceOffset = 0.1f;
public float springForce = 20f;
public float damping = 5f;
float uniformScale = 1f;
void Start()
{
deformingMesh = GetComponent<MeshFilter>().mesh;
originalVertices = deformingMesh.vertices;
displacedVertices = new Vector3[originalVertices.Length];
vertexVelocities = new Vector3[originalVertices.Length];
for (int i = 0; i < originalVertices.Length; i++)
{
displacedVertices[i] = originalVertices[i];
}
}
public void AddDeformingForce(Vector3 point, float force)
{
Debug.Log("Deformer called with: " + point + " point");
point = transform.InverseTransformPoint(point);
for (int i = 0; i < displacedVertices.Length; i++)
{
AddForceToVertex(i, point, force);
}
}
void AddForceToVertex(int i, Vector3 point, float force)
{
Vector3 pointToVertex = displacedVertices[i] - point;
pointToVertex *= uniformScale;
float attenuatedForce = force / (1f + pointToVertex.sqrMagnitude);
float velocity = attenuatedForce * Time.deltaTime;
vertexVelocities[i] += pointToVertex.normalized * velocity;
}
void Update()
{
uniformScale = transform.localScale.x;
for (int i = 0; i < displacedVertices.Length; i++)
{
UpdateVertex(i);
}
deformingMesh.vertices = displacedVertices;
deformingMesh.RecalculateNormals();
}
void UpdateVertex(int i)
{
Vector3 velocity = vertexVelocities[i];
Vector3 displacement = displacedVertices[i] - originalVertices[i];
displacement *= uniformScale;
velocity -= displacement * springForce * Time.deltaTime;
velocity *= 1f - damping * Time.deltaTime;
vertexVelocities[i] = velocity;
displacedVertices[i] += velocity * (Time.deltaTime / uniformScale);
}
}
不确定这是否会有所帮助,但这是球体编码,以防万一。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class CubeSphere : MonoBehaviour
{
public int gridSize;
public float radius = 1f;
private Mesh mesh;
private Vector3[] vertices;
private Vector3[] normals;
private Color32[] cubeUV;
private void Awake()
{
Generate();
}
private void Generate()
{
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
mesh.name = "Procedural Cube";
CreateVertices();
CreateTriangles();
CreateColliders();
}
private void CreateVertices()
{
int cornerVertices = 8;
int edgeVertices = (gridSize + gridSize + gridSize - 3) * 4;
int faceVertices = (
(gridSize - 1) * (gridSize - 1) +
(gridSize - 1) * (gridSize - 1) +
(gridSize - 1) * (gridSize - 1)) * 2;
vertices = new Vector3[cornerVertices + edgeVertices + faceVertices];
normals = new Vector3[vertices.Length];
cubeUV = new Color32[vertices.Length];
int v = 0;
for (int y = 0; y <= gridSize; y++)
{
for (int x = 0; x <= gridSize; x++)
{
SetVertex(v++, x, y, 0);
}
for (int z = 1; z <= gridSize; z++)
{
SetVertex(v++, gridSize, y, z);
}
for (int x = gridSize - 1; x >= 0; x--)
{
SetVertex(v++, x, y, gridSize);
}
for (int z = gridSize - 1; z > 0; z--)
{
SetVertex(v++, 0, y, z);
}
}
for (int z = 1; z < gridSize; z++)
{
for (int x = 1; x < gridSize; x++)
{
SetVertex(v++, x, gridSize, z);
}
}
for (int z = 1; z < gridSize; z++)
{
for (int x = 1; x < gridSize; x++)
{
SetVertex(v++, x, 0, z);
}
}
mesh.vertices = vertices;
mesh.normals = normals;
mesh.colors32 = cubeUV;
}
private void SetVertex(int i, int x, int y, int z)
{
Vector3 v = new Vector3(x, y, z) * 2f / gridSize - Vector3.one;
float x2 = v.x * v.x;
float y2 = v.y * v.y;
float z2 = v.z * v.z;
Vector3 s;
s.x = v.x * Mathf.Sqrt(1f - y2 / 2f - z2 / 2f + y2 * z2 / 3f);
s.y = v.y * Mathf.Sqrt(1f - x2 / 2f - z2 / 2f + x2 * z2 / 3f);
s.z = v.z * Mathf.Sqrt(1f - x2 / 2f - y2 / 2f + x2 * y2 / 3f);
normals[i] = s;
vertices[i] = normals[i] * radius;
cubeUV[i] = new Color32((byte)x, (byte)y, (byte)z, 0);
}
private void CreateTriangles()
{
int[] trianglesZ = new int[(gridSize * gridSize) * 12];
int[] trianglesX = new int[(gridSize * gridSize) * 12];
int[] trianglesY = new int[(gridSize * gridSize) * 12];
int ring = (gridSize + gridSize) * 2;
int tZ = 0, tX = 0, tY = 0, v = 0;
for (int y = 0; y < gridSize; y++, v++)
{
for (int q = 0; q < gridSize; q++, v++)
{
tZ = SetQuad(trianglesZ, tZ, v, v + 1, v + ring, v + ring + 1);
}
for (int q = 0; q < gridSize; q++, v++)
{
tX = SetQuad(trianglesX, tX, v, v + 1, v + ring, v + ring + 1);
}
for (int q = 0; q < gridSize; q++, v++)
{
tZ = SetQuad(trianglesZ, tZ, v, v + 1, v + ring, v + ring + 1);
}
for (int q = 0; q < gridSize - 1; q++, v++)
{
tX = SetQuad(trianglesX, tX, v, v + 1, v + ring, v + ring + 1);
}
tX = SetQuad(trianglesX, tX, v, v - ring + 1, v + ring, v + 1);
}
tY = CreateTopFace(trianglesY, tY, ring);
tY = CreatebottomFace(trianglesY, tY, ring);
mesh.subMeshCount = 3;
mesh.SetTriangles(trianglesZ, 0);
mesh.SetTriangles(trianglesX, 1);
mesh.SetTriangles(trianglesY, 2);
}
private int CreateTopFace(int[] triangles, int t, int ring)
{
int v = ring * gridSize;
for (int x = 0; x < gridSize - 1; x++, v++)
{
t = SetQuad(triangles, t, v, v + 1, v + ring - 1, v + ring);
}
t = SetQuad(triangles, t, v, v + 1, v + ring - 1, v + 2);
int vMin = ring * (gridSize + 1) - 1;
int vMid = vMin + 1;
int vMax = v + 2;
for (int z = 1; z < gridSize - 1; z++, vMin--, vMid++, vMax++)
{
t = SetQuad(triangles, t, vMin, vMid, vMin - 1, vMid + gridSize - 1);
for (int x = 1; x < gridSize - 1; x++, vMid++)
{
t = SetQuad(
triangles, t,
vMid, vMid + 1, vMid + gridSize - 1, vMid + gridSize);
}
t = SetQuad(triangles, t, vMid, vMax, vMid + gridSize - 1, vMax + 1);
}
int vTop = vMin - 2;
t = SetQuad(triangles, t, vMin, vMid, vMin - 1, vMin - 2);
for (int x = 1; x < gridSize - 1; x++, vTop--, vMid++)
{
t = SetQuad(triangles, t, vMid, vMid + 1, vTop, vTop - 1);
}
t = SetQuad(triangles, t, vMid, vTop - 2, vTop, vTop - 1);
return t;
}
private int CreatebottomFace (int[] triangles, int t, int ring)
{
int v = 1;
int vMid = vertices.Length - (gridSize - 1) * (gridSize - 1);
t = SetQuad(triangles, t, ring - 1, vMid, 0, 1);
for (int x = 1; x < gridSize - 1; x++, v++, vMid++)
{
t = SetQuad(triangles, t, vMid, vMid + 1, v, v + 1);
}
t = SetQuad(triangles, t, vMid, v + 2, v, v + 1);
int vMin = ring - 2;
vMid -= gridSize - 2;
int vMax = v + 2;
for (int z = 1; z < gridSize - 1; z++, vMin--, vMid++, vMax++)
{
t = SetQuad(triangles, t, vMin, vMid + gridSize - 1, vMin + 1, vMid);
for (int x = 1; x < gridSize - 1; x++, vMid++)
{
t = SetQuad(
triangles, t,
vMid + gridSize - 1, vMid + gridSize, vMid, vMid + 1);
}
t = SetQuad(triangles, t, vMid + gridSize - 1, vMax + 1, vMid, vMax);
}
int vTop = vMin - 1;
t = SetQuad(triangles, t, vTop + 1, vTop, vTop + 2, vMid);
for (int x = 1; x < gridSize - 1; x++, vTop--, vMid++)
{
t = SetQuad(triangles, t, vTop, vTop - 1, vMid, vMid + 1);
}
t = SetQuad(triangles, t, vTop, vTop - 1, vMid, vTop - 2);
return t;
}
private void OnDrawGizmos()
{
if (vertices == null)
{
Debug.Log("Returning");
return;
}
for (int i = 0; i < vertices.Length; i++)
{
Gizmos.color = Color.black;
//Gizmos.DrawSphere(vertices[i], 0.1f);
Gizmos.color = Color.yellow;
//Gizmos.DrawRay(vertices[i], normals[i]);
}
}
private void CreateColliders()
{
gameObject.AddComponent<SphereCollider>();
}
private static int SetQuad(int[] triangles, int i, int v00, int v10, int v01, int v11)
{
triangles[i] = v00;
triangles[i + 1] = triangles[i + 4] = v01;
triangles[i + 2] = triangles[i + 3] = v10;
triangles[i + 5] = v11;
return i + 6;
}
}
现在,你们中的一些人可能会看到偏移、阻尼和比例等数字并不完全正确。我一直在编辑它们,试图让顶点加速它们的移动。然而,由于弹簧的影响,当它们移动得太快以使其变形更快以匹配球滚动时的实际接触点时,它会导致它在整个地方反弹。阻尼最初是为了阻止这种情况,但它会再次减慢它的速度。
另一个问题是,如果球静止不动,即使它与地面接触,变形也会恢复正常。我考虑添加一个检查现有联系人的语句,但担心区分对象以维护表单会导致复杂性增加,最终无论如何都不起作用。
最后,实际的对撞机不会随着变形而改变。由于顶点的方向在某种程度上基于对撞机的位置,我不确定如何解决这个问题。因此,进退两难,我不确定是否值得完全放弃这种技术,或者我是否可以采取解决方案来解决这个问题。
解决方案
我继续寻找替代方案,我真正看到的唯一一个效果很好的是来自 Bullet Physics 下的资产商店的那个。它包括用于软体球体的脚本,并且可能适用于我的情况。我将对其进行修改以尝试使其正常工作,但这里是被带到此页面的任何人的链接。
https://assetstore.unity.com/packages/tools/physics/bullet-physics-for-unity-62991
我会在接下来的几天里继续检查,以防有人有更好的解决方案。
推荐阅读
- maven - mvn 命令无法识别 JAVA
- visual-studio-debugging - 尽管启用了本机代码调试,但仍无法从托管调试本机代码
- angular - 更改@input 值后,Angular Material 模态不会在冷组件上触发 ngChanges
- c# - 如何使用 system.refelection 设置值
- javascript - 执行 HTTP POST 请求时指定套接字发送缓冲区大小
- google-cloud-platform - 在 GCP 上重置 DHCP IP
- c++ - SFML:显示 2 个矩形的问题
- fortran - 识别输入文件 Fortran 中的空格
- tesseract - 正方体使训练缺少训练的基本事实
- html - 冲突组件角度测试