Программное движение автомобиля во флеш - теперь с прицепом

25.07.2008, автор Stormit, рубрики: ActionScript, Flash игры

Это продолжение дорожных приключений. Тогда попросили сделать вариант с прицепом. Я понимаю желание сделать “побырику” flash-игру типа “Парковка” имея под рукой готовый код, поэтому дальше описан пример как это сделать. И не один прицеп, а целый поезд.

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

В жизни, на колеса действует множество сил, и машина с прицепом - сложная система для расчетов. На решение такой задачи может претендовать какой-нибуть физический движок. У меня же нет точных расчетов и все что можно - имитируется. Поэтому, если ваш вариант будет вести себя не совсем корректно, некоторые параметры нужно будет изменить по ситуации. Я не часто вижу как ездят автомобили с прицепом, поэтому все настраивал интуитивно. Прошу оценить реализм :)

От предыдущего примера код почти не отличается. Добавилось несколько новых клипов и одна функция в коде.

  1. Добавился клип прицепа trailer. Также, как в машине так и в прицепе появились 2 круглых символа. Они нужны чтобы считывать с них координаты и присоединять по этим точкам прицеп. Чтобы точка была не видна, внутри клипа можно прописать:
    _visible = false;
  2. На той же временной линейке, где находятся клипы car и trailer,в кадре пишем такой код:
    WIDTH = zone._width;
    HEIGHT = zone._height;
    LEFT = zone._x;
    TOP = zone._y;
    RIGHT = LEFT + WIDTH;
    BOTTOM = TOP + HEIGHT;
     
    speed = 0;
    speedLimit = 8;
    speedStep = .5;
    rotLimit = 35;
    rotStep = 3;
    trailerRotLimit = 55 / 180 * Math.PI;
     
    processKey = function () {
    	if (Key.isDown(Key.CONTROL) || Key.isDown(Key.SPACE)) {
    		car.kovsh.play();
    	}
    	if (Key.isDown(Key.LEFT)) {
    		if (car.left._rotation > -rotLimit) {
    			car.left._rotation = car.right._rotation -= rotStep;
    		}
    	} else if (Key.isDown(Key.RIGHT)) {
    		if (car.left._rotation < rotLimit) {
    			car.left._rotation = car.right._rotation += rotStep;
    		}
    	}
    	if (Key.isDown(Key.UP)) {
    		speed += (speed < speedLimit)? speedStep : 0;
    	} else if (Key.isDown(Key.DOWN)) {
    		speed += (speed > -speedLimit)? -speedStep : 0;
    	} else {
    		speed += (speed < 0)? speedStep : (speed > 0)? -speedStep : 0;
    	}
    };
    checkCycle = function () {
    	bounds = car.getBounds(this);
    	boundWidth = bounds.xMax - bounds.xMin;
    	boundHeight = bounds.yMax - bounds.yMin
    	if(bounds.xMax < LEFT) {
    		car._x = RIGHT + car._x - bounds.xMin;
    	} else if(bounds.xMin > RIGHT) {
    		car._x = LEFT - (bounds.xMax - car._x);
    	}
    	if(bounds.yMax < TOP) {
    		car._y = BOTTOM + (car._y - bounds.yMin);
    	} else if(bounds.yMin > BOTTOM) {
    		car._y = TOP - (bounds.yMax - car._y);
    	}
    };
    function move(){
    	if(Math.abs(speed) > 0) {
    		car._rotation += car.left._rotation * speed / car.left._x;
    	}
    	angle = car._rotation * Math.PI / 180;
    	speedX = speed * Math.cos(angle);
    	speedY = speed * Math.sin(angle);
    	car._x += speedX;
    	car._y += speedY;
    }
     
    function correctTrailer(slave, master){
    	if(speed){
    		//Пересчитываем точку в родительскую систему координат
    		var masterDot = {x:master.dotOut._x, y:master.dotOut._y};
    		master.localToGlobal(masterDot);
    		globalToLocal(masterDot);
     
    		var dx = masterDot.x - slave._x;
    		var dy = masterDot.y - slave._y;
     
    		//Угол между прицепом и машиной
    		var aRad = Math.atan2(dy, dx);
     
    		//Угол поворота самой машины
    		var masterRotRad = master._rotation / 180 * Math.PI;
     
    		//Разница между ними
    		var dAngle = masterRotRad - aRad;
     
    		//Коррекция перехода 180/-180 градусов
    		if (dAngle > Math.PI) {
    			dAngle = -Math.PI*2 + dAngle;
    		} else if (dAngle < -Math.PI) {
    			dAngle = Math.PI*2 + dAngle;
    		}
    		if(speed < 0) {
    			//Выполняется только при движении назад
    			//Ограничение на поворот и торможение из-за прицепа
    			if(dAngle >= trailerRotLimit) {
    				aRad = masterRotRad - trailerRotLimit;
    				speed *= .8;
    			} else if(dAngle <= -trailerRotLimit) {
    				aRad = masterRotRad + trailerRotLimit;
    				speed *= .8;
    			}
     
    			//Обратная связь на машину в результате торможения
    			//Коэффициенты .5 и 3 подбираются вручную
    			if(dAngle >= trailerRotLimit * .5) {
    				master._rotation += speed / speedLimit * 3;
    			} else if(dAngle <= -trailerRotLimit * .5) {
    				master._rotation -= speed / speedLimit * 3;
    			}
    		}
     
    		//Убираем паразитную скорость (погрешность округления чисел)
    		if(Math.abs(speed) < speedStep && !Key.isDown(Key.DOWN)) {
    			speed = 0;
    		}
     
    		//Поворачиваем прицеп к машине
    		var a = aRad / Math.PI * 180;
    		slave._rotation = a;
     
    		//Пододвигаем прицеп чтобы была стыковка
    		slave._x = masterDot.x - Math.cos(aRad) * slave.dotIn._x;
    		slave._y = masterDot.y - Math.sin(aRad) * slave.dotIn._x;
    	}
    }
    onEnterFrame = function() {
    	processKey();
    	move();
    	checkCycle();
    	correctTrailer(trailer, car);
    }

    За прицеп отвечает функция correctTrailer(прицеп, куда_цеплять);

  3. Управление стрелками.
  4. Можно пойти дальше.
    Добавим в клип прицепа точку с именем dotOut.
  5. Я специально вынес код прицепа в отдельную функцию. Теперь, передавая параметрами что и куда цеплять, можно сделать небольшой паровозик. Тогда в обработчике enterFrame, одна строка заменится на эти три:
    correctTrailer(trailer1, car);
    correctTrailer(trailer2, trailer1);
    correctTrailer(trailer3, trailer2);

    Я отключил здесь зацикленность движения, так что не уезжайте далеко за границы флэшки.

Один минус - в расчетах не учитывается масса объектов. На это можно повлиять коэффициентами, но все равно прицепы при заднем ходе дергаются слишком резко.
А может это мне кажется… Что думаете?

Возвращаясь к flash-игре типа “Припаркуй авто”, мне кажется что столкновение проще определять путем расстановки точек по периметру машины и прицепа. Брать в цикле их координаты и проверять на hitTest() с окружающими объектами. Нет громоздкого физического движка и не нужно ломать голову над проекцией векторов.

Интересно на 34%

(23) Хитрых на тему «Программное движение автомобиля во флеш - теперь с прицепом»

  1. Stormit

    Общий принцип с прицепом такой: мы его поворачиваем так, чтобы его центр, точка прицепа и точка автомобиля лежали на одной прямой. Затем двигаем прицеп, чтобы эти точки совместились. И так каждый кадр. Сложности с обратной связью, когда прицеп должен воздействовать на машину.

  2. Nicholass

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

  3. Stormit

    Чем смогу - помогу

  4. Sarjick

    Здрасте, спасибо за пример. Для меня - это одна из лучших хитростей на этом сайте. Денис, ты мегакрут

  5. Destroyer

    А не по такому принципу делается “змейка”?

  6. Stormit

    Можно и по такому, если возможен поворот на случайный градус, только там движения назад нет. А самые примитивные змейки делаются путем гашения/подсвечивания клеточки.

  7. KUSAKA

    Черт, я не удержался и сделал мини-гейм “Парковка” =)

  8. Ixifeus

    Супер!!! Такого,если не ошибаюсь,я ещё нигде не видел.Тоже с удовольствием поигрался в парковку,а учитывая прицеп,игра стала поинтересней )))

  9. Stormit

    >>Черт, я не удержался и сделал мини-гейм “Парковка” =)
    Блин, я так и знал ))

  10. coolclub

    Cууууупееееррр!Можете мне прислать на coolcub.3dn@mail.ru саму игру на флеш, или хотябы готовый код чтобы добавить к себе на сайт.
    P.S.Я просто в этом не разбираюсь :) :-)

  11. Kudashov

    Спасибо, пост действительно толково написан и по делу, есть что почерпнуть.

  12. Dalaylam

    Класс! То, что надо.

  13. Lora-lora

    Отличная статья.Респект автору.

  14. Pb7

    Автор, поясни пожалуйста действии конструкции ? :
    Пример твоего кода ниже:
    speed += (speed 0)? -speedStep : 0;

  15. Stormit

    Вообще-то там такой код:
    speed += (speed < speedLimit)? speedStep : 0;
    Раз вы выделили конструкцию, то наверное знаете. Я так понимаю:
    (если_тру)? тогда_это : иначе_это
    В моем случае - если скорость меньше предельной, немного ее увеличим, иначе прибавим 0.

  16. Pb7

    Спасибо!

  17. alart

    люфта на сцепках нет, а так всё супер !

  18. Михаэль

    почемуто невыходит прицепить 2 и более прицепов, делаю всё по инструкции в итоге 2й прицеп стоит наместе и реагирует только на езду назад

  19. Егор

    У меня прицеп почему то едет на некотором расстоянии от машины. В чем дело?

  20. Mad_Alex

    Я давно искал и нашел. Только я искал не прицеп а регдолл. Наверно и регдолл можно сделать на основе этого? Принцип тот же, только добавить гравитацию и столкновения.

  21. Stormit

    Там другой принцип, там всё построено на реальной физике (попробуйте Box2DFlashAS3).
    Здесь создаётся только видимость физики путём ручной подгонки и определённым устройством символов.

  22. Mad_Alex

    Ага. Кстати говоря, соседние темы - создание человечков, палящих по курсору - это, например, я давно умею. Так что я все искал инструкцию по созданию регдолла.

  23. GHOzzD

    Ну типа круто но тележко спокойно может залезьть на машинку

Оставить комментарий