понедельник, 19 декабря 2011 г.

Создание порта движка для создания двухмерных игр Flixel на Haxe

1. Что такое Haxe?

Haxe – это объектно-ориентированный язык для кроссплаторменной разработки, в настоящее время в качестве целевых платформ для него выступают flash, javascript, php, c++ (в начале 2012 года ожидаются c# и java). Если Вы раньше писали программы на ActionScript 3, то синтаксис Haxe покажется Вам знакомым (еще один немаловажный плюс). При этом для haxe существует множество различных библиотек, существенно облегчающих различные задачи. Одной из таких библиотек является NME (Neko Media Engine), позволяющая пользоваться flash api при разработке графических приложений для Windows, Mac, Linux, Android, iOS, WebOS, HTML5 и, конечно, flash (куда же без него), это означает, что Вы пишете один код (ну, почти что один), и он работает на всех перечисленных платформах. Кроме того, программы под Windows, Mac, Linux, Android, iOS и WebOS используют аппаратное ускорение графики (через OpenGL, OpenGL ES), что на данный момент дает значительное преимущество в производительности перед флэшом (аппаратное ускорение в котором есть на персоналках, но на мобильных платформах его пока только обещают).

2. Что такое Flixel?

Flixel – это, как ясно из заголовка статьи, движок для 2d-игр, написанный на AS3. Он очень прост для изучения и имеет множество плюшек (далее вольный перевод с официального сайта):
- отображение на экране тысяч объектов;
- объединение объектов в группы (например, для обработки столкновений между группами);
- встроенная простая система столкновений между объектами;
- системы частиц;
- создание уровней с помощью tilemap’ов (для этого существует ряд редакторов, лучшим считается dame-editor);
- дополнительные классы для отображения текста, работы с сохранением игр, скроллинга игрового мира;
- классы для работы с клавиатурой и мышью;
- утилиты для работы с цветом и геометрическими преобразованиями.
В число «продвинутых» особенностей движка входят:
- возможность записи и воспроизведения игрового процесса;
- встроенный визуальный отладчик;
- система камер, позволяющая создавать игры в режиме split-screen (и не только);
- функция поиска пути в плиточном мире;
- простая система многократного использования объектов (object recycling).

3. Flixel + Haxe = HaxeFlixel

В качестве эксперимента (и более глубокого понимания языка, а также основ геймдева) решил я портировать Flixel на Haxe. Было это в середине лета (2011). К тому времени уже существовал ряд таких портов, единственным их минусом было «моральное устаревание» (просто это порты более старых версий). В качестве основы был взят этот порт, т.к. он считался наиболее завершенным и у него было наибольшее число «наблюдателей». Спасибо автору, т.к. без его работы моего порта могло бы и не быть. И так, в середине июля, пошел процесс: в одной вкладке FlashDevelop’а я открывал файл на as3 и переводил его на haxe, а во второй – его старый порт, с которым сверялся в трудных для понимания местах (т.к. язык знал плохо). Примерно за 2 недели все классы были переведены, потом я на две недели уехал в «отпуск». А после возвращения начались попытки компиляции простеньких примеров – сразу же посыпался «мильон» ошибок, большая часть из которых решалась крайне просто – путем приведения типов переменных к нужному виду. Дело в том, что в Haxe более строгая типизация, чем в AS3, поэтому, если некоторый метод ожидает в качестве аргумента получить целочисленное значение (Int), а Вы передается ему число с плавающей точкой (Float – местный аналог Number), то это вызовет ошибку компиляции. AS3 в этом отношении менее строг и сам отбросит дробную часть. На данном этапе больше всего мне запомнился момент, когда после очередной попытки запуска проекта вылетело где-то 250 ошибок, а после исправления пары из них их стало за 300 (рифмовать не надо :)). К середине сентября простые примеры уже начали запускаться и начался отлов ошибок портирования, чистка кода.
Началом следующего этапа в создании порта стала новость о выходе новой версии библиотеки NME 3.0 (начало октября), которая обещала более простой процесс кроссплаторменной разработки и множество улучшений в работе. Почитав на официальном форуме в списке рассылки о преимуществах новой версии этой библиотеки, решил интегрировать их, чтобы была возможность писать игрушки под мобилы (так, ради интереса, смартфона-то у меня и нет). Это было несложно: те места, которые не хотели компилироваться на c++, я просто комментировал так, чтобы они исполнялись только на flash-платформе. Тогда из порта пришлось выбросить сохранения игр. Где-то за неделю я управился и оставил на форуме haxenme.org сообщение о своем порте. Ответы не порадовали: для меня оказалось новостью, что операции с битмапами (а они являются основой системы рендеринга flixel) выполняются на cpu (а он, на телефонах слабоват) и простые демки дико тормозили (ну вот такой я нуб-самоучка). Кроме опроса на форуме, проводились тесты и на компьютере, они тоже дали неутешительный результат – программы, скомпилированные под с++, работали даже медленнее, чем во flash-player’e. После этого начались поиски наиболее оптимальных методик для работы с графикой. Слава Богу, что эту работу до меня уже сделали члены haxe-сообщества: для быстрой обработки графики предлагалось использовать операции drawTriangles и drawTiles. С первой Вы, скорее всего, знакомы, т.к. она присутствует во флэше (для тех, кто не знает: с помощью этой команды можно нарисовать треугольник, используя в качестве «текстуры» любую битмапдату). Вторая операция – нововведение в NME, она аналогична drawTriangles, но отрисовывает уже прямоугольник (пока что работает только в c++). Первоначальная реализация данного метода была довольно негибкой: можно было задавать только положение такого прямоугольника, поэтому я решил использовать drawTriangles и посвятил пару недель экспериментированию. Но результаты меня не устраивали, ведь drawTriangles не позволяет задавать цветовую трансформацию отрисовываемой текстуры, а я очень этого хотел (иначе пришлось бы сделать еще одно «обрезание» моему порту). И тут вышел первый релиз-кандидат NME 3.1, в котором появилась возможность кроссплатформенной работы с LocalSharedObject (т.е. вернулась возможность сохранять игры на всех поддерживаемых платформах), улучшенная работа с ассетами (шрифтами, графическими, звуковыми и текстовыми файлами), а также (барабанная дробь) расширение функционала метода drawTiles – теперь он позволяет задавать тайлам масштаб, поворот, прозрачность и цветовую трансформацию. Радости моей не было предела, начался новый этап экспериментирования с этим методом. С нюансами его работы разобраться удалось не сразу. Оказалось, что работать с ним нужно аккуратно, т.к. если пытаться использовать одну и ту же битмапдату в качестве «текстуры» для двух разных tileSheet’ов (объектов для отрисовки тайлов), то «волшебным» образом пропадали прозрачность и цветовые трансформации. Но оно и к лучшему, т.к. это заставило более внимательно относиться к ресурсам, не создавая «дублирующих» объектов. Таким образом, я пришел к идее класса-менеджера, отвечающего за работу всех tileSheet’ов – он является центром всей новой системы рендеринга. Если в оригинальном Flixel’е каждый объект непосредственно рисуется на битмапдате камеры, то в новой системе каждый объект передает в менеджер свои данные (положение, поворот, масштаб и т.д.), и только после того, как все объекты передадут свои данные, идет отрисовка всех тайлов. С этой особенностью связана еще одна проблема – проблема сортировки глубины отрисовки объектов. Если в оригинальном Flixel’е порядок отрисовки можно изменить отсортировав массив самих объектов (отрисовка ведется в порядке их расположения в таком массиве), то в новой системе объекты отрисовываются пакетно, т.е. все объекты, имеющие в качестве графического представления одну и ту же битмапдату, отрисовываются за одну операцию. Эту проблему удалось решить частично за счет:
- возможности отдельной сортировки объектов, делящих между собой одну и ту же битмапдату (т.е. уже имеющимися средствами flixel);
- а также возможности сортировки самих tileSheet’ов в менеджере (для этого были добавлены соответствующие методы для получения текущей глубины отрисовки tileSheet’а и ее изменения).
Разработав основу новой системы, я провел несколько тестов и увидел значительный прирост в производительности, т.к. теперь операции отрисовки в с++ программах стали осуществляться с помощью gpu. Убедившись, что теперь можно показать предварительный результат работы, я снова написал на форум; отзывы показали, что работа не была потрачена впустую (это было в начале декабря). Эти отзывы придали мне новых сил, и я с удвоенным рвением продолжил доработку системы рендеринга, попутно заборов еще парочку багов.
Теперь немного о грустном. К сожалению, в NME еще не все так гладко (хотя в последнее время библиотека очень сильно преобразилась в лучшую сторону), например, есть некоторые проблемы с клавиатурой, когда нажатия на разные клавиши выдают одинаковые значения кодов (keyCode), поэтому пока что неправильно работают клавиши цифрового блока, а также F1-F12. Также для звука на с++ приходиться конвертировать файлы в wav-формат (а это дополнительный объем), если же использовать mp3, то одновременно получается воспроизводить только один звук. Кроме того, под c++ реализован еще не весь flash api, и мне пришлось писать свою упрощенную реализацию метода hitTest для битмапдаты. Некоторые методы класса BitmapData работают не так, как на флэше, из-за чего возникают артефакты – здесь тоже пришлось писать свою реализацию (для зеркального отражения битмапдаты).
Но, как я уже сказал раньше, библиотека постоянно улучшается, и я надеюсь, что эти проблемы скоро уйдут в небытие.
Ах да, чуть не забыл дать ссылку на репозиторий проекта: https://github.com/Beeblerox/HaxeFlixel
А также скомпилированная на c++ демка (под Windows): https://github.com/downloads/Beeblerox/HaxeFlixel/ModeDemo20122011.zip

четверг, 14 июля 2011 г.

Нахождение точки пересечения двух отрезков

Рассмотрим алгоритм нахождения точки пересечения двух векторов. Для этого нам понадобится параметрическое уравнение отрезка.
Пусть имеется некоторый отрезок P0P1, уравнение данного отрезка будет иметь вид:
где Р0 и Р1 – начальная и конечная точки отрезка;
u = P1-P0 – вектор, показывающий направление и длину отрезка;
s – координата точки на отрезке (или прямой, содержащей данный отрезок).
При  0 <= s <= 1 мы имеем множество точек, лежащих между точками Р0 и Р1, в противном случае (s < 0 или s > 1) – мы получим все точки, лежащие на прямой, содержащей данный отрезок, но за его границами («левее» и «правее»).
Рассмотрим теперь 2 отрезка, заданных параметрическими уравнениями:
Данные отрезки будут параллельны, если
где c – некоторое число.
Вспомним теперь понятие перпендикулярного скалярного произведения – модификации скалярного произведения, в котором первый вектор заменен его левой нормалью. Важным свойством данного произведения будет его равенство нулю в случае параллельности перемножаемых векторов (вытекает из свойств обычного скалярного произведения векторов), т.е.:
Это все была необходимая вводная информация. Перейдем теперь непосредственно к нахождению точки I пересечения отрезков P(s) и Q(t).
Иллюстрация к нахождению точки пересечения двух отрезков

Проведем вектор w, соединяющий начала отрезков P(s) и Q(t) (т.е. через точки P0 и Q0):

Условием пересечения отрезков P(s) и Q(t) будет перпендикулярность векторов P(s1)-Q0 и левой нормали вектора v  (см. рисунок), т.е. равенство их скалярного произведения нулю:
где
Тогда, подставив в выражение скалярного произведения векторов данные координаты векторов, получим:

Раскрыв скобки и перегруппировав, получим:

отсюда
Выражение в числителе полученной дроби – есть перпендикулярное скалярное произведение векторов v  и w, а выражение в знаменателе – перпендикулярное скалярное произведение векторов v  и u, взятое со знаком «минус». Тогда:
Аналогично найдем значение параметра t1 для отрезка Q(t):
 Таким образом, мы нашли значения параметров s1 и t1. Координаты самой точки пересечения можно найти, подставив значения одного из параметров в соответствующее уравнение отрезка. Например:
 Но полученная таким образом точка может лежать как в границах заданных отрезков, так и за их пределами, и нам необходимо определить лежит ли данная точка в границах отрезков, или нет. Для этого нужно, чтобы выполнялись следующие условия:
 Вот и вся теория необходимая нам для данной операции. Данную статью Вы можете скачать в формате pdf здесь (более читабельна).
Краткое описание алгоритма было взято отсюда. Я его только перевел и подробно расписал.

пятница, 1 июля 2011 г.

Мой первый проект на GitHub

Давно хотел создать какой-нибудь общедоступный проект, ну и начал с самого мне необходимого – математической библиотеки, доступной здесь. Сейчас в ней только один класс, обеспечивающий работу с матрицами – теми самыми, что Вы могли встретить в курсе высшей математики в институте.
Для создания объекта-матрицы вызываем конструктор, который создает нулевую матрицу (т.е. матрицу, у которой все элементы равны нулю):
var newMatrix:MatrixMath = new MatrixMath(numRows, numCols);
где numRows – число строк, а numCols – число столбцов матрицы.
Для заполнения коэффициентов матрицы случайными числами вызываем следующий метод:
newMatrix.random(min, max);
где min и max – границы диапазона случайных чисел, которые будут заполнять матрицу.
Для задания значения конкретного элемента матрицы используйте метод:
newMatrix.setElement(row, col, value);
где row – номер строки, col – номер столбца (внимание, row и col отсчитываются от нуля), value – задаваемое значение элемента.
Есть также пара методов для задания значений всех элементов матрицы из массивов:
setMatrixFromRows(rowsVector:Vector.<Vector.<Number>>) – как видно, в качестве аргумента принимает двумерный массив элементов (число строк и столбцов матрицы рассчитывается исходя из размеров данного массива);
setMatrixFromVector(matrixVector:Vector.<Number>, numRows:uint, numCols:uint) – в качестве аргумента принимает одномерный массив, а также число строк и столбцов матрицы.
Для чтения значений элементов матрицы есть метод
getElement(row:uint, col:uint)
Основным применением данного класса является решение систем линейных уравнений. В моей реализации данного класса эту задачу можно выполнить тремя способами:
1) это решение уравнений с помощью метода Крамера. Данный метод решения наиболее ресурсоемкий и использовать его для систем более, чем с 8 неизвестными – крайне не рекомендуется (программа «повиснет»). Был реализован чисто «из спортивного интереса». Пример использования:
// Создаем матрицу, состоящую из коэффициентов при неизвестных 
// и заполняем ее случайными значениями
var testMatr1:MatrixMath = new MatrixMath(3, 3);
testMatr1.random(0, 100);
trace("A:");
// Трассируем содержимое матрицы
testMatr1.traceMatrix();
// Создаем матрицу, состоящую из свободных членов уравнений
var testMatr2:MatrixMath = new MatrixMath(3, 1);
testMatr2.random(0, 50);
trace("B:");
testMatr2.traceMatrix();
trace("Cramer Solution:");
// Создаем матрицу-столбец, содержащую решение системы уравнений
var testMatr:MatrixMath = MatrixMath.CramerSolution(testMatr1, testMatr2);
// Выводим на экран ее содержимое
trace(testMatr.m);

2) решение системы с помощью метода Гаусса:
var testMatr1:MatrixMath = new MatrixMath(3, 3);
testMatr1.random(0, 100);
trace("A:");
testMatr1.traceMatrix();
var testMatr2:MatrixMath = new MatrixMath(3, 1);
testMatr2.random(0, 50);
trace("B:");
testMatr2.traceMatrix();
trace("Gauss Solution");
// Создаем матрицу-столбец, содержащую решение системы уравнений
testMatr = MatrixMath.GaussSolution(testMatr1, testMatr2);
trace(testMatr.m);

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

3) Решение с помощью LUP-разложения позволяет быстро решать множество систем уравнений, отличающихся лишь значениями свободных членов. Данный способ отличается от метода Гаусса тем, что позволяет «запоминать» последовательность операций, необходимых для решения системы уравнений. Пример использования:
trace("LUP Solution");
var testMatr1:MatrixMath = new MatrixMath(3, 3);
testMatr1.random(0, 100);
trace("A:");
testMatr1.traceMatrix();
// Создаем т.н. матрицу перестановок, в которой хранится 
// информация о необходимых действиях
var permutationMatr:MatrixMath = new MatrixMath(testMatr1.rows, 1);
// Внимание, метод LUPDecomposition изменяет значения элементов 
// в матрицах testMatr1 и permutationMatr, поэтому, 
// если Вам необходимо сохранить исходные значения 
// элементов матриц, то используйте метод clone:
var cloneMatr:MatrixMath = testMatr1.clone();
// Выполняем необходимые преобразования матриц
MatrixMath.LUPDecomposition(cloneMatr, permutationMatr);
// Создаем матрицу, состоящую из свободных членов уравнений
var testMatr2:MatrixMath = new MatrixMath(3, 1);
testMatr2.random(0, 50);
trace("B:");
testMatr2.traceMatrix();
// Создаем матрицу-столбец, содержащую решение системы уравнений
testMatr = MatrixMath.LUPSolution(cloneMatr, testMatr1, testMatr2);
// Выводим на экран решение
trace(testMatr.m);
// Давайте теперь решим другую систему уравнений, 
// отличающуюся только значениями свободных членов
// Зададим новые значения свободных членов уравнений:
testMatr2.random(0, 100);
// Снова решаем систему уравнений, 
// используя уже преобразованные матрицы
testMatr = MatrixMath.LUPSolution(cloneMatr, testMatr1, testMatr2);
trace(testMatr.m);

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

На сегодня – все. Надеюсь, что следующая статья будет посвящена продолжению темы векторов :)

пятница, 1 апреля 2011 г.

Создание документации средствами FlashDevelop

Я все время думал, что создание документации к коду – довольно сложное занятие, но только сегодня узнал, как я ошибался. А все благодаря возможностям FlashDevelop (здесь я рассматриваю версию 3.3.4).
Всем известно, что в комплекте с FlexSDK (который необходим для разработки Flash-приложений без использования Flash IDE) поставляется инструмент ASDoc – программа, работающая из командной строки и позволяющая из особым образом отформатированных комментариев к коду генерировать документацию. Но работать с программами из командной строки неудобно, и тут на помощь приходит FlashDevelop.

Чтобы создать документацию в html-формате нужно сначала написать комментарии к коду. Рассмотрим основные правила и приемы для написания таких комментариев.
Для того, чтобы создать заготовку для документации к какому-либо методу, свойству или описания всего класса, нужно всего лишь перед комментируемым участком кода набрать: /**, и тогда появиться следующая подсказка:
Выбрав пункт Empty, мы получим пустую заготовку:

Во второй строчке этого комментария можно ввести описание следующего за ним public-свойства, getter’а / setter’а, или всего класса.

Выбрав пункт Method details, мы получим заготовку для подробной документации метода:
На второй строке получившегося комментария можно написать описание метода.
На строке с тегом @param имяПараметра пишется описание данного параметра
Строка с тегом @return описывает возвращаемый методом результат.
Все строки заполнять необязательно, некоторые можно и удалить.
Это были основные теги, со списком остальных Вы можете ознакомиться например здесь (Теги ASDoc. AS3).

Кроме автоматического варианта создания заготовки комментария-документации к методу, Вы можете также сами прописывать тэги. Для этого, находясь в комментарии, Вам достаточно набрать @, и тогда появиться список всех тэгов:
Задокументировав таким образом наш класс, мы может перейти непосредственно к созданию справки в html-формате. Для этого в меню Tools нужно выбрать пункт Documentation Generator…:
В появившемся диалоговом окне заполняем следующие поля:
Page title – заголовок страницы документации
Exclude classes – здесь перечисляем через запятую классы, документацию к которым создавать не нужно
Output directory – папка, в которую будет помещена справка (адрес этой папки не должен содержать кириллических символов)
Classpaths – список папок с as-файлами
В выпадающем списке выбираем пункт ASDOC (AS3).

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

Чтобы каждый раз для данного проекта не прописывать все настройки по созданию документации, Вы можете воспользоваться кнопками Save Project… и Open Project… (названия, думаю, говорят сами за себя).

На этом на сегодня все.

воскресенье, 20 марта 2011 г.

Введение в векторы

Данной статьей я хочу начать небольшой цикл по возможному использованию векторов, а также освежить Ваши знания по данному предмету. Под вектором здесь я понимаю не «векторную графику», а Вектор – в смысле НАПРАВЛЕННЫЙ ОТРЕЗОК (надеюсь, что Вы сами поймете, почему я написал «Вектор» с большой буквы). В основном мы будем рассматривать векторы на плоскости, но будут и небольшие дополнения по предмету о векторах в пространстве.
Для чего же нужны векторы вообще? Отвечаем: с помощью векторов очень удобно задавать положение тел на плоскости и в пространстве, их скорости и ускорения (т.к. они, по сути, являются векторными величинами), всевозможные силовые факторы (гравитация, силы упругости, трения и т.д.). Иными словами, с их помощью можно создавать различные механические модели. Но на этом применение векторов не ограничивается, они используются и в трехмерной графике (например, для модели освещенности поверхностей, расположенных под различными углами к источнику света).
Заинтересованы? Как же столь простая вещь, как простой отрезок, может дать нам такие возможности? Что ж, давайте начнем путешествие в мир Вектора!
Рис.1. Вектор на плоскости

Математически вектор на плоскости может быть выражен парой чисел, причем несколькими способами:
1) полярный способ задания следует из определения вектора: первое число – его длина (т.е. расстояние между концом и началом вектора), второе – угол, который он образует с некоторой осью отсчета (на рис.1 такой осью является ось Ох). Данный способ мы будем использовать крайне редко (если вообще будем);
2) более распространенным является координатный способ задания вектора:
Взаимосвязи между длиной вектора, его углом на плоскости и координатами могут быть выражены следующими зависимостями:
Как видно, зная любые две характеристика вектора, мы можем найти остальные.
Теперь рассмотрим основные операции с векторами:
1) Умножение вектора на число

Рис.2. Умножение вектора на число (k>1)
Результатом умножения вектора AB на число k (масштабирование вектора) является вектор АС с координатами:

Таким образом:
- умножение вектора на k =1  оставит вектор без изменений;
- умножение вектора на k =0 превратит его в точку;
- умножение вектора на k =(-1) изменит его направление на противоположное;
- умножение вектора на |k| >1 увеличит его длину;
- умножение вектора на |k| <1 уменьшит его длину.

2) Нормализация вектора (приведение к единичному виду)
Часто бывает необходимо найти вектор, который был бы сонаправлен с исходным, но имел бы единичную длину (данные случаи мы рассмотрим в следующих статьях). Координаты такого вектора найти крайне просто, воспользовавшись операцией масштабирования вектора (пункт 1). Например, для вектора AB координаты единичного вектора найдем как:
где len – первоначальная длина вектора

3) Сложение векторов
Рис.3. Сложение векторов

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

4) Разностью векторов a и b является третий вектор c с координатами:
Т.е.
5) Поворот вектора на некоторый угол α
Рис.4. Поворот вектора АВ на угол α

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

где len – первоначальная длина вектора АВ (ее мы должны найти перед выполнением операции поворота).


6) Скалярным произведением двух векторов a и b является скаляр (число), равный:
где |a| и |b| – длины векторов; α – угол между ними.
Чаще всего скалярное произведение векторов используют для определения угла между ними и для проецирования одного вектора на второй (данный вопрос будет рассмотрен подробно далее).
Формула для определения угла между векторами:

Скалярное произведение перпендикулярных векторов равно нулю (с помощью данного свойства скалярного произведения можно определять взаимную перпендикулярность двух векторов):
У скалярного произведения есть физический смысл – оно равно работе силы a на перемещении b.

7) Векторное произведение двух векторов a и b есть третий вектор, перпендикулярный плоскости этих двух векторов. Длина данного вектора равна:
Как видно из этой формулы, если векторы a и b параллельны, то длина их векторного произведения равна нулю (т.к. sin(0)=sin(180)=0). Поэтому для случая вектора на плоскости данное произведение чаще всего используют для проверки параллельности векторов. В случае вектора в пространстве, векторным произведением пользуются для нахождения нормали к поверхности, образованной данными векторами, а сама нормаль поверхности может использоваться для определения видимости поверхности, степени ее освещенности (случай вектора в пространстве сейчас мы не будем рассматривать).Геометрический смысл векторного произведения – его длина равна площади параллелограмма, построенного на этих векторах.
В физике векторное произведение используется для нахождения момента силы относительно точки.

8) Помимо описанных операций, просто необходимо уметь находить нормали вектора.
Рис. 5. Вектор и его нормали: ln - левая нормаль, rn - правая нормаль

Нормаль – это вектор, перпендикулярный данному и имеющий ту же длину.
Как видно из рис. 5 у вектора есть 2 нормали (правая и левая). Координаты вектора и его нормалей связаны следующими зависимостями:
9) Проецирование вектора
Рис. 6. Проецирование вектора

Для нахождения координат вектора v3, являющегося проекцией вектора v1 на вектор v2, воспользуемся свойствами скалярного произведения.
Как видно из рис. 6, длина вектора v3 равна:
А так как скалярное произведение векторов v1 и v2, равно:
, то

Теперь нужно найти составляющие вектора v3 по координатным осям. Очевидно, что единичный вектор v3 будет совпадать с единичным вектором v2 (т.к. они направлены вдоль одной линии), поэтому чтобы найти координаты вектора v3, нужно умножить его нормализованный вектор v2 на длину вектора v3:
Для чего же нам могут понадобится проекции (и такие вычисления)?
Например, они нужны нам для расчетов столкновений, которые мы рассмотрим в следующих статьях.

10) Cкалярное произведение с перпендикулярным вектором.
Cкалярным перпендикулярным произведением (perproduct) векторов a и b является скалярное произведение левой нормали вектора a на вектор b. Данное произведение используется для нахождения точек пересечения векторов (подробно мы его рассмотрим в одной из следующих статей).

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

Использованная литература
1.    http://ru.wikipedia.org/wiki/Вектор_(математика)
2.    Базовые алгоритмы определения столкновений
3.    Rex van der Spuy. AdvancED Game Design with Flash.
4.    Vectors for Flash