Собственно классическое решение задачи поворота спрайта на заданный угол вокруг произвольной точки (чаще всего его центра) путем матричных преобразований описана в куче литературы и интернет-источниках и звучит следующим образом: перенос спрайта таким образом, чтобы точка совпадал с точкой 0.0,0.0 , поворот на заданный угол, перенос обратно.
Функция RotateAroundAxis представляет реализацию данного метода (Axis — ось или точка, вокруг которой будет повернут спрайт, Angle — угол поворота):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private function RotateAroundAxis(Axis:Vector2,Angle:Number):void { // Перенос в 0,0,0 var MoveVector:Vector2 = new Vector2(); Vector2.Vec2Subtract(Axis,new Vector2(0.0,0.0),MoveVector); var matrix:Matrix = new Matrix(); matrix.identity(); matrix.translate(MoveVector.x,MoveVector.y); CountWorldMatrix(matrix); // Поворот var RotAngle:Number = Angle*Math.PI/180.0; // Получить матрицу вращения var Rot:Matrix = new Matrix(); Rot.identity(); Rot.rotate(RotAngle); CountWorldMatrix(Rot); // Вернутся обратно matrix.identity(); matrix.translate(Axis.x,Axis.y); CountWorldMatrix(matrix); } |
Вспомогательная функция CountWorldMatrix добавляет полученную матрицу трансформации к исходной матрице объекта, за счет чего и выполняется поворот:
1 2 3 4 5 |
private function CountWorldMatrix(M:Matrix):void { var w:Matrix = transform.matrix; if(M!=null) w.concat(M); transform.matrix = w; } |
При вызове функции RotateAroundAxis необходимо, чтобы координаты оси были заданны в той же самой системе координат, в которой находится сам поворачиваемый спрайт. Если не соблюдать это правило спрайт будет поворачиваться и убегать куда угодно, только не в ту точку, в которую его необходимо было поместить. Чаще же всего поворачивать спрайт удобнее всего вокруг точки, заданной в системе координат сцены, т.е. того контейнера в котором собираются все отображаемые спрайты. Поэтому, чтобы повернуть спрайт например вокруг центра экрана, сначала необходимо width/2 и height/2 перевести в систему координат спрайта.
Для этих целей служит функция globalToLocal, которая собственно умножает переданную в нее точку на обратную concatenated-матрицу объекта из которого вызывается. Однако же на практике ее использование оказалось достаточно своеобразным. Когда спрайт является чайлдом сцены — globalToLocal срабатывает правильно. Стоит же создать иерархию из 2-3 подчиненных уровней сцена -спрайт — чайлд спрайта — … начинаются нестыковки. В случаях, если спрайт сначала повернуть, после чего добавить ему чайлда и если к спрайту добавить уже повернутого чайлда, функция срабатывает далеко не так, как нужно. Поэтому функцию приходится переопределять (заодно переопределяется тип входного параметра на Vector2 — переопределенный Point со статическими методами операций над двумерными векторами):
1 2 3 4 5 6 7 8 |
internal function GlobalToLocal(Vector:Vector2):Vector2 { var TransformedVector:Vector2 = new Vector2(Vector.x,Vector.y); var P:Point = globalToLocal(Point(TransformedVector)); if(parent!=null) P = parent.globalToLocal(Point(TransformedVector)); TransformedVector.x = P.x; TransformedVector.y = P.y; return TransformedVector; } |
Соответственно, делаем обертку над вызовом функции RotateAroundAxis:
1 2 3 4 |
internal function RotateAround(Axis:Vector2,Angle:Number):Boolean { var TransformedAxis:Vector2 = GlobalToLocal(Axis); return RotateAroundAxis(TransformedAxis,Angle); } |
Такое переопределение позволяет правильно вращать спрайт вокруг произвольной оси независимо от того, является он чьим-то чайлдом или нет.
Вращение спрайта вокруг центра сцены на 45 градусов будет выглядеть следующим образом:
1 |
RotateAround(new Vector2(width/2,height/2),45); |