Статьи

AS3 101: массивы — Basix

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

Массивы — это способы хранения списков значений. Массивы полезны для хранения произвольного числа связанных элементов, сгруппированных вместе, и могут представлять сложные структуры данных через вложение. Как мы увидим, в ActionScript массивы обладают достаточной гибкостью и функциональностью. После обычного абстрактного введения мы применим то, чему научились (и еще немного изучим в процессе), создав довольно простую игру-головоломку.

Немного больно начинать с того, что «Новый Оксфордский американский словарь определяет массив как …», но я все равно собираюсь это сделать.

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

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

Мое любимое непрограммное использование массива слов — Очень Большой Массив, набор из 27 антенн, которые образуют радиообсерваторию в Нью-Мексико. Если вы видели фильм «Контакт», вы знаете, что это такое. С официального сайта VLA :

VLA — это интерферометр; это означает, что он работает путем умножения данных из каждой пары телескопов вместе, чтобы сформировать шаблоны помех. Структура этих интерференционных паттернов и то, как они изменяются со временем при вращении Земли, отражают структуру радиоисточников на небе: мы можем взять эти паттерны и использовать математический метод, называемый преобразованием Фурье, для создания карт.

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

Изображение предоставлено NRAO / AUI

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

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

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

1
var ary:Array = new Array();

Мы просто создаем переменную с именем ary типа Array и присваиваем ей значение нового пустого массива. Новое ключевое слово в ActionScript — это то, что вы увидите много; как вы можете сделать вывод, это создает нечто стоящее отдельно или независимо от того, что следует за ним. В этом случае мы следуем за ним с типом Array , поэтому мы получаем новый Array.

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

Другой основной вид создания Array — квадратная скобка, также известная как литерал массива . По сути, это просто ярлык к тому, что мы сделали на последнем шаге. Это выглядит так (если хотите, откройте новый документ AS3 Flash, а затем откройте панель «Действия» и следуйте инструкциям):

1
var ary:Array = [];

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

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

1
var ary:Array = new Array(«one», «two», «three»);

Чтобы сделать то же самое, используя литерал Array:

1
var ary:Array = [«one», «two», «three»];

Обратите внимание, что количество элементов не имеет значения, вам просто нужно разделить их запятыми. И тип не имеет значения, либо. Мы использовали строки. Вы можете использовать Numbers, MovieClips и даже другие массивы (мы вернемся к этому в несколько шагов). Они не должны быть одного типа, либо. Следующее совершенно верно:

1
var ary:Array = [«one», 2, new MovieClip()];

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

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

1
var ary:Array = new Array(10);

вы получите массив из десяти предметов со всеми элементами, установленными на ноль . Однако, если вы напишите:

1
var ary:Array = [10];

вы получите массив из одного элемента, и этот элемент будет номером 10.

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

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

1
trace(ary);

Вы увидите что-то вроде:

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

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

Но прежде чем идти дальше, мы должны убедиться, что ясно, что ActionScript является языком с нулями , а это означает, что первый элемент в массиве имеет индекс 0 (ноль) . Второй элемент имеет индекс 1. Последний элемент в массиве будет иметь индекс числа элементов в массиве минус 1 (подумайте об этом … это будет иметь смысл, если сначала этого не было ).

Распространенная ошибка — думать о массивах как об одном , особенно когда вы начинаете свою карьеру программиста, что приводит к тому, что называется ошибкой « один за другим» . Этот вид логической ошибки может быть трудно отследить, но когда вы начинаете видеть такие вещи, как результаты «один-слишком-много» или «один-слишком мало», убедитесь, что вы начали с 0.

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

1
2
var ary:Array = [«one», «two», «three»];
ary[3] = «four»;

После создания массива с тремя строками во второй строке указывается, что массив добавляет еще одну строку в четвертую позицию (то есть позицию с индексом 3). Использование квадратных скобок сразу после ссылки на массив позволяет получить доступ к отдельной позиции в массиве, которая определяется числовым значением в квадратных скобках. Это называется нотацией доступа к массиву .

Давайте посмотрим, что это сделало с массивом:

1
trace(ary);

Запустите программу, и вы должны увидеть это на панели «Вывод»:

Теперь вы можете видеть, что в вашем массиве есть четыре элемента.

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

1
ary.push(«five»);

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

снова проследите, и теперь вы увидите, что в нем пять элементов.

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

1
2
ary.push(«six», «seven», «eight»);
trace(ary)

Это может быть неочевидно на панели «Вывод», но вы добавили в список три отдельных элемента, увеличив его длину с 6 до 9. Вы можете добавить столько элементов, сколько захотите, в методе push.

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

1
ary.unshift(«zero»);

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

Идите и проследите массив снова, чтобы увидеть это в действии.

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

Нотация доступа к массиву может использоваться для вывода данных так же легко, как и для ввода. Предполагая, что у нас все еще есть переменная «ary», и нам доступно 9 элементов, от «нуля» до «восьми». Следующее:

1
trace(ary[4]);

приведет к:

Вы можете думать, что нотация доступа к массиву очень похожа на одну переменную, только вместо 9 переменных с этими строками у нас есть одна переменная («ary»), которая способна содержать 9 значений сама по себе, и использовать любую из них в качестве переменной, мы используем квадратные скобки.

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

1
2
3
trace(ary);
trace(ary.pop());
trace(ary);

Вы увидите что-то подобное на панели «Вывод»:

В первой строке мы видим текущий массив из 9 элементов.

Затем во второй строке мы выталкиваем последний элемент из конца массива. Мы можем проследить это значение и увидеть, что это на самом деле значение, которое было в конце.

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

сдвиг , чтобы вытолкнуть, что unshift , чтобы подтолкнуть Он удаляет первый элемент и возвращает его, сдвигая оставшиеся элементы на один индекс вниз, чтобы закрыть пробел. Мы можем попробовать подобное упражнение, как мы делали с pop :

1
2
3
trace(ary);
trace(ary.shift());
trace(ary);

Надеемся, что результаты будут ясны на данный момент.

Очень часто хочется знать, сколько элементов в массиве. Мы можем спросить сам массив для его длины . Например:

1
trace(ary.length);

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

Важно помнить, что хотя индексы массива начинаются с нуля, длина фактически равна единице. Это не противоречит; это констатация факта. Мы спрашиваем массив: «Сколько у вас предметов?» не «Каков индекс вашего последнего элемента?» Даже если бы массивы были основаны на 42, до тех пор, пока в массиве было 7 элементов (индексы с 42 по 48), длина все равно составляла бы 7. Это часто вызывает путаницу среди новичков в массивах, поэтому постарайтесь запомнить тонкое различие между нулевым индексом последнего элемента и длиной массива.

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

1
2
3
var ary:Array = [];
ary[42] = «Forty-two»;
trace(ary.length);

Даст «43», хотя 42 из этих 43 предметов не определены. Но мы определили элемент на 42, неявно создавая пустые слоты во всем, вплоть до этого индекса, таким образом увеличивая длину. В компьютерных науках это иллюстрирует разреженный массив , означающий, что массивы ActionScript не требуют, чтобы все значения были «плотно упакованы» без пустых значений, занимающих место. Некоторые языки имеют плотные массивы, которые являются только этим, но не ActionScript.

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

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

1
trace(ary.join(«\n»));

Дайте этому шанс и посмотрите, что произойдет.

Тангенциально связанным является метод String дополнения, split . Взятие строки, разбиение ее на массив, а затем соединение обратно в строку — это быстрый и грязный способ выполнить простой поиск и замену:

1
2
var text:String = «The rain in Spain falls mainly on the plain.»;
trace(text.split(«ain»).join(«AIN»));

Обратный делает только то, что вы думаете, он будет делать. Идите и попробуйте:

1
2
3
trace(ary);
ary.reverse();
trace(ary);

Надеюсь, результаты не слишком вас удивили.

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

1
2
3
trace(ary);
ary.sort();
trace(ary);

Но метод сортировки можно сказать, как и на что сортировать. См. Документы для полного описания, но, чтобы подвести итог, можно отсортировать по номерам (по умолчанию числа сортируются по алфавиту, так что «10» предшествует «9»), без учета регистра (по умолчанию «Zoo» предшествует «abacus») ), по возрастанию или по убыванию, а также с другими вариантами.

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

splice — это метод Array, который позволяет вам удалять и / или вставлять элементы из / в Array. Это довольно сложный метод, позволяющий удалять и вставлять одновременно. Давайте начнем с этого основного массива:

1
var ary:Array = [«one», «two», «three», «four», «five», «six»];

Чтобы удалить элемент из массива, нам нужно использовать сплайс с двумя параметрами: первый — это индекс, с которого мы хотим начать операцию, а второй — количество элементов, удаляемых из начальной точки. Таким образом, чтобы удалить элемент «четыре» (с индексом 3), мы написали бы:

1
ary.splice(3, 1);

Мы также можем использовать сращивание, чтобы вставить элемент. Чтобы вставить элемент «четыре точки два» между элементами «четыре» и «пять», мы можем сделать следующее

1
ary.splice(4, 0, «three-point-two»);

Это начинается с пункта 4 (значение «пять»), ничего не удаляет (для этого и нужен 0), а затем вставляет данную строку, толкая все на единицу, чтобы освободить место.

indexOf — это простой метод, который дает вам индекс указанного элемента. Например:

1
2
var ary:Array = [«one», «two», «three»];
trace(ary.indexOf(«three»));

Это дает вам число (или, в частности, int) 2. Это индекс первого элемента, соответствующего заданному значению. Это быстрый способ поиска в массиве известного значения или даже для проверки, содержит ли массив значение, сравнивая результат indexOf, чтобы увидеть, больше ли он -1 (indexOf возвращает -1, если значение не найдено в массиве).

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

Обратите внимание, что в «традиционном» программировании массивы имеют тенденцию быть неизменными . То есть, если они были созданы с определенным набором значений, их нельзя (легко) изменить. Причины этого связаны с выделением памяти и темами более низкого уровня, с которыми вы сталкиваетесь в таких языках, как C. Но в ActionScript и большинстве языков сценариев большая часть этого абстрагирована, и у нас остаются изменяемые массивы или массивы. это можно изменить по желанию. При вызове реверса или сплайсинга происходит довольно много проблем, но не спрашивайте, просто радуйтесь, что вы сэкономили кучу времени.

Элементы массива могут быть любого типа. Массивы могут фактически содержать другие массивы. Эти «массивы массивов» называются вложенными массивами или многомерными массивами . Затем их можно использовать для хранения сложных структур данных. Наиболее распространенным из них является двумерный массив, представляющий собой отдельный массив, в котором каждый элемент представляет собой другой массив. Каждый из этих «подмассивов» содержит более примитивное значение.

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

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

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

Мы можем построить такой массив постепенно:

01
02
03
04
05
06
07
08
09
10
11
12
13
var spreadsheet:Array = [];
spreadsheet[0] = [];
spreadsheet[0][0] = «row 0, col 0»;
spreadsheet[0][1] = «row 0, col 1»;
spreadsheet[0][2] = «row 0, col 2»;
spreadsheet[1] = [];
spreadsheet[1][0] = «row 1, col 0»;
spreadsheet[1][1] = «row 1, col 1»;
spreadsheet[1][2] = «row 1, col 2»;
spreadsheet[2] = [];
spreadsheet[2][0] = «row 2, col 0»;
spreadsheet[2][1] = «row 2, col 1»;
spreadsheet[2][2] = «row 2, col 2»;

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

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

1
2
3
4
var spreadsheet:Array = [];
spreadsheet[0][0] = «row 0, col 0»;
spreadsheet[0][1] = «row 0, col 1»;
spreadsheet[0][2] = «row 0, col 2»;

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

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

1
2
3
4
5
var spreadsheet:Array = [
    [«row 0, col 0», «row 0, col 1», «row 0, col 2»],
    [«row 1, col 0», «row 1, col 1», «row 1, col 2»],
    [«row 2, col 0», «row 2, col 1», «row 2, col 2»]
];

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

Для дальнейшего чтения о многомерных массивах, в статье Википедии о массивах есть раздел о многомерных массивах , а для математического обоснования посетите статью о матрицах для некоторого легкого чтения. ActionScript 3 с использованием матриц в объекте flash.geom.Matrix .

Вектор — это новый тип данных, представленный в Flash Player 10, который практически идентичен массиву. Основное отличие состоит в том, что вектор настроен так, что он может содержать только определенный тип значения. Например, если вы отслеживаете позиции, вам может потребоваться вектор, содержащий только цифры. Конечно, и массив может содержать только числа, но вектор позволяет вам применять его. Лучший способ проиллюстрировать это на примере.

Мы создадим массив, и он будет содержать кучу строк.

1
2
3
4
var ary:Array = new Array();
ary.push(«one»);
ary.push(«two»);
ary.push(«three»);

Теперь мы получим второй элемент.

1
var item:String = ary[1];
1
trace(item.toUpperCase());

Все идет нормально. Однако обратите внимание на тип данных в переменной «item». Это действительно только потому, что я знаю, что я положил ничего, кроме Strings в массив. Что произойдет, если я облажался и вместо этого вставил int?

1
2
3
4
5
6
var ary:Array = new Array();
ary.push(«one»);
ary.push(new Sprite());
ary.push(«three»);
var item:String = ary[1];
trace(item.toUpperCase());

Попытка вывести ary [1] и поместить его в строковую переменную приведет к проблемам. Может быть ошибка во время выполнения, но вы не получите предупреждение компилятора об этом. Идите вперед и опубликуйте вышеуказанный скрипт. SWF будет скомпилирован без ошибок, но результат будет не таким, как вы ожидаете:

Объект Sprite превращается в строку при извлечении из массива.

Вектор, однако, гарантированно содержит только один тип объекта. Настройка выглядит немного странно по сравнению с остальной частью ActionScript:

1
var vec:Vector.<Sprite> = new Vector.<Sprite>();

Это создаст новый вектор, который ожидает только строки. Dont и угловые скобки — только синтаксис для этого. Полный тип данных — «Вектор. <Sprite>», где «Sprite» технически может быть любым другим типом данных, который вы хотите сохранить.

Теперь, перейдя к настройке данных, мы можем попробовать это:

1
2
vec.push(new Sprite());
vec.push(42);

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

Если мы исправим это, но затем попытаемся извлечь из него данные следующим образом:

1
var item:int = vec[1];

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

Зачем использовать векторы над массивами? Основная причина — безопасность типов. Большую часть времени у вас есть только один тип данных в вашем массиве, так что вы можете также сделать его официальным. Кроме того, векторы должны быть быстрее массивов, особенно для больших векторов, содержащих примитивные типы, такие как числа. Мне кажется, что вы не можете ошибиться с вектором, поэтому если вы используете Flash Player 10, используйте векторы. Однако если вы беспокоитесь о совместимости с Flash 9, вам следует использовать массивы (например, в библиотеке кода, которая иначе не зависит от функций Flash 10). Поскольку FlashTuts + не готов заблокировать Flash 9, в оставшейся части этого урока мы будем использовать массивы.

Пришло время начать вкладывать всю эту учебу в уличную мудрость. Мы собираемся сделать простую игру-головоломку, которой будет невозможно управлять без помощи массивов. Вы, наверное, видели этот тип головоломки раньше. Это сетка кнопок, каждая из которых включена или выключена. Когда вы нажимаете кнопку, она переключает свое состояние. Но не только его собственное государство; четыре кнопки, которые являются непосредственными соседями нажатой кнопки, также будут переключаться. Цель состоит в том, чтобы выключить (или включить, в зависимости от того, как вы на это смотрите) все кнопки. Проще просто сыграть самому, чем объяснить словами:

Чтобы увидеть эту демонстрационную часть, у вас должен быть установлен Flash Player 9 или выше, и включен JavaScript.

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

Создайте новый документ ActionScript 3 Flash. Обязательно сохраните его и установите размеры сцены на 600 x 400. Я установил фоновый слой с прямоугольником в нем с коричневой градиентной заливкой.

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

Дважды щелкните по вновь созданному мувиклипу, чтобы ввести его временную шкалу. Расширьте временную шкалу до 9 кадров. В зависимости от того, какой переход вы хотите использовать и какую версию Flash вы используете, у вас будет другой подход к созданию реальной анимации. Я оставляю это на усмотрение читателя, чтобы разобраться с этим — в моем случае я создал анимацию формы и анимировал градиент. Как бы то ни было, цель состоит в том, чтобы в кадрах 1 и 9 у нас было «нормальное» состояние, а в кадре 5 — «выключено», с коротким и приятным переходом между этими двумя состояниями.

На новом слое поместите «традиционный» ключевой кадр в кадры 1 и 5 и поместите простой скрипт «stop ()» в эти кадры. Идея в том, что мы будем контролировать анимацию от бесконечного зацикливания. Он должен начинаться с кадра 1 и ждать, и когда мы напишем еще немного логики, мы скажем ролику воспроизвести, что позволит анимации воспроизводиться до тех пор, пока она не достигнет кадра 5, и снова на обратном пути.

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

Начните с верхнего левого квадрата и дайте ему имя экземпляра «s1». Нажмите на следующий квадрат справа и назовите его «s2» и продолжайте аналогичным образом, пока не дойдете до верхнего правого квадрата с именем «s6». Начните с левой стороны, перейдите ко второму ряду и назовите «s7». Продолжайте в том же духе, пока не окажетесь в правом нижнем углу с именем «s24».

Обратите внимание, что в загрузку включен скрипт JSFL с именем «nameGrid.jsfl». Если вы просто выберете все 24 квадрата, то дважды щелкните по этому сценарию, он запустится и назовите квадраты для вас (надеюсь).

Теперь, когда у нас настроены визуальные элементы, мы можем начать писать сценарий. Мы начнем с создания нового слоя, в котором будет находиться код, щелкнув его фрейм и нажав F9 (Windows) / Opt-F9 (Mac).

Мы добьемся успеха, определив некоторые переменные. На самом деле, первые две могут быть константами:

1
2
const ROWS:int = 4;
const COLUMNS:int = 6;

Затем мы собираемся установить два массива, которые будут хранить ссылки на мувиклипы, составляющие сетку. Мы создадим двумерный массив, называемый «сеткой», который мы заполним через умный процесс позже. Мы также создадим массив массивов списков с именем «list», который теперь будет заполнен мувиклипами:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var grid:Array = [];
var list:Array = [
    s1,
    s2,
    s3,
    s4,
    s5,
    s6,
    s7,
    s8,
    s9,
    s10,
    s11,
    s12,
    s13,
    s14,
    s15,
    s16,
    s17,
    s18,
    s19,
    s20,
    s21,
    s22,
    s23,
    s24
];

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

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

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

1
2
3
4
list.forEach(setupSquare);
function setupSquare(square:MovieClip, index:int, array:Array):void {
    square.addEventListener(MouseEvent.CLICK, onSquareClick);
}

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

  1. Элемент в массиве
  2. Индекс этого элемента
  3. Сам массив

Мы вернемся к этой функции и уточним ее более подробно, но сейчас давайте просто напишем эту функцию onSquareClick и протестируем фильм:

1
2
3
function onSquareClick(me:MouseEvent):void {
    trace(me.currentTarget.name);
}

Если вы сейчас тестируете фильм, вы сможете щелкнуть каждый квадрат и увидеть его название на панели «Вывод». Надеемся, что вы сможете оценить объем работы, который мы сэкономили, используя метод forEach, а не добавлять по отдельности 24 прослушивателя событий.

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

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

01
02
03
04
05
06
07
08
09
10
function toggleSquare(square:MovieClip):void {
    if (!square) return;
    if (square.isOn) {
        square.isOn = false;
        square.gotoAndPlay(2);
    } else {
        square.isOn = true;
        square.gotoAndPlay(6);
    }
}

Вот что происходит:

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

Затем мы проверяем, есть ли квадрат isOn. Если это так, мы удостоверимся, что «isOn» равно false, и скажем квадратному клипу воспроизвести его временную шкалу из кадра 2 (чтобы он перешел в состояние «выключено» в кадре 5). Если это не так, мы устанавливаем «isOn» в значение «истина» и воспроизводим временную шкалу из кадра 6 (который будет воспроизводиться до состояния «включено» в кадре 9, а затем возвратиться к кадру 1, где он снова остановится).

Но подождите, откуда это «isOn»? Мы должны добавить это. Мы просто динамически добавляем в MovieClip свойство, которое является квадратом, и позволяем каждому квадрату отслеживать свое собственное состояние. Для первоначальной настройки вернитесь к функции setupSquare и добавьте следующую строку:

1
square.isOn = true;

Мы будем работать с этим позже, но сейчас мы просто хотим убедиться, что переменная объявлена ​​и имеет точное значение (поскольку мы инициируем плату с состоянием «все включено», нам нужно убедиться, что «isOn» «переменная верна ).

Последний момент здесь: в onSquareClick удалите трассировку и добавьте следующее:

1
toggleSquare(me.currentTarget as MovieClip);

Идите и проверьте это; теперь у вас должна быть интерактивная сетка переключающих квадратов!

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

Сначала нам нужно определить строку и столбец. Здесь мы будем полагаться на тот факт, что наши клипы были установлены в массиве «list» в порядке слева направо, сверху вниз. В setUpSquare добавьте эти строки:

1
2
var row:int = Math.floor(index / COLUMNS);
var col:int = index % COLUMNS;

То, что мы делаем, это используем «index» как способ выяснить, в какой строке и столбце мы находимся. Я не буду слишком углубляться в математику, но это отличный трюк, чтобы спрятаться и использовать при необходимости. Я предложу объяснение символа% в коде: это оператор по модулю, который дает нам остаток от операции деления двух чисел (10% 3 дает нам 1, потому что 10, деленное на 3, это 3 с остаток от 1). В качестве упражнения для запутанного читателя, я оставляю вам возможность просмотреть каждый указатель, один за другим, и разработать математику для себя (используя бумагу, а не компьютер). Вы быстро увидите шаблон и сэкономите мне несколько абзацев ввода текста.

Теперь используйте эти значения для настройки многомерного массива:

1
2
if (!grid[col]) grid[col] = [];
grid[col][row] = square;

Сначала нам нужно убедиться, что grid [col] является массивом. Если он не существует, то нам нужно настроить его как новый массив. Затем мы можем рассматривать сетку как двумерный массив и вставлять мувиклип в соответствующую позицию строки / столбца.

С помощью нашего двумерного массива мы можем добавить этот код в функцию onSquareClick:

01
02
03
04
05
06
07
08
09
10
11
12
var index:int = list.indexOf(me.currentTarget);
var row:int = Math.floor(index / COLUMNS);
var col:int = index % COLUMNS;
 
toggleSquare(grid[col][row — 1]);
toggleSquare(grid[col][row + 1]);
if (col > 0) {
    toggleSquare(grid[col — 1][row]);
}
if (col < COLUMNS — 1) {
    toggleSquare(grid[col + 1][row]);
}

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

Наконец, мы просто вызываем toggleSquare на квадратах, которые являются соседями квадрата, на который нажали. Мы определяем их, добавляя или вычитая один из столбцов или строк, по которым щелкнули.

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

Чтобы защититься от этого, мы уже добавили флажок «если фактический квадрат не пройден, ничего не делайте» в toggleSquare. Но в случае граничных столбцов проблема фактически заключается в том, что весь столбец не существует. Представьте себе: вы нажимаете в левом столбце. Столбец определен в позиции 0. Чтобы найти левого соседа, вычтите один из столбца. Это дает нам столбец -1. Однако доступ к массиву сетки с индексом -1 не приводит к значению; нет ничего со значением -1! Итак, мы делаем две проверки значения столбца, чтобы убедиться, что он находится в пределах границ, прежде чем пытаться переключать квадрат с этим индексом соседнего столбца.

Если вы попробуете это сейчас, у вас должна быть почти работающая игра.

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

1
2
3
if (list.every(checkWin)) {
    trace(«WIN!»);
}

Каждый метод массива в принципе похож на метод forEach , хотя это способ проверки ситуации да / нет в массиве. По сути, функция, которую мы предоставляем каждому методу, должна возвращать либо true, либо false . Сам метод вернет true, если каждый элемент в массиве привел к истинному возвращению из предоставленной функции, или false, если хотя бы один элемент вызвал ложное возвращение. Мы напишем функцию «checkWin», которая возвращает true , если данный квадрат в массиве «on», и false, если «off». Поместите эту функцию в любое место, которое имеет смысл, вне любой другой функции:

1
2
3
function checkWin(square:MovieClip, index:int, array:Array):Boolean {
    return square.isOn;
}

Как и метод forEach , эта функция получит элемент в массиве (мувиклип), а также индекс и сам массив. Нас интересует только мувиклип, в частности переменная isOn, содержащаяся в нем. Однако, в отличие от метода forEach , нам нужно возвращать логическое значение. Мы просто вернем значение самой переменной isOn. Если какой-либо из квадратов выключен, его переменная isOn будет иметь значение false , а каждый метод будет возвращать значение false , что означает, что мы не победим. Однако, если все включено, мы получим значение true для каждого метода и, следовательно, увидим сообщение о победе.

Почему мы используем каждый вместо forEach ? Ну, во-первых, это больше подходит для того, что мы пытаемся сделать. Мы могли бы сделать это с помощью forEach , но это потребовало бы больше шагов. Тем не менее, еще одним преимуществом является эффективность. При первом возврате false каждому , каждый прекратит итерацию и просто вернет false . Итак, если первый квадрат выключен, то мы знаем, что это не победа, и нам не нужно перебирать оставшиеся 23 квадрата.

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

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

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

Тем не менее, мы должны убедиться, что вся доска настроена, прежде чем мы попытаемся переключить любой из квадратов. Таким образом, самый простой подход — это установить еще один вызов forEach после вызова forEach, который устанавливает квадраты. Добавьте эту строку прямо под list.forEach (setupSquare); линия:

1
list.forEach(setupBoard);

А затем найдите подходящее место для написания новой функции (имеет смысл поместить ее после функции «setupSquare», но это на самом деле не имеет значения. Организуйте свой код так, чтобы он имел смысл для вас):

1
2
3
4
5
function setupBoard(square:MovieClip, index:int, array:Array):void {
    if (Math.random() < 0.4) {
        square.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
    }
}

Это означает, что по статистике, около 40% квадратов будут проинструктированы для переключения в выключенное состояние. Это интересная техника, но мы можем «подделать» событие щелчка, сообщив квадрату об отправке искусственного MouseEvent. Это, в свою очередь, вызывает функцию «onSquareClick», как если бы пользователь щелкнул квадрат, переключая его и его соседей. В этом случае, однако, несколько квадратов переключаются в мгновение ока, устанавливая доску для выигрышной игры.

Протестируйте этот фильм сейчас, и у вас должна быть относительно сложная доска, готовая к игре. Вы можете изменить сложность, увеличив число справа от сравнения. Не устанавливайте его в 0 или 1, но чем ближе к 0, тем легче; чем ближе к 1, тем сложнее (вообще говоря).

На этом этапе вы должны быть сертифицированным мастером Array. Вы использовали массивы способами, о которых не знают даже некоторые бывалые ActionScripters (спросите у опытного ActionScripter, знает ли он или она, что делает каждый метод). Массивы играют важную роль практически в каждом приложении. Вы быстро узнаете, что массивы в сочетании с итерацией могут сэкономить вам все виды времени. При разумной настройке с учетом будущего расширения, если вы просто измените массив, программа сама позаботится об остальном. Например, чтобы создать доску с большим количеством квадратов, все, что нам нужно сделать, это сделать визуальные изменения и обновить массив «list». После этого итерационные функции элегантно обрабатывают массивы независимо от того, сколько элементов в них.

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