Игра на развитие памяти

Дедукция

Исходный файл:
Следующая игра полностью основана на логике. Один из наиболее популярных ее вариантов (для двух игроков) известен под названием "Mastermind".
Цель игры - угадать произвольную последовательность из пяти цветов. Игрок начинает с предположений. Затем компьютер отвечает, отгадано ли хоть что-то, то есть игрок получает информацию о количестве правильно расположенных цветов и количестве правильных цветов, оказавшихся не на своих местах. На основе этой информации игрок пробует угадать еще раз. Так продолжается определенное число попыток или до тех пор, пока игрок не угадает последовательность.
На Рисунок 14.3 показан фрагмент этой игры (см. ролик Deduction.fla,). Область игры - выпиленное полено, а цвета представлены в виде камешков. Игрок может выбрать любой из пяти цветных камешков, чтобы заполнить пустое пространство, или не выбрать вообще. Компьютер отвечает следующим образом: отображает белый камешек для каждого правильного цвета, и черный камешек для каждого правильного цвета, помещенного не на свое место.


Другие возможности



Другие возможности

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




Другие возможности



Другие возможности


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




Другие возможности



Другие возможности


Один момент в этой игре пропущен - способ узнавать, когда игра закончилась. Вы можете поместить кнопку "I'm Done!" в кадре и дать возможность игроку решать, когда игра закончена.
Однако можно использовать ActionScript, чтобы определить два возможных пути завершения игры. Первый, это когда все блоки удалены. Это редкость; большинство игр оставляют несколько неудаляемых блоков. Но, если это случится, вы можете определить это, выясняя, не пуст ли самый нижний левый блок после того, как функция collapseDown запушена.
if (_root["block 0 11"]._currentFrame == 5) {

gotoAndStop("gameOver");

}
Большинство игр оставляет несколько блоков, не соседствующих ни с одним блоком своего цвета. Определить такой случай будет более трудной задачей.
Проще всего это сделать с помощью цикла по всем блокам, пропуская пустые и проверяя непустые, не соседствует ли с ними блок такого же цвета. Если есть хоть один такой блок, игра не считается законченной.




Другие возможности



Другие возможности


Тема игры может быть любой. Достаточно изменить фон и вид элементов. Количество элементов зависит о того, насколько сложной вы хотите сделать игру.




Игра "Йога"



Игра "Йога"


Исходный файл:
Классическая игра "Йога" известна уже тысячи лет. Современные версии сделаны из дешевого пластика. С развитием Web-технологий появился и виртуальный вариант этой игры.
Для игры требуется решетка с отверстиями для колышков (Рисунок 14.7). Колышки расположены во всех отверстиях, кроме одного. Игрок должен взять один колышек и "перепрыгнуть" через другой, при этом попасть на пустое место. Колышек, через который "перепрыгнули", при этом удаляется. Игра продолжается до тех пор, пока нельзя будет сделать больше ни одного передвижения.


Игра на дедукцию позволяет построить



Рисунок 14.3 Игра на дедукцию позволяет построить последовательность из камешков, затем выводит результаты с помощью белых и черных камешков


Игра на развитие памяти



Игра на развитие памяти


Исходный файл:
Иногда эту игру называют "Simon", потому что именно так называлось популярное электронное устройство, которое позволяло в нее играть. В игре имеется четыре разных фрагмента, которые проигрываются в произвольном порядке. Вы можете это увидеть, запустив файл Memory.fla. При проигрывании фрагмента загорается световое табло и воспроизводится звуковой сигнал. Элементами этой игры являются четыре птицы, сидящие на ветке (Рисунок 14.1).


Игра начинается с того, что отображаются пять свободных пустых мест и кнопка Done



Рисунок 14.4 Игра начинается с того, что отображаются пять свободных пустых мест и кнопка Done

Игра начинается с того, что отображаются пять свободных пустых мест и кнопка Done

Игрок может щелкнуть по любому пустому месту, чтобы изменить его цвет. Цвет изменяется в соответствии с шестью вариантами: пятью различными цветами и пустым местом.
После того как игрок указал цвета для пяти мест, он может щелкнуть по кнопке Done, чтобы получить результат. Появление белого камешка свидетельствует о правильно расположенном цвете, черного - о правильном цвете, помешенном не на свое место.
Затем под имеющимся рядом отображаются следующие пять свободных мест вместе с кнопкой Done. На Рисунок 14.5 показано, как выглядит игра после нескольких шагов.


Игра проиграна, код поместил правильную последовательность решения внизу экрана



Рисунок 14.6 Игра проиграна, код поместил правильную последовательность решения внизу экрана

Игра проиграна, код поместил правильную последовательность решения внизу экрана

Единственный оставшийся фрагмент кода - небольшой сценарий, прикрепленный к кнопке в каждом клипе "rock". Его задача состоит в том, чтобы переключать цвета один за другим и возвращаться к первому кадру (пустому месту), если пользователь щелкнул определенное число раз. Этот код также проверяет свойство active, чтобы убедиться, что ряд - текущий.
on (press) {

if (active) {

f = _currentFrame+1;

if (f > 6) f = 1;
gotoAndStop(f);
}}


Игра "Рекурсивные блоки" начинается со случайной решетки цветных блоков Задача проекта



Рисунок 14.8 Игра "Рекурсивные блоки" начинается со случайной решетки цветных блоков


К сведению



К сведению


He забудьте присвоить последним двум кадрам клипа "rock" метки "white” и "black". Также обязательно поместите кнопку Done внутрь клипа "done», которому потом необходимо присвоить имя "done" в панели Linkage Properties. К сожалению, это необходимо, так как ActionScript не может динамически создавать кнопки сами по себе. Считайте, что клип "done" - это просто "обертка" для кнопки Done.


К сведению



К сведению

В игре имеется поле score, помешенное под игровой областью. Оно отслеживает переменную score, которая увеличивается в процессе исполнения функции clickBlock.


К сведению



К сведению


Во всех шести кадрах ролика должна быть команда stop (). В кадрах со второго по шестой это единственная команда.
В кадрах "wait", "correct" и "wrong" находятся кнопки. В кадре "wait" кнопка переводит игру к кадру "play", где проигрывается последовательность мелодий. Кнопка кадра "correct" снова переносит игрока к кадру "play". Однако также должна быть вызвана функция addNewNote, чтобы добавить сообщение в последовательность.
on (press) {

addNewNote();

gotoAndPlay("play");
}
Кнопка в кадре "wrong" переводит ролик к кадру "start", где можно заново начать игру.
Также не забудьте добавить текстовое поле, связанное с переменной scoreDisplay, чтобы игроки знали о результатах своей игры.


Наиболее распространенная конфигурация доски с колышками. Вы можете создать свой вариант



Рисунок 14.7 Наиболее распространенная конфигурация доски с колышками. Вы можете создать свой вариант

Наиболее распространенная конфигурация доски с колышками. Вы можете создать свой вариант


Хороший игрок может выиграть, если у него останется только один колышек. При быстрой игре, без раздумий, может остаться примерно от 8 до 12 колышков.


Подготовка ролика



Подготовка ролика


Для игры требуется всего два клипа: "peg" и "hole". В клипе "peg" должна быть расположена кнопка, которая сообщает основной временной шкале о щелчке мышью (пользователь нажимает и отпускает ее кнопку). Так как перед началом ролика на рабочем поле нет ни одного клипа, им необходимо назначить свойства связи. Код обращается к ним по именами "peg" и "hole".


Подготовка ролика



Подготовка ролика


Ролик содержит довольно мало элементов, за исключением клипа в библиотеке, представляющего блоки. Этот клип содержит 4 кадра, по кадру для каждого цвета. Также в каждом кадре есть маленькая картинка. Пятый кадр пустой и представляет пустое место в решетке. Первые четыре блока имеют кнопку, чтобы по ним можно было щелкнуть. Клипу необходимо присвоить имя в панели Linkage Properties, поскольку он не находится изначально на рабочем поле.
Главная временная шкала ролика включает в себя два кадра: первый, содержащий инструкции и кнопку Play, а второй - это кадр "Play".


Подготовка ролика В клипе "rock"




Подготовка ролика В клипе
Теперь осталось поместить на рабочее поле фон и указания. Камешки и кнопка Done создаются кодом. Для клипа "rock" установите имя ("rock") в панели Linkage Properties. Я также создал небольшую кнопку, чтобы можно было перейти внутрь клипа камешка. Кнопку "0опе"создать очень просто, но так как нам нужно управлять ее положением, мы должны поместить ее внутри клипа и указать для него свойство "linkage".


Подготовка ролика



Подготовка ролика


В ролике шесть кадров ( Рисунок 14.2). Из них кадры под номерами 3 и 4 -ключевые, они называются "play" и "repeat" соответственно. В кадре "play" воспроизводится чириканье птиц, а в кадре "repeat" игрок может щелкать по птицам.


Подход



Подход


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


Подход



Подход


Последовательность мелодий хранится в массиве. Каждый раз в массив добавляется новый, случайный номер от 1 до 4. Номер определяет, какая птица должна чирикать.
В первой части игры воспроизводится чириканье птиц. Этот кадр управляется клипом "actions", который анализирует номера в последовательности и сообщает программе, какая птица когда должна петь.
В следующей части игра ждет, когда пользователь начнет щелкать по птицам. После каждого щелчка проверяется, правильно ли была выбрана птица. Таким образом программа все время следит за пользователем, ожидая момента, когда он ошибется. Если игрок сделал все правильно, в последовательность добавляется новая мелодия, и все начинается заново.


Рекурсивные блоки



Рекурсивные блоки


Исходный файл:
Я встречал эту игру в разных вариациях под разными названиями. Основная идея ее в том, что экран заполнен большой решеткой, состоящей из цветных блоков. Игрок может нажать на блок, чтобы удалить его из решетки. Но чтобы удаление блока стало возможным, он должен иметь сверху, снизу или рядом соседний блок того же цвета.
Когда игрок нажимает на доступный для удаления блок, он убирается. К тому же, убирается и соседний блок того же цвета. Все соседи одного цвета с удаленным блоком удаляются тоже. Таким образом, связанная область блоков одного цвета может быть удалена нажатием на любой из блоков этой области.
После удаления группы блоков их верхние соседи спускаются вниз на освободившиеся места. Если какая-нибудь колонка блоков оказывается совершенно пустой, решетка сдвигается влево, чтобы заполнить свободные места.
На Рисунок 14.8 показано начало игры. В добавление к одному из четырех цветов я поместил на каждый блок маленький рисунок, соответствующий цвету блока. Это облегчает распознавание блоков на черно-белом рисунке. Единственный способ действительно понять игру- это сыграть в нее, поэтому сделайте это прямо сейчас, прежде чем продолжать читать дальше.


Шесть кадров игры на развитие памяти: "start", "wait", "play", "repeat", "correct" и "wrong"



Рисунок 14.2 Шесть кадров игры на развитие памяти: "start", "wait", "play", "repeat", "correct" и "wrong"

Шесть кадров игры на развитие памяти:


Клип каждой птицы состоит из трех частей. Первая - непосредственно клип с самой птицей, первый кадр которого представляет собой изображение, второй и остальные кадры - анимация (птица чирикает, открывая и закрывая рот). Ролик начинается с того, что клип каждой птицы находится в первом кадре.
Вторая часть клипа - кнопка. Одна и та же кнопка используется для каждой птицы, но к ней прикреплен разный код. Кнопка расположена за изображением птицы, так что ее не видно.
Третья часть клипа каждой птицы - просто анимация, показывающая сообщение, которое «вылетает» из клюва птицы. Этот простой клип используется для каждой птицы один раз. Экземпляр анимации сообщения и птица соответственно. Например, первая птица называется «bird1», а сообщение – “note1”. В анимации сообщения первый кадр пустой, там анимация останавливается. Во втором кадре анимации происходит возврат к первому кадру. В соответствующее время мы инициализируем сообщение, которое «вылетает» из клюва птицы.
Если вы внимательно посмотрите на элемент "Bird Button" из исходного ролика, вы увидите, что кадры "Up", "Over" и "Down" пусты, а в кадре "Hit" содержится контур птицы. Это значит, что никакого изображения на рабочем поле для кнопки не будет, а область нажатия кнопки будет совпадать с формой кадра "Hit". Такой способ создания невидимой кнопки отличается от уже описанного, когда создается кнопка и ее значение _alpha обнуляется.
Если рассмотреть анимацию птиц, вы увидите, что звук прикреплен к каждой из них. Звук автоматически воспроизводится при проигрывании анимации с птицей. Каждый звук немного отличается от другого.
В различных кадрах появляются различные части клипов птиц. Клипы
с изображениями самих птиц представлены во всех шести кадрах. Однако кнопки появляются только в кадре "repeat", потому что только там
игрок может щелкать мышкой по птицам. Анимация сообщения появляется только в кадрах под номерами 3-6, в первом и втором кадрах она не нужна.
Также в каждом кадре ролика содержатся указания, что должен делать пользователь. В верхнем правом углу всех кадров, кроме первого, находится текстовое поле scoreDisplay.


Создание кода



Создание кода


Код игры целиком расположен в первом кадре основной временной шкалы, начинается он с установки целого набора переменных. Эти числа определяют положение различных элементов на рабочем поле при их создании. Вместо того чтобы распределять эти переменные по всему коду, я собрал их в начале программы - так их будет проще найти и изменить.
Последовательность решения задается с помощью пяти случайных чисел и хранится в массиве solution. В переменной row хранится номер попытки. Для начала игры вызывается функция createRow.
initGame();

stop() ;


function initGame() {

// Определяется расположение и расстояние между элементами.

topRowLoc = 60;

leftColLoc = 280;

roghtColLoc = 390;

horizSpace = 21;

vertSpace = 23 ;

solutionx = 336;

solutiony = 320;

// Создается произвольная последовательность решения

solution = new Array();

for(i=0;i
solution.push(int(Math.Random()*5)) ;

}

// Инициализируются переменные.

rows = new Array();

row = 0;

// Устанавливается первый ряд.

createRow();

}
В начале каждой попытки функция createRow создает пять пустых мест и определяет их положение. Вдобавок устанавливается новый клип done, который располагается справа (Рисунок 14.4).
function createRow() {

// Создается новый ряд из пяти пустых мест.

attachMovie("rock","rock"+row+"-"+i,row*10+i);

clip = _root["rock"+row+"-"+i];

clip._x = leftColLoc + i*horizSpace;

clip._y = topRowLoc + row*vertSpace;

clip, active = true;

}

// Создается кнопка Done.

attachMovie("done","done",1000);

_root.done._x = rightColLoc+20;
_root.done._y = topRowLoc + row*vertSpace;

}
Когда игрок щелкает по кнопке Done, вызывается функция doneGuess. Сначала пять кнопок в текущем ряду становятся неактивными. В функции createRow свойству active каждого клипа было присвоено значение true. Теперь же этому свойству присваивается значение false (1). Код, прикрепленный к каждой кнопке, с помощью этого свойства определяет, можно ли щелкнуть по кнопке или нет.
В массиве temp содержатся номера каждого цвета последовательности (2). Например, если даны два первых цвета, один четвертый и два пятых, массив будет выглядеть следующим образом: [2,0,0,1,2].
Следующий цикл проверяет, сколько цветов точно совпадают с предопределенным расположением (3). Если были найдены совпадения, числа в массиве temp уменьшаются. То есть теперь в этом массиве отображаются те цвета, совпадения с которыми еще надо найти.
Следующий цикл проверяет несовпавшие цвета в последовательности игрока и определяет, какие из этих цветов находятся в массиве temp (4). Подсчитав, можно выяснить, сколько цветов правильно угаданы, но помешены не на свое место.
Следующие два цикла создают белые и черные камешки, которые будут соответствовать числу правильно угаданных цветов и числу угаданных цветов, расположенных не на своем месте (5).
Кнопка Done удаляется, так что теперь она не будет появляться в каждой строке (6). Когда пользователю дается следующая попытка, создается новая кнопка Done.
В конце функции проверяется, совпадают ли все пять цветов или нет (7). Если да, то ролик переходит к кадру "win". В противном случае код проверяет, была ли эта попытка десятой (последней), и если так, ролик перейдет к кадру "lose", иначе для следующей попытки вызывается функция createRow.



function doneGuess() {

numRightSpot = 0;

numRightColor = 0;

(1) // Пять кнопок становятся неактивными,

for (i=0;i

_root ["rock"+row+"-"+i] .active = false;

}

(2) // Определяем, сколько имеется кнопок одного цвета,

temp = [0,0,0,0,0] ;

for (i=0;i

temp[solution[i] ]++;}

(3)// Выясняем, сколько цветов правильно угадано,

for (i=0;i

color = _root["rock"+row+"-"+i]._currentFrame - 2;

if (color == solution[i]) {

numRightSpot++;

temp[color]--;

}}

(4)// Проверяем, сколько цветов угадано,

// но находится не на своих местах.

for (i=0;i

color = _root["rock"+row+"- currentFrame - 2;

if (color != solution[i]) {

if (temp[color] > 0) {

numRightColor++;

temp[color]--;

}
}}

(5)// Создаем белые камешки.

level = row*10+5;

х = rightColLoc;

for(i=0;i

attachMovie("rock","white rock"+level,level)

clip = _root["white rock"+level];

clip.gotoAndStop("white");

clip._x = x;

clip._y = topRowLoc + row*vertSpace;

level++;

x+=horizSpace;

}

// Создаем черные камешки.

for(i=0;i

attachMovie("rock","black rock"+level,level);

clip = _root["black rock" + level] ;

clip.gotoAndStop ("black") ;

clip._x = x;

clip._y = topRowLoc + row*vertSpace;

level++;

x+=horizSpace;

}

(6)// Удаляем кнопку Done.

done.removeMovieClip()

(7)// Выясняем, выиграл ли игрок,

if (numRightSpot ==5) {

gotoAndPlay("win");

} else {

row++;

// Проверяем, проиграл ли игрок,
if (row >= 10) {

showSolution();

gotoAndPlay("lose");

} else {

createRow();

}}}

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

function showSolution() {

// Размещаем правильный вариант внизу экрана.

for(i=0;i

attachMovie("rock","solution"+i,1001 + i) ;

clip = _root["solution"+i];

clip._x = solutionx + i*horizSpace;

clip._y = solutiony;

clip.foroAndStop(solution[i]+2);
}}

На Рисунок 14.6 показан фрагмент ролика, когда игра уже проиграна. Функция showSolution поместила правильную последовательность (решение) внизу экрана.


Создание кода



Создание кода


Почти весь код находится в одном кадре основной временной шкале. Он начинается с создания экземпляров клипов "peg" и "hole" и их размещения на рабочем поле. С помощью оператора if создаются необходимые для игры отверстия. Также код проверяет, во все ли отверстия, кроме одного в центре, вставлены колышки.
В конце функции переменной maxHole присваивается значение, равное количеству всех отверстиях. Эта' переменная будет использоваться в тех функциях, где необходимо выяснить, все ли клипы удовлетворяют определенным условиям.
initGame();

stop();

function initGame() {

// Определяем постоянное расположение клипов.

holeSpace =30;

puzzleLeft = 160;

puzzleTop = 80;

// Просматриваем все отверстия создаем в них колышки.

i = 0;

for(y=0;y
for (x=0;x
// Проверяем, должен ли быть создан колышек.

// Если да, создаем его.

if (((у < 3) or (у > 5)) and ((х < 3) or (x > 5) continue;


// Добавляем и размещаем новое отверстие. attachMovie("hole", "hole"+i, i);

_root["hole"+i]._x = x*holeSpace + puzzleLeft;

_root["hole"+i]._y - y*holeSpace + puzzleTop;

II He добавляем колышек в центральное отверстие,
if ((x != 4) or (у != 4)) {

// Добавляем и размещаем

// новое отверстие.

attachMovie("peg","peg"+i,100+i);

_root["peg"+i]._x = x*holaSpace + puzzleLeft;

_root["peg"+i]._y = y*holaSpace + puzzleTop;
}

i++;
}}

// Запоминаем количество колышков.
maxHole = i;
}
Когда игрок щелкает по колышку, на самом деле он щелкает по кнопке, расположенной внутри клипа. Оба действия, "press" и "release", передаются функциям в основной временной шкале. Функция dragPeg вызывается действием "press". Эта функция сохраняет положение колышка в переменных pegх и pegу, а затем разрешает перемешать колышек. Также она использует функцию swapDepths, чтобы колышек отображался поверх всех остальных.
function dragPeg(peg) {
// Запоминаем исходное положение колышка,
pegх = peg._х; pegу = peg._у;
// Размещаем колышек поверх остальных,
peg.swapDepths(2000);
// Разрешаем программе Flash перемещать клип.
startDrag(peg, true);
}



Функция dropPeg использует полезную функцию pegThere, которой передается расположение отверстия и которая возвращает значение true, если именно в этом месте обнаружен клип "peg".

function pegThere(x.у) {
// Выясняем, находится ли в этом месте колышек,
for (i=0;i peg = _root["peg"+i];
if ((peg._x == x) and (peg._y == y)) {
return true;
}}
return false;
}

Во время игры внизу экрана все время находится кнопка Restart Game (Начать заново), которая позволяет заново начать игру, когда больше нельзя сделать ни одного действия или когда пользователь понимает, что плохо начал игру. Эта кнопка должна не только вызывать функцию initGame, но и очищать оставшиеся клипы "peg" и "hole".

function restartGamef) {
// Удаляем все клипы,
for (i=0;i _root["peg"+i].removeMovieClip();
_root["hole"+i].removeMovieClip();
}
// Начинаем новую игру.
initGame();
}

Код, прикрепленный к кнопке, расположенной внутри клипа "peg» очень прост. Все, что он делает, это передает функциям действия press и release.

on (press) {
_root.dragPeg(this);
}


on (release) {
_root.dropPeg(this);
}


Создание кода



Создание кода



Создание блоков происходит в функции startGame. Функция совершает циклы по 20 горизонтальным колонкам и 12 вертикальным рядам и создает 240 блоков. Каждый блок - это новый экземпляр клипа "block", созданного функцией attachMovie. Кроме того, чтобы оказаться в нужном месте рабочего поля, каждый блок обладает свойствами x и у, таким образом, позже он сможет определить свое положение.
Выбирается случайное число от 1 до 4, и клип с блоком отправляется к этому кадру.
function startGaine () {

// Переходим к кадру игры.

gotoAndStop("Play");

score = 0;

// Создаем решетку из блоков,
level = 0;

for(var x=0;x
forlvar y=0;y
mc = _root.attachMovie("block","block "+x+" "+y,level);

mc._x = 20*x + 85;

mc._y = 20*y + 60;

mс.x = x ;

mс.у = у ;

// Выбираем случайный цвет.

mc.gotoAndStop(Math.ceil(Math.random()*4));

level++;

}}
}
Когда пользователь шелкает по блоку, чтобы выбрать его, короткий сценарий посылает свойства х и у блока функции, чтобы программа могла решить, какие действия предпринять
on(release) {

_root.clickBlock(x,у);

}
Функция clickBlock определяет цвет блоков и хранит его в переменной Затем блок удаляется (точнее, его клип переходит к пустому кадру). Функция testNeighbor вызывается четыре раза, один раз для каждого соседа. Функция возвращает количество подходящих соседей, найденных в данном направлении. Если, например, сосед сверху того же цвета и три его соседа тоже, возвращается число 4. Добавляя это число к переменной n, вы получаете полное количество блоков в группе.
Если обнаружено, что группа состоит более чем из одного блока, вызываются функции collapseDown и collapseAcross, чтобы удалить все блоки группы. В противном случае выделенный блок возвращается к прежнему цвету, и кажется, что ничего не произошло.
// Произошел щелчок по блоку,

function clickBlock(x,у) {

Обратите внимание на ключевое слово var перед описанием переменной с. Это делает с локальной переменной, существующей только в функции clickBlock. Любое использование с за пределами функции clickBlock будет отнесено к самостоятельной переменной. При применении рекурсивных техник, когда функция вызывает саму себя или похожую функцию, лучше использовать локальные переменные, чтобы переменные не наступали друг другу на пятки, когда программа движется от функции к функции.



// Определяем цвет этого блока.

var с = _root["block "+x+" "+y]._currentframe;

_root["block "+x+" "+y].gotoAndStop(5);

II Проверяем его соседей по всем направлениям.

var n = 1;

n += testNeighbor(х-1,у,с);

n += testNeighbor(х+1,у,с);

n += testNeighbor(х,у-1,с);

n += testNeighbor(х,у+1,с);

// Если один их них того же цвета,

if (n > 1) {

// Уничтожаем группу блоков.

collapseDown();

collapseAcross();

// Начисляем очки,

score += n*n;

} else {

// Оставляем блок на месте.

_root["block "+x+" "+y].gotoAndStop(с);

}}

Функция testNeighbor проверяет цвет нового блока. Если цвет подходит, блок удаляется. Дальше функция вызывает сама себя и проверяет цвета четырех соседних блоков. Она следит за переменной n, которая определяет количество подходящих соседей.
Если текущий блок не подходит по цвету, вместо n возвращается 0, чтобы показать, что в этом направлении не было найдено ни одного подходящего блока.

function testNeighbor(x,у,с) {

if (_root["block "+x+" "+y]._current frame = = с) {

// Удаляем соседа.

_root [ "block "+x+""+y].gotoAndStop(5);

var n = 1;

// Проверяем всех его соседей,

n += testNeighbor(x-1,y,с);
n += testNeighbor(х+1,у,с);

n += testNeighbor(х,у-1,с);

n += testNeighbor(x,y+l,с);

// Возвращаем количество найденных соседей,

return(n);

} else {

// Не найдено никаких подходящих блоков.

return(0);

}}

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

function collapseDown() {

// Проходим по всем столбцам.

for(var x=0;x

// Проходим по всем блокам столбца снизу вверх.

for(var y=11;y>0;y--) {

// Смотрим, пустой ли блок.
thisColor = _root["block "+x+'"+y]._currentFrame;



if (thisColor == 5) {

// Если да, просматриваем все блоки над ним.

for(var i=y-1;i> = 0;i —- ) {

// Если блок не пустой.

aboveCoior = _root[ "block "+x+" ,"+i] ._currentframe;

if (aboveCoior != 5) {

// Смещаем его вниз.

_root["block "+x+""+y].gotoAndStop(aboveCoior);

_root["block "+x+" "+i].gotoAndStop(5)

break;

}}}}}}

После того как все блоки передвинуты, может оказаться, что некоторые столбцы пустые. Если так произошло, весь столбец справа от пустого должен быть сдвинут влево, чтобы закрыть промежуток.
Чтобы это осуществить, функция collapseAcross движется сквозь все столбцы слева направо. Если найден пустой столбец, за которым следует непустой, то последний сдвигается влево на одну позицию.
Этот процесс повторятся снова и снова, пока не перестанут попадаться пустые столбцы. Тогда цикл оканчивается, и функция выполнена.

function collapseAcross() {

// Продолжаем, пока есть пустые столбцы,

do {

n = 0;

// Проверяем все столбцы,

for(var x=0;x

// Если текущий столбец пустой.

if (_root["block "+x+" 11"]._currentframe = = 5) {

// Если следующий - полный, if (_root["block "+(x+1)+" 11"] _currentframe != 5)

{ n++;

// Смещаем все блоки.

for(var у=0,-у

c = _root["block "+(x+1)+" "+y]._currentframe;

_root["block "+x+" "+y].gotoAndStop(c);

_root["block "+(x+1)+" "+y].gotoAndStop(5);

}}}}
// цикол прекращается, если больше не обнаружено пустых столбцов

} while (n > 0);

}


Создание кода



Создание кода


Код этого ролика делится на две части. Первая часть - набор функций, расположенных в первом кадре ролика. Вторая часть находится в клипе "playback actions", который управляет воспроизведением мелодий в кадре "play". Небольшие фрагменты кода есть и в других местах. Функции, используемые в этой ролике, расположены в первом кадре основной временной шкалы. Они инициализируют переменные игры, отвечают за реакцию на щелчки мышью и воспроизводят мелодии.
Сначала в игре вызывается функция initGame, которая устанавливает все переменные. Эта функция вызывается в начале игры всегда и в конце, если игрок хочет сыграть заново, то есть снова переходит к первому кадру.
Функция initGame очищает массив notes, затем вызывает функцию addNextNote, чтобы поместить в этот массив случайное число. Также она указывает, что счет игры должен отображаться равным 0.
initGame();

stop();

function initGame() {

// Очищаем массив сообщений,

notes = new Array();

scoreDisplay = "Score: 0";

// Добавляем первое сообщение.

addNewNote();

}
Функция addNextNote случайным образом выбирает номер кадра от 1 до 4 и помещает его в массив notes.
function addNextNote() {

// Выбираем случайное число из диапазона от 1 до 4.

r = int(Math.Random()*4+1);

// Добавляем число в массив,

notes.push(r);

}
Остальные функции этой игры используются позже. Функция startRepeat вызывается тогда, когда игрок прослушал пение птиц и должен щелкать по их изображениям, чтобы воспроизвести соответствующие мелодии.
Переменная repeatNum отслеживает, по какому элементу должен щелкнуть игрок. Изначально ее значение равно 0, а затем оно изменяется каждый раз, когда пользователь щелкает по птице.
function startRepeat() {

// Определяем, по какому элементу

// должен теперь щелкнуть пользователь.

repeatNum = 0;

// Переходим к кадру, где размещены кнопки.

gotoAndPlay("repeat");

}
Функция clickBird вызывается теми кнопками, которые спрятаны за изображениями птиц; ей передается номер элемента, по которому щелкнули. Сначала функция проигрывает анимацию птицы и соответствующую анимацию пения. Затем сравнивает сообщение со следующим сообщением в списке. Если они совпадают, значит, игрок правильно выбрал птицу, и значение переменной repeatNum увеличивается. Если это было последнее сообщение, игра переходит к кадру "correct". В противном случае функция ждет, пока не выберут следующее сообщение. Если сообщение не совпадает с предопределенным, ролик переходит к кадру "wrong", и игра заканчивается.



function clickBird(note) {

// Воспроизводим анимацию птицы.

_root["bird"+note].gotoAndPlay(2);

// Воспроизводим анимацию сообщения.
_root["note"+note].gotoAndPlay(2);

// Проверяем, правильно ли был выбран элемент,

if (note == notes[repeatNum]) {

// Ожидаем воспроизведения следующей мелодии.

repeatNum++;

// Если мелодий больше нет, игрок правильно угадал

// последовательность.

if (repeatNum > notes.length-1) {

scoreDisplay = "Score: " + notes.length;

gotoAndPlay("correct ");

}

} else {

// Игрок ошибся. gotoAndPlay("wrong");

}}

Кадр "play" проигрывает каждую мелодию из последовательности в соответствии со сценарием, прикрепленным к клипу "playback actions". Сценарий начинается с того, что устанавливает две переменные. Переменная noteNum следит за тем, какое сообщение должно быть проиграно следующим, а переменная nextTime - когда оно должно быть проиграно.

onClipEvent(load) {

// Начинаем с мелодии, под номером 0.

noteNum = 0;

nextTime = 0 ;

}

На каждом шаге ролика в кадре "play" текущее время сравнивается со значением переменной nextTime. Если текущее время превышает это значение, проигрывается следующая мелодия. Анимация пения и птицы инициализируются точно так же, как если бы они вызывались при щелчке мышью. Затем изменяются значения переменных noteNum и nextTime, чтобы подготовиться к воспроизведению следующей мелодии. Переменная nextTime увеличивается на 1000, то есть следующая мелодия будет проиграна через одну секунду. Когда будут проиграны все сообщения, ролик вызовет функцию startRepeat, которая была рассмотрена ранее. То есть игра перейдет к кадру "repeat" и будет ждать, когда пользователь попытается восстановить последовательность.

onClipEvent(enterFrame) {

// Выясняем, пришло ли время проиграть следующую мелодию,

if (getTimerO > nextTime) {

// Получение сообщения,

note = _root.notes[noteNum];

// Проигрывается анимация пения

//и соответствующий звуковой файл.

_root["bird"+note].gotoAndPlay(2);

_root[Mnote"+note].gotoAndPlay(2);

// Ждем одну секунду, прежде чем проиграть следующую

// мелодию.

nextTime = getTimer() + 1000;

noteNUm++;

// Если больше сообщений нет, продолжаем со следующего

// шага.

if (noteNum > _root.notes.length) {

_root.startRepeat();}

}}

Каждая кнопка, расположенная на рабочем поле в кадре "repeat", при щелчке по ней должна вызывать функцию clickBird. Каждый из четырех сценариев немного отличается от остальных, так как они должны передать функции различные числа. Вот сценарий для одной из кнопок:

on (press) {
clickBird (1);
}


Так выглядит игровое поде после нескольких попыток пользователя Подготовка ролика



Рисунок 14.5 Так выглядит игровое поде после нескольких попыток пользователя


В игре на развитие памяти на экране показаны четыре птицы



Рисунок 14.1 В игре на развитие памяти на экране показаны четыре птицы

В игре на развитие памяти на экране показаны четыре птицы


В игре две основные фазы. Первая фаза - птицы чирикают в определенной последовательности. Во второй фазе пользователь пытается воссоздать эту последовательность/Затем фазы повторяются, при этом добавляется еще одна мелодия. Очень скоро последовательность становитсянастолько длинной, что пользователь не может ее воссоздать, и игра заканчивается.


Решетка представляет собой серию клипов,



Подход

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

Решетка представляет собой серию клипов,
Метод удаления блоков является рекурсивным. Каждый сосед выделенного курсором блока проверяется, какого он цвета. Если того же; проверяется каждый его сосед, и т. д. Этот рекурсивный метод в конце концов находит блок, который не имеет соседей того же цвета, и рекурсия заканчивается.
Если вы не вполне уверены, что понимаете, как работает эта рекурсия, посмотрите пример шаг за шагом. Например, пользователь щелкнул по красному блоку. Из четырех его соседей блок сверху и блок справа тоже красные. Возьмите блок сверху. Среди его соседей нет красных блоков. Возьмите блок справа. Блок снизу от него тоже красный. Возьмите этот третий красный блок. Среди его соседей нет красных блоков. Таким образом, блок, по которому пользователь щелкнул, удаляется. Удаляется также блок сверху, блок справа и блок снизу от блока справа.
Хотя игра не определяет, сделал ли пользователь все возможное, она присуждает очки. Лучший способ определения очков в подобной игре -это экспоненциальная система присуждения очков. Таким образом, если удаляется группа из двух блоков, игрок получает 4 очка: 2 раза по 2. Если удаляется группа из 3 блоков, игрок получает 3 раза по 3, или 9 очков. Если же удаляется группа из 4 блоков, игрок получает 4 раза по 4, или 16 очков.
Эта система подсчета очков поощряет игрока находить большие группы блоков. Например, удаление 10 групп по 2 блока принесет пользователю 40 очков (2 раза по 2 будет 4, 10 раз по 4 будет 40). Удаление же одной группы из 20 блоков приносит пользователю 400 очков (20 раз по 20). Пользователь должен не только искать большие группы блоков, но пытаться сам их создавать, вытаскивая маленькие группы таким образом, чтобы большие группы пришли в соприкосновение и сформировали еще большую группу.

с создания произвольной последовательности из



Подход

Игра начинается с создания произвольной последовательности из пяти цветов, которая хранится компьютером в тайне до конца игры.

с создания произвольной последовательности из
При каждой попытке игроку предоставляется пять новых пустых мест, которые нужно заполнить. В дополнение к ним справа появляется кнопка Done (Готово). На Рисунок 14.4 показано, что видит игрок в начале игры.

и перемешает колышки. Неверный ход



Задача проекта

Цель проекта - создать компьютерный вариант игры "Йога" (Рисунок 14.7). Игрок щелкает мышкой и перемешает колышки. Неверный ход сделать нельзя, а правильный автоматически удаляет элемент, через который "перепрыгнули".

Игровой автомат

Другие возможности



Другие возможности


В более сложных игровых автоматах в одном окне могут находиться сразу несколько символов. В некоторых автоматах символы отображаются также и по вертикали, так что игрок может выиграть, если совпадут символы по трем горизонтальным линиям и двум диагоналям. Иногда игрок может выбирать, сколько монет (от одной до пяти) "вставить" в автомат, чтобы определить потенциальный выигрыш (он будет зависеть от величины ставки).
Так как Flash-ролик - не игра на деньги, а просто развлечение, то более важно предусмотреть возможность изменения графики для соответствия различным темам Web-сайтов. Например, для детского сайта можно сделать картинки с конфетами. Изменяя внешний вид автомата и картинки, одну и ту же программу можно использовать для оформления разных сайтов.




Другие возможности



Другие возможности


Суммы, указанные в функции winnings, я выбрал, руководствуясь обычными правилами покера. Однако вы можете указать другие числа в зависимости от той суммы, которую хотите переводить на счет виртуального казино или игрока.
Можно сделать и так: когда на счету игрока не остается денег, переходить, например, к кадру game over.
Еше одна вариация игры - позволить игроку ставить от одного до пяти долларов за игру. Таким образом, игрок ставит меньшую сумму, когда чувствует, что ему не везет, и большую - в других случаях.




Другие возможности



Другие возможности


Эта игра очень хороша для изучения языка ActionScript, но она разочарует игроков в двадцать одно, играющих на деньги. Поэтому для хороших ActionScript-профаммистов далее представлено руководство, как добавить некоторые возможности этой игры, которые были опущены.
Легче всего добавить возможность раздачи только двух карт. Для этого нужно создать кнопку Double (Удваивание ставки) в кадре с меткой "Player". Когда игрок щелкнет по ней, ему дадут еще одну карту, ставка еще раз будет вычтена из суммы наличных денег, и игра перейдет к раздающему. Однако прежде следует убедиться, что игрок не взял какие-либо дополнительные карты, поскольку такая раздача возможна только тогда, когда у игрока на руках первые две карты.
Возможность страхования создать немного сложнее, так как при этом надо написать еше одну "ветвь" программы. Страхование возникает тогда, когда раздающему показывается туз. В этом случае игрок может застраховаться от того, что у раздающего будет 21, на сумму, обычно равную ставке. Если было взято страхование, и у раздающего 21 очко, игра заканчивается. Игрок теряет исходную ставку, но получает страховую сумму.
Разделение - сложное дополнение к игре. Если у игрока две карты одного достоинства, например две девятки, тогда ему может быть разрешено разделить карты на два расклада, каждый из которых будет начинаться с девятки. Следовательно, чтобы можно было хранить карты игрока в одном массиве playerHand, вы должны создать массив из массивов. В большинстве случаев в таком массиве playerHand содержится один массив, который отражает расклад на одной руке. Если игрок разделит карты, то в массиве будут содержаться два массива. Игроку обычно позволяется разделять карты несколько раз, таким образом, в массиве playerHand может содержаться три, четыре и более массивов. Затем нужно сыграть с каждым раскладом игрока. Вы понимаете, насколько это усложняет каждую часть игры. Игрок может выиграть, проиграть или сыграть в ничью по нескольким или всем своим раскладам.




Другие возможности



Другие возможности


Игру можно сделать значительно проще, если вы разрешите игроку переворачивать колоду уже открытых карт и смотреть карты столько раз, сколько он захочет. Вы можете сделать это, опознавая, когда массив deck пуст, и помещая каждую карту из массива stack обратно в массив deck.
Кроме этого пасьянса вы можете создать много других. В моей коллекции более 200 вариантов подобных игр. Большинство из них требует вытаскивания карт и перекладывания из стопки в стопку. Это делает код гораздо более сложным, но не невозможным для опытного программиста.




Игра в очко, или двадцать одно



Игра в очко, или двадцать одно


Исходный файл:
Двадцать одно - еще одна популярная карточная игра в казино, которую легко можно перенести на компьютер. Раздающий карты следуют определенному набору правил, следовательно, можно написать программу, которая будет имитировать действия раздающего.


Игровой автомат



Игровой автомат


Исходный файл:
Игровой автомат - игра функционально простая, но с довольно сложным интерфейсом. Игрок просто щелкает по рычагу игрового автомата и ждет результата. Автомат сам выполняет всю оставшуюся работу.
На Рисунок 15.1 показан фрагмент ролика Slotmachine.fla. Рычаг справа единственный элемент, который будет реагировать на действия игрока. Когда игровой автомат останавливается, в трех окошках отображаются картинки.


Эти кадры анимаиии создают эффект крутящегося барабана при быстром воспроизведении



Рисунок 15.2 Эти кадры анимаиии создают эффект крутящегося барабана при быстром воспроизведении

Эти кадры анимаиии создают эффект крутящегося барабана при быстром воспроизведении


В каждом из трех окон будет отображаться копия клипа spin. Когда придет время вращения барабана, всем трем клипам будет сообщаться о воспроизведении, а также передаваться точное число вращений. Эти значения будут различными для каждого клипа, так что барабаны будут останавливаться не одновременно.
Просмотрите ролик Slotmachine.fla, чтобы увидеть, как крутятся барабаны и как они последовательно останавливаются.


К сведению



К сведению


Если вы посмотрите на ролик, который приводится в качестве примера, то увидите, что фон игрового автомата на самом деле располагается на переднем плане. Три окна - это "дырки" в изображении, сквозь них видно, как проигрывается анимация вращения, а также видны символы на барабанах.
Вы можете указать действия, которые будут совершаться, если деньги на счету игрока закончатся. Можно проверять это условие и, если оно оказывается истинным, переходить к другому кадру. Можно сообщить игроку, что игра окончена, либо предоставить еще 100 долларов, чтобы игрок сыграл еще раз.


К сведению



К сведению


К каждой кнопке Hold/Draw прикреплен собственный фрагмент кода. Для первой кнопки он выглядит следующим образом:
on (press) { holdDraw(0);}
Цифра 0 сообщает функции holdDraw, что действие производится с нулевой картой, для остальных четырех кнопок вместо нуля стоят числа от 1 до 4.
Хотя первый кадр ролика сразу же вызывает функцию startGame (см. раздел "Создание кода"), второй кадр должен вызывать функцию startDeal. Просмотрите ролик, приведенный в качестве примера, чтобы самостоятельно изучить, как это реализовано.


К сведению



К сведению


Основная временная шкала этого ролика - самая сложная из всех игр, рассмотренных в этой книге. Поэтому важно просмотреть ролик на Web-сайте, чтобы получить четкое представление о том, что и где расположено.
Также необходимо создать текстовые поля, в которых будут отображаться сумма очков игрока и раздающего, ставка, сумма наличных и результат.


К сведению



К сведению


Кнопка New на экране имеет простой сценарий и сначала вызывает clearGame, а потом startGame. Это перезапускает игру в любой удобный пользователю момент.
on (press) {

clearGame();

startGame();

}


Пасьянс "Пирамида"



Пасьянс "Пирамида"


Исходный файл:
Пасьянс "Пирамида" не так популярен, как обычный пасьянс, и не так сложен; однако, ему легко научиться и он очень затягивает. На Рисунок 15.7 . представлено начало игры.
Пасьянс

Рисунок 15.7 В пасьянс "Пирамида" играют с помощью пирамиды из 28 карт и остальной колоды


Играть можно любой полностью открытой картой. Если часть карты закрыта другой картой пирамиды, играть этой картой можно только в том случае, если закрывающую ее карту удалить. Удалить карту игрок может только в том случае, если найдет подходящую ей другую карту, чтобы сумма их значений равнялась бы 13. Например, 10 и 3 или 6 и 7 могут составить пару. Туз имеет значение 1, валет значение 11, дама 12 и король 13. Это значит, что только король может быть выбран сам по себе, без второй карты.
Цель, естественно, в том, чтобы убрать из пирамиды все карты. Оставшиеся 24 карты колоды помешены под пирамидой, и за один ход можно перевернуть одну карту. Любая перевернутая карта может быть выбрана вместе с картой из пирамиды так, чтобы составить в сумме число 13.
Попробуйте поиграть в этот пасьянс. Вам придется отложить книгу, поскольку понадобится потратить какое-то время, чтобы окончить игру.


Подготовка ролика



Подготовка ролика


В этом ролике используются три ключевых библиотечных эталона, все остальное - элементы фона. Первый эталон - рычаг, в его первом кадре находится кнопка, по которой может щелкнуть игрок. Остальная часть клипа - анимация рычага, показывающая, как нажали рычаг.
Клип "spin" начинается с пустого кадра, где он "ждет", пока игрок не щелкнет по рычагу. Кадр прозрачный, так что сквозь него виден клип "symbols".
В остальных кадрах клипа "spin" находится размытая анимация. За изображениями, показанными на Рисунок 15.2, расположен белый непрозрачный фон, закрывающий клип "symbols" во время вращения колес.
В клипе "symbols" находятся семь кадров, во всех содержатся разные символы. В ролике Slotmachine.fla это изображения желудя, яблока, листа, бревна, луны, солнца и лисы.


Подготовка ролика



Подготовка ролика


Основной библиотечный элемент в данной игре - колода карт, то есть клип с 54 кадрами. Первый кадр отображает пустой контур карты. Во втором кадре содержится рубашка карты. Кадры 3-54 показывают различные карты колоды. Каждый кадр имеет такое же имя, какое используется в коде для идентификации карт. Например, "c1" - туз треф и "h11" -валет червей.
Просмотрите ролик Videopoker.fla и вы увидите, что для клипа "deck" выделена целая папка библиотеки, заполненная графическими изображениями. Это упрощает повторное использование элементов для разных карт.
Ролик составлен из пяти экземпляров клипа "deck" (колода), которые называются "card0", "card1", "card2", "card3" и "card4". В первом кадре
нужна только кнопка "Deal" (Раздать), которая отображается также и в третьем кадре.
Во втором кадре находится кнопка "Draw" (Выигрыш), а под каждой картой - кнопка "Hold/Replace" (Оставить/Поменять).


Подготовка ролика



Подготовка ролика


В отличие от видеопокера на руках может быть от двух до 11 карт. Для каждого расклада создаются 11 экземпляров клипов, имена которых изменяются от "player0" до "player10" и от "dealer0" до "dealer10". Кадры клипа "deck" - пусты. Следовательно, когда вы поместите клип на рабочее поле, вы не увидите ничего, кроме меток, которые устанавливаются программой Flash для клипов по умолчанию. На Рисунок 15.5 показаны все 22 метки.


Подготовка ролика



Подготовка ролика


Ролик содержит в библиотеке такой же клип "deck", как в игре "Двадцать одно". Ему необходимо присвоить имя в панели Linkage Properties,чтобы он экспортировался вместе с роликом. То же относится и к клипу "outline". ' В игре присутствует только два кадра: кадр "Play" и кадр "Game over". Последний будет использоваться только в редком случае выигрыша. Оба кадра должны содержать кнопку New, чтобы пересдавать карты в любой удобный игроку момент.


Подход



Подход


Единственной сложной частью кода в игре является вращение. Осуществить это можно следующим образом: сделать так, чтобы различные картинки прокручивались в окошке снизу вверх. Проблема заключается в том, что картинки должны вертеться так же быстро, как крутятся барабаны в реальном игровом автомате. На большинстве компьютеров программа Flash не может проигрывать ролик с такой скоростью. Вместо этого можно воспользоваться размытой анимацией вращения, которая представляет собой клип, состоящий из нескольких "размытых" кадров. На Рисунок 15.2 показаны несколько подобных кадров.


Подход



Подход


Первое, что необходимо программе, - это перетасованная колода карт, которая будет представлять собой массив с символьными данными, например "h7", что означает семерка червей. Четыре масти представлены буквами "с", "d", "h" и "s". Туз обозначен "1", то есть «c1" означает туз треф, а валет, дама и король обозначены цифрами "И", "12" и "13" соответственно.
Создать упорядоченную колоду карт просто, совсем другое дело - создание перетасованной колоды. Для этого вы берете упорядоченный массив и, выбирая из него случайным образом элементы, один за другим помешаете их в новый массив.
Затем первые пять карт в массиве отображаются на экране. Под каждой картой располагается кнопка. Один щелчок мыши по ней переворачивает карту, так что будет видна рубашка карты. Второй щелчок возвращает карту в исходное положение на тот случай, если игрок передумал.
Когда игрок будет удовлетворен своим выбором, он щелкает по кнопке Draw (Поменять). Те карты, которые были выбраны длят замены, замешаются следующими картами из перетасованной колоды.
Самый сложный фрагмент программного кода нужен для конечной фазы игры. Окончательный массив из пяти карт должен быль оценен с точки зрения покера. Вот список возможных вариантов:
• младшая пара - пара карт одного достоинства (десятка или ниже);

• старшая пара - пара валетов, дам, королей или тузов;

• тройка - три карты одного достоинства;

• стрит - пять карт, ранг каждой из которых на единицу превышает ранг предыдущей карты, например восьмерка, девятка, десятка, валет и дама. Туз может располагаться как перед двойкой, так и послекороля;

• флэш - пять карт одной масти;

• фул хаус - пара и три карты одного достоинства;

• каре - четыре карты одного достоинства;

• стрейт флэш - пять карт одной масти, ранг каждой из которых на единицу превышает ранг предыдущей карт;

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


Подход



Подход


Как и в видеопокере, здесь есть массив deck, в котором содержится перетасованная колода карт. Разница заключается в том, что в этой игре в массиве находятся шесть колод. В игре в очко такой массив называется shoe.
Как для игрока, так и для раздающего создается массив, в котором представлены карты, имеющиеся на руках. Вам нужно будет подумать насчет первой карты раздающего, так как она не должна быть видна до тех пор, пока игрок не закончит набирать карты.
В отличие от видеопокера оценка расклада в этой игре проста. Единственная сложность: туз может стоить как одно, так и 11 очков. Однако, так как два туза дадут 22 очка, второй туз никак не может стоить 11. То есть все, что нужно, - определить, есть ли на руках туз, подсчитать, что он равен единице, а затем добавить 10, если при этом на руках не будет перебора. Например, расклад с тройкой, девяткой и тузом рассматривается как 13 очков, потому что если считать, что туз стоит 11, то на руках окажется 23 очка, то есть перебор.


Подход



Подход


Мы используем такую же колоду, как и в игре "Двадцать одно". Игра начинается с тасовки карт и выстраивания пирамиды. Остальные карты помешаются стопкой мастью вниз внизу экрана. Игрок может щелкнуть, по верхней карте в стопке, и карта отобразится мастью вверх в соседней стопке карт, которая изначально была пустой. Каждый щелчок по стопке карт, лежащих мастью вниз, добавляет одну карту к стопке карт, лежащих мастью вверх.
Игрок может выбрать любую незакрытую карту из пирамиды или из стопки карт с открытой мастью. Выбранная карта выделяется рамкой.
Когда игрок щелкает по следующей карте, она сравнивается с первой, чтобы узнать, составит ли она с ней вместе 13 очков. Если да, обе карты удаляются. Если при этом одна из карт находится в стопке карт мастью вверх, то лежащая под ней карта открывается.
Определить, открыта ли какая-то карта в пирамиде, можно, пройдя циклом по всем картам в пирамиде и выясняя, присутствует ли на месте одна
из двух карт, которые должны закрывать данную. Например, вторая карта в третьем ряду должна быть закрыта второй и третьей картой в четвертом ряду. Если какая-нибудь из них на месте, карта не может быть выбрана. Когда карта удаляется из стопки открытых карт, становится видна предыдущая открытая карта. Это означает, что вам нужно следить за стопкой открытых карт, помещая их в массив, как только они оказываются перевернутыми.
Особым случаем является король. Выбранный король немедленно удаляется как из колоды карт с открытой мастью, так и из пирамиды, если, конечно, выбранный король был открыт.


Простой игровой автомат с рычагом и тремя окошками



Рисунок 15.1 Простой игровой автомат с рычагом и тремя окошками

Простой игровой автомат с рычагом и тремя окошками


выигрыша, этот предельно прост. Игрок щелкает по рычагу, чтобы привести его в действие. Затем барабаны в трех окнах начинают крутиться. Один за другим они будут останавливаться, показывая произвольный элемент (картинку).
Когда все три барабана останавливаются, картинки, оказавшиеся в них, и определяют результат. Выигрыш соответствует тем суммам, которые показаны внизу игрового автомата (см. Рисунок 15.1).


Расклад, когда раздающий выиграл, так как у него 21, а у игрока только 18 очков



Рисунок 15.4 Расклад, когда раздающий выиграл, так как у него 21, а у игрока только 18 очков

Расклад, когда раздающий выиграл, так как у него 21, а у игрока только 18 очков


Если с первыми двумя картами у игрока 21 очко, он сразу же выигрывает и получает дополнительно 50% суммы выигрыша. Если у раздающего получается 21, игра сразу же приостанавливается. Если же так не случилось, то выигрывает тот, у кого на руках большее количество очков, не превышающее 21.
Игрок может контролировать свои ставки (от 5 до 25 долларов), каждый раз повышая ставку на пять долларов.


Создание кода



Создание кода


Большая часть кода для этой игры находится в основной временной шкале. Однако сначала разберем те фрагменты кода, которые относятся к клипам.
В ролике Slotmachine.fla под клипом с рычагом размещена кнопка. К ней прикреплен простой сценарий, который вызывает функцию pull, находящуюся в основной временной шкале.
on (release) {
pull();
}
В клипе "spin" содержатся два небольших сценария. Команда stop () находится в первом .кадре. Сценарий, который уменьшает значение счетчика numTimes на единицу, располагается в последнем кадре. Если значение счетчика равно нулю, сценарий переходит к первому кадру ролика и сообщает об этом функции в основной временной шкале. В противном случае клип снова возвращается ко второму кадру.
numTimes--;

if (numTimes
gotoAndStop(l);

_root.spinDone(_name);

} else {

gotoAndPlay(2); }

Большой фрагмент программы в основной временной шкале определяет, сколько денег выиграл пользователь, если выиграл вообще. Программа начинается с того, что случайным образом выбирает кадр для трех символов и предоставляет игроку $100, на которые он может играть в этой виртуальной игре.
initGame() ;

stop();

function initGame() {

// Случайным образом выбираем кадры для трех символов,

for (i=l;i
_root["symbol"+i].gotoAndStop(randomSymbol());

}

// Начинаем со $100.

cash = 100;

showCash();

}
Вместо того чтобы отображать сумму, имеющуюся у игрока, просто цифрами, следующая функция помещает перед ней знак доллара и добавляет переменную cash в displayCash. Эта переменная связана с текстовым полем на рабочем поле.
// Отображаем сумму в долларах,
function showCash() {

cashDisplay.= "$"+cash;

}
Когда игрок щелкает по рычагу, происходит несколько событий. Сначала из суммы наличных денег игрока вычитается $1. Затем клип "arm' переходит ко второму кадру, то есть показывается, что рычаг переходит в нижнее положение. Затем все три клипа "spin" начинают проигрываться. Каждому клипу сообщаются различные значения переменной numTimes - 8,6 и 4 - то есть первая анимация будет проигрываться восемь раз, вторая - шесть и третья - четыре. Также здесь определяется результат каждого вращения, устанавливаются клипы "symbol", хотя они скрыты за непрозрачными анимациями "spin" до тех пор, пока эти анимации не будут проиграны.



// Рычаг переходит в нижнее положение,

function pull(); {

// Когда рычаг приводится в действие,
// каждый раз вычитаем один доллар.
cash--;

showCash() ;

// Воспроизводим анимацию рычага.

_root["arm"].gotoAndPlay(2);

// Воспроизводим анимацию вращения,

for (i=l; i

// Сообщаем, сколько раз нужно вращать барабан.

_root["spin"+i].numTimes = 8-i*2;

_root["spin"+i].gotoAndPlay(2); }

// Выбираем результат для каждого символа случайным образом.

for (i=l; i

_root["symbol"+i].gotoAndStop(randomSymbol());
}}

Запрограммировать случайный выбор символа непросто. Если вероятность выбора каждого из семи символов одинакова, тогда вероятность Я вывода трех совпадений одинакова для любого символа. Другими словами, три изображения желудя появляются так же часто, как и три изображения лисы. То есть не имеет смысла выплачивать различные суммы за совпадения различных картинок.

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

Но вероятность появления одного символа должна отличаться от вероятности появления другого символа. Например, символ с бревном появляется в 29% случаев. С другой стороны, символ лисы появляется только в 3% случаев.
Это осуществляется с помощью массива, куда записывается вероятность появления того или иного символа. Затем произвольным образом выбирается число из этого массива. Если оно попадает в диапазон между нулем и вероятностью появления первого символа, выбирается первый символ. В противном случае программа переходит к следующему числу в массиве. Так происходит до тех пор, пока какой-нибудь символ не будет выбран. Просмотрите следующий код, чтобы увидеть, как работает эта схема.

// Выбираем произвольный символ в зависимости

//от вероятности его появления.

function randomSymbol() {

// Задаем вероятность появления каждого символа.

chances = [29,21,16,12,9,7,6];



// Определяем сумму вероятностей.

totalChances = 0;

for (j=0; j

totalChances += chances[j];

// Выбираем случайное число.

г = int(Math.random()*totalChances);

// Определяем, какой символ это число представляет,
for (j=0; j< chances.length; j++) {

if (r < chances[j]) {

return!j+1);

} else {

r -= chances[ j ] ;
}}}

Когда анимация клипа "spin" завершается, вызывается функция spinDone, которой передается имя экземпляра клипа. Клип "spin1" представляет собой последнюю анимацию в последовательности. Когда эта анимация закончена (то есть все три барабана прекратили вращаться), вызывается функция calcwin, чтобы определить сумму выигрыша.

// Вызываем функцию после каждой анимации вращения,

function spinDone(name) {

// Определяем, последний ли это символ,

// остановились ли барабаны.
if (name == "spinl") {

// Подсчитываем сумму выигрыша,

win = calcWin();

cash += win;
showCash() ;
}}

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

// Определяем сумму выигрыша,

function calcWin() {

// Выясняем, сколько выпало изображений с желудем

numAcorns = 0;

for (i=l; i

if (_root["symbol"+i]._currentFrame == 2)
numAcorns++;

// Проверяем, совпадают ли три символа.

firstSymbol = _root["symboll"]._currentFrame;

threeOfAkind = true;

for(i=2; i

if (_root["symbol"+i]._currentFrame != firstSymbol)
threeOfAKind= false;

// Определяем сумму выигрыша в соответствии с типом символа.

if (threeOfAKind) {

if (firstSymbol == 1) {

win = 20;

} else

if (firstSymbol == 2) {

win = 10;

} else if (firstSymbol ==3) {

win = 30;

} else if (firstSymbol == 4) {

win = 50;

} else if (firstSymbol == 5) {

win = 80;

} else if (firstSymbol == 6) {

win = 100;

} else if (firstSymbol == 7) {

win = 1000;

// Два изображения желудя.

} else if (numAcorns ==2) {

win = 2;

// Один желудь.

} else if (numAcorns ==1) {

win = 1;

// Изображений желудя нет.

} else {

win = 0;

return(win);}


Создание кода



Создание кода


Большая часть кода содержится в первом кадре основной временной шкалы. Начинается она с того, что игроку предоставляется 100 долларов.
startGame();

stop () ;

// "Выдаем" исходную сумму,

function startGame() {

cash = 100;
}
Как и в предыдущем проекте, перед суммой наличных денег игрока отобразите знак "$".
// Отображаем сумму наличных со знаком доллара,

function showCash() {

cashDisplay = "$"+cash;

}
Раздача карт начинается с того, что у игрока изымается один доллар. Каждая раздача производится из новой колоды, состоящей из 52 карт. Функция f irstDraw берет первые пять карт, а функция showCards помешает клипы соответствующих карт на рабочее поле.
// Сдача карты,

function startDeal() {

// Уменьшаем сумму наличных денег.

cash--;

showCash();

// Перетасовываем карты и снова сдаем их.

createDeck();

f irstDraw();

showCards(); }
Создание полностью произвольной перетасованной колоды включает в себя два шага. Первый - создание упорядоченной колоды. Это осуществляется путем циклического просмотра всех мастей и всех рангов карт и для каждой комбинации добавляется соответствующий элемент массива.
Затем программа случайным образом выбирает карты из упорядоченной колоды и помещает их в другой массив. Когда массив заполняется, а предыдущий массив оказывается пустым, у вас получается перетасованная колода карт.
// Создаем перетасованную колоду,

function createDeckO {

// Создаем упорядоченную колоду,

suits = ["с","d","s","h"];

temp = new Array();

for(suit=0; suit
for (num=l; num
temp.push(suits[suit]+num);

}

// Случайным образом выбираем карты,

// пока колода не будет полностью перетасована.

deck = new Array();

while (temp.length > 0) {

r = int(Math.random()*temp.length),

deck.push(temp[r]);

temp.splice(r,1); }
Функция firstDraw берет пять карт из колоды и помешает их в массив cards, а также создает небольшой массив hold, в котором хранятся имена тех карт, которые игрок хочет оставить.



// Сдаем первые пять карт.

function firstDraw() {

cards = new Array();

for (i=0; i

cards.push(deck.pop());

}

// Допускаем, что игрок оставляет все карты,

hold = [true, true, true, true, true];

showCards();
}

Для того чтобы преобразовать содержимое массива cards в то, что игрок видит на экране, функция showCards на рабочем поле устанавливает кадры для каждого из пяти экземпляров клипа. Кадры должны соответствовать символьной строке, расположенной в массиве hand.

// Определяем вид клипов карт, сданных игроку. function showCards() {

for (i=0; i

_root["card"+i].gotoAndStop(cards[i]) ;

}}

После того как все карты будут показаны игроку, он должен решить, что делать дальше. Кнопка "Hold/Draw" под каждой картой вызывает функцию holdDraw и передает ей число от 0 до 4.
Первый раз, когда щелкают по кнопке, программа изменяет экземпляр клипа так, что отображается рубашка карты. Если игрок щелкает по ней еще раз, карта возвращается в исходное состояние. Игрок может сколько угодно переворачивать карты, прежде чем щелкнуть по кнопке Draw.
В массиве hold будет находиться значение true, если игрок хочет оставить соответствующую карту, и false, если хочет ее заменить.

// Переворачиваем карту, предназначенную для замены,

function holdDraw(cardNum) {

// Переворачиваем карту, которая находится среди тех,
// которые игрок хочет оставить,

if (hold[cardNum]) {

_root["card"+cardNum].gotoAndStop("back");

hold[cardNum] = false;

// Если карта перевернута еще раз, оставляем ее.

} else {

_root["card"+cardNum].gotoAndStop(cards[cardNum]);

hold[cardNum] = true;

}}

Когда игрок щелкает по кнопке "Draw", функция secondDraw заменяет те карты, для которых в массиве hold значения были равны false. Затем вызывается функция showCards, чтобы изменения были отражены на экране.
Затем программа с помощью функции handvalue определяет, какой расклад имеется у игрока. Ценность расклада передается функции winning, которая рассчитывает, какую сумму следует добавить к величине cash (сумме наличных). Переменная resultsDisplay используется для отображения этих значений на экране.



// Заменяем карты и определяем выигрыш,

function secondDraw() {

// Заменяем карты, for (i=0; i

if (!hold[i]> { cards[i] = deck.pop();

showCards();

// Определяем, что на руках у игрока.

handVal = handValue(cards);

// Расчитываем сумму выигрыша.

winAmt = winning(handVal);

resultsDisplay = handVal + ": " + winAmt;

// Добавляем сумму выигрыша к имеющейся сумме наличных,

cash += winAmt;

showCash();

gotoAndPlay("done");

}

Прежде чем перейти к рассмотрению функции handvalue, необходимо создать функцию compareHands. Функция handvalue сортирует карты на руках у игрока по возрастанию. Программа Flash ничего не знает о колоде игральных карт, так что вам придется "научить" ее распознавать сочетания покера.
Функция compareHands берет две карты и сравнивает их. Для каждой карты из символьной строки она выбирает первый и второй символы, то есть игнорирует нулевой символ. Таким образом, карта "с7" становится "7", а "с13" - "13".
Затем функция возвращает один из трех вариантов ответов: -1 - первая карта меньше по достоинству второй карты, 0 - карты одинакового достоинства, и 1 - ранг первой карты на единицу больше ранга второй.
Эта функция необходима для команды sort, использующейся в функции handvalue. Если для сортировки не будет специальной функции, программа попытается отсортировать массив hand по алфавиту, то есть все трефы будут расположены перед бубнами, так как трефовая масть начинается с буквы "с", а бубновая - с "d". А вам нужно, чтобы карты были отсортированы в соответствии с их рангом.

// Эта функция используется командой сортировки для определения,
// какие карты идут первыми,

function compareHands(a,b) {

// Получаем ранг карты.

numa = Number(a.substr(1,2));

numb = Number (b. subs t r (1,2) ) ;

// Возвращаем -1, 0 или 1 в зависимости

//от результата сравнения,

if (numa < numb) return(-1);

if (numa == numb) return(O);

if (numa > numb) return(1);}



Следующая функция handvalue начинается с того, что копирует массив cards и помешает его в массив hand. Затем она сортирует полученный массив с использованием функции compareHands (1).
Например, если на руках у игрока имеются карты ["h4", "d5", "c2", "s3", "h6"], после сортировки массив будет выглядеть следующим образом: ["с2", "s3", "h4", "d5", "h6"]. Так гораздо проще узнать, находится ли на руках у игрока "стрит".
"Стрит" определяется путем просмотра каждой карты и выяснением, больше ли ранг этой карты на единицу ранга карты предыдущей (2). Если такое условие выполняется для всего массива, то тогда на руках у игрока "стрит".
Таким образом будет определен не только "стрит": когда "стрит" начинается с десятки и заканчивается тузом, то это "флэш стрит". Произошло ли так или нет, можно определить с помощью простого теста (3).
Затем вы проверяете, одной ли масти карты (4). Для этого все карты, кроме первой, сравниваются с первой. Если масть всех карт совпадает с мастью первой карты, значит, все карты одной масти.
На следующем шаге создается массив counts, в котором будет храниться число карт одинакового достоинства (5). В этом массиве находится 14 элементов, каждый из которых будет отвечать за число карт определенного ранга, имеющихся у ифока. Первый элемент массива не используется, так как нет карт с нулевым рангом.

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

Например, если на руках туз, две тройки, четверка и валет, массив будет выглялеть следующим образом: [0,1,0,2,1,0,0,0,0,0,0,1,0,0].
Теперь, наконец, программа может начать определение расклада на руках у игрока. Просматривается массив counts и отмечаются все значения пары, тройки или четверки (6). Если один раз встречаются две карты одного достоинства, то у игрока пара. Если один раз встречается три карты одного достоинства, то у игрока тройка; если один раз встречается четыре карты одного достоинства, то - каре. Также можно дважды обнаружить две карты одного достоинства или пару и тройку. В первом случае будет две пары, а втором - фул хаус.
Затем проверяется, есть ли на руках пары с валетом или картами высшего достоинства (7). Обычно видеопокер отмечает только такие пары.
Следующий тест проверяет, есть ли в раскладе туз (8). Это будет нужно для определения флэш стрита. Если у ифока флэш стрит, и одна из карт - туз, то у него самый высший тип флэш стрита - флэш роял.
Теперь у функции есть весь набор значений логических переменных: straight, flush, hasAce, fourOfAKind, threeOfAKind, twoPair, pair, fullHouse и jackOrHigher. С их помощью определяется действительная ценность расклада, и возвращается символьная строка (9).



// Определяем расклад,

function handValue() {

// Копируем и сортируем карты игрока.

hand = cards.slice();

(1) hand, sort (compareHands) ;

// Создаем массив мастей и рангов для более легкого доступа

//к ним.

suits = new Array();

nums = new Array();

for (i=0; i

suits.push(hand[i].substr(0,1));

nums.push(Number(hand[i].substr(1,2)));

(2)// Проверяем, расположены ли они по возрастанию,

straight = true;

for (i=0; i

if (nums[i]+l != num[i+l]) straight = false;

(3)// Ищем десятку, валета, даму, короля и туза,

if (nums[0] == 1) and (nums[l] == 10) and (nums[2]) == 11)

and (nums[3] == 12) and (nums[4] == 13)) straight = true;

(4)// Выясняем, одной ли масти карты,

flush = true;

for (i=l; i

if (suits[i] != suitstO]) flush = false;

// Создаем массив для подсчета, сколько карт одного

(5)// достоинства находится в раскладе,

counts = new Array ();

for (i=0; i

for (i=0; i

(6)// Используя массив counts, ищем совпадения,

pair = false;

twoPair = false;

threeOfAKind = false;

fourOfAKind = false;

for (i=l; i

// Найдена пара.

if (counts[i] == 2) {

// Найдена вторая пара.

if (pair) {

twoPair = true;

// Найдена только одна пара.

} else {

pair = true;

// Три карты одного достоинства.

} else if (counts[i] == 3) {

threeOfAKind .= true;

// Четыре карты одного достоинства.

} else if (countsfi] == 4) {

fourOfAKind = true;

// Проверяем, есть ли среди совпадающих карт валеты или

(7)// карты более высокого ранга.

jackOrHigher = false;

for (i=l; i

if (((i==l) or (i>10) and (counts[i]>=2)) {

jackOtHigher = true;

(8) // Выясняем, является ли карта более высокого ранга тузом.

hasAce = false; if (counts[1] > 0) hasAce = true;

(9)// Делаем вывод, какой расклад на руках у игрока.
if (straight and flush and hasAce) {

return("Royal Flush");

} else if (straight and flush) {

return("Straight Flush");

} else if (fourOfAKind) {

return("Four-Of-A-Kind");

} else if (pair and threeOfAKind) {

return ("Full House");



} else if (flush) {

return("Flush");

} else if (straight) {

return("Straight");

} else if (threeOfAKind) {

return("Three-Of-A-Kind");

} else if (twoPair) {

return("Two Pair");

} else if (pair and jaskOrHigher) {

return!"High Pair"); } else if (pair) {

return("Low Pair");

} else {

return("Nothing");

И последняя оставшаяся функция - winning, которой передается строчка, сгенерированная функцией handvalue и которая определяет соответствующую сумму выигрыша.

//Исходя из типа расклада возвращаем сумму выиграша

function winnings(handVal) {

if (handVal == "Royal Flush") return(800);

if (handVal == "Straight Flush") return(50);

if (handVal == "Four-Of-A-Kind") return(25);

if (handVal == "Full House") return(8);

if (handVal == "Flush") return(5);

if (handVal == "Straight") return(4);

if (handVal == "Three-Of-A-Kind") return(3);

if (handVal == "Two Pair") return(2);

if (handVal == "High Pair") return(1);

if (handVal == "Low Pair") return(0);

if (handVal == "Nothing") return(0);

}


Создание кода



Создание кода


Первый кадр вызывает функцию initGame , но после нее не идет команда stop (), так как указатель должен двигаться и перейти к кадру "shuffle".
initGame();
Функция initGame определяет исходную сумму наличных денег игрока.
function initGame() {

cash = 100;

showCash();

}
Функция createDeck похожа на одноименную функцию, используемую в видеопокере, но здесь она шесть раз добавляет каждую карту, чтобы создать шесть колод карт.
Один из недостатков использования шести колод состоит в том, что программе требуется время для, их перетасовки. Следовательно, кадр "shuffle" появляется перед ключевым кадром, вызывающим функцию createDeck. Поэтому слово "shuffle" (Идет перетасовка колоды) появится на экране прежде, чем код начнет тасовать карты. Следовательно, игрок не должен удивляться, почему его компьютер вдруг "завис".
// Создаем перетасованную колоду карт,

function createDeck() {

// Создаем упорядоченную колоду.

suits = ["с", "d", "s", "h"];

temp = new Array();

for (i=0; i
for (suit=0; suit
for (num=1; num temp.push(suits[suit]+num);

}}}

// Карты выбираются случайным образом до тех пор, пока

// колода не будет перемешана,
deck = new Array();

while (temp.length>0) {

r = int(Math.random()*temp.length) ;

deck.push(temp[r]);

temp.splice(r,1);

}}
В функции initHand создаются массивы playerHand и dealerHand. Переменной showDealerFirstCard присваивается значение false, по умолчанию делается ставка в пять долларов.
// Инициализируем массивов расклада и определяем сумму ставки,

function initHand() {

playerHand = new Array();

dealerHand = new Array();

showDealerFirstCard = false;

bet = 5;

showBet();

}
Когда игрок щелкает по кнопке Add to bet (Повысить ставку), вызывав функция addToBet, которая повышает ставку на пять долларов и не позволяет сделать ставку, превышающую 25 долларов.
// Функция увеличивает ставку игрока вплоть до 25 долларов,

function addToBet() {




bet += 5;

if (bet > 25) bet = 25;

showBet;

}

Когда игрок щелкает по кнопке Deal (Раздать), вызывается функция makeBet, которая вычитает сумму ставки из суммы наличных игрока, Затем ролик проходит по четырем кадрам от "Deal1" до "Deal4".

// Вычитаем сумму ставки из суммы наличных денег игрока.

function makeBet() {

cash -= bet;

showCash();
}

В каждом из четырех кадров вызывается функция dealCard, дважды с использованием массива playerHand и дважды - dealerHand. При выполнении этой функции по две карты раздаются игроку и раздающему. Также в каждом кадре вызывается функция showCards.

// Раздаем игроку одну карту из колоды.

function dealCard(hand) {

hand.push(deck.pop());

}

Функции showBet и showCash отображают текущую ставку и текущую сумму наличных с добавлением знака "$" впереди. Не забудьте создать, соответствующие текстовые поля для каждой из этих сумм.

// Отображаем сумму наличности со знаком "$".

function showCash() {

cashDisplay = "$" + cash;

}

// Отображаем сумму ставки со знаком "$".
function showBet() {

betDisplay = "$" + bet;

}

Функция showCards просматривает карты, имеющиеся на руках у игрока и раздающего, и помещает соответствующие клипы на рабочее поле. С помощью переменной showDealerFirstCard указывается, отображается ли первая карта раздающего или показывается всего лишь рубашка карты.

// Отображаем карты на руках у игрока и раздающего,

function showCards() {

// Отображаем первую карту раздающего, когда игроку

// все карты розданы.

if (showDealerFirstCard) {

_root["dealerO"].gotoAndStop(dealerHand[0]);

} else {

_root[“dealer0"].gotoAndStop(2);

}

// Показываем остальные карты раздающего,

for (i=1; i

_root["dealer"+i].gotoAndStop(dealerHand[i]);

}

// Показываем все карты игрока,

for (i=0; i

_root["player"+i].gotoAndStop(playerHand[i]);

}

// Отображаем сумму карт на руках.

playerValue = handValue(playerHand);



dealerValue = handValue(dealerHand);

}

После того как были розданы первые две карты, существует вероятность того, что у кого-то уже двадцать одно очко. Если у игрока, то он сразу же выигрывает, сумма выигрыша составляет 150% от ставки. Если же у раздающего, то игрок проигрывает.

// Проверяем, есть ли 21 очко,

function checkForBlackjack() {

// Если двадцать одно у игрока,

// выигрыш составляет 150% ставки.

if (playerHand.length == 2) and (playerValue == 21)) {

cash += bet*2.5;

showCash();

result = "Blackjack!";

gotoAndPlay("Done");

// Если у раздающего двадцать одно, игрок проиграл.

} else if ((dealeerHand.length == 2) and (dealerHand == 21)) {

result = "Dealer has blackjack!";

gotoAndPlay("Done");
}}

После того как были розданы первые четыре карты, ролик переходит в режим ожидания, к кадру "Player". В этом кадре находятся две кнопки: «Hit” (Еще) и "Stay" (Хватит). Игрок может щелкнуть по кнопке Hit" и запросить еще одну карту, при этом вызывается функция hit. Если с новой картой у игрока 21 очко или больше, игра автоматически переходит дальше к кадру выигрыша или проигрыша соответственно.

// Игрок берет еще одну карту.

function hit() {

dealCard(playeerHand);

showCards();

playerValue = handValue(playerHand);

// Если у игрока 21 очко или больше, выясняем,

// сколько очков у раздающего.

if (playerValue >= 21) startDealer();
}

Когда игрок закончит набирать карты, очередь переходит к раздающему. Функция startDealer начинается с того, что переменной showDealerFirstCard присваивается значение true. Когда снова вызывается функция showCards, отображается первая карта раздающего. Затем игра переходит к кадру "Dealer".

// Отображаем первую карту раздающего,

// теперь он может набирать себе карты.

function startDealer() {

showDealerFirstCard = true;

showCards(0);

gotoAndPlay("Dealer");

}

Кадр "Dealer" проигрывается снова и снова, каждый раз вызывается функция dealerMove, проверяется сумма очков раздающего, больше ли она 17 или нет. Правилами казино определено, что раздающий берет еще одну карту до тех пор, пока сумма очков не превысила 16. Когда раздающий закончил набирать карты, вызывается функция decideWinner.



// Раздающий берет еще одну карту до тех пор,

// пока сумма очков меньше 17.

function dealerMove() {

if (handValue(dealerHand) < 17) {

dealCard(dealerHand);

shoCards();

gotoAndPlay("Dealer");

// Раздающий закончил набирать карты.

} else {

decidewinner();

}}

Функция handValue используется во многих ранее рассмотренных функциях для того, чтобы определить сумму очков на руках. Ранг каждой карты добавляется к сумме очков, при этом туз равен одному очку. Если на руках находится туз и добавление 10 очков не приведет к перебору, тогда к обшей сумме прибавляется 10 очков.

// Подсчитываем очки,

function handValue(hand) {

total = 0;

асе = false;

for (i=0; i

// Добавляем ранг карты.

val = Number(hand[i].substr(1,2));

11 За валета, даму и короля начисляем 10 очков,
if (val > 10) val = 10;

total += val;

// Запоминаем, если был найден туз.

if (val == 1) асе = true;

}

// Туз может стоить 11 очков, если у игрока не будет перебора.

if ((асе) and (total

return(total);
}

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

// Определяем победителя или случай игры в ничью,

function decideWinner() {

showCash();

if (playerValue > 21) {

result = "You Busted!";

} else if (dealerValue > 21) {

cash += bet*2;

result = "Dealer Busts. You Win!";

} else if (dealerValue > playerValue) {

result = "You Lose!";

} else if (dealerValue == playerValue) {

cash += bet;

result = "Tie!";

} else if (dealerValue < playerValue) {

cash += bet*2;

result = "You Win!";

}

showCash(); gotoAndPlay("Done");

}

В кадре Done находится кнопка Next Hand (Сыграть еще раз), которая вызывает следующую функцию, проверяющую, осталось ли в колоде 26 карт. Если в колоде карт меньше, заново создается перетасованная колода. Если осталось достаточное количество карт, вызывается функция initHand, и игра возвращается к кадру "Bet". В любом случае вызывается функция resetCards, которая устанавливает все находящиеся на рабочем поле клипы "deck" в первый кадр, благодаря чему карты не остаются на экране.

// Начинаем следующую раздачи карт.

function newDeaK) { resetCards();

// Если в колоде менее 26 карт,

// Создаем новую перетасованную колоду,

if (deck.length < 26) {

gotoAndPlay("shuffle");

} else {

initHand();

gotoAndPlay("Bet");

}}

// Удаляем карты со стола.

function resetCards() {

for (i=0; i

_root["dealer"+i.gotoAndStop(1);

} for (i=0; i

_root t"player"+i].gotoAndStop(1);

}}


Создание кода



Создание кода


Практически весь код находится в главной временной шкале. Он начинается с функции "startGame". После того как создана новая, перетасованная, колода, создается семь рядов карт (10). Первый ряд содержит» одну карту, второй - две и т.д.
Каждая карта помешается на свое место в соответствии с рядом и местом в ряду (11). К тому же масштаб каждой карты уменьшен на 50%, поскольку колода, использованная в игре "Двадцать одно", в два раза больше по размеру той, что необходима для этой игры (12).
Значение карты берется из массива deck (13). Оно соответствует метке кадра внутри клипа "deck". Это значение сохраняется в свойстве value клипа, после чего клип отправляется в соответствующий кадр. В свойствах клипа row и col хранится позиция клипа в пирамиде (14).
Затем вы создаете клипы для стопок карт мастью вниз и мастью вверх (15). Клип для карт мастью вниз отправляется в кадр "back", показывающий изображение рубашки карты. Другая стопка остается в первом кадре, который пуст.
Переменная firstcard установлена как undefined (16). Эта переменная содержит значение первой карты в паре, выбираемой самим игроком. Массив stack используется для слежения за судьбой карт из стопки мастью вверх. В случае, когда карта из этой стопки используется, должно быть отыскано значение предыдущей выбранной карты.
В заключение должен быть создан экземпляр клипа рамки (17). В начальный момент он помешается за пределами видимости.
startGame();

stop();

function startGame() {

// Тасуем колоду.

createDeck();


(10)// Выстраиваем карты в пирамиду,


level = 0;

for(row=0;row
for(i=0;i
// Создаем новый клип.

mc = _root.attachMovie("Deck","card"+level,level);


(11)// Задаем его расположение.


mc._x = i*60-row*30 + 275;

mc. _у = row*30 + 50;


(12)// Задаем масштаб.


mc._xscale = 50;

mc.__yscale = 50;


(13)// Устанавливаем знчение карты,


mc. value = deck.pop();

mc.gotoAndStop(mc.value);


(14)// Запоминаем позицию карты,


mc.row = row;




mc.col = i;

level++;
}}

(15)// Размещаем клипы открытой и закрытой колод,

for(i = 0 ; i

me = _root.attachMovie("Deck","stack"+i,level)

mc._x = i*60+100;

mc._y = 340;

mc._xscale =50;

mc._yscale = 50;

level++;}

// Показываем "рубашку" для закрытой колоды.

_root["stackO"].gotoAndStop("back");

(16) // Задаем значение первой выбранной карты и массив для

// открытой колоды.

firstCard = undefined;

stack = new Array();

(17) // Создаем и размещаем рамку.

outline = _root.attachMovie("outline","outline",1000);

outline._xscale = 50;

outline._yscale = 50;

outline._x = -1000;

}

Функция createDeck такая же, как в игре "Двадцать одно". Однако теперь вы сортируете только одну колоду. Результат представляется в виде глобальной переменной deck.

// Создаем перетасованную колоду.

function createDeck() {

// Создаем упорядоченную колоду.
suits = ["с","d","s","h"];

temp = new Array();

for(suit=0;suit

for(num=l;num

temp.pushfsuits[suit]+num);

}}

// Выбираем случайные карты, пока не создадим перетасованную

// колоду.

deck = new Array();

while (temp.length > 0) {

r = int(Math.random()*temp.length);

deck.push(tempt[r]);

temp.splice(r, 1) ;
}}

Вместо того, чтобы использовать сценарий клипа или кнопку для обнаружения щелчков мыши, я определю функцию для обработки события onMouseDown.
Сначала она совершает цикл по всем картам в пирамиде и определяет, не совершен ли щелчок по одной из них. (18). Цикл начинается с 28 и совершает обратный отсчет. Таким образом, карты наверху рассматриваются сначала, а карты внизу - потом.
Далее мы проверяем, если по карте был совершен щелчок, то программа определяет, закрывают ли данную карту другие карты пирамиды (19). Для этого вызывается функция cardPresent вместе со значениями ряда и колонки двух карт, которые могли бы закрывать карту, по которой был совершен щелчок.
Если локальная переменная card все еще не определена, значит, ни одна карта не была выбрана. Нужно еще проверить, не выбрал ли игрок карту из стопки карт с открытой мастью. Эту колоду представляет клип "stack1" (20).
Если карта была выбрана, а глобальная переменная firstcard все еще не определена, значит, не выбрана никакая другая карта. Ссылка на выбранную карту помещается в firstcard (21).
Если другая карта уже выбрана, значения старой и новой карт складываются. Функция cardValue используется для хранения численных значений карт (22). Если сумма равна 13, обе карты удаляются при помощи функции removeCard.
Если, с другой стороны, значение firstcard равно 13, значит, это король, и он может быть удален сам по себе (23).
Если по клипу "stack0" был совершен щелчок, это значит, что игрок решил перенести карту из закрытой колоды в открытую. В этом случае берется последнее значение в массиве deck и используется для изменения кадра клипа "stack1" (24). Массив stack используется для отслеживания карт, которые перемешаются между стопками. Чтобы выделить выбранную карту, клип "outline" перемешается в то же положение, что и выбранная карта (25).
И наконец, проверяется первая карта пирамиды (26). Если она отсутствует, значит, игрок ее удалил и выиграл игру.



_root.onMouseDown = function() {

var card = undefined;

(18) // Смотрим, был ли щелчок по одной из карт пирамиды.

for(var i=27;i>=0;i--) {

if (_root["card"+i].hitTest(_xmouse,_ymouse)) {

var card = _root["card"+i];

break;

}}

(19)// Если был, закрывают ли эту карту другие карты?

if (card != undefined) {

if (cardPresent(card.row+l,card.col) or cardPresent(card.row+1,card.col+l)) {

card = undefined;

}}

(20)// Был ли щелчок по стопке карт, лежащих мастью вверх?

if (card == undefined) {

if (stackl.hitTest(_xmouse,_ymouse)) {

card = stack1;

}}

// Проверяем, выбрана ли еще одна карта,

if (card != undefined) {

(21)// Первая выбранная карта,

if (firstCard == undefined) {

firstCard = card;

// Игнорируем второй щелчок по той же карте.

} else if (firstCard == card) {

(22)// Если выбраны две карты и их сумма равна 13.

} else if (cardValueffirstCard) + cardValue(card) == 13) {

// Удаляем обе карты.

removeCard(card);

removeCard(firstCard);

firstCard = undefined;

// В противном случае считаем, что это первая выбранная

// карта.

} else {

firstCard = card;

}}

(23)// Если выбрана одна карта, и это "король"

if (cardValueffirstCard) ==13) {

removeCardffirstcard);

firstCard = undefined;

}}

(24)// Если щелкнули по колоде закрытых карт, переворачиваем

// очередную карту.

if (stackO.hitTest(_xmouse,_ymouse)) {

stackl.value = deck.pop();

stackl.gotoAndStop(stackl.value);

stack.push(stackl.value);

// Когда закрытая колода кончается, удаляем ее.

if (deck.length == 0) {

stackO.removeMovieClip();

}}

(25)// Помещаем рамку около выделенной карты,

if (firstCard != undefined) {

outline._x = firstCard._x;

outline._y = firstCard._y;

} else {

outline._x = -1000;}

(26)// Если удалена первая карта в пирамиде, значит игрок

// выиграл.

if (_root["card0"] == undefined) {

gotoAndStop("game over");

}}

Осталось рассмотреть несколько полезных функций. Первая, removeCard, удаляет карту из колоды открытых карт или из пирамиды. Чтобы удалить карту из открытой колоды , она должна просто отправить клип "stack1" к кадру, который представляет предыдущую карту. Именно в этом месте оказывается полезным массив stack. Каждая карта, положенная мастью вверх, добавляется к stack. Чтобы достать карту опять, вам нужно удалить последнюю карту из массива stack и затем обратиться к последнему члену этого массива. Если карта оказалась из пирамиды, то клип просто удаляется.



function rernoveCard(thisCard) {

if (thisCard == stackl) {

// Удаляем карту из открытой колоды,

stack1.gotoAndStop(1);

stack.pop();

stackl.value = stack[stack.length-l];

stack1.gotoAndStop(stackl.value);

} else {

// Удаляем карту из пирамиды.

thisCard.removeMovieClip();
}}

Следующая функция, cardPresent, проверяет в цикле все карты, чтобы определить, существует ли карта в позиции, определенной аргументам вызова данной функции.

function cardPresent(row, col) {

// Проверяем, существует ли в пирамиде данная карта.

for(var i=0;i

thisCard = _root["card"+i] ;

if ((thisCard.row == row) and(thisCard.col == col)) {

return (true);

}}

return(false);

}

Функция cardvalue берет свойство value клипа карты, отбрасывает первый символ и возвращает численное значение. Например, если значение value карты равно с9, возвращается число 9.

function cardValue(card) {

// Удаляем первый символ из значения value.

n = card.value;

n = parselnt(n.substr(1,2));

return(n);
}

Последняя функция проходит в цикле по всем картам пирамиды и удаляет все оставшиеся карты. Также она удаляет "stack0" и "stackl". Это очищает рабочее поле для подготовки к следующему раунду.

function clearGame() {

// Удаляем карты из пирамиды.

for(var i=0;i

_root["card"+i].removeMovieClip();

}

// Удаляем обе колоды
stack0.removeMovieClip();

stack1.removeMovieClip();

}


В сложной основной временной шкале игры в очко для каждого шага существует помеченный кадр



Рисунок 15.6 В сложной основной временной шкале игры в очко для каждого шага существует помеченный кадр

В сложной основной временной шкале игры в очко для каждого шага существует помеченный кадр


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


Видеопокер показывает игроку пять карт



Рисунок 15.3 Видеопокер показывает игроку пять карт

Видеопокер показывает игроку пять карт



Затем игрок может выбрать те карты, которые следует поменять (можно поменять все). После замены программа должна рассмотреть окончательный набор карт и подсчитать, сколько они "стоят".


Видеопокер



Видеопокер


Исходный файл:
В казино автоматы с видеопокером располагаются рядом с игровыми автоматами. По существу, они представляют собой то же самое: вы вставляете монету и можете выиграть некоторую сумму денег. Единственное различие состоит в том, что в течение игры вы должны совершать некоторые действия, чтобы повлиять на результат, а автомат будет на них реагировать.
Не ошибитесь: казино всегда выигрывает. Но так как игрок может заменять некоторые карты в течение игры, ему кажется, что у него появляется больше шансов на выигрыш. Такая возможность усложняет написание программы.


Все карты представлены в виде



Рисунок 15.5 Все карты представлены в виде маленьких кружочков, меток клипа, так как на данный момент-карты не видны

Все карты представлены в виде


В этом ролике сложная основная временная шкала. Каждая метка представляет собой различный этап игры. На Рисунок 15.6 показана шкала в момент, когда видна большая часть меток.




Задача проекта

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



Задача проекта

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



Задача проекта

Наша цель - создать базисный вариант игры в очко, не стремясь реализовать полный набор функций. Некоторые правила в этой игре редко используются, например удваивание ставки, страхование и разделение, но если вы захотите включить их в свою игру, написание кода окажется очень сложной задачей. Оценить наличие таких правил смогут только избранные, поэтому здесь они опущены, чтобы не перегружать книгу лишней информацией.
На Рисунок 15.4 показан кадр ролика Blackjack.fla. Вы видите, что игрок взял пять карт, которые в сумме дали 18 очков. А раздающий карты взял три карты, их сумма составляет 21.
В этой простой игре игроку и раздающему дается по две карты. Первая карта раздающего остается лежать рубашкой вверх до тех пор, пока игрок не закончит набирать карты. Игрок может брать карты до тех пор, пока у него не окажется двадцать одно очко или более. Затем компьютер "выдает" карты раздающему, пока у того не будет минимум 17 очков.



Задача проекта

Цель этого проекта - создание полной версии игры в пасьянс "Пирамида". Программа должна узнавать даже тот редкий случай, когда игрок выигрывает, удаляя все карты из пирамиды. Если игрок чувствует себя в безвыходном положении или текущая партия представляется неплодотворной, он может нажать кнопку и в любое время пересдать карты.

Космический бой

Астероиды



Астероиды


Исходный файл:
Теперь создадим игру, аналогичную предыдущей, но на этот раз кораблем будет небольшой графический объект в центре экрана. Астероиды движется в различных направлениях. Эта игра похожа на многие классические аркады 70-х и 80-х годов, хорошо знакомые большинству читателей. На Рисунок 16.3 показан кадр ролика Spacerocks.fla.


Другие возможности



Другие возможности


В таких играх, как "Астероид", можно придумать много интересного. Например, время от времени вокруг корабля может летать небольшой НЛО; если игрок его собьет, то заработает дополнительные очки. Зачастую корабль оснащен щитом, который защищает его от определенного количества столкновений. Функция "Гиперпространство" переносит корабль в произвольную точку экрана, а "бомба" взрывает все астероиды, находящиеся на заданном расстоянии от корабля.
Это превосходная игра, в которой начинающий программист ActionScrip может попробовать свои силы и применить все имеющиеся знания. Чем разнообразнее игра, тем лучше.




Другие возможности



Другие возможности


Есть множество способов усовершенствовать эту игру. У вас может быть несколько кроликов, специальные ягоды, которые на короткое время будут позволять лисе гоняться за кроликом. Через определенные промежутки
времени из центральной области будет выходить новый кролик, а в коридорах лабиринта будут появляться бонусные очки.
Возможно, вам в голову придут и другие идеи, например, сделать так, чтобы кролик был более сообразительным: гонялся за лисой, соперничал с лисой и поедал ягоды. Можно начислять дополнительные очки в конце каждого уровня в зависимости от того, насколько быстро он был пройден.




Другие возможности



Другие возможности


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




Другие возможности



Другие возможности


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




Другие возможности



Другие возможности


Возможно, вы захотите создать свои собственные вариации этого ролика. Допустим, реализовать такую идею: пули всегда будут попадать в одну точку экрана. Игрок сможет управлять кораблем с помощью клавиш со стрелками. Например, если игрок нажимает клавишу со стрелкой "влево", то чтобы создать иллюзию перемещения, все астероиды двигаются вправо.
Также можно усовершенствовать игру, предусмотрев уровни и изменив отображение числа столкновений с астероидом. Например, у вас есть некий механизм подсчета этого числа: когда его значение приблизится к максимальной отметке, начнет мигать красная лампочка.




К сведению



К сведению


He забудьте, что нужно присвоить имена связи клипам лисы, кролика и ягоды. Для каждого кадра, в том числе и в небольших клипах, вам понадобится функция stop().


К сведению



К сведению


В этом ролике к каждой кнопке Play прикреплена одна строчка кода. Просмотрите каждый из этих сценариев, чтобы понять, к какому кадру переходит ролик или какая функция вызывается.
Обратите внимание, что во всех трех типах ускорителей использовалась одна и та же анимация - thrust animation. Для левого и правого ускорителей клип поворачивался на 90° или на -90°. Размер анимации боковых ускорителей был уменьшен, так как предполагается, что они слабее основного ускорителя.


К сведению



К сведению


Клип с лисой содержит три части: "stand", "run" и "jump". В первом кадре содержится команда stop(). В последнем кадре анимации "run" содержится команда gotoAndPlay ("run"). Благодаря этому движение зацикливается.
Клип "acorn" также содержит команду stop О в первом кадре.
Еще один важный момент - это установка центра координат клипов. Он расположен внизу и отцентрирован по горизонтали. На рис 16.9 изображен клип "box" и его центр.
К сведению

Рис 16.9 Во всех клипах центр находится внизу точно посередине


К сведению



К сведению


В клипе "foreground" находятся текстовые поля, связанные с переменными hits и damage. К сожалению, так как они расположены на первом уровне внутри клипа, они не будут реагировать на изменения значений hits и damage, принадлежащих основной временной шкале. То есть для этих текстовых полей необходимо указать команды типа _root. displayDamage = damage. Для того чтобы избежать недоразумений, эти области были названы displayDamage и displayHits.


К сведению



К сведению


Как всегда, убедитесь, что для клипов в библиотеке установлены свойства связи, чтобы они были включены в swf-файл. Свойства связи необходимо установить для астероидов и пуль, потому что до начала игры их нет на рабочем поле.
Я также создал три текстовых поля, отображающих значения переменных gameLevel, lives и score. Эти поля расположены в разных углах экрана, над ними добавлен поясняющий текст.


Клип "ship" включает следующие кадры: "normal", "up", "left", "right" и "explode"



Рисунок 16.7 Клип "ship" включает следующие кадры: "normal", "up", "left", "right" и "explode"

Клип

В первом кадре представлен корабль без включенных ускорителей. Во втором кадре включен главный ускоритель, который поднимает корабль вверх. В третьем и четвертом кадрах отображены боковые ускорители. На Рисунок 16.7 показан кадр "left". Обратите внимание, что слово "left" означает, что корабль будет двигаться влево, а пламя ускорителя находится справа. Кадр "explode" представляет собой начало небольшой анимации, в которой луноход взрывается. Эта анимация используется в том случае, когда игрок посадил корабль не на площадку.
В дополнение к клипу "background", который совсем не используется в коде, три других клипа представляют собой рельеф поверхности для трех уровней игры. Эти клипы называются "Ground - level I", "Ground - level 2" и "Ground - level 3".
Клип "pad" - всего лишь желтый прямоугольник, который показывает, что непосредственно под луноходом находится посадочная площадка.
Основная временная шкала этого ролика действительно сложная. Информация о кадрах и их содержимом приведена в табл. 16.1.


Таблица 16.1 Кадры основной временной шкалы



Кадры Содержание
start Содержит введение и кнопку Play, чтобы начать игру
start level Предупреждает игрока о начале первого уровня
level 1 В этом кадре игрок проходит первый уровень, если его корабль останется цел, ролик перейдет к следующему кадру
level 1 done Если игрок благополучно закончил первый уровень, ему выдается об этом сообщение, и отображается кнопка Play, с помощью которой можно начать второй уровень
level2 В этом кадре игрок проходит второй уровень, если его корабль останется цел, ролик перейдет к следующему кадру
level2 done Если игрок благополучно закончил второй уровень, ему выдается об этом сообщение, и отображается кнопка Play, с помощью которой можно начать третий уровень
level3 В этом кадре игрок проходит третий уровень, если его корабль останется цел, ролик перейдет к следующему кадру
game over Игрока поздравляют с тем, что он три раза успешно посадил корабль. Кнопка Play предлагает попробовать сделать это еще раз
lost ship Если на каком-либо уровне корабль игрока взрывается, ролик переходит к этому кадру, и игра заканчивается. С помощью кнопки Play можно попытаться еще раз пройти игру




У вас также будет два простых клипа: "fuel gauge" и "fuel meter". Первый представляет собой пустой прямоугольник, а второй - сплошной. Клип "meter" помещается внутри клипа "gauge". Далее в коде вы укажете,чтобы при сжигании горючего размеры клипа "meter" уменьшались, то есть он будет занимать меньше места в прямоугольнике "gauge".


Когда в программе Flash отображена сетка, можно увидеть пересечения линий, на которых появятся ягоды



Рисунок 16.5 Когда в программе Flash отображена сетка, можно увидеть пересечения линий, на которых появятся ягоды

Когда в программе Flash отображена сетка, можно увидеть пересечения линий, на которых появятся ягоды


Сетка помогает быстро создать фон с помощью инструмента рисования прямоугольников. Для этого нужно обвести контуром некоторые области сетки и залить их цветом. Вы должны быть уверены, что границы фона немного перекрывают линии сетки. Когда программа будет помешать ягоды на пересечения линий сетки, они не появятся вдоль границы фона.
Первый кадр клипа "fox" - обычный статический кадр, в остальных кадрах представлена небольшая анимация - лиса съедает ягоду. Когда это происходит, клип воспроизводит второй кадр: игрок видит, как лиса ест ягоду, а затем клип возвращается к первому кадру.
В клипе "berry" два кадра: первый содержит изображение ягоды, а второй пустой. Когда лиса съедает ягоду, клип переходит ко второму кадру. Таким образом, клипы остаются на рабочем поле, но они видны до тех пор, пока ягода не будет съедена.
Клип "bunny" представляет собой анимацию: кролик всегда двигается, так что нет необходимости создавать статический кадр.
Все клипы, кроме "background", не будут видны до тех пор, пока не начнется ролик и программа не создаст их с помощью команды attachMovie.
В ролике содержатся практически такие же кадры, что и в ролике игры "Space Rocks": "start", "play", "lose life", "level over" и "game over".


Космический бой



Космический бой


Исходный файл:
Аркады иногда называют играми на реакцию, так как единственные необходимые здесь навыки - умение быстро реагировать. Это особенно верно для первой игры, "Космический бой".
На Рисунок 16.1 показан фрагмент ролика Spacecombat.fla: игрок как бы находится внутри космического корабля, навстречу которому летят астероиды.


Лиса пытается собрать все ягоды, в то время как кролик стремится ее догнать Задача проекта



Рисунок 16.4 Лиса пытается собрать все ягоды, в то время как кролик стремится ее догнать


Луноход снижается, пытаясь приземлиться



Рисунок 16.6 Луноход снижается, пытаясь приземлиться на одну из приспособленных для этого площадок Задача проекта Цель - создать стандартную игру, в которой корабль должен удачно совершить посадку. Он начинает свое движение вверху экрана, игрок управляет сильным вертикальным ускорителем, расположенным под кораблем, а также более слабыми ускорителями - по бокам корабля.

Луноход снижается, пытаясь приземлиться
С течением времени корабль снижается под воздействием гравитации. Игрок не может постоянно пользоваться ускорителями, так как у корабля ограниченный запас горючего.
Задача игрока состоит в том, чтобы корабль в полной сохранности совершил посадку на одну из отведенных для этого площадок. Если он сядет не на площадку или как-то иначе коснется поверхности луны, то произойдет взрыв. В этой игре несколько уровней. В ролике, который приводится в качестве примера, их три, и в каждом отображается различный рельеф. Подготовка ролика Сначала рассмотрим клип "ship". На Рисунок 16.7 показан полноэкранный вид окна программы Rash в момент, когда выбран клип корабля. В этом клипе несколько кадров с метками: "normal", "up", "left", "right" и "explode".


Луноход



Луноход


Исходный файл:
Вот еще одна классическая игра. Игра "Луноход" была, возможно, второй из когда-то созданных компьютерных игр. Ее первая версия предназначалась для универсальной ЭВМ и была полностью основана на тексте. Каждую секунду компьютер в виде текста выдавал, где в данный момент находится луноход и какова его скорость. По мере снижения лунохода игрок мог вносить коррективы.
С тех пор игра воссоздавалась в различных графических форматах, большинство из которых, как и здесь, использовали вид сбоку (Рисунок 16.6).


Платформенный скроллер



Платформенный скроллер


Исходный файл:
После первой волны аркадных игр, которая спала где-то в начале 80-х, следующим значительным течением было появление игр типа платформенный скроллер. Этот жанр приобрел ширенную популярность после появления серии игр "Mario Bros" от компании Nintendo. В конце 80-х платформенные скроллеры были абсолютным фаворитом среди консольных игр. Игры этого типа продолжают выходить и сегодня.
Игровой процесс типичного скроллера состоит в следующем: герой игры передвигается взад и вперед по двухмерному миру (вид сбоку). Игровое
пространство имеет несколько горизонтальных уровней - платформ, и игрок может прыгать и перемешаться по ним. Цель состоит в собирании различных предметов, за которые начисляются очки, и в том, чтобы избегать (а также уничтожать) других созданий игрового мира.
На рисунке 16.8 показан простенький пример скроллера с лисой в главной роли. Лиса может перемещаться туда-сюда по горизонтали и прыгать по платформам. При этом ей необходимо собирать орехи и избегать кроликов.


Подготовка ролика



Подготовка ролика


Ролик содержит всего четыре клипа. Клип "fox" содержит некоторое количество кадров. В первом кадре изображена неподвижная лиса, далее некоторое количество кадров изображают анимацию бегущей (в одном направлении) лисы, и последние кадры изображают лису в прыжке. Имя экземпляра этого клипа на рабочем столе также "fox"; для экземпляра выставлен масштаб 25%.
Клип "box" - это просто коричневый квадрат размером 50x50. Этот клип содержит один кадр.
Клип "acorn" начинается с кадра со статичным изображением ореха. В этом кадре находится команда stop О . Остальная часть клипа изображает постепенное появление числа "100". Эта анимация проигрывается, когда игрок берет орех и ему начисляются очки.
Клип "bunny" состоит из двух кадров изображающих кролика, шевелящего лапками. Эта анимация работает независимо от направления перемещения кролика.
Клипам "box", "acorn" и "bunny" необходимо присвоить имена в панели Linkage Properties, чтобы был возможен их экспорт.
В главной временной шкале находятся три кадра. Первый содержит инструкции и кнопку Play, последний содержит сообщение "Game Over". В среднем кадре "Play" находится лиса и фон, изображающий землю. Все остальные элементы будут созданы программно при запуске игры.


Подготовка ролика



Подготовка ролика


В этом ролике не так много библиотечных элементов. На переднем плане сверху и снизу расположены элементы (см. Рисунок 16.1), представляющие собой интерьер вашего корабля. Все элементы переднего плана, включая текстовые области подсчета очков и повреждений внизу экрана, находятся в клипе "foreground". Благодаря этому можно легко поместить весь клип на передний план относительно пуль и астероидов.
Объекты-астероиды расположены в клипе "rock", который состоит из трех частей. Первая часть - статический кадр с изображением астероида, когда он приближается к кораблю. Вторая часть состоит из нескольких кадров анимации: взрыв астероида при попадании в него пули. Эта последовательность помечена как "explode red". И последняя часть - последовательность "explode blue". Просмотрите исходный ролик на Web-сайте, чтобы понять, как был создан этот клип (Рисунок 16.2).
В ролике находится также клип "point", в котором генерируются объекты-пули, и клип "cursor", заменяющий обычный вид курсора на перекрестие.
В ролике на главной временной шкале расположены три кадра: "start", "play" и "game over". Все действие происходит в кадре "play".


Подготовка ролика



Подготовка ролика


Библиотека для этого ролика несложная. В ней содержатся астероиды трех типов: "rock1", "rock2" и "госк1". Для того чтобы внести в игру некоторое разнообразие, каждый раз при создании астероида случайным образом выбирается один из трех клипов.
В клипе "ship" первый кадр - статический, во втором кадре начинается анимация, которая показывает, как загораются ускорители. Первый кадр этой анимации называется "thrust". Вместо того чтобы расположить корабль на рабочем поле с помощью команды attachMovie, его помешают в центр поля вручную, поэтому можно не заботиться о создании и размещении этого клипа с помощью кода.
Для формирования пуль используется клип "bullet". Код игры содержится в клипе "actions" и сценарии кнопки "button". Единственная кнопка на экране, реально выполняющая функцию кнопки, - это кнопка Play.
В ролике будут находиться кадры "start", "play", "ship hit", "level over" и "game over", в каждом из которых создается соответствующий текст. В каждом кадре, кроме "play", находится копия кнопки Play, но к каждому экземпляру этой кнопки прикреплен разный код. Просмотрите ролик. Spacerocks.fla, чтобы понять, где расположены все эти элементы и какой код прикреплен к каждому из них.


Подход



Подход


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


Подход



Подход


Пространство игры представляет собой массив небольших объектов. Эти объекты имеют свойство type, соответствующие клипу, который будет использоваться. Клип имеет свойства х и у, определяющие его местоположение. Вот, например, как может выглядеть такой объект {type: "box", х:100, у: 0}.
Игрок начинает в позиции (0, 0). Если он передвигается вправо, то его позиция по х увеличивается. Если блок находится в позиции 0 по х, а игрок - в позиции 100, это значит, что блок располагается на 100 пикселов правее игрока. Если же игрок смешается в позицию 10 по х, то блок будет находиться на 90 пикселов правее.
Лиса не перемешается по горизонтали, а всегда находится в центре экрана. Все объекты прорисовываются в соответствии с их расстоянием от лисы. Когда речь идет о передвижениях лисы, имеется в виду ее виртуальное перемещение по пространству игры, а не по экрану.
Определение областей, в которых возможно (невозможно) передвижение лисы, является важнейшей задачей этой игры. Если лиса упирается в блок, она должна остановить свое перемещение в этой позиции. Эта задача реализована посредством постоянного сравнения положения лисы с положениями всех блоков. Если блок находится к игроку ближе других блоков в этом направлении, тогда его координаты принимаются : за самую далекую точку в данном направлении, до которой возможно перемещение лисы. Например, если блок находится на расстоянии 200 пикселов вправо от лисы, значит, лиса не может быть перемешена далее чем на 200 пикселов вправо. Если же в процессе проверки всех остальных блоков обнаружен блок, находящийся на расстоянии 100 пикселов от лисы, то значение 100 принимается как максимальное для возможного перемещения лисы в данном направлении.
То же самое происходит и по вертикали. Когда лиса прыгает, ей передается значение скорости по вертикали. При каждом обращении к кадру эта скорость регулируется за счет воздействия силы тяжести, так что лиса постепенно замедляется, а затем падает обратно вниз. Все объекты игрового пространства проверяются для определения наивысшей и наинизшей точек возможного движения лисы. Когда лиса достигает низшей точки, вертикальное движение прекращается. Это может быть самый низкий уровень ("земля") или одна из платформ других уровней.


Погоня в лабиринте



Погоня в лабиринте


Исходный файл:
Следующая игра - "Погоня в лабиринте" - классическая аркадная игра. Самое известное воплощение этой игры - Рас-Man.
В игре, фрагмент которой изображен на Рисунок 16.4, лиса бегает в лабиринте, пытаясь съесть все ягоды. Там же бегает злой кролик. Лиса должна не столкнуться с кроликом, пока не соберет все ягоды.


Реализованный во Flash платформенный



Рисунок 16.8 Реализованный во Flash платформенный скроллер содержит все основные элементы этого жанра


Создание кода



Создание кода


В первом кадре содержится копия кнопки Play (Начать игру). Сценарий этого кадра не только переводит ролик к кадру "play", но и устанавливает несколько переменных.
on (press) {

lives = 3;

score = 0;

gameLevel = 1;

gotoAndPlay("play"); }
В кадре "play" содержится клип "actions" с прикрепленным к нему сценарием. Этот сценарий инициализирует все элементы в начале игры, после того как лиса теряет жизнь и перед началом нового уровня. Он также вызывает функции move и moveBunny для продолжения игры.
onClipEvent(load) {

// Инициализируем исходные элементы _root.startLevel();}

onClipEvent(enterFrame) {

// Перемещаем лису.

_root.move();

// Перемещаем кролика.

_root.moveBunny(); }
Все функции этой игры можно найти в сценарии основной временной шкалы для кадра "play". Сначала идет функция startLevel.
Основная работа данной функции заключается в создании ягод, для этого просматриваются все 15 положений по вертикали и 21 положение по горизонтали и проверяется, не расположены ли они поверх клипа "background". Если нет, то на пересечении линий сетки помешаются клипы "berry", а в массив berries добавляются указатели на эти клипы.
Также эта функция создает экземпляры клипов "fox" и "bunny", создает и устанавливает несколько новых свойств для каждого клипа, в том числе и dest (новое положение).
Обратите внимание, что свойство dest для клипов "fox" и "bunny" представляет собой просто координату: (х:275, у.275}. Благодаря этому значительно упрощается обращение к данным величинам. Например, можно обратиться к новому горизонтальному положению клипа, записав fox.deet.x.
function startLevel() {

level = 0;

// Создаем ягоды,

berries = new Array();

// Ищем пересечения линий сетки,

for (y=l; у
for (x=l; х
// Выясняем, расположены ли пересечения поверх фона.

if (!(background.hitTest(x*25,y*25,true))) {

// Создаем и размещаем клип "berry".




attachMovie ("berry", "berry"+level, level);

clip = _root["berry"+level];

clip._x = x*25; clip._y = y*25;

// Добавляем клип в массив berries.

berries.push(clip);

level++;

}}}

// Создаем клип с лисой и определяем его положение.
attachMovie("fox", "fox", level++);

fox._x = 275;

fox._y = 275;

// Определяем направление движения и координаты цели,

fox.move = {x:0; у:0};

fox.dest = {х:275, у:275};

fox.nextmove = 0;

// Создаем и размещаем клип с кроликом.

attachMovie("bunny", "bunny", level++);

bunny._x = 275;

bunny._y = 125;

// Определяем направление движения и координаты цели.

// Запоминаем координаты предыдущего положения,

bunny.move = {х:1, у:0};

bunny.dest = {х:300, у:125};

bunny.nextmove = 0;

bunny.last = {x:275, у:125};

}

В каждом кадре вызывается функция move, которая передвигает клип с лисой в соответствии с объектом move. В функции содержится специальное условие, чтобы можно было определить, перемещается ли лиса за пределы экрана справа или слева, и если да, то лиса появляется на противоположной стороне (по тоннелю лиса может переходить с одной стороны экрана на другую).
Новое положение цели как для лисы, так и для кролика - всегда координаты следующей ягоды. Даже если ягоду не видно, клип все равно находится на своем месте. Когда лиса доходит до ягоды, координаты которой совпадают со значениями объекта fox.dest, вызывается функция eatBerry, чтобы удалить ягоду, если она все еще видна. Затем с помощью функции nextMove определяются координаты следующего положения цели.
Обратите внимание, что лиса каждый раз перемещается на пять пикселов. Так как все ягоды расположены на расстоянии 25 пикселов друг от друга, то, чтобы дойти до следующей ягоды, необходимо сделать пять шагов. Скорость передвижения лисы - число, которое прибавляется к свойству nextMove. В данном случае добавляется число 50, то есть между шагами пройдет, по крайней мере, 50 миллисекунд.

function move() {

// Выясняем, можно ли сделать следующий шаг.



if (getTimert) > fox.nextmove) {

// Нельзя сделать следующий шаг в течение 50 миллисекунд,

fox.nextmove = getTimer() + 50;

// Перемещаем лису.

fox._x += fox.move.x*5;

fox._y += fox.move.y*5;

// Определяем, прошла ли лиса сквозь тоннель,

if (fox._x > 550) fox._x = 0;

if (fox._x < 0) fox._x = 550;

// Проверяем, достигла ли лиса положения своей цели,

if ((fox._x == fox.dest.x) and (fox.__y == fox.dest.у)) {

eatBerry();

nextMove();

}}}

Для того чтобы лиса съела ягоду, нужно просмотреть массив berries и определить, совпадает ли положение какой-либо ягоды с положением лисы. Если да, то клип этой ягоды переходит ко второму кадру, ягода исчезает, а количество очков увеличивается.
Так как в поисках нужной ягоды вы просматриваете массив berries, можете посчитать, сколько ягод не было съедено. Если была найдена не съеденная ягода, то уровень не заканчивается.

function eatBerry() {

// Предположим, что все ягоды были съедены.

allGone = true;

// Просматриваем массив berries,

for (i=0; i
// Выясняем, находится ли ягода там же, где и лиса.

if ((berries[i]._currentFrame == 1) and (berries[i]._x == fox._x) and (berries[i]._y == fox._y)) {

// Удаляем ягоду.

berries[i].gotoAndStop("nothing");

// Лиса открывает рот.

fox.gotoAndPlay("eat");

// Увеличиваем число очков.

score++;

// Если какая-то ягода еще не съедена,

// то уровень не закончен.

} else if (berries[i]._currentFrame== 1){

allGone = false;

}} // ЕСЛИ все ягоды были съедены, уровень завершен.

if (allGone) {

gotoAndPlay("level over");

gameLevel++;

}}

Когда лиса доходит до ягоды, необходимо определить следующее положение цели. С помощью объекта Key можно проверить все четыре клавиши со стрелками, если нажата одна из них, для лисы определяется
объект move. Однако это не значит, что лиса будет двигаться в указанном направлении.
Для того чтобы лиса могла сделать шаг, необходимо, чтобы в том направлении на расстоянии ровно в 25 пикселов находился клип ягоды.
Если же его там нет, то свойство move объекта обнуляется.



function nextMove () {
// Предположим, что лиса двигаться не будет.
fox.move.х =0;

fox. move, у =0;
// Проверяем клавиши со стрелками.
// Определяем потенциальное направление движения.

// Если нужно, поворачиваем лису влево или вправо.
if (Key.isDownfKey.LEFT)) {
fox.move.x = -1;

fox._xscale = Math.abs(fox._xscale);

} else if (Key.isDown(Key.RIGHT)) {

fox.move.x = 1;

fox._xscale = -Math.abs(fox._xscale);

} else if (Key.isDown(Key.UP)) {

fox.move.у = -1;

} else if (Key.isDownfKey.DOWN)) {

fox.move.у = 1;

}

// Определяем потенциальное положение цели.

newx = fox._x + fox.move.x*25;

new = fox._y + fox.move.y*25;

okToMove = false;

// Просматриваем массив berries: совпадают ли координаты

// потенциального положения цели с координатами какой-либо

// ягоды,

for(i=0;i< berries.length;i++) {

// Совпадение найдено, указываем новое положение цели,

if ((berries[i]._x == newx) and (berries[i]._y == newy)) {

fox.dest.x = newx;

fox.dest.y = newy;

okToMove = true;

}}

// Указываем специальные установки для прохода сквозь тоннель,

if (newx == 550) {

okToMove = true;

fox.dest.x = 25;

} if (newx == 0) {

okToMove = true;

fox.dest.x = 525;}

// Если совпадение не найдено, не перемещаем лису.

if (!okToMove) {

fox.move.x = 0;

fox.move.у = 0;
}}

Кролик двигается непредсказуемо. Когда он доходит до ягоды, следующий фрагмент кода просматривает массив berries и определяет, какие клипы ягод находятся рядом с текущим положением кролика. Затем случайным образом выбирается один из этих клипов, координаты которого и будут положением цели кролика. Не может быть выбрано только то направление, откуда пришел кролик. Другими словами, кролик не может вдруг повернуть назад. Таким образом, кролик идет по длинным коридорам, не останавливаясь и не шарахаясь из стороны в сторону. Однако если кролик заходит в тупик, он может идти в обратном направлении, так как это единственно возможный путь.
И последнее, что делает функция move Bunny, - это проверяет, не находится ли кролик вблизи от лисы. Если да, то кролик ловит лису, и она теряет одну жизнь.



function moveBunny() {

// Проверяем, пришло ли время для следующего шага кролика,

if (getTimert) > bunny.nextmove) {

// Определенное время кролик двигаться не может,

bunny.nextmove = getTimerO + 60 - gameLevel*10;

// Перемещаем кролика.

bunny._x += bunny.move.x*5;

bunny._y += bunny.move.у*5;

// Выясняем, дошел ли кролик до положения цели,

if (bunny._x == bunny.dest.x and bunny._y == bunny .dest .y) {

// Создаем массив с координатами

// возможных положений кролика,

possibilities = new Array;

for (i=0; i

// Определяем расстояние между

// текущим положением кролика и ягодой,

xdiff = Math.abs(berries[i]._x - bunny._x);

ydiff = Math.abs(berries[i]._y - bunny._y);

// Если эта ягода находится рядом...

if ((xdiff == 25 and ydiff == 0) or (xdiff == 0 and ydiff == 25) {

// ...тогда ее положение может

// быть положением цели кролика.

possibilities.push(berries[i]);
}} // Среди возможных положений цели выбираем

// произвольное,

do {

r = int(Math.random()*possibilities.length);

// Указываем новое положение цели

//и направление движения.

bunny.dest.x = possibilities[r]._х;

bunny.dest.y = possibilities[r]._y;

bunny.move.х = (possibilities[r]_x - bunny._х)/25;

bunny.move.у = (possibilities[r]._y - bunny._у)/25;

// Принимаем выбранные положения, если кролик

// не возвращается назад или если этот

// путь - единственно возможный.

} while ((bunny.dest.х = lastx and bunny.dest.y == lasty) and (possibilities.length > 1));

// Запоминаем координаты предыдущего положения,

lastx = bunny._x; lasty = bunny._y;
}}

// Проверяем, находится ли кролик поблизости с лисой,
if (Math.abs(bunny._x - fox._x)

} else {

lives--;

gotoAndStop("lost life");
}}}


Создание кода



Создание кода


В трех кадрах "level1", "level2" и "level3" у вас будут клипы "actions", которые вызывают необходимые для игры функции. В начале уровня вызывается только одна функция, а в течение игры постоянно - другая.
onClipEvent(load) {

_root.startLevel();

} onClipEvent(enterFrame) {

_root.moveShip(); }
Обе эти функции расположены в основной временной шкале первого кадра. Сценарий ролика начинается с функции startGame, которая вызывается, когда в первом кадре игрок щелкает по кнопке Play. Она устанавливает значение переменной gameLevel, отображаемое в текстовом поле в верхнем правом углу экрана. Затем с помощью функции startLevel начинается первый уровень.
function startGame() {

gameLevel = 1;

startLevel(); }
Функция startLevel выполняет множество задач. Сначала она переводит ролик к кадру в соответствии со значением переменной gameLevel, затем помешает корабль вверху экрана. Скорость корабля, которая определяется величинами dx и dy, обнуляется. Для увеличения скорости корабля и для того, чтобы он начал двигаться вниз, используется переменная gravity.
В массивах hitPoints и footPoints содержатся координаты некоторых точек относительно центра корабля. Для определения, коснулся ли корабль поверхности луны, используются элементы массива hitPoints. А оба элемента массива footPoints нужны, чтобы определить, находятся ли обе опоры лунохода на посадочной площадке.
В массиве pads хранятся имена трех клипов посадочных площадок. На одном уровне имеется всего лишь две посадочные площадки, но язык ActionScript этого не учитывает.
function startLevel() {

gotoAndStop("level"+gameLevel);

// Размещаем клип корабля.

ship._x = 275;

ship._x = 25;

// Корабль не движется.

ship.dx = 0;

ship.dy = 0;

// Инициализируем гравитацию.

gravity = .1;

// Инициализируем индикатор горючего.

fuel = 100;

showFuel();

// Определяем точки корабля, которые могут коснуться

// поверхности Луны.

hitPoints = new Array();

hitPoints.push({x:-9, у: 13});




hitPoints.push({x:9, y:13});

hitPoints.push({x:0, y:-10});

hitPoints.push({x:-9, y:-7});

hitPoints.push({x:8, y:-7});

// Определяем координаты опор лунохода

footPoints = new Array();

footPoints.push({x:-9, y:13});

footPoints.push({x:9, y:13});

// Создаем массив из клипов "pad" (посадочная площадка)
pads = new Array();

for (i=0; i

pads.push(_root["pad"+i]);

}}

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

function moveShip() {

shipThrusters();

shipMovement();

checkForLand();

checkForCrash();}

Функция shipThrusters проверяет, осталось ли еще горючее, если нет, то клип "ship" переходит к кадру "normal". В противном случае, если игрок нажимает на одну из клавиш со стрелками "вверх", "влево" или "вправо", включается соответствующий ускоритель. Клип "ship" переходит к соответствующему кадру, и чтобы отразить эффект действия ускорителя, изменяются значения dx и dy. Также уменьшается количество топлива.

function shipThrusters() {

// Проверяем ускорители и корректируем скорость,

if (fuel < 0) {

ship.gotoAndStop("normal");

} else if (Key.isDown(Key.UP)) {

ship.dy -= .4;

ship.gotoAndStop("up");

fuel -= 2;

showFuel();

} else if (Key.isDownfKey.LEFT)) {

ship.dx -= .2;

ship.gotoAndStop("left");

fuel -= 1;

showFuel();

} else if (Key.isDownfKey.RIGHT)) {

ship.dy += .2;

ship.gotoAndStop("right");

fuel -= 1;

showFuel();

} else {

//He включен ни один из ускорителей,

ship.gotoAndStop("normal");
}}

Функция shipMovement изменяет скорость корабля согласно гравитации и перемешает корабль в зависимости от скорости лунохода.

function shipMovement() {

// Гравитация заставляет корабль двигаться вниз,

ship.dy += gravity;

// Перемещаем корабль.



ship._x += ship.dx;

ship._y += ship.dy;}

Функция checkPorLand проверяет массив footPoints: находится ли хоть одна опора лунохода внутри клипа "pad". Если там нет ни одной опоры, переменной landed присваивается значение false. Такое же значение присваивается и в том случае, когда скорость лунохода больше трех единиц, поскольку данная скорость слишком высока для того, чтобы луноход смог совершить посадку.
Если после этого значение переменной landed все еще равно true, значит, луноход удачно совершил посадку. Все, что, теперь необходимо сделать, - перейти к следующему кадру в основной временной шкале и увеличить значение переменной gameLevel на единицу.

function checkForLand() {

// Выясняем, обе ли опоры находятся на посадочной площадке. landed = true;

for(i=0; i

// Просматриваем все площадки.

footDown = false;

for(j=0; j

// Проверяем, находится ли опора на площадке,

if (pads[j].hitTest(ship._x+footPoints[i].x, ship._y+footPoints[i].y, true)) {

footDown = true;

break;}}

// Если опора не находится на площадке,

// значит, корабль не совершил посадку,

if (!footDown) {

landed = false;

break;}} // Проверяем, не слишком ли быстра движется корабль.

if (ship.dy > 3.0) landed = false;

if (landed) {

// Посадка совершена. gotoAndPlay(_currentFrame+1);

gameLevel++;

}}

С другой стороны, необходимо проверять, взорвался луноход или нет. В массиве hitPoints содержится список координат точек вокруг корабля: двух опор, середины каждой стороны, вершины и центра днища. Так как невозможно определить, полностью ли один объект накладывается на другой, вы просто проверяете эти точки. Если какая-то из них оказалась внутри элемента "activeground", тогда корабль разбился. У клипа "Ground - level X" на каждом уровне - имя экземпляра "activeground".

function checkForCrash() {

// Корабль не совершил посадку. Проверяем, коснулся ли он

// поверхности Луны,

if (Handed) {

// Просматриваем все возможные точки касания.

for(i=0; i

// Проверяем, не коснулся ли корабль поверхности



// Луны в этой точке.

if (activeground.hitTest(ship._x+hitPoints[i].x, ship._y+hitPoints[i].y, true)) {

ship.gotoAndPlay("explode");

// Уменьшаем количество жизней

// или завершаем игру.

gotoAndPlay("lost ship");
break;
}}}}

Еще одна полезная функция в этом ролике - showFuel. Она берет клип "meter" и присваивает его свойству _xscale величину fuel. Так как сначала значение переменной fuel (запас горючего) равно 100, а затем уменьшается до 0, его легко использовать для свойства _xscale. Если бы диапазон значений fuel был другим, вам бы пришлось сначала преобразовать их так, чтобы они находились в диапазоне от 0 до 100, а лишь затем присваивать их свойству _xscale.

function showFuel() {

gauge.meter._xscale = fuel;

}

Обратите внимание: для того чтобы размеры клипа "meter" уменьшались справа налево, необходимо поместить центр клипа так, чтобы центр прямоугольника располагался справа от него. Если центр клипа окажется слева, то изменение величины _xscale приведет к тому, что объект будет сокращаться к центру, а не справа налево.


Создание кода



Создание кода


За небольшими исключениями весь код этой игры находится в кадре "Play". Он начинается с вызова функции startGame. Она инициализирует набор констант таких как скорость лисы и кролика, устанавливает начальное положение лисы и вызывает функции createWorld и creatObjects, которые создают все элементы игрового пространства.
startGame();

stop();

function startGame() {

// Устанавливаем константы,
floor = 350;

foxSpeed = 10;

bunnySpeed = 2;

jumpPower = 60;

// Задаем параметры лисы.

foxPos = {x:0,y:0};

fallSpeed = 0;

falling = false;

fox.swapDepths(999);

// При каждом обращении к кадру вызываем moveFox.

_root.onEnterFrame = moveFox;

// Создаем элементы игры.

createWorldf);

createObjects() ;}
Функция creatWorld создает массив objects и заполняет его положением всех блоков, орехов и кроликов. Она также задает глобальную переменную worldEnd, которая определяет правую границу игрового пространства.
function createWorld() {

objects = new Array();

objects.push({type:"box", x:250, y:0});

objects.push({type:"box", x:300, y:0});

objects.push({type:"box", x:500, y:0});

objects.push({type:"box", x:550, y:0});

objects.push({type:"box", x:600, y:0});

objects.push({type:"box", x:650, y:0});

objects.push({type:"box", x:700, y:0});

objects.push({type:"box", x:550, y:50});

objects.push({type:"box", x:600, y:50});

objects.push({type:"box", x:650, y:50});

objects.push({type:"box", x:850, y:0});

objects.push({type:"box", x:900, y:0});

objects.push({type:"box", x:1050, y:100});

objects.push({type:"box", x:1100, y:100});

objects.push({type:"box", x:1150, y:100});

objects.push({type:"acorn", x:150, y:0});

objects.push({type:"acorn", x:275, y:200});

objects.push({type:"acorn", x:1100, y:250});

objects.push({type:"bunny", x:400, y:0});

objects.push({type:"bunny", x:1200, y:0});




worldEnd = 1400;

}

Если вы собираетесь сделать пространство игры больше или, возможно, хотите добавить новые уровни, то, вероятно, вы захотите изменить функцию creatworld так, чтобы она считывала данные из внешнего текстового файла вместо того, чтобы создавать каждый элемент в отдельной строке кода. Текстовый файл может определять тип и расположение каждого элемента в отдельных позициях так, как это было сделано в главе 12.

После создания массива objects функция creatObjects просматривает его в цикле и создает все соответствующие клипы.

function createObjects() {

for(var i=0;i

_root.attachMovie(objects[i].type,"object "+i,i);

} }

Функция moveFox - это главный движущий механизм всей игры. В ней идет ряд проверок, которые отслеживают появление определенных событий в игре.
Сначала вызывается функция determineBounds, которой передается значение координат лисы (1). Эта функция возвращает максимальные значения расстояний вверх, вниз, вправо и влево, на которые может передвигаться лиса, не натыкаясь на какой-нибудь блок.
Далее, функция moveFox проверяет, есть ли пустое место под лисой и не находится ли лиса в состоянии падения, что определяется с помощью переменной falling (2). Если это так, то лиса начинает падать.
Если falling равняется true, то вызывается функция checkFall для обработки вертикального движения (3).
Далее, проверяется не нажаты ли стрелки "вправо" и "влево" (4). Если да, то проверяются оба значения foxBounds.left и foxBounds.right, чтобы определить, есть ли место для движения лисы. Также проверяются границы игрового пространства - 0 и worldEnd, - чтобы лиса не выходила за их пределы. Переменная moving принимает значение true только если нажата клавиша со стрелкой. Эта переменная определяет, какая часть клипа "fox" будет проигрываться.
Лиса прыгает при нажатии пробела (5). Прыжок противоположен падению, поэтому переменная fallSpeed принимает положительное значение (когда лиса падает, ее значение меньше ноля). Если в момент нажатия пробела лиса стоит, то запускается анимация прыжка лисицы. Если же лиса находится в движении, когда нажат пробел, то клип лисы остается в позиции шага (то есть она прыгает в том положении, в котором ее застало нажатие на пробел).
Следующий фрагмент кода отвечает за анимацию лисы (6). Если лиса, движется и не падает вниз (не прыгает вверх), то клип лисы переходит к следующему кадру. Благодаря этому ноги лисы "шагают". Если же лиса движется или падает, то клип лисы переходит в первый кадр с изображением стоящей лисы.
В то время как горизонтальная позиция лисы постоянно находится в центре экрана, ее вертикальное положение может меняться. Вертикальное положение лисы задается свойством foxPos.y, а земля находится в вертикальной позиции 0. Значит, чтобы получить свойство _у клипа лисы, мы должны вычесть foxPos.y из значения переменной floor (7).
В конце функции moveFox вызываются еще три функции: moveBunnies отвечает за движение кроликов, drawObjects перерисовывает блоки, кроликов и орехи в соответствии с новым положением игрового поля относительно лисы, а функция getAcorns проверяет, не съела ли лиса какой-нибудь из орехов.



function moveFox() {

(1) // Определяем границы возможного перемещения лисы.

foxBounds = determineBounds(foxPos);

(2) // Если под лисой пусто, она начинает падать.

if ((foxBounds.bottom > 0) and (!falling)) falling = true;

(3)// Падение.

if (falling) checkFall();

(4) // Если нажата левая стрелка, то движемся влево, если там

// нет препятствия.

if (Key.isDownfKey.LEFT)) {

if (foxSpeed < foxBounds.left) {

foxPos.x -= foxSpeed;}

if (foxPos.x < 0) foxPos.x = 0;

fox._xscale = 25;

moving = true;

// Если нажата правая стрелка, то движемся вправо, если там

// нет препятствия.

} else if (Key.isDown(Key.RIGHT)) {

if (foxSpeed < foxBounds.right) {

foxPos.x += foxSpeed;}

if (foxPos.x > worldEnd) foxPos.x = worldEnd;

fox._xscale = -25;

moving = true;

// Если не движемся.

} else {

moving = false;}

(5)// Если стоим на поверхности и нажат пробел - прыгаем,

if (Key.isDown(Key.SPACE) and (!falling)) {

fallSpeed = jumpPower;
// Прыжок = падение вверх

falling = true;

if (Jmoving) {
// Используем анимацию прыжка только

// если лиса не идет.

fox.gotoAndPlay("jump");}}

(6)// Если идет и не падает, то анимируем ходьбу,

if (moving and !falling) { fox.nextFrame();

// Если не идет или падает - кадр со стоящей лисой.

} else if (imoving and !falling) {

fox.gotoAndStop(1);

}

(7)// Позиция лисы по вертикали.

fox._y = floor - foxPos.у;

// Активируем кролика.

moveBunnies ();

// Перерисовываем все объекты в соответствии с новой

// позицией.

drawObjects();

// Проверяем,не съеден ли орех.

getAcorns();}

Функция determineBounds выглядит сложно, но на самом деле она довольно простая. Вначале мы предполагам, что пространство слева, справа и сверху вокруг лисы пустое на расстоянии 1000 пикселов. Также полагаем, что нет пустого пространства под лисой. Вертикальную позицию лисы мы храним в свойстве pos.у.
Далее следует цикл по всем объектам типа box. Вычисляется расстояние от лисы до блока и записывается в переменные dx и dy.
Если блок занимает то же положение по вертикали, что и лиса (другими словами - если он на том же расстоянии от земли), то функция проверяет, находится ли он справа или слева. Далее проверяется, если расстояние до блока справа (слева) меньше текущего значения bounds.right (.left), то значение bounds.right (.left) переопределяется. Аналогично проверяются вертикальные границы.
После того как все блоки были проверены, объект bounds содержит горизонтальные и вертикальные границы для лисы в текущем положении. Например, если bounds.left равно 20, то ближайший к лисе блок справа находится на расстоянии 20 пикселов.
Функция determineBounds написана в достаточно общем виде, чтобы ее можно было использовать как для лисы, так и для кроликов. В качестве аргумента pos функции можно передать как объект foxPos, так и элемент массива objects, например кролика.



function determineBounds(pos) {

// Определяем границы перемещения.

var bounds = {left: 1000 , right: 1000, top: 1000 ,bottom:pos.у};

// Цикл по всем объектам.

for(var i=0;i

// Рассматриваем только блоки,

if (objects[i].type == "box") {

var dx = objects[i].x - pos.x;

var dy = objects[i].y - pos.y;

// Если блок в той же вертикальной позиции,

if ((dy >= 0) and (dy

// Определяем, является ли ближайшим левый блок,

if ((dx+50

bounds.left = Math.abs(dx+50);

// Определяем, является ли ближайшим правый блок.

} else if ((dx >= 0) and (dx < bounds.right)) {

bounds. right = dx-50;

}} // Блок в той же горизонтальной позиции,

if ((dx >= -50) and (dx

// Определяем, является ли ближайшим нижний блок.

if ((dy+50

bounds.bottom = Math.abs(dy+50);

// Определяем, является ли ближайшим верхний блок.
} else if ((dy-50 >= 0) and (dy-50 < bounds.top)) {

bounds.top = dy-50;

}}}}

return(bounds);}

Если лиса находится в воздухе, то независимо от того, прыгает ли она вверх или падает вниз, это состояние рассматривается как падение. Функция CheckFall следит за вертикальным перемещением лисы. Когда игрок прыгает, переменная fallSpeed принимает значение jumpPower, которое равно 60. Таким образом, лиса пытается переместиться на 60 пикселов по вертикали. В каждом кадре, в котором происходит процесс "прыжок/падение", переменная fallSpeed уменьшается на 10, что создает эффект силы тяжести. В конечном счете значение fallSpeed обнулится в верхней точке прыжка и начнет уменьшаться, а лиса устремится вниз.
Если значения скорости падения еще недостаточно, чтобы лиса достигла земли, то падение продолжается. Но как только уровень земли достигнут или пройден, падение прекращается, и вертикальная позиция лисы устанавливается равной положению земли.
Функция checkFall также обращается к свойству foxBound.top, чтобы проверить, нет ли блока над лисой. Если лиса упирается в верхнюю границу возможного движения, то импульс, направленный вверх, пропадает, переменная falispeed обнуляется. Процесс "прыжок/падение" продолжается, но является теперь только падением.



function checkFall() {

// Учитываем силу тяжести.

fallSpeed -= 10;

// Проверяем, есть ли место для падения,
if (fallSpeed > -foxBounds.bottom) {

foxPos.y += fallSpeed;

// Прекращаем падение и устанавливаем лису на уровень земли.

} else {

foxPos.y -= foxBounds.bottom;

fallSpeed = 0;

falling = false;

fox.gotoAndStop(1);

}

// Смотрим, не упирается ли лиса в верхний блок.

if (foxPos.y > foxBounds.top) {

foxPos.y = foxBounds.top;

fallSpeed = 0;

}}

Функция drawobjects создает эффект движения лисы. Она перерисовывает все объекты в соответствии с положением лисы.

function drawObjects() {

// Цикл по всем объектам.

for(var i=0;i

// Устанавливаем горизонтальную позицию в соответствие

// с положением лисы.

_root["object "+i]._x = х = 275 + objects[i].x -foxPos. х;

// Устанавливаем горизонтальную позицию в соответствие

//со значением floor.

_root["object "+i]._y = floor - objects[i].у;
}}

Функция getAcorns просматривает объекты в поисках орехов. Каждый орех проверяется, достаточно ли близко он находится к лисе, чтобы она могла его взять.
Если орех съеден, его свойство type устанавливается в значении used. Этот орех теперь игнорируется всеми функциями, так как они не проверяют объекты типа used.

function getAcorns() {

// Просматриваем все объекты,

for(var i=objects.length-1;i>=0;i--) {

if (objects[i].type == "acorn") {

// Если ближе 30 пикселов - хватаем орех,

if (distance(_root["object "+i],fox) < 30) {

_root["object "+i].play();

objects[i].type = "used";

score += 100;

}}}}

Функция getAcorns вызывает функцию distanse для определения расстояния между лисой и орехом. Эта же функция будет в дальнейшем использоваться функцией moveBunnies.

// Функция для определения расстояния между клипами,

function distance(mc1,mc2) {

d = Math.sqrt(Math.powfmcl._x-mc2._x,2)+ Math.powfmcl._y-mc2._y,2));

return d;

}

Все кролики контролируются функцией moveBunnies. Нет смысла следить за кроликами вне экрана, поэтому перемешаются только кролики, находящиеся не далее чем 275 пикселов от лисы.
Перед перемещением кролика вызывается функция determineBounds для определения границ его возможного перемещения. Если границы позволяют, кролик начинает передвигаться в сторону позиции игрока. Если лиса оказывается достаточно бестолковой, чтобы подобраться близко к кролику, игра заканчивается.



function moveBunnies() {

// Просматриваем все объекты в поисках кроликов.

for(var i=objects.length-l;i>=0;i-) {

if (objects[i].type == "bunny") {

// Перемещаем только видимых кроликов.

if (Math.abs(objects[i].x-foxPos.x) < 275) {

// Движемся в сторону лисы,

if (foxPos.x < objects[i].x) {

var dx = -bunnySpeed;

} else if (foxPos.x > objects[i].x) {

var dx = bunnySpeed;

// Определяем границы.

bunnyBounds = determineBounds(objects[i]);

// Движемся только в пределах этих границ.

if ((dx < 0) and (bunnyBounds.left > Math.abs(dx))) {

objects[i].x += dx;

} else if ((dx > 0) and (bunnyBounds.right > Math.abs(dx))) {

objects[i].x += dx;}

// He подобрался ли кролик достаточно близко к лисе.

if (distance(_root["object "+i],fox) < 30) {

_root.onEnterFrame = undefined;

trace("got ya");
}}}}}


Создание кода



Создание кода


Как и в игре "Космический бой", за пределами рабочего поля находятся клип "actions" и кнопка "button", к которым добавлены соответствующие сценарии. В клипе "actions" содержится одна функция, начинающая уровень, и несколько функций, которые при каждом обращении к кадру перемещают элементы на экране.
onClipEvent(load) {

// Устанавливаем все элементы игры.

_root.startLevel();}

onClipEvent(enterFrame) {

// Перемещаем корабль на один шаг.

_root.shipMove();

// Перемещаем все пули на один шаг.

_root.bulletsMove();

// Перемещаем астероиды на один шаг.

_root.rocksMove();

// Выясняем, произошло ли столкновение.

_root.checkHits(); }
К кнопке "button" прикреплен код, содержащий несколько обработчиков событий on, которые реагируют на нажатие различных клавиш и совершают соответствующие действия.
on (keyPress "") {

// Выстрел.

shipFire() ;}

on (keyPress "") {

// Поворот на 30° вправо.

shipTirn(30);}

on (keyPress "") {

// Поворот на 30° влево.
shipTirn(30) ;}

on (keyPress "") {

// Перемещаем корабль вперед.

shipThrust(); }

on (keyPress "") {

// Корабль останавливается.

shipBreak();}
Когда игрок щелкает по кнопке Play, чтобы начать игру, в кадре "start" можно установить несколько переменных: обнулить количество набранных очков, начать первый уровень игры и указать, что игрок имеет три жизни.
on (press) {

gameLevel = 1;

lives = 3;

score = 0;

gotoAndPlay("play");}
Все функции находятся в сценарии кадра "play" основной временной шкалы. Первая функция вызывается в начале игры, а также сразу после того, как у игрока уменьшилось количество жизней.
Экземпляру клипа "ship" на рабочем поле присвоено имя "ship". В дополнение к стандартным свойствам клипа, таким как _хи_у, вы добавите несколько новых. Например, свойства dx и dy будут отражать расстояние, на которое перемещается корабль по горизонтали и вертикали соответственно.
Функция startLevel создает массивы bullets и rocks, в которых хранится список клипов пуль и астероидов.
Переменная level, используемая командой attachMovie, определяет уровень клипа, а не уровень игры, для этого применяется переменная gameLevel.
Далее для начала уровня создаются большие астероиды. На первом уровне - два астероида, на втором - три и т.д.
Потом в функции shipFire используется переменная timeOfLastFire, чтобы игрок не мог слишком часто стрелять.



function startLevel() {

// Корабль не движется,

ship.dx = 0.0;

ship.dy = 0.0;

// Создаем новые массивы,

bullets = new Array();
rocks = new Array();

// Начинаем игру с нулевым уровнем клипа,
level = 0;

// Добавляем новые астероиды, количество которых на единицу

// больше значения переменной

gamelevel. tor(i=0; i

newRock{100,0,0) ;}

// Можно стрелять сразу же.

timeOfLastFire = 0;}

Следующий набор функций состоит из функций, реагирующих на нажатие клавиш. Все они так или иначе управляют кораблем. Первая функция поворачивает корабль на определенный угол. Если вы еще раз посмотрите, как кнопка "button" вызывала эту функцию, то заметите, что в первом случае функции сообщалось значение 30, а в другом -30. При этом корабль поворачивается на угол 30° в том или ином направлении.

function shipTurn(amt) {

// Поворачиваем корабль,

ship._rotation += amt;}

Следующая функция активизирует ускорители. С помощью функций Math.cos и Math.sin она преобразует угол поворота корабля в горизонтальную и вертикальную компоненты. К сожалению, свойство клипа _rotation представлено в градусах, а две математические функции требует величины в радианах. Для перевода значения из градусов в радианы необходимо из величины „rotation вычесть 90° (чтобы 0° находился в верхней точке окружности, а не в правой), а затем умножить на 2? и разделить на 360.

function shipThrust() {

// Перемещаем корабль туда, куда направлен его нос.

ship._dx += Math.cos(2.0*Math.Pi*(ship._rotation-90)/360.0);

ship._dy += Math.sin(2.0*Math.Pi*(ship._rotation-90)/360.0);

// Показываем, как включаются ускорители,

ship.gotoAndPlay("thrust");}

С другой стороны, когда игрок нажимает клавишу со стрелкой "вниз", можно легко остановить корабль, обнулив свойства dx и dy.

function shipBreak() {

// Останавливаем корабль.

ship.dx = 0;

ship.dy = 0;}

Когда игрок нажимает клавишу Пробел, корабль должен выстрелить. Однако сначала проверяется, достаточно ли времени прошло с момента последнего выстрела.
С помощью клипа "bullet" генерируется пуля, свойства dx и dy создаются и устанавливаются так же, как и для корабля. А в массив bullets добавляется указатель на клип, чтобы потом можно было легко к нему обратиться.



function shipFire() {

// Выясняем, достаточно ли времени прошло

//с момента последнего выстрела.

if (timeOfLastFire+200 < getTimer()) {

// Запоминаем, когда производится этот выстрел.
timeOflastFire = getTimer();

// Создаем пулю.

level++;

attachMovie("bullet", "bullet"+level, level);

// Определяем положение и направление движения пули,

clip = _root["bullet"+level];

clip._x = ship._x;

clip._y = ship._y;

clip.dx = 10.0*Math.cos(2.0*Math.PI*-(ship._rotation-90)/360.0);

clip.dy = 10.0*Math.sin(2.0*Math.PI*(ship._rotation-90)/360.0) ;

// Добавляем элемент массива

bullets, bullets.push(clip);
}}

В каждом кадре корабль перемешается на один шаг: для этого увеличиваются значения свойств _х и _у на величины dx и dy соответственно. Если корабль слишком далеко подвинулся вправо, то есть за пределы рабочего поля, то из значений этих свойств вычитается 550 пикселов, чтобы корабль появился с противоположной стороны экрана. Аналогичные действия производятся для левой, нижней и верхней границ рабочего поля.

function shipMove() {

// Перемещаем корабль по горизонтали или, если нужно,
//на противоположную сторону экрана.

ship._x += ship.dx;

if (ship._x > 550) ship._x -= 550;

if (ship._x < 0) ship._x += 550;

// Перемещаем корабль по вертикали или, если нужно,

//на противоположную сторону экрана.
ship._y += ship.dy;

if (ship._y > 400) ship._y -= 400;

if (ship._y < 0) ship._y += 400;
}

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

function bulletsMove() {

// Передвигаем все пули.

for(i=bullets.length-1; i>=0; i--) {

// Перемещаем пулю по вертикали и горизонтали,

bulletsti]._х += bullets[i].dx;

bullets [ij ._y += bullets [i] .dy;

// Выясняем, вылетела ли пуля за границу экрана,

if ((bullets[i]._x > 550) or (bullets[i]._x < 0) or (bullets[i]._y > 400) or (bullets[i]._y < 0)) {



// Удаляем клип и элемент массива,

bullets[i].removeMovieClip();

bullets.splice(i,1);
}}}

Астероиды создаются с помощью функции newRock, которая, как и функция fire, добавляет новый клип. Однако здесь функция выбирает один из трех эталонов: "rock1", "rock2" и "госк3".
Параметры, передаваемые функции, определяют положение и размер астероида. Направление движения указывается случайным образом, при переходе на следующий уровень скорость астероидов увеличивается.
Астероиды вращаются либо по часовой стрелке, либо против со скоростью, значение которой определяется произвольно.
Для всех астероидов добавляются указатели в массив rocks, чтобы потом можно было к ним обратиться.

function newRock(size,x,у) {

// Создаем клип астероида.

level++;

rockNum = int(Math.random()*3+1);

attachMovie("rock"+rockNum,"rock"+level,level);

// Указываем положение и размер астероида.

clip = _root["rock"+level];

clip._x = x;

clip._y = y;

clip._xscale = size;

clip._yscale = size;

// Определяем скорость и направление движения астероида.

speed = gameLevel*2;

clip.dx = Math.random()*speed+2;

if (math.random() < .5) clip.dx *= -1;

clip.dy = Math.random()*speed+2;

if (math.random() < .5) clip.dy *= -1;

// Устанавливаем направление и скорость вращения,

clip.spin = Math.random()*б-3;

// Добавляем астероид в массив rocks,

rocks.push(clip); }

Для движения астероида используется такой же код, что и для движения корабля. Астероиды, как и корабль, возвращаются на рабочее поле с противоположной стороны, если достигают его границы. Не забывайте поворачивать астероид в соответствии с созданным вами свойством spin.

function rocksMove() {

// Просматриваем все астероиды,

for(i=rocks.length-1; i>=0; i-) {

clip rocks[i].clip;

// Перемещаем астероид по горизонтали и, если нужно,

//на противоположную сторону экрана.

rocks[i]._x += rocks[i].dx;

if (rocks[i]._x > 550) rocks[i]._x -= 550;

if (rocks[i]._x < 0) rocks[i]._x += 550;



// Перемещаем астероид по вертикали и, если нужно,

// на противоположную сторону экрана.

rocks[i]._y += rocks[i].dy;

if (rocks[i]._y > 400) rocks[i]._y -= 400;

if (rocks[i] ._y < 0) rocks [i].__y += 400;

// Вращаем астероид.

rocks[i]._rotation += rocks[i].spin;
}}

Для простоты код, определяющий столкновения, был помешен в одну функцию, которая просматривает все астероиды и пули и с помощью команды hitTest проверяет, попала ли пуля в один из астероидов.
Когда пуля попадает в астероид, его размер и положения записываются, а сам астероид удаляется. Если размер астероида превышал 25%, в этой же точке создается два новых астероида, каждый из которых в два раза меньше своего предшественника. Направление движения и вращения каждого астероида выбирается случайным образом.
Функция checkHits также проверяет, не столкнулся ли какой-либо астероид с кораблем, если да, то уменьшается количество жизней, и ролик переходит к кадру "ship hit". Если не осталось ни одной жизни, то -к кадру "game over".
И в конце функция checkHits проверяет, остались ли элементы в массиве rocks. Если нет, значит, игрок разрушил все астероиды, и ролик переходит к кадру "level over".

function checkHits() {

// Просматриваем все астероиды,

for (j=rooks.length-1; i>=0; i--) {

// Просматриваем все пули.

for (j=bullets.length-1; j>=0; j--) {

// Выясняем, попала ли пуля в астероид.

if (rocks[i].hitTest(bullets[j]._x,bullets[j]._y,true)) {

// Удаляем пулю.

bullets[j].removeMovieClip();

bullets.splice(j);

// Опредляем размер и положение новых астероидов.

newsize = rocks[i]._xscale / 2 ;

x = rocks[i]._x;

у = rocks[j]._y;

// Удаляем астероид.

rocks[i].removeMovieClip();

rocks.splice(i,1);

// Создаем на этом месте два новых астероида,

if (newsize >= 25) {

newRock(mewsize,x,y);

newRock(mewsize,x,y);}

// Увеличиваем количество очков. score++;

// Больше не нужно проверять, попала ли пуля

//в какой-нибудь астероид.

break;}}

// Определяем, столкнулся ли корабль с астероидом,



if (rocks[i].hittest(ship._x, ship._y, true)) {

// Выясняем, остались ли еще жизни,

if (lives < 1) {

removeAll();

gotoAndPlay("game.over");

// Жизнь еще осталась,

// уменьшаем количество жизней на единицу.

} else {

removeAll();

lives--;

gotoAndPlay("ship hit");
}}}

// Проверяем, остались ли еще астероиды,

if (rocks.length == 0) {

removeAll();

gotoAndPlay("level over");

gameLevel++;

}}

Функция removeAll используется непосредственно перед функцией checkHits, которая переводит ролик к следующему кадру. С экрана удаляются все астероиды и пули, чтоб они не заслоняли текст последующих кадров.

function removeAll() {

// Удаляем все клипы пуль,

for (i=0; i

bullets[i].removeMovieClip();}

// Удаляем все клипы астероидов.

for (i=0; i

rocks[i].removeMovieClip();
}}


Создание кода Код инициализируется




Создание кода Код инициализируется

// Загружаем игру. _root.initGame();

}

onClipEvent (enterFrame) {

// Перемещаем перекрестие.

_root.moveCursor();

// Перемещаем все пули.

_root.moveBullets();

//С вероятностью 10% создаем новый астероид,

if (Math.random() < .1) _root .createRock() ;

// Перемещаем все астероиды.

_root.moveRocks();
}
Кнопка "button" реагирует на нажатие клавиши Пробел и вызывает функцию основной временной шкалы.
on (keypress "") {

// Нажатие клавиши Пробел означает выстрел,

fire () ;

}
Вы обнаружите все функции в основной временной шкале. Функция initGame создает массивы, в которых будет храниться информация об астероидах и пулях. Она помешает клипы "foreground" и "cursor" поверх остальных, таким образом оказывается, что астероиды и пули располагаются под ними. Будет казаться, что они находятся вне космического корабля.
Восстанавливаются значения переменных damage и hits. Переменная level не имеет отношения к уровню игры, а используется для определения вновь создаваемого клипа. Каждый новый клип - пуля или астероид - помешается на новый уровень.
С помощью команды Mouse.hide () с экрана удаляется обычный курсор, вместо этого положение курсора вы будете определять с помощью клипа.
function initGame() {

// Создаем массивы для пуль и астероидов,
bullets = new Array();

rocks = new Array();

// Помещаем перекрестие и кабину корабля поверх остальных

// элементов.

_root["foreground"].swapDepths(9999999);

_root["cursor"].swapDepths(9999999);

// Устанавливаем переменные,

level = 0;

damage = 0;

hits = 0;

// Убираем обычный курсор, вместо него отображаем перекрестие.

Mouse.hide();}
При каждом обращении к клипу "actions" вызывается функция moveCursor, которая помешает клип "cursor" в точку, где находится курсор мыши. Игрок использует такой курсор, чтобы прицеливаться.
function moveCursor() {

// Перемещаем перекрестие в точку,

// где находится курсор мыши.

cursor._x = _xmouse;

cursor._y = _ymouse;




}

Кнопка "button" при нажатии клавиши Пробел вызывает функцию fire, в которой определяется положение курсора и создается пара новых клипов "point" для пуль. Кроме того, в массив bullets добавляются следующие элементы: исходное положение, конечное положение, пройденное расстояние и имя клипа для каждой пули.

function fire() {

// Определяем положение мыши.

х = _xmouse;

y = _ymouse;

// Создаем левую пулю.

level++;

attachMovie("point","bullet"+level,level);

bullets.push({startx:50, starty:350, destx:x, desty:y,dist:1.0, clip: "bullet"+level});

// Создаем правую пулю.

level++;

attachMovie("point","bullet" + level,level) ;

bullets.push({startx:500, starty:350, destx:x, desty:y, dist:1.0, clip: "bullet"+level});

}

После того как пуля выпушена, ее движением во всех кадрах управляет функция moveBullets, которая использует массив bullets, чтобы отслеживать путь каждой пули. В каждом кадре значение свойства dist уменьшается на 40% от своего предыдущего значения. Пуля отображается между своим исходным и конечным положением в зависимости от значения dist. Если это значение равно 1,0, пуля находится в исходном положении, а при 0,0 - в конечном.
Однако когда значение свойства dist становится равным 0,01, считается, что пуля практически закончила свой путь. В этот момент вызывается функция checkForHit, чтобы определить, попадет ли пуля в астероид или нет. Независимо от результата пуля удаляется из массива и ролика.
Эта игра не претендует на трехмерную модель реального пространства. Она, скорее, воссоздает типичную аркадную игру.

function moveBullets() {

// Перемещаем все пули.

for (i=bullets.length-1; i>=0; i--) {

// Увеличиваем пройденное расстояние на 40%.

bullets[i].dist *= .4;

// Если пуля оказалась слишком далеко от астероида,

// удаляем ее.

if (bullets[fi].dist < .01) {

checkForHit(bullets[i].destx, bullets[i].desty);

_root[bullets[i].clip].removeMovieClip();

bullets.splice(i,1);



// Помещаем пулю ближе к цели.

} else {

bullets[i].x = bullets[i].dist*bullets[i].startx + (1.0-bullets[i] .dist)* bullets[i] .destx;

bullets[i].у = bullets[i].dist*bullets[i].starty + (l.0-bulletsfi].dist)* bullets[i].desty;

_root [bullets[i] .clip] ._x = bulletsfi] .x; _root[bullets[i].clip]._y = bullets[i].y;

}}}

Астероид создан так же, как и пуля. Однако координаты появления и исчезновения астероида выбираются случайно. Стартовая точка находится на расстоянии 25 пикселов по горизонтали и вертикали от центра экрана. Конечная точка отстоит от центра на расстоянии 550 пикселов по горизонтали и 400 по вертикали, что ровно в два раза больше размера рабочего поля. Это означает, что астероид появляется всегда в районе центра экрана, но может финишировать в любой точке вне видимой его области.

function createRock() {

// Задаем случайное положение для астероида,

startx = Math.random()*50+250;

starty = Math.random()*50+175;

// Задаем случайное направление движения,

destx = Math.random()*1100-275;

desty = Math.randomO*800-200;

// Добавляем астероид.

level + + ;

attachMovie("rock","rock"+level,level++);

rocks.push({startx: startx, starty: starty, destx: destx,desty: desty, dist: .01, clip: "rock"+level});

}

Подобно функции moveBullets функция moveRocks использует свойство dist каждого астероида, чтобы передвинуть его. Однако со временем он приближается к экрану и его начальное значение 0,01 в каждом кадре увеличивается на 10%. Помимо положения астероида его свойства _xscale и _yscale также зависят от dist, это делает возможным увеличивать астероид и создавать иллюзию его приближения к кораблю.
Если значение dist становится больше 1,0 и астероид все еще находится в видимой области экрана, считается, что астероид попал в корабль. Астероид взрывается, и значение переменной damage увеличивается. Если значение переменной damage больше или равно 20, то игра заканчивается.

function moveRocks() {

// Перемещаем все астероиды,

for(i=rocks.length-1;i>=0;i-) {



// Уменьшаем расстояние до корабля на 10%.

rocks[i].dist *= 1.1;

// Проверяем, может ли астероид задеть корабль.
if (rocks[i].dist > 1.0) {

// Проверяем, ударил ли астероид корабль,

if (rocks[i].destx > 0 and rocks[i].destx < 550 and rocks[i].desty > 0 and rocks[i].desty < 400) {

// Взрываем астероид и увеличиваем количество

// повреждений.

_root[rocks[i].clip].gotoAndPlayf"explode blue");

damage++;

foreground.displayDamage = damage;

// Смотрим, превысило ли количество повреждений

// допустимый уровень.

if (damage >= 20) {

removeAllRocks();

Mouse.show();

gotoAndStop("game over");}

// Если астероид не попал по кораблю, то убираем его.

} else {

_root[rocks[i].clip].removeMovieClip();

}

// Убираем элемент из массива,

rocks.splice(i,1);

// Перемещаем астероид.

} else {

rocks[i].x = (1.0-rocks[i].dist)*rocks[i].startx + rocks [i].dist*rocks[i].destx;

rocks[i].y = (1.0-rocks[i].dist)*rocks[i].starty + rocks[i].dist*rocks[i].desty;

_root[rocks[i].clip]._x = rocks[i].x; _root[rocks[i].clip]._y = rocks[i].y;

// Увеличиваем астероид.

_root[rocks[i].clip]._xscale = 100*rocks[i].dist;

_root[rocks[i].clip]._yscale = 100*rocks[i].dist;

}}}

Когда пуля достигает своей цели, вызывается функция checkForHit. Она проверяет все астероиды: находится ли один из них там же, где и пуля. Если да, астероид взрывается и удаляется из массива, увеличивается значение переменной hits.

function checkForHit(x,у) {

// Просматриваем все объекты-астероиды

//на предмет попадания по ним.

for(j=rocks.length-1; j>=0; j--) {

// Выясняем, попадет ли пуля в этот астероид,

if (_root[rocks[j].clip].hitTest(x,y)) {

// Если да, взрываем астероид

//и удаляем из массива.

hits++;

foreground.displayHits = hits;

_root[rockfj].clip].gotoAndPlay("explode red");

rocks.splice(j,1);

}}}

В конце каждой анимации взрыва (Рисунок 16.2) небольшой сценарий вызывает функцию killRock, которая удаляет любой вызывающий ее клип. Таким образом, можно удалить астероид сразу же, как только он взорвался.

function killRock(clip) {

вызываем эту функцию,

// Когда астероид взорвался,
// чтобы удалить его.

clip.removeMovieClip();
}

Когда игра заканчивается, вызывается функция removeAllRocks, чтобы в кадре "game over" не отображались оставшиеся пули и астероиды.

function removeAllRocks() {

// Удаляем все астероиды.

for (i=rocks.length-1; i>=0; i--) {

_root[rocks[i].clip.removeMovieClip();

}}


В игре "Астероиды" действующие



Рисунок 16.3 В игре "Астероиды" действующие объекты - небольшой корабль и астероиды различных размеров


В игре "Космический бой" вы



Рисунок 16.1 В игре "Космический бой" вы - пилот космического корабля, который пытается пролететь сквозь поле астероидов


fla всего лишь несколько библиотечных



Подготовка ролика

В ролике Mazechase. fla всего лишь несколько библиотечных эталонов, основными из которых являются фон, лиса, кролик и ягода.

fla всего лишь несколько библиотечных
Создать фон просто, но делать это следует аккуратно. Представьте себе экран в виде сетки из квадратов со стороной 25 пикселов. Ягоды располагаются на всех пересечениях линий сетки, кроме тех, которые расположены на фоне, а не на дорожке лабиринта.
На Рисунок 16.5 представлен клип "background", для которого отображена сетка размером 25x25. Сетку можно изменить, выбрав пункт меню View -> Grid-> Edit Grid (Вид -> Сетка -> Редактировать).

в этой игре означает прокрутку



в этой игре означает прокрутку
Перемещение игрока в этой игре означает прокрутку всего игрового пространства, в то время как игрок на самом деле все время находится в центре экрана. Ощущение движения лисы возникает за счет того, что в игре движется все, кроме самой лисы.
Каждый орех добавляет игроку 100 очков. Лиса должна собирать орехи и избегать кроликов.
Дизайн игрового пространства очень легко усложнить. После того как мы закончим разбирать процесс создания этого ролика, вы можете сделать собственный вариант уровня игры.

В игре подсчитывается число разрушенных



В игре подсчитывается число разрушенных
В игре подсчитывается число разрушенных астероидов и число астероидов, ударившихся о корабль. Некоторые астероиды могут пролететь сверху, снизу, справа или слева от корабля, не причиняя никакого вреда. Игра заканчивается тогда, когда произойдет 20 столкновений.

Игрок может повернуть корабль вправо



Игрок может повернуть корабль вправо
Игрок может повернуть корабль вправо или влево, включить ускорители и таким образом переместить корабль вперед, а также может стрелять по астероидам. После использования ускорителя корабль продолжает двигаться по инерции. Если игрок повернет корабль и включит ускорители, его скорость изменится из-за сообщенного ему импульса. Нажав клавишу со стрелкой "вниз", игрок в любой момент может остановить корабль.
Пули - это небольшие клипы, исходное положение которых совпадает с положением корабля; пули движутся туда, куда направлен нос корабля в момент выстрела. Количество пуль в игре не ограничено, но игрок может выстрелить снова только после того, как пройдет время, необходимое на перезарядку.
Изначально размер астероида составляет 100%, скорость и направление движения произвольные. Когда по нему первый раз попадает пуля, он распадается на два .астероида, размер каждого из которых - 50%, направление их движения также произвольно. Эти астероиды, в свою очередь, могут распасться на два более мелких, размер которых будет составлять 25% от исходного астероида. Если после этого в астероид попадает пуля, он взрывается. Когда все астероиды будут разрушены, игрок сможет перейти на следующий уровень. Однако если астероид столкнется с кораблем, уменьшится количество жизней.

Выполнение и оптимизация

Был получен при проигрывании ролика



Рисунок 17.1 был получен при проигрывании ролика в окне предварительного просмотра. Что же произойдет, если открыть окончательный swf-файл с помощью Flash-проигрывателя вне программы Flash? Вы получите несколько лучший результат, примерно 42 кадр/сек. То есть ролик воспроизводится Flash-проигрывателем быстрее, чем в окошке предварительного просмотра. Проигрывание Flash-ролика в браузере (Internet Explorer 5.1) дает меньшую скорость - примерно 22 кадр/сек.
Все числа и параметры скорости работы относятся к компьютеру, на котором работал автор. У вас, естественно, могут быть совсем другие значения.
Конечно, эти числа имеют значение только при сравнении друг с другом. Когда те же действия производились на других компьютерах (как под управлением ОС Windows, так и Macintosh), результаты были другими: где-то ролик проигрывался быстрее, где-то - медленнее. Вы даже не можете быть уверены, что скорость анимации будет одинаковой на другом компьютере того же производителя и такой же модели. У компьютера может быть другое разрешение, на нем могут больше работать в сети (это уменьшит скорость воспроизведения ролика при загрузке из сети), или диск может быть больше фрагментирован. Проводить тест установления контрольных точек имеет смысл только на одинаковых компьютерах при одинаковых условиях.


Демонстрация списка лучших игроков


Демонстрация списка лучших игроков


Исходный файл:
На Web-сайте надо разместить две программы, с помощью которых и будет формироваться список лучших игроков. Первая программа представляет собой демонстрационный ролик, показывающий, как набранные очки заносятся в базу данных и как они затем из нее извлекаются.
Кадр такого ролика, Highscores.fla, изображен на Рисунок 17.5. Область слева внизу позволяет вам ввести новый результат, а в области справа отображается список лучших игроков.


Добавление списка лучших игроков в игру



Добавление списка лучших игроков в игру


Исходный файл:
Как пример еще одной игры с использованием списка лучших игроков приведем файл Whackafoxwithscores.fla. Это все та же игра "Поймай лису", которую мы рассматривали в главе 8, но здесь она содержит еще и список лучших игроков.
К этой игре достаточно легко добавить список лучших результатов, потому что она подразделяется на три части. Сцена "Начало игры" была изменена таким образом, что уже при загрузке игры выводится список лучших игроков. Поэтому к стандартной команде stop О в начале первого кадра этой сцены вам понадобится добавить те коды, которые будут отвечать за отображение списка лучших игроков:
// Указываем путь к программе getscores.pl.

url = "getscore.pl";

// Запрашиваем данные. loadVariables (url,this);

// Выводим на экран информацию о том, что по кнопке щелкнули.

highscoresText = "Getting High Scores...";

stop() ;
Текстовое поле под названием highscoresText добавляется посередине экрана (Рисунок 17.6).


Другие типы экранов загрузки



Другие типы экранов загрузки


Только что рассмотренные экраны загрузки - стандартные. Кроме них существует множество других вариантов. Самый простой: можно отображать только статический кадр с надписью "Loading, Please Wait..." (Идет загрузка, пожалуйста, подождите...). Когда загрузка будет завершена, ролик просто перейдет следующему кадру.
Этот вариант можно усложнить и поместить какую-либо повторяющуюся анимацию в кадр.
Еще более сложный вариант: каждый кадр будет отображать различные этапы загрузки. Например, будет собираться некая картинка: чем ближе загрузка будет подходить к концу, тем более целостным будет становиться изображение.
Возможно, для создания таких экранов загрузки функции getBytesTotal () и getBytesLoaded () не понадобятся. Вместо них можно воспользоваться функцией ifFrameLoaded (), чтобы узнать, загружены ли все графические объекты определенного кадра. Эта функция пригодится, если вы хотите, чтобы пользователь начал какую-то часть игры, но не смог продолжить до тех пор, пока необходимые кадры не будут готовы.
Но лучше сделать время загрузки полезным для игрока. Например, поместить в кадр руководство к игре. Вместо того чтобы просто сидеть и смотреть, как заполняется полоса загрузки, игрок сможет узнать полезную и необходимую для него информацию.




Использование альтернативных возможностей



Использование альтернативных возможностей



Основная концепция оптимизации - существование нескольких путей выполнения одной и той же задачи. Для большинства случаев это действительно так. Например, если вы хотите переместить графический объект с одной стороны рабочего поля на другую, можно использовать анимацию движения (motion tween) или написать код на языке ActionScript.
Когда вы создаете что-либо в программе Flash, у вас есть две альтернативы. Первая - выполнение одной задачи разными способами. Например, результат перемещения графического объекта с помощью построения промежуточных изображений или ActionScript одинаков.
Другая альтернатива - изменение программы, чтобы результат был похож, но все-таки отличался. Например, если графический объект движется по экрану слишком медленно, можно его уменьшить, и теоретически скорость его перемещения возрастет.
Ключ к успеху оптимизации состоит в том, что вы должны четко представлять себе существующие альтернативы и тщательно взвесить их преимущества и недостатки. Для определения преимуществ можно воспользоваться техникой, которая называется "установление контрольных точек".


Экран загрузки



Экран загрузки


Хотя вы всегда должны стремиться, чтобы размер готового файла был минимальным, у вас наверняка будут ролики, загрузка которых займет больше, чем несколько секунд при работе через модем. Если игра имеет размер в сотни килобайт, у некоторых пользователей ее загрузка может занять несколько минут.
Для таких больших роликов разработчики, как правило, создают экраны загрузки во Flash или Shockwave. Обычно экран загрузки представляет собой первый кадр или сиену ролика, таким образом, это первые данные, которые пройдут через модем пользователя. Затем на экране появляется надпись "Loading Game..." (Загрузка игры), чтобы пользователи знали, что происходит.
Во время загрузки ролика вы можете предоставлять еще какую-либо информацию пользователю. С использованием языка ActionScript можно показывать, какая часть ролика уже загружена или какую часть еще надо загрузить.
Создание полосы загрузки
Исходный файл:
На Рисунок 17.3 показан первый кадр ролика Loader1.fla. Этот кадр появится перед пользователем после того, как несколько килобайт ролика будут загружены. Затем с помощью строки, расположенной в центре экрана, пользователь увидит, как идет процесс загрузки.


Как обойти защиту от копирования



Как обойти защиту от копирования


Этот план зашиты от копирования не безупречен. Есть один простой способ, с помощью которого вор может обойти вашу защиту. Он может оставить игру на вашем сервере и создать свою собственную HTML-страницу, в которой параметр src тэгов OBJECT И EMBED указывает на swf-файл, расположенный на вашем сайте. Пользователь будет хранить у себя HTML, а вы - swf-файл. Свойство _url будет сообщать, что игра находится на вашем сайте, то есть зашита от копирования действовать не будет.
Однако это далеко не лучший способ кражи, потому что файл все равно остается под вашим контролем. Вы всегда можете его переименовать или переместить в другое место. Если вы обнаружили, что кто-то украл вашу программу подобным образом, вы можете "поиграть" с ним: переместить игру в другое место сайта, обновить свою HTML-страницу, чтобы путь к игре был верным, а затем заменить старый swf-файл файлом, в котором будет содержаться грозное сообщение для посетителей сайта вора.




Как работает список лучших игроков



Как работает список лучших игроков


Список победителей можно создать разными способами. Один из них -написать программу на основе языка Java с использованием XML Socket, с которой ваша Flash-игра могла бы поддерживать связь через объект XML. Другой вариант - разместить на связанной с сервером Web-странице вашу Flash-игру, которая посылает и получает количество очков с помощью языка сценариев на основе Java.
Наиболее простым способом вести учет очков будет использование языка Perl, ориентированного на работу в сети. Именно об этом и пойдет сейчас речь. Значит, вам придется познакомиться с языком Perl и работой CGI-сценариев.
Сценарии CGI (Common Gateway Interface - общий шлюзовой интерфейс) - это программы, которые выполняются на стороне сервера по запросу клиента. Язык Perl (Practical Extraction and Report Language) - это язык программирования, схожий с языком ActionScript, но предназначенный для использования на Web-серверах. Обычно программы, написанные на языке Perl, имеют расширение .pi или .cgi, а не .html, которое характерно для простой Web-страницы. Когда пользователь просматривает программу, написанную на языке Perl, то, в отличие от текста программы, сохраненной как HTML-страница, ее команды воспринимаются сервером как команды языка Perl. Если Perl-программа получает какой-либо результат, он отсылается обратно на Web-сервер или, как в данном случае, передается Flash-ролику.
Язык Perl достаточно сложен, и его рассмотрению стоит посвятить отдельную книгу (и не одну). В действительности о Perl написано гораздо больше книг, чем о Rash. Если вы хотите получить более подробную информацию о Perl, идите в ближайший книжный магазин и выбирайте подходящую книгу.
В данном примере для составления списка лучших игроков используются две программы на языке Perl. Первая - Submitscore.pl - отвечает за получение нового результата игры из ролика Flash. Вторая программа, Get-scores, pi, отбирает 10 лучших результатов и отсылает обратно во Flash-ролик. Каждой из этих программ необходимо иметь доступ к небольшой базе данных, где будут храниться заработанные игроками очки. CGI-программы обычно обращаются к базе данных, находящейся на сервере, например СУБЛ MySQL. Однако в нашем примере используется более простой вариант - обычный текстовый файл. Каждая строка этого текстового файла содержит отличный от других строк результат игры. Каждая запись включает: имя игрока, набранные им очки и время, когда данный результат был занесен в базу данных. Имя игрока и его очки заносятся в программу Submitscore.pl из Flash-ролика, а время программа вводит самостоятельно.
Программа Getscores.pl просматривает текстовый файл с результатами игры и выполняет две функции. Во-первых, она анализирует каждую
запись и переписывает текстовый файл, убирая результаты, слишком долго находящиеся в базе данных. Это позволяет периодически обновлять список лучших игроков. Во-вторых, программа Getscores.pl размещает десять лучших результатов по порядку и затем отправляет их обратно во Flash-ролик. Именно этот список демонстрируется игрокам.
Мне нравится идея исключать из базы данных устаревшие записи. Это не дает возможность одному или нескольким рекордам лидировать в списке лучших результатов на протяжении нескольких лет. Кроме того, подобный подход побуждает хороших игроков вновь и вновь возвращаться к игре, чтобы снова побороться за право называться победителем.


Определение версии Flash-плагина



Определение версии Flash-плагина


Исходный файл:
Язык ActionScript во Flash 4 и 5 отличался так сильно, что ролик со сложным сценарием, созданный во Flash 5, скорее всего вообще бы не работал при использовании плеера четвертой версии. Однако Flash 5 и Flash MX достаточно сходны для того, чтобы ролик с кодом, созданным в версии MX, мог достаточно часто работать во Flash 5-плеере.
Это создает интересную проблему. Довольно легко сделать игру во Flash MX, которая будет работать у многих людей с Flash 5-плеером до какого-то момента. Когда же попадется участок кода, требующий Flash MX-плеера, игра или перестанет работать, или начнет вести себя непредсказуемым образом. Как узнать версию плеера Необходимо убедиться, что у пользователей установлен плеер шестой версии, прежде чем он начнет игру. Сделать это можно с помощью функции getVersion().
Функция getVersion () возвращает строку вида MAC 6,0 21,0. Первое слово - это аббревиатура платформы, обычно MAC или WIN. Вторая часть - это версия Flash-плеера, в настоящий момент запущенная на пользовательской машине.
Каждая часть номера версии отделена запятыми. Это создает трудности при сравнении имеющейся версии с требуемой. Как вы, например, определите, MAC 6,0 21,0 больше или равно версии 6?
С помощью некоторых операций со строкой вы можете перевести значение, возвращаемое функцией getVersion () во что-нибудь более пригодное к использованию. Следующая функция разбивает строку, используя пробелы и запятые как разделители. Она создает переменную -объект, к которому вы можете легко обращаться.
function getVersionNumber() {

// Определяем позиции запятых и пробелов.

spacePos = getVersion().indexOf(" ");

firstComma = getVersion().indexOf(",");

secondComma = getVersion().indexOf(",",firstComma+1);

thirdComma = getVersion().indexOf(",",secondComma+1);

lastChar = getVersion().length;

// Создаем объект version.

version = new Object();

version.platform = getVersion().substring(0,spacePos);

version.n1 = getVersion().substring(spacePos+1,firstComma);

version.n2 = getVersion().substring(firstComma+1,secondComma);

version.n3 = getVersion().substring(secondComma+1,thirdComma);

version.n4 = getVersion().substring(thirdComma+1,lastChar);

return(version);

}
В результате работы getVersionNumber получится примерно следующее:
{platform: MAC, nl: 6, n2: 0, пЗ: 21, п4: 0}
Таким образом, если вы хотите получить главный номер версии Flash, просто используйте getVersionNumber () .n1.


Оптимизация в ActionScript


Оптимизация в ActionScript


Исходный файл:
Тесты с помощью установки контрольных точек позволяют также выяснить, влияет ли на скорость ролика выбор альтернативных алгоритмов. Техника такая же: измеряем время на выполнение задачи и сравниваем его с временем, затраченным на выполнение задания альтернативным способом. Допустим, вы заметили, что в коде используется следующее сравнение:
if (myVariable == true) {
Понятно, что то же самое можно записать проще:
if (myVariable) {
Иногда первую строчку легче понять, но почему второй вариант увеличивает скорость работы программы? Вы могли использовать сотни или тысячи подобных сравнений внутри циклов и т.д.
Дело в том, что одно сравнение может экономить всего лишь несколько миллисекунд, то есть при сравнении одного варианта с другим разница едва ли будет заметна. Так что необходимо сравнивать тысячи подобных условий.
Следующий фрагмент построен для сравнения типа "истина/ложь". Сначала создается массив из 5000 элементов вида false и true. Этот процесс при подсчете времени учитываться не будет. Время засекается после того, как массив был создан. Измеряем время на подсчет количества элементов, равных true. Для этого используется оператор = =. В конце сценарий выводит время, которое потребовалось для 5000 сравнений.
function benchMacrk1() {

// Создаем новый массив.

testArray = new Array;

// Заполняем массив значениями true или false,

temp = false;

for(I = 0; I< 5000 ; I + +) {

testArray.push(temp);

if (temp == false) {

temp = true;

} else if (temp == true)

temp = false; }}

// Определяем время начала проверки.

startTime = getTimer();

// Подсчитываем, сколько значений true находится в массиве.

count = 0;

for(1=0; I
if (testArray[I] == ture) {

count++;}}

// Определяем время, потребовавшееся для проведения проверки.
totalTime = getTimer() - startTime;

// Отображаем результаты.

trace("Benchmark Test: using ==")

trace(:Total Time: " + totalTime + “ms”);}

Сам по себе результат ничего не значит. Однако вы также создадите функцию benchmark2, в которой строчка if (testArray [I] == true) { будет заменена на if (testArray [I]) {. Это единственная разница между функциями. Следовательно, если скорость выполнения сценария изменится, это произойдет вследствие замены строчек.
В результате оказалось, что второй вариант быстрее. На моем компьютере функции benchmark1 понадобилось 978 миллисекунд, a benchmark2 -958. Использование оператора == замедляет процесс сравнения истинных и ложных значений. Посмотреть, как работает приведенный выше код, в файл Benchmark3.fla.
Такие тесты с помощью установки контрольных точек проводят все профессиональные программисты независимо от того, на каком языке они пишут. Если вы часто пользуетесь языком ActionScript, будет полезно определить, какие методы работают быстрее. После проведения подобных тестов можно обобщить полученную информацию, которая позволит оптимизировать ваши Flash-игры.




Полоса, отражающая прогресс загрузки



Рисунок 17.3 Полоса, отражающая прогресс загрузки и текстовое поле, сообщают пользователю о том, как проходит загрузка ролика

Полоса, отражающая прогресс загрузки



Код для этого довольно прост. С помощью функции getBytesTotal () можно определить, каков размер клипа. Если перед функцией getBytes Total () поставить префикс _root, это позволит выяснить объем всего ролика. Чтобы определить, сколько килобайт уже загружено, надо воспользоваться функцией getBytesLoaded().
В ролике Loaderl.fla на рабочем поле находится экземпляр клипа, который представляет собой простой прямоугольник. Он называется "Progress Bar Fill" (Заполнение полосы загрузки). Этот экземпляр был создан так, чтобы центр клипа располагался в верхнем левом углу прямоугольника. Точно также был создан индикатор запаса горючего в игре "Луноход" (см. главу 16). Так как центр клипа находится в верхнем левом углу прямоугольника, свойству _xscale можно присвоить любое значение от 0 до 100, ширина клипа будет изменяться, то есть он будет заполнять область, расположенную справа от его центра.
К экземпляру клипа прикрепите небольшой сценарий, который с помощью функции getBytesLoaded() основной временной шкалы выяснит, сколько байт уже загружено, и в соответствии с этим изменит вид прямоугольника. Когда весь ролик будет загружен, временная шкала перейдет к кадру "complete" (Рисунок 17.4). В этом кадре появляется сообщение о том, что загрузка завершена. Для того чтобы начать игру, пользователь должен щелкнуть по кнопке, расположенной в этом кадре. Такая кнопка -очень полезный элемент ролика, так как во время долгой загрузки пользователь может не смотреть на монитор, а вы бы не хотели, чтоб он пропустил начало анимации.


Последний кадр обновленной игры



Рисунок 17.7 Последний кадр обновленной игры "Поймай лису" дает игроку возможность записать результат своей игры
Набранные очки пересылаются серверу таким же образом, как и в демонстрационном файле Highscores.fla. Отличие заключается в добавлении команды play(), благодаря которой происходит смена кадра. Второй кадр аналогичен первому, за исключением того, что здесь отсутствует кнопка Submit Score. Это сделано для того, чтобы игрок не мог несколько раз щелкать по кнопке, вводя таким образом несколько результатов.


Программы на языке Perl



Программы на языке Perl


Исходные файлы:
Программы Submitscore.pl и Getscores.pl, написанны на Perl. Чтобы эти программы работали на вашем сервере, понадобится загрузить эти файлы и отладить их. Если вы прежде этим не занимались, проконсультируйтесь с администратором сети или Internet-провайдером. Учтите, что не все серверы могут выполнять программы, созданные с помощью CGI. Если вы пользуетесь услугами дешевого хостинга или ваш провайдер предоставляет бесплатное Web-пространство для своих пользователей, будьте готовы к тому, что CGI-программы не будут работать на вашем сервере.
Хотя этот раздел не называется "Руководство по программированию на Perl для начинающих", ниже приведены листинги программ Submitscore.pl и Getscores.pl с краткими пояснениями, как работает каждая из их частей. Если вы незнакомы с программами, использующими стандарт CGI, обратитесь за разъяснениями к людям, сведущим в этой области.
Комментарий в языке Perl начинаются с символа #. Программа Submit-score.pl начинается с комментария, который на самом деле используется Web-сервером для определения адреса программы, интерпретирующей сценарии Perl. Нижеприведенный путь широко распространен и должен работать на вашем сервере. Если возникнут какие-либо проблемы, свяжитесь с вашим Internet-провайдером.
#!/usr/bin/perl
Первая строка текста самой программы начинается с отправки результативных данных обратно во Flash-ролик. Именно за это отвечает выражение Content-type: text /html, за которым следуют две пустые строки. Первая строка любого файла, посылаемого с Web-сервера, сообщает
браузеру (в данном случае Flash-ролику) о типе передаваемых данных. В этом примере передаваемые данные - обычный текст.
# Устанавливаем метку для текстового документа, отсылаемого

# назад браузеру.

print "Content-type: text/html\n\n";
Затем данные из Flash-ролика передаются в программу, где для их временного хранения используется переменная $get_data:
# Получаем переданные данные.

$get_data = $ENV{'QUERY_STRING'};



Когда данные пересылаются через сеть, они обычно кодируются таким образом, чтобы не допустить их некорректную передачу. Буквы не кодируются в отличие от пробелов, которые передаются как знак "+", и небуквенно-цифровых символов, передающихся как символьные последовательности, например %ОА. Чтобы раскодировать данные, необходимо дополнить программу следующими строками:

# Преобразуем закодированные символы.

$get_data =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack ("С",hex($l))/eg; #get_data =~ tr/\+/ /;

Далее нужно получить текущее время, измеренное на Unix-сервере в секундах, прошедших с 01.01.1970. Эти данные вы будете использовать как то время, когда был получен результат. Лучше оперировать именно этим показателем, а не временем, которое показывает сам Flash-ролик: игроки могут находиться в разных часовых поясах, а у некоторых из них может быть неправильно установлено время на их компьютерах.

# Вносим в базу данных время, оказываемое сервером, в секундах. $server_time = time();

Следующий шаг - организация цикла, который берет последовательность символов, хранящуюся в переменной $get_data, и разделяет ее на части. Например, такая последовательность имеет вид: name=Gary&score=967. Она разделяется на два элемента: до и после символа &, а затем каждый из этих элементов подразделяется еще на два - на ключ и на значение, теперь уже благодаря символу =.

# Обрабатываем полученные данные и создаем массив.

@split_data = split("&",$get_data);

foreach $data_item (@split_data)

{

($key, $value) = split("=",$data_item)

$info{$key} = Svalue;

}

Текстовой базой данных, где хранятся лучшие результаты, служит файл Highscore.txt. Чтобы запустить этот файл, загрузите его и настройте таким образом, чтобы с помощью программ на языке Perl можно было вводить в него информацию. Для этого предназначены следующие кодовые строки:

# Определяем имя файла, который будет содержать базу данных.

$filename = "highscore.txt";

# Открываем базу данных.



open(OUTFILE, "»$filename") exit;

После того как файл будет открыт, программа внесет в него единичные записи, содержащие набранные очки, имя игрока и время, например: 967&Gary&978887513.

# Вносим информацию в базу данных.

print OUTFILE "$info{'score'}&$info{'name'}&$server_time\n";

Программа Submitscore.pl заканчивает свою работу, закрывая текстовый файл и пересылая определенную информацию во Flash. В данном случае переменной resultText присваивается символьная строка ОК.

# Закрываем базу данных.

Close(OUTFILE);

# Подтверждаем ввод данных,

print "resultText=OK\n";

# Завершаем программу,

exit;

Программа Getscores.pl несколько сложнее, чем Submitscore.pl. В ее задачу входит просмотр базы данных, удаление устаревших записей и затем выдача списка десяти лучших результатов. Начало этой программы аналогично началу Submitscore.pl. Далее файл открывает базу данных и перебирает все ее записи с использованием переменной типа "массив" @score_text.

#!/usr/bin/perl

# Устанавливаем метку для текстового документа,

# получаемого на выходе.

print "Content-type: text/html\n\n";

# Указываем имя файла, содержащего базу данных.

$filename = "highscore.txt";

# Просматриваем записи базы данных,

open(DATABASE,$filename) exit;

@score_text = ;

close(DATABASE);

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

# Определяем время на сервере.

$server_time = time();

# Выясняем, как долго сохранять полученные результаты.

# (7 дней * 24 часа * 60 минут * 60 секунд)

$keep_time = 7*(24*60*60) ;

Теперь база данных открыта и готова для записи. Старый файл перезаписывается с учетом новых данных.

# Открываем базу данных для перезаписи.

open(DATABASE,">" . $filename) exit;

Следующий цикл проверяет каждую запись и определяет, как давно она была внесена в базу данных. Если запись устарела, то она не переписывается в новый файл.

# Просматриваем каждую запись.



foreach $score_line (@score_text) {

# Получаем данные о набранных очках.

@score_item = split(/&/,$score_line);

# Определяем время занесения результата.

$score_time = $score_item[2];

# Если запись не устарела, переписываем ее в новый файл.

if ($score_time > ($server_time-$keep_time)) {

print DATABASE "$score_Iine";

# Закрываем базу данных. close(DATABASE);

Далее программа сортирует результаты игры. Так как в начале каждой строки базы данных содержатся набранные очки, именно по ним упорядочиваются записи, от лучшего результата игры к худшему.

# Упорядочиваем набранные очки.

@score_text = sort { $b $а } @score_text;

Следующий цикл отбирает первые десять результатов и выдает их как выходные данные. Каждый элемент строки специальным образом форматируется. Эта строка выглядит, например, следующим образом: "l.Gary 967". После номера строки ставится точка. Затем идет имя игрока: если оно содержит меньше 18 символов, оставшееся место заполняется пробелами. Далее выводятся набранные игроком очки - они выравниваются по правому краю, и если число цифр меньше девяти, то свободные места с левой стороны также заполняются пробелами.

# Определяем число результатов,

# которые программа отошлет как выходные данные.

$num_scores = 10;

$score_count = 1;

# Просматриваем первые записи и отправляем данные,

print "highScoresText=";

foreach $score_line (@score_text) {

# Прочитываем записанные данные и создаем массив.

@split_data = split("&",$score_line);

# Выводим номер строки с точкой после него.

printf("%2i. ", $score_count);

# Выводим имя игрока, выравниваем по левому краю, оставшиеся

# свободные места до 18 символов заполняем пробелами,

printf("%-18s ",$split_data[l]);

# Выводим набранные очки, выравниваем по правому краю,

# оставшееся слева место до девяти символов заполняем

# пробелами.

printf("%9i\n",$split_data[0]); $score_count++;

if ($score_count > $num_scores) { last; } }

# Выходим из программы,

exit;

Если вы прежде программировали только на Flash и никогда раньше не сталкивались с Perl, вам будет достаточно сложно писать программы на этом языке. Можете не беспокоиться по этому поводу, потому что обычно подобные проекты разрабатываются двумя программистами совместно: один из них владеет языком Flash, другой - Perl. Если возможно, постарайтесь с кем-нибудь объединиться.


Ролик, отображающий список лучших



Рисунок 17.5 Ролик, отображающий список лучших игроков, позволяет проверить правильность работы программ, написанных вами на языке Perl
Имена игроков и очки выровнены по левому краю (Рис 17.5). Я использовал моноширинный шрифт (Courier). Если использовать другие шрифты, например Ariel, то будет не так просто выровнять столбцы в рамках одного текстового поля.
Все программные коды в файле Highscores.fla привязаны к двум кнопкам (см. Рисунок 17.5).

Ролик, отображающий список лучших
Часть "Добавить результат" отображает имя игрока и набранные им очки в текстовых окнах, содержимое которых может изменяться. Когда вы запускаете эту программу, вы можете ввести имя и набранные очки, а затем щелкнуть по кнопке Submit Score (Добавить результат). Ниже приведен текст программы, закрепленной за этой кнопкой.
on (press) {

// Путь к файлу

submitscore.pl. url = "submitscore.pl";

// Определяем объект LoadVars.

myLoadVars = new LoadVars();

myLoadVars.name = name;

myLoadVars.score = score;

// Пересылаем данные.

returnedVars = new LoadVars();

myLoadVars.sendAndLoad(url,returnedVars,"GET" resultText = "Sending...");

// Задаем результат.

returnedVars.onLoad = function!) { resultText = "Done."}}
Вы уже использовали объект LoadVars в главе 12 для создания игр-викторин. Тогда вы не отсылали никаких данных серверу, вам требовался лишь текстовый файл. В этом примере вы вызываете программу, написанную на Perl, чтобы отсылать данные на сервер. Элемент GET В конце команды SendAndLoad сообщает Rash, что данные будут передаваться с помощью протокола GET. Это один из вариантов, второй - использование протокола POST. Названные протоколы соотносятся с двумя способами передачи данных из форм, которые используются HTML-страницами. Данные, которые мы передаем, - это два свойства объекта myLoadVars: name и score. Когда передача завершается, выполняется функция, определяемая методом onLoad. В нашем случае она выводит сообщение в текстовое поле resultText. К кнопке Get Scores (Обновить список) также прикреплена небольшая программа, но здесь не требуется пересылать значения каких-либо переменных:
on (press) {

// Указываем путь к программе getscores.pl.

url = "getscore.pl";

// Запрашиваем данные.

myLoadVars = new LoadVars();

myLoadVars.load(url);

highscoresText = "Getting High Scores...";

/ / Данные получены.

myLoadVars.onLoad = function() {

highscoresText = myLoadVars.highscoresText;

}
Текст, выдаваемый программой Getscores.pl на выходе, начинается с high-ScoresText=. За счет этого оставшаяся часть текста помешается в одноименную переменную. Функция onLoad получает это значение из объекта myLoadVars и помешает в текстовое поле с соответствующим именем. Как только заканчивается пересылка текста, он выводится на экран.


Ролик с небольшим изображением проигрывается быстрее



Рисунок 17.2 Ролик с небольшим изображением проигрывается быстрее

Ролик с небольшим изображением проигрывается быстрее


Маленькая лиса двигается быстрее, чем большая. Во Flash-проигрывателе на моем компьютере ролик воспроизводился со скоростью 60-70 кадр/сек, что примерно в два раза превышает скорость предыдущего ролика. Это подтверждает теорию о том, что для построения меньшего графического объекта требуется меньше времени.
Теперь остается выяснить, удовлетворяет вашим потребностям анимация с меньшим изображением лисы? Если да, то вопрос выбора альтернативы решен.


Сложности применения списка лучших




Сложности применения списка лучших
Давая игрокам возможность вводить в качестве их имен любую информацию, вы тем самым бесплатно предоставляете им на своем Web-сайте пространство для деятельности. Конечно, большинство игроков введут
свое имя или прозвище, но найдутся и те, которые воспользуются случаем написать что-нибудь нехорошее.
Почему бы не проверять Flash-игру или Perl-программу на наличие нецензурных выражений? Вы можете так и поступить, но это не решит проблему окончательно. Одни из игроков могут слегка изменить написание слов либо использовать между ними пробелы или тире, другие абсолютно нормальными словами скажут что-нибудь неприличное. Иначе говоря, совсем избежать неприятных ситуаций, скорее всего, не удастся.
Большие, профессионально сделанные сайты обычно требуют, чтобы перед тем, как сохранить результаты своей игры, пользователь зарегистрировался. Однако система учета очков зарегистрированных игроков гораздо сложнее примеров, которые описываются в этой главе. Для ее внедрения потребуется готовая система баз данных со встроенной системой зашиты.
Другая проблема - это жульничество. Как только будут объявлены лучшие результаты, игроки станут искать способы смошенничать. И это достаточно легко сделать. Можно использовать различные методы - от остановки игры в решающий момент, чтобы перевести дух и найти решение в сложившейся ситуации до использования "взламывающих" программ, которые могут считывать информацию из памяти компьютера игрока и записывать новые данные. Таким образом, 42 балла можно легко превратить в 42 миллиона баллов. Однако, если эти сложности вас не пугают и вы все-таки решили включить в игру список лучших игроков, не ограничивайте свои возможности только описанными здесь приемами. Попробуйте использовать другие языки программирования. Например, РНР - очень популярный язык, схожий с языком Perl. С равным успехом CGI-программы можно создавать на основе более старых и известных языков, таких как С и C++.




Сложные экраны загрузки



Сложные экраны загрузки


Исходный файл:
На Рисунок 17.3 видно, что в текстовом поле отображается величина 63%. Вместо такого метода измерения можно сообщать пользователю, сколько байт уже загружено и каков размер всего ролика. Для этого нужно изменить значение loadingmessage, записав следующее:
_root.loadingmessage = int(bytesLoaded/1000) + "kb/" + int(totalFileSize/1000) + "kb";
Тогда вместо 63% вы увидите 90 Kb/143 Kb, что лично мне больше нравится.
Выполняя несложные математические вычисления, вы можете сообщить пользователю, как быстро идет загрузка. Для этого нужно создать переменные, в которых будет храниться количество загруженных байт и время на их загрузку. Теперь вы можете определить скорость загрузки, разделив первую величину на вторую. Это позволит спрогнозировать, сколько времени займет загрузка всего ролика.
Следующий код представлен в ролике Loader2.fla. Он похож на предыдущий фрагмент кода, только здесь на экран выводится еще и дополнительная информация: скорость загрузки и оставшееся время.
onClipEvent(load) {

// Получаем исходную информацию.

totalFileSize = _root.getBytesTotal();

startTime = getTimer О;

startBytes = _root.getBytesLoadedf);

// Прячем полосу загрузки,

this. _xscale = 0;}

onClipEvent(enterFrame) {

// Выясняем, сколько байт уже загружено.

bytesLoaded = _root.getBytesLoaded();

// Преобразуем это значение в величину от 0 до 1.

amountLoaded = bytesLoaded/totalFileSize;

// Преобразуем полученное значение в величину от 0 до 100.

percentLoaded = int(100*amountLoaded);

// Определяем масштаб полосы загрузки.

this._xscale = percentLoaded;

// Определяем скорость загрузки.

timeSoFar = getTimerО - startTime;

speed = bytesLoaded/timeSoFar;

// Выясняем, сколько времени осталось до конца загрузки.

bytesLeft = totalFileSize - bytesLoaded; timeLeft = (bytesLeft/speed)/1000;

// 'Преобразуем полученное значение

// в величину с одной цифрой после запятой.

speed = int(10*speed)/10;

// Устанавливаем текстовые поля.

_root.bytesMessage = int(bytesLoaded/1000) + "kb/" + int(totalFileSize/1000) + "kb";

_root.speedMessage = speed + "k/sec";

_root.timeMessage = int(timeLeft) + " seconds remaining";
Поскольку мы округлили значение timeLeft до целого числа, то в течение последней секунды загрузки в соответствующем окне будет отображаться ноль. Если вас не устраивает такое положение дел, то вы можете округлить это значение до одного или большего количества знаков после запятой.
// Проверяем, все ли уже загружено.
if (amountLoaded >= 1.0) {

_root.gotoAndStop("complete");
}}


Список лучших игроков



Список лучших игроков


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


Теперь при загрузке игры "Поймай лису" на экран выводится список лучших игроков



Рисунок 17.6 Теперь при загрузке игры "Поймай лису" на экран выводится список лучших игроков


Теперь при загрузке игры



В конце игры, в сцене "Игра закончена", появляется клип с несколькими текстовыми полями и кнопкой Submit Score. При этом очки, набранные в игре, должна подсчитывать и выводить сама программа. Сама по себе игра управляется действиями, которые относятся к клипу "actions" сцены "Игра". Поэтому переменная score привязана именно к этому клипу, а не к основной временной шкале. После добавления к тексту программы строки _root.score = score в основной временной шкале также появляется переменная score, которой присваивается число набранных очков. Когда приходит очередь сцены "Игра Закончена", первый кадр помешает набранные очки в клип "Добавление результатов". Одновременно удаляется предыдущее значение переменной name этого клипа.
sendscores.name = "" ;

sendscores.score = score;

stop();
На Рисунок 17.7 показан кадр из сцены "Игра закончена". Здесь вы можете видеть, что клип "Добавление результатов" содержит набранные игроком очки, текстовое поле, куда игрок должен ввести свое имя, и кнопку Submit Score.
Игрок вводит свое имя в первое текстовое поле, в то время как другое поле уже содержит набранные им очки, и он не может изменить результат. После этого игрок может щелкнуть по кнопке Submit Score, которая запустит следующую программу:
on (press) {

// Указываем путь к программе submitscore.pl.

url = "submitscore.pl";

// Отправляем значения переменных:

// "score" - счет игры,
// "name" - имя игрока.

loadVariables(url,this,"GET");

// Меняем кадр, чтобы по кнопке Submit нельзя было

// щелкнуть дважды,

play();
}


Требуемые версии



Требуемые версии


Обычно вам нужно сравнить номер версии с минимальными требованиями, необходимыми для игры. Например, вы можете задать минимальную версию, необходимую пользователю для вашей игры, 6.0.21.0. Вот функция, которая берет полный номер требуемой версии и сравнивает с номером версии, запушенной в настоящее время на компьютере пользователя. Она возвращает истинное значение только в том случае, если установленная версия больше или равна требуемой.
function requiredVersion(required) {

version = getVersionNumber();

// Возвращаем false, если номер версии меньше необходимого,

if (version.nl < required.nl) return(false);

// Возвращаем true, если номер версии больше необходимого,

if (version.nl > required.nl) return(true);

// Главные позиции номера совпадают, сравниваем следующие

// позиции.

if (version.n2 < required.n2) return (false);

if (version.n2 > required.n2) return (true);

if (version.n3 < required.n3) return (false);

if (version.n3 > required.n3) return (true);

if (version.n4 < required.n4) return (false);

if (version.n4 >= required.n4) return (true);

Вы можете использовать эту функцию так:
if (requiredVersion) ({nl: 6, n2: 0, пЗ: 21, п4:0})}

gotoAndPlay("start");

} else {

gotoAndStopt"version problem");}
ЕСЛИ версия пользователя меньше, чем 6.0.21.0, ролик переходит к кадру, который объясняет, что данная игра требует обновления версии плеера.
Все коды в функциях getVereionNumber и requiredVersion будут работать и с Flash бис Rash 6. Весь код ничего не будет стоить, если будет работать только с Flash 6. Идея состоит в том, чтобы предупредить пользователя Flash 5-плеера о возможных проблемах.
Обычное данную функцию предполагается использовать для того, чтобы обнаруживать небольшие обновления Flash-плеера. Предположим, например, что Macromedia выпускает Flash версии 6.0.42.0. Эта версия содержит исправление, которое необходимо для корректной работы вашей игры. Вы должны проверить, есть ли у пользователя данная версия, и предупредить его, если ее нет.
Важно, чтобы эта проверка возвращала true только в том случае, если у пользователя установлена требуемая версия или более новая. Обычная ошибка, которую делает программист, это поиск точного номера версии, например, 6. Тогда, если у пользователя установлена 5-я или любая другая версия, он получает предупреждение о необходимости обновления. Спустя год выходит версия 7, а поскольку версия 7 также не является версией 6, пользователь получает точно такое же предупреждение! Я постоянно сталкиваюсь с этим в программировании во Flash, Stockwave и JavaScript. Функция requiredVersion всегда возвращает true если версия пользователя более новая, чем требуемая.


Установление контрольных точек


Установление контрольных точек


Исходный файл:
Если вы хотите сравнить два альтернативных варианта увеличения скорости, вам нужно как-то ее измерить. Конечно, вы можете определить скорость на глаз: когда объект движется быстрее; но такой способ не очень точен. Установление контрольных точек позволяет выяснить, какая из альтернатив работает быстрее и насколько.
Обычно с помощью контрольных точек сравниваются две или более альтернатив при выполнении одинаковых или аналогичных задач. В каждом случае засекается время в начале и конце выполнения задания.
В результате вы определяете, сколько времени необходимо на выполнение этого задания. Проведя несложные математические расчеты, вы можете преобразовать эту величину в скорость воспроизведения ролика, то есть количество кадров в секунду (fps - frames per second).
В качестве примера возьмите какой-либо графический объект, перемещающийся через рабочее поле. На Рисунок 17.1 в центре экрана изображена лиса в тот момент, когда бежит справа налево.


В кадре "complete" ролик ждет



Рисунок 17.4 В кадре "complete" ролик ждет, пока игрок не щелкнет по кнопке, и лишь затем продолжается воспроизведение анимации

В кадре

onClipEvent(load) {

// Определяем размер файла.

totalFileSize = _root.getBytesTotal();

// Прячем полосу загрузки.

this._xscale = 0;

}

onClipEvent(enterFrame) {

// Выясняем, сколько байт уже загружено.

bytesLoaded = _root.getBytesLoaded();

// Преобразуем это значение в величину от 0 до 1.

amountLoaded = bytesLoaded/totalFileSize;

// Преобразуем полученное значение в величину от 0 до 100.

percentLoaded = int(100*amountLoaded);

// Определяем масштаб полосы загрузки.

this._xscale = percentLoaded;

// Устанавливаем текстовое поле в основной временной шкале.

_root.loadingMessage = percentLoaded + "%";

// Проверяем, все ли уже загружено,

if (amountLoaded >= 1.0) {

_root.gotoAndStopt"complete") ;
}}
Просмотрите ролик Loaderl.fla, чтобы понять, как работает приведенный код. Загрузка выполняется в первой сцене, а остальная часть ролика - во второй. Когда пользователь щелкает по кнопке Play (Начать игру), с помощью команды play() ролик переходит от кадра "complete" к последующим.
Обратите внимание, что если вы откроете swf-файл Loaderl.fla и просмотрите его во Flash, то увидите немногое. С вашего локального жесткого диска ролик грузится слишком быстро, чтобы можно было увидеть экран загрузки. Вам нужно разместить файл на вашем Web-сайте, а затем просмотреть его с помощью браузера.
Намного проще все-таки использовать возможность Rash эмулировать модемное соединение. В меню окна предварительного просмотра выберете View->Show Streaming (или нажмите Ctrl+Enter) и ролик будет отображаться так же, как если бы он загружался по модемному, то есть очень медленному соединению. Пропускную способность этого виртуального соединения вы можете установить, выбрав пункт меню Debugging в окне предварительного просмотра.


В течение 30 кадров большая лиса бежит справа налево



Рисунок 17.1 В течение 30 кадров большая лиса бежит справа налево

В течение 30 кадров большая лиса бежит справа налево




Индикатор в правом верхнем углу Рисунок 17.1 показывает, что скорость этого ролика составляет 35 кадров в секунду. На самом деле ролику задана скорость 120 кадров в секунду. 35 кадр/сек - реальный результат работы Flash-проигрывателя, который воспроизводит анимацию.
Для того чтобы в верхнем правом углу отображалась надпись "35 fps", необходимо добавить небольшой фрагмент кода. В первый кадр кода надо поместить следующую строчку, чтобы запомнить время начала анимации:
startTime = getTimer();
В последнем, 30-м кадре, программа, используя время начала и конца анимации, подсчитывает, сколько миллисекунд заняло ее воспроизведение. Полученная величина отображается в текстовом поле.
totalTime = getTimerO - startTime;

totalFrames = 30;

fps = totalFrames/(totalTime/1000);

readout = int(fps) + "fps";
Результат работы этого кода можно увидеть в ролике Benchmarkl.fla. Когда я проигрывал ролик на своем Макинтоше в окне предварительного просмотра, результат варьировал от 33 до 35 кадр/сек. Скорость также зависит от изменения масштаба. Попробуйте изменить масштаб в окне предварительного просмотра и посмотрите, как изменится скорость ролика.
Важно помнить, что максимальная скорость, которую может предоставить Flash в таких случаях, - 120 кадр/сек. Если вы укажете скорость по умолчанию, равную, скажем, 12 кадр/сек, анимация будет проигрываться только с этой скоростью или медленнее. Даже если один способ будет быстрее другого, вы не заметите никакой разницы, так как в обоих случаях скорость окажется равна 12 кадр/сек.


Возможности Flash 6-плеера



Возможности Flash 6-плеера


Кроме выяснения номера версии Flash-плеера, вам также может понадобиться узнать другие возможности пользовательского варианта плеера. Флэш может работать на разных платформах, например, на карманных компьютерах. Некоторые из этих устройств неспособны поддерживать весь набор свойств, которыми оперирует Flash 6 на MAC и Windows.
Вы можете проверить некоторые характеристики системы с помощью объекта System.capabilities. Свойства этого объекта содержат требуемые параметры. Вот пример кода, который совершает цикл по всем свойствам и возвращает каждое значение.
// Список всех свойств.

capabilitiesDisplay = "";

for(i in System.capabilities) capabilitiesDisplay += i"+: "+System.capabilities[i] + newline;}
Возвращаемый список выглядит примерно так на моем Macintosh Powerbook:
language: en-US

input: point

manufacturer: Macromedia Macintosh

os: Mac OS 10.1.3

serverString:

A=t&MP3=t&AE=t&VE=t&ACC=f&DEB=t&V=MAC%206%2C0%2C21%2C0&M=Macromedia

Macintosh&R=1152x768&DP=72&COL=color&AR=l&I=point&OS=Mac OS

10.1.3&L=en

isDebugger: true

version: MAC 6,0,21,0

hasAudio: true

hasMP3: true

hasAudioEncoder:true

hasVideoEncoder: true

screenResolutionX: 1152

screenResolutionY:768

screenDPI: 72

screenColor: color
Одна из проблем, связанных с использованием System.capabilities,состоит в том, что нет никакой гарантии, что названия свойств и типы значений остаются постоянными от плеера к плееру. Вам придется проверить System.capabilities на любой системе, на которой хотите проигрывать ваш клип. Потом вам нужно составить лист возможных свойств и значений, которые хотите проверить. Например, вы можете заметить,что на некоторых плеерах hаsМРЗ не работает, а некоторые плееры даже не показывают свойство hаsМРЗ. Поскольку Flash 6-плеер может быть подсоединен к разным машинам, только время покажет, насколько различным может быть список System, capabilities работает.
Одно из полезных применений System.capabilities состоит в том, что можно взять значения свойств screenResolutionX и screenResolutionY, чтобы, например, сообщить пользователю, что установки его монитора соответствуют размеру экрана, который слишком мал для удобного просмотра вашего ролика. Вы также можете разбить на части свойство System, capabilities, os, также, как мы уже разбирали get .Version О , чтобы можно было проверить, обладает ли пользователь достаточно новой версией операционной системы.




Выбор лучшей альтернативы Исходный


Можно уменьшить число кадров с 30 до 15, скорость увеличится в два раза, но анимация станет менее плавной. Так как клип лисы управляет длиной шага, лиса сделает в два раза меньше шагов, чтобы пробежать то же самое расстояние.
Если такой вариант не помогает, возможно, стоит изменить размер лисы. Что произойдет, если уменьшить лису в два раза? С одной стороны, число векторов изображения останется прежним, то есть время на построение изображения не зависит от его масштаба. С другой стороны, так как лиса будет меньше, для создания ее изображения понадобится меньшее число пикселов, следовательно, скорость ролика увеличится (понадобится меньше ресурсов для отображения каждого кадра).
Проверить эти гипотезы можно с помощью установки контрольных точек. Если уменьшить в два раза фигуру лисы в ключевых кадрах, вы получите такую же анимацию, только лиса будет меньше. Сценарий покажет, повлияло ли это на скорость ролика.. Результат сравнения показан на Рисунок 17.2.


Выполнение и оптимизация



Выполнение и оптимизация


При разработке игр с помощью программы Flash всегда возникает вопрос о скорости. До пятой версии Rash язык программирования был недостаточно эффективным и не позволял создавать многие игры. Но даже версия 5 была довольно медленной. Достаточно медленной, чтобы было невозможно реализовывать многие игры. Версия MX значительно улучшила быстродействие программы. Теперь можно создавать игры, для которых Flash 5 был слишком медленной программой. Даже игры, созданные в пятой версии и запушенные с помощью проигрывателя версии 6, смотрятся лучше. Но независимо от того, используете ли вы версию 5 или MX, техника оптимизации может помочь ускорить любую игру или программу.


Защита от копирования



Защита от копирования


Исходный файл:
Итак, как помешать кому-либо украсть вашу игру? Никак. Но вы можете помешать ему проигрывать игру с другого сайта, таким образом, украденная игра станет бесполезной.
Ключ к такой защите - свойство _url, которое возвращает строчку, содержащую весь путь к вашему swf-файлу. Если ролик располагается по адресу http://www.ktrnk.org/my .swf. именно эту строчку вернет свойство _url.
После того как вы ее получили, можете разбить ее на части и выделить имя основного домена. Например, в вышеприведенном адресе основной домен – ktrnk.org. Просмотрите следующий фрагмент программы Copyprotection l.fla:
// Показываем весь путь.

urlText = "This game is being loaded from: " + _url;

// Получаем строку, находящуюся между символами "://"

//и следующим символом "/". cl = _url.indexOf("://"+3; с2 = _url.indexOf("/", cl);

domain = _url.sunstring(cl, c2);

// Удаляем все символы перед первой точкой.

сЗ = domain.lasrIndexOf(".")-1;

с4 = domain.lastlndexOf(".", сЗ) +1;

domain = domain.substring(c4, domain.length);

// Отображаем основной домен.

domainText = "The root domain is: " + domain;

Полученное имя основного домена вы можете сравнить с доменом, на котором находится ваш сайт. Основной домен, используемый в пути к вашей игре, для разных пользователей может выглядеть по-разному. Например, адреса http:// ktrnk.org и http://www. ktrnk.org относятся к одной страничке, но свойство _url для них будет различным. Также в адресах некоторых сайтов перед доменом могут бьпъ указаны не только символы типа "www.", например, www .games. ktrnk.org.
Если основной домен не соответствует тому, который ожидает программа, значит, игру украли. В этом случае вместо перехода к ролику игры программа может перейти к другому кадру, в котором, например, будет выводиться угрожающее сообщение.
Возможно, вы не захотите, чтобы сообщение было слишком угрожающим. Много раз я предоставлял клиенту лицензированную копию одной из игр для его сайта и забывал изменить код, чтобы игра шла на его сайте. Если вы попадете в такую ситуацию, мнение клиента о вас может измениться в худшую сторону.



Исходный файл:

Далее следует фрагмент программы, сравнивающей свойство _url с тем доменом, на котором располагается ваша игра. Этот код вы можете посмотреть в файле Copyprotection2.fla.

// Получаем строку, находящуюся между символами "://"

//и следующим символом "/".

cl = __url.indexOf("://"+3; с2 = _url.indexOf("/", cl) ;

domain = _url.sunstring(cl, c2);

// Удаляем все символы перед первой точкой.

сЗ = domain.lasrIndexOf(".")-1;

с4 = domain.lastlndexOf(".", сЗ) +1;

domain = domain.substring(c4, domain.length);

// Переходим к соответствующей странице,

if (domain != " ktrnk.org ") {

gotoAndPlay("stolen");

} else {

gotoAndPlat("start");

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




    Сайт: Аннимация - Видео - Графика