2D игра вектор движения C#

Я новичок в разработке игр, и я застрял в проблеме.

Я хотел бы знать новую позицию игрока каждую секунду, вот пример:

Игрок начинает с (2,5;2,5) и переходит к (6,5;3,8).вектор движения игрока

Его скорость составляет, например, 2 единицы в секунду, и я хотел бы узнать позицию игрока через 1 секунду. Так что-то вроде этого:

То, что я хотел бы знать, это А каждую секунду, но я совсем не знаю, как я могу это сделать... Я надеюсь, что вы сможете мне помочь, заранее спасибо!

5 ответов

Решение

Его скорость составляет, например, 2 единицы в секунду.

Я предполагаю, что "единица" означает "вектор длины = 1".

Прежде всего, вам нужно рассчитать вектор AB (вектор движения):

mov_vec = [xb-xa, yb-ya] = [6.5 - 2.5, 3.8 - 2.5] = [4, 1.3]

Итак, мы знаем, что общая единица совершила движение на [4, 1.3]. Нам нужно нормализовать этот вектор. Нормализованный вектор (единичный вектор) 'norm_mov_vec' будет сонаправлен с 'mov_vec', но его длина будет равна 1. Посмотрите эту ссылку, если вы хотите узнать больше о единичных единичных векторах.

Вычислить длину вектора движения:

mov_vec_len = sqrt( 4^2 + 1.3^2 ) ~= 4.2059

Вычислить нормализованный вектор:

norm_mov_vec = [4/4.2059, 1.3/4.2059] ~= [0.9510, 0.3090]

И это все. "norm_mov_vec" - это ваш "вектор движения-единицы", поэтому, если игрок движется в этом направлении со скоростью N единиц в секунду, вы можете очень легко вычислить его позицию через T секунд:

pos_after_T_sec_with_speed_N_units_per_sec = start_pos + (N * T * norm_mov_vec)

РЕДАКТИРОВАТЬ: Пример кода, используя тип Vector2 из XNA. Не могу проверить это, но я надеюсь, что вы поймете идею:

//In your case:
//start_pos = 'A' point
//end_pos = 'B' point
//time = number of seconds that elapsed
//speed = number of units per second
Vector2 calculatePosition(ref Vector2 start_pos, ref Vector2 end_pos, Uint32 time, Uint32 speed)
{
    Vector2 mov_vec = Vector2.Substract(end_pos, start_pos);

    Vector2 norm_mov_vec = Vector2.Normalize(mov_vec);

    Vector2 delta_vec = norm_mov_vec * time * speed;

    return Vector2.Add(start_pos, delta_vec);
}

Сначала вам нужно определить общее пройденное расстояние, это ваш вектор. Вектор - это движение, а не две точки в пространстве.

Затем вы просто делите каждое измерение, в данном случае x и y, на время, необходимое для перемещения в единицах измерения (в секундах), чтобы получить расстояние в секунду.

Затем вы умножаете каждые x и y на количество секунд от 0, т. Е. 1 секунду в вашем примере, чтобы получить позицию через 1 секунду.

Я не знаю, что вам доступно в ваших рамках или библиотеках, но хороший Vector класс будет очень полезен, вы захотите иметь возможность выполнять математику непосредственно на экземпляре вектора, например:

Point origin = sprite.Position; // Assumes some sprite object with a position.
Point dest = new Point(200,344); // Destination.

Vector totalTranslation = new Vector(dest.X - origin.X, dest.Y - origin.Y);
Vector perSecond = totalTranslation / 60; // assuming takes a minute to move.
Vector distanceMoved = perSecond * 4; // distance moved after 4 seconds.

Point newPosition = new Point(origin.X + distanceMoved.X, origin.Y + distanceMoved.Y);

sprite.Position = newPosition; // Or using some orchestration class...
spriteManager.Move(sprite, newPosition); // ...like this.

Обратите внимание на возможность делить вектор напрямую. В противном случае вам нужно разделить каждое пространственное измерение вектора и создать новый вектор, или создать вспомогательный класс для этого.

В реальной жизни вы можете рассчитывать на миллисекунды. Я бы не использовал фиксированный счетчик кадров, так как он мог бы выглядеть непредсказуемо, но работал бы все на основе таймера.

Как я уже сказал, ключевой здесь является хорошая библиотека или неизменяемая структура / класс Vector. Тогда стоит подумать о проблеме на миллиметровке.

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

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

Это не программирование, а в основном векторная математика, но в любом случае:

Ваш игрок движется по вектору BA (точка B минус точка A), который

Direction Vector: ( 4.0 / 1.3 )

Этот вектор имеет длину:

SquareRoot(4.0 * 4.0 + 1.3 * 1.3) = 4.2

Таким образом, вектор с одинаковым направлением и длиной в одну единицу будет вектором с обоими компонентами, разделенными на длину 4,2:

Direction Vector of length 1: (0.95 / 0.30)

Поскольку ваш игрок быстр и передвигается на две единицы, он будет иметь двойную длину:

Direction Vector of length 2: (1.90 / 0.60)

Теперь каждый тик, добавляйте 1.90 и 0.60 соответственно к координатам вашего игрока, пока они не будут равны (примерно) координатам цели.

Смещение по оси x: 6,5-2,5 = 4

смещение по оси у: 3,8-2,5 = 1,3

Math.sqrt ((4n) (4n) + (1,3n) (1,3n)) = 2

п = 2 / Math.sqrt (17.69)

смещение по оси X / секунда = 4n = 8 / Math.sqrt (17,69) = 1,90207

смещение по оси Y / секунда = 1,3n = 2,6 / Math.sqrt (17,69) = 0,61817

поэтому после получения этих значений очень легко вычислить позицию каждую секунду

Вы можете использовать (в качестве общего решения) эти простые формулы тригонометрии:

  x = A.x + v * cos(fi) * t;
  y = B.y + v * sin(fi) * t;
  fi = atan2(B.y - A.y, B.x - A.x);

образец раствора

// Since there's no common 2d Point double based type,
// let (x, y) point be represented as Tuple<Double, Double>
// where Item1 is x, and Item2 is y
public static Tuple<Double, Double> Move(Tuple<Double, Double> fromPoint,
                                         Tuple<Double, Double> toPoint,
                                         Double velocity,
                                         Double time) {
  Double fi = Math.Atan2(toPoint.Item2 - fromPoint.Item2, toPoint.Item1 - fromPoint.Item1);

  return new Tuple<Double, Double>(
    fromPoint.Item1 + velocity * Math.Cos(fi) * time,
    fromPoint.Item2 + velocity * Math.Sin(fi) * time);
}

...

for (int t = 0; t < 10; ++t) {
  Tuple<Double, Double> position = 
    Move(new Tuple<Double, Double>(2.5, 2.5), 
         new Tuple<Double, Double>(6.5, 3.8), 
         2.0, 
         t);

  Console.Write("t = ");
  Console.Write(t);
  Console.Write(" x = ");
  Console.Write(position.Item1);
  Console.Write(" y = ");
  Console.Write(position.Item2);
  Console.WriteLine();
}
Другие вопросы по тегам