четверг, 11 декабря 2014 г.

Пишем игру "Пятнашки". Часть 2

Привет! Итак, продолжаем создавать игру "Пятнашки". В прошлой статье мы создали графику для нашей игры. Теперь приступим к программированию. Писать код мы будем на языке ActionScript 3.0 в среде FlashDevelop.


Так как "Пятнашки" очень простая игра, код будет относительно несложен и состоять всего из трёх классов:
  1. Main.as   -  базовый класс для запуска игры. Большая его часть генерируется автоматически.
  2. Game.as  - главный класс. Вся игровая логика сосредоточена здесь.
  3. Tile.as - класс отвечающий за плитки с числами в игре.

Итак приступим к написанию кода игры.

Во FlashDevelop создаём новый проект (AS3Project) без прелоадера. В папке с проектом автоматически создадутся три папки: "bin", "lib" и "src".
В папку "lib" необходимо скопировать файл библиотеки SWC, который мы создали в прошлом уроке.
Теперь во FlashDevelop, в окне Project, в папке "lib" найдем наш файл SWC, щелкнем по нему правой кнопкой и в появившемся контекстном меню выберем "Add to library". Тем самым мы добавили нашу графику в проект и теперь можем обращаться к ней.



Теперь сделаем небольшую настройку проекта. Зайдем в меню Project -> Properties. В открывшемся окне установим размеры (Dimensions) - 288 x 400 px.



Начнём писать код с класса "Game". Но сначала этот класс нужно создать. В окне Project, щелкнем правой кнопкой по папке "src", выберем Add -> New Class..



Теперь можно рассмотреть листинг класса  "Game" :

package  
{
 import flash.display.Sprite;
 import flash.events.MouseEvent;
 /**
  * ...
  * @author RablV
  */
 public class Game extends Sprite
 {
  // матрица плиток
  private  var matrix:Vector.<Vector.<Tile>> = new <Vector.<Tile>>[ new <Tile> [null, null, null, null],
                    new <Tile> [null, null, null, null],
                    new <Tile> [null, null, null, null],
                    new <Tile> [null, null, null, null] ];
  // эталонная матрица для проверки победы
  private  var checkMatrix:Array = new Array( [1,  2,  3,  4],
             [5,  6,  7,  8],
             [9,  10, 11, 12],
             [13, 14, 15, 0] );                  
                    
  //массив для хранения рандомной последовательности
  private var randomMas:Array = new Array();
  
  // счетчик ходов
  private var countMc:Count_mc;
  // переменная для хранения колличества ходов
  private var moveCount:int = 0;
  // флаг победы
  private var flagVictory:Boolean = false;
  
  public function Game() {
   
       
   setFon();  // устанавливаем фон
   setRestartBtn(); // устанавливаем кнопку Рестарт        
   setCountMc();  // устанавливаем счетчик ходов
   setTiles();   // устанавливаем плитки
    
  }
  
  // устанавливаем фон
  private function setFon():void {
   var fon:Fon_mc = new Fon_mc(); // берем фон из библиотеки SWC
   addChild(fon);
  }  
       
  // установка кнопки Рестарт
  private function setRestartBtn():void {
   var restartBtn:Restart_btn = new Restart_btn();
   addChild(restartBtn);
   restartBtn.x =  44, 7;
   restartBtn.y = 326, 5;
   restartBtn.addEventListener(MouseEvent.CLICK, newRandGame);
  }
  
  // установка счетчика
  private function setCountMc():void {
   countMc = new Count_mc();
   addChild(countMc);
   countMc.x =  192,55;
   countMc.y = 326,5;
  }
  
  // устанавливаем плитки
  private function setTiles():void {
   
   
   for (var j:int = 0; j < 4; j++ ) {  // в двойном цикле ставим плитки по вертикали и горизонтали
    for (var i:int = 0; i < 4; i++ ) {
     matrix[i][j] = new Tile();
     matrix[i][j].i = i;     // присваемвам плиткам их координаты в матрице
     matrix[i][j].j = j;
     matrix[i][j].x = 4 + i * 64 + 8*i;  // расставляем плитки на игровом поле
     matrix[i][j].y = 4 + j * 64 + 8*j;
     matrix[i][j].tileMc.stop();   // каждую плитку нужно остановить, чтоб не мерцала (т.к. в ней 2 кадра)
     matrix[i][j].addEventListener(MouseEvent.CLICK, move);  // слушатель клика на каждую плитку
     addChild(matrix[i][j]);   
    }
   }
   
   newRandGame();
   
  }
  
  // обработка клика на плику, перемещение плит
  private function move(e:MouseEvent):void {
   
   var currNumber:int = e.currentTarget.number;  // сохраняем номер текущей плитки
   
   if (currNumber != 0 && !flagVictory) {   // если это не пустое поле и не победное положение, то продолжаем
    
   var numberStr:String = String(currNumber) // номер текущей плитки запишем в строку
   var i:int = e.currentTarget.i;    // сохраним координаты плитки в матрице
   var j:int = e.currentTarget.j;
   var flag:Boolean = false;   // флаг для определения завершения хода
   
   
   for (var k:int = 0; k < 4; k++ ) {  // нам нужно проверить наличие пустой плитки с четырёх сторон
 
     switch(k) {
      case 0 :{ 
      if (i+1 < 4 && matrix[i + 1][j].number == 0) { // если плитка не крайняя и снизу есть пустое поле, то   
       matrix[i + 1][j].tileMc.gotoAndStop(1);   // пустое поле превращаем в плитку
       matrix[i + 1][j].tileMc.Number_field.text = numberStr; // задаем новой плитке строку с числом
       matrix[i + 1][j].number = currNumber;  // теперь новая плитка превратилась в ту, на которую мы нажали
       flag = true;   // говорим что ход заврешен
       };
      break;
      }
      
      case 1 :{ 
       if (j+1 < 4 && matrix[i][j+1].number == 0) {
        matrix[i][j + 1].tileMc.gotoAndStop(1);
        matrix[i][j+1].tileMc.Number_field.text = numberStr;
        matrix[i][j + 1].number = currNumber;
       flag = true;
       };
      break;
      }
      
      case 2 :{ 
      if (i-1 > -1 && matrix[i - 1][j].number == 0) {
      
       matrix[i - 1][j].tileMc.gotoAndStop(1);
       matrix[i - 1][j].tileMc.Number_field.text = numberStr;
       matrix[i - 1][j].number = currNumber;
       flag = true;
       };
      break;
      }
      
      case 3 :{ 
       if (j-1 > -1 && matrix[i][j-1].number == 0) {
        matrix[i][j - 1].tileMc.gotoAndStop(1);
        matrix[i][j - 1].tileMc.Number_field.text = numberStr;
        matrix[i][j - 1].number = currNumber;
       flag = true;
       };
      break;
      }
     }
     if (flag) { // если ход завершен
      matrix[i][j].number = 0;  // текущую плитку делаем пустым полем
      e.currentTarget.tileMc.gotoAndStop(2);
      moveCount++;   // счетчик ходов
      countMc.Count_field.text = String(moveCount); // увеличиваем счетчик
      if(check()) flagVictory=true; // проверка на победу
      break; 
     }
    }
   }
  }
  
  // функция создания новой игры
  private function newRandGame(e:MouseEvent = null):void {
   flagVictory = false;  // сброс флага победы
   moveCount=0;  // сброс счетчика ходов
   countMc.Count_field.text = String(moveCount); 
   
   var tempcount:int = 0;  // временная переменная для присвоения занчений в массиве
   random();    // генерируем рандомную последовательность от 1 до 15 в массив
   for (var j:int = 0; j < 4; j++ ) { // в двойном цикле пишем рандомную последовательность в нашу матрицу
    for (var i:int = 0; i < 4; i++ ) {
     matrix[i][j].tileMc.gotoAndStop(1);
     matrix[i][j].tileMc.Number_field.text = String(randomMas[tempcount]);
     matrix[i][j].number = randomMas[tempcount];
     tempcount++;
    }
  }
  

  matrix[3][3].tileMc.gotoAndStop(2); // плитку в правом нижнем углу делаем пустым полем
  matrix[3][3].number = 0;
  }
  
  // создание масива с рандомной последовательностью от 1 до 15
  private function random():void {
   var flag:Boolean = true;
   
   var temp:int = 0;
   var count:int = 0;
   for (var k:int = 0; k < 16; k++ ) randomMas[k]=0 // обнуляем массив
    
   
   for (var j:int = 0; j <15; j++ ) { // в цикле создаем и записываем в массив 15 чисел
    while (flag) {    // этот цикл работает до тех пор, пока не сгенерируется число, которого еще нет в массиве
     if (count == 15) break;
     temp = 15 * Math.random() + 1;
     flag = false;
     for (var i:int = 0; i < 16; i++ ) {  // цикл проверяет сгенерированное число, на его наличие в массиве
      if (randomMas[i] == temp) { flag = true; break; }
     }
    }
    
    randomMas[j] = temp;
    count++;
    flag = true;
   }
  }
   
  // проверка победы
  private function check():Boolean {

   // сравниваем текущую матрицу плиток с эталонной матрицей
   for (var j:int = 0; j < 4; j++ ) {
    for (var i:int = 0; i < 4; i++ ) {
     if (matrix[i][j].number != checkMatrix[j][i]) {
      return false;
      }
    }}
   return true;
   }
  }

}


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

Теперь посмотрим как устроен класс "Tile"

package  
{
 import flash.display.Sprite;
 /**
  * ...
  * @author RablV
  */
 public class Tile extends Sprite
 {
  public var i:int;  // положение в матрице
  public var j:int;
  public var number:int;  // номер плитки
  public var tileMc:Tile_mc;  
  public function Tile() 
  {
   tileMc = new Tile_mc();
   addChild(tileMc);
   
  }
  
 }

}


Здесь всё просто. Три параметра и собственно сама плитка из нашей библиотеки SWC.

Ну и последний листинг класса "Main"

package 
{
 import flash.display.Sprite;
 import flash.events.Event;
 
 import flash.display.SimpleButton;
 
 /**
  * ...
  * @author RablV
  */
 public class Main extends Sprite 
 {
  
  public function Main():void 
  {
   if (stage) init();
   else addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event = null):void 
  {
   removeEventListener(Event.ADDED_TO_STAGE, init);
   // entry point
   var game:Game = new Game(); // создаём новую игру
   addChild(game);
   
  }
  
 }
 
}


Здесь просто создаётся игра, с помощью класса Game.

И так, игру мы написали, теперь можно её скомпилировать и запустить.
В итоге у нас должно получиться вот что:



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

Проект с игрой можно скачать по ссылке.

На этом всё. В следующей статье мы попробуем портировать наши "Пятнашки" на Андроид с помощью технологии Adobe Air

Комментариев нет:

Отправить комментарий