TowerDefence #2. Карта проходимости

В прошлом уроке мы определились с базовой структурой нашей игры и создали небольшую заготовку. Следующий наш этап — это определиться с базовой структурой мира и заложить фундамент.

Но прежде немного вернемся к предыдущему уроку и вспомним домашнее задание. Я, конечно, рассчитывал на то, что домашнее задание достаточно простое и его не придется отдельно разбирать. Но проблема возникла там, где её никто не ждал. Согласно не проверенным пока (мною) данным в новом Flash CS5 появился новый рутовый не документированный класс World. ? из-за него пользователям этой версии Flash не удалось создать World.as. Чтобы избежать этой проблемы класс World.as мы переименуем в Universe.as.

Возвращаемся к теме урока. Определение структуры игрового мира — это наиболее важный момент в разработке игры, от которого многое зависит. Честно признаюсь, до этого я еще не написал ни одного TowerDefence, поэтому сейчас мне сложно однозначно определить какая структура будет наиболее подходящей и удобной. Так что придется немного импровизировать и возможно, по ходу дальнейших уроков, вносить какие-либо изменения в игру. Но, я думаю, что даже если это и случится, то это будет интересной демонстрацией того, как находить свои недочеты и своевременно их исправлять :)

?гровой мир

Любой игровой мир делится на несколько частей:

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

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

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

? так, у нас получилось три основных составляющих игрового мира. Теперь это все нужно объединить в одном классе мира и заставить работать. С чего же начать и как это все органично совместить?

Немного о графике

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

Карта проходимости

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

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

  • СВОБОДНА (FREE) — по такой ячейке сможет перемещаться враг, и игрок сможет строить свои постройки;
  • ТОЛЬКО_ПОСТРОЙК? (BUILD_ONLY) — в такой ячейке игрок может строить башни, но враг не может проникать в эти ячейки, даже если ячейка не занята башней игрока;
  • ЗАНЯТА (BUSY) — в такой ячейке нельзя строить постройки, и враг не может по ним перемещаться.

 

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

Что ж, от теории к делу, реализуем карту проходимости.

Программируем кусочками

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

Примечание: Возможно некоторым профи не понравится такой подход программирования. Но для новичков это 100% верный путь. Такой подход упрощает поиск ошибок, потому что программист знает, какие строки только что редактировал и где мог наделать опечаток.

Реализация карты проходимости у нас будет состоять из следующих задач:

  • Задача 1 — игроку не обязательно видеть сетку карты и тем более карту проходимости, но нам на этапе разработки обязательно все это нужно будет видеть. Поэтому первым делом мы сделаем сетку карты.
  • Задача 2 — реализовать карту проходимости.
  • Задача 3 — связать карту проходимости с отладочной сеткой так, чтобы можно было видеть состояние ячеек карты проходимости на сетке.
  • Задача 4 — оптимизировать то безобразие, которое у нас получится в итоге :)

 

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

Задача 1

Прежде, чем начнем писать код, подготовим графику для отображения сетки. Создаем новую папку в библиотеке нашего fla проекта с названием «Debug» и в папке этой создаем новый MovieClip с названием DebugCell_mc. В свойствах клипа ставим галочку экспорта в ActionScript и открываем его для редактирования. В этом клипе у нас будет 2 кадра, в каждом из которых будет содержаться визуальное состояние для свободной и занятой ячейки. Для этого в первом кадре рисуем зеленый квадратик размером 32x32 пикселя, а во втором кадре красный квадратик тоже 32x32. Для этих квадратиков желательно сделать цвет обводки контрастным цветом (например черным или белым). Сохраняем результат.

?нформация: Поскольку названия экспортируемых клипов в ActionScript могут совпадать с именами рабочих классов приложения, то в целях избежания этой проблемы всем экспортируемым клипам из библиотеки к их имени я добавляю постфикс. В данном случае это «_mc». При создании экземпляра клипа в коде, этот постфикс дает нам понять, что мы работаем не классом DebugCell, а именно с клипом из библиотеки. Тут, возможно, существуют какие-то стандарты использования постфиксов и префиксов, но я с ними не сталкивался, поэтому вот так...

Теперь приступаем непосредственно к коду. Первым делом добавим три статические константы в Universe.as которые будут у нас отвечать за максимальный размер карты в ячейках и за размер одной ячейки:

public static const MAP_WIDTH_MAX:int = 20;
public static const MAP_HEIGHT_MAX:int = 15;
public static const MAP_CELL_SIZE:int = 32;

Пишем метод makeDebugGrid(), который будет создавать отладочную сетку.

private function makeDebugGrid():void
{
  var grid:Sprite = new Sprite(); // Контейнер для всех ячеек
  var cellSprite:MovieClip; // Текущая ячейка
  var posX:int = 0; // Положение текущей ячейки по ширине
  var posY:int = 0; // Положение текущей ячейки по высоте
            
  // Двигаемся по высоте карты
  for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
  {
    // Двигаемся по ширине карты
    for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
    {
      // Создаем визуальную ячейку
      cellSprite = new DebugCell_mc();
      cellSprite.x = posX;
      cellSprite.y = posY;
      cellSprite.stop();
                    
      // Добавляем ячейку в контейнер
      grid.addChild(cellSprite);
                
      // Меняем положение текущей ячейки по ширине
      posX += MAP_CELL_SIZE;
    }
                
    // Меняем положение текущей ячейки по высоте
    posY += MAP_CELL_SIZE;
    posX = 0;
  }
            
  addChild(grid);
}

Добавляем вызов метода makeDebugGrid() в конструктор класса Universe.as и компилируем нашу игру. Если все сделано верно, то перед вами должен получится зеленый экран в клеточку. Если экран зеленый и нет клеточек, значит вы забыли сделать обводку для квадратиков в клипе DebugCell_mc. Так же не забудьте добавить import flash.display.MovieClip; в классе Universe.as, чтобы Flash не ругался на якобы неизвестный ему MovieClip.

Задача 2

Теперь сделаем карту проходимости. В текущей версии AS3 нет родной поддержки многомерных массивов, но вкладывая один массив в другой мы сможем получить нужный нам результат. Для этого напишем небольшой метод создания чистой карты проходимости. Но вначале создадим еще две глобальные статические константы, которые у нас будут отвечать за состояние клеток в карте проходимости. Значения констант будут соответствовать номеру кадра в клипе DebugCell_mc:

public static const STATE_CELL_FREE:int = 1;
public static const STATE_CELL_BUSY:int = 2;

Добавим локальную переменную _mapMask:Array — это будет наша карта проходимости. ? напишем метод clearMapMask(), который будет создавать новую карту проходимости:

private function clearMapMask():void
{
  // Создаем новый массив
  _mapMask = [];
  _mapMask.length = MAP_HEIGHT_MAX;

  // Двигаемся по высоте карты
  for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
  {
    // Добавляем новую строку в массив
    _mapMask[ay] = [];
    _mapMask[ay].length = MAP_WIDTH_MAX;
                
    // Двигаемся по ширине карты
    for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
    {
      // Задаем ячейке свободное состояние
      _mapMask[ay][ax] = STATE_CELL_FREE;
    }
  }
}

Добавляем вызов метода clearMapMask() в конструктор класса Universe.as обязательно перед вызовом метода makeDebugGrid(). Тестируем нашу игру. Если сделано все правильно, то не должны возникнуть ошибки и мы должны увидеть результат предыдущей задачи, так как в данном шаге мы не сделали никаких визуальных изменений.

Задача 3

Теперь настало время сделать отображение реального состояния ячеек в карте проходимости. Для этого нам понадобятся два публичных метода для работы с картой проходимости. Первый метод getCellState() будет возвращать состояние указанной ячейки, а второй setCellState() будет устанавливать новое состояние для указанной ячейки.

public function getCellState(ax:int, ay:int):int
{
  // Проверка на выход за пределы массива
  if (ax >= 0 && ax < MAP_WIDTH_MAX &&
    ay >= 0 && ay < MAP_HEIGHT_MAX)
  {
    return _mapMask[ay][ax];
  }
  else
  {
    return STATE_CELL_BUSY;
  }
}
        
public function setCellState(ax:int, ay:int, state:int = STATE_CELL_FREE):void
{
  // Проверка на выход за пределы массива
  if (ax >= 0 && ax < MAP_WIDTH_MAX &&
    ay >= 0 && ay < MAP_HEIGHT_MAX)
  {
    _mapMask[ay][ax] = state;
  }
}

Примечание: обращаясь к двумерному массиву, помните, что первым параметром у нас указывается номер строки, а вторым номер ячейки.

В будущем мы будем использовать эти методы везде для проверки состояния карты проходимости и для внесения в неё изменений.

Теперь пора сделать реальное отражение состояния карты проходимости. Вернемся к методу makeDebugGrid(), найдем в нем строку cellSprite.stop() и заменим её на cellSprite.gotoAndStop(getCellState(ax, ay)); Компилируем игру и в случае успеха окно игры должно выглядеть так же, как и после первой задачи без каких-либо ошибок и изменений. А все потому, что на нашей карте проходимости нет ни одного препятствия! :)

Чтобы установить препятствие, используем теперь метод setCellState() и изменим конструктор класса Universe.as следующим образом:

clearMapMask(); // Создаем маску
// Устанавливаем препятствия
setCellState(1, 1, STATE_CELL_BUSY);
setCellState(2, 1, STATE_CELL_BUSY);
// Создаем сетку
makeDebugGrid();

Примечание: Помните, что координаты ячеек у нас всегда начинаются с 0 и заканчиваются по ширине 24, по высоте 14 (всегда на 1 меньше чем указано в константах). Поэтому чтобы установить препятствие в первую ячейку нам нужно передать нулевые координаты: setCellState(0, 0, STATE_CELL_BUSY);

Компилируем игру и в результате мы должны увидеть два красных квадратика. Правда все просто?! Но не спешите радоваться.

Задача 4

Профи, написавшие хотя бы одну игру, уже должны были заметить, что способ, которым я решил так наглядно сделать вывод игровой сетки и карты проходимости, хоть и удобен с точки зрения дизайна, но крайне не производителен! Все дело в том, что для отображения каждой ячейки мы создаем отдельный экземпляр MovieClip'a, а каждый экземпляр кушает производительность и ресурсы нашей игры. Я сейчас не буду вдаваться в технические подробности, но на деле, чем меньше визуальных объектов вроде MovieClip на сцене, тем быстрее будет работать наша игра. А в данном примере на одну только сетку 20 * 15 = 300 клипов используется, что является полным расточительством драгоценных ресурсов! Этот недочет нельзя оставлять на потом (до того как игра начнет тормозить), и обязательно нужно сделать небольшую оптимизацию здесь и сейчас.

Чтобы сделать оптимизацию, нам нужно все это множество клипов объединить в один клип, а еще лучше сделать сетку растровой картинкой. Для этого заведем локальную переменную _debugGrid:Bitmap; и импортируем базовые классы:

import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.*;

Далее перепишем метод makeDebugGrid() следующим образом:

private function makeDebugGrid():void
{
  var grid:Sprite = new Sprite(); // Контейнер для всех ячеек
  var cellSprite:MovieClip; // Текущая ячейка
  var posX:int = 0; // Положение текущей ячейки по ширине
  var posY:int = 0; // Положение текущей ячейки по высоте
            
  // Двигаемся по высоте карты
  for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
  {
    // Двигаемся по ширине карты
    for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
    {
      // Создаем визуальную ячейку
      cellSprite = new DebugCell_mc();
      cellSprite.x = posX;
      cellSprite.y = posY;
      cellSprite.gotoAndStop(getCellState(ax, ay));
                
      // Добавляем ячейку в контейнер
      grid.addChild(cellSprite);
        
      // Меняем положение текущей ячейки по ширине
      posX += MAP_CELL_SIZE;
    }
            
    // Меняем положение текущей ячейки по высоте
    posY += MAP_CELL_SIZE;
    posX = 0;
  }
            
  // Растеризация векторной сетки для оптимизации 
  var bmpData:BitmapData = new BitmapData(MAP_CELL_SIZE * MAP_WIDTH_MAX, MAP_CELL_SIZE * MAP_HEIGHT_MAX, true, 0x00000000);
  var matrix:Matrix = new Matrix();
  var rect:Rectangle;
  rect = grid.getRect(grid);
  matrix.translate(-rect.x, -rect.y);
  bmpData.draw(grid, matrix);
            
  // Если сетка уже была создана ранее, удаляем её
  if (_debugGrid && contains(_debugGrid))
    removeChild(_debugGrid);
            
  // Создаем новую растровую сетку
  _debugGrid = new Bitmap(bmpData);
  addChild(_debugGrid);
            
  // Удаляем клипы ячеек и сам контейнер
  while (grid.numChildren > 0)
  {
    grid.removeChild(grid.getChildAt(0));
  }
  grid = null;
}

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

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

Домашнее задание

Если вы помните, то в начале урока речь шла о трех состояниях ячеек: свободная (FREE), занятая (BUSY) и только для постройки башен (BUILD_ONLY). Сегодняшнее домашнее задание будет заключаться в самостоятельном добавлении третьего состояния ячейки в игровой мир. Это достаточно простая задача. Добавьте новую публичную константу, новый кадр в DebugCell_mc ну и пробуйте установить данное состояние ячейки соответствующим методом в конструкторе мира.

Заключение

Не смотря на то, что я обещал в прошлый раз рассказать об общей структуре игры, у меня этого сегодня не получилось. Я решил что теория — это скучно, пусть будет больше практики, а структура игры прояснится к середине уроков. Сегодня запись вновь получилась больше рассчитанная на новичков, но не переживайте об этом, сложность будет возрастать в геометрической прогрессии. Уже в следующем уроке мы добавим врагов и заставим их бегать (пока игнорируя препятствия), а через урок напишем самый простой алгоритм поиска пути и таким образом уже через пару уроков, я надеюсь, дело дойдет до строительства башен и истребления вражин :)

Ссылка на исходники — CS4, *.zip, 96 кб.

Вопросы относительно уроков вы можете задавать мне на почту. Контактный e-mail есть на странице «Об авторе».

Содержание

  1. Вступление
  2. Структура игры
  3. Карта проходимости
  4. Первый враг
  5. Готовимся к поиску пути
  6. Поиск пути
  7. Редактор уровней
  8. Движение врагов
  9. Первая башня
  10. Кэширование объектов
  11. Полоса жизни
  12. Вражеские волны
  13. Загрузка вражеских волн
  14. Продолжение следует...

 

 

Спасибо, Антон!
Как раз закончил читать очередную главу книги Колина Мука:) Как ты и советовал, пытаюсь параллельно твоим урокам учить все теоретические основы по "Action Script 3. Подробное руководство"

Dyrk
24 Октября 2010
— 23:19
#

Отличный урок, как раз делаю товер. С нетерпением жду следуещего))

Legioner
25 Октября 2010
— 00:34
#

Говорите префикс, а добавляете в конец:) В конец это постфикс:)
Спасибо за урок!

Степа
25 Октября 2010
— 07:45
#

Кстати, чтобы избежать конфликтов в именах, помимо пре- и постфиксов можно просто ставить своим мувикам пэккэджи. Просто вписываем в поле Linkage что-то вроде:

symbols.pickups.HealthPickup (ну это я чисто для примеру).

Потом у вас в коде будет

import symbols.pickups.HealthPickup

? даже если где-то и будет класс HealthPickup, он едва ли будет конфликтовать еще и по неймспейсам.

Ну и вообще с точки зрения порядку:)

Еще раз спасибо за урок!

Стёпа
25 Октября 2010
— 07:55
#

Усложнил себе домашку. Создаем клетки программно.
1. Создаем файл DebugCell_dinamic.as
2. Пишем код:
[code]
package com.towerdefence {
import flash.display.Shape;
import flash.display.MovieClip;
public class DebugCell_dinamic extends MovieClip{
function Create_DebugCell(cellStatus:uint,cellS:uint){//Передаем статус клетки, размер клетки
var bgColor:uint;
//Определяем цвет клетки
switch (cellStatus) {
case 1 :
bgColor =0x1CE36B;
break;
case 2 :
bgColor =0xB13C0A;
break;
case 3 :
bgColor =0x2281DD;
break;
default :
bgColor =0xB13C0A;
}
//Рисуем квадрат
var box:Shape = new Shape();
box.graphics.beginFill(bgColor);
box.graphics.lineStyle(1,0xFF0000);
box.graphics.drawRect(0, 0, cellS, cellS);
this.addChild(box);
}
}
}
[/code]

Genm
25 Октября 2010
— 12:41
#

3. Меняем в Universe.as
var cellSprite:MovieClip; // Текущая ячейка
на
var cellSprite:DebugCell_dinamic; // Текущая ячейка
4. Меняем
// Создаем визуальную ячейку
cellSprite = new DebugCell_mc();
cellSprite.x = posX;
cellSprite.y = posY;
cellSprite.gotoAndStop(getCellState(ax, ay));
на
[code]
// Создаем визуальную ячейку
cellSprite = new DebugCell_dinamic();
cellSprite.x = posX;
cellSprite.y = posY;
cellSprite.Create_DebugCell(getCellState(ax, ay),MAP_CELL_SIZE);
[/code]

Genm
25 Октября 2010
— 12:45
#

5. Еще заметил одну вещь, если заменить
// Растиризация векторной сетки для оптимизации
var bmpData:BitmapData = new BitmapData(MAP_CELL_SIZE * MAP_WIDTH_MAX, MAP_CELL_SIZE * MAP_HEIGHT_MAX, true, 0x00000000);
var matrix:Matrix = new Matrix();
var rect:Rectangle;
rect = grid.getRect(grid);
matrix.translate(-rect.x, -rect.y);
bmpData.draw(grid, matrix);
на

// Растиризация векторной сетки для оптимизации
var bmpData:BitmapData = new BitmapData(MAP_CELL_SIZE * MAP_WIDTH_MAX, MAP_CELL_SIZE * MAP_HEIGHT_MAX, true, 0x00000000);
var matrix:Matrix = new Matrix();
bmpData.draw(grid, matrix);

Тоже все работает ;D
Теперь еще сделать вместо
setCellState(1, 1, STATE_CELL_BUSY); // Устанавливаем препятствия
Создание поля нажатием мышки, будет ваще замечательно)

Genm
25 Октября 2010
— 12:50
#

@Стёпа, да, с префиксами и постфиксами ошибся. Я пока в разных проектах ставлю разные приставки и в разных местах, не определился еще как удобнее и всегда их называю про себя префиксами :) Пост поправил, спасибо!

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

Ant.Karlov
25 Октября 2010
— 14:42
#

@Genm, а теперь еще усложните себе задачу и откажитесь от лишнего класса DebugCell_dinamic. Функционал для которого вы используете специальный класс, можно легко уместить в 9 строк кода :)

Ant.Karlov
25 Октября 2010
— 14:50
#

Круто, спасибо! Когда следующий урок?))

pkasha
25 Октября 2010
— 17:19
#

Ок)
Тогда так… Берем исходный проект и меняем.
cellSprite = new DebugCell_mc();
на
cellSprite = new MovieClip;

?
cellSprite.gotoAndStop(getCellState(ax, ay));
меняем на
Create_DebugCell(cellSprite,getCellState(ax, ay),MAP_CELL_SIZE)

Добавляем функцию туда же в Universe.as
private function Create_DebugCell(mc:MovieClip,cellStatus:uint,cellS:uint):void{//Передаем Мувиеклип,куда добавляем квадрат, статус клетки, размер клетки
var bgColor:uint;
//Определяем цвет клетки
switch (cellStatus) {
case 1 :
bgColor =0x09771E;
break;
case 2 :
bgColor =0xB13C0A;
break;
case 3 :
bgColor =0x2281DD;
break;
default :
bgColor =0xB13C0A;
}
//Рисуем квадрат
var box:Shape = new Shape();
box.graphics.beginFill(bgColor);
box.graphics.lineStyle(1,0xFF0000);
box.graphics.drawRect(0, 0, cellS, cellS);
mc.addChild(box);
}

Genm
25 Октября 2010
— 18:33
#

@Genm, а зачем?

f-duck
25 Октября 2010
— 18:37
#

А так уже ближе к теме, я думаю)
Меняем в исходном файле. Universe.as
Добавляем:
import flash.display.Shape;

Меняем:
// Создаем визуальную ячейку
cellSprite = new DebugCell_mc();
cellSprite.x = posX;
cellSprite.y = posY;
cellSprite.gotoAndStop(getCellState(ax, ay));

на
var bgColor:uint;
// Создаем визуальную ячейку
cellSprite = new MovieClip;
cellSprite.x = posX;
cellSprite.y = posY;
//Определяем цвет клетки
switch (getCellState(ax, ay)) {
case 1 :
bgColor =0x09771E;
break;
case 2 :
bgColor =0xB13C0A;
break;
case 3 :
bgColor =0x2281DD;
break;
default :
bgColor =0xB13C0A;
}
//Рисуем квадрат
var box:Shape = new Shape();
box.graphics.beginFill(bgColor);
box.graphics.lineStyle(1,0xFF0000);
box.graphics.drawRect(0, 0, MAP_CELL_SIZE, MAP_CELL_SIZE);
cellSprite.addChild(box);

Genm
25 Октября 2010
— 18:48
#

f-duck, а хз) Могу предположить, что будет меньше размер флэшки и изменять легче размер квадратиков...

Genm
25 Октября 2010
— 18:50
#

Спасибо за урок. Не дождусь, когда прийду домой и сделаю это все на практике. Только не надо усложнять. Уровень уроков как-раз отличный. С одной стороны - все понятно. С другой - очень много нового... :)

jarofed
25 Октября 2010
— 18:56
#

Отличный урок. Жду следующего!

?ван
25 Октября 2010
— 20:21
#

Ураа=))
Спасибо за уроки и за понятное ясное обьяснение AS3! А когда будет следующий урок??

Руслан
25 Октября 2010
— 21:26
#

Возник еще вот какой вопрос: Почему мы делаем три типа клеток: пустая (можно двигаться и ставить башни), только для башен и занятая (нельзя ни вигаться ни ставить башни)? Получается, что мы можем ставить башни на дорогу, тем самым превращая клетки в занятые и блокируя проход.

Тогда как в большинстве TD-игор (тех, в которые приходилось играть) ставить башни на дорогу нельзя. То-есть по логике там три клетки: только для башен, только для движения и занятые.

Как в случае, представленном в уроке, избежать блокирования дороги башнями? ?ли, возможно, я что-то не так понял?

jarofed
25 Октября 2010
— 21:48
#

@jarofed, есть TD-игра - "Plants vs. Zombies", а там вообще можно ставить башни на все клетки, блокируя все ходы. Я думаю, что это не повредит самой игре.

Антон, по-моему, после 1ой задачи сетка не будет появлятся, мы же не создавали объект Universe в классе App, а создали там только объект класса Game.

? еще, если я правильно понял, то у нас папки towerdefence и Debug обе лежат в папке com? А не какая-то из них лежит в другой?

xiii
25 Октября 2010
— 23:13
#

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

Если обновление debugGrid будет происходить постоянно, то такая оптимизация будет только зря плодить мусор для Carbage Collector`а, который итак не слишком расторопен.

Buxom Berry
25 Октября 2010
— 23:28
#

Самый удачный вариант - создать отдельный класс на основе спрайта, который будет отрисовывать на себя отладочную сетку (тем же this.graphics.drawRect).
Причем только тогда, когда изменяется _mapMask.
А сам _mapMask в нем же и хранить.

Buxom Berry
25 Октября 2010
— 23:34
#

xiii, сетка появляется. Мы создавали в домашнем задании и объект класса universe. В app.as или в game.as. А в сам function universe() { } прописываете вызов метода makeDebugGrid()

Dyrk
26 Октября 2010
— 00:05
#

xiii
извините за 2 комментария подряд, не нашел кнопку "редактировать" (Антон, ?:).

Вы папку Debug создаете в библиотеке вашего .fla проекта. Найдите вкладку Library - New folder. ? туда уже помещайте новый мувиклип. То есть вы создаете папку не в самой файловой системе Windiws. Если я правильно понял, что вы имели ввиду

Dyrk
26 Октября 2010
— 00:11
#

@pkasha, следующий урок теперь наверное не раньше следующей недели. Что-то у меня со временем совсем туго :(

Ant.Karlov
26 Октября 2010
— 01:42
#

@jarofed, ты все правильно понял. Дело в том, что большинство товердефенсов представляют коридорные дорожки по которым монстры бегут как по рельсам, я решил немного отойти от этого стандарта и сделать более разнообразные дороги. В нашем примере будет простой алгоритм поиска пути на местности, таким образом в итоге вообще может и не быть как таковой дороги, монстры могут бежать по пересеченной местности, а ставя башни у них на дороге мы сможем в какой-то мере менять маршрут их движения и за счет этого разнообразим стратегию игры. Конечно таким образом у игрока будет как бы возможность построить забор из башен, но мы этого не позволим сделать :) Чтобы избежать полного блокирования пути для врагов, достаточно перед постройкой башни искать пункт от точки появления врагов до их цели с условно построенной башней, и если путь не будет найден, то значит башню которую игрок планирует поставить, загораживает последний проход и тогда построить мы ему не дадим!

Подобная идея движения врагов конечно не нова, но все же не так замучена как рельсовые игры. Так что будет интересно...

Ant.Karlov
26 Октября 2010
— 01:54
#

@xiii, у нас происходит вложение классов один в другой. App.as создает в себе Game.as, а при создании Game.as создается Universe.as, так что все работает как нужно. Такая структура игры нужна чтобы потом когда будет более менее рабочий геймплей, мы бы легко смогли добавить игровые интерфейсы.

Я написал в записи, что папку Debug необходимо создать в библиотеке нашего fla проекта, а потом в ней создать клип. То есть мы создаем эту папку именно внутри документа TowerDefence.fla.

Ant.Karlov
26 Октября 2010
— 01:58
#

@Buxom Berry, абсолютно верно. Данный способ отрисовки сетки больше всего подходит для единичного или очень редкого её создания. Только вот «мусор» будет плодится скорее не переводом клипов в растр, а именно созданием такого множества клипов и последующего, почти моментального их удаления.

Такую сетку можно нарисовать используя всего один клип (если сетку надо красиво оформить)! ?ли не используя клипов и объектов вообще. Но тут нужно уже включить немного голову и подумать как это можно сделать ;) Когда нам понадобится сделать регулярное обновление состояние карты проходимости, то мы к этому вернемся.

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

* — это когда хранятся прямые ссылки на разные классы внутри друг друга и нам приходится к ним обращаться как-то так: Enemy.as -> Universe.as -> MapMask.as, что в итоге может привести к полному хаусу и невозможности быстро и аккуратно вносить изменения ничего не ломая.

Ant.Karlov
26 Октября 2010
— 02:09
#

@Genm, раз уж пошла такая «пьянка», то вот самый оптимальный код создания векторной сетки без изысков:

var grid:Sprite = new Sprite();
grid.graphics.lineStyle(1, 0x57ACC1);
for (var dy = 0; dy < MAP_HEIGHT_MAX; dy++)
{
for (var dx = 0; dx < MAP_WIDTH_MAX; dx++)
{
grid.graphics.drawRect(dx * MAP_CELL_SIZE, dy * MAP_CELL_SIZE, MAP_CELL_SIZE, MAP_CELL_SIZE);
}
}
addChild(grid);

Нужно только раскрасить клеточки и при необходимости сделать растровую копию ;)

Ant.Karlov
26 Октября 2010
— 02:14
#

Антон, объясните пожалуйста зачем нужна секция кода под комментарием "Если сетка уже была создана ранее, удаляем её". Я не понимаю, как она уже может быть создана, если создается всего один раз при компиляции?

?лья
26 Октября 2010
— 08:28
#

?лья, эт на случай если захочется сетку отрубить, а потом в какой-нить момент включить. Чтоб быть уверенным что все начинается с чистого листа так сказать :) На самом деле это чаще всего перестраховка на случай если что-то пойдет не совсем как рассчитываешь )

Fushigi
26 Октября 2010
— 14:32
#

Расскажите как прошла растеризация, очень не понятный момент(((

pkasha
26 Октября 2010
— 14:47
#

Спасибо за объяснение, Антон. Теперь все понятно. Действительно, так даже интересно, поскольку с такими TD-играми мне не приходилось еще встречаться. Тем интереснее будет сделать ее самому. :)))

По возможности не затягивай со следующим уроком, пожалуйста. Сам понимаешь - это почти как наркотик. Что-то уже начинает получаться, хочется дальше... а дальше нет... надо ждать. :(

jarofed
26 Октября 2010
— 17:31
#

jarofed, вот пример такого дефенса:
Pirate Defense
Гораздо интереснее обычного, т.к. есть где разгуляться фантазии в обороне. В данной игре как раз тоже реализован запрет на строительство сплошной стены из "башен"

Dyrk
26 Октября 2010
— 17:49
#

Спасибо, оценил. :) Странно, что раньше никогда не попадались подобные игры... Действительно интересно, чем классическое движение "по дорожке".

jarofed
26 Октября 2010
— 19:01
#

Большое спасибо :)

Тимур
26 Октября 2010
— 23:46
#

@Ant.Karlov Да действительно, зачем создавать эти mc...
Переделал под твой код,


var grid:Sprite = new Sprite(); // Контейнер для всех ячеек

// Двигаемся по высоте карты
for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
{
// Двигаемся по ширине карты
for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
{
var bgColor:uint;
// Создаем визуальную ячейку
//Определяем цвет клетки
switch (getCellState(ax, ay)) {
case 1 :
bgColor =0x09771E;
break;
case 2 :
bgColor =0xB13C0A;
break;
case 3 :
bgColor =0x2281DD;
break;
default :
bgColor =0xB13C0A;
}
//Рисуем квадрат
grid.graphics.beginFill(bgColor);
grid.graphics.lineStyle(1,0xFF0000);
grid.graphics.drawRect(ax * MAP_CELL_SIZE,ay * MAP_CELL_SIZE, MAP_CELL_SIZE, MAP_CELL_SIZE);
}
}


и увидел, что производительность повысилась почти в 6 раз. Причем твой код (где квадратики ручками создаются) быстрее выполняется, чем тот где они создаются динамически и не имеет значения через класс, функцию или внутри... Наверное, из-за того, что постоянно создаются mc...
Кстати, еще поменял

_mapMask[ay] = [];
_mapMask[ay].length = MAP_WIDTH_MAX;

на

_mapMask[ay] = new Vector.<int>(MAP_WIDTH_MAX, true);

Толку от этого никакого, но все равно приятно :D

Genm
27 Октября 2010
— 00:24
#

Причем можно заменить:

var grid:Sprite = new Sprite(); // Контейнер для всех ячеек

на

var grid:Shape = new Shape(); // Контейнер для всех ячеек

Меньше памяти занимает и чуть производительность улучшилась, вроде...

Genm
27 Октября 2010
— 00:44
#

Genm, Сишник что ли?

fduck
27 Октября 2010
— 11:59
#

а почему в ячейке карты линка на объект занимающий ячейку нет?
Чем больше объектов на карте будет, тем дороже будет их перебор в поисках например ближайших башен или выбора кого именно атаковать. Ерунда когда один два прохода, а если для полсотни юнитов, да не дай бог на каждый кадр? Дерево видимости строится будет? :)
Учитывая что путь наверняка по a-star будет считаться, что уже не быстро на флеше.

Так, просто мысли. У самого подобие дефендера застряло в разработке.

?горь
27 Октября 2010
— 18:06
#

Антон, спасибо, слежу за темой!!!
Совершенно не понимаю, зачем некоторые люди приводят свой код, как код проще или правильнее? Антон просто хочет показать варианты создания сетки, ведь некоторые возможно и не умели делать двумерные массивы, а теперь умеют. Даже хорошо, что нам показывают разносторонние подходы к решению задачи, мы же больше и узнаем. Так что я думаю, что правильнее просто следовать уроку, а не предлагать свои варианты, если считаете что вы правильнее, то делайте свои уроки и выкладывайте. Когда спрашивают, разъяснение кода, то это нормально, но когда, а бы сделал так.... ,или так.. а так меньше строчек.....зачем??? Остановитесь! Думаете, Антон не знает как сделать код проще? Он же пытается нам показать всё "на пальцах", для нас же старается.

Samana
27 Октября 2010
— 20:37
#

Samana, +
уже посты с кодом занимают больше места чем сама статья. Давайте не будем мерятся .... а просто посмотрим как делают другие!

?ван
27 Октября 2010
— 20:47
#

@Genm, как ты узнал, что производительность повысилась в 6 раз? Это можно как-то во флэше отследить или ты сам просчитал?

xiii
27 Октября 2010
— 20:54
#

ебята, сори за мой быдлокод…
Просто скучно одному изучать этот ас3.
Я смотрю на эти статьи, как на повод к размышлению, а не как шаблону для копирования. ? если нахожу, какое нибудь решение, то спешу поделиться с другими. Теми кто следит за статьями Антона, такими же новичками, как я. Самим же потом будет легче.

@xiii
Я просто в App.as вставил такой код:
trace(APP_VERSION);
var now:Date = new Date();
_game = new Game();
addChild(_game);
var now1:Date = new Date();
var now2:Date = new Date(now1.getTime()-now.getTime());
trace(now2.getTime());
и увеличил ширину поля:
public static const MAP_WIDTH_MAX:int = 200;

Genm
27 Октября 2010
— 21:07
#

Genm, я лично считаю статьи Антона не как способ изучить AS3, а скорее методы и подходы к написанию игр.

?ван
27 Октября 2010
— 21:13
#

@?горь, ссылки на объект в карте нет потому что и самих объектов как таковых пока нет. Когда будут объекты и если будет в этом необходимость, то обязательно все добавим :) Главное не забегать вперед раньше времени. Я отметил в статье, что сам до этого не писал товер дефенсов и что какие-то конкретные решения будут находится и появляться в ходе написания уроков.

Но на предмет заданного вопроса, забегая вперед, мысль к размышлению: врагов полсотни, а ячеек в карте 375 (25x15) — и вот думаю я, что врагов будет быстрее перебрать чем карту ;)

Поиск пути будет самый простой и от того не всегда идеальный результат, но зато достаточно «дешевый» — для игр подобного плана в самый раз.

Ant.Karlov
28 Октября 2010
— 02:09
#

@Samana, я лично не против того, чтобы обсуждать детали уроков и способы решения тех или иных задач. Мне кажется, что в ходе таких дискуссий каждый может узнать что-нибудь новое и интересное. Во всяком случае я как раз целюсь на то, чтобы не просто показать как можно делать и как нельзя, но и на то чтобы активировать мозг и предложить альтернативные варианты. Не обязательно публично, но все же :) Ведь самое главное не просто копипастить готовые уроки, но и разобраться что и зачем и только тогда от этого будет толк.

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

Ant.Karlov
28 Октября 2010
— 02:13
#

О towerdefence) Я такой на GM писал. Уже со всем этим сталкивался. ? изучать флеш начал с него, а теперь вот написал простою аркаду. Подожду еще пару уроков, и присоединюсь ко всем. В написании TD.

Антон, спасибо за уроки!

WeslomPo
28 Октября 2010
— 09:35
#

По поводу свободной расстановки башен. Ничего страшного в том, что башни будут блокировать путь врагу - нет.
Вражеские юниты, в такой ситуации (когда путь из начала в конец движения не может быть построен), могут скапливаться у стен или конкретных башен и ударами потихоньку разрушать их.
Чем дороже башня тем обиднее ее терять... ? баланс сил сохранится, если башни, не зависимо от их стоимости, будут ломаться одинаково быстро.

Buxom Berry
28 Октября 2010
— 18:18
#

@Buxom Berry советую тебе заценить праотца современных флеш дефенсов: Defense Grid. То что там сделано до сих пор копируется чуть ли ни в каждом дефенсе. Буквально на днях на конге увидел игру которая тоже оперирует data cores - т.е. слизано вообще в наглую.

@Ant.Karlov: ты таки снова вывел мой енджин на новый уровень! О чем речь?

public static var pGame в App дает мне доступ к pGame из любого класса проекта. А раньше мне приходилось ссылку на game таскать с собой везде где он мог бы понадобиться (придётся снова переписать весь двиг, но оно того стоит) Вобщем спасибо тебе!))

RaymondGames
28 Октября 2010
— 20:14
#

@RaymondGames разве объявление public static var pGame в классе App позволяет избавится от явного обращения вида App.pGame?

Ну, например, так:
class foo
{
...
public function bar():void
{
pGame.bar();
}
};

Buxom Berry
28 Октября 2010
— 21:27
#

@RaymondGames, если это действительно так, то не спеши пока переписывать свой движок чтобы время зря не терять. В следующем уроке будет продемонстрировано элегантное решение от избавления ссылок на единственные в приложении часто используемые классы ;)

Ant.Karlov
28 Октября 2010
— 21:48
#

@BuxomBerry позволяет не таскать ссылку на pGame через все конструкторы. Ты игрушку посмотрел которую я привел в пример? Если нет смотри. Там ответ на твой ответ про башни. Очень интересный и существенно модернизирует геймплей.

@Ant.Karlov уже переписал. Я ждать не могу - процесс идет)) Может потом еще раз перепишу, но пока всё устраивает.

RaymondGames
28 Октября 2010
— 22:39
#

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


private function makeDebugGrid():void{
//Массив для изображений
var BMData = new Vector.<BitmapData>(4, true);
//Создаем фон для карты
BMData[0] = new BitmapData(MAP_CELL_SIZE*MAP_WIDTH_MAX, MAP_CELL_SIZE*MAP_HEIGHT_MAX);
//Добавляем в библиотеку проекта 3 изображения или одно, которое потом режем. Прописываем классы (bmp1,...)
BMData[1] = new bmp1();
BMData[2] = new bmp3();
BMData[3] = new bmp2();
//Прямоугольник для копирования области из изображений
var rect:Rectangle = new Rectangle(0, 0, MAP_CELL_SIZE, MAP_CELL_SIZE);
// Двигаемся по высоте карты
for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++){
// Двигаемся по ширине карты
for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++){
//Рисуем карту
var pt:Point = new Point(ax * MAP_CELL_SIZE,ay * MAP_CELL_SIZE);
BMData[0].copyPixels(BMData[getCellState(ax, ay)], rect, pt);
}
}

// Рисуем карту на экране
var bm:Bitmap = new Bitmap(BMData[0]);
this.addChild(bm);
//Очищаем переменные
BMData = null;
rect = null;
pt = null;
}

Genm
28 Октября 2010
— 23:47
#

Отличный туториал!
В итоге получится "дебаг" практически так как и в моем тд :)
http://clip2net.com/clip/m53698/1288433230-clip-34kb.png

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

ps_coder (FlashRushGames)
30 Октября 2010
— 14:25
#

Спасибо! Очень познавательно.

Василий
6 Ноября 2010
— 14:48
#

Антон, в твоих исходниках при изменении размеров окошка .swf-файла (простом растягивании) сетка тоже растягивается (то есть ее верхний и нижний края всегда приклеены к верхнему и нижнему краю окошка).
А в моем случае просто появляются белые пространства со всех сторон, то есть сама графика не масштабируется. Подскажи, где можно это настроить (подозреваю, что проблема может решиться одной галочкой:)

dada
7 Ноября 2010
— 23:56
#

2 dada
Всё, уже разобрался сам:)

dada
8 Ноября 2010
— 00:01
#

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

Max
24 Ноября 2010
— 14:24
#

удаление детей бужет на время тормозить.
лучше делать

while (grid.numChildren)
grid.removeChildAt(0)
}

эта мелочь работает в десятки раз быстрее чем указанная в статье

mitien
26 Ноября 2010
— 18:44
#

@mitien, ну тормозить не будет конечно :) Но removeChildAt(0) действительно более лучший вариант. Забыл я про него когда писал пример. Спасибо!

Ant.Karlov
29 Ноября 2010
— 04:05
#

спасибо огромное за урок! как всегда очень познавательно и самое главное так вовремя ;)

coolsiu
3 Декабря 2010
— 14:39
#

?звините. а вот как из массива извлекать информацию и вставлять на карту? Вот например есть двухмерный массив, где 0-пусто,1-занято,3-только для стройки. мне интересно а сам разобраться не могу. и огромное спасибо за урок

никита
4 Января 2011
— 12:15
#

большое спасибо, вкуснейший материал для новичка!

di
8 Февраля 2011
— 19:29
#

"Добавим локальную переменную _mapMask:Array — это будет наша карта проходимости."

Насколько я понял, эту локальную переменную надо объявить в функции clearMapMask(), да?
Если да, то компилятор пишет следующее:

TypeError: Error #1010: Термин не определен и не имеет свойств.
at ru.towerdefence::Universe/clearMapMask()
at ru.towerdefence::Universe()
at ru.towerdefence::App()


Подскажите пожалуйста в чем я не прав.

Indigo
10 Марта 2011
— 16:21
#

@Indigo, локальная переменная — это значит, что для всего класса нужно объявить эту переменную. С нижнего подчеркивания начинаются все локальные/наследуемые переменные используемые только внутри класса.

Ant.Karlov
10 Марта 2011
— 17:33
#

Насчет поэтапного программирование - это не очень имхо хорошо. Потеря времени на модификации кода в дальнейшем.

pavezlo
11 Апреля 2011
— 23:09
#

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

Ant.Karlov
12 Апреля 2011
— 09:57
#

Антон, а зачем делают проверку на существование stage в конструкторе класса-владельца сцены?

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

public function App()
{
if (stage)
{
init(null);
}
else
{
addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
}
}

Вопрос возник из-за неожиданных сложностей с прелоадером.

Алексей
23 Апреля 2011
— 16:04
#

@Алексей:
Так делают потому, что stage это всего-лишь переменная класса DisplayObject, и в конструкторе она еще не инициализирована (не забывайте, что работаете с последовательными программами), а вообще говоря она может, и не только в конструкторе, принимать значение null если образец не был добавлен в display list.

Кстати, Stage в сцене всегда существует и притом только один, об этом повествует документация.

BuxomBerry
23 Апреля 2011
— 17:05
#

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

Ant.Karlov
24 Апреля 2011
— 21:29
#

>>Создаем новую папку в библиотеке нашего fla проекта с названием «Debug» и в папке этой создаем новый MovieClip с названием DebugCell_mc.

>А как это сделать? уже 2 день маюсь, стыдно признаться (:

Korsvian
8 Мая 2011
— 23:39
#

@Korsvian, в библиотеки клипов внизу есть маленькая кнопочка с иконкой папки — её нажимаем и создается новая папка.

Ant.Karlov
9 Мая 2011
— 00:07
#

@Ant.Karlov, это понятно) а MovieClip как создать?

Korsvian
9 Мая 2011
— 18:45
#

Ой, спасибо большое, не сразу допер, что именно в библиотеке, а не в Project`е.

Korsvian
10 Мая 2011
— 12:45
#

Спасибо! Продолжаем изучать)

D@eMonD
27 Мая 2011
— 03:56
#

var grid:Sprite = new Sprite(); // Контейнер для всех ячеек

>>>а что такое контейнер?

Создаем визуальную ячейку
cellSprite = new DebugCell_mc();
cellSprite.x = posX;
cellSprite.y = posY;
cellSprite.stop();

>>>а можно подробнее каждую строчку?

Паша
19 Августа 2011
— 00:49
#

Недавно взялся за ас3, и благодаря этому блогу сильно вырос энтузиазм, теперь все свободное время уделяю разборам уроков и экспериментам в языке. Спасибо за интересные уроки, и примеры !!!

Вот только возник вопрос, который вроде и по уроку, а в то же время вроде и не очень:
Сетка выводится так как и должна, только не 20х15, а 10х12, если скомпилированный ролик развернуть на весь экран, то сетка получается 16х15??? trace`ом проверил координаты ячеек и их кол-во - все норм. 20х15, то есть получается почему-то съехала координатная сетка и действительно, задав начальные координаты где-то х=256 y=256, сетка нормально отображается, как координатную сетку вернуть в свои нормальные 0х0 ???????????

DimtRill
23 Августа 2011
— 16:48
#

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

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

> как координатную сетку вернуть в свои нормальные 0х0 ?

Предположим у вас есть два клипа: clipPapa, clipMama и вы делаете следующее:

clipMama.x = 256;
clipPapa.addChild(clipMama);
clipPapa.x = 256;

Таким образом клип clipMama.x в глобальных кординатах будет равен 512. То есть если клип вложен в другой клип и другой клип сдвинут на N расстояние, то вложенный клип так же смещается согласно смещению своего родителя — я думаю это ваш случай, проверяйте иерархию клипов и ищите место где вы сместили какой-нибудь основной клип.

Ant.Karlov
24 Августа 2011
— 13:05
#

@Паша, контейнер — это такая штука как коробочка и в него как и в коробочку можно складывать другие штуки.

Подробно про каждую строчку:
cellSprite = new DebugCell_mc(); — создаем экземпляр класса DebugCell_mc() и сохраняем указатель в переменную cellSprite. DebugCell_mc — это MovieClip который мы нарисовали во Flash и экспортировали в код.

cellSprite.x = posX; — здесь как и здесь:
cellSprite.y = posY; — мы устанавливаем новое положение в двухмерной системе координат нашему экземпляру класса DebugCell_mc обращаясь к нему через указатель в переменной cellSprite.

cellSprite.stop(); — поскольку переменная cellSprite содержит в себе указатель на экземпляр обычного MovieClip то в этой строчки мы останавливаем его воспроизведение так как в нем может быть более одного кадра.

Ant.Karlov
24 Августа 2011
— 13:27
#

@pkasha писал:
Расскажите как прошла растеризация, очень не понятный момент(((

+100

Ivo_Bobul
25 Августа 2011
— 16:37
#

// Создаем новую растровую сетку
_debugGrid = new Bitmap(bmpData);
addChild(_debugGrid);


Перед _debugGrid желательно поставить var

Владимир
7 Ноября 2011
— 17:56
#

@Владимир, _debugGrid — это переменная класса, а не метода в котором она пересоздается. Переменные определенные внутри класса обычно начинаются с подчеркивания.

Ant.Karlov
7 Ноября 2011
— 21:14
#

Наверное я эту переменную не объявил вверху класса, поэтому когда присвоил ей объект класса без var у меня ошибка была.

Владимир
8 Ноября 2011
— 19:17
#

Может кто-нибудь в нескольких словах объяснить или дать ссылку с примером, как сделать гравитацию на такой карте с сеткой?!

Владимир
9 Ноября 2011
— 00:39
#

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

komarVadim
20 Ноября 2011
— 01:53
#

komarVadim
Скорее всего при создании в библиотеке мувиклипа состояния ячеек у вас точка отсчета (крестик такой если залезть в сам мувиклип) стоит аккуратно в центре вашей клеточки. Передвиньте квадратик так, чтобы крестик совпадал с левым верхним углом мувилипа. Короче говоря выровняйте ваш мувиклип по левому верхнему углу а не по центру сцены.
Так как цикл расставления сетки ячейками строится от нуля - то за ноль берется ваше нынешнее отцентрованное посередине мувиклипа положение - соответственно вся сетка смещается на полмувиклипа левее и выше.

Hudson
16 Декабря 2011
— 14:18
#

Здраствуйте, а застрял на первой задаче, вот ошибка:
The private attribute may be used only on class property definitions

На вот этом коде:
private function makeDebugGrid():void

Max
7 Января 2012
— 04:18
#

Оказывается я закрыл класс там где объявлялся класс класс
public class Universe extends Sprite
{

}

Я закрыл его не в конце, и конечно весь код ниже не пренадлижал ему..

Max
7 Января 2012
— 04:42
#

Только одно не понял, как работает этот код в Задаче 1:
grid.addChild(cellSprite) (этот вроде понятно, выводит наш квадрат) но зачем тогда ниже еще и этот код:
addChild(grid);

Пожалуйста Антон объясни ( я новичек)..

Max
7 Января 2012
— 04:49
#

Всё понял, кроме 4 задачи, вообще ничего не понимаю там, и как это новички могут понять? Не понимаю что такое BImapData, Matrix,Rectangle, вообще ничего...

Денис
9 Января 2012
— 17:24
#

@Денис, Может это потому, что урок рассчитан на тех кто знает ActionScript3? ? новичок - новичку рознь, вы наверно слишком новичковый новичок.

name
10 Января 2012
— 11:59
#

А где это изучить? Задачу 3 я елеле понял, экспериментируя с кодом, а вот с Задачой 4 никак не поэкспериментируешь ибо создается картинка.

Денис
10 Января 2012
— 14:45
#

изучить ActionScript 3 можно по книге Essential ActionScript 3.0 авт.Colin Moock

Относится к этому уроку нужно лишь как к подсказке, а не как к руководству.
4я задача решается очень просто, но вы не знаете как устроена работа с растровыми буферами (они же BitmapData) и потому вам это кажется сложным. Почитайте книжку Мука, легче станет.

name
10 Января 2012
— 15:14
#

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

Ant.Karlov
10 Января 2012
— 22:23
#

@name, Да, когда пойму это, мне вообще будет легко, раньше этот урок вообще казался сложным, но потом поэкспериментировавши с кодом понял какой код что делает. BitmapData - пиксели вроде. А var rect:Rectangle; это вроде как облать какая-то..

Денис
11 Января 2012
— 18:24
#

А можно очень важный момент объяснить?
после первой задачи сетка не появляется, возможно не понимаю что значит Добавляем вызов метода makeDebugGrid() в конструктор класса Universe.as
в App.as мы же ничего не трогаем? добавляются только константы и новая function в Universe, вроде. у меня идет так:
public function Universe() {
trace("Мир загружен...");
}
private function makeDebugGrid():void {
...

и после этого шага сетки нет, а компилятор пишет: 1180: Вызов предположительно неопределенного метода DebugCell_mc.
может у меня неправильно мувиклип привязан к классу? пытался и дефолтный оставлять и прописывать туда com.towerdefence.Universe - не помогает((

zergus
12 Января 2012
— 16:56
#

сорри за перепост, но заработало)
добавил в function Universe(){
trace("Мир загружен...");
[b]makeDebugGrid();{/b]
}

но почему-то кажется это криво написано.. я не прав?

zergus
12 Января 2012
— 17:12
#

Вы загружайте внутрь "класса", должны быть 2 скобки

Den
12 Января 2012
— 22:21
#

Зачем вообще нужны эти 2 строки:
var rect:Rectangle;

rect = grid.getRect(grid);

Не проще просто указать тут :
matrix.translate(0, 0);
0 0 ? Ведь так же получается

? зачем вообще стоит минут? (-rect.x, - rect.y);

Макс
17 Февраля 2012
— 22:38
#

Засел за уроки )
Спасибо автору огромное!

Нахожусь на заре создания своей первой игры-пазла, пока разбираюсь в коде, появилось два вопроса:

1. Я хочу сделать, чтобы ячейки начинались рисоваться не с нулевых координат, а с какой-то определенной точки отсчета - не смог найти, куда это можно вписать в коде )
Создать некий виртуальный (0,0) координат.

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

Kingo
26 Марта 2012
— 14:37
#

to Kingo
1)
Фукнция private function makeDebugGrid():void

var posX:int = 0; // Положение текущей ячейки по ширине
var posY:int = 0; // Положение текущей ячейки по всоте

Это и есть начальные координаты верхнего левого угла всей сетки ячеек.
Чтобы все корректно работало нужно еще подправить в конце код на нужное значение по X

// Меняем положение текущей ячейки по высоте
posY += MAP_CELL_SIZE;
[/b] posX = 0; [/b]

2)
В программировании индексация массивов начинается с нуля. ActionScript не исключение. Это стандартно. Нет особого смысла переходить на единицу, потому что меняется не только начало циклов но и условия выполнения циклов:
For (var i:int = 0; i < 4; i++) // Это начало с нуля
For (var i:int = 1; i <= 4; i++) // Это начало с единицы
Лучше привыкнуть к 0, так как это более распространенный вариант нумерации массивов.

FirstFlashGame
26 Марта 2012
— 16:36
#

FirstFlashGame, спасибо за ответ.

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

По оси Y ничего не меняется.
Не пойму, почему.
Ощущение, что что-то в другом коде как-то перебивает эти значения...

Про массивы уяснил )

Kingo
26 Марта 2012
— 16:55
#

to Kingo
Одновременно нужно поставить:

var posX:int = 100; // Положение текущей ячейки по ширине
var posY:int = 200; // Положение текущей ячейки по всоте

// Меняем положение текущей ячейки по высоте
posY += MAP_CELL_SIZE;
posX = 100;

FirstFlashGame
26 Марта 2012
— 17:10
#

Пробовал, не выходит каменный цветок )

Реагирует только либо на изменение нижнего posX - сдвигает по X все ряды, кроме верхнего.

Либо только один верхний, если увеличить только var posX:int

Если их вместе изменить, то никакого эффекта нет! - всё как с нулями.

По Y вообще не реагирует!

Скачал прилагаемые тут исходники - поведение то же самое.

Добавил внутрь циклов trace (posX, posY) - координаты выдаются правильные, как и должны быть со смещением. Но отрисовка мувиков им несоответствует!

Мне нужно сместить все поле на два пикселя по X и по Y - чтобы первый мувик рисовался в (2,2).

Kingo
26 Марта 2012
— 20:14
#

@Kingo, думал без меня тут разберетесь, но похоже не получается :) FirstFlashGame все правильно посоветовал, но закавыка в том, что созданная сетка из клипов потом преобразуется в растровое изображение и на сцену добавляется уже растровая картинка. Чтобы сдвинуть сетку вам следует задать необходимое смещение, например здесь:

matrix.translate(-rect.x + 100, -rect.y + 100);

Но тут следует задавать смещение если вам нужно нарисовать сетку со смещением непосредственно в битмапедате. Но я уверен что для ваших целей подойдет способ и по проще:

// Создаем новую растровую сетку
_debugGrid = new Bitmap(bmpData);
_debugGrid.x = 100; // Смещение сетки по ширине
_debugGrid.y = 200; // Смещение сетки по высоте
addChild(_debugGrid);

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

Ant.Karlov
26 Марта 2012
— 21:58
#

@Kingo, по поводу индексов массивов: настоятельно рекомендую вам привыкнуть к тому что индексы массивов начинаются с нуля, таким образом вы с экономите себе в будущем кучу времени и нервов :) ?ндексы массивов в программировании начинаются с нуля абсолютно везде (во всех языках программирования).

Ant.Karlov
26 Марта 2012
— 22:01
#

Антон, спасибо, работает! )
Я ещё не разбирался с растеризацией, битмапами - поэтому эти части кода мне пока не очень ясны. Планирую изучить.

Вопрос тогда такой: если я буду менять координаты только растра, а координаты исходных клипов будут начинаться с (0,0) - то как мне лучше действовать в дальнейшем - при отрисовке объектов в клетках? Какие из координат использовать - "нулевые" клипов или "приведенные" растра?

? второй вопрос: если сетка растрируется при выводе, почему же тогда она все-таки смещается (но не так, как надо), если изменить posX на этапе создания сетки из мувиклипов?

А пространство да - нужно для рамки и панели инструментов.
Поскольку не могу подогнать подходящие мне по количеству и величине ячейки точно по размерам экрана - остаются пустые поля.

Kingo
26 Марта 2012
— 23:18
#

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

В будущем, чтобы у вас небыло проблем с координатами и все как и надо имело бы свои координаты независимо от того смещено оно или нет — нужно заключать в контейнер, например, в нашем случае основным игровым контейнером сейчас является Universe внутри которого происходят все баталии. ? если вам действительно нужно освобождать пространство на экране под интерфейс, то следует сместить удобным образом непосредственно Universe, а не то что внутри него. ?гровой мир в Universe как в коробке, и вы можете сколько угодно двигать эту коробку (спрайт Universe) и при этом внутри него все будет работать правильно. В общем если вам нужно сдвинуть все, то не нужно двигать каждый объект по отдельности, двигайте всю коробку целиком! :)

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

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

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

Ant.Karlov
27 Марта 2012
— 01:12
#

Ок, спасибо.
Дописал в [/]App.as[/b]:
_universe.x=3; // Смещение игрового поля по X (панель справа)
_universe.y=3; // Смещение игрового поля по Y


А, и вспомнил ещё один нюанс: планирую все объекты делать изначально растровыми - я так понимаю, для таких случаев их ещё раз растрировать не нужно? )
Только в контейнер единый группировать?

Kingo
27 Марта 2012
— 13:47
#

Привет помогите пожалусто..Вы писали что не работаете с FlashDevelop. Но я учусь на нем, и застопорился на загрузке спрайта...

cellSprite = new DebugCell_mc();

как это изменить, ибо мовеклипов нету в FlashDevelop

Dgon
1 Апреля 2012
— 12:43
#

to Dgon

Смотрите абзац "Задача 1"

DebugCell_mc - так называется мувиклип, лежащий в середине FLA файла. Он являет собой набор кадров с прямоугольниками разных цветов. Отредактировать его можно с помощью Flash IDE. Ваш FlashDevelop узнает об этом классе прочитав FLA файл. У Вас должен быть FLA файл, а в внутри клип с названием DebugCell_mc, в свойствах которого проставлена галочка "Export for actionscript"

FirstFlashGame
1 Апреля 2012
— 18:30
#

FirstFlashGame извеняюсь за наивность, но только начал изучение.. Подскажите как этот самый FLA файл создать и как создать в нем DebugCell_mc.

Dgon
1 Апреля 2012
— 19:05
#

to Dgon

Лучше начать с начала:
http://www.ant-karlov.ru/pishem-igru-vstuplenie.html
Загрузить Flash CS5

http://www.ant-karlov.ru/towerdefence-1-struktura-igri.html
Продолжить делать все по шагам – создать свой проект и вперед! Антон Карлов все детально описал. Вы немножечко забежали вперед, начав со второго урока.

Но если Вы не ищите легких путей – достаточно кликнуть на «Ссылка на исходники» в конце урока и скачать архив с проектом.

FirstFlashGame
1 Апреля 2012
— 19:48
#

to FirstFlashGame

дык дело в том, что я не на Flash CS5 работаю, а на FlashDevelop и когда вопрос задавал, просил описать алтернативу под него.

Dgon
2 Апреля 2012
— 09:09
#

to Dgon
Вбейте этот запрос в google:
"Компилируйте код в Flash Develop, а графику в Flash IDE" и получите ответ.
FD - это редактор для кода
Flash IDE - это редактор для кода (Вам не нужно) + редактор для графики (именно поэтому Вам нужен CS5).
? для начала, возможно, стоит освоить именно работу исключительно с Flash IDE, а потому уже выбирать редактор себе по душе, будь-то FlashDevelop, FlashBuilder...

FirstFlashGame
2 Апреля 2012
— 11:21
#

Плиз скажите куда писать функцию makeDebugGrid() и откуда ее вызывать,а то я написал ее в Universe.as запихал и вызывал как и 2 другие функции но там нефига неполучилось

макс
29 Сентября 2012
— 01:00
#

у меня сетка создолась но только с левого края что нетак может быть?

HHHack
29 Сентября 2012
— 03:50
#

У меня стоит Adobe Flash CS6, проблема в том что код выдает ошибку на addChild(grid); и поэтому у меня не получается результат в конце, кто знает как исправить эту проблему?

Дмитрий
9 Января 2013
— 15:46
#

@Дмитрий, вероятнее всего проблема не в CS6, а в ошибке которую вы могли допустить в коде. Напишите текст ошибки которая возникает, тогда можно будет попробовать прояснить ситуацию.

Ant.Karlov
10 Января 2013
— 03:11
#

Я перепроверил код,(с вашим ) все оказалось правильно, но ошибок стало гораздо больше

comdefenesWorld.as, Line 5 1084: Syntax error: expecting identifier before public.
comdefenesWorld.as, Line 5 1086: Syntax error: expecting semicolon before extends.
comdefenesWorld.as, Line 7 1012: The static attribute may be used only on definitions inside a class.
comdefenesWorld.as, Line8 1012: The static attribute may be used only on definitions inside a class.

comdefenesWorld.as, Line 6 1012: The static attribute may be used only on definitions inside a class.
comdefenesWorld.as, Line 15 1013: The private attribute may be used only on class property definitions.
comdefenesWorld.as, Line 62 1084: Syntax error: expecting rightbrace before end of program.

Дмитрий
10 Января 2013
— 22:43
#

@Дмитрий, сколько бы ошибок не выводилось, всегда надо фокусироваться на первой ошибке так как она своим присутствием может порождать все остальные проблемы, что собственно в вашем случае и получилось.

В данном конкретном случае у вас проблема с синтаксисом: скорее всего закрывающая фигурная или обычная скобочка отсутствует или присутствует лишняя, а может быть и что-то другое что не дает интерпретатору разобраться в вашем коде. Внимательно изучайте строки от 1 до 15 — проблема где-то там.

Ant.Karlov
11 Января 2013
— 00:47
#

Здравствуйте, спасибо большое за уроки. Только я сделал первую задачу, а у меня запускается то, что у меня запускалось в конце первого урока. Надо где-то подключить класс universe.as? Подскажите, пожалуйста, что делать.

brodsky
19 Января 2013
— 10:33
#

@brodsky, да, вероятнее всего вы забыли создать экземпляр класса Universe в классе Game. Скачайте исходники урока и сверьтесь со своим проектом.

Ant.Karlov
19 Января 2013
— 23:29
#

Спасибо. Я подключил, а в классе Universe.as у меня было записано вот что:

public static const MAP_WIDTH_MAX:int = 20;
public static const MAP_HEIGHT_MAX:int = 15;
public static const MAP_CELL_SIZE:int = 32;
private function makeDebugGrid():void
{
import flash.display.MovieClip;
var grid:Sprite = new Sprite(); // Контейнер для всех ячеек
var cellSprite:MovieClip; // Текущая ячейка
var posX:int = 0; // Положение текущей ячейки по ширине
var posY:int = 0; // Положение текущей ячейки по высоте
// Двигаемся по высоте карты
for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
{
// Двигаемся по ширине карты
for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
{
// Создаем визуальную ячейку
cellSprite = new DebugCell_mc();
cellSprite.x = posX;
cellSprite.y = posY;
cellSprite.stop();
// Добавляем ячейку в контейнер
grid.addChild(cellSprite);
// Меняем положение текущей ячейки по ширине
posX += MAP_CELL_SIZE;
}
// Меняем положение текущей ячейки по высоте
posY += MAP_CELL_SIZE;
posX = 0;
}
addChild(grid);
}

А он стал выдавать ошибки:

Universe.as, Line 1 1114: The public attribute can only be used inside a package.
Universe.as, Line 1 1012: The static attribute may be used only on definitions inside a class.
Universe.as, Line 2 1114: The public attribute can only be used inside a package.
Universe.as, Line 2 1012: The static attribute may be used only on definitions inside a class.
Universe.as, Line 3 1114: The public attribute can only be used inside a package.
Universe.as, Line 3 1012: The static attribute may be used only on definitions inside a class.
C:Documents and SettingsтсдМои документыильяEliAdobe Flash Professional CS5.5TowergamecomtowerdefenceUniverse.as, Line 4 1013: The private attribute may be used only on class property definitions.

Подскажите, пожалуйста, что делать.

brodsky
20 Января 2013
— 11:54
#

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

В вашем случае содержимое класса не обернуто в package (ну и судя по всему описание класса отсутствует) и компилятору не понятно в какое пространство помещать public и static константы — константы не могут быть публичными или статичными вне пакета.

Скачайте исходники примера и посмотрите каким образом оформлен класс Universe.as, а так же любой другой класс из примера.

Ant.Karlov
20 Января 2013
— 16:31
#

Я догадался, как устранить часть ошибок. Теперь у меня вылазят такие ошибки:
Universe.as, Line 3 1012: The static attribute may be used only on definitions inside a class.
Universe.as, Line 4 1012: The static attribute may be used only on definitions inside a class.
Universe.as, Line 5 1012: The static attribute may be used only on definitions inside a class.
Universe.as, Line 6 1013: The private attribute may be used only on class property definitions.
Можете помочь их исправить?

brodsky
20 Января 2013
— 16:34
#

Хорошо, посмотрю. Спасибо.

brodsky
20 Января 2013
— 16:35
#

Это для as3? у меня нету в других панелях проекта

Дмитрий
21 Января 2013
— 20:16
#

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

Богдан
1 Февраля 2013
— 17:49
#

@Богдан, что вы подразумеваете под «меняю размеры окна»? Вы изменяете размеры окна в коде или пытаетесь растягивать окно мышкой? Если второе — то это вероятно из-за битмапа в который производится растеризация, так как изменения размеров битмапа без сглаживания могут приводить к потери рядов пикселей.

В идеале конечно бы увидеть скриншот проблемы чтобы не гадать о сути проблемы.

Ant.Karlov
1 Февраля 2013
— 22:02
#

Да, я имел в виду второе. Немного погуглив на тему сглаживания и отыскав информацию о свойстве smoothing, я попробовал изменить код, где создается сетка, на такой :
_debugGrid = new Bitmap (bmpData, "auto", true);
Теперь границы не пропадают, но имеют разную толщину(при растягивании/сжимании окна мышкой).
Ну это я так, может нубам, вроде меня будет интересно)

Богдан
2 Февраля 2013
— 17:14
#

Совсем нубский вопрос: как нарисовать квадрат 32х32 в клипе DebugCell_mc?

Руслан
5 Марта 2013
— 22:18
#

@Руслан, не очень понятно, что вы подразумевали под "нарисовать квадрат"?

Но если вам необходимо изменить содержимое DebugCell_mc — то необходимо открыть файл TowerDefence.fla и найти этот клип в библиотеке клипов, далее пользуемся инструментами из панели рисования слева.

Ant.Karlov
5 Марта 2013
— 22:38
#

Уже разобрался. Меня интересовало то, как именно задать размер квадрата в пикселях. Теперь не совсем понятна часть кода, добавленная в метод makeDebugGrid. Зачем нужна матрица и что значат эти строки:
rect = grid.getRect(grid);
matrix.translate(-rect.x, -rect.y);
bmpData.draw(grid, matrix);

Руслан
6 Марта 2013
— 21:27
#

@Руслан,

> rect = grid.getRect(grid);
matrix.translate(-rect.x, -rect.y);
bmpData.draw(grid, matrix);


Это преобразование координат с учетом смещения, то есть смещаем точку регистрации с нулевых координат до -rect.x и y. Если честно, то я уже точно не помню всех деталей и в частности есть ли необходимость в этом приобразовании :) Вы можете по экспериментировать с этим самостоятельно, а так же можете почитать документацию для большей ясности по методу draw().

Ant.Karlov
11 Марта 2013
— 15:30
#

К сожалению, застопорился на первом задании- после компиляции не появляется поле из квадратиков, а появляется один квадрат, который безостановочно мерцает (переходит по кадрам с 1 на 2).

Anatrius
25 Апреля 2013
— 05:04
#

Тысяча извинений- редактор изменил в одном из свойств букву- и все полетело наперекосяк..

Anatrius
25 Апреля 2013
— 05:08
#

Добрий день у меня к вам вопрос))
из-за етой строки:
cellSprite = new DebugMapa_mc();

компилятор викидает такую ошибку:
1180: Вызов предположительно неопределенного метода DebugMapa_mc.

как мне с етой ошибкой справиться?)

п. с.)
Спасибо за виложений урок))) п. с.)

Виталий
11 Июля 2013
— 17:14
#

тут она , строка DebugСell_mc();

Виталий
11 Июля 2013
— 17:15
#

@Ant. Karlov, вот я после прочтения первых ваших статей захотел сам попробовать создать свою игру. Выбрал простую казуалку по типу три в линию. ? я пришёл к вопросу - нужна ли мне здесь отладочная сетка и зачем? Нужна ли карта проходимости? ? зачем вы её использовали?

SuriTheAngel
12 Января 2014
— 01:50
#

@SuriTheAngel,

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

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

3. Карта проходимости используется для определения проходимых и не проходимых клеточек. То есть карта проходимости определяет клетки куда могут быть помещены игровые фишки, а куда нет.

Ant.Karlov
12 Января 2014
— 12:00
#

@Ant.Karlov, спасибо за ответ. Теперь стало намного понятней, что, где и зачем.

SuriTheAngel
13 Января 2014
— 02:12
#

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

Mica
14 Января 2014
— 02:24
#

У меня та же проблема ^ ^

TOM
16 Марта 2014
— 22:33
#

разобрался... для тех кто застрял с первой задачей:

в Universe.as пишем конструктор и вызываем MakeDebugGrid....

ТОМ
17 Марта 2014
— 17:24
#

http://pixs.ru/showimage/Snimokekra_8409297_13955211.png

На скриншоте сетка из примера и прямоугольник. Почему-то сетка съехала влево-вверх. Никак не разберусь что я сделал не так.

CS6

kozlovski
23 Сентября 2014
— 18:05
#

Разобрался - нарисовал прямоугольники левее центра.

kozlovski
23 Сентября 2014
— 18:21
#

Привет! ? снова вопрос по 1 задаче. Нет сетки, мигают квадраты. Растолкуйте, как добавить вызов метода makeDebugGrid() в конструктор класса Universe.as? Думаю, может здесь что-то не то. ?ли вот о каком конструкторе говорит ТОМ?

Alex-Sline
6 Ноября 2014
— 08:53
#

ReferenceError: Error #1065: Переменная DebugCell_mc не определена.

вот такую ошибку выдает.

Alex-Sline
17 Ноября 2014
— 09:47
#

Разобрался. Я создавал не мувик, а новый файл fla , поэтмоу переменной не было. Может кому в будущем поможет, что мувик можно создать, нажав F8:);)

Alex-Sline
17 Ноября 2014
— 11:06
#

В CS 6 написал код задачи 1, при компиляции никаких ошибок не выдает но на экране ничего не появляется, то же самое при компиляции исходников с сайта

Sectronik
17 Ноября 2014
— 14:00
#

@Sectronic, там еще мувик создать нужно;)

Alex-Sline
1 Декабря 2014
— 12:58
#

почему

igor
13 Февраля 2015
— 09:34
#

оо. фф рулит. а в эксплорере не хочет принимать код проверки на спам.. так вот:

Значения констант будут соответствовать номеру кадра в клипе DebugCell_mc:
1 public static const STATE_CELL_FREE:int = 1;
2 public static const STATE_CELL_BUSY:int = 2;

вопрос: откуда константы знают что 1 и 2 - это номера кадров в клипе, а не обычные целые цисла. где указана эта связь

igor
13 Февраля 2015
— 09:38
#

вообщем ясно. через gotoAndStop идёт на 1 и 2 кадры. блин прочитал 2 книги, но научиться мыслить как программер -это похоже отдельный вопрос... вот как такое может всё в голову приходить. ну ладно ТД описан 100раз, но другие, а свои?!.. блин это сколько практики отсидеть надо.. чтоб всё так всплывало и упорядочивалось. ну может декомпилы для начала, но не всю жизнь же. как научиться мыслить как программист!? вопрос риторический. пора опять писать видать. опять бросать, читать, и писать же ну.. хочу быть свободным фрилансером, продающим свои программы! ну или игры, если уж совсем деньги понадобятся

igor
24 Февраля 2015
— 14:34
#

Спасибо, Антон!

Bella
11 Июня 2015
— 12:34
#

Сделал все как написанно, и ни один MoveClip не нарисован, все перепроверил 1000 раз. Бьюсь головой об стену. Даже заного пересоздавал,
Хоть богу молись не отрисовывается и ве. Ошибок компиляции нет, программ Adobe CS3

Николай
11 Марта 2016
— 08:19
#