Add dynamics behavior

This commit is contained in:
MrKBear 2022-03-30 16:19:29 +08:00
parent c811b5b1e5
commit e5ae05e737
7 changed files with 179 additions and 24 deletions

View File

@ -1,13 +1,10 @@
import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior";
import { Template } from "./Template";
import { Dynamics } from "./Dynamics";
const AllBehaviors: IAnyBehaviorRecorder[] = new Array(4).fill(0).map((_, i) => {
let behavior = new BehaviorRecorder(Template);
behavior.behaviorId = behavior.behaviorId + i;
behavior.behaviorName = behavior.behaviorName + Math.random().toString(36).slice(-6);
behavior.category = "Category" + Math.floor(Math.random() * 3).toString();
return behavior;
});
const AllBehaviors: IAnyBehaviorRecorder[] = [
new BehaviorRecorder(Dynamics)
]
/**
*
@ -52,4 +49,6 @@ function categoryBehaviors(behaviors: IAnyBehaviorRecorder[]): ICategory[] {
return res;
}
console.log(AllBehaviorsWithCategory)
export { AllBehaviors, AllBehaviorsWithCategory, ICategory as ICategoryBehavior };

122
source/Behavior/Dynamics.ts Normal file
View File

@ -0,0 +1,122 @@
import { Behavior } from "@Model/Behavior";
import Group from "@Model/Group";
import Individual from "@Model/Individual";
import { Model } from "@Model/Model";
type IDynamicsBehaviorParameter = {
mass: "number",
maxAcceleration: "number",
maxVelocity: "number",
resistance: "number"
}
type IDynamicsBehaviorEvent = {}
class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEvent> {
public override behaviorId: string = "Dynamics";
public override behaviorName: string = "$Title";
public override iconName: string = "Running";
public override describe: string = "$Intro";
public override category: string = "$Physics";
public override parameterOption = {
mass: {
name: "$Mass",
type: "number",
defaultValue: 1,
numberStep: .01,
numberMin: .001
},
maxAcceleration: {
name: "$Max.Acceleration",
type: "number",
defaultValue: 0.5,
numberStep: .01,
numberMin: 0
},
maxVelocity: {
name: "$Max.Velocity",
type: "number",
defaultValue: 0.5,
numberStep: .01,
numberMin: 0
},
resistance: {
name: "$Resistance",
type: "number",
defaultValue: .01,
numberStep: .001,
numberMin: 0
}
};
public override terms: Record<string, Record<string, string>> = {
"$Title": {
"ZH_CN": "动力学",
"EN_US": "Dynamics"
},
"$Intro": {
"ZH_CN": "一切可以运动物体的必要行为,执行物理法则。",
"EN_US": "All necessary behaviors that can move objects and implement the laws of physics."
},
"$Mass": {
"ZH_CN": "质量 (Kg)",
"EN_US": "Mass (Kg)"
},
"$Max.Acceleration": {
"ZH_CN": "最大加速度 (m/s²)",
"EN_US": "Maximum acceleration (m/s²)"
},
"$Max.Velocity": {
"ZH_CN": "最大速度 (m/s)",
"EN_US": "Maximum velocity (m/s)"
},
"$Physics": {
"ZH_CN": "物理",
"EN_US": "Physics"
}
};
public override finalEffect(individual: Individual, group: Group, model: Model, t: number): void {
// 计算当前速度
const currentV = individual.vectorLength(individual.velocity);
// 计算阻力
const resistance = Math.max(1 - currentV * currentV * this.parameter.resistance, 0);
// 计算加速度
individual.acceleration[0] = individual.force[0] * resistance / this.parameter.mass;
individual.acceleration[1] = individual.force[1] * resistance / this.parameter.mass;
individual.acceleration[2] = individual.force[2] * resistance / this.parameter.mass;
// 加速度约束
const overA = Math.max(individual.vectorLength(individual.acceleration) - this.parameter.maxAcceleration, 0);
individual.acceleration[0] = individual.acceleration[0] - individual.acceleration[0] * overA;
individual.acceleration[1] = individual.acceleration[1] - individual.acceleration[1] * overA;
individual.acceleration[2] = individual.acceleration[2] - individual.acceleration[2] * overA;
// 计算速度
individual.velocity[0] = individual.velocity[0] + individual.acceleration[0] * t;
individual.velocity[1] = individual.velocity[1] + individual.acceleration[1] * t;
individual.velocity[2] = individual.velocity[2] + individual.acceleration[2] * t;
// 速度约束
const overV = Math.max(individual.vectorLength(individual.velocity) - this.parameter.maxVelocity, 0);
individual.velocity[0] = individual.velocity[0] - individual.velocity[0] * overV;
individual.velocity[1] = individual.velocity[1] - individual.velocity[1] * overV;
individual.velocity[2] = individual.velocity[2] - individual.velocity[2] * overV;
// 应用速度
individual.position[0] = individual.position[0] + individual.velocity[0] * t;
individual.position[1] = individual.position[1] + individual.velocity[1] * t;
individual.position[2] = individual.position[2] + individual.velocity[2] * t;
};
}
export { Dynamics };

View File

@ -120,7 +120,7 @@ class BehaviorList extends Component<IBehaviorListProps & IMixinSettingProps> {
</div>
<div className="behavior-content-view">
{this.renderTerm(behavior, name, "title-view", needLocal)}
{this.renderTerm(behavior, info, "info-view", needLocal)}
{this.renderTerm(behavior, info, "info-view", true)}
</div>
<div className="behavior-action-view">
{this.renderActionButton(behavior)}

View File

@ -64,7 +64,7 @@ interface IBehaviorParameterOptionItem<T extends IParamType = IParamType> {
/**
*
*/
type: T;
type: T | string;
/**
*
@ -269,6 +269,7 @@ class BehaviorRecorder<
this.behaviorId = this.behaviorInstance.behaviorId;
this.behaviorName = this.behaviorInstance.behaviorName;
this.describe = this.behaviorInstance.describe;
this.category = this.behaviorInstance.category;
this.terms = this.behaviorInstance.terms;
}
}
@ -371,7 +372,7 @@ class Behavior<
* @param model
* @param t
*/
public beforeEffect(individual: Individual, group: Group, model: Model, t: number): void {};
public effect(individual: Individual, group: Group, model: Model, t: number): void {};
/**
*
@ -380,7 +381,7 @@ class Behavior<
* @param model
* @param t
*/
public effect(individual: Individual, group: Group, model: Model, t: number): void {};
public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {};
/**
*
@ -389,7 +390,7 @@ class Behavior<
* @param model
* @param t
*/
public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {};
public finalEffect(individual: Individual, group: Group, model: Model, t: number): void {};
}

View File

@ -330,7 +330,7 @@ class Group extends CtrlObject {
*
* @param
*/
public runner(t: number, effectType: "beforeEffect" | "effect" | "afterEffect" ): void {
public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
this.individuals.forEach((individual) => {
for(let j = 0; j < this.behaviors.length; j++) {
this.behaviors[j][effectType](individual, this, this.model, t);

View File

@ -12,9 +12,9 @@ class Individual {
* @param y y
* @param z z
*/
public static vectorLength(x: number[]): number;
public static vectorLength(x: number, y: number, z: number): number;
public static vectorLength(x: number | number[], y?: number, z?: number): number {
public vectorLength(x: number[]): number;
public vectorLength(x: number, y: number, z: number): number;
public vectorLength(x: number | number[], y?: number, z?: number): number {
if (Array.isArray(x)) {
return ((x[0] ?? 0)**2 + (x[1] ?? 0)**2 + (x[2] ?? 0)**2)**.5;
} else {
@ -28,10 +28,10 @@ class Individual {
* @param y y
* @param z z
*/
public static vectorNormalize(x: number[]): [number, number, number];
public static vectorNormalize(x: number, y: number, z: number): [number, number, number];
public static vectorNormalize(x: number | number[], y?: number, z?: number): [number, number, number] {
let length = Individual.vectorLength(x as number, y as number, z as number);
public vectorNormalize(x: number[]): [number, number, number];
public vectorNormalize(x: number, y: number, z: number): [number, number, number];
public vectorNormalize(x: number | number[], y?: number, z?: number): [number, number, number] {
let length = this.vectorLength(x as number, y as number, z as number);
if (Array.isArray(x)) {
return [
(x[0] ?? 0) / length,
@ -52,6 +52,39 @@ class Individual {
*/
public position: number[] = [0, 0, 0];
/**
*
*/
public velocity: number[] = [0, 0, 0];
/**
*
*/
public acceleration: number[] = [0, 0, 0];
/**
*
*/
public force: number[] = [0, 0, 0];
/**
*
*/
public applyForce(x: number[]): [number, number, number];
public applyForce(x: number, y: number, z: number): [number, number, number];
public applyForce(x: number | number[], y?: number, z?: number): [number, number, number] {
if (Array.isArray(x)) {
this.force[0] += x[0] ?? 0;
this.force[1] += x[1] ?? 0;
this.force[2] += x[2] ?? 0;
} else {
this.force[0] += x ?? 0;
this.force[1] += y ?? 0;
this.force[2] += z ?? 0;
}
return this.force as [number, number, number];
}
/**
*
*/
@ -107,7 +140,7 @@ class Individual {
* @param position
*/
public distanceTo(position: Individual | number[]): number {
return Individual.vectorLength(this.vectorTo(position));
return this.vectorLength(this.vectorTo(position));
}
/**

View File

@ -258,7 +258,7 @@ class Model extends Emitter<ModelEvent> {
for (let i = 0; i < this.objectPool.length; i++) {
let object = this.objectPool[i];
if (object instanceof Group && object.update) {
object.runner(t, "beforeEffect");
object.runner(t, "effect");
}
}
@ -266,7 +266,7 @@ class Model extends Emitter<ModelEvent> {
for (let i = 0; i < this.objectPool.length; i++) {
let object = this.objectPool[i];
if (object instanceof Group && object.update) {
object.runner(t, "effect");
object.runner(t, "afterEffect");
}
}
@ -274,7 +274,7 @@ class Model extends Emitter<ModelEvent> {
for (let i = 0; i < this.objectPool.length; i++) {
let object = this.objectPool[i];
if (object instanceof Group && object.update) {
object.runner(t, "afterEffect");
object.runner(t, "finalEffect");
}
}