Java 3D LWJGL 碰撞

Java 3D LWJGL collision

我正在使用 LWJGL 库制作 3D Java 游戏,我想知道如何添加碰撞检测,这样玩家就不会通过模型。

我正在使用 OBJ 模型。 这是加载模型的 OBJLoader class:

package renderEngine;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import models.RawModel;

import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

public class OBJLoader {


    public static RawModel loadObjModel(String fileName, Loader loader){
        FileReader fr = null;
        try {
            fr = new FileReader(new File("res/"+fileName+".obj"));
        } catch (FileNotFoundException e) {
            System.err.println("Couldn't load file!");
            e.printStackTrace();
        }
        BufferedReader reader = new BufferedReader(fr);
        String line;
        List<Vector3f> vertices = new ArrayList<Vector3f>();
        List<Vector2f> textures = new ArrayList<Vector2f>();
        List<Vector3f> normals = new ArrayList<Vector3f>();
        List<Integer> indices = new ArrayList<Integer>();
        float[] verticesArray = null;
        float[] normalsArray = null;
        float[] textureArray = null;
        int[] indicesArray = null;
        try{
            while(true){
                line = reader.readLine();
                String[] currentLine = line.split(" ");
                if(line.startsWith("v ")){
                    Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]), 
                            Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
                    vertices.add(vertex);
                }else if(line.startsWith("vt ")){
                    Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]), 
                            Float.parseFloat(currentLine[2]));
                    textures.add(texture);
                }else if(line.startsWith("vn ")){
                    Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]), 
                            Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
                    normals.add(normal);
                }else if(line.startsWith("f ")){
                    textureArray = new float[vertices.size() * 2];
                    normalsArray = new float[vertices.size() * 3];
                    break;
                }
            }

            while(line != null){
                if(!line.startsWith("f ")){
                    line = reader.readLine();
                    continue;
                }
                String[] currentLine = line.split(" ");
                String[] vertex1 = currentLine[1].split("/");
                String[] vertex2 = currentLine[2].split("/");
                String[] vertex3 = currentLine[3].split("/");

                processVertex(vertex1, indices, textures, normals, textureArray, normalsArray);
                processVertex(vertex2, indices, textures, normals, textureArray, normalsArray);
                processVertex(vertex3, indices, textures, normals, textureArray, normalsArray);
                line = reader.readLine();
            }

            reader.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        verticesArray = new float[vertices.size()*3];
        indicesArray = new int[indices.size()];


        int vertexPointer = 0;
        for(Vector3f vertex:vertices){
            verticesArray[vertexPointer++] = vertex.x;
            verticesArray[vertexPointer++] = vertex.y;
            verticesArray[vertexPointer++] = vertex.z;
        }
        for(int i=0;i<indices.size(); i++){
            indicesArray[i] = indices.get(i);
        }
        return loader.loadToVAO(verticesArray, textureArray, normalsArray, indicesArray);
    }

    private static void processVertex(String[] vertexData, List<Integer> indices, 
            List<Vector2f> textures, 
            List<Vector3f> normals, float[] textureArray, float[] normalsArray){

        int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
        indices.add(currentVertexPointer);
        Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
        textureArray[currentVertexPointer*2] = currentTex.x;
        textureArray[currentVertexPointer*2+1] = 1 - currentTex.y;
        Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2])-1);
        normalsArray[currentVertexPointer*3] = currentNorm.x;
        normalsArray[currentVertexPointer*3+1] = currentNorm.y;
        normalsArray[currentVertexPointer*3+2] = currentNorm.z;
    }

}

谢谢!

这里有几个选项。最简单的方法是简单地在对象周围创建一个轴对齐的边界框 (AABB);这可以通过简单地找到每个轴的最小值和最大值来通过算法来完成。对于某些应用程序,这会很好地工作,但这显然不是很精确。不过值得注意的是,如果两个 AABB 不相交,那么对象本身肯定也不相交;您可以将此事实用作碰撞检查算法的提前退出。

除了边界框之外,一些游戏引擎还使用其他基本类型的边界体积,例如边界球。检测一个点是否在球体中就像检查球体中心和该点之间的距离是否小于或等于球体半径一样简单。调查bounding volume Wikipedia page for other types。您通常可以通过创建复合边界体积来近似对象的真实边界——也就是说,边界体积由几个更简单的体积组成,如球体、盒子、圆柱体等。这就是 Unity 游戏引擎处理碰撞检测的方式。

您可能还想研究 2D 碰撞检测算法,例如分离轴定理示例(请参阅进一步阅读部分)。这些算法几乎总是可以扩展到更高的维度。

如果这一切看起来太复杂,我建议您选择预烘焙的解决方案,例如 Bullet (Java port)。 Google绕了一下;您可能会找到适合您的用例的东西。如果您感到不知所措,请不要难过;碰撞检测是一个复杂的课题,经过数十年的研究。

进一步阅读:

我也在通过LWJGL制作游戏。我使用一个相当简单的过程来确定碰撞。首先,我找到了焦点实体一定距离内的所有实体。我使用这样的方法:

public static float getDistance(Vector3f pointOne, Vector3f pointTwo) {
    float distance = 0;

    float x1 = pointOne.x;
    float y1 = pointOne.y;
    float z1 = pointOne.z;

    float x2 = pointTwo.x;
    float y2 = pointTwo.y;
    float z2 = pointTwo.z;

    distance = (float) Math.pow((Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) + Math.pow(z1 - z2, 2)), .5f);

    return distance;
}

接下来,我调用该方法并确定哪些实体在焦点实体的特定半径内,并将它们添加到列表中。

List<Entity> possibleCollision;
if(distance < radius) {
    possibleCollision.add(entity);
}

最后,我确定了两个不同列表中每个顶点的坐标。一个列表用于存储焦点实体的坐标,另一个用于存储可能碰撞范围内的所有其他实体的坐标。

List<Vector3f> focal_vertices = new ArrayList<Vector3f>();
//Get Vertices for Focal Entity
focal_verticies.add(vertex);

List<Vector3f> entity_vertices = new ArrayList<Vector3f>();
//Get Vertices for Focal Entity
entity_vertices.add(vertex);

最后,我 运行 循环检查列表是否包含重复条目。这是系统最简单的部分。

for(int i = 0; i < entity_vertices.size() - 1; i++) {

    if(player_vertices.contains(entity_vertices.get(i))) {

        //Collision Detected

    }

}

这适用于我测试过的每个 OBJ 文件。希望这有帮助。