Движение мыши: щелкните по спрайту, чтобы перейти к точке, на которую нажали.

Я довольно новичок в C # и XNA и хорошо программирую (например, я могу следовать учебнику, но большая часть его создания самостоятельно все еще очень сложна). Прямо сейчас я хожу по кругу, пытаясь понять, как сделать эту «простую» вещь.

Вот идея, это будет игра в жанре Tower Defense; прямо сейчас я работаю над основами голой кости. У меня есть мой маленький парень-спрайт, который будет перемещаться с помощью ввода с клавиатуры, теперь я хочу щелкнуть где-нибудь на экране и заставить его «идти» к этой точке. Только я теряюсь в логике. Я могу щелкнуть, и он прыгнет туда с

if (aMouse.LeftButton == ButtonState.Pressed)
{
  Position.X = aMouse.X;
  Position.Y = aMouse.Y;
} 

Из того, что я прочитал для другого ввода мыши, я думаю, что мне понадобится какой-то цикл (может быть, логический?), который будет перемещать спрайт в направлении, и мне нужно будет запустить проверку, чтобы увидеть, добрался ли он до этого точка еще. Но получить эту точку после щелчка мыши и создать этот цикл, запустить проверку... Я ничего не понимаю.


person Steph    schedule 17.02.2011    source источник
comment
Вам нужно решить две проблемы. Люди отвечают на один из них. Другой — что делать, если на вашем пути есть препятствия. См. en.wikipedia.org/wiki/Pathfinding, как только вы дойдете до этого места.   -  person Merlyn Morgan-Graham    schedule 17.02.2011


Ответы (4)


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

Point2D targetPos;

И некоторые константы:

const Point2D speed;

Когда вы выполняете свой цикл Update(), обновляйте текущую позицию, добавляя к ней вектор скорости (конечно, в правильном направлении), пока вы не окажетесь в пределах предопределенного порога от целевой позиции (обычно пороги вычисляются из вектора скорости - если расстояние от текущей позиции до целевой позиции меньше длины вектора скорости, то вы находитесь в своей позиции). Использование bool в этом случае будет работать хорошо. Когда вы щелкаете мышью, установите другую переменную экземпляра (перемещение) в значение true, а как только вы достигнете своей целевой позиции, установите перемещение в значение false.

person João Lourenço    schedule 17.02.2011
comment
Благодарю за ваш ответ! Мне нужно узнать больше о Point2D и определении пороговых значений. - person Steph; 17.02.2011
comment
Без проблем. Я имел в виду логические пороги. Это просто проверка того, находится ли ваша текущая позиция в пределах определенного расстояния (порога) от вашей целевой позиции. - person João Lourenço; 17.02.2011

Люди отвечают на ваш вопрос на очень низком уровне. Иногда полезно подумать о проблеме на более высоком уровне.

Что вам нужно, так это тип управления состоянием. Причудливый термин в области компьютерных наук для этого — конечный автомат. Но не утруждайте себя поиском этого прямо сейчас. Поначалу это довольно сухо и запутанно :)

У вашего персонажа на данный момент одно состояние - "стоять рядом". Вам нужно добавить и обработать состояние «идти к месту назначения».

Из того, что я прочитал для другого ввода мыши, я думаю, мне понадобится какой-то цикл

Вам нужен один цикл, называемый игровым циклом. Если вы используете XNA, он у вас уже есть. Но ты на правильном пути.

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

В своем игровом цикле вы проверяете, произошел ли только что щелчок мышью. Если это так, то установите некоторые данные (куда двигаться) и скажите ему, чтобы он начал идти, установив его состояние на «идти к месту назначения». При следующем обновлении вы будете обрабатывать это состояние.

Когда ваш персонаж находится в состоянии «идти к месту назначения», вам необходимо обновить его положение в зависимости от количества времени, прошедшего с момента последнего обновления игры. В XNA это рассчитывается за вас. Если вы не используете XNA, вам придется проверить это самостоятельно. Возможно, вы сможете использовать что-то вроде класса Stopwatch и проверить поле Elapsed.

Если персонаж находится в пункте назначения, вам нужно переключить его обратно в состояние «стоять рядом».

Если вы получаете еще один щелчок мышью, вам решать, хотите ли вы, чтобы состояние «идти к месту назначения» обращало на это внимание или нет. Если вы обращаете на это внимание, вы устанавливаете тот же тип данных, что и при переходе из состояния «стоять рядом».

Итак, вам понадобятся следующие переменные:

  • Таймер, чтобы узнать время, прошедшее с момента последнего игрового цикла (XNA предоставляет его вам)
  • Текущее состояние игрока (возможно, enum)
  • Текущая позиция игрока (вектор)
  • Скорость ходьбы игрока (вероятно, поплавка), измеряемая в единицах в секунду (или миллисекундах).
  • Данные для состояния "идти к месту назначения" - целевое положение (другой вектор)
  • Данные, связанные с пользовательским вводом (события мыши, которые произошли с момента последнего обновления, позиция этих кликов и т. д.).

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

Я не буду рассматривать векторную математику для того, как на самом деле вычислить материал частичного движения, так как другие люди рассмотрели это. Нет смысла дублировать эти ответы. В основном это сводится к созданию вектора между вашей текущей позицией и целевой позицией и умножению/делению его на скорость ходьбы (чтобы сократить его до расстояния, пройденного за одно обновление).

person Merlyn Morgan-Graham    schedule 17.02.2011
comment
Благодарю за ваш ответ! Это действительно помогает мне думать о том, что мне нужно. Я думал сделать состояние с перечислением, но я не думал о том, чтобы использовать его для стояния или ходьбы, я думаю, что могу легко добавить что-то в этом роде, я играл с этим для чего-то еще. Я думаю, что я понимаю логику того, что должно произойти, синтаксис — это то, где я чувствую, что я действительно застрял. Знаете ли вы книгу или онлайн-учебник, который работает с перемещением спрайта по щелчку мыши? Если я смогу работать с другим примером, я действительно смогу разобраться с ним и адаптировать его к тому, что я хочу. - person Steph; 18.02.2011
comment
@Steph: Каждый учебник, который я видел по этому вопросу (несколько лет назад), либо предполагал много вещей, либо был слишком общим, либо был слишком сложным. Вот один, который я только что нашел в Google: create.msdn.com/en -US/education/catalog/sample/, хотя это может показаться сложным. Также из описания неясно, поддерживает ли он состояние игры низкого уровня (для каждого объекта) или только состояние высокого уровня (например, меню игры, окончание игры, пауза и т. д.). - person Merlyn Morgan-Graham; 18.02.2011

Я предполагаю, что у вас есть три вещи:

  1. Текущая позиция
  2. Желаемая должность
  3. Скорость перемещения каждого игрового тика // не знаю, что такое игровой тик? выяснить!

Вы смотрите на это

введите здесь описание изображения

// dx, dy are delta x and delta y. It's how far in each direction
// the player must travel
// note, I fixed a typo where they were desired - desired... should be
// desired - current, as they are now
float dx = desiredX - currentX;
float dy = desiredY - currentY;

// d uses the pythagorean theorum to find the distance between current and desired
float d = sqrt(dx*dx + dy*dy);
// fac is how far along that line between desired and current will you move
float fac = d / speed;

// mx is the component of the dx line proportional to the size of fac : d
// which means it's how far in the x direction you'll move
float mx = dx * fac;
float my = dy * fac;

// the new postition is the old position plus the move value
float newPositionX = dx + mx;
float newPositionY = dy + my;
person corsiKa    schedule 17.02.2011
comment
Спасибо за ваш ответ! У меня есть текущая позиция и скорость, я хочу, чтобы желаемая позиция исходила от пользовательского ввода с помощью мыши. Я не уверен, как получить это от мыши-пользователя-ввода. Я получаю изображение, но я не понимаю ваш код и математику здесь... (я действительно новичок во всем этом) В этом примере вы устанавливаете позицию disred, а затем переходите от текущей к желаемой с учетом скорости? Почему dx - желаемое x минус желаемое x? а что значит sqrt и fac? Извиняюсь! Вам не нужно объяснять все это, если вы не хотите! Я хотел бы понять, но я знаю, что я здесь не в своей тарелке. - person Steph; 17.02.2011
comment
P.S. Я сам нарисовал эту картину. Ага... Я отдаю разработку спрайтов на подряд! - person corsiKa; 17.02.2011
comment
Ах!! Понимаю. Большое спасибо, что объяснили это!! Я очень надеюсь на вашу помощь (и я прекрасно понимаю, что вы имеете в виду, когда говорите о рисовании спрайтов, мой парень сейчас занимается квадратными блоками :p). - person Steph; 18.02.2011
comment
Без проблем. Разработка игр — это очень весело, но в ней много математики. Я слежу за тегом game-development, так что не забудьте указать его, если будете задавать вопросы. И если это когда-нибудь станет играбельным, пришлите ссылку :} - person corsiKa; 18.02.2011
comment
ржу не могу. Сделаю! Такими темпами может пройти ОЧЕНЬ много времени, прежде чем это станет игровым мячом. У меня есть еще один вопрос по поводу этой математики, абсолютно ли необходимо вычислять расстояние? Я думаю, что либо нужно рассчитать расстояние, либо должен быть логический цикл, чтобы проверить, достигнуто ли желаемое (X, Y) ... Правильно ли я думаю? Я не фанат математики и всегда стараюсь избегать этого, но с такой формулой она будет установлена ​​​​один раз, а затем будет просто работать по мере необходимости ... и я думаю, что с точки зрения обработки это будет быстрее тогда логический цикл? - person Steph; 18.02.2011
comment
(извините, что продолжаю приставать к вам) Когда я пробил все это в sqrt, это не сработало. Я получаю, что имя «sqrt» не существует в текущем контексте, поэтому я попробовал Math.Sqrt и получил тип неявного преобразования Cannont «double to «float». и существует явное преобразование (вам не хватает приведения?). Я использую XNA 3.0 в MS Visual C# 2008. Я могу настроить преобразование, но думаю, что должен быть более простой способ... - person Steph; 18.02.2011
comment
Я пройдусь по ним по очереди. 1) оператор if для определения if(d < speed) будет определять, достигнет ли игрок точки. 2) sqrt вполне может взять дубль. В этом случае используйте двойные значения для ваших координат или приведите результат к типу с плавающей запятой, например float d = (float)sqrt((double)(dx*dx + dy*dy)); - person corsiKa; 18.02.2011
comment
Мне любопытно - не могли бы вы привести пример логического цикла? :-) Вы имеете в виду оператор if? Просто так мы на той же странице - person corsiKa; 18.02.2011

Я нашел этот код наиболее полезным... кроме того, я добавил пару дополнительных строк, чтобы предотвратить возникновение определенных ситуаций. Например, будут случаи, когда направление равно 0,83, а скорость может быть изменена игровыми факторами, такими как местность/погода/и т. д.... если скорость ниже 1, спрайт может вообще не двигаться или даже двигаться в неправильном направлении!

 if (Vector2.Distance(Position, TargetPosition) > 2.0f)
 {
    velocity = Vector2.Subtract(TargetPosition, Position);
    velocity.Normalize();
    /// Now no matter which direction we are going we are always moving @ sprite.Speed
    /// at velocity or speed below 1 - problems occur where the unit may not move at all!!
    if(current_Speed < 1)
    {
       Vector2 temp = (velocity * 10) * (current_Speed * 10);
       Position += temp / 10;
    }
    else
    { 
       Vector2 temp = velocity * current_Speed;
       Position += temp;
    }
    //convert to int to render sprite to pixel perfect..
    Position = new Vector2((int)Position.X, (int)Position.Y);
 }
person Bixel    schedule 17.12.2013