Какие методы я могу использовать для физики мягкого тела на шаре - PullRequest
0 голосов
/ 19 июня 2019

Я делаю игру в Unity с резиновым шариком для одного из объектов. Я просмотрел различные доступные материалы по физике мягких тел, и ни один из них не выглядел особенно, что я ищу. Я хочу запрограммировать мяч, который будет подпрыгивать, и растягиваться, когда он падает на землю. Это будет центральным моментом в моей игре, и я хочу, чтобы она выглядела настолько хорошо, насколько я могу ее получить, однако все решения, которые я нахожу в Интернете, представляют собой небольшие исправления или плохо написаны. Я также хотел бы изучить решение, чтобы я мог адаптироваться и учиться на нем в будущем, я не хочу просто покупать актив и бросать его в игру.

Я впервые обратился к World of Zero и их видео деформации сетки. Однако они казались исключительно сложными для моих нужд, и я был привлечен к его видео 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;
}


}

Теперь, как некоторые из вас могут видеть числа со смещением, демпфированием и шкалой, они не совсем корректны. Я редактировал их, пытаясь заставить вершины ускорить их движение. Однако из-за влияния пружины, которое происходит, когда они движутся слишком быстро, чтобы заставить его деформироваться быстрее, чтобы соответствовать фактической точке контакта, когда шарик катится, это заставляет его подпрыгивать повсюду. Изначально демпфирование должно было остановить это, однако оно делает это до такой степени, что снова замедляет его.

Другая проблема заключается в том, что если мяч остановился, деформация возвращается к нормальной, даже если он находится в контакте с землей.Я подумал о добавлении проверки операторов для существующего контакта, но беспокоился о том, что различие между объектами для поддержания формы приведет к увеличению сложности, которая в конечном итоге не будет работать.

Наконец, фактический коллайдер не изменяется с деформацией.Поскольку направления вершин в некоторой степени основаны на местоположении коллайдера, я не уверен, как решить эту проблему.Поэтому из-за дилеммы я не уверен, стоит ли полностью отказываться от этой техники или есть решение, которое я мог бы принять, чтобы приблизиться к этому.

1 Ответ

0 голосов
/ 26 июня 2019

Я продолжал искать альтернативы, и единственная, что я действительно видел, которая работает хорошо, это одна из магазина активов в Bullet Physics.Он включает в себя сценарии для сфер мягких тел и может работать для моего случая.Я буду изменять его, чтобы попытаться заставить его работать должным образом, но вот ссылка для всех, кто попадает на эту страницу.

https://assetstore.unity.com/packages/tools/physics/bullet-physics-for-unity-62991

Снимок экрана

Я буду проверять в течение следующих нескольких дней, если у кого-то будет лучшее решение.

...