living-together/source/Behavior/Tracking.ts

186 lines
5.8 KiB
TypeScript

import { Behavior } from "@Model/Behavior";
import { Group } from "@Model/Group";
import { Individual } from "@Model/Individual";
import { Model } from "@Model/Model";
type ITrackingBehaviorParameter = {
target: "CLG",
strength: "number",
range: "number",
angle: "number",
lock: "boolean"
}
type ITrackingBehaviorEvent = {}
class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEvent> {
public override behaviorId: string = "Tracking";
public override behaviorName: string = "$Title";
public override iconName: string = "Bullseye";
public override describe: string = "$Intro";
public override category: string = "$Interactive";
public override parameterOption = {
target: { type: "CLG", name: "$Target" },
lock: { type: "boolean", name: "$Lock", defaultValue: false },
range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 },
angle: { type: "number", name: "$Angle", defaultValue: 180, numberMin: 0, numberMax: 360, numberStep: 5 },
strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
};
private target: Individual | undefined = undefined;
private currentDistant: number = Infinity;
private angle2Vector(v1: number[], v2: number[]): number {
return Math.acos(
(v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) /
(
(v1[0] ** 2 + v1[1] ** 2 + v1[2] ** 2) ** 0.5 *
(v2[0] ** 2 + v2[1] ** 2 + v2[2] ** 2) ** 0.5
)
) * 180 / Math.PI;
}
private searchTarget(individual: Individual) {
for (let i = 0; i < this.parameter.target.objects.length; i++) {
const targetGroup = this.parameter.target.objects[i];
targetGroup.individuals.forEach((targetIndividual) => {
// 排除自己
if (targetIndividual === individual) return;
let dis = targetIndividual.distanceTo(individual);
if (dis < this.currentDistant && dis <= this.parameter.range) {
// 计算目标方位
const targetDir = [
targetIndividual.position[0] - individual.position[0],
targetIndividual.position[1] - individual.position[1],
targetIndividual.position[2] - individual.position[2]
];
// 计算角度
const angle = this.angle2Vector(individual.velocity, targetDir);
if (angle < (this.parameter.angle ?? 360) / 2) {
this.target = targetIndividual;
this.currentDistant = dis;
}
}
});
}
}
private clearTarget() {
this.target = undefined as Individual | undefined;
this.currentDistant = Infinity;
}
public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
this.clearTarget();
if (this.parameter.lock) {
let isValidTarget = false;
this.target = individual.getData("Tracking.lock.target");
if (this.target) {
// 校验目标所在的群是否仍是目标
let isInTarget = false;
for (let i = 0; i < this.parameter.target.objects.length; i++) {
if (this.parameter.target.objects[i].equal(this.target.group)) {
isInTarget = true;
break;
}
}
// 如果还在目标范围内,校验距离
if (isInTarget) {
let dis = individual.distanceTo(this.target);
// 校验成功
if (dis <= this.parameter.range) {
this.currentDistant = dis;
isValidTarget = true;
}
}
}
// 如果目标无效,尝试搜索新的目标
if (!isValidTarget) {
this.clearTarget();
this.searchTarget(individual);
// 如果成功搜索,缓存目标
if (this.target && this.currentDistant && this.currentDistant !== Infinity) {
individual.setData("Tracking.lock.target", this.target);
}
// 搜索失败,清除目标
else {
individual.setData("Tracking.lock.target", undefined);
}
}
}
else {
this.searchTarget(individual);
}
if (this.target && this.currentDistant && this.currentDistant !== Infinity) {
individual.applyForce(
(this.target.position[0] - individual.position[0]) * this.parameter.strength / this.currentDistant,
(this.target.position[1] - individual.position[1]) * this.parameter.strength / this.currentDistant,
(this.target.position[2] - individual.position[2]) * this.parameter.strength / this.currentDistant
);
}
}
public override terms: Record<string, Record<string, string>> = {
"$Title": {
"ZH_CN": "追踪",
"EN_US": "Tracking"
},
"$Target": {
"ZH_CN": "追踪目标",
"EN_US": "Tracking target"
},
"$Lock": {
"ZH_CN": "追踪锁定",
"EN_US": "Tracking lock"
},
"$Range": {
"ZH_CN": "追踪范围 (m)",
"EN_US": "Tracking range (m)"
},
"$Strength": {
"ZH_CN": "追踪强度系数",
"EN_US": "Tracking intensity coefficient"
},
"$Intro": {
"ZH_CN": "个体将主动向最近的目标群个体发起追踪",
"EN_US": "The individual will actively initiate tracking to the nearest target group individual"
},
"$Interactive": {
"ZH_CN": "交互",
"EN_US": "Interactive"
},
"$Angle": {
"ZH_CN": "可视角度",
"EN_US": "Viewing angle"
}
};
}
export { Tracking };