В лекции 6 мы рассмотрели некоторые способы взаимодействия пользователя с объектами на экране. Теперь мы рассмотрим, как объекты могут реагировать друг на друга при обнаружении коллизий. Целая лекция, посвященная обнаружению коллизий? Да, поскольку мы расскажем о многих сопутствующих моментах, разобравшись в которых, вы будете прекрасно разбираться в коллизиях.
В то время, когда я создавал учебные пособия и открытые исходные файлы в качестве примеров, многие люди присылали мне интересные файлы, созданные ими на базе основных принципов, описанных в книге. Учитывая этот факт, я не буду использовать сложную графику и слишком большие файлы. Я расскажу о некоторых основных принципах, чтобы вы на самом деле разобрались в них, после чего вы сможете опробовать свои навыки на создании нескольких несложных игр. Прибавьте к этому порцию вашего воображения и наслаждайтесь программированием в свое удовольствие!
Сначала мы расскажем о простейшем из всех методов обнаружения коллизий - hitTest. Если вы читаете эту книгу, то, возможно, вам уже приходилось использовать hitTest. Так или иначе, мы пользовались этим методом в предыдущей лекции, однако существует несколько способов его использования, и сейчас мы их рассмотрим.
Есть два способа использования hitTest, причем второй имеет две возможности, итого получается три различных метода работы с hitTest. Мы рассмотрим их в порядке возрастания сложности.
Наиболее простая интерпретация hitTest, как вы помните из предыдущей лекции, такова.
movieClip1_mc.hitTest (movieClip2_mc);
Здесь просто проверяется факт коллизии двух фильмов. Не имеет совершенно никакого значения, на какой позиции находится каждый из фильмов. Если два фильма перекрывают друг друга, будет возвращено значение"истина", в противном случае - "ложь". Следовательно, hitTest используется почти исключительно в выражении if следующим образом.
if (movieClip1_mc.hitTest(movieClip2_mc)) {
// do something
}
При использовании hitTest необходимо знать о так называемых граничных прямоугольниках. Это невидимые прямоугольники, полностью соответствующие границам фильма. Верх прямоугольника соответствует самому верхнему видимому элементу фильма, низ - самому нижнему видимому элементу, а левый и правый края прямоугольника соответствуют самым дальним левой и правой точкам фильма. Если вы расположите фильм на рабочем месте и затем щелкнете на нем, чтобы выделить его, вы увидите синюю рамку вокруг фильма (если вы не изменили цвет рамок по умолчанию). Это и будет прямоугольник, являющийся границей фильма:

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

Как можно обойти этот недостаток? Никак, если вы собираетесь использовать только данную версию hitTest. Это и есть та цена, которую вы платите за скорость и простоту использования данного метода. Хотя для многих приложений этот способ вполне годится. В небольшой игре-стрелялке, созданной нами в предыдущей лекции, все двигалось довольно быстро, и вы не заметили бы какие-либо неточности. Но представьте себе игру, например, Atari's Asteroids, где объекты могут двигаться довольно медленно. Вы собираетесь подвести ваш корабль близко к краю астероида, но тут он внезапно взрывается. "Я же его не касался!" - скажете вы. Да, как видите, эта версия hitTest не была бы хорошим решением в данной ситуации.
Вы наверняка использовали этот метод ранее, по крайней мере, в предыдущей лекции, поэтому мы не будем терять время на рассмотрение примеров с его использованием. Перейдем к следующему способу использования hitTest.
Приведем hitTest с помощью shapeFlag.
movieClip_mc.hitTest (x, y, shapeFlag);
Я знаю, что вы сразу обратили внимание на параметр shapeFlag, но не торопитесь. Давайте прочтем выражение слева направо.
В начале мы видим movieClip_mc, который, очевидно, является именем проверяемого фильма. Заметьте, что мы в этот раз проверяем только один фильм. Итак, было ли попадание в этом фильме? Ответ является просто точкой. Эта точка определяется следующими двумя параметрами x и y. Здесь задаем вопрос, находится ли точка (x,y) внутри области фильма movieClip_mc.
Теперь переходим к параметру shapeFlag. С его помощью мы можем выбирать, нужно или не нужно использовать граничный прямоугольник фильма. Если shapeFlag установлен на значение "ложь" или 0, hitTest будет просто проверять, находится ли точка внутри фильма. Но если вы установите shapeFlag на значение "истина" или любое ненулевое численное значение, мы сможем выйти за рамки ограничений использования способа граничных прямоугольников и проверять, находится ли точка в видимой части фильма.
Да, этот способ намного эффективнее. Использование shapeFlag в качестве альтернативы методу hitTest довольно широко распространено. На самом деле я очень редко пользовался hitTest без shapeFlag.
Теперь все выглядит намного приличнее, однако прежде чем заключить, что все наши проблемы успешно решены, нужно выяснить, откуда берутся эти значения x и y. Вспомните, что с помощью первого рассмотренного нами способа фильм проверялся в сопопоставлении с другим фильмом. Мы можем имитировать это посредством использования одного фильма как фильма, выполняющего функцию, а параметры _x и _y второго фильма в качестве параметров x и y hitTest. Это будет выглядеть так.
movieClip1_mc.hitTest (movieClip2_mc._x, movieClip2_mc._y, true);
Посмотрев на следующий рисунок, вы поймете, с какой проблемой мы сейчас столкнемся.
Попадание не будет зарегистрировано, так как параметры x и y здесь ссылаются на центр movieClip2_mc, который не находится внутри нужной области попадания.

Итак, мы попали из огня да в полымя: Да, я люблю метафоры. Я хотел бы дать вам исчерпывающий совет по поводу того, как обойти эту проблему, однако так и не нашел поистине эффективного способа. Это задачка вроде поиска Св. Грааля: представить фильм с помощью shapeFlag для проверки попадания фильма. На самом деле выход существует всегда, поэтому не бейтесь головой о стену, думая, что вы упустили что-то очевидное. Я дам вам несколько советов, чтобы вам было легче.
hitTest, и маленький для предоставления параметров X и Y.hitTest из этого объекта, так как вам нужна форма видимой области этого объекта.Это, по большому счету, все, что необходимо знать о hitTest. Теперь я хочу сразу перейти к более сложным методам обнаружения коллизий, однако если у вас есть сомнения в том, что вы хорошо разбираетесь в различных типах hitTest, поэкспериментируйте со следующим упражнением до тех пор, пока не уясните для себя все вопросы.
movieClip1 и movieClip2. Придайте обоим фильмам неправильную форму и назовите их инстансы именами movieClip1_mc и movieClip2_mc соответственно.actions.movieClipl_mc.onEnterFrame = function() {
if (this.hitTest(movieClip2_mc)) {
trace("hit");
}
};
movieClip2_mc.onEnterFrame = function() {
this._x = _xmouse;
this._y = _ymouse;
};
shapeFlag, поэтому просто замените имеющийся код из шага 2 следующим кодом.movieClipl_mc.onEnterFrame = function() {
if (this.hitTest(movieClip2_mc._x, movieClip2_mc._y, false)) {
trace("hit");
}
};
movieClip2_mc.onEnterFrame = function() {
this._x = _xmouse;
this._y = _ymouse;
};
Cтолкновение не регистрируется до тех пор, пока курсор не войдет (хотя он не виден) в граничную рамку вокруг movieClip1.
shapeFlag. Замените весь имеющийся код в кадре 1 следующим кодом.movieClip1_mc .onEnterFrame = function () {
if (this.hitTest(movieClip2_mc._x, movieClip2_mc._y, true)) {
trace("hit");
}
};
movieClip2_mc.onEnterFrame = function() {
this._x = _xmouse;
this._y = _ymouse;
};
Эксперименты с каждым и этих методов дадут вам представление о работе каждого из них, а также о том, какой именно метод нужно использовать в той или иной ситуации.
Несмотря на то, что мы рассказали все о hitTest и рассмотрели его преимущества и ограничения, мы еще очень далеки от нашей цели. hitTest является встроенным во Flash методом обнаружения коллизий, однако существуют несколько созданных вручную альтернативных способов, которые в определенных задачах могут быть гораздо мощнее.
Что мы на самом деле ищем при проверке на коллизию? Коллизия - это столкновение или соприкосновение двух объектов.
Пусть у нас есть объект в определенном месте. Если другой объект находится в том же самом месте, тогда между ними происходит коллизия. Предположим, имеется стена, расположение которой _x=500. Если координата _x другого объекта больше 500, то он должен быть в состоянии столкновения со стеной, так? Да, поскольку мы решили, что у стены бесконечная высота, так как мы не проверяем значение _y. Самое замечательное заключается в том, что нам даже не нужно создавать стену. Мы просто говорим, что она там есть, посредством указания значений ее позиции. Самый простой способ - установить константу в начале фильма, к примеру right=500 ;. Давайте рассмотрим этот случай и попробуем с ним поработать.
init. Переименуйте слой по умолчанию actions и введите следующий код.init(); function init () Х right = 500; left = 50; }
ball и дайте его инстансу имя ball_mc.init ();
function init() {
right = 500;
left = 50;
ball_mc. velX = 5;
}
onEnterFrame. Присвоим функцию управления в секции init и после этого создадим функцию move. Добавьте следующий код в имеющуюся программу.init ();
function init() {
right = 500;
left = 50;
ball_mc.velX = 5;
ball_mc.onEnterFrame = move;
}
function move() {
this._x += this.velX;
}
Здесь берется скорость и прибавляется к позиции _x. Так как velX = 5, эта функция прибавляет 5 к позиции _x шарика в каждом кадре. Запустив этот фильм, вы увидите, что шарик переместится вправо и исчезнет. Итак, сейчас мы займемся
if (this._x>right-(this._width/2)) {
// then we have a hit
}
Мы добавим этот код в функцию move сразу после того, как настроим позицию шарика.
Берем параметр _width фильма ball и делим его на два. Это даст расстояние от центра круга до его края (т.е. радиус). Имейте в виду, что это будет работать только в случае с объектами, у которых имеется центральная точка регистрации, и которые отцентрированы по оси x.

function move() {
this._x += this.velX;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
}
}
if для проверки столкновения между шариком и левой стенкой. Добавьте следующий новый код в функцию move.if (this._x<left+(this._width/2)) {
this.velX *= -1;
}
init();
function init() {
right = 500;
left = 50;
ball_mc.velX = 5;
ball_mc.onEnterFrame = move;
}
function move() {
this._x += this.velX;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
}
if (this._x<left+(this._width/2)) {
this.velX *= -1;
}
}


Если вы не видите этого, уменьшите либо частоту кадров фильма, либо значение скорости. Когда вы бросаете мяч в направлении стены, он ведь не проникает в нее, а просто отскакивает! (Имеется в виду обычный человек, бросающий в стену обычный теннисный мячик, а не Арнольд Шварценеггер, кидающий железное ядро). Сейчас мы постараемся решить эту проблему.
function move() {
this._x += this.velX;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
this, x = right-(this, width/2);
}
if (this._x<left+(this._width/2)) {
this.velX *= -1;
this._x = left+(this._width/2);
}
}
_x или velX с использованием _y и velY, а также добавлю новые переменные с именами top и bottom. Также _width будет заменено на _height. Нарисуйте еще две линии вверху и внизу рабочего места на позициях (x,50) и (x, 350) для отображения стенок. Следующий код не представляет проблем.init();
function init() {
right = 500;
left = 50;
top = 50;
bottom = 350;
ball_mc.velX = 5;
ball_mc.velY = 5;
ball_mc.onEnterFrame = move;
}
function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
this._x = right-(this._width/2);
}
if (this._x<left+(this._width/2)) {
this.velX *= -1;
this._x = left+(this._width/2);
}
if (this._y>bottorn-(this._height/2)) {
this.velY *= -1;
this._y = bottom-(this._height/2);
}
if (this._y<top+(this._height/2)) {
this.velY *= -1;
this._y = top+(this._height/2);
}
}
pc (я добавил надписи на ракетках, чтобы следующий рисунок был понятнее).paddles .onEnterFrame для двух ракеток.init();
function init() {
RIGHT = 500;
LEFT = 50;
TOP = 50;
BOTTOM = 350;
ball_mc.velX = 0;
ball_mc.velY = 0;
ball_mc.onEnterFrame = move;
pc_mc.onEnterFrame = pcMove;
player_mc.onEnterFrame = playerMove;
}
pcMove и playerMove. pcMove начинается как простое уравнение приближения, поэтому ракетка компьютера будет всегда пытаться попасть на позицию шарика. Чтобы было интереснее, мы позволим этой ракетке двигаться только в том случае, если шарик передвигается в ее направлении (ball_mc.velX<0). Ракетка player, т.е. ракетка игрока? будет просто проверять нажатие клавиш [ВВЕРХ] и [ВНИЗ] и, в случае нажатия, перемещаться, соответственно, вверх или вниз. В обоих случаях с ракетками будет использоваться проверка коллизий по расстоянию, чтобы не допустить их исчезновения с игрового поля.function pcMove() {
if (ball_mc.velX<0) {
this._y += (ball_mc._y-this._y)/5;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
function playerMove() {
if (Key.isDown(Key.UP)) {
this._y -= 10;
} else if (Key.isDown(Key.DOWN)) {
this._y += 10;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
_root функции приемника событий Key, а также установкой события onKeyDown на начало движения шарика. Добавьте следующие четыре строки в функцию init и затем создайте функцию serve в конце всего кода.Key.addListener(_root);
_root.onKeyDown = serve;
pcServe = true;
baseSpeed = 10;
function serve() {
if (Key.getCode(j == Key.SPACE) {
if (playerServe) {
ball_mc.velX = -baseSpeed;
ball_mc.velY = Math.random()*20-10;
playerServe = false;
} else if (pcServe) {
ball_mc.velX = baseSpeed;
ball_mc.velY = Math.random()*20-10;
pcServe = false;
}
}
}
Теперь при запуске игры, шарик не движется до тех пор, пока вы не нажмете пробел. Когда значением pcServe станет "истина", будет выполнен второй блок, и шарик направится к ракетке игрока.
Это значит, что шарик должен находиться ниже верхнего края ракетки, но выше нижнего ее края. Если он находится между этими двумя точками при ударе о right, значит, есть попадание. Если шарик пролетел мимо, он исчезает. Здесь мы могли бы применить старый добрый hitTest, однако я воспользуюсь этим случаем для представления новой концепции и действия под названием getBounds. Это одна из редко используемых команд. Она как бы разбивает hitTest на составные части и позволяет использовать именно те из них, которые необходимы.
getBounds - это метод объекта movieClip. При вызове он возвращает объект. Этот объект имеет четыре параметра, которые сообщают нам о местоположении фильма. Вот способ использования данного метода.
boundsObject=movieClip.getBounds (scope);
boundsObject имеет следующие четыре параметра: xMin, xMax, yMin и yMax. Они логически соответствуют четырем краям граничного прямоугольника фильма. Однако, вместо того, чтобы заставлять Flash вычислять, что находится внутри, а что вне рамки, мы теперь можем работать с численными значениями. Теперь можно математическим путем определять наши собственные коллизии, пользуясь значением ширины и т.п., как мы делали в случае со стенками.
Единственное, что осталось здесь определить, это параметр scope (область). Параметры min и max возвращаемого объекта boundsObject представлены в пикселях, но относительно чего? Это и есть наша область. В основном вам нужно будет вводить _root в качестве области, или любую другую позицию на временной диаграмме, на которой вы осуществляете проверку коллизий. Это даст вам значения X и Y фильма из _root.
Если вы сделали сам фильм областью, он будет возвращать значения своих углов такими, каковы они есть с его точки зрения. Например, квадрат со стороной 100 пикселей с центральной точкой регистрации всегда будет возвращать значения -50 и 50 в качестве минимального и максимального значений, независимо от того, в каком месте экрана находится фильм. С точки зрения фильма, он распространяется на 50 пикселей вверх от центра, на 50 пикселей вниз, на 50 влево и на 50 вправо.

_root. Добавьте следующую новую строку в функцию move.function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
Теперь playerBounds будет содержать четыре параметра, упомянутые ранее. Параметры, которые нас больше всего интересуют - это yMin и yMax. Если ball_mc._y находится между этими двумя значениями после передачи им точки, определенной выражением right-(this._width/2), то он ударяется о ракетку игрока.

AND (). Имейте в виду, что при этом также учитывается ширина шарика, поэтому мы реализуем проверку не только относительно центра шарика. Таким образом, мы получим гораздо более реалистичный удар.function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
upper = playerBounds.yMin - this._height/2;
lower = playerBounds.yMax + this._height/2;
if (this._y>upper this._y<lower) {
this.velX *= -1;
this.velY += Math.random()*20 - 10;
this._x = right-(this._width/2);
}
}
}
if (this._y>upper this._y<lower) {
this.velX *= -1;
this.velY += Math.random()*20-10;
this._x = right-(this._width/2);
baseSpeed *= 1.01;
this.velX *= 1.01;
}
else.function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
if (this._y>playerBounds.yMin this._y<playerBounds.yMax) {
this.velX *= -1;
this._x = right-(this._width/2);
} else {
this._x = 275;
this._y = 200;
this.velX = 0;
this.velY = 0;
pcScore++;
playerServe = true;
}
}
Здесь вы можете реализовать все, что вам придет в голову. Мне хватило и того, что я добавил pcScore.

init();
function init() {
RIGHT = 500;
LEFT = 50;
TOP = 50;
BOTTOM = 350;
ball_mc.velX = 0;
ball_mc.velY = 0;
ball_mc.onEnterFrame = move;
pc_mc. onEnterFrame = pcMove;
player_mc.onEnterFrame = playerMove;
Key.addListener(_root);
_root.onKeyDown = serve;
pcServe = true;
baseSpeed = 10;
}
function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
upper = playerBounds.yMin-this._height/2;
lower = playerBounds.yMax+this._height/2;
if (this._y>upper this._y<lower) {
this.velX *= -1;
this.velY += Math.random()*20-10;
this._x = right-(this._width/2);
baseSpeed *= 1.01;
this.velX *= 1.01;
} else {
this._x = 275;
this._y = 200;
this.velX = 0;
this.velY = 0;
pcScore++;
playerServe = true;
}
}
if (this._x<left+(this._width/2)) {
pcBounds = pc_mc.getBounds (_root);
if (this._y>pcBounds.yMin-this._height/2
Кthis._y<pcBounds.yMax+this._height/2) {
this.velX *= -1;
this.velY += Math.random()*20-10;
this._x = left+(this._width/2);
baseSpeed *= 1.01;
this.velX *= 1.01;
} else {
this._x = 275;
this._y = 200;
this.velX = 0;
this.velY = 0;
playerScore++;
pcServe = true;
}
}
if (this._y>bottom-(this._height/2)) {
this.velY *= -1;
this._y = bottom-(this._height/2);
}
if (this._y<top+(this._height/2)) {
this.velY *= -1;
this._y = top+(this._height/2);
}
}
function pcMove() {
if (ball_mc.velX<0) {
this._y += (ball_mc._y-this._y)/5;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
function playerMove() {
if (Key.isDown(Key.UP)) {
this._y -= 10;
} else if (Key.isDown(Key.DOWN)) {
this._y += 10;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
function serve() {
if (Key.getCode() == Key. SPACE) {
if (playerServe) {
ball_mc.velX = -baseSpeed;
ball_mc. velY = Math.random()*20-10;
playerServe = false;
} else if (pcServe) {
ball_mc.velX = baseSpeed;
ball_mc. vel Y = Math.random()*20-10;
pcServe = false;
}
}
}
Я продублировал рассмотренные шаги для проверки попадания в другую ракетку, а также реализовал отображение очков игрока в playerScore. Теперь вы можете и поиграть.
Можете смело экспериментировать с этой игрой. В коде на диске есть несколько дополнений, однако вы сами можете создать очень много интересного.
Это последний метод обнаружения коллизий, который мы рассмотрим. Он полностью подходит для идеально круглых объектов, хотя можно немного схитрить и применить его к тем объектам, которые имеют более-менее округлую форму.
Ранее мы говорили о том, что если два объекта находятся примерно на одной позиции, можно считать, что они находятся в состоянии столкновения. Иными словами, расстояние между двумя объектами равно нулю. Создадим два круглых фильма и будем вычислять расстояние между ними. Для этого мы применим теорему Пифагора. Теорема гласит, что сумма квадратов двух меньших сторон прямоугольного треугольника равна квадрату большей стороны треугольника. Смотрите.

Я пометил стороны буквами x, y и d. Теорема Пифагора гласит.
x2 + y2 = d2
Т.е.:
x*x + y*y = d*d
Теперь, если провести некоторые элементарные алгебраические преобразования, получим следующее.
d = Math.sqrt (x*x + y*y);
Это значит, что если возвести в квадрат обе стороны, сложить их и затем извлечь квадратный корень, получим длину гипотенузы. Теперь немного изменим наш рисунок, и вам все станет ясно.

На рисунке показано, что dx - это расстояние по оси x между mc1_mc и mc2_mc. dy - это расстояние по оси y, а dist - это среднее расстояние между ними. Зная все это, можно интерпретировать данные выводы следующим образом с помощью обычного кода.
dx = mc2_mc._x-mc1_mc._x; dy = mc2_mc._y-mc1_mc._y; dist = Math.sqrt (dx*dx+dy*dy);
Это еще одна шпаргалка для вас. Запишите эти выражения, они вам пригодятся.
Ранее я говорил, что когда расстояние между двумя фильмами обращается в ноль, объекты сталкиваются. Однако вспомните последнее упражнение с игрой в пинг-понг, где нужно было учитывать размер шарика. Теперь нам нужно брать в расчет размеры обоих фильмов. В нашем случае на данном этапе расстояние между фильмами в 0 пикселей означало бы полное совмещение центров двух шариков, и только в этом случае было бы зарегистрировано столкновение. Это уж слишком!
В последний раз мы просто брали радиус шарика для определения его состояния столкновения. Здесь мы сделаем то же самое, однако вычислим радиус одного шарика и прибавим к нему радиус другого. Если расстояние между шариками будет меньше, чем эта величина, будет регистрироваться столкновение. Красота этого метода состоит в том, что не имеет значения размер фильмов, так как мы будем определять радиусы динамически.

Приведем код для выяснения того, произошло столкновение или нет.
dx=mc2_mc._x-mc1_mc._x;
dy=mc2_mc._y-mc1_mc._y;
dist=Math.sqrt(dx*dx+dy*dy);
if (dist<mc1_mc._width/2 + mc2_mc._width/2){
// you have a hit!
}
То, что будет происходить после
Это будет еще одна небольшая игра, что-то наподобие Atari's Asteroids, однако вы можете превратить эту игру во все, что придет вам в голову.
ball и отметьте опцию Export for ActionScript в окне Linkage Properties. Введите ball.player_mc.init с помощью цикла for. В нем мы присваиваем препятствиям случайные позиции, скорости и размеры, азатем присваиваем управляющий элемент onEnterFrame. Какой продуктивный цикл! Добавим все это в отдельный слой с именем actions.init();
function init() {
for (i=0; i<10; i++) {
ball_mc = attachMovie("ball", "b"+i, i);
ball_mc._x = Math.random()*550;
ball_mc._y = Math.random()*400;
ball_mc.velX = Math.random()*10-5;
ball_mc.velY = Math.random()*10-5;
ball_mc._xscale = Math.random()*40+10;
ball_mc._yscale = Math.random()*40+10;
ball_mc.onEnterFrame = ballMove;
}
}
ballMove.function ballMove() {
this._x += this.velX;
this._y += this.velY;
if (this._x>550) {
this._x = 0;
}
if (this._y<0) {
this._x = 550;
}
if (this._y>400) {
this._y = 0;
}
if (this._y<0 {
this._y = 400;
}
}
Это уже вам хорошо знакомо. Здесь скорость прибавляется к позиции и проверяется, не ушел ли шарик за пределы фильма. Если да, шарик возвращается на противоположный край рабочего места. Если хотите, можете сделать так, что шарик будет отскакивать от стенки, использовав код, разработанный нами в предыдущем упражнении.
player_mc.onEnterFrame. Сначала присвоим его в конце функции init.player_mc.onEnterFrame = playerMove;
function playerMove() {
this._x += (_root._xmouse-this._x)/20;
this._y += (_root._ymouse-this._y)/20;
}
player_mc в каждом кадре.dx = player_mc._x-this._x;
dy = player_mc._y-this._y;
dist = Math, sqrt (dx*dx+dy*dy);
if (dist<player_mc._width/2+this._width/2) {
player_mc._visible = false;
}
Единственной реакцией на столкновение будет исчезновение player_mc, однако после регистрации столкновения нам нужно, чтобы можно было продолжить игру без перезапуска самого фильма. Для этого мы добавим следующую функцию определения в конец функции init, чтобы player_mc появлялся снова после щелчка мыши.
player_mc. onMouseDown = function() {
this._visible = true;
};
init();
function init() {
for (i=0; i<10; i++) {
ball_mc = attachMovie("ball", "b"+i, i);
ball_mc._x = Math.random 0*550;
ball_mc._y = Math.random 0*400;
ball_mc.velX = Math.random 0*10-5;
ball_mc.velY = Math.random 0*10-5;
ball_mc._xscale = Math.random()*40+10;
ball_mc._yscale = Math.random()*40+10;
ball_mc.onEnterFrame = ballMove;
player_mc.onEnterFrame = playerMove;
player_mc. onMouseDown = function() {
this._visible = true;
};
}
}
function ballMove() {
this._x += this.velX;
this._y += this.velY;
if (this._x>550) {
this._x = 0;
}
if (this._y<0 {
this._x = 550;
}
if (this._y>400) {
this._y = 0;
}
if (this._y<0) {
this._y = 400;
}
dx = player_mc._x-this._x;
dy = player_mc._y-this._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<player_mc._width/2+this._width/2) {
player_mc._visible = false;
}
}
function playerMove() {
this._x += (_root._xmouse-this._x)/20;
this._y += (_root._ymouse-this._y)/20;
}
В следующей лекции мы будем изучать применение некоторых математических и физических принципов, но пока я хочу рассказать еще об одном моменте. Это стратегия поддержки столкновений большого числа объектов.
До сих пор мы рассматривали столкновение одного объекта с другим объектом или столкновение одного объекта с несколькими объектами. В игре, в которой мы стреляем по космическим кораблям пришельцев, корабли используют цикл for для проверки всех снарядов по очереди. Как вариант, можно было бы реализовать в снарядах проверку попадания во все корабли по очереди, однако здесь нам бы потребовалось знать количество активных в данный момент кораблей, а также их имена. Это возможно, но первый способ проще.
В последнем упражнении мы могли бы "перевернуть" проверку коллизий для объекта player. В нем можно было бы использовать цикл for, как и в космических кораблях, и проверять каждый меньший шарик. Это реализуется в функции playerMove следующим образом.
for (i=0; i<10; i++) {
dx = this._x-_root["b"+i]._x;
dy = this._y-_root["b"+i]._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<this._width/2+_root["b"+i]._width/2) {
this._visible = false;
}
}
Этот вариант работает отлично, однако мы обеспечиваем лишь проверку одного объекта относительно одного, или одного относительно нескольких объектов. А что делать в случае, если у нас имеется набор движущихся объектов, и каждый из них должен реагировать на столкновение с любым другим объектом? Можно было бы добавить в каждый из них цикл for, как показано выше, чтобы каждый объект был обработан циклом для проверки на столкновение с любым другим объектом, однако это было бы очень неэффективно. Представьте себе следующий сценарий.
Имеется три объекта: A, B и C. A проверяет столкновение между собой и B, затем столкновение между собой и C; B проверяет столкновение между собой и A, а также между собой и C; наконец, C осуществляет проверку относительно A и B. Итак, мы имеем:
Мы получили шесть вариантов, но это вдвое больше, чем нужно. A:B - это то же самое, что и B:A, A:C идентично C:A, а B:C - то же, что и C:B.
По мере увеличения числа объектов такая проверка теряет смысл, производя в любом случае в два раза больше проверок, нежели это требуется. Поэтому нам нужен более эффективный способ реализации проверки, т.е. процедура, которая проверяет пару объектов только один раз. Приведем наилучший, на наш взгляд, метод осуществления проверок на столкновение. Он довольно короток и ясен.
Вместо поддержки объектов самих по себе, которую мы использовали до сих пор, лучше поддерживать их на главной временной диаграмме. Самый удобный способ осуществить это - использовать массив. При создании каждого объекта мы добавляем его в массив, и тогда все наши объекты будут находиться в одном массиве для простоты доступа и управления.
ball в Library и сохраните его под другим именем. Удалите все инстансы с рабочего места, а также весь имеющийся код. Начните с добавления в кадр 1 слоя actions этого кода.init();
function init() {
max = 10;
balls_array = new Array();
for (n=0; n<max; n++) {
balls_array[n] = attachMovie("ball", "b"+n, n) ;
balls_array[n]._x = Math.random 0*550;
balls_array[n]._y = Math.random()*400;
balls_array[n].velX = Math.random()*10-5;
balls_array[n].velY = Math.random()*10-5;
balls_array[n]._xscale = balls_array[n]._yscale =
Math.random()*50+30;
}
}
Здесь мы установили значение max, которое будет определять число создаваемых шариков. Затем создается массив для хранения в нем фильмов. Цикл для присвоения фильмов практически идентичен предыдущему файлу, за исключением того, что он присваивает фильм элементу массива и осуществляет доступ к нему через этот элемент для присвоения параметров. Мы также опустили управляющий элемент onEnterFrame, так как управление фильмами будет осуществляться извне.
onEnterFrame в _root. Сначала реализуем присвоение в функции init (под строкой max = 10; ):_root.onEnterFrame = main;
main, в которой и будет заключаться весь смысл. Сначала обрабатываем циклом массив для передвижения каждого шарика по отдельности. Здесь все довольно просто.function main() {
for (i=0; i<balls_array.length; i++) {
balls_array[i] ._x += balls_array[i] .velX;
balls_array[i] ._y += balls_array[i] .velY;
if (balls[i]_array._x>550) {
balls[i]_array._x = 0;
}
if (balls[i]_array._x<0) {
balls[i]_array._x = 550;
}
if (balls[i]_array._y>400) {
balls[i]_array._y = 0;
}
if (balls[i]_array._y<0) {
balls[i]_array._y = 400;
}
}
}
Выглядит знакомо? Так и должно быть. Это тот же самый код, что и ранее, однако здесь мы осуществляем доступ к фильму извне через элемент массива, в котором он сохранен, вместо внутреннего доступа с использованием this. Запуститe фильм. Вы увидите десять кругов, в случайном порядке двигающихся по экрану. Теперь перейдем к
for. Внешний цикл будет обрабатывать каждый элемент массива (содержащий ссылку на фильм), а внутренний цикл будет использоваться для проверки этого элемента относительно каждого последующего элемента в массиве. Приведем псевдокод, чтобы показать, как это реализуется.for (i=0; i<balls_array, length-1; i++) {
for (j=i+l; j<balls_array. length; j++) {
// test between balls_array[i] and balls_array[j] here
}
}
Это еще одна ваша шпаргалка. Мы будем использовать код проверки коллизий на основе окружностей/расстояния для проверки столкновения шариков. В случае столкновения оба шарика будут удаляться.
array.splice (index, number). Эта команда удаляет число number элементов из массива, начиная с index. Измените функцию main следующим образом.function main() {
for (i=0; i<balls_array.length; i++) {
balls_array[i]._x += balls_array[i].velX;
balls_array[i]._y += balls_array[i].velY;
if (balls_array[i]._x>550) {
balls_array[i]._x = 0;
}
if (balls_array[i]._x<0) {
balls_array[i]._x = 550;
}
if (balls_array[i]._y>400) {
balls_array[i]._y = 0;
}
if (balls_array[i]._y<0) {
balls_array[i]._y = 400;
}
}
for (i=0; i<balls_array.length-1; i++) {
for (j=i+1; j<balls_array.length; j++) {
dx = balls_array[i]._x-balls_array[j]._x;
dy = balls_array[i]._y-balls_array[j]._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<balls_array[i]._width/2+ balls_array [j]._width/2) {
removeMovieClip(balls_array[j]);
balls_array.splice(j, 1);
removeMovieClip(balls_array[i]);
balls_array. splice(i, 1);
}
}
}
}
onMouseDown в начале функции init._root.onMouseDown = createBall;
function createBall() {
ball_mc = attachMovie("ball", "b"+n, n++);
ball_mc._x = _root._xmouse;
ball_mc._y = _root._ymouse;
ball_mc.velX = Math.random()*10-5;
ball_mc.velY = Math.random()*10-5;
ball_mc._xscale = Math.random()*50+30;
ball_mc._yscale = Math.random()*50+30;
ball_mc.onEnterFrame = ballMove;
bal1s_array.push(ball_mc);
}
Это похоже на код, использованный в init для создания исходных фильмов ball. Основное отличие заключается в том, что здесь ссылка на фильм записывается во временную переменную, присваиваются все его параметры, и затем используется array.push для добавления его в массив.
Интересным моментом при настройке наших циклов в функции main является то, что мы использовали balls_array.length для определения числа повторений. Параметр length массива будет обновляться при добавлении и удалении объектов из массива, поэтому наши циклы будут всегда выполняться нужное число раз.
init();
function init () {
max = 10;
_root.onEnterFrame = main;
_root.onMousedown = createBall;
balls_array = new Array();
for (n=0; n<max; n++) {
balls_array[n] = attachMovie("ball", "b"+n, n);
balls_array[n]._x = Math.random()*550;
balls_array[n]._y = Math.random()*400;
balls_array[n].velX = Math.random()*10-5;
balls_array[n].velY = Math.random()*10-5 ;
balls_array[n]._xscale = balls_array[n]._yscale= Math. random 0*50+30;
}
}
function main() {
for (i=0; i<balls_array.length; i++) {
balls_array[i]._x += balls_array[i].velX;
balls_array[i]._y += balls_array[i].velY;
if (balls_array[i]._x>550) {
balls_array[i]._x = 0;
}
if (balls_array[i]._x<0) {
balls_array[i]._x = 550;
}
if (balls_array[i]._y>400) {
bal1s_array[i]._y = 0;
}
if (balls_array[i]._y<0) {
balls_array[i]._y = 400;
}
}
for (i=0; i<balls_array.length-1; i++) {
for (j=i+1; j<balls_array.length; j++) {
dx = balls_array[i]._x-balls_array[j]._x;
dy = balls_array[i]._y-balls_array[j]._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<balls_array[i]._width/2+ balls_array[j] ._width/2) {
removeMovieClip(balls_array[j]);
balls_array.splice(j, 1);
removeMovieClip(balls_array[i]);
balls_array.splice(i, 1);
}
}
}
}
function createBall() {
ball_mc = attachMovie("ball", "b"+n, n++);
ball_mc._x = _root._xmouse;
ball_mc._y = _root._ymouse;
ball_mc.velX = Math.random()*10-5;
ball_mc.velY = Math.random()*10-5;
ball_mc._xscale = Math.random()*50+30;
ball_mc._yscale = Math.random()*50+30;
ball_mc.onEnterFrame - ballMove;
balls_array.push(ball_mc);
}
В лекции 6 мы рассмотрели некоторые способы взаимодействия пользователя с объектами на экране. Теперь мы рассмотрим, как объекты могут реагировать друг на друга при обнаружении коллизий. Целая лекция, посвященная обнаружению коллизий? Да, поскольку мы расскажем о многих сопутствующих моментах, разобравшись в которых, вы будете прекрасно разбираться в коллизиях.
В то время, когда я создавал учебные пособия и открытые исходные файлы в качестве примеров, многие люди присылали мне интересные файлы, созданные ими на базе основных принципов, описанных в книге. Учитывая этот факт, я не буду использовать сложную графику и слишком большие файлы. Я расскажу о некоторых основных принципах, чтобы вы на самом деле разобрались в них, после чего вы сможете опробовать свои навыки на создании нескольких несложных игр. Прибавьте к этому порцию вашего воображения и наслаждайтесь программированием в свое удовольствие!
Сначала мы расскажем о простейшем из всех методов обнаружения коллизий - hitTest. Если вы читаете эту книгу, то, возможно, вам уже приходилось использовать hitTest. Так или иначе, мы пользовались этим методом в предыдущей лекции, однако существует несколько способов его использования, и сейчас мы их рассмотрим.
Есть два способа использования hitTest, причем второй имеет две возможности, итого получается три различных метода работы с hitTest. Мы рассмотрим их в порядке возрастания сложности.
Наиболее простая интерпретация hitTest, как вы помните из предыдущей лекции, такова.
movieClip1_mc.hitTest (movieClip2_mc);
Здесь просто проверяется факт коллизии двух фильмов. Не имеет совершенно никакого значения, на какой позиции находится каждый из фильмов. Если два фильма перекрывают друг друга, будет возвращено значение"истина", в противном случае - "ложь". Следовательно, hitTest используется почти исключительно в выражении if следующим образом.
if (movieClip1_mc.hitTest(movieClip2_mc)) {
// do something
}
При использовании hitTest необходимо знать о так называемых граничных прямоугольниках. Это невидимые прямоугольники, полностью соответствующие границам фильма. Верх прямоугольника соответствует самому верхнему видимому элементу фильма, низ - самому нижнему видимому элементу, а левый и правый края прямоугольника соответствуют самым дальним левой и правой точкам фильма. Если вы расположите фильм на рабочем месте и затем щелкнете на нем, чтобы выделить его, вы увидите синюю рамку вокруг фильма (если вы не изменили цвет рамок по умолчанию). Это и будет прямоугольник, являющийся границей фильма:

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

Как можно обойти этот недостаток? Никак, если вы собираетесь использовать только данную версию hitTest. Это и есть та цена, которую вы платите за скорость и простоту использования данного метода. Хотя для многих приложений этот способ вполне годится. В небольшой игре-стрелялке, созданной нами в предыдущей лекции, все двигалось довольно быстро, и вы не заметили бы какие-либо неточности. Но представьте себе игру, например, Atari's Asteroids, где объекты могут двигаться довольно медленно. Вы собираетесь подвести ваш корабль близко к краю астероида, но тут он внезапно взрывается. "Я же его не касался!" - скажете вы. Да, как видите, эта версия hitTest не была бы хорошим решением в данной ситуации.
Вы наверняка использовали этот метод ранее, по крайней мере, в предыдущей лекции, поэтому мы не будем терять время на рассмотрение примеров с его использованием. Перейдем к следующему способу использования hitTest.
Приведем hitTest с помощью shapeFlag.
movieClip_mc.hitTest (x, y, shapeFlag);
Я знаю, что вы сразу обратили внимание на параметр shapeFlag, но не торопитесь. Давайте прочтем выражение слева направо.
В начале мы видим movieClip_mc, который, очевидно, является именем проверяемого фильма. Заметьте, что мы в этот раз проверяем только один фильм. Итак, было ли попадание в этом фильме? Ответ является просто точкой. Эта точка определяется следующими двумя параметрами x и y. Здесь задаем вопрос, находится ли точка (x,y) внутри области фильма movieClip_mc.
Теперь переходим к параметру shapeFlag. С его помощью мы можем выбирать, нужно или не нужно использовать граничный прямоугольник фильма. Если shapeFlag установлен на значение "ложь" или 0, hitTest будет просто проверять, находится ли точка внутри фильма. Но если вы установите shapeFlag на значение "истина" или любое ненулевое численное значение, мы сможем выйти за рамки ограничений использования способа граничных прямоугольников и проверять, находится ли точка в видимой части фильма.
Да, этот способ намного эффективнее. Использование shapeFlag в качестве альтернативы методу hitTest довольно широко распространено. На самом деле я очень редко пользовался hitTest без shapeFlag.
Теперь все выглядит намного приличнее, однако прежде чем заключить, что все наши проблемы успешно решены, нужно выяснить, откуда берутся эти значения x и y. Вспомните, что с помощью первого рассмотренного нами способа фильм проверялся в сопопоставлении с другим фильмом. Мы можем имитировать это посредством использования одного фильма как фильма, выполняющего функцию, а параметры _x и _y второго фильма в качестве параметров x и y hitTest. Это будет выглядеть так.
movieClip1_mc.hitTest (movieClip2_mc._x, movieClip2_mc._y, true);
Посмотрев на следующий рисунок, вы поймете, с какой проблемой мы сейчас столкнемся.
Попадание не будет зарегистрировано, так как параметры x и y здесь ссылаются на центр movieClip2_mc, который не находится внутри нужной области попадания.

Итак, мы попали из огня да в полымя: Да, я люблю метафоры. Я хотел бы дать вам исчерпывающий совет по поводу того, как обойти эту проблему, однако так и не нашел поистине эффективного способа. Это задачка вроде поиска Св. Грааля: представить фильм с помощью shapeFlag для проверки попадания фильма. На самом деле выход существует всегда, поэтому не бейтесь головой о стену, думая, что вы упустили что-то очевидное. Я дам вам несколько советов, чтобы вам было легче.
hitTest, и маленький для предоставления параметров X и Y.hitTest из этого объекта, так как вам нужна форма видимой области этого объекта.Это, по большому счету, все, что необходимо знать о hitTest. Теперь я хочу сразу перейти к более сложным методам обнаружения коллизий, однако если у вас есть сомнения в том, что вы хорошо разбираетесь в различных типах hitTest, поэкспериментируйте со следующим упражнением до тех пор, пока не уясните для себя все вопросы.
movieClip1 и movieClip2. Придайте обоим фильмам неправильную форму и назовите их инстансы именами movieClip1_mc и movieClip2_mc соответственно.actions.movieClipl_mc.onEnterFrame = function() {
if (this.hitTest(movieClip2_mc)) {
trace("hit");
}
};
movieClip2_mc.onEnterFrame = function() {
this._x = _xmouse;
this._y = _ymouse;
};
shapeFlag, поэтому просто замените имеющийся код из шага 2 следующим кодом.movieClipl_mc.onEnterFrame = function() {
if (this.hitTest(movieClip2_mc._x, movieClip2_mc._y, false)) {
trace("hit");
}
};
movieClip2_mc.onEnterFrame = function() {
this._x = _xmouse;
this._y = _ymouse;
};
Cтолкновение не регистрируется до тех пор, пока курсор не войдет (хотя он не виден) в граничную рамку вокруг movieClip1.
shapeFlag. Замените весь имеющийся код в кадре 1 следующим кодом.movieClip1_mc .onEnterFrame = function () {
if (this.hitTest(movieClip2_mc._x, movieClip2_mc._y, true)) {
trace("hit");
}
};
movieClip2_mc.onEnterFrame = function() {
this._x = _xmouse;
this._y = _ymouse;
};
Эксперименты с каждым и этих методов дадут вам представление о работе каждого из них, а также о том, какой именно метод нужно использовать в той или иной ситуации.
Несмотря на то, что мы рассказали все о hitTest и рассмотрели его преимущества и ограничения, мы еще очень далеки от нашей цели. hitTest является встроенным во Flash методом обнаружения коллизий, однако существуют несколько созданных вручную альтернативных способов, которые в определенных задачах могут быть гораздо мощнее.
Что мы на самом деле ищем при проверке на коллизию? Коллизия - это столкновение или соприкосновение двух объектов.
Пусть у нас есть объект в определенном месте. Если другой объект находится в том же самом месте, тогда между ними происходит коллизия. Предположим, имеется стена, расположение которой _x=500. Если координата _x другого объекта больше 500, то он должен быть в состоянии столкновения со стеной, так? Да, поскольку мы решили, что у стены бесконечная высота, так как мы не проверяем значение _y. Самое замечательное заключается в том, что нам даже не нужно создавать стену. Мы просто говорим, что она там есть, посредством указания значений ее позиции. Самый простой способ - установить константу в начале фильма, к примеру right=500 ;. Давайте рассмотрим этот случай и попробуем с ним поработать.
init. Переименуйте слой по умолчанию actions и введите следующий код.init(); function init () Х right = 500; left = 50; }
ball и дайте его инстансу имя ball_mc.init ();
function init() {
right = 500;
left = 50;
ball_mc. velX = 5;
}
onEnterFrame. Присвоим функцию управления в секции init и после этого создадим функцию move. Добавьте следующий код в имеющуюся программу.init ();
function init() {
right = 500;
left = 50;
ball_mc.velX = 5;
ball_mc.onEnterFrame = move;
}
function move() {
this._x += this.velX;
}
Здесь берется скорость и прибавляется к позиции _x. Так как velX = 5, эта функция прибавляет 5 к позиции _x шарика в каждом кадре. Запустив этот фильм, вы увидите, что шарик переместится вправо и исчезнет. Итак, сейчас мы займемся
if (this._x>right-(this._width/2)) {
// then we have a hit
}
Мы добавим этот код в функцию move сразу после того, как настроим позицию шарика.
Берем параметр _width фильма ball и делим его на два. Это даст расстояние от центра круга до его края (т.е. радиус). Имейте в виду, что это будет работать только в случае с объектами, у которых имеется центральная точка регистрации, и которые отцентрированы по оси x.

function move() {
this._x += this.velX;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
}
}
if для проверки столкновения между шариком и левой стенкой. Добавьте следующий новый код в функцию move.if (this._x<left+(this._width/2)) {
this.velX *= -1;
}
init();
function init() {
right = 500;
left = 50;
ball_mc.velX = 5;
ball_mc.onEnterFrame = move;
}
function move() {
this._x += this.velX;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
}
if (this._x<left+(this._width/2)) {
this.velX *= -1;
}
}


Если вы не видите этого, уменьшите либо частоту кадров фильма, либо значение скорости. Когда вы бросаете мяч в направлении стены, он ведь не проникает в нее, а просто отскакивает! (Имеется в виду обычный человек, бросающий в стену обычный теннисный мячик, а не Арнольд Шварценеггер, кидающий железное ядро). Сейчас мы постараемся решить эту проблему.
function move() {
this._x += this.velX;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
this, x = right-(this, width/2);
}
if (this._x<left+(this._width/2)) {
this.velX *= -1;
this._x = left+(this._width/2);
}
}
_x или velX с использованием _y и velY, а также добавлю новые переменные с именами top и bottom. Также _width будет заменено на _height. Нарисуйте еще две линии вверху и внизу рабочего места на позициях (x,50) и (x, 350) для отображения стенок. Следующий код не представляет проблем.init();
function init() {
right = 500;
left = 50;
top = 50;
bottom = 350;
ball_mc.velX = 5;
ball_mc.velY = 5;
ball_mc.onEnterFrame = move;
}
function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
this.velX *= -1;
this._x = right-(this._width/2);
}
if (this._x<left+(this._width/2)) {
this.velX *= -1;
this._x = left+(this._width/2);
}
if (this._y>bottorn-(this._height/2)) {
this.velY *= -1;
this._y = bottom-(this._height/2);
}
if (this._y<top+(this._height/2)) {
this.velY *= -1;
this._y = top+(this._height/2);
}
}
pc (я добавил надписи на ракетках, чтобы следующий рисунок был понятнее).paddles .onEnterFrame для двух ракеток.init();
function init() {
RIGHT = 500;
LEFT = 50;
TOP = 50;
BOTTOM = 350;
ball_mc.velX = 0;
ball_mc.velY = 0;
ball_mc.onEnterFrame = move;
pc_mc.onEnterFrame = pcMove;
player_mc.onEnterFrame = playerMove;
}
pcMove и playerMove. pcMove начинается как простое уравнение приближения, поэтому ракетка компьютера будет всегда пытаться попасть на позицию шарика. Чтобы было интереснее, мы позволим этой ракетке двигаться только в том случае, если шарик передвигается в ее направлении (ball_mc.velX<0). Ракетка player, т.е. ракетка игрока? будет просто проверять нажатие клавиш [ВВЕРХ] и [ВНИЗ] и, в случае нажатия, перемещаться, соответственно, вверх или вниз. В обоих случаях с ракетками будет использоваться проверка коллизий по расстоянию, чтобы не допустить их исчезновения с игрового поля.function pcMove() {
if (ball_mc.velX<0) {
this._y += (ball_mc._y-this._y)/5;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
function playerMove() {
if (Key.isDown(Key.UP)) {
this._y -= 10;
} else if (Key.isDown(Key.DOWN)) {
this._y += 10;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
_root функции приемника событий Key, а также установкой события onKeyDown на начало движения шарика. Добавьте следующие четыре строки в функцию init и затем создайте функцию serve в конце всего кода.Key.addListener(_root);
_root.onKeyDown = serve;
pcServe = true;
baseSpeed = 10;
function serve() {
if (Key.getCode(j == Key.SPACE) {
if (playerServe) {
ball_mc.velX = -baseSpeed;
ball_mc.velY = Math.random()*20-10;
playerServe = false;
} else if (pcServe) {
ball_mc.velX = baseSpeed;
ball_mc.velY = Math.random()*20-10;
pcServe = false;
}
}
}
Теперь при запуске игры, шарик не движется до тех пор, пока вы не нажмете пробел. Когда значением pcServe станет "истина", будет выполнен второй блок, и шарик направится к ракетке игрока.
Это значит, что шарик должен находиться ниже верхнего края ракетки, но выше нижнего ее края. Если он находится между этими двумя точками при ударе о right, значит, есть попадание. Если шарик пролетел мимо, он исчезает. Здесь мы могли бы применить старый добрый hitTest, однако я воспользуюсь этим случаем для представления новой концепции и действия под названием getBounds. Это одна из редко используемых команд. Она как бы разбивает hitTest на составные части и позволяет использовать именно те из них, которые необходимы.
getBounds - это метод объекта movieClip. При вызове он возвращает объект. Этот объект имеет четыре параметра, которые сообщают нам о местоположении фильма. Вот способ использования данного метода.
boundsObject=movieClip.getBounds (scope);
boundsObject имеет следующие четыре параметра: xMin, xMax, yMin и yMax. Они логически соответствуют четырем краям граничного прямоугольника фильма. Однако, вместо того, чтобы заставлять Flash вычислять, что находится внутри, а что вне рамки, мы теперь можем работать с численными значениями. Теперь можно математическим путем определять наши собственные коллизии, пользуясь значением ширины и т.п., как мы делали в случае со стенками.
Единственное, что осталось здесь определить, это параметр scope (область). Параметры min и max возвращаемого объекта boundsObject представлены в пикселях, но относительно чего? Это и есть наша область. В основном вам нужно будет вводить _root в качестве области, или любую другую позицию на временной диаграмме, на которой вы осуществляете проверку коллизий. Это даст вам значения X и Y фильма из _root.
Если вы сделали сам фильм областью, он будет возвращать значения своих углов такими, каковы они есть с его точки зрения. Например, квадрат со стороной 100 пикселей с центральной точкой регистрации всегда будет возвращать значения -50 и 50 в качестве минимального и максимального значений, независимо от того, в каком месте экрана находится фильм. С точки зрения фильма, он распространяется на 50 пикселей вверх от центра, на 50 пикселей вниз, на 50 влево и на 50 вправо.

_root. Добавьте следующую новую строку в функцию move.function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
Теперь playerBounds будет содержать четыре параметра, упомянутые ранее. Параметры, которые нас больше всего интересуют - это yMin и yMax. Если ball_mc._y находится между этими двумя значениями после передачи им точки, определенной выражением right-(this._width/2), то он ударяется о ракетку игрока.

AND (). Имейте в виду, что при этом также учитывается ширина шарика, поэтому мы реализуем проверку не только относительно центра шарика. Таким образом, мы получим гораздо более реалистичный удар.function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
upper = playerBounds.yMin - this._height/2;
lower = playerBounds.yMax + this._height/2;
if (this._y>upper this._y<lower) {
this.velX *= -1;
this.velY += Math.random()*20 - 10;
this._x = right-(this._width/2);
}
}
}
if (this._y>upper this._y<lower) {
this.velX *= -1;
this.velY += Math.random()*20-10;
this._x = right-(this._width/2);
baseSpeed *= 1.01;
this.velX *= 1.01;
}
else.function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
if (this._y>playerBounds.yMin this._y<playerBounds.yMax) {
this.velX *= -1;
this._x = right-(this._width/2);
} else {
this._x = 275;
this._y = 200;
this.velX = 0;
this.velY = 0;
pcScore++;
playerServe = true;
}
}
Здесь вы можете реализовать все, что вам придет в голову. Мне хватило и того, что я добавил pcScore.

init();
function init() {
RIGHT = 500;
LEFT = 50;
TOP = 50;
BOTTOM = 350;
ball_mc.velX = 0;
ball_mc.velY = 0;
ball_mc.onEnterFrame = move;
pc_mc. onEnterFrame = pcMove;
player_mc.onEnterFrame = playerMove;
Key.addListener(_root);
_root.onKeyDown = serve;
pcServe = true;
baseSpeed = 10;
}
function move() {
this._x += this.velX;
this._y += this.velY;
if (this._x>right-(this._width/2)) {
playerBounds = player_mc.getBounds(_root);
upper = playerBounds.yMin-this._height/2;
lower = playerBounds.yMax+this._height/2;
if (this._y>upper this._y<lower) {
this.velX *= -1;
this.velY += Math.random()*20-10;
this._x = right-(this._width/2);
baseSpeed *= 1.01;
this.velX *= 1.01;
} else {
this._x = 275;
this._y = 200;
this.velX = 0;
this.velY = 0;
pcScore++;
playerServe = true;
}
}
if (this._x<left+(this._width/2)) {
pcBounds = pc_mc.getBounds (_root);
if (this._y>pcBounds.yMin-this._height/2
Кthis._y<pcBounds.yMax+this._height/2) {
this.velX *= -1;
this.velY += Math.random()*20-10;
this._x = left+(this._width/2);
baseSpeed *= 1.01;
this.velX *= 1.01;
} else {
this._x = 275;
this._y = 200;
this.velX = 0;
this.velY = 0;
playerScore++;
pcServe = true;
}
}
if (this._y>bottom-(this._height/2)) {
this.velY *= -1;
this._y = bottom-(this._height/2);
}
if (this._y<top+(this._height/2)) {
this.velY *= -1;
this._y = top+(this._height/2);
}
}
function pcMove() {
if (ball_mc.velX<0) {
this._y += (ball_mc._y-this._y)/5;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
function playerMove() {
if (Key.isDown(Key.UP)) {
this._y -= 10;
} else if (Key.isDown(Key.DOWN)) {
this._y += 10;
}
if (this._y>bottom-this._height/2) {
this._y = bottom-this._height/2;
this.vy = 0;
}
if (this._y<top+this._height/2) {
this._y = top+this._height/2;
this.vy = 0;
}
}
function serve() {
if (Key.getCode() == Key. SPACE) {
if (playerServe) {
ball_mc.velX = -baseSpeed;
ball_mc. velY = Math.random()*20-10;
playerServe = false;
} else if (pcServe) {
ball_mc.velX = baseSpeed;
ball_mc. vel Y = Math.random()*20-10;
pcServe = false;
}
}
}
Я продублировал рассмотренные шаги для проверки попадания в другую ракетку, а также реализовал отображение очков игрока в playerScore. Теперь вы можете и поиграть.
Можете смело экспериментировать с этой игрой. В коде на диске есть несколько дополнений, однако вы сами можете создать очень много интересного.
Это последний метод обнаружения коллизий, который мы рассмотрим. Он полностью подходит для идеально круглых объектов, хотя можно немного схитрить и применить его к тем объектам, которые имеют более-менее округлую форму.
Ранее мы говорили о том, что если два объекта находятся примерно на одной позиции, можно считать, что они находятся в состоянии столкновения. Иными словами, расстояние между двумя объектами равно нулю. Создадим два круглых фильма и будем вычислять расстояние между ними. Для этого мы применим теорему Пифагора. Теорема гласит, что сумма квадратов двух меньших сторон прямоугольного треугольника равна квадрату большей стороны треугольника. Смотрите.

Я пометил стороны буквами x, y и d. Теорема Пифагора гласит.
x2 + y2 = d2
Т.е.:
x*x + y*y = d*d
Теперь, если провести некоторые элементарные алгебраические преобразования, получим следующее.
d = Math.sqrt (x*x + y*y);
Это значит, что если возвести в квадрат обе стороны, сложить их и затем извлечь квадратный корень, получим длину гипотенузы. Теперь немного изменим наш рисунок, и вам все станет ясно.

На рисунке показано, что dx - это расстояние по оси x между mc1_mc и mc2_mc. dy - это расстояние по оси y, а dist - это среднее расстояние между ними. Зная все это, можно интерпретировать данные выводы следующим образом с помощью обычного кода.
dx = mc2_mc._x-mc1_mc._x; dy = mc2_mc._y-mc1_mc._y; dist = Math.sqrt (dx*dx+dy*dy);
Это еще одна шпаргалка для вас. Запишите эти выражения, они вам пригодятся.
Ранее я говорил, что когда расстояние между двумя фильмами обращается в ноль, объекты сталкиваются. Однако вспомните последнее упражнение с игрой в пинг-понг, где нужно было учитывать размер шарика. Теперь нам нужно брать в расчет размеры обоих фильмов. В нашем случае на данном этапе расстояние между фильмами в 0 пикселей означало бы полное совмещение центров двух шариков, и только в этом случае было бы зарегистрировано столкновение. Это уж слишком!
В последний раз мы просто брали радиус шарика для определения его состояния столкновения. Здесь мы сделаем то же самое, однако вычислим радиус одного шарика и прибавим к нему радиус другого. Если расстояние между шариками будет меньше, чем эта величина, будет регистрироваться столкновение. Красота этого метода состоит в том, что не имеет значения размер фильмов, так как мы будем определять радиусы динамически.

Приведем код для выяснения того, произошло столкновение или нет.
dx=mc2_mc._x-mc1_mc._x;
dy=mc2_mc._y-mc1_mc._y;
dist=Math.sqrt(dx*dx+dy*dy);
if (dist<mc1_mc._width/2 + mc2_mc._width/2){
// you have a hit!
}
То, что будет происходить после
Это будет еще одна небольшая игра, что-то наподобие Atari's Asteroids, однако вы можете превратить эту игру во все, что придет вам в голову.
ball и отметьте опцию Export for ActionScript в окне Linkage Properties. Введите ball.player_mc.init с помощью цикла for. В нем мы присваиваем препятствиям случайные позиции, скорости и размеры, азатем присваиваем управляющий элемент onEnterFrame. Какой продуктивный цикл! Добавим все это в отдельный слой с именем actions.init();
function init() {
for (i=0; i<10; i++) {
ball_mc = attachMovie("ball", "b"+i, i);
ball_mc._x = Math.random()*550;
ball_mc._y = Math.random()*400;
ball_mc.velX = Math.random()*10-5;
ball_mc.velY = Math.random()*10-5;
ball_mc._xscale = Math.random()*40+10;
ball_mc._yscale = Math.random()*40+10;
ball_mc.onEnterFrame = ballMove;
}
}
ballMove.function ballMove() {
this._x += this.velX;
this._y += this.velY;
if (this._x>550) {
this._x = 0;
}
if (this._y<0) {
this._x = 550;
}
if (this._y>400) {
this._y = 0;
}
if (this._y<0 {
this._y = 400;
}
}
Это уже вам хорошо знакомо. Здесь скорость прибавляется к позиции и проверяется, не ушел ли шарик за пределы фильма. Если да, шарик возвращается на противоположный край рабочего места. Если хотите, можете сделать так, что шарик будет отскакивать от стенки, использовав код, разработанный нами в предыдущем упражнении.
player_mc.onEnterFrame. Сначала присвоим его в конце функции init.player_mc.onEnterFrame = playerMove;
function playerMove() {
this._x += (_root._xmouse-this._x)/20;
this._y += (_root._ymouse-this._y)/20;
}
player_mc в каждом кадре.dx = player_mc._x-this._x;
dy = player_mc._y-this._y;
dist = Math, sqrt (dx*dx+dy*dy);
if (dist<player_mc._width/2+this._width/2) {
player_mc._visible = false;
}
Единственной реакцией на столкновение будет исчезновение player_mc, однако после регистрации столкновения нам нужно, чтобы можно было продолжить игру без перезапуска самого фильма. Для этого мы добавим следующую функцию определения в конец функции init, чтобы player_mc появлялся снова после щелчка мыши.
player_mc. onMouseDown = function() {
this._visible = true;
};
init();
function init() {
for (i=0; i<10; i++) {
ball_mc = attachMovie("ball", "b"+i, i);
ball_mc._x = Math.random 0*550;
ball_mc._y = Math.random 0*400;
ball_mc.velX = Math.random 0*10-5;
ball_mc.velY = Math.random 0*10-5;
ball_mc._xscale = Math.random()*40+10;
ball_mc._yscale = Math.random()*40+10;
ball_mc.onEnterFrame = ballMove;
player_mc.onEnterFrame = playerMove;
player_mc. onMouseDown = function() {
this._visible = true;
};
}
}
function ballMove() {
this._x += this.velX;
this._y += this.velY;
if (this._x>550) {
this._x = 0;
}
if (this._y<0 {
this._x = 550;
}
if (this._y>400) {
this._y = 0;
}
if (this._y<0) {
this._y = 400;
}
dx = player_mc._x-this._x;
dy = player_mc._y-this._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<player_mc._width/2+this._width/2) {
player_mc._visible = false;
}
}
function playerMove() {
this._x += (_root._xmouse-this._x)/20;
this._y += (_root._ymouse-this._y)/20;
}
В следующей лекции мы будем изучать применение некоторых математических и физических принципов, но пока я хочу рассказать еще об одном моменте. Это стратегия поддержки столкновений большого числа объектов.
До сих пор мы рассматривали столкновение одного объекта с другим объектом или столкновение одного объекта с несколькими объектами. В игре, в которой мы стреляем по космическим кораблям пришельцев, корабли используют цикл for для проверки всех снарядов по очереди. Как вариант, можно было бы реализовать в снарядах проверку попадания во все корабли по очереди, однако здесь нам бы потребовалось знать количество активных в данный момент кораблей, а также их имена. Это возможно, но первый способ проще.
В последнем упражнении мы могли бы "перевернуть" проверку коллизий для объекта player. В нем можно было бы использовать цикл for, как и в космических кораблях, и проверять каждый меньший шарик. Это реализуется в функции playerMove следующим образом.
for (i=0; i<10; i++) {
dx = this._x-_root["b"+i]._x;
dy = this._y-_root["b"+i]._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<this._width/2+_root["b"+i]._width/2) {
this._visible = false;
}
}
Этот вариант работает отлично, однако мы обеспечиваем лишь проверку одного объекта относительно одного, или одного относительно нескольких объектов. А что делать в случае, если у нас имеется набор движущихся объектов, и каждый из них должен реагировать на столкновение с любым другим объектом? Можно было бы добавить в каждый из них цикл for, как показано выше, чтобы каждый объект был обработан циклом для проверки на столкновение с любым другим объектом, однако это было бы очень неэффективно. Представьте себе следующий сценарий.
Имеется три объекта: A, B и C. A проверяет столкновение между собой и B, затем столкновение между собой и C; B проверяет столкновение между собой и A, а также между собой и C; наконец, C осуществляет проверку относительно A и B. Итак, мы имеем:
Мы получили шесть вариантов, но это вдвое больше, чем нужно. A:B - это то же самое, что и B:A, A:C идентично C:A, а B:C - то же, что и C:B.
По мере увеличения числа объектов такая проверка теряет смысл, производя в любом случае в два раза больше проверок, нежели это требуется. Поэтому нам нужен более эффективный способ реализации проверки, т.е. процедура, которая проверяет пару объектов только один раз. Приведем наилучший, на наш взгляд, метод осуществления проверок на столкновение. Он довольно короток и ясен.
Вместо поддержки объектов самих по себе, которую мы использовали до сих пор, лучше поддерживать их на главной временной диаграмме. Самый удобный способ осуществить это - использовать массив. При создании каждого объекта мы добавляем его в массив, и тогда все наши объекты будут находиться в одном массиве для простоты доступа и управления.
ball в Library и сохраните его под другим именем. Удалите все инстансы с рабочего места, а также весь имеющийся код. Начните с добавления в кадр 1 слоя actions этого кода.init();
function init() {
max = 10;
balls_array = new Array();
for (n=0; n<max; n++) {
balls_array[n] = attachMovie("ball", "b"+n, n) ;
balls_array[n]._x = Math.random 0*550;
balls_array[n]._y = Math.random()*400;
balls_array[n].velX = Math.random()*10-5;
balls_array[n].velY = Math.random()*10-5;
balls_array[n]._xscale = balls_array[n]._yscale =
Math.random()*50+30;
}
}
Здесь мы установили значение max, которое будет определять число создаваемых шариков. Затем создается массив для хранения в нем фильмов. Цикл для присвоения фильмов практически идентичен предыдущему файлу, за исключением того, что он присваивает фильм элементу массива и осуществляет доступ к нему через этот элемент для присвоения параметров. Мы также опустили управляющий элемент onEnterFrame, так как управление фильмами будет осуществляться извне.
onEnterFrame в _root. Сначала реализуем присвоение в функции init (под строкой max = 10; ):_root.onEnterFrame = main;
main, в которой и будет заключаться весь смысл. Сначала обрабатываем циклом массив для передвижения каждого шарика по отдельности. Здесь все довольно просто.function main() {
for (i=0; i<balls_array.length; i++) {
balls_array[i] ._x += balls_array[i] .velX;
balls_array[i] ._y += balls_array[i] .velY;
if (balls[i]_array._x>550) {
balls[i]_array._x = 0;
}
if (balls[i]_array._x<0) {
balls[i]_array._x = 550;
}
if (balls[i]_array._y>400) {
balls[i]_array._y = 0;
}
if (balls[i]_array._y<0) {
balls[i]_array._y = 400;
}
}
}
Выглядит знакомо? Так и должно быть. Это тот же самый код, что и ранее, однако здесь мы осуществляем доступ к фильму извне через элемент массива, в котором он сохранен, вместо внутреннего доступа с использованием this. Запуститe фильм. Вы увидите десять кругов, в случайном порядке двигающихся по экрану. Теперь перейдем к
for. Внешний цикл будет обрабатывать каждый элемент массива (содержащий ссылку на фильм), а внутренний цикл будет использоваться для проверки этого элемента относительно каждого последующего элемента в массиве. Приведем псевдокод, чтобы показать, как это реализуется.for (i=0; i<balls_array, length-1; i++) {
for (j=i+l; j<balls_array. length; j++) {
// test between balls_array[i] and balls_array[j] here
}
}
Это еще одна ваша шпаргалка. Мы будем использовать код проверки коллизий на основе окружностей/расстояния для проверки столкновения шариков. В случае столкновения оба шарика будут удаляться.
array.splice (index, number). Эта команда удаляет число number элементов из массива, начиная с index. Измените функцию main следующим образом.function main() {
for (i=0; i<balls_array.length; i++) {
balls_array[i]._x += balls_array[i].velX;
balls_array[i]._y += balls_array[i].velY;
if (balls_array[i]._x>550) {
balls_array[i]._x = 0;
}
if (balls_array[i]._x<0) {
balls_array[i]._x = 550;
}
if (balls_array[i]._y>400) {
balls_array[i]._y = 0;
}
if (balls_array[i]._y<0) {
balls_array[i]._y = 400;
}
}
for (i=0; i<balls_array.length-1; i++) {
for (j=i+1; j<balls_array.length; j++) {
dx = balls_array[i]._x-balls_array[j]._x;
dy = balls_array[i]._y-balls_array[j]._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<balls_array[i]._width/2+ balls_array [j]._width/2) {
removeMovieClip(balls_array[j]);
balls_array.splice(j, 1);
removeMovieClip(balls_array[i]);
balls_array. splice(i, 1);
}
}
}
}
onMouseDown в начале функции init._root.onMouseDown = createBall;
function createBall() {
ball_mc = attachMovie("ball", "b"+n, n++);
ball_mc._x = _root._xmouse;
ball_mc._y = _root._ymouse;
ball_mc.velX = Math.random()*10-5;
ball_mc.velY = Math.random()*10-5;
ball_mc._xscale = Math.random()*50+30;
ball_mc._yscale = Math.random()*50+30;
ball_mc.onEnterFrame = ballMove;
bal1s_array.push(ball_mc);
}
Это похоже на код, использованный в init для создания исходных фильмов ball. Основное отличие заключается в том, что здесь ссылка на фильм записывается во временную переменную, присваиваются все его параметры, и затем используется array.push для добавления его в массив.
Интересным моментом при настройке наших циклов в функции main является то, что мы использовали balls_array.length для определения числа повторений. Параметр length массива будет обновляться при добавлении и удалении объектов из массива, поэтому наши циклы будут всегда выполняться нужное число раз.
init();
function init () {
max = 10;
_root.onEnterFrame = main;
_root.onMousedown = createBall;
balls_array = new Array();
for (n=0; n<max; n++) {
balls_array[n] = attachMovie("ball", "b"+n, n);
balls_array[n]._x = Math.random()*550;
balls_array[n]._y = Math.random()*400;
balls_array[n].velX = Math.random()*10-5;
balls_array[n].velY = Math.random()*10-5 ;
balls_array[n]._xscale = balls_array[n]._yscale= Math. random 0*50+30;
}
}
function main() {
for (i=0; i<balls_array.length; i++) {
balls_array[i]._x += balls_array[i].velX;
balls_array[i]._y += balls_array[i].velY;
if (balls_array[i]._x>550) {
balls_array[i]._x = 0;
}
if (balls_array[i]._x<0) {
balls_array[i]._x = 550;
}
if (balls_array[i]._y>400) {
bal1s_array[i]._y = 0;
}
if (balls_array[i]._y<0) {
balls_array[i]._y = 400;
}
}
for (i=0; i<balls_array.length-1; i++) {
for (j=i+1; j<balls_array.length; j++) {
dx = balls_array[i]._x-balls_array[j]._x;
dy = balls_array[i]._y-balls_array[j]._y;
dist = Math.sqrt(dx*dx+dy*dy);
if (dist<balls_array[i]._width/2+ balls_array[j] ._width/2) {
removeMovieClip(balls_array[j]);
balls_array.splice(j, 1);
removeMovieClip(balls_array[i]);
balls_array.splice(i, 1);
}
}
}
}
function createBall() {
ball_mc = attachMovie("ball", "b"+n, n++);
ball_mc._x = _root._xmouse;
ball_mc._y = _root._ymouse;
ball_mc.velX = Math.random()*10-5;
ball_mc.velY = Math.random()*10-5;
ball_mc._xscale = Math.random()*50+30;
ball_mc._yscale = Math.random()*50+30;
ball_mc.onEnterFrame - ballMove;
balls_array.push(ball_mc);
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.