Класс вектора
Знание векторной математики при создании динамичных флеш игр (где присутствуют повороты, ускорения и т.д.) есть первейшая необходимость для разработчика. Я с улыбкой вспоминаю те времена, когда я делал свои первые игры, часами вычисляя на бумажке как повернуть морду комолёта, как сделать так, чтобы космолёт двигался с ускорением к точке и не промахивался мимо неё, как сделать так чтобы он ещё мог стрелять в разные стороны, а не только в направлении своего движения… ну и т.д.
После такого непродолжительного вступления, представляю на ваше обозрение мой класс вектора. В нём помимо собственно операций с векторами, находятся ещё разные полезные для игростроя функции. Класс этот продолжает развиваться так что скорее всего я буду выкладывать обновлённые версии.
Класс также задействует математическую библиотечку, выложенную мною ранее. Без неё работать не будет
Итак:
package aneg { import aneg.*; public class ageVector { var x:Number; var y:Number; function ageVector(setX:Number=0, setY:Number=0) { x = setX; y = setY; } //Копирует вектор function copy( v:ageVector ):void { x = v.x; y = v.y; } //Устанавливает компоненты вектора function set( setX:Number, setY:Number ):void { x = setX; y = setY; } //Прибавляет вектор v function add( v:ageVector ):void { x += v.x; y += v.y; } //Вычитает вектор v function sub( v:ageVector ):void { x -= v.x; y -= v.y; } //Умножает вектор на число i function mulScalar( i:Number ):void { x *= i; y *= i; } //Равны ли вектора c заданной погрешностью aprox function isEqual( v:ageVector , aprox:Number=0.00001):Boolean { return (ageMath.aproxEqual(x,v.x,aprox) && ageMath.aproxEqual(y,v.y,aprox)); } //Длина вектора function len():Number { return Math.sqrt( x*x + y*y ); } //Длина вектора в квадрате function len2():Number { return x*x + y*y; } //Ограничить длину вектора максимальной длинной function trimLen(maxLen:Number):void { if(len2()>maxLen*maxLen) { normThis(); mulScalar(maxLen); } } //получить проекцию вектора на вектор v function ProjOnVec(v:ageVector):ageVector { var res:ageVector = v.norm(); res.mulScalar(ProjOnScalar(v)); return res; } // скалярная проекция вектора на вектор v function ProjOnScalar(v:ageVector):Number { return dot(v)/v.len(); } // скалярное произведение векторов function dot(v:ageVector):Number { return x*v.x + y*v.y; } // нормализация вектора (получить единичный вектор) function norm():ageVector { var l:Number = len(); var res:ageVector = new ageVector(x,y); if (l) { res.x /= l; res.y /= l; } return res; } // нормализация этого вектора function normThis():void { var l:Number = len(); if (l) { x /= l; y /= l; } } // Lerp с другим вектором. Результат записывается в этот же вектор //return cx + s*(cy - cx); function lerpThis(v:ageVector,s:Number):void { var tmpV:ageVector = new ageVector(); tmpV.copy(v); tmpV.sub(this); tmpV.mulScalar(s); add(tmpV); } //Поворот вектора на угл в радианах function rot(ang:Number):void { var dx:Number=x; x=dx*Math.cos(ang)-y*Math.sin(ang); y=y*Math.cos(ang)+dx*Math.sin(ang); } //Поворот вектора на угл в градусах function rotDeg(ang:Number):void { var dx:Number=x; var angR:Number=ang*Math.PI/180; x=dx*Math.cos(angR)-y*Math.sin(angR); y=y*Math.cos(angR)+dx*Math.sin(angR); } // получить угол в градусах function getAngleDeg():Number { return Math.atan2(y, x)/Math.PI*180; } // получить угол в радианах function getAngle():Number { return Math.atan2(y, x); } // получить угол в градусах с нормированным переходом function getAngleDegNorm():Number { var angle:Number=Math.atan2(y, x)/Math.PI*180; if(angle<0) angle=360+angle; else if(angle>=360) angle=angle-360; return angle; } // получить угол в радианах с нормированным переходом function getAngleNorm():Number { var angle:Number=Math.atan2(y, x); if(angle<0) angle=Math.PI*2+angle; else if(angle>=Math.PI*2) angle=angle-Math.PI*2; return angle; } } }
Весь класс можно скачать здесь: http://www.anegmetex.com/devblog/files/ageVector.as
Категория: Полезные классы

Январь 20th, 2010 at 20:46
Интересно, только вот в играх, нужны конкретные функции, а тащить сразу всю библиотеку целиком – это воспитание от Microsoft сказывается
Январь 21st, 2010 at 1:08
Чёт не очень понятно имеются ли в виду функции в классе вектора, или функции математической библиотеки, использованной в классе вектора )
Февраль 19th, 2010 at 14:33
Math.PI*180 и Math.PI/180 можно высчитать заранее, и избавиться от лишней операции
Февраль 19th, 2010 at 19:52
Т.к. Math.PI это константа и 180 это константа, то я больше чем уверен, что компилятор это оптимизирует и считает за меня
Февраль 22nd, 2010 at 11:24
я имел в виду, что сразу написать число 565,4866 или 0,01745 и вообще ничего не считать …
Февраль 22nd, 2010 at 12:02
Да я понял что ты имел ввиду )
Я же говорю о том, что в итоге компилятор сам посчитает это значение и подставит в формулу, и в скомпилированной программе будет это самое значение 0,01745 и никаких вычислений констант не будет. т.е. получится как раз так как ты сказал )
Март 3rd, 2010 at 18:17
А в 10 версии Flash класс не скомпилится. Там уже есть зарезервированый класс Vector для однотипных массивов. Надо по другому как-то обзывать класс и все ссылки на него внутри..Советую и тут поменять, все таки скоро все на 10 перейдут, а блог думаю долго существовать будет
Март 4th, 2010 at 17:30
Поменял
Июль 13th, 2010 at 17:42
Привет, Олег! Взял я у тебя из класса не глядя полезную функцию rot(ang:Number) для своего аналогичного. А как понадобилась мне эта функция, выяснилось, что расчеты производятся не верно. Долго бился головой о стену пытаясь понять в чем дело и уже было начал думать, что это я троечник хочу от этой формулы то что рассчитывается другой формулой. Даже с формулами в Интернет сверился
И тут меня осенило в чем проблема. В твоей реализации расчеты производятся сразу в переменные вектора и это вызывает ошибку в расчетах. Нужно результат рассчитывать во временные переменные, а потом после расчета присваивать их вектору.
// Во второй строке X уже изменился
// согласно выражению в первой строке и
// поэтому Y считается уже не верно!
x=x*Math.cos(ang)-y*Math.sin(ang);
y=y*Math.cos(ang)+x*Math.sin(ang);
Воот..
Июль 13th, 2010 at 23:21
Привет, Антон. Cпасиб, Zloba мне тоже уже говорил об этом, но я чёт забыл здесь поправить
Странно, но в Toy Tales я не заметил эффекта от этой ошибки, хотя точно помню что юзал где-то поворот)
Вообщем должно быть так:
var dx:Number=x;
x=dx*Math.cos(ang)-y*Math.sin(ang);
y=y*Math.cos(ang)+dx*Math.sin(ang);
Июль 21st, 2010 at 16:07
Здравствуйте! А откуда расчитывается начало вектора? из начала координат?
Ведь для задания вектора нужны 2 точки,а здесь задается только одна!
Июль 21st, 2010 at 16:34
2 randomGuy
Ха, вот это очень интересный вопрос. В играх вектор всегда задаётся относительно начала системы координат (далее СК).
А вот СК может быть очень разной. Может быть СК уровня, может быть СК юнита и т.д.
Отчего это зависит? Да вообщем-то от конкретных нужд разработчика. Обычно вектором задают или направление или позицию.
Пример. Нам нужно задать юнит для топ-даун игрушки. Для него важна позиция и направление. Будем задавать примерно так:
class GameUnit
{
var dirVec:Vector;
var posVec:Vector;
}
Здесь dirVec задаёт текущее направление юнита относительно СК юнита (считается что длина вектора всегда равна единице).
А posVec задаёт позицию юнита относительно СК уровня.
Вот как то так.