import { MathUtils } from '@uino/base-thing';
import { BaseComponent } from '@uino/thing';
import { Utils } from '../common/Utils';
/**
* 跟随组件类,用于以指定角度和距离平滑地跟随目标对象。
* @class FollowerComponent
* @memberof THING
* @extends THING.BaseComponent
* @public
*/
class FollowerComponent extends BaseComponent {
/**
* 用于以指定角度和距离平滑地跟随目标对象。
* @example
* camera.addComponent(THING.FollowerComponent, 'follower');
* camera.follower.start(THING.App.current.query('.car01')[0]);
*/
constructor() {
super();
this._tracingObject = null;
this._options = {};
this._init();
}
// #region Private Functions
// Update.
_update() {
var tracingObject = this._tracingObject;
// If the target had been destroyed then stop to follow
if (tracingObject.destroyed) {
this.stop();
return;
}
// Get the options
var options = this._options;
var direction = options['direction'];
var factor = options['factor'];
var speed = options['speed'];
// Get the center position to look at
var orientedBox = tracingObject.orientedBox;
var center = orientedBox.center;
var radius = orientedBox.radius;
var distance = radius * factor;
// Get the follower position
var position = MathUtils.addVector(center, MathUtils.scaleVector(direction, distance));
// Get the current camera position and target
var object = this.object;
var cameraPosition = MathUtils.lerpVector(object.position, position, speed);
var cameraTarget = MathUtils.lerpVector(object.target, center, speed);
object.position = cameraPosition;
object.target = cameraTarget;
// Check whether reach target
const epsilon = 0.0001;
const positionDistance = MathUtils.getDistance(cameraPosition, position);
const targetDistance = MathUtils.getDistance(cameraTarget, center);
if (targetDistance < epsilon && positionDistance < epsilon) {
// Check whether to stop when reach target
var once = options['once'];
if (once) {
this.stop();
}
}
}
// Initialize options.
_initOptions() {
this._options = {
horzAngle: 45,
vertAngle: 45,
direction: null,
factor: 2.5,
speed: 0.1,
};
}
// Update direction.
_updateDirection() {
var options = this._options;
options['direction'] = MathUtils.getDirectionFromAngles(options['horzAngle'], options['vertAngle']);
}
// Initialize.
_init() {
this._initOptions();
}
// #endregion
// #region BaseComponent Interface
onUpdate(deltaTime) {
if (this._tracingObject) {
this._update();
}
}
// #endregion
/**
* 停止跟随对象。
* @public
*/
stop() {
this._tracingObject = null;
this._initOptions();
}
/**
* 开始跟随对象。
* @param {THING.BaseObject} object 要跟随的对象。
* @param {object} options 选项。
* @public
*/
start(object, options = {}) {
if (!object) {
return;
}
// Stop current following action
this.stop();
// Parse options
this._options['horzAngle'] = Utils.parseValue(options['horzAngle'], this._options['horzAngle']);
this._options['vertAngle'] = Utils.parseValue(options['vertAngle'], this._options['vertAngle']);
this._options['factor'] = Utils.parseValue(options['factor'], this._options['factor']);
this._options['speed'] = Utils.parseValue(options['speed'], this._options['speed']);
this._options['once'] = Utils.parseValue(options['once'], false);
// Start to follow object
this._tracingObject = object;
// Update direction
this._updateDirection();
}
/**
* 获取正在跟随的对象。
* @type {THING.BaseObject}
* @public
*/
get tracingObject() {
return this._tracingObject;
}
/**
* 获取/设置水平角度。
* @type {number}
* @public
*/
get horzAngle() {
return this._options['horzAngle'];
}
set horzAngle(value) {
this._options['horzAngle'] = value;
this._updateDirection();
}
/**
* 获取/设置垂直角度。
* @type {number}
* @public
*/
get vertAngle() {
return this._options['vertAngle'];
}
set vertAngle(value) {
this._options['vertAngle'] = value;
this._updateDirection();
}
/**
* 获取/设置距离因子。
* @type {number}
* @public
*/
get factor() {
return this._options['factor'];
}
set factor(value) {
this._options['factor'] = value;
}
/**
* 获取/设置速度。
* @type {number}
* @public
*/
get speed() {
return this._options['speed'];
}
set speed(value) {
this._options['speed'] = value;
}
}
export { FollowerComponent }