@matematrolii/sketchbook
Version:
3D matematrolii playground built on three.js and cannon.js
916 lines (809 loc) • 28.6 kB
text/typescript
import * as THREE from "three";
// @ts-ignore
import * as CANNON from "cannon";
import * as _ from "lodash";
import * as Utils from "../core/FunctionLibrary";
import { KeyBinding } from "../core/KeyBinding";
import { VectorSpringSimulator } from "../physics/spring_simulation/VectorSpringSimulator";
import { RelativeSpringSimulator } from "../physics/spring_simulation/RelativeSpringSimulator";
import { Idle } from "./character_states/Idle";
import { ICharacterAI } from "../interfaces/ICharacterAI";
import { World } from "../world/World";
import { ICharacterState } from "../interfaces/ICharacterState";
import { IWorldEntity } from "../interfaces/IWorldEntity";
import { Item } from "../items/Item";
import { CollisionGroups } from "../enums/CollisionGroups";
import { CapsuleCollider } from "../physics/colliders/CapsuleCollider";
import { GroundImpactData } from "./GroundImpactData";
import { ClosestObjectFinder } from "../core/ClosestObjectFinder";
import { Bullet } from "./Bullet";
import { EntityType } from "../enums/EntityType";
import { EventType } from "../enums/EventType";
import { ModelType } from "../enums/WorldType";
export class Character extends THREE.Object3D implements IWorldEntity {
public isHit: boolean = false;
public updateOrder: number = 1;
public entityType: EntityType = EntityType.Character;
public height: number = 0;
public tiltContainer: THREE.Group;
public modelContainer: THREE.Group;
public materials: THREE.Material[] = [];
public mixer: THREE.AnimationMixer;
public animations: any[];
// Movement
public acceleration: THREE.Vector3 = new THREE.Vector3();
public velocity: THREE.Vector3 = new THREE.Vector3();
public arcadeVelocityInfluence: THREE.Vector3 = new THREE.Vector3();
public velocityTarget: THREE.Vector3 = new THREE.Vector3();
public arcadeVelocityIsAdditive: boolean = false;
public defaultVelocitySimulatorDamping: number = 0.8;
public defaultVelocitySimulatorMass: number = 50;
public velocitySimulator: VectorSpringSimulator;
public moveSpeed: number = 4;
public angularVelocity: number = 0;
public orientation: THREE.Vector3 = new THREE.Vector3(0, 0, 1);
public orientationTarget: THREE.Vector3 = new THREE.Vector3(0, 0, 1);
public defaultRotationSimulatorDamping: number = 0.5;
public defaultRotationSimulatorMass: number = 10;
public rotationSimulator: RelativeSpringSimulator;
public viewVector: THREE.Vector3;
public actions: { [action: string]: KeyBinding };
public characterCapsule: CapsuleCollider;
// Ray casting
public rayResult: CANNON.RaycastResult = new CANNON.RaycastResult();
public rayHasHit: boolean = false;
public rayCastLength: number = 0.57;
public raySafeOffset: number = 0.03;
public wantsToJump: boolean = false;
public initJumpSpeed: number = -1;
public groundImpactData: GroundImpactData = new GroundImpactData();
public world: World;
public charState: ICharacterState;
public behaviour: ICharacterAI;
private physicsEnabled: boolean = true;
public shootDirection: THREE.Vector3 = new THREE.Vector3();
public shootVelo: number = 15;
public raycaster = new THREE.Raycaster();
public type = ModelType.PLAYER;
private gltf: any = null;
private bulletTimePassed: boolean = true;
constructor(gltf: any, texture: string, type: ModelType, name: string) {
super();
this.type = type;
this.gltf = gltf;
this.readCharacterData(gltf, texture);
this.setAnimations(gltf.animations);
// The visuals group is centered for easy character tilting
this.tiltContainer = new THREE.Group();
this.add(this.tiltContainer);
// Model container is used to reliably ground the character, as animation can alter the position of the model itself
this.modelContainer = new THREE.Group();
this.modelContainer.position.y = -0.57;
this.tiltContainer.add(this.modelContainer);
this.modelContainer.add(gltf.scene);
this.mixer = new THREE.AnimationMixer(gltf.scene);
this.velocitySimulator = new VectorSpringSimulator(
60,
this.defaultVelocitySimulatorMass,
this.defaultVelocitySimulatorDamping
);
this.rotationSimulator = new RelativeSpringSimulator(
60,
this.defaultRotationSimulatorMass,
this.defaultRotationSimulatorDamping
);
this.viewVector = new THREE.Vector3();
// Actions
this.actions = {
pick: new KeyBinding("KeyE"),
respawn: new KeyBinding("KeyR"),
up: new KeyBinding("KeyW"),
down: new KeyBinding("KeyS"),
left: new KeyBinding("KeyA"),
right: new KeyBinding("KeyD"),
run: new KeyBinding("ShiftLeft"),
jump: new KeyBinding("Space"),
primary: new KeyBinding("Mouse0"),
secondary: new KeyBinding("Mouse1"),
};
const radius = this.type === ModelType.ENEMY ? 0.35 : 0.2;
// Physics
// Player Capsule
this.characterCapsule = new CapsuleCollider({
mass: 1,
position: new CANNON.Vec3(),
height: 0.4,
radius,
segments: 3,
friction: 0.0,
name
});
// capsulePhysics.physical.collisionFilterMask = ~CollisionGroups.Trimesh;
this.characterCapsule.body.shapes.forEach((shape) => {
// tslint:disable-next-line: no-bitwise
shape.collisionFilterMask = ~CollisionGroups.TrimeshColliders;
});
this.characterCapsule.body.allowSleep = false;
(this.characterCapsule.body as any).userData = {
type,
name,
isHit: false
};
// Move character to different collision group for raycasting
this.characterCapsule.body.collisionFilterGroup = 2;
// Disable character rotation
this.characterCapsule.body.fixedRotation = true;
this.characterCapsule.body.updateMassProperties();
// Physics pre/post step callback bindings
this.characterCapsule.body.preStep = (body: CANNON.Body) => {
this.physicsPreStep(body, this);
};
this.characterCapsule.body.postStep = (body: CANNON.Body) => {
this.physicsPostStep(body, this);
};
// States
this.setState(new Idle(this));
}
public setAnimations(animations: []): void {
this.animations = animations;
}
public setArcadeVelocityInfluence(
x: number,
y: number = x,
z: number = x
): void {
this.arcadeVelocityInfluence.set(x, y, z);
}
public setViewVector(vector: THREE.Vector3): void {
this.viewVector.copy(vector).normalize();
}
/**
* Set state to the player. Pass state class (function) name.
* @param {function} State
*/
public setState(state: ICharacterState): void {
this.charState = state;
this.charState.onInputChange();
}
public setPosition(x: number, y: number, z: number): void {
if (this.physicsEnabled) {
this.characterCapsule.body.previousPosition = new CANNON.Vec3(x, y, z);
this.characterCapsule.body.position = new CANNON.Vec3(x, y, z);
this.characterCapsule.body.interpolatedPosition = new CANNON.Vec3(
x,
y,
z
);
} else {
this.position.x = x;
this.position.y = y;
this.position.z = z;
}
}
public resetVelocity(): void {
this.velocity.x = 0;
this.velocity.y = 0;
this.velocity.z = 0;
this.characterCapsule.body.velocity.x = 0;
this.characterCapsule.body.velocity.y = 0;
this.characterCapsule.body.velocity.z = 0;
this.velocitySimulator.init();
}
public setArcadeVelocityTarget(
velZ: number,
velX: number = 0,
velY: number = 0
): void {
this.velocityTarget.z = velZ;
this.velocityTarget.x = velX;
this.velocityTarget.y = velY;
}
public setOrientation(
vector: THREE.Vector3,
instantly: boolean = false
): void {
let lookVector = new THREE.Vector3().copy(vector).setY(0).normalize();
this.orientationTarget.copy(lookVector);
if (instantly) {
this.orientation.copy(lookVector);
}
}
public resetOrientation(): void {
const forward = Utils.getForward(this);
this.setOrientation(forward, true);
}
public setBehaviour(behaviour: ICharacterAI): void {
behaviour.character = this;
this.behaviour = behaviour;
}
public setPhysicsEnabled(value: boolean): void {
this.physicsEnabled = value;
if (value === true) {
this.world.physicsWorld.addBody(this.characterCapsule.body);
} else {
this.world.physicsWorld.remove(this.characterCapsule.body);
}
}
public readCharacterData(gltf: any, textureUrl: string): void {
const image = new Image();
image.crossOrigin = "Anonymous";
const texture = new THREE.Texture(image);
image.onload = () => {
texture.needsUpdate = true;
};
image.src = textureUrl;
texture.flipY = false;
const material = new THREE.MeshPhongMaterial({
shininess: 0,
map: texture,
});
//material.map.encoding = THREE.sRGBEncoding;
material.skinning = true;
material.map.flipY = false;
gltf.scene.traverse((child) => {
if (child.isMesh) {
//Utils.setupMeshProperties(child);
if (child.material.isGLTFSpecularGlossinessMaterial) {
child.onBeforeRender = function () {};
}
child.material = material;
child.castShadow = true;
child.receiveShadow = true;
this.materials.push(material);
}
});
}
public handleKeyboardEvent(
event: KeyboardEvent,
code: string,
pressed: boolean
): void {
// Free camera
if (code === "KeyC" && pressed === true && event.shiftKey === true) {
this.resetControls();
this.world.cameraOperator.characterCaller = this;
this.world.inputManager.setInputReceiver(this.world.cameraOperator);
} else if (code === "KeyR" && pressed === true) {
this.world.outOfBoundsRespawn(this.characterCapsule.body);
} else {
for (const action in this.actions) {
if (this.actions.hasOwnProperty(action)) {
const binding = this.actions[action];
if (_.includes(binding.eventCodes, code)) {
this.triggerAction(action, pressed);
}
}
}
}
}
public handleMouseButton(
event: MouseEvent,
code: string,
pressed: boolean
): void {
if (code === "mouse0" && pressed === true && this.bulletTimePassed) {
// Shoot balls
const bullet = new Bullet();
this.world.add(bullet);
let x = this.position.x;
let y = this.position.y;
let z = this.position.z;
const cameraDirection = this.getCameraRelativeVector();
this.shootDirection.copy(cameraDirection);
this.setOrientation(cameraDirection, true);
bullet.bulletBox.body.velocity.set(
this.shootDirection.x * this.shootVelo,
this.shootDirection.y * this.shootVelo,
this.shootDirection.z * this.shootVelo
);
// Move the ball outside the player sphere
x +=
this.shootDirection.x *
(this.characterCapsule.options.radius +
bullet.bulletBox.options.radius);
y += this.shootDirection.y;
z +=
this.shootDirection.z *
(this.characterCapsule.options.radius +
bullet.bulletBox.options.radius);
bullet.bulletBox.body.position.set(x, y, z);
const that = this;
this.bulletTimePassed = false;
const timeout = setTimeout(() => {
that.bulletTimePassed = true;
clearTimeout(timeout);
}, 1000);
} else {
for (const action in this.actions) {
if (this.actions.hasOwnProperty(action)) {
const binding = this.actions[action];
if (_.includes(binding.eventCodes, code)) {
this.triggerAction(action, pressed);
}
}
}
}
}
public handleMouseMove(
event: MouseEvent,
deltaX: number,
deltaY: number
): void {
this.world.cameraOperator.move(deltaX, deltaY);
}
public handleMouseWheel(event: WheelEvent, value: number): void {
//this.world.scrollTheTimeScale(value);
}
public triggerAction(actionName: string, value: boolean): void {
// Get action and set it's parameters
let action = this.actions[actionName];
if (action && action.isPressed !== value) {
// Set value
action.isPressed = value;
// Reset the 'just' attributes
action.justPressed = false;
action.justReleased = false;
// Set the 'just' attributes
if (value) action.justPressed = true;
else action.justReleased = true;
// Tell player to handle states according to new input
this.charState.onInputChange();
// Reset the 'just' attributes
action.justPressed = false;
action.justReleased = false;
}
}
public takeControl(): void {
if (this.world !== undefined) {
this.world.inputManager.setInputReceiver(this);
} else {
console.warn(
"Attempting to take control of a character that doesn't belong to a world."
);
}
}
public resetControls(): void {
for (const action in this.actions) {
if (this.actions.hasOwnProperty(action)) {
this.triggerAction(action, false);
}
}
}
public update(timeStep: number): void {
if (this.behaviour) this.behaviour?.update(timeStep);
if(this.charState) this.charState?.update(timeStep);
if (this.physicsEnabled) this.springMovement(timeStep);
if (this.physicsEnabled) this.springRotation(timeStep);
if (this.physicsEnabled) this.rotateModel();
if (this.mixer !== undefined) this.mixer.update(timeStep);
// Sync physics/graphics
if (this.physicsEnabled) {
this.position.set(
this.characterCapsule.body.interpolatedPosition.x,
this.characterCapsule.body.interpolatedPosition.y,
this.characterCapsule.body.interpolatedPosition.z
);
} else if (this.characterCapsule) {
let newPos = new THREE.Vector3();
this.getWorldPosition(newPos);
this.characterCapsule.body.position.copy(Utils.cannonVector(newPos));
this.characterCapsule.body.interpolatedPosition.copy(
Utils.cannonVector(newPos)
);
}
this.updateMatrixWorld();
}
public inputReceiverInit(): void {
this.world.cameraOperator.setRadius(1.6, true);
this.world.cameraOperator.followMode = false;
// this.world.dirLight.target = this;
}
public inputReceiverUpdate(timeStep: number): void {
// Look in camera's direction
this.viewVector = new THREE.Vector3().subVectors(
this.position,
this.world.camera.position
);
this.getWorldPosition(this.world.cameraOperator.target);
}
public setAnimation(clipName: string, fadeIn: number): number {
if (this.mixer !== undefined) {
// gltf
let clip = THREE.AnimationClip.findByName(this.animations, clipName);
let action = this.mixer.clipAction(clip);
if (action === null) {
//console.error(`Animation ${clipName} not found!`);
return 0;
}
action.setEffectiveWeight(1);
action.enabled = true;
this.mixer.stopAllAction();
action.fadeIn(fadeIn);
action.play();
return action.getClip().duration;
}
}
public springMovement(timeStep: number): void {
// Simulator
this.velocitySimulator.target.copy(this.velocityTarget);
this.velocitySimulator.simulate(timeStep);
// Update values
this.velocity.copy(this.velocitySimulator.position);
this.acceleration.copy(this.velocitySimulator.velocity);
}
public springRotation(timeStep: number): void {
// Spring rotation
// Figure out angle between current and target orientation
let angle = Utils.getSignedAngleBetweenVectors(
this.orientation,
this.orientationTarget
);
// Simulator
this.rotationSimulator.target = angle;
this.rotationSimulator.simulate(timeStep);
let rot = this.rotationSimulator.position;
// Updating values
this.orientation.applyAxisAngle(new THREE.Vector3(0, 1, 0), rot);
this.angularVelocity = this.rotationSimulator.velocity;
}
public getLocalMovementDirection(): THREE.Vector3 {
const positiveX = this.actions.right.isPressed ? -1 : 0;
const negativeX = this.actions.left.isPressed ? 1 : 0;
const positiveZ = this.actions.up.isPressed ? 1 : 0;
const negativeZ = this.actions.down.isPressed ? -1 : 0;
return new THREE.Vector3(
positiveX + negativeX,
0,
positiveZ + negativeZ
).normalize();
}
public getCameraRelativeMovementVector(): THREE.Vector3 {
const localDirection = this.getLocalMovementDirection();
const flatViewVector = new THREE.Vector3(
this.viewVector.x,
0,
this.viewVector.z
).normalize();
return Utils.appplyVectorMatrixXZ(flatViewVector, localDirection);
}
public getCameraRelativeVector(): THREE.Vector3 {
const localDirection = new THREE.Vector3(0, 0, 1).normalize();
const flatViewVector = new THREE.Vector3(
this.viewVector.x,
0,
this.viewVector.z
).normalize();
return Utils.appplyVectorMatrixXZ(flatViewVector, localDirection);
}
public setCameraRelativeOrientationTarget(): void {
let moveVector = this.getCameraRelativeMovementVector();
if (moveVector.x === 0 && moveVector.y === 0 && moveVector.z === 0) {
this.setOrientation(this.orientation);
} else {
this.setOrientation(moveVector);
}
}
public rotateModel(): void {
this.lookAt(
this.position.x + this.orientation.x,
this.position.y + this.orientation.y,
this.position.z + this.orientation.z
);
this.tiltContainer.rotation.z =
-this.angularVelocity * 2.3 * this.velocity.length();
this.tiltContainer.position.setY(
Math.cos(Math.abs(this.angularVelocity * 2.3 * this.velocity.length())) /
2 -
0.5
);
}
public jump(initJumpSpeed: number = -1): void {
this.wantsToJump = true;
this.initJumpSpeed = initJumpSpeed;
}
public findItemToPick(): void {
let itemFinder = new ClosestObjectFinder<Item>(this.position, 1);
if (this.world) {
this.world.items.forEach((item) => {
itemFinder.consider(item, item.position);
});
if (itemFinder.closestObject !== undefined) {
let item = itemFinder.closestObject;
if (!item.isPicked) {
item.isPicked = true;
item.removeFromWorld(this.world);
this.world.triggerEvent({
type: EventType.ITEM,
targets: [],
});
}
}
}
}
public physicsPreStep(body: CANNON.Body, character: Character): void {
character.feetRaycast();
}
public feetRaycast(): void {
// Player ray casting
// Create ray
let body = this.characterCapsule.body;
const start = new CANNON.Vec3(
body.position.x,
body.position.y,
body.position.z
);
const end = new CANNON.Vec3(
body.position.x,
body.position.y - this.rayCastLength - this.raySafeOffset,
body.position.z
);
// Raycast options
const rayCastOptions = {
collisionFilterMask: CollisionGroups.Default,
skipBackfaces: true /* ignore back faces */,
};
// Cast the ray
this.rayHasHit = this.world.physicsWorld.raycastClosest(
start,
end,
rayCastOptions,
this.rayResult
);
}
public physicsPostStep(body: CANNON.Body, character: Character): void {
// Get velocities
let simulatedVelocity = new THREE.Vector3(
body.velocity.x,
body.velocity.y,
body.velocity.z
);
// Take local velocity
let arcadeVelocity = new THREE.Vector3()
.copy(character.velocity)
.multiplyScalar(character.moveSpeed);
// Turn local into global
arcadeVelocity = Utils.appplyVectorMatrixXZ(
character.orientation,
arcadeVelocity
);
let newVelocity = new THREE.Vector3();
// Additive velocity mode
if (character.arcadeVelocityIsAdditive) {
newVelocity.copy(simulatedVelocity);
let globalVelocityTarget = Utils.appplyVectorMatrixXZ(
character.orientation,
character.velocityTarget
);
let add = new THREE.Vector3()
.copy(arcadeVelocity)
.multiply(character.arcadeVelocityInfluence);
if (
Math.abs(simulatedVelocity.x) <
Math.abs(globalVelocityTarget.x * character.moveSpeed) ||
Utils.haveDifferentSigns(simulatedVelocity.x, arcadeVelocity.x)
) {
newVelocity.x += add.x;
}
if (
Math.abs(simulatedVelocity.y) <
Math.abs(globalVelocityTarget.y * character.moveSpeed) ||
Utils.haveDifferentSigns(simulatedVelocity.y, arcadeVelocity.y)
) {
newVelocity.y += add.y;
}
if (
Math.abs(simulatedVelocity.z) <
Math.abs(globalVelocityTarget.z * character.moveSpeed) ||
Utils.haveDifferentSigns(simulatedVelocity.z, arcadeVelocity.z)
) {
newVelocity.z += add.z;
}
} else {
newVelocity = new THREE.Vector3(
THREE.MathUtils.lerp(
simulatedVelocity.x,
arcadeVelocity.x,
character.arcadeVelocityInfluence.x
),
THREE.MathUtils.lerp(
simulatedVelocity.y,
arcadeVelocity.y,
character.arcadeVelocityInfluence.y
),
THREE.MathUtils.lerp(
simulatedVelocity.z,
arcadeVelocity.z,
character.arcadeVelocityInfluence.z
)
);
}
// If we're hitting the ground, stick to ground
if (character.rayHasHit) {
// Flatten velocity
newVelocity.y = 0;
// Move on top of moving objects
if (character.rayResult.body.mass > 0) {
let pointVelocity = new CANNON.Vec3();
character.rayResult.body.getVelocityAtWorldPoint(
character.rayResult.hitPointWorld,
pointVelocity
);
newVelocity.add(Utils.threeVector(pointVelocity));
}
// Measure the normal vector offset from direct "up" vector
// and transform it into a matrix
let up = new THREE.Vector3(0, 1, 0);
let normal = new THREE.Vector3(
character.rayResult.hitNormalWorld.x,
character.rayResult.hitNormalWorld.y,
character.rayResult.hitNormalWorld.z
);
let q = new THREE.Quaternion().setFromUnitVectors(up, normal);
let m = new THREE.Matrix4().makeRotationFromQuaternion(q);
// Rotate the velocity vector
newVelocity.applyMatrix4(m);
// Compensate for gravity
// newVelocity.y -= body.world.physicsWorld.gravity.y / body.character.world.physicsFrameRate;
// Apply velocity
body.velocity.x = newVelocity.x;
body.velocity.y = newVelocity.y;
body.velocity.z = newVelocity.z;
// Ground character
body.position.y =
character.rayResult.hitPointWorld.y +
character.rayCastLength +
newVelocity.y / character.world.physicsFrameRate;
} else {
// If we're in air
body.velocity.x = newVelocity.x;
body.velocity.y = newVelocity.y;
body.velocity.z = newVelocity.z;
// Save last in-air information
character.groundImpactData.velocity.x = body.velocity.x;
character.groundImpactData.velocity.y = body.velocity.y;
character.groundImpactData.velocity.z = body.velocity.z;
}
// Jumping
if (character.wantsToJump) {
// If initJumpSpeed is set
if (character.initJumpSpeed > -1) {
// Flatten velocity
body.velocity.y = 0;
let speed = Math.max(
character.velocitySimulator.position.length() * 4,
character.initJumpSpeed
);
body.velocity = Utils.cannonVector(
character.orientation.clone().multiplyScalar(speed)
);
} else {
// Moving objects compensation
let add = new CANNON.Vec3();
character.rayResult.body.getVelocityAtWorldPoint(
character.rayResult.hitPointWorld,
add
);
body.velocity.vsub(add, body.velocity);
}
// Add positive vertical velocity
body.velocity.y += 4;
// Move above ground by 2x safe offset value
body.position.y += character.raySafeOffset * 2;
// Reset flag
character.wantsToJump = false;
}
}
public collideListener(colider: any) {
//console.log('Character colider: ', colider.body.characterType);
const that = this;
//console.log('Character colider 2: ', colider.body.characterType);
if (colider.body?.userData?.type === ModelType.ENEMY) {
// TODO: should work once I put isHit into userData
const enemyIsHit = colider.body?.userData?.isHit;
//console.log('Character Enemy found')
setTimeout(function() {
if (that && that.world && that.characterCapsule && !that.isHit && !enemyIsHit) {
that.isHit = true;
that.characterCapsule.body.removeEventListener("collide", that.collideListener);
// Player dies
that.world.triggerEvent({
type: EventType.PLAYER,
targets: [],
});
that.world.remove(that);
}
}, 0);
}
}
public addToWorld(world: World): void {
if (_.includes(world.characters, this)) {
console.warn("Adding character to a world in which it already exists.");
} else {
// Set world
this.world = world;
// Register character
world.characters.push(this);
// Register for bullet collision
world.characterNames[(this.characterCapsule.body as any).userData.name] = this;
// Register physics
world.physicsWorld.addBody(this.characterCapsule.body);
// Add to graphicsWorld
world.graphicsWorld.add(this);
if (this.type === ModelType.PLAYER) {
this.characterCapsule.body.addEventListener("collide", this.collideListener.bind(this));
}
// Shadow cascades
this.materials.forEach((mat) => {
world.sky.csm.setupMaterial(mat);
});
}
}
public removeFromWorld(world: World): void {
// TODO: dealocate memory properly https://stackoverflow.com/questions/20997669/memory-leak-in-three-js
if (!_.includes(world.characters, this)) {
console.warn(
"Removing character from a world in which it isn't present."
);
} else {
if (world.inputManager.inputReceiver === this) {
world.inputManager.inputReceiver = undefined;
}
this.world = undefined;
// Remove from characters
_.pull(world.characters, this);
// Remove physics
world.physicsWorld.remove(this.characterCapsule.body);
// Remove visuals
world.graphicsWorld.remove(this);
this.modelContainer.remove(this.gltf.scene);
this.tiltContainer.remove(this.modelContainer);
this.remove(this.tiltContainer);
this.materials.forEach(material => material.dispose());
if (this.behaviour) {
this.behaviour.character = undefined;
this.behaviour = undefined;
}
this.materials = undefined;
this.modelContainer = undefined;
this.tiltContainer = undefined;
this.mixer = undefined;
this.velocitySimulator = undefined;
this.rotationSimulator = undefined;
this.viewVector = undefined;
this.characterCapsule = undefined;
this.actions = undefined;
this.isHit = undefined;
this.updateOrder = undefined;
this.entityType = undefined;
this.height = undefined;
this.animations = undefined;
this.acceleration = undefined;
this.velocity = undefined;
this.arcadeVelocityInfluence = undefined;
this.velocityTarget = undefined;
this.arcadeVelocityIsAdditive = undefined;
this.defaultVelocitySimulatorDamping = undefined;
this.defaultVelocitySimulatorMass = undefined;
this.moveSpeed = undefined;
this.angularVelocity = undefined;
this.orientation = undefined;
this.orientationTarget = undefined;
this.defaultRotationSimulatorDamping = undefined;
this.defaultRotationSimulatorMass = undefined;
this.rayResult = undefined;
this.rayHasHit = undefined;
this.rayCastLength = undefined;
this.raySafeOffset = undefined;
this.wantsToJump = undefined;
this.initJumpSpeed = undefined;
this.groundImpactData = undefined;
this.charState = undefined;
this.behaviour = undefined;
this.physicsEnabled = undefined;
this.shootDirection = undefined;
this.shootVelo = undefined;
this.raycaster = undefined;
this.type = undefined;
}
}
}