Библиотека Интернет Индустрии I2R.ru |
|||
|
Flash 8 и ActionScript. Создаем игрыДанный материал представляет собой одну главу книги "Flash 8 и ActionScript. Библиотека пользователя (+CD)", подробнее о которой вы можете узнать в нашем анонсе, а заказать - в Издательском доме "Питер" Авторы: Д. Гурский, Ю. Гурский Глава 10...Вы уже немало изучили и почти готовы к тому, чтобы продолжить путь в мире Flash самостоятельно. В этой главе вы узнаете не так много новых теоретических сведений. Скорее она будет направлена на то, чтобы закрепить в памяти уже изученное нами. Крестики-нолики — это, пожалуй, самая простая из всех игр, которые придумало человечество за всю историю борьбы со скукой. Дети еще не умеют писать, когда уже в совершенстве владеют искусством победы в незамысловатом поединке на девяти клетках. При обучении программированию алгоритм крестиков-ноликов считается азбукой, и его обычно создают уже на первом или втором практическом занятии. Не будем нарушать не нами установленную традицию и напишем Flash-версию этой игры. Для тренировки гибкости мышления создадим крестики-нолики без использования массивов. Готовый файл kr&nol.fla данного проекта находится на прилагаемом к книге компакт-диске в папке Проекты\Глава 10. Обращайтесь к нему, если что-то вам покажется непонятным. Для начала, как и всегда во Flash-программировании, необходимо создать объекты, которыми мы будем управлять. В нашем случае это будет игровое поле и, что очевидно, символы крестика и нолика. Начнем с игрового поля. Чтобы его нарисовать, зайдите в режим редактирования нового клипа (Ctrl+F8) и включите режим отображения сетки (команда Grid->Show Grid (Сетка->Показать сетку) контекстного меню рабочей области). Используя инструмент Rectangle (Прямоугольник), создайте желтый квадрат размером 3 * 3 клетки. Инструментом Line (Линия) проведите в нем разделительные линии. Чтобы они получились равноотстоящими друг от друга, активизируйте режим Snap to Grid (Привязать к сетке), выполнив одноименную команду подменю Snapping (Привязка) контекстного меню рабочей области (при этом начало и конец линий будут привязываться к узлам сетки). Нарисовав поле, увеличьте его до размеров 90 * 90 пикселов. Для этого, выделив его, введите необходимые значения в поля W (Ширина) и Н (Высота) инспектора свойств. Затем расположите поле так, чтобы его левый верхний угол (позиционирование проводим при увеличенном масштабе) точно совпадал с точкой начала координат клипа. Это необходимо, чтобы мы могли по координатам указателя мыши в момент нажатия ее кнопки определить, в какой квадрат игрок хочет поставить крестик (или нолик). Закончив рисование поля, вернитесь на основную временную шкалу и перетяните на нее из библиотеки экземпляр созданного клипа. Расположив его в центре листа, назовите pole. СОВЕТ Рис. 10.1. Палитра History (История) Нарисовать символы крестика и нолика несложно. Сделав это, переведите их в клипы и подберите на созданном ранее игровом поле для них размеры так, чтобы они максимально удачно вписывались в его клетки. Крестик назовите krestik, а нолик, следовательно, — nolik. Проверьте, чтобы точка начала координат клипов соответствовала центру их графической составляющей. Разместите созданные клипы вне поля фильма. Этап рисования на этот раз оказался очень коротким. Приступим к написанию кода. В тот момент, когда пользователь щелкнет кнопкой мыши на игровом поле, на нем должен появиться новый крестик или нолик — в зависимости от того, за какую «команду» он играет. Чтобы «отловить» это событие, воспользуемся обработчиком onMouseDown: pole.onMouseDown=function(){}; Если кнопка мыши нажимается, то в первую очередь нужно проверить, где располагался указатель. Вполне вероятно, что щелчок был вовсе не на игровом поле. Для этого воспользуемся свойствами _xmouse и _ymouse, хранящими координаты указателя мыши. Для разных клипов значения этих свойств будут различными, так как точки начала координат у них почти никогда не совпадают. Нас будут интересовать значения _xmouse и _ymouse в системе координат клипа pole. Если они окажутся больше нуля и меньше 90, то щелчок был произведен на игровом поле, следовательно, можно выполнять дальнейшие действия: if (pole._xmouse>0 && pole._ymouse>0 && pole._xmouse<90 && pole. _ymouse<90) {} Проверить, находился ли указатель мыши над клипом в момент щелчка, можно также, используя уже знакомый нам метод Если условие о позиции указателя окажется выполненным, то в положенную клетку поля следует поместить экземпляр крестика или нолика. Для этого он прежде всего должен быть «изготовлен». Создать копию имеющегося в кадре клипа можно, воспользовавшись функцией duplicateMovieClip(user,"obj"+n,n); Функция user — строка с именем клипа, копия которого должна быть создана. Так как в нашей игре символ, которым обозначаются ходы противников, будет определяться случайным образом, используем в качестве этого параметра не krestik или nolik, а переменную user. Определять ее значение будет следующий код:
var n=l; В конец блока кода, осуществляющего ход пользователя, необходимо добавить:
n — глубина клипа. Эта величина задает, как вы помните, выше или ниже других объектов будет располагаться клип. В нашем случае относительность размещения экземпляров не имеет значения, поэтому полагаем данный параметр равным определенной ранее переменной n (значения глубины клипов должны быть различными, поэтому нельзя его задать просто как некоторое число). После того как копия необходимого символа будет создана, ее нужно разместить в той клетке, на которой был произведен щелчок. Для этого добавьте в блок следующие выражения:
Несмотря на внушительный вид, данные формулы достаточно прозрачные. Х- и Y-координатам созданного последним экземпляра из группы obj (обращаемся к нему с помощью оператора доступа к свойствам по строковым ключам «[]») присваиваются координаты центра той клетки игрового поля, на которой был произведен щелчок. Определяются они следующим образом. Координата указателя мыши в системе отсчета клипа pole делится на значение длины ребра одной клетки игрового поля (она равна 30 пикселам), а затем результат округляется. При этом узнается, к какой по счету строке или столбцу относится клетка (переменные stroka и stolbec). От координаты указателя предварительно необходимо отнять величину, равную половине длины ребра клетки. Если этого не сделать, то для разных точек одной и той же клетки будут вычисляться различные номера строки и столбца. Далее, умножив полученные значения на 30, мы определим координаты левого верхнего угла клетки. Чтобы вычислить координаты ее центра, к ним нужно прибавить значение, равное половине длины ее ребра. Найденные значения координат центра клетки соответствуют системе отсчета клипа pole. Наш же дубликат расположен на основной временной шкале. Чтобы пересчитать полученные значения для системы координат _root, к ним достаточно прибавить координаты начальной точки клипа pole в этой системе отсчета (pole._x и pole._y). Видите, каким удачным решением было совместить левый верхний угол игрового поля с точкой начала координат клипа! Тестируем фильм и проверяем, правильно ли размещаются объекты при щелчках кнопкой мыши на клетках игрового поля. Если да, то продолжаем работу. С проблемой визуализации хода пользователя мы справились. Решить аналогичную задачу для шагов компьютера теперь не составит труда. Самое время придумать алгоритм игры, исходя из которого будем разрабатывать наш проект дальше. Подумаем, как играют в крестики-нолики. Очевидно, что игрок анализирует возможные ходы и оценивает их эффективность, выбирая наиболее удачный. Следовательно, мы в первую очередь должны сообразить, как машина может оценить удачность каждого хода. Так как клеток в ряду только три, создать иерархию ценности возможных позиций совсем не сложно.
Когда наступает очередь хода компьютера, он должен проверить ценность позиции каждой пустой клетки. При этом нужно учитывать, что клетка всегда принадлежит нескольким (от двух до четырех) рядам и ее значимость в разных рядах может быть различной. Поэтому характеристикой ценности позиции клетки должна быть сумма ценностей ее положений в каждом из рядов. После того как эффективность хода в каждую из свободных клеток будет вычислена, необходимо определить, какой из них соответствует наибольшее значение эффективности. Именно туда и должен быть поставлен знак. Как видите, на уровне идей алгоритм крестиков-ноликов совсем не сложный. Гораздо труднее будет реализовать его в коде с учетом того, что мы решили не использовать массивы. В первую очередь необходимо создать величины, в которых будет храниться информация о содержимом каждой клетки. Достаточно логичным будет сделать их свойствами клипа pole (так как клетки — это его фрагменты). Назовем величины по следующему принципу: общий корень + номер строки + номер столбца. Таким образом мы имитируем массивы, которые нам недоступны. Изначально каждую клетку нужно связать со значением 1, которое будет обозначать, что она пустая:
Когда пользователь делает ход, свойству, соответствующему заполняемой клетке, должно быть присвоено значение 10 (в случае хода компьютера это будет 100). Реализовать это очень просто, так как ранее в обработчике события onMouseDown мы уже создали переменные, хранящие номера строки и столбца той клетки игрового поля, на которой был произведен щелчок (stroka и stolbec): pole["kl"+stroka+stolbec]=10; ВНИМАНИЕ Пользователь может сделать ход только в пустую клетку. Поэтому, прежде чем запускать механизм дублирования символа, его позиционирования и переопределения описывающего клетку свойства, нужно проверить, не была ли она заполнена ранее:
Сейчас нам предстоит самая сложная в этом проекте задача — реализовать функцию-«мозг», которая будет решать, в какую клетку нужно сделать ход. Первым шагом в этом направлении будет создание переменных, хранящих маршрут обхода рядов для каждой клетки. Как вы помните, необходимо проанализировать ценность позиции клетки относительно всех рядов, к которым она относится, а вывод сделать по их сумме. Конечно, можно попытаться написать и универсальный алгоритм, способный обходить все ряды, которым принадлежит клетка, вне зависимости от ее положения. Но это слишком сложно для крестиков-ноликов. Гораздо проще и быстрее будет перечислить в индивидуальной для каждой клетки переменной индексы клеток, образующих ряды, к которым она относится:
На первый взгляд приведенный код кажется сложным и непонятным. На самом деле это не так. Проанализируем, например, первую строку. Имя переменной: hod_kl — общий корень, 00 — индексы столбца и строки, к которым относится клетка (то есть это левая верхняя клетка поля). Значение такое: 000102001020001122. Первые шесть цифр соответствуют верхней строке, следующие шесть — левому столбцу, а оставшиеся — ряду по диагонали. Каждая пара цифр определяет одну клетку. Как видите, все просто. Задав переменные маршрута обхода рядов, можно приступать к созданию самой функции «интеллекта» игры:
Запускаться функция mozg() будет тогда, когда компьютер должен сделать ход. Что при этом станет происходить? Очевидно, будет проверяться, в какую клетку выгоднее сделать ход. Следовательно, в первую очередь необходимо реализовать механизм, который позволит просматривать все клетки поля. Сделать это очень просто, задав два цикла. Первый будет изменять индекс строки в имени свойства pole.klnm, а второй — столбца:
Совершенно излишне проверять ценность позиции клетки, если в нее уже был сделан ход. Поэтому, прежде чем запустить «умный» код, проверяем, является ли текущая клетка пустой:
if (pole["kl"+i+j]==1) {} Если клетка пустая, то активизируем функцию var ves=obhod(i,j); Функция Немного отвлечемся от функции mozg() и займемся созданием ее «подчиненной» — функции
Для того чтобы сократить код функции var way=_root["hod_kl"+i+j]; Определять, какое сочетание знаков соответствует каждому ряду, к которому относится клетка, мы будем по сумме значений свойств pole.klnm его клеток. Например, величина 102 будет обозначать, что в ряду имеется один знак компьютера (pole.klnm=100) и две пустые клетки (pole.klnm=1). Индексы nm клеток рядов упакованы в переменной way таким образом, что каждые шесть символов отвечают за один ряд. Чтобы последовательно исследовать все ряды, запускаем цикл. Предел изменения его итератора определяем по тому, сколько шестерок символов может поместиться в хранимой way строке: for (var k=0; k Чтобы вычислить сумму свойств pole.klnm для ряда, нужно последовательно выделить из текущих шести символов три пары знаков, являющихся необходимыми индексами nm. Получив нужную подстроку, ее необходимо слить с корнем pole.kl и, переведя результат в идентификатор, прибавить связанное с ним значение к специальной переменной. Данная переменная должна быть изначально определена как ноль:
Узнав, какая комбинация знаков расположена в ряду, нужно ее оценить. Критерии такой оценки мы уже определили. Необходимо только перевести их в код. Для этого создадим специальную функцию
При создании функции Если окажется, что некоторая клетка принадлежит ряду, в котором все клетки заполнены знаками пользователя (в этом случае функция
Пока не созданная функция Если функция
Переменную var summ_ves=0; Значение, которое будет сохранено в результате полной прокрутки цикла k в переменной return summ_ves; Функция
Мы сохранили индексы клетки в строке, так как иначе потребовалось бы создать вместо одной hod две переменные. Одна лишняя строка — это, конечно, немного, но изящность решения также имеет немалое значение. После того как клетка с наибольшим рейтингом будет определена, необходимо отобразить в ней символ, которым играет компьютер, а также переопределить значение соответствующего свойства pole.klnm. Для лучшей читабельности сценария вынесем необходимый для осуществления описанных действий код в отдельную функцию:
Код в «теле» функции
В общем алгоритм крестиков-ноликов практически завершен. Осталось предусмотреть некоторые детали. Когда пользователь сделает ход, необходимо проверить, не стал ли он победным. Для этого следует протестировать с помощью функции
Новую переменную game вводим для того, чтобы пользователь не мог выполнить ход после завершения игры. В список условий, при которых щелчок кнопкой мыши воспринимается как ход игрока, необходимо добавить проверку на равенство game значению true. Объявить переменную game нужно в начале сценария равной true: var game=true; Остановить выполнение кода в обработчике
Если пользователь не одержал последним ходом победу и на поле еще есть пустые клетки, то должна быть вызвана функция, выполняющая ход компьютера:
Победным может оказаться и ход компьютера. Наиболее простой способ это определить — проверить значение рейтинга клетки, в которую был сделан ход. Если он окажется равным либо больше 1000, то данная клетка принадлежит ряду, в котором уже есть два знака компьютера:
Закончить игру вничью может и ход компьютера. Чтобы «отловить» это событие, добавляем в функцию
Результат игры будем отображать в специальном информационном поле. Пока же, чтобы можно было провести полное тестирование игры, будем выводить его в окне Output (Вывод):
Вроде бы все. Какой непростой оказалась реализация самой элементарной игры! Запускаем режим тестирования и пробуем играть. Что же, неплохо... Совсем неплохо! Авторам ни разу не удалось выиграть у компьютера. Правда, и машине тоже не слишком везло с победой. Но это уже вопросы техники игры. Главное, что все получилось! Единственный заметный недостаток в наших крестиках-ноликах — это то, что компьютер делает ход тотчас же после игрока. Это выглядит очень неестественно, поэтому промежуток времени, в течение которого компьютер «обдумывает» ход, нужно искусственно увеличить. Чтобы это сделать, замените вызов функции time=setInterval(mozg,1500); Функция Переменная var time=null; Когда произойдет вызов функции clearInterval(time); // Функция, останавливающая таймер Тестируем фильм. Вот сейчас компьютер играет вполне «по-человечески»! Теперь, когда промежуток времени между ходами пользователя и компьютера стал значительным, важно предусмотреть, чтобы в течение его игрок не сделал непредусмотренный ход. В обработчике onMouseDown после строки с таймером набираем: game=false; Разрешить продолжать игру пользователю можно тогда, когда ход сделает компьютер. Для этого в блок функции game=true; На нынешнем этапе реализации нашей игры первым всегда ходит пользователь. Это не совсем справедливо (так как игрок, делающий ход в центр поля, имеет явное преимущество), поэтому создадим функцию, которая случайным образом будет определять, кому из противников начинать поединок:
Расположить вызов функции Играя в крестики-нолики, противники редко ограничиваются одной партией. Поэтому в наш фильм необходимо добавить кнопку, которая будет начинать новую игру. Рисовать ее не будем, а позаимствуем из библиотеки кнопок Flash: Window->Common Libraries->Buttons (Окно->Общие библиотеки->Кнопки). Выбрав подходящую кнопку, перетащите ее на поле и назовите new_game. Нажатие кнопки «слушает» обработчик событий new_game.onRelease=function(){}; Чтобы начать новую игру, в первую очередь нужно удалить с поля оставшиеся после предыдущего поединка знаки. Сделать это будет очень просто, так как и крестики и нолики имеют однотипные названия, образованные корнем "obj" и их порядковыми номерами. Удалить же экземпляр клипа можно, используя метод
Также необходимо обнулить используемые алгоритмом переменные и свойства:
После того как игра приведена к начальному состоянию, определяем, кто будет ходить первым: rand_hod(); Готово. Запускаем режим тестирования и играем несколько партий подряд. Если все в порядке, то продолжаем работу. В процессе игры противники постоянно обмениваются репликами. Чтобы в наши крестики-нолики было интересно играть, следует постараться имитировать живое общение. Одним из решений этой задачи является создание специального текстового поля, в котором во время игры будут отображаться комментарии. Именно им мы и воспользуемся. Введите динамическое текстовое поле такого размера, чтобы в него могли поместиться два слова средней длины. Расположите его рядом с игровым полем на подложке такого цвета, который бы выделялся на окружающем фоне. Свяжите текстовое поле (строка Var (Переменная) инспектора свойств) с переменной info. Какую информацию будем выводить в созданное поле? Во-первых, мы должны сообщать игроку об исходе партии. Для этого перепишите функцию end() следующим образом:
Во-вторых, пользователь должен знать, что наступила его очередь делать ход. Поэтому последним выражением в функции info="Baш ход!!!"; После хода игрока компьютер искусственно «изображает» 1,5-секундное раздумье и лишь затем ставит в нужную клетку знак. Чтобы пользователь не решил, что фильм завис или в нем проявился «глюк», в информационное поле выводим infо="Думаю..."; Изначально переменная info должна быть создана равной строке "Ваш ход!!!". Этим мы предусмотрим то, что игра может начаться ходом пользователя. Последним штрихом в реализации крестиков-ноликов будет окно со счетом игры. Чтобы его создать, введите небольшое текстовое поле (в него должно поместиться пять знаков) и свяжите его с переменной schet. Саму эту переменную нужно объявить в начале сценария: var schet="0:0"; // Счет в начале игры Чтобы можно было сохранять информацию о количестве побед каждого участника, объявим специальные переменные: var user_schet=0; При завершении игры в функции
schet :
Подписываем информационные поля и создаем заголовок игры. Как крестики-нолики получились у авторов, показано на рисунке. Вот и все... Каким все-таки непростым оказалось создание самой элементарной игры! Представляете, сколько сил нужно потратить, чтобы написать, например, тетрис? На самом деле не так много — при условии неплохого знания языка и наличия в голове продуманного алгоритма. Этой работой займемся после теоретической вставки 23, посвященной особенностям типизации в ActionScript. Рис. 10.2. Крестики-нолики
|
|
2000-2008 г. Все авторские права соблюдены. |
|