Используйте Список под названием «Путь» для хранения путевых точек, которые описывают ваш путь, и дважды связанный список под названием «Змея» для хранения движущихся объектов и Пути.
Ведущий объект определяет новые путевые точки во время движения. Следующие объекты движутся по пути, как определено этими путевыми точками.
Каждый объект имеет зону безопасности, определяемую некоторым расстоянием. Если ведущий объект останавливается, следующие объекты перемещаются только до тех пор, пока они не коснутся зоны безопасности своего предшественника.
Вот некоторый псевдокод для того, как эти вещи могут быть реализованы. Имейте в виду, что это может быть не самым элегантным решением с точки зрения распределения обязанностей и инкапсуляции.
class Position {
property x;
property y;
}
class WayPoint extends ListNode {
property position;
}
class Path extends List {
property WayPoints = array();
// Find out the x, y coordinates given the distance traveled on the path
function getPositionFromDistanceFromEnd(distance) {
currentWayPoint = this->first();
while(distance > 0) {
distanceBetweenWayPoints = this->getDistance(currentWayPoint, currentWayPoint->next());
if(distanceBetweenWayPoints > distance) {
position = ... // travel remaining distance between currentWayPoint and currentWayPoint->next();
return position;
} else {
distance -= distanceBetweenWayPoints;
currentWayPoint = currentWayPoint->next();
}
}
}
function addWayPoint(position) {
// Vector describing the current and new direction of movement
currentDirection = this->first() - this->second();
newDirection = position - this->first();
// If the direction has not changed, there is no need to add a new WayPoint
if( this->sameDirection(currentDirection, newDirection) {
this->first->setPosition(position);
} else {
this->add(position);
}
}
}
class Snake extends DoublyLinkedList {
property Path;
property MovingObjects = array();
}
abstract class MovingObject extends DoublyLinkedListNode {
property Snake; // shared among all moving objects of the same snake
property position;
const securityDistance = 10;
abstract function move() { }
}
class MovingObjectLeader extends MovingObject {
property direction;
function move() {
this->position += this->direction * this->Snake->speed;
this->Snake->Path->addWayPoint(this->position);
if(this->hasFollower()) {
this->follower->move();
}
}
}
class MovingObjectFollower extends MovingObject {
property distanceFromEnd;
function move() {
this->distanceFromEnd += this->Snake->speed;
// If too close to leader: stop in order to respect security distance
if(this->distanceFromEnd > this->leader()->distanceFromEnd - this->securityDistance) {
this->distanceFromEnd = this->leader()->distanceFromEnd - this->securityDistance;
}
this->position = this->Snake->getPositionFromDistanceFromEnd(this->distanceFromEnd);
if(this->hasFollower()) {
this->follower->move();
}
}
}
Path-> WayPoints становится больше и больше, чем дольше продолжается игра. Если ваша Змея существует в течение достаточно долгого времени, вам нужно удалять последнюю точку маршрута всякий раз, когда последний элемент змеи передал второй до последнего путь точки пути. Не забудьте также уменьшить distanceFromEnd во всех MovingObjects of Snake соответственно.
По сути, вам понадобятся две структуры данных (логическая, навязчивая или реальная, в зависимости от остальной части вашего кода). Первый будет отслеживать цепочки объектов, а другой путь.
Цепочка Просто вам нужно знать, какие объекты следуют за другими объектами. В простейшем случае это будет просто А следует за В, но может включать больше последователей. В цепи есть назначенный лидер .
Путь Для каждой цепочки вам понадобится путь. В зависимости от того, как ваша игра работает, будет зависеть, как она будет структурирована. В большинстве случаев это будет какой-то связанный список. Это позволит отслеживать позиции, которым должны следовать все в цепочке.
Теперь лидер в цепочке будет добавлять предметы на путь . Каждый раз, когда он перемещается, он добавляет что-то в начало списка. Каждый объект в цепочке запоминает, где он находится в списке. Когда дело доходит до перемещения, он просто перемещается к следующему элементу в списке (при необходимости интерполируется соответствующим образом). Когда последний элемент в цепочке проходит мимо элемента в списке, этот элемент можно отбросить (он будет в хвосте).
Метафорически лидер оставляет след своих последователей. Последний последователь в списке потребляет хлебную крошку.
Содержит ли ваш список отдельные точки, или только вершины пути, или что-то еще, полностью определяется вашим игровым движком. Но в любом случае я не вижу, что вы сможете избежать самого списка.
источник
Найти A * поиск пути. Это общий и простой способ заставить ваши игровые объекты / объекты переходить / следовать за позицией.
источник