Jump to content

Как настроить положение и скорость NPC движущемуся по сплайну


Recommended Posts

Приветствую, уважаемые разработчики!

Первый вопрос наглядно продемонстрирован на скриншоте. Уже несколько дней пытался выправить НПС, но знаний моих недостаточно( не получается.. Есть ли возможность выравнивать персонажей строго вертикально, а не к нормали сплайна, как сейчас?

spline_0.thumb.jpg.e0e29b2ff60dc46adeebb3d5aa0d972a.jpg

Ну и второй вопрос касательно скорости перемещения нпс.  Сейчас, если поинты расположены на разном удалении, то скорость нпс будет так же разной, т.е. если поинты близко, нпс будет идти медленно этот участок и наоборот если далеко, то пробежит его очень быстро, так как скорость перемещения между поинтами одна... Как это можно победить, а то размещать точки сплайна на одинаковом расстоянии нереально трудно... И да, было бы отлично, если бы существовала возможность устанавливать поинты на поверхность  инструментом  Drop To Ground, а то крайне неудобно их размещать. 

Вот столько вопросов накопилось. Спасибо за любую помощь и совет!)

Link to comment

Здравствуйте.

Чтобы вращать персонажа при движении только по вертикальной оси, от графа достаточно получить только направление движения. После спроецировать его на плоскость XY и применить к ноде. Вот небольшой пример:

vec3 forwardDirection = graph.GetWorldRotation() * segment.CalcTangent(time);
forwardDirection = MathLib.ProjectOntoPlane(forwardDirection, vec3.UP);
forwardDirection.Normalize();

node.SetWorldDirection(forwardDirection, vec3.UP, MathLib.AXIS.Y);

Тут нужно будет отдельно обработать случай, когда направление вперёд совпадёт с вектором вверх.

С равномерным движением всё немного сложнее. Чтобы такого добиться нужно пройтись по всему сплайну с одинаковым шагом, запоминая на каждой итерации в каком сегменте мы оказались, а также в каком именно времени сегмента. Для упрощения можно взять скорость 1 м/c. Тогда длина сегмента будет равна времени движения по нему. Выглядит это следующим образом:

private struct UniformInfo
{
	public int segmentIndex;
	public float segmentTime;
}
private List<float> uniformTimes = new List<float>();
private List<UniformInfo> uniformInfos = new List<UniformInfo>();

//...
totalTime = 0.0f;
foreach (var segment in segments)
  totalTime += segment.Length;
currentTime = 0.0f;

uniformTimes.Add(0.0f);
uniformInfos.Add(new UniformInfo { segmentIndex = 0, segmentTime = 0.0f });

const float step = 1.0f / 10.0f;
for (float time = step; time < totalTime - step; time += step)
{
  GetUniformInfo(time, out UniformInfo info);
  uniformTimes.Add(time);
  uniformInfos.Add(info);
};

uniformTimes.Add(totalTime);
uniformInfos.Add(new UniformInfo { segmentIndex = segments.Count - 1, segmentTime = 1.0f });
//...
private void GetUniformInfo(float time, out UniformInfo info)
{
  info = new UniformInfo();

  float totalTime = 0.0f;
  for (int i = 0; i < segments.Count; i++)
  {
    if (time < totalTime + segments[i].Length)
    {
      info.segmentTime = segments[i].LinearToParametric((time - totalTime) / segments[i].Length);
      info.segmentIndex = i;
      break;
    }

    totalTime += segments[i].Length;
  }
}
//...

Далее используем эти данные для вычисления положения и направления в любое время от 0 до totalTime:

private void GetUniformPointAndTangent(float uniform_time, out vec3 position, out vec3 tangent)
{
	position = vec3.ZERO;
	tangent = vec3.RIGHT;

	int frame_0 = 0;
	int frame_1 = uniformTimes.Count - 1;

	while ((frame_1 - frame_0) > 1)
	{
		int frame = (frame_0 + frame_1) / 2;
		if (uniformTimes[frame] < uniform_time)
			frame_0 = frame;
		else
			frame_1 = frame;
	}

	float k = (uniform_time - uniformTimes[frame_0]) / (uniformTimes[frame_1] - uniformTimes[frame_0]);

	position = MathLib.Lerp(
		segments[uniformInfos[frame_0].segmentIndex].CalcPoint(uniformInfos[frame_0].segmentTime),
		segments[uniformInfos[frame_1].segmentIndex].CalcPoint(uniformInfos[frame_1].segmentTime),
		k
	);

	// TODO: you need to use interpolation via rotation
	tangent = MathLib.Lerp(
		segments[uniformInfos[frame_0].segmentIndex].CalcTangent(uniformInfos[frame_0].segmentTime),
		segments[uniformInfos[frame_1].segmentIndex].CalcTangent(uniformInfos[frame_1].segmentTime),
		k
	);
	tangent.Normalize();
}

Во вложении есть пример такого движения.

spline_movement_test.upackage

  • Like 2
Link to comment

karpych11, великолепно!

Всё работает, код взял из примера. Правда пришлось dvec3 преобразовать в vec3 кое где...

Большое спасибо!

  • Thanks 1
Link to comment
×
×
  • Create New...