Реализация 2d камеры в игровом движке

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

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

Какие параметры должна учитывать матрица?
1. Позиция, поворот и масштаб (zoom) камеры;
2. Позиция, поворот, масштаб и точка привязки (pivot point) для текстуры;
3. Позиция и размеры ViewPort;


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

// Середина вьюпорта (середина канваса камеры)
var viewPortCenterX:Number = cameraViewport.x + cameraViewport.width/2;
var viewPortCenterY:Number = cameraViewport.y + cameraViewport.height/2;
 
// Масшаб viewport (APP_WIDTH/APP_HEIGHT - размеры самого приложения)
var viewPortScaleX:Number = cameraViewport.width/_config.APP_WIDTH;
var viewPortScaleY:Number = cameraViewport.height/_config.APP_HEIGHT;
 
// Позиция камеры
var cameraX:Number;
var cameraY:Number;
 
// Зум камеры
var cameraZoom:Number;
 
// Поворот камеры
var cameraRotation:Number;
 
// Позиция текстуры 
var textureX:Number;
var textureY:Number;
 
// Масштаб текстуры
var textureScaleX:Number;
var textureScaleY:Number;
 
// Поворот текстуры
var textureRotation:Number;
 
// Точка привязки текстуры
var texturePivotX:Number;
var texturePivotY:Number;
 
//
var sin:Number = Math.sin(-cameraRotation);
var cos:Number = Math.cos(-cameraRotation);
 
var dx:Number = textureX - cameraX;
var dy:Number = textureY - cameraY;
 
var newTextureX:Number = (dx*cos - sin*dy)*cameraZoom*viewPortScaleX + viewPortCenterX;
var newTextureY:Number = (dx*sin + cos*dy)*cameraZoom*viewPortScaleY + viewPortCenterY;
 
var totalRotation:Number = textureRotation - cameraRotation;
var totalScaleX:Number = textureScaleX * cameraZoom * viewPortScaleX;
var totalScaleY:Number = textureScaleY * cameraZoom * viewPortScaleY;
 
texturePivotX *= totalScaleX;
texturePivotY *= totalScaleY;
 
// 
_matrix.identity();
_matrix.scale(totalScaleX, totalScaleY);
_matrix.translate(texturePivotX, texturePivotY);
_matrix.rotate(totalRotation);
_matrix.translate(newTextureX, newTextureY);
 
//
_canvas.draw(textureBitmapData, _matrix, null, null, cameraViewport);

Результат
На сцену я добавил две камеры разного размера — правая нижняя статическая, а левой верхней можно управлять.
(стрелки движение, A/S — вращение, Z/X — масштабирование)

This movie requires Flash Player 9

Поделиться в соц. сетях

Опубликовать в LiveJournal
Опубликовать в Google Plus
  • Эмиль Шодиев

    Привет!

    Все можно сделать проще:
    _matrix.rotate( tRotation );
    _matrix.translate( tX, tY );
    _matrix.scale( scaleX, scaleX );

    tX и tY — координаты текстуры в мировой системе координат

    а еще, может быть, можно получить прирост производительности так (эффект аналогичен коду выше):

    var cos:Number = scale * Math.cos( tRotation );
    var sin:Number = scale * Math.sin( tRotation );
    mtx.a = cos;
    mtx.b = sin;
    mtx.c = -sin;
    mtx.d = cos;
    mtx.tx = scaleX * dp.x;
    mtx.ty = scaleY * dp.y;

    • VirtualMaestro

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

  • Эмиль Шодиев

    да, поторопился отправлять коммент

    насчет прироста производительности — я не мерил, насколько прирастает, да и вообще, прирастает ли.
    но, почему-то, кажется, что так быстрее

    • VirtualMaestro

      Спасибо, проверю и отпишусь по результатам.