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