В данном уроке мы создадим простую игру, являющуюся внебрачным сыном арканоида и пинг-понга.
Суть игры в том, чтобы отбить мячик до того как он коснется вашей стороны экрана. На другой стороне нам оппонирует компьютерный игрок, занимающийся аналогичным делом. Для этого нам понадобится создать 2 новых класса соответсвенно для нашего шарика и для платформы, представялющей игроков. Взаимодействие объектов будет определяться путем обнаружения столкновений между ними, меняя тем самым их поведение.
Создаем новый класс, назовем его как-нибудь по оригинальнее, например Ball. Не забудем расширить его от класса Sprite.
Данный класс во много напоминает собственный курсор из предыдущего урока, за одним исключением - для мячика мы применим радиальную градиентную заливку.
Ball.as :
Code
package
{
import flash.display.*;
import flash.geom.Matrix;
public class Ball extends Sprite
{
public function Ball()
{
var matr:Matrix = new Matrix(); /// для задания одного из параметров заливки нам понадобиться матрица, тут мы ее создаем
matr.createGradientBox(15, 15, 0, -5, -5); /// тут мы определям ее размеры, вращение и координаты
this.graphics.lineStyle(1);
/// тип градиента, массивы для цвета, прозрачности, расположения цветов, задание матрицы
this.graphics.beginGradientFill(GradientType.RADIAL, [0xFF0000, 0xFFC000], [1, 1], [50, 255], matr, SpreadMethod.REFLECT);
this.graphics.drawCircle(0, 0, 15);
this.graphics.endFill();
}
}
}
Теперь создадим новый класс для "рокетки", назовем его Paddle.
По скольку данный класс будет нами использован для задания как игрока таки его противника, у нас резонно возникает желание задать различный внешний вид рокеток. Сделать это можно используя параметы функции-конструкторв класса Paddle. Изменять таким образом можно все что душе угодно, но здесь мы ограничемся заданием цвета.
Итак в параметрах функции public function Paddle() в круглых скобках пишем colors:Array. Мы используем массив в качестве типа параметра потому, что цвет градиентной заливки определяется массивом, элементами которого являются цвета. В нашем случаем мы используем 2 цвета для задания градиента, но прошу обратить внимание на то, что ряд других параметров функции beginGradientFill также представляет собой массивы. Количество элементов данных массивов одинаково и равно числу заданных цветов.
Эти параметры связаны друг с другом и порядковый номер эелемента соответсвующего массива относится к цвету с точно таким же порядковым номером. Всего можно задать 15 цветов (то есть максимально массив может содержать 15 элементов).
Paddle.as :
Code
package
{
import flash.display.*;
import flash.geom.Matrix;
public class Paddle extends Sprite
{
public function Paddle(colors:Array)
{
var matr:Matrix = new Matrix();
matr.createGradientBox(100, 20, 45, 0, 0);
this.graphics.lineStyle(1);
this.graphics.beginGradientFill(GradientType.LINEAR, colors, [1, 1], [0, 255], matr); /// для рокетки мы использовали линейный тип заливки, параметр colors задает цвета принятые функцией-конструтором во время создания экземпляра класса Paddle
this.graphics.drawRect(0, 0, 100, 20);
this.graphics.endFill();
}
}
}
Теперь открываем Main.mxml и пишем саму игру.
Создаем глобальные переменные для нашей игры:
Code
private var ball:Ball; /// создаем мяч
private var player:Paddle; /// создаем игрока
private var enemy:Paddle; /// создаем компьютерного игрока
private var xDir:int = 10; /// переменные задающие направление движения мячика по осям x и y
private var yDir:int = -10;
private var targetX:Number = 0; /// числовая переменная, обозначающая место куда мы ходим привести нашу рокетку (определяется движением мыши)
private var easing:Number = 3; /// сглаживание движения
Главная функция задающая игру после запуска приложения:
Code
private function init():void
{
Mouse.hide(); /// прячем курсор
player = new Paddle([0x00FF00, 0x0000FF]); /// создаем экземпляр класса Paddle для игрока и задаем ему цвета.
player.x = 200;
player.y = 350;
stage.addChild(player);
enemy = new Paddle([0xFF0000, 0xFF00FF]); /// таже операция и для врага
enemy.x = 200;
enemy.y = 30;
stage.addChild(enemy);
player.addEventListener(Event.ENTER_FRAME, movePlayer); /// задаем функцию игрока вызываемую при каждом обновлении кадра
enemy.addEventListener(Event.ENTER_FRAME, moveEnemy); /// то есть в дангой игре мы используем покадровую анимацию
ball = new Ball(); /// создаем мячик
ball.x = 250;
ball.y = 200;
stage.addChild(ball);
ball.addEventListener(Event.ENTER_FRAME, moveBall); /// двигаем мяч
}
Далее сами функции движения объектов с подробными комментариями:
Code
private function moveBall(e:Event):void
{
/// если мяч уперся в какую либо из стен игрового экрана - меняем его направление на противоположное (отражаем)
if (ball.x <= 0) {
xDir *= -1;
}
else if (ball.x >= this.width - ball.width/2) {
xDir *= -1;
}
//// здесь иы используем функцию hitTestObject() объекта ball для проверки столкновения с объектами player и enemy.
//// в случае обнаружения оного, функция возвращает значение [b]true[/b] и мы также как и в случае со стенкой отражаем мяч
if (ball.hitTestObject(player)) {
yDir *= -1;
}
else if (ball.hitTestObject(enemy)) {
yDir *= -1;
}
if (ball.y <= 0) {
yDir *= -1;
}
else if (ball.y >= this.height - ball.height/2) {
yDir *= -1;
}
ball.x += xDir; //// двигаем мяч согласно определнному выше направлению по обоим осям
ball.y += yDir;
}
private function movePlayer(e:Event):void
{
if (this.mouseX < player.width/2) { //// если мышь левее точки равной половине рокетки, то не даем рокетке выйти за пределы экрана
targetX = 0;
}
else if(this.mouseX > this.width - player.width/2) { //// тоже самое для правой границы
targetX = this.width - player.width;
}
else { //// если куроср между этими двумя точками, то координаты цели движения равны положению мыши минус половина ширины рокетки
targetX = this.mouseX - player.width/2;
}
player.x += (targetX/2 - player.x/2) / easing; //// координаты местоположения игрока опредеяются следующей формулой, где easing это параметр задающий плавность движения рокетки, своего рода энерции
}
private function moveEnemy(e:Event):void
{
//// функция движения компьютера очень похожа по механизму на функцию игрока, думаю тут все понятно
//// иллюзия искусственного интеллекта здесь создается за счет постоянного следования рокетки за самим мячом, а не за курсором мыши как в случае с игроком
//// однако выиграть у того ИИ вряд ли получится) у меня не вышло... хотя с увеличением энерции (easing) это становится все легче
var enemyTargetX:Number;
enemyTargetX = ball.x - enemy.width / 2;
if (enemyTargetX <= 0) {
enemyTargetX = 0;
}
else if (enemyTargetX >= this.width - enemy.width / 2) {
enemyTargetX = this.width - enemy.width / 2;
}
enemy.x += (enemyTargetX / 2 - enemy.x / 2) / easing;
}
Полный код Main.mxml
Code
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="500" height="400" applicationComplete="init()" frameRate="24">
<mx:Script>
<![CDATA[
import flash.events.Event;
import flash.events.MouseEvent;
private var ball:Ball;
private var player:Paddle;
private var enemy:Paddle;
private var xDir:int = 10;
private var yDir:int = -10;
private var targetX:Number = 0;
private var easing:Number = 3;
private function init():void
{
Mouse.hide();
player = new Paddle([0x00FF00, 0x0000FF]);
player.x = 200;
player.y = 350;
stage.addChild(player);
enemy = new Paddle([0xFF0000, 0xFF00FF]);
enemy.x = 200;
enemy.y = 30;
stage.addChild(enemy);
player.addEventListener(Event.ENTER_FRAME, movePlayer);
enemy.addEventListener(Event.ENTER_FRAME, moveEnemy);
ball = new Ball();
ball.x = 250;
ball.y = 200;
stage.addChild(ball);
ball.addEventListener(Event.ENTER_FRAME, moveBall);
}
private function moveBall(e:Event):void
{
if (ball.x <= 0) {
xDir *= -1;
}
else if (ball.x >= this.width - ball.width/2) {
xDir *= -1;
}
if (ball.hitTestObject(player)) {
yDir *= -1;
}
else if (ball.hitTestObject(enemy)) {
yDir *= -1;
}
if (ball.y <= 0) {
yDir *= -1;
}
else if (ball.y >= this.height - ball.height/2) {
yDir *= -1;
}
ball.x += xDir;
ball.y += yDir;
}
private function movePlayer(e:Event):void
{
if (this.mouseX < player.width/2) {
targetX = 0;
}
else if(this.mouseX > this.width - player.width/2) {
targetX = this.width - player.width;
}
else {
targetX = this.mouseX - player.width/2;
}
player.x += (targetX/2 - player.x/2) / easing;
}
private function moveEnemy(e:Event):void
{
var enemyTargetX:Number;
enemyTargetX = ball.x - enemy.width / 2;
if (enemyTargetX <= 0) {
enemyTargetX = 0;
}
else if (enemyTargetX >= this.width - enemy.width / 2) {
enemyTargetX = this.width - enemy.width / 2;
}
enemy.x += (enemyTargetX / 2 - enemy.x / 2) / easing;
}
]]>
</mx:Script>
</mx:Application>
Компилим, играем.
B1z ©