Создание камеры HoverCamera3D
Материал из AlternativaPlatform Wiki
Часто поведение нативного контроллера Альтернативы SimpleObjectController не удовлетворяет потребностям. Давайте создадим один из стандартных обработчиков событий мыши — камеру, реагирующую на положение мыши пользователя в сцене. Подобное поведение очень удобно для радиального просмотра единичных объектов, потому как камера движется вокруг объекта. Правда в одной плоскости. Но расширить пример будет достаточно просто впоследствии.
Итак, давайте наследуем стандартную камеру Альтернативы — Camera3D.
public class HoverCamera3D extends Camera3D {}
Чтобы реагировать на события мыши на сцене передадим в конструктор ссылку на объект stage, ведь без реагирования на события мыши наша задумка не будет иметь смысла, а снимать эти события мы будем именно со сцены. Также, для того, чтобы определить положение камеры будем считать, что камера всегда смотрит в одну точку. Настроить эту точку мы дадим возможность пользователю также через конструктор, передав в него расстояние до камеры от точки наблюдения и саму эту точку. В конструкторе нашей камеры после выполнения конструктора родительского класса выполняем действия по настройке камеры:
super(); if (!stage) throw new Error("HoverCamera3D must have link on stage object in passing parameters of constructor!"); _stage = stage; _cameraTarget = target || new Vector3D(0, 0, 0); _distance = distance;
Далее, будем помнить, что пользователь может изменить размер сцены и тогда наши данные о ней будут не актуальны, поэтому подпишемся на событие изменения размеров сцены
addEventListener(Event.RESIZE, resizeListener);
и будем контролировать горизонтальный максимум смещения в слушателе события resizeListener
_maxX = _stage.stageWidth >> 1;
Далее в конструкторе настраиваем положение камеры, согласно наших исходных данных:
var m:Matrix3D = matrix; m.appendTranslation(0, 0, _distance); m.appendRotation(-_tiltAngle, new Vector3D(1, 0, 0), _cameraTarget); m.appendRotation(-_panAngle, new Vector3D(0, 1, 0), _cameraTarget); matrix = m;
Также нужно создать геттеры и сеттеры для параметров камеры, чтобы пользователь мог их менять по необходимости:
- factor (множитель для величины поворота камеры, чем он больше, тем на больший угол будет поворачиваться камера за раз, значение по-умолчанию — .005),
- limit (величина "мертвой зоны" хода мыши — пространство, где камера не будет реагировать на поворот, чем больше значение, тем больше "мертвая зона", значение по-умолчанию — 50)
Для удобства управления камерой также создадим два дополнительных метода: changeTiltAngle (меняет угол наклона камеры вверх-вниз, в градусах), changePanAngle (меняет угол поворота камеры влево-вправо, в градусах).
Все подготовительные действия совершены. Теперь переходим к самому интересному — переопределим метод render камеры. Так как он должен вызываться в каждом кадре приложения, в нем мы и будем анимировать камеру в зависимости от положения мыши. Для этого определим величину на которую мышь отклонена от центра:
var dx:Number = _stage.mouseX - _maxX;
На основании этой величины при условии, что она лежит вне "мертвой зоны", мы будем менять положение нашей камеры:
if (Math.abs(dx) - _limit > .001) { var m:Matrix3D = matrix; m.appendRotation(dx * _factor, new Vector3D(0, 1, 0), _cameraTarget); matrix = m; }
Не забываем о логике родительского класса:
super.render();
Наш простой контроллер камеры, интегрированный в саму камеру для удобства готов. Вот код всего класса:
package { import alternativa.engine3d.core.Camera3D; import flash.display.Stage; import flash.geom.Matrix3D; import flash.geom.Vector3D; import flash.events.Event; public class HoverCamera3D extends Camera3D { private var _stage:Stage = null; private var _distance:Number = -200; private var _factor:Number = .005; private var _limit:Number = 50.; private var _maxX:Number = 0; private var _cameraTarget:Vector3D = new Vector3D(0, 0, 0); public function HoverCamera3D (stage:Stage, tiltAngle:Number = 30, panAngle:Number = 0, distance:Number = -200, target:Vector3D = null) { super(); if (!stage) throw new Error("HoverCamera3D must have link on stage object in passing parameters of constructor!"); _stage = stage; _cameraTarget = target || new Vector3D(0, 0, 0); _distance = distance; resizeListener(null); addEventListener(Event.RESIZE, resizeListener); var m:Matrix3D = matrix; m.appendTranslation(0, 0, _distance); m.appendRotation(-tiltAngle, new Vector3D(1, 0, 0), _cameraTarget); m.appendRotation(panAngle, new Vector3D(0, 1, 0), _cameraTarget); matrix = m; } public override function render():void { var dx:Number = _stage.mouseX - _maxX; if (Math.abs(dx) - _limit > .001) { var m:Matrix3D = matrix; m.appendRotation(dx * _factor, new Vector3D(0, 1, 0), _cameraTarget); matrix = m; } super.render(); } public function set factor (n:Number):void { _factor = n; } public function get factor ():Number { return _factor; } public function set limit (n:Number):void { _limit = n; } public function get limit ():Number { return _limit; } public function changeTiltAngle (n:Number):void { var m:Matrix3D = matrix; m.appendRotation(-n, new Vector3D(1, 0, 0), _cameraTarget); matrix = m; } public function changePanAngle (n:Number):void { var m:Matrix3D = matrix; m.appendRotation(n, new Vector3D(0, 1, 0), _cameraTarget); matrix = m; } private function resizeListener (e:Event):void { _maxX = _stage.stageWidth >> 1; } } }
Далее для работы нашей камеры можете её размещать в любом проекте Альтернативы 7 вместо стандартной камеры Camera3D. Контроллер SimpleObjectController в этом случае не нужен.
Вопрос оптимизации производительности данного примера оставлю открытым.
Чтобы сделать камеру реагирующей на события мыши относительно вертикали достаточно будет повторить все действия, связанные с горизонталью и для вертикали.
Код урока можно загрузить отсюда.