ฉันจะสร้างสนามแข่ง 3 มิติจากเส้นโค้งได้อย่างไร


9

ฉันต้องการสร้างลู่วิ่งแบบสามมิติรอบ ๆ เส้นโค้งที่อธิบายรูปร่างของมัน นี่คือตัวอย่างวิดีโอ

แทร็กควรเป็นอุโมงค์ที่ไม่มีที่สิ้นสุดที่กวาดไปตามเส้นโค้ง 3 มิติพร้อมกับอุปสรรคบางอย่างความคิดที่ดีที่สุดของฉันจนถึงตอนนี้ก็คือการใช้หลังคาทรงกลมตามแนวโค้ง

ฉันยังต้องการพอยน์เตอร์บางส่วนเกี่ยวกับวิธีจัดการเรขาคณิตเช่นวิธีสร้างจากการดำเนินการในห้องใต้หลังคาจัดการ 'วงจรชีวิต' ในหน่วยความจำการชนและการทำพื้นผิว


1
ดูมากเช่น proun: proun-game.com หากไม่มีปัญหาลิขสิทธิ์คุณอาจได้รับคำตอบที่ดีจากผู้เขียนเกมนี้ คุณสามารถได้รับข้อมูลของเขาที่นี่: blog.oogst3d.net
Vite Falcon

ใช่ฉันกำลังคิดเกี่ยวกับที่ - tnx!
Valentin Galea

คำตอบ:


5

ฉันไม่แน่ใจว่าคุณใช้ภาษาใด แต่มีตัวอย่างการบีบอัดตาข่ายสำหรับ Unity3D ที่นี่:

http://unity3d.com/support/resources/example-projects/procedural-examples

ฉันแน่ใจว่าคุณสามารถดูรหัสและทำใหม่สำหรับสถานการณ์ของคุณ

แก้ไข: ฉันกำลังทำงานกับเกมที่ใช้ระบบรถไฟแบบ extruded ขั้นตอนเช่นเดียวกับที่คุณกำลังเริ่มต้น แต่มันอยู่ใน C # ใน Unity3d ฉันจะให้ภาพรวมของวิธีที่ฉันสร้างการอัดขึ้นรูปรางของฉันตามเส้นทาง Cubic Bezier ดังนั้นแม้ว่าตาข่ายของรางจะถูกสร้างขึ้นตามขั้นตอน แต่ก็ขึ้นอยู่กับเส้นทาง Bezier ที่ฉันกำหนดไว้ล่วงหน้าในการแก้ไข มันจะเป็นเหมือนเครื่องมือแก้ไขเลเวลในกรณีของเกมของคุณในกรณีของฉันมันออกแบบตารางพินบอล รายการด้านล่างเป็นตัวอย่างของวิธีการที่ฉันทำ:

1. ) สร้าง / ค้นหาและใช้คลาสพา ธ Bezier สิ่งนี้จะทำให้คุณมีแหล่งข้อมูลสำหรับการอัดขึ้นรูปตาข่ายของคุณ มีหนึ่งใน C # ที่นี่ซึ่งคุณสามารถพอร์ตไปยัง c ++

http://forum.unity3d.com/threads/32954-Waypoints-and-constant-variable-speed-problems?p=213942

2. ) เมื่อคุณสร้างเส้นทาง Bezier แล้วจุดข้อมูลจากเส้นทางนี้จะถูกสุ่มตัวอย่าง ซึ่งสามารถทำได้ผ่านวิธีการ Interp ในชั้นเรียนที่ระบุไว้ข้างต้น นี่จะให้รายการ / อาเรย์ของจุด Vector3 ตามเส้นทาง Bezier

3. ) สร้างคลาสผู้ช่วยเพื่อแปลงข้อมูลพา ธ Vector3 Bezier จากขั้นตอนที่ 2 ในกรณีนี้ฉันมีคลาสแบบง่าย ๆ ที่เรียกว่า ExtrudedTrailSection ตามที่กำหนดไว้ด้านล่าง:

public class ExtrudedTrailSection
{
    public Vector3 point;
    public Matrix4x4 matrix;
    public float time;

    public ExtrudedTrailSection() { }
}

4. ) วนซ้ำตามข้อมูลตัวอย่าง Vector3 ของคุณและแปลงไปยังอาร์เรย์ของ ExtrudedTrailSections ที่ให้ข้อมูลตัวอย่างและเมทริกซ์ฐานที่จะเป็นตำแหน่งรูทของตาข่ายของคุณ

  1. ) ใช้อาร์เรย์ของ ExtrudedTrailSections เพื่อสร้างอาร์เรย์สุดท้ายของ Matrix4x4 [] โดยใช้รหัสต่อไปนี้:

Matrix4x4 worldToLocal = rootTransform.worldToLocalMatrix;

    for (int i = 0; i < trailSections.Count; i++)
    {
            if (i == 0)
            {
                direction = trailSections[0].point - trailSections[1].point;
                rotation = Quaternion.LookRotation(direction, Vector3.up);
                previousRotation = rotation;
                finalSections[i] = worldToLocal * Matrix4x4.TRS(position, rotation, Vector3.one);
            }
            // all elements get the direction by looking up the next section
            else if (i != trailSections.Count - 1)
            {
                direction = trailSections[i].point - trailSections[i + 1].point;
                rotation = Quaternion.LookRotation(direction, Vector3.up);

                // When the angle of the rotation compared to the last segment is too high
                // smooth the rotation a little bit. Optimally we would smooth the entire sections array.
                if (Quaternion.Angle(previousRotation, rotation) > 20)
                    rotation = Quaternion.Slerp(previousRotation, rotation, 0.5f);

                previousRotation = rotation;
                finalSections[i] = worldToLocal * Matrix4x4.TRS(trailSections[i].point, rotation, Vector3.one);
            }
            // except the last one, which just copies the previous one
            else
            {
                finalSections[i] = finalSections[i - 1];
            }
        }

6. ) ตอนนี้คุณมีอาร์เรย์ของ Matrix4x4 [] และสามารถขับไล่ตาข่าย แต่ก่อนอื่นเราต้องใช้ตาข่ายอ้างอิงเพื่อขับไล่จาก ฉันมีคลาสยูทิลิตี้ที่จะสร้างหน้าตาข่ายแบบวงกลมที่เราจะจัดหาให้กับวิธีการรีดตาข่าย

public static List<Vector2> CreateCircle (double radius, int sides)
{
    List<Vector2> vectors = new List<Vector2> ();

    const float max = 2.0f * Mathf.PI;
    float step = max / sides;

    for (float theta = 0.0f; theta < max; theta += step) {
        vectors.Add (new Vector2 ((float)(radius * Mathf.Cos (theta)), (float)(radius * Mathf.Sin (theta))));
    }


    return vectors;
}

7. ) ค้นหาศูนย์กลางของข้อมูลนี้:

    public static Vector2 CalculateCentroid(List<Vector2> vectorList)
    {
        //////////////////////////////////////////////////////////////////////////
        // Local variables.
        float fArea = 0.0f, fDistance = 0.0f;
        Vector2 vCenter = Vector2.zero;
        int nIndex = 0, nLastPointIndex = vectorList.Count - 1;
        //
        //////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////////
        // Run through the list of positions.
        for (int i = 0; i <= nLastPointIndex; ++i)
        {
            //////////////////////////////////////////////////////////////////////////
            // Cacluate index.
            nIndex = (i + 1) % (nLastPointIndex + 1);

            // Calculate distance.
            fDistance = vectorList[i].x * vectorList[nIndex].y - vectorList[nIndex].x * vectorList[i].y;

            // Acculmate area.
            fArea += fDistance;

            // Move center positions based on positions and distance.
            vCenter.x += (vectorList[i].x + vectorList[nIndex].x) * fDistance;
            vCenter.y += (vectorList[i].y + vectorList[nIndex].y) * fDistance;
        }
        //
        //////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////////
        // Calculate the final center position.
        fArea *= 0.5f;
        vCenter.x *= 1.0f / (6.0f * fArea);
        vCenter.y *= 1.0f / (6.0f * fArea);
        //
        //////////////////////////////////////////////////////////////////////////

        return vCenter;
    }

8. ) ตอนนี้เรามีข้อมูลขอบและศูนย์สำหรับตาข่ายหน้าเรเดียลคุณสามารถสร้างวัตถุตาข่ายโดยใช้ข้อมูลของคุณ จุดสุดยอดสุดท้ายในตาข่ายคือจุดศูนย์กลางที่เราคำนวณ ตาข่ายสุดท้ายเป็นเพียงใบหน้าที่มอบให้กับวิธีการอัดรีดแบบตาข่ายที่ฉันให้ตัวอย่างไว้ในคลาสการบีบอัดตาข่ายแบบขั้นตอนของแพ็คเกจ Unity นี่เป็นวิธีการของฉันและแน่นอนว่าคุณต้องป้อนข้อมูลนี้ใน OpenGL หากคุณมีไลบรารียูทิลิตี้ 3 มิติที่คุณกำลังใช้หรือสามารถเขียนคลาส mesh ของคุณเองมันน่าจะดีกว่าที่จะสร้าง mesh extruded สุดท้ายเพราะ opengl ไม่จำเป็นต้องใช้ข้อมูลนี้ในการแสดงผล ตาข่ายหน้านี้ใช้เป็นข้อมูลอ้างอิงสำหรับการอัดขึ้นรูปตาข่ายเท่านั้น

    List<Vector3> levelVerts = new List<Vector3>();
    List<Vector2> levelUVBary = new List<Vector2>();
    List<Vector2> levelUVs = new List<Vector2>();
    List<int> levelTris = new List<int>();

    int verticesPerNode = 4;
    int edgeCount = sourceMeshData.Count;

    List<Vector3> sourceVerts = new List<Vector3>();
    //Debug.Log("smd.c:" + sourceMeshData.Count);
    for (int i = 0; i < edgeCount; i++)
    {
        //Debug.Log("adding:"+levelShapeData[i].x+"/"+levelShapeData[i].y);
        sourceVerts.Add(new Vector3(sourceMeshData[i].x, sourceMeshData[i].y, 0));
        levelUVs.Add(new Vector2(0, 0));
        //sourceVerts.Add(new Vector3(levelShapeData[i].x, levelShapeData[i].y, modelLength / 2f));
    }

    sourceVerts.Add(new Vector3(sourceMeshCenter.x, sourceMeshCenter.y, 0));
    levelUVs.Add(new Vector2(0, 0));

    for (int i = 0; i < edgeCount - 1; i++)
    {                                       //0, 1, 2, 3
        levelTris.Add(sourceVerts.Count - 1); //4, 4, 4, 4 
        levelTris.Add(i);                   //0, 1, 2, 
        levelTris.Add(i + 1);               //1, 2, 3,
    }

    levelTris.Add(sourceVerts.Count - 1);
    levelTris.Add(edgeCount - 1);
    levelTris.Add(0);

9. ) ค้นหาขอบด้านนอกของตาข่ายวงกลมตามที่ต้องการโดยวิธีการรีดตาข่าย อีกครั้งรหัสนี้มีให้ในแพ็คเกจสามัคคี

public class Edge
{
    // The indiex to each vertex
    public int[]  vertexIndex = new int[2];
    // The index into the face.
    // (faceindex[0] == faceindex[1] means the edge connects to only one triangle)
    public int[]  faceIndex = new int[2];
}

public static Edge[] BuildManifoldEdges (Mesh mesh)
{
    // Build a edge list for all unique edges in the mesh
    Edge[] edges = BuildEdges(mesh.vertexCount, mesh.triangles);

    // We only want edges that connect to a single triangle
    ArrayList culledEdges = new ArrayList();
    foreach (Edge edge in edges)
    {
        if (edge.faceIndex[0] == edge.faceIndex[1])
        {
            culledEdges.Add(edge);
        }
    }

    return culledEdges.ToArray(typeof(Edge)) as Edge[];
}

10. ) ดึงข้อมูลทั้งหมดนี้เข้าสู่วิธีการอัดขึ้นรูปตาข่าย ..

public static void ExtrudeMesh (Mesh srcMesh, Mesh extrudedMesh, Matrix4x4[] extrusion, Edge[] edges, bool invertFaces)
{
    int extrudedVertexCount = edges.Length * 2 * extrusion.Length;
    int triIndicesPerStep = edges.Length * 6;
    int extrudedTriIndexCount = triIndicesPerStep * (extrusion.Length -1);

    Vector3[] inputVertices = srcMesh.vertices;
    Vector2[] inputUV = srcMesh.uv;
    int[] inputTriangles = srcMesh.triangles;

    //Debug.Log("inputUV:" + inputUV.Length);

    Vector3[] vertices = new Vector3[extrudedVertexCount + srcMesh.vertexCount * 2];
    Vector2[] uvs = new Vector2[vertices.Length];
    int[] triangles = new int[extrudedTriIndexCount + inputTriangles.Length * 2];

    // Build extruded vertices
    int v = 0;
    for (int i=0;i<extrusion.Length;i++)
    {
        Matrix4x4 matrix = extrusion[i];
        float vcoord = (float)i / (extrusion.Length -1);
        foreach (Edge e in edges)
        {
            //Debug.Log(e.vertexIndex.Length);
            vertices[v+0] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[0]]);
            vertices[v+1] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[1]]);

            uvs[v+0] = new Vector2 (inputUV[e.vertexIndex[0]].x, vcoord);
            uvs[v+1] = new Vector2 (inputUV[e.vertexIndex[1]].x, vcoord);

            v += 2;
        }
    }       

    // Build cap vertices
    // * The bottom mesh we scale along it's negative extrusion direction. This way extruding a half sphere results in a capsule.
    for (int c=0;c<2;c++)
    {
        Matrix4x4 matrix = extrusion[c == 0 ? 0 : extrusion.Length-1];
        int firstCapVertex = c == 0 ? extrudedVertexCount : extrudedVertexCount + inputVertices.Length;
        for (int i=0;i<inputVertices.Length;i++)
        {
            vertices[firstCapVertex + i] = matrix.MultiplyPoint(inputVertices[i]);
            uvs[firstCapVertex + i] = inputUV[i];
        }
    }

    // Build extruded triangles
    for (int i=0;i<extrusion.Length-1;i++)
    {
        int baseVertexIndex = (edges.Length * 2) * i;
        int nextVertexIndex = (edges.Length * 2) * (i+1);
        for (int e=0;e<edges.Length;e++)
        {
            int triIndex = i * triIndicesPerStep + e * 6;

            triangles[triIndex + 0] = baseVertexIndex + e * 2;
            triangles[triIndex + 1] = nextVertexIndex  + e * 2;
            triangles[triIndex + 2] = baseVertexIndex + e * 2 + 1;
            triangles[triIndex + 3] = nextVertexIndex + e * 2;
            triangles[triIndex + 4] = nextVertexIndex + e * 2 + 1;
            triangles[triIndex + 5] = baseVertexIndex  + e * 2 + 1;
        }
    }

    // build cap triangles
    int triCount = inputTriangles.Length / 3;
    // Top
    {
        int firstCapVertex = extrudedVertexCount;
        int firstCapTriIndex = extrudedTriIndexCount;
        for (int i=0;i<triCount;i++)
        {
            triangles[i*3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex;
        }
    }

    // Bottom
    {
        int firstCapVertex = extrudedVertexCount + inputVertices.Length;
        int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length;
        for (int i=0;i<triCount;i++)
        {
            triangles[i*3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex;
        }
    }

    if (invertFaces)
    {
        for (int i=0;i<triangles.Length/3;i++)
        {
            int temp = triangles[i*3 + 0];
            triangles[i*3 + 0] = triangles[i*3 + 1];
            triangles[i*3 + 1] = temp;
        }
    }

    extrudedMesh.vertices = vertices;
    extrudedMesh.uv = uvs;
    extrudedMesh.triangles = triangles;
}

ผลลัพธ์สุดท้ายในกรณีของฉันมีลักษณะเช่นนี้ ..

ป้อนคำอธิบายรูปภาพที่นี่

โชคดีเกมของคุณดูเท่จริงๆ! แจ้งให้เราทราบหากคุณคิดออก?

เชย


ฉันสนใจ OpenGL และ C ++ เป็นหลัก แต่ 'pseudocode' น่าจะช่วยได้เช่นกัน :)
Valentin Galea

ฉันได้อัปเดตโพสต์ดั้งเดิมของฉันเพื่อรวมโซลูชันที่ฉันเพิ่งทำเสร็จ
Chuck D
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.