2015-05-29 3 views
1

Я делаю 3D-игру Java с библиотекой LWJGL, и мне было интересно, как добавить обнаружение столкновений, чтобы игрок не просматривал модели.Java 3D LWJGL collision

Я использую модели OBJ. Вот класс OBJLoader, который загружает модели:

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; 
    } 

} 

Спасибо!

ответ

2

У вас есть несколько вариантов здесь. Проще всего просто создать ориентированный по оси ограничивающий прямоугольник (AABB) вокруг объекта; это можно сделать алгоритмически, просто найдя минимальное и максимальное значения для каждой оси. Для некоторых приложений это будет работать нормально, но это, очевидно, не очень точно. Стоит отметить, однако, что если два AABB не пересекаются, то сами объекты определенно не пересекаются; вы можете использовать этот факт как ранний выход для вашего алгоритма проверки столкновений.

В дополнение к ограничивающим коробкам, в некоторых игровых двигателях используются другие основные типы ограничивающих объемов, например, ограничивающие сферы. Обнаружение, если точка находится в сфере, так же просто, как проверка того, что расстояние между центром сферы и точкой меньше или равно радиусу сферы. Исследуйте bounding volume Wikipedia page for other types. Вы можете часто приближать истинные границы вашего объекта, создавая сложные ограничивающие тома, то есть ограничивающие объемы, состоящие из нескольких более простых томов, таких как сферы, коробки, цилиндры и т. Д. Так игровой движок Unity обрабатывает обнаружение столкновений.

Возможно, вам также понадобится изучить алгоритмы обнаружения двумерных столкновений, например, пример теоремы о разделительной оси (см. Раздел дополнительной информации). Эти алгоритмы можно почти всегда масштабировать до более высоких измерений.

Если все это кажется слишком сложным, я бы рекомендовал собрать предварительно обработанное решение, такое как Bullet (Java port). Google немного; вы, вероятно, найдете что-то, что подходит вашему прецеденту. Не чувствуйте себя плохо, если вы чувствуете себя подавленными; обнаружение столкновений - сложный вопрос, подкрепленный десятилетиями исследований.

Дальнейшее чтение:

+0

Большое спасибо! Я делал вещи, связанные с ограничивающей коробкой, и для игры, которую я делаю, это может сработать. Это довольно просто, и я не собираюсь добавлять очень сложные модели. –

+0

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

2

Я также в процессе создания игры через 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, с которым я когда-либо тестировал его. Надеюсь это поможет.

+0

Получение позиции объекта не будет работать, так как это будет одинаково независимо от размера модели (по крайней мере, в моем текущем коде). Это будет работать с небольшими моделями, но не большими, где центр объекта находится дальше, чем сторона объекта, ближайшего к игроку. –

+0

Я динамически определяю самую дальнюю точку от центра объекта, чтобы установить его как радиус для проверки. Мы используем очень похожие OBJ-погрузчики, поэтому я знаю, что это сработает для вас. Это мой точный исходный код для моей системы обнаружения столкновений. [Ссылка] (http://pastebin.com/Na51XHc6). Кроме того, обязательно учитывайте масштаб объекта, иначе он будет выполнен неправильно. Надеюсь это поможет! –