ActionScript 3: Создание игры «три-в-ряд» Bejeweled (первая часть)

Bejeweled первая «современная» (первый релиз игры состоялся в 2001 году) игра, которая будет рассмотрена в этой книге. Я решил посвятить последние три главы этой книги современным играм, чтобы показать вам, как относительно легко можно создать код большинства современных успешных казуальных игр, а это значит, что в этой нише рынка хорошие идеи преобладают над сложностью игры. Миллионы копий Bejeweled были проданы, и в их Facebook версии играют миллионы людей каждый месяц.

Игра происходит на сетке размером 8×8 с 64 драгоценными камнями семи различных видов, расположенных на ней. Цель — обменивать местами драгоценный камень с соседним, чтобы сформировать горизонтальную или вертикальную линию из трех и более драгоценных камней. Эти камни затем исчезают и сверху падают новые, чтобы заполнить пробелы.

В этой главе вы напишете полностью рабочий код прототипа Bejeweled, изучив следующие методы:

  • цикл do while, чтобы создавать хотя бы одно повторение действий
  • свойство visible класса DisplayObject, чтобы быстро сделать объекты невидимыми
  • строковой метод split, чтобы разбивать строку на массив подстрок используя данный оператор
  • выражение with, чтобы определять объект по умолчанию, который будет использоваться в скрипте

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

Создание документов и объектов

Создайте новый файл (File -> New), затем в окне «New Document» выберите «ActionScript 3.0». Установите свойства документа: ширина 640px, высота 480px, цвет заднего фона #000033 (темно-синий), и частоту кадров 30. Также определите класс документа как Main и сохраните файл как bejeweled.fla.

Мы используем два объекта: первый c семью кадрами, которые содержат все драгоценные камни, имеет имя gem_mc, второй, с именем selector_mc, используется для квадрата, который отображает выбранные камни.

Все ресурсы нарисованы с точкой регистрации в координатах 0,0 и спроектированы так, чтобы вмещаться в тайл размером 60×60.

В файле bejeweled.fla создайте два символа типа MovieClip и назовите их selector_mc и gem_mc. Установите их как экспортируемые для ActionScript и оставьте все другие настройки по умолчанию, также как вы делали в предыдущей главе.

Затем нарисуйте что-то вроде этого:

Символы selector_mc (выделение) и gem_mc (драгоценные камни) типа MovieClip

В верхнем левом углу находится объект selector_mc, в то время как остальные картинки представляют собой семь драгоценных камней в игре.

Размещение драгоценных камней

Размещение драгоценных камней на сцене может показаться простым делом — добавить какие-то случайные объекты типа DisplayObject в список отображения (Display List), но вы увидите, что мы скоро столкнемся с некоторыми трудностями.

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

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

  • двумерный массив для представления игрового поля
  • объект типа DisplayObject в качестве контейнера для всех драгоценностей
  • экземпляр объекта gem_mc

Два цикла for будут заполнять массив случайными числами, когда объекты типа gem_mc будут помещаться на сцену.

Не закрывая bejeweled.fla создайте новый файл и в окне New Document выберите ActionScropt 3.0 Class. Сохраните этот файл как Main.as в той же директории, что и файл bejeweled.fla. Затем напишите:

package {
 import flash.display.Sprite;
 import flash.events.MouseEvent;
 public class Main extends Sprite {
  private var jewels:Array=new Array();
  private var gemsContainer:Sprite=new Sprite();
  private var gem:gem_mc;
  public function Main() {
    jewelsInit();
  }
  private function jewelsInit():void {
   addChild(gemsContainer);
   for (var i:uint=0; i<8; i++) {
     jewels[i]=new Array();
     for (var j:uint=0; j<8; j++) {
       jewels[i][j]=Math.floor(Math.random()*7);
       gem = new gem_mc(jewels[i][j],i,j);
       gemsContainer.addChild(gem);
     }
   }
  }
 } 
}

Сначала я импортирую необходимые классы для игры, такие как Sprite и MouseEvent, так как игра будет управляться мышью.

Затем нам нужно три переменных класса уровня: jewels — массив, который будет содержать данные игрового поля, gemsContainer — экземпляр класса DisplayObject, который будет содержать все драгоценные камни, и gem — экземпляр gem_mc, который сам по себе и есть драгоценный камень.

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

Эта функция очень простая, поскольку она только добавляет gemsContainer в список отображения (Display List), затем создает и заполняет двумерный массив 8×8 случайными целочисленными значениями между 0 и 7. Как только добавляется новый элемент jewels, создается новый экземпляр класса gem_mc.

Единственное, что вы не видели ранее это конструктор класса gem_mc, который имеет три аргумента: сам экземпляр драгоценного камня, его положение по горизонтали и по вертикали.

Очевидно, что класс gem_mc будет управлять внешним видом драгоценностей и их позицией.

Не закрывая bejeweled.fla, создайте новый файл и в окне New Document выберите ActionScript 3.0 Class. Сохраните этот файл как gem_mc.as в той же директории, где сохранен файл bejeweled.fla. Затем напишите следующий код:

package {
  import flash.display.MovieClip;
  public class gem_mc extends MovieClip {
    public function gem_mc(val:uint,row:uint,col:uint) {
      gotoAndStop(val+1);
      name=row+"_"+col;
      x=col*60;
      y=row*60;
    }
  }
}

Обратите внимание, что класс расширяет MovieClip поскольку имеет временную шкалу. Как видите, он просто показывает правильный кадр в соответствии со значением val, и помещает драгоценный камень, учитывая то, что мы играем в квадратных тайлах со сторонами в 60 пикселей.

Я также дал имя этому экземпляру класса DisplayObject, чтобы сделать его выбор мышью более легким при необходимости. Имя состоит из номера, за которым стоит знак подчеркивания, далее идет номер столбца. Вы уже видели эту концепцию во время разработки игры «сапер».

Проверьте работу приложения, вы должны увидеть игровое поле заполненное драгоценными камнями. Вот когда стоит немного расстроиться. Проверьте приложение еще несколько раз, и вы поиграете вот в такую игру:

Несколько комбинаций драгоценных камней в начале игры Bejeweled

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

Проверка комбинаций драгоценных камней в начале игры

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

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

Разработка: Проверка линии не отличается от проверки на победу в игре «Connect 4». Можно даже сказать, что это намного проще, поскольку нужно просматривать только по горизонтали и вертикали, не заботясь о диагонали.

Следуя концепциям, рассмотренным во время создания игры «Connect 4», давайте начнем с создания основных функций.

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

Давайте назовем функцию checkGem и сделаем так, чтобы она возвращала значение true, если есть драгоценный камень в ряду row-th и столбце col-th, иначе значение false.

private function checkGem(gem:uint,row:int,col:int):Boolean {
  if (jewels[row]==null) {
    return false;
  }
  if (jewels[row][col]==null) {
    return false;
  }
  return gem==jewels[row][col];
}

Функция просто проверяет, чтобы значение элемента массива jewels[row][col] равнялось gem, как только мы проверили, что это не нулевое значение. Могут быть нулевые значения, когда мы пытаемся найти драгоценный камень в неверной позиции, которая находится за пределами заполненного массива.

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

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

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

Это функция rowStreak с аргументами row и col, представляющими позицию проверяемого драгоценного камня, которая возвращает количество совпадающих драгоценных камней.

private function rowStreak(row:uint,col:uint):uint {
  var current:uint=jewels[row][col];
  var streak:uint=1;
  var tmp:int=col;
  while (checkGem(current,row,tmp-1)) {
    tmp--;
    streak++;
  }
  tmp=col;
  while (checkGem(current,row,tmp+1)) {
    tmp++;
    streak++;
  }
  return (streak);
}

Давайте кратко рассмотрим код:

var current:uint=jewels[row][col];

Сохраняем в текущей переменной значение массива jewels[row][col], то есть драгоценный камень, который мы будем искать.

var streak:uint=1;

Переменная streak будет сохранять счетчик (count) строки. Он установлен на 1 потому что начальный драгоценный камень сам по себе, который является частью линии.

var tmp:int=col;

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

while (checkGem(current,row,tmp-1)) { ... }

Этот цикл while сканирует драгоценные камни слева. Так как переменная tmp — это значение столбца, уменьшение которого означает перемещение одного столбца влево. Вот что мы делаем внутри цикла:

tmp--;
streak++;

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

Как только мы выходим из цикла, значение переменной tmp сохраняется в col и мы начинаем сканирование справа таким же образом.

В конце функции возвращается значение streak.

Таким же образом мы можем записать функцию colStreak, которая проверяет вертикальные линии.

private function colStreak(row:uint,col:uint):uint {
  var current:uint=jewels[row][col];
  var streak:uint=1;
  var tmp:int=row;
  while (checkGem(current,tmp-1,col)) {
    tmp--;
    streak++;
  }
  tmp=row;
  while (checkGem(current,tmp+1,col)) {
    tmp++;
    streak++;
  }
  return (streak);
}

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

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

Вот что делает функция isStreak. Она просто проверяет, есть ли горизонтальная или вертикальная линия-комбинация, то есть линия длиннее двух драгоценных камней.

private function isStreak(row:uint,col:uint):Boolean {
  return rowStreak(row,col)>2||colStreak(row,col)>2;
}

Как только мы узнаем, что есть линия-комбинация, то мы так же можем предотвратить функцию jewelsInit для ее генерации. Мы просто запустим генерацию случайного драгоценного камня, пока он не станет частью линии-комбинации.

Изменим функцию jewelsInit следующим образом:

private function jewelsInit():void {
  addChild(gemsContainer);
  for (var i:uint=0; i<8; i++) {
    jewels[i]=new Array();
    for (var j:uint=0; j<8; j++) {
      do {
        jewels[i][j]=Math.floor(Math.random()*7);
      } while (isStreak(i,j));
      gem = new gem_mc(jewels[i][j],i,j);
      gemsContainer.addChild(gem);
    }
  }
}

Теперь проверьте приложение, и вы не увидите каких-либо линий-комбинаций.

Теперь в начале игры Bejeweled не создаются линии-комбинации

Главная работа происходит в цикле do while, используемом в функции.

Цикл do while работает также как и цикл while, за исключением того, что код в цикле выполняется хотя бы раз, потому что условие while проверяется в конце блока цикла.

Теперь можно поиграть и проверить приложение.

Это первая часть урока по созданию игры «три-в-ряд» Bejeweled на ActionScript 3 из книги «Flash game development by example» Emanuele Feronato. Если вам понравился урок, то добавьте его в закладки социальных сетей и сохраните (значки внизу).

Автор перевода: Jean Winters

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

Почта не публикуется.Обязательные поля отмечены *