Воскресенье, 24 Ноября 2024, 22:16

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
Flex и ActionScript 3.0 для создания мини-флеш игр. Часть 3
B1zDelKinДата: Понедельник, 25 Января 2010, 11:50 | Сообщение # 1
частый гость
Сейчас нет на сайте
В данном уроке мы расширим возможности созданной в предыдущем уроке игры. А именно:
- Разнообразим отскок от рокетки в зависимости от места попадания мячом
- Сделаем ведение счета игры 2-мя способами: средствами среды Flex и AS3
- Добавим эффект свечения при ударе мяча

Открываем наш проект. В файле Main.mxml находим функцию движения мяча moveBall.
Находим строчку

Code

if (ball.hitTestObject(player)) {
  yDir *= -1;
}

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

Code

if (ball.hitTestObject(player)) {
  yDir *= -1;
  [b]checkHitPosition(player);[/b] /// параметром данной функции является рокетка стлокновения с которой мы анализируем. в данном случае это игрок
}

Теперь подробно рассмотрим функцию проверки места столкновения:

Code

private function checkHitPosition(paddle:Paddle):void
{
  var hitPercent:Number;

                 /*переменная hitPercent отражает место в которое ударился мяч, 0 - это левый край рокетки, 50 - это ее середина, 100 - правый край

  var ballPosition:Number = ball.x - paddle.x; /// эта переменная отражает положение мяча относительно положения рокетки
     
  hitPercent = (ballPosition / (paddle.width - ball.width)) - .5; //// данная формула вычисляет место удара
  xDir = hitPercent * 20;  /// тут мы выбираем под каким углом отразится мяч, мне понравился коэффициент 20, можете поперебирать и выбрать понравившийся себе вариант
  yDir *= 1.04; /// добавляем ускорение мячу
}

Те же самые действия необходимо повторить и для столкновения с рокеткой соперника.

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

К делу. Переходим в Main.mxml к Disign View. Находим вкладку Components -> Controls и перетаскиваем оттуда на сцену компонент label. Возвращаемся в Source View и приводим наш компонент к следующему виду:

Code

<mx:Label id="escore" text="{enemyScore.toString()}" fontFamily="Arial" fontSize="24" x="15" y="15" width="60" height="35"/>

Обратите внимание на строку text="{enemyScore.toString()}" здесь мы используем привязку данных для атрибута text компонента label. Это означет, что в качестве строкового значения будет использоваться значение переменной enemyScore и любоей изменение ее значения повлечет изменение текста.

Самое время теперь добавить эту глобальную переменную:

Code

[Bindable]  /// данный тег указывает на то, что переменная используется в привязке данных (data binding)
private var enemyScore:int = 0;

Это пример использования средств Флекс. Теперь рассмотрим использование AS3 для данной цели. Нам нужно создать новый класс, выполняющий те же самые функции. Создаем новый класс, назовем его ScreenText и расширим его от класса Sprite.

Вот полный код ScreenText.as с комментариями:

Code

package   
{
  import flash.display.Sprite;
  import flash.text.TextField;
  import flash.text.TextFormat;
   
public class ScreenText extends Sprite
{
  protected var displayText:TextField;  //// экземпляр класса текст
  protected var format:TextFormat;     //// экземпляр клааса форматирования класса
    
  public function ScreenText()  
  {
   displayText = new TextField();
   displayText.text = "0";                  //// задаем первоначальный текст счета "0"
   displayText.selectable = false;       //// делаем текст "невыбираемым"
   displayText.autoSize;                    //// авторазмер
   displayText.x = 0;
   displayText.y = 0;
     
   format = new TextFormat("Arial", 24);    //// задаем шрифт и размер
   displayText.setTextFormat(format);       //// атачим созданный формат текстовому полю
     
   addChild(displayText);
  }
    
  public function setScore(newscore:String):void    //// здесь мы создадим публичную (общедоступную) функцию изменения текста
  {
   this.displayText.text = newscore;
   format = new TextFormat("Arial", 24);
   displayText.setTextFormat(format);
  }
    
}

}

Теперь осталось добавить экземпляр ScreenText'а в нашу игру:

Code

private var scoreLabel:ScreenText;    /// создаем компоненту
private var playerScore:int = 0;        /// создаем переменную счета для игрока

private function init():void
{
  Mouse.hide();
     
  player = new Paddle([0x00FF00, 0x0000FF]);
  player.x = 300;
  player.y = 430;
  stage.addChild(player);
     
  enemy = new Paddle([0xFF0000, 0xFF00FF]);
  enemy.x = 300;
  enemy.y = 50;
  stage.addChild(enemy);
     
  player.addEventListener(Event.ENTER_FRAME, movePlayer);
  enemy.addEventListener(Event.ENTER_FRAME, moveEnemy);
     
  ball = new Ball();
  ball.x = 350;
  ball.y = 410;
  stage.addChild(ball);
     
  [b]scoreLabel = new ScreenText(); /// инициируем
  scoreLabel.x = 15;                         /// задаем местоположение
  scoreLabel.y = 450;
  stage.addChild(scoreLabel);           /// обратите внимание на то что счет должен находить в самом верху списка отображения и перекрывать все игровые элементы, поэтому мы добавляем его на сцену последним[/b]
     
  ball.addEventListener(Event.ENTER_FRAME, moveBall);
}

Теперь мы имеем совершенно идентичные стороннему человеку компоненты созданные и работающие по-разному.
Осталось только добавить возможности прогирывать и выигрывать "сеты".

Для этого возвращаемся к функции движения мяча moveBall и меняем

Code

if (ball.y <= 0) {
  yDir *= -1;
}
else if (ball.y >= this.height - ball.height/2) {
  yDir *= -1;
}

на

Code

if (ball.y <= 0) {                            //// если мяч коснулся верхней части экрана
  ++playerScore;            //// увеличиваем счет игрока
  scoreLabel.setScore(playerScore.toString());   //// вызываем публичную функцию класса ScreenText и задем ей в качесвте параметра новый счет игрока
  resetBallPosition();     //// теперь надо начать новый "сет" при помощи вызова данной функции
}
else if (ball.y >= this.height - ball.height/2) {     //// если мяч коснулся нижней части экрана
  ++enemyScore;        //// увеличиваем лицевой счет компьютера, из-за привязки данных, увеличения значения переменной автоматически повлечет за собой увеличение значения тесктового поля созданой нами компаненты Label
  resetBallPosition();
}

Ну и простая функция новой игры, просто возвращающей первоначальные позиции элемнтам игры:

Code

private function resetBallPosition():void
{
  enemy.x = 300;
  ball.x = 350;
  ball.y = 410;
  xDir = 10;
  yDir = -10;
}

Ну и напоследок давайте добавим в нашу игру спецэффектов))

Откроем файл Ball.as и внесем ряд изменений в наш класс. Добавим 2 внутренние функции:

Code

public function glowBall(color:Array):void               /// в параметр функции подсветки мяча добавим массив цветов, для того чтобы иметь возможности присваивать различные цвета в зависимости от наших целей
{
  var gradientGlow:GradientGlowFilter = new GradientGlowFilter();  /// создаем новый экземпляр класса градиентного свечения
  gradientGlow.distance = 0;                    /// задаем различные параметры, поиграейте с ними, чтобы понять, что есть что  
  gradientGlow.angle = 45;
  gradientGlow.colors = color;
  gradientGlow.alphas = [0, 1];
  gradientGlow.ratios = [0, 255];
  gradientGlow.blurX = 50;
  gradientGlow.blurY = 50;
  gradientGlow.strength = 4;
  gradientGlow.quality = BitmapFilterQuality.MEDIUM;
  gradientGlow.type = BitmapFilterType.OUTER;
  this.filters = [gradientGlow];                    //// эффекты добавляются во внутренний маасив класса Sprite [b]filters[/b]
     
  var timer:Timer = new Timer(100, 1);                        //// создаем таймер отсчитывающий 100 милисекунд один раз
  timer.addEventListener(TimerEvent.TIMER_COMPLETE, unGlowBall);  //// по завершению таймера вызываем функцию затухания
  timer.start();   //// стартуем
}
    
protected function unGlowBall(e:TimerEvent):void
{
  this.filters = null;    //// "нуллим" массив тем самым убирая все эффекты
}

Обратите внимание на то, как осуществляется анимация свечения в данном случае. По скольку положение мяча просчитывается каждый кадр, то столкновение длиться не больше чем 1 секунда / 24 кадра в секунду. "Маловато будет!" (С). Поэтому мы используем анимацию по времени. Свечение в данном случае пропадет через 100 милисекунд после появления. Это обеспечит созданный нами таймер.

Теперь непосредственно добавим подсветку при столкновении мяча с препятствиями внутри функции moveBall:

Code

if (ball.x <= 0) {
  xDir *= -1;
  ball.glowBall([0x000000, 0x0000FF]); /// синяя подсветка для столкновения со стенками
}
else if (ball.x >= this.width - ball.width/2) {
  xDir *= -1;
  ball.glowBall([0x000000, 0x0000FF]);
}
if (ball.hitTestObject(player)) {
  yDir *= -1;
  ball.glowBall([0x000000, 0x00FF00]);  /// зеленая подсветка для столкновения с рокеткой игрока
  checkHitPosition(player);
}
else if (ball.hitTestObject(enemy)) {
  yDir *= -1;
  ball.glowBall([0x000000, 0xFF0000]);  /// красная подсветка для столкновения с рокеткой компьютера
  checkHitPosition(enemy);
}

Полный код Main.mxml (с измененными размерами экрана):

Code

<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="absolute" width="700" height="500" frameRate="24" applicationComplete="init()" xmlns:mx="http://www.adobe.com/2006/mxml">
     <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 var scoreLabel:ScreenText;
   private var playerScore:int = 0;
   [Bindable]
   private var enemyScore:int = 0;
    
   private function init():void
   {
    Mouse.hide();
     
    player = new Paddle([0x00FF00, 0x0000FF]);
    player.x = 300;
    player.y = 430;
    stage.addChild(player);
     
    enemy = new Paddle([0xFF0000, 0xFF00FF]);
    enemy.x = 300;
    enemy.y = 50;
    stage.addChild(enemy);
     
    player.addEventListener(Event.ENTER_FRAME, movePlayer);
    enemy.addEventListener(Event.ENTER_FRAME, moveEnemy);
     
    ball = new Ball();
    ball.x = 350;
    ball.y = 410;
    stage.addChild(ball);
     
    scoreLabel = new ScreenText();
    scoreLabel.x = 15;
    scoreLabel.y = 450;
    stage.addChild(scoreLabel);
     
    ball.addEventListener(Event.ENTER_FRAME, moveBall);
   }
    
   private function resetBallPosition():void
   {
    enemy.x = 300;
    ball.x = 350;
    ball.y = 410;
    xDir = 10;
    yDir = -10;
   }
    
   private function checkHitPosition(paddle:Paddle):void
   {
    var hitPercent:Number;
    var ballPosition:Number = ball.x - paddle.x;
     
    hitPercent = (ballPosition / (paddle.width - ball.width)) - .5;
    xDir = hitPercent * 20;
    yDir *= 1.04;
   }
    
   private function moveBall(e:Event):void
   {
     
    if (ball.x <= 0) {
     xDir *= -1;
     ball.glowBall([0x000000, 0x0000FF]);
    }
    else if (ball.x >= this.width - ball.width/2) {
     xDir *= -1;
     ball.glowBall([0x000000, 0x0000FF]);
    }
    if (ball.hitTestObject(player)) {
     yDir *= -1;
     ball.glowBall([0x000000, 0x00FF00]);
     checkHitPosition(player);
    }
    else if (ball.hitTestObject(enemy)) {
     yDir *= -1;
     ball.glowBall([0x000000, 0xFF0000]);
     checkHitPosition(enemy);
    }
    if (ball.y <= 0) {
     ++playerScore;
     scoreLabel.setScore(playerScore.toString());
     resetBallPosition();
    }
    else if (ball.y >= this.height - ball.height/2) {
     ++enemyScore;
     resetBallPosition();
    }
     
    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:Label id="escore" text="{enemyScore.toString()}" fontFamily="Arial" fontSize="24" x="15" y="15" width="60" height="35"/>
</mx:Application>

Полный код ScreenText.as:

Code

package   
{
  import flash.display.Sprite;
  import flash.text.TextField;
  import flash.text.TextFormat;
   
  public class ScreenText extends Sprite
  {
   protected var displayText:TextField;
   protected var format:TextFormat;
    
   public function ScreenText()  
   {
    displayText = new TextField();
    displayText.text = "0";
    displayText.selectable = false;
    displayText.autoSize;
    displayText.x = 0;
    displayText.y = 0;
     
    format = new TextFormat("Arial", 24);
    displayText.setTextFormat(format);// .defaultTextFormat = format;
     
    addChild(displayText);
   }
    
   public function setScore(newscore:String):void
   {
    this.displayText.text = newscore;
    format = new TextFormat("Arial", 24);
    displayText.setTextFormat(format);
   }
    
  }

}

Полный код Ball.as:

Code

package   
{
  import flash.display.*;
  import flash.events.TimerEvent;
  import flash.geom.Matrix;
  import flash.filters.BitmapFilterQuality;
  import flash.filters.BitmapFilterType;
  import flash.filters.GradientGlowFilter;
  import flash.utils.Timer;
   
  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();
   }
    
   public function glowBall(color:Array):void
   {
    var gradientGlow:GradientGlowFilter = new GradientGlowFilter();
    gradientGlow.distance = 0;
    gradientGlow.angle = 45;
    gradientGlow.colors = color;
    gradientGlow.alphas = [0, 1];
    gradientGlow.ratios = [0, 255];
    gradientGlow.blurX = 50;
    gradientGlow.blurY = 50;
    gradientGlow.strength = 4;
    gradientGlow.quality = BitmapFilterQuality.MEDIUM;
    gradientGlow.type = BitmapFilterType.OUTER;
    this.filters = [gradientGlow];
     
    var timer:Timer = new Timer(100, 1);
    timer.addEventListener(TimerEvent.TIMER_COMPLETE, unGlowBall);
    timer.start();
   }
    
   protected function unGlowBall(e:TimerEvent):void
   {
    this.filters = null;
   }
    
  }

}

Компилим, играем.

B1z ©

reload2Дата: Воскресенье, 28 Августа 2011, 16:47 | Сообщение # 2
частый гость
Сейчас нет на сайте
Спосибо тебе огромное!

Если я тебе помог поставь пожалуста мне +1 к репутации)
  • Страница 1 из 1
  • 1
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг