Найти тему
Making games is easy!

Программирование под Android. Движение персонажа согласно рельефу в 3D игре.

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

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

Я представлю вариант основанный на очень известном алгоритме Моллера — Трумбора (пересечения луча с треугольником).

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

Разобью задачу на две.

1) Найду треугольник над которым находится персонаж.

2) Найду высоту рельефа в месте где находится персонаж (в найденном треугольнике).

3D модели часто представлены именно из треугольников.

И наш рельеф, его вид сверху можно схематично представить так

Красный кружок наш человечек, он гуляет себе по этому рельефному полю.

1) И так первый пункт, выполню с помощью 2D алгоритма, проверки нахождения точки в треугольнике. Почему 2D, потому что если посмотреть на картинку представленную выше, высота нам и не нужна. Берем Х и Z (я использую координатную систему принятую в Opengl ES 2.0).

-2

Воспользуемся например следующим алгоритмом. Код как всегда представлен на Java.

private float sign (float[] p1, float[] p2, float[] p3)
{// Это вспомогательная функция к основной
return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
}

// Первая искомая точка, дальше 3 точки треугольника

// Как вы поняли под точками я здесь понимаю X и Z координаты

// Они записаны в массив float[]

// Например так, float[] pt = new float[]{ 0.0f, 0.15f }; // XZ

// XZ - положение нашего человечка
private boolean PointInTriangle (float[] pt, float[] v1, float[] v2, float[] v3)

{

float d1, d2, d3;

boolean has_neg, has_pos;

    d1 = sign(pt, v1, v2);

    d2 = sign(pt, v2, v3);

    d3 = sign(pt, v3, v1);

    has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);

    has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);

return !(has_neg && has_pos);

}

// Функция возвращает true если точка в треугольнике.

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

В результате мы получили треугольник над которым находится наш объект, если функция вернула true.

2) Теперь запущу Алгоритм Моллера — Трумбора (пересечение луча с треугольником), и найду высоту пересечения луча с данным треугольником. Понятное дело я добавил и Y высоту.

В алгоритм Моллера — Трумбора нужно передать следующие параметры.

1. Начальная точка луча (XYZ)

2. Вектор (именно вектор “отрезка”), вектор вычисляем как обычно в линейном уравнении

3. Собственно точки треугольника понятное дело их 3, и каждая (XYZ)

Выглядеть это может приблизительно так

float[] origin_ = {0.0f, 0.05f, 0.15f }; // Начальная точка

float[] direction_1_ = {0.0f, 0.05f+2.0f, 0.15f}; // Конечная точка

// Луч идет снизу вверх или наоборот как вам надо

// Поэтому X и Z одинаковы

/* Как вы поняли координаты X и Z, это место где находится наш человечек

Там понятно и находится наш луч

//*/

// Y надо подогнать так, что бы алгоритм работал для вашего рельефа

// Рассчитываем вектор

float[] direction_ = {direction_1_[0] - origin_[0], direction_1_[1] - origin_[1], direction_1_[2] - origin_[2]};

// Ну и запустим саму функцию (класс с функцией будет представлен ниже)

Intersection_1 mIntersection_1 = new Intersection_1();

boolean rez_peres_ = false;

// Здесь массив mUnevers_mass точки нашего треугольника

// Все три точки у меня записаны в один массив

if(mIntersection_1.Intersection_1_(origin_, direction_, new float[]{

mUnevers_mass[0],

mUnevers_mass[1],

mUnevers_mass[2]},

new float[]{

mUnevers_mass[3],

mUnevers_mass[4],

mUnevers_mass[5]},

new float[]{

mUnevers_mass[6],

mUnevers_mass[7],

mUnevers_mass[8]} )) 

{
// Пересекает
rez_peres_ = true;
}
else { /// Не пересекает

}

float distance_ = mIntersection_1.distance_();

// distance_ - это и есть наша корректировка которую надо добавить к

// высоте нашего игрового персонажа (3D человечка)

Ниже даю сам класс где содержится алгоритм Моллера — Трумбора .

Класс назван Intersection_1

package com.my.intersection;

import java.util.ArrayList;

public class Intersection_1 {//Möller–Trumbore intersection algorithm
private boolean intersected = false;
private float distance;
private float u;
private float v;

public static final float EPSILON = 0.000001f; // 0.0000001f; //

public static void cross(float[] dest,float[] v1,float[] v2){
        dest[0] = v1[1]*v2[2]-v1[2]*v2[1];
        dest[1] = v1[2]*v2[0]-v1[0]*v2[2];
        dest[2] = v1[0]*v2[1]-v1[1]*v2[0];
    }

public static float dot(float[] v1,float[] v2){
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
    }

public static void sub(float[] dest, float[] v1, float[] v2){
        dest[0] = v1[0] - v2[0];
        dest[1] = v1[1] - v2[1];
        dest[2] = v1[2] - v2[2];
    }

public float length(float x, float y, float z) {
return (float) Math.sqrt(x*x + y*y + z*z);
    }

public float[] normalize(float x, float y, float z) {
float len = length(x, y, z);
        x /= len;
        y /= len;
        z /= len;
float[] ghj = {x,y,z};
return ghj;
    }

public boolean Intersection_1_(float[] orig, float[] dir, float[] vert0 , float[] vert1 , float[] vert2 ){//

float t;
dir  = normalize(dir[0],dir[1],dir[2]); // Нормализуем вектор

float[] edge1 = new float[3];
float[] edge2 = new float[3];
float[] tvec = new float[3];
float[] pvec = new float[3];
float[] qvec = new float[3];
float det;
float inv_det;

//Find vectors for two edges sharing V1
        sub
(edge1, vert1, vert0);
sub(edge2, vert2, vert0);

// Вычисление вектора нормали к плоскости
        //Begin calculating determinant - also used to calculate u parameter
        cross
(pvec, dir, edge2); // cross - векторное произведение
        //if determinant is near zero, ray lies in plane of triangle or ray is parallel to //plane of triangle


det = dot(edge1, pvec);  // determinant of the matrix M

// dot - скалярное произведение

if(false) { // Альтернативная реализация

if(det < EPSILON)return false;
sub(tvec, orig, vert0);
u = dot(tvec, pvec);
if(u < 0 || u > det)return false;
cross(qvec,tvec,edge1);
v = dot(dir, qvec);
if(v < 0 || u + v > det)return false;
        t =
dot(edge2, qvec);
        inv_det = 1f / det;
        t *= inv_det;
u *= inv_det;
v *= inv_det;
//boolean rty = true;

} else {
        // Луч параллелен плоскости
        //если det близок к нулю, луч лежит в плоскости треугольника, иначе // нет
         // vector is parallel to the plane (the intersection is at infinity)
if (det > -EPSILON && det < EPSILON) return false;
        inv_det = 1.0f / det;
//if(inv_det>0){
        //inv_det = inv_det *(-1);
        //}


        //calculate distance from V1 to ray origin
        sub
(tvec, orig, vert0);
//Calculate u parameter and test bound
u = dot(tvec, pvec) * inv_det; // dot - скалярное произведение
        //The intersection lies outside of the triangle
if (u < 0 || u > 1) return false;
        //Calculate V parameter and test bound
        cross
(qvec, tvec, edge1);  // cross - векторное произведение
        //Prepare to test v parameter
v = dot(dir, qvec) * inv_det; // dot - скалярное произведение
        //The intersection lies outside of the triangle

        if (v < 0 || u + v > 1) return false;
t = dot(edge2, qvec) * inv_det;
    }
distance = t;
if(t > EPSILON) { //ray intersection
intersected = true;
return true;
        }
else { // No hit, no win
intersected = false;
return false;
        }
//return intersected;
}
public boolean intersected(){
return intersected;
    }
public float distance_(){
return distance;
    }

}

На этом все.

Вот некоторые из планируемых статей к выпуску:

1) Программирование игр под Android. Переводим 3D координаты вершин в 2D экрана.

2) Сортировка слоев в анимации 2D игр. Программирование под Android.

3) Программирование игр под Android. Реализация простейшего полета снаряда в 2D и 3D играх.

4) Программирование под Android. Всплывающие окна выбора (с кнопками).

5) Программирование игр под Android. Реализация касания 3D объектов.

6) Математические алгоритмы. 3D геометрия для игр (Java).

7) Android. Рисование на экране 2D.

Подписывайтесь на канал, не пропустите новые публикации. Если понравилась статья, не забудьте поставить палец вверх.