import { Utils } from '@uino/base-thing';
import { MathUtils, BaseComponent, EventType } from '@uino/thing';
const maxPitch = (85 / 180) * Math.PI;
// the camera's position will be changed by tween when it be attached,
// so we should make the dirty state for a period of time to ensure
// the camera reach a stable state
const attachingTime = 1;
function _initprotectedMembers(that, param) {
that[that._protected] = {};
const _protected = that[that._protected];
_protected._attachingTime = 0;
_protected._objectName = '';
_protected._attachCamera = null;
_protected._attaching = false;
_protected._yaw = 0;
_protected._yawSpeed = 1;
_protected._pitch = 0;
_protected._pitchSpeed = 1;
_protected._mouseMoveX = 0.0;
_protected._mouseMoveY = 0.0;
_protected._mouseOppositeMode = false;
_protected._minPitch = -maxPitch;
_protected._maxPitch = maxPitch;
_protected._dirty = false;
_protected.checkAttaching = (time) => {
if (_protected._attaching) {
_protected._attachingTime += time;
if (_protected._attachingTime >= attachingTime) {
_protected._attachingTime = 0;
_protected._attaching = false;
}
}
return _protected._attaching;
};
_protected.resetPitchByAttachCamera = () => {
let cameraForward = _protected._attachCamera.forward;
cameraForward = MathUtils.normalizeVector(cameraForward);
let cameraForwardWithZeroY = [cameraForward[0], 0, cameraForward[2]];
cameraForwardWithZeroY = MathUtils.normalizeVector(cameraForwardWithZeroY);
let dot = MathUtils.dotVector(cameraForward, cameraForwardWithZeroY);
_protected._pitch = MathUtils.acos(dot);
if (cameraForward[1] < 0) {
_protected._pitch = -_protected._pitch;
}
}
_protected.resetYawByAttachCamera = () => {
let cameraForward = _protected._attachCamera.forward;
cameraForward = MathUtils.normalizeVector(cameraForward);
let cameraForwardWithZeroY = [cameraForward[0], 0, cameraForward[2]];
cameraForwardWithZeroY = MathUtils.normalizeVector(cameraForwardWithZeroY);
let vecNorth = [0, 0, -1];
let dot = MathUtils.dotVector(vecNorth, cameraForwardWithZeroY);
_protected._yaw = MathUtils.acos(dot);
if (cameraForwardWithZeroY[0] > 0) {
_protected._yaw = 2 * Math.PI - _protected._yaw;
}
}
_protected.resetCameraTarget = () => {
if (!!_protected._attachCamera) {
let pos = _protected._attachCamera.position;
let dir = _protected._attachCamera.forward;
// let dis = _protected._attachCamera.distance;
// 使用20替代dis,避免因为前进导致dis越来越小,从而引起如下的bug
// bug: 长按W前进,然后按下Q转向一段时间抬起,按下D右移,抬起后方向突变
let delta_vec = MathUtils.scaleVector(dir, 20);
_protected._attachCamera.target = MathUtils.addVector(pos, delta_vec);
}
}
_protected.resetYaw = () => {
var preX = _protected._mouseMoveX;
if (!!_protected._mouseOppositeMode) {
preX = -preX;
}
var yawDelta = preX * _protected._yawSpeed;
_protected._yaw = _protected._yaw + yawDelta;
_protected._dirty = true;
}
_protected.resetPitch = () => {
var preY = _protected._mouseMoveY;
if (!!_protected._mouseOppositeMode) {
preY = -preY;
}
var pitchDelta = preY * _protected._pitchSpeed;
_protected._pitch = _protected._pitch + pitchDelta;
if (_protected._pitch > _protected._maxPitch) {
_protected._pitch = _protected._maxPitch;
}
else if (_protected._pitch <= _protected._minPitch) {
_protected._pitch = _protected._minPitch;
}
_protected._dirty = true;
}
}
/**
* @class CameraAttachedComponent
* @summary 相机附加组件,用于将摄像机绑定到某个对象上并跟随其变换。
* 提供摄像机偏航(Yaw)和俯仰(Pitch)的角度控制接口,支持鼠标反向模式,
* 是 FlyControlComponent 和第三人称摄像机控制的底层基础组件。
* 组件会将目标对象的变换应用到绑定的摄像机,使摄像机的朝向和位置与对象保持关联。
* @memberof THING
* @extends THING.BaseComponent
* @public
*/
class CameraAttachedComponent extends BaseComponent {
static beAttached = false;
/**
* 相机附加组件类构造函数,用于管理相机附加组件。
* @param {object} param 参数列表
*/
constructor(param = {}) {
super(param);
this._protected = Symbol('protected');
_initprotectedMembers(this, param);
}
/**
* 设置鼠标反向模式。
* 当反向为true时,鼠标向右转动时,玩家将向左转动。
* 俯仰操作也会相应反向。
* @param {bool} opposite 是否启用鼠标反向模式
*/
setMouseOppositeMode(opposite) {
var _protected = this[this._protected];
_protected._mouseOppositeMode = opposite;
}
/**
* 设置偏航速度
* @param {number} speed 相机水平旋转速度
*/
setYawSpeed(speed) {
var _protected = this[this._protected];
_protected._yawSpeed = speed;
}
/**
* 设置俯仰速度
* @param {number} speed 相机垂直旋转速度
*/
setPitchSpeed(speed) {
var _protected = this[this._protected];
_protected._pitchSpeed = speed;
}
/**
* 设置附加相机
* @param {Thing.Camera} attachCamera 要附加的相机
*/
setAttachCamera(attachCamera) {
if (!!CameraAttachedComponent.beAttached) {
Utils.error("it already has camera be attached!!");
return;
}
CameraAttachedComponent.beAttached = true;
var _protected = this[this._protected];
_protected._attachCamera = attachCamera;
_protected._oriCameraEnable = _protected._attachCamera.enable;
_protected._attachCamera.enable = false;
_protected.resetYawByAttachCamera();
_protected.resetPitchByAttachCamera();
_protected.resetCameraTarget();
_protected._attaching = true;
_protected._dirty = true;
// this.addEventListener('change', (ev) => {
// var _position = [];
// var _target = [];
// this.object.getWorldPosition(_position);
// this.object.control.getTarget(_target);
// this.app.trigger(EventType.CameraChange, {
// position: _position,
// target: _target
// });
// });
// this.addEventListener('wheel', (ev) => {
// var _position = [];
// var _target = [];
// this.object.getWorldPosition(_position);
// this.object.control.getTarget(_target);
// this.app.trigger(EventType.CameraChange, {
// position: _position,
// target: _target
// });
// });
}
onAdd(object) {
super.onAdd(object);
}
onRemove() {
var _protected = this[this._protected];
if (!!_protected._attachCamera) {
_protected._attachCamera.enable = _protected._oriCameraEnable;
}
_protected.resetCameraTarget();
CameraAttachedComponent.beAttached = false;
super.onRemove();
}
onUpdate(deltaTime) {
var _protected = this[this._protected];
if (!_protected._attachCamera) {
return;
}
_protected.resetCameraTarget();
_protected._attachCamera.onUpdate(0);
_protected._dirty = _protected.checkAttaching(deltaTime);
}
}
export { CameraAttachedComponent }