Utils / TweenUtil
TweenUtil Class
Animation (from in between) is a concept that allows you to change property of objects in a smooth way. You just need to tell it which property to change, what final values they should have when the session ends, and how long this will take, The interpolation engine will be responsible for calculating the values from the starting point to the ending point.
Usage example: create a script named TweenExample, place it in the object bar, open the script, modify the original content as follows, save and run the game, press G key, a missile will be randomly generate in the scene and fly to the destination according to the set trajectory.
@Component
export default class TweenExample extends Script {
protected async onStart(): Promise<void> {
this.useUpdate = true;
if (SystemUtil.isClient()) {
let char = (await Player.asyncGetLocalPlayer()).character;
InputUtil.onKeyDown(Keys.G, async () => {
let daoDan = new DaoDanScript(char.worldTransform.position, new Vector(Math.random() * 1000, Math.random() * 1000, 0));
})
}
}
protected onUpdate(dt: number): void {
TweenUtil.TWEEN.update();
}
}
class DaoDanScript {
//Missile prefab
private prefab: GameObject
//Constructor. When new, a position is passed in for initialization
constructor(bornPos: Vector, toPos: Vector) {
this.init(bornPos, toPos)
}
//Initialize DaoDan
private async init(bornPos: Vector, toPos: Vector) {
// Use object pool to create prefab and set position of prefab
const bombAssetId = "44948";
const fireAssetId = "13404";
const cubeAssetId = "197386";
this.prefab = await GameObjPool.asyncSpawn(cubeAssetId, GameObjPoolSourceType.Asset);
this.prefab.setVisibility(PropertyStatus.Off, false);
GameObject.asyncSpawn({ guid: bombAssetId }).then(obj => {
obj.attachToGameObject(this.prefab);
obj.localTransform.position = new Vector(0, 0, 0);
obj.localTransform.rotation = new Rotation(0, -90, 0);
})
EffectService.playOnGameObject(fireAssetId, this.prefab, {position:new Vector(0, 0, 0), rotation:new Rotation(0, -90, 0), scale: new Vector(2, 2, 2)});
this.prefab.worldTransform.position = bornPos;
this.fireToPos(toPos);
}
//The missile flies towards a coordinate
private async fireToPos(targetPos: Vector) {
// Play the takeoff animation first
this.fireReadyAnim()
// Create a special effect (warning effect) at the target location
EffectService.playAtPosition("155714", targetPos, {loopCount:1});
// Deconstructing the starting point coordinates (to facilitate Tween's transition)
let startLoc = { x: this.prefab.worldTransform.position.x, y: this.prefab.worldTransform.position.y, z: this.prefab.worldTransform.position.z }
// Deconstruct the coordinates of target point (convenient for Tween to transition)
let targetLoc = { x: targetPos.x, y: targetPos.y, z: targetPos.z }
// Previous Frame position
let lastPos: Vector = this.prefab.worldTransform.position.clone()
// Current frame position
let nowPos: Vector = Vector.zero
// Position of middlemen (avoid frequent visits to new Vector)
let tempPos: Vector = Vector.zero
// Create a Tween starting from the position of the missile
const newTween = new mw.Tween(this.prefab.worldTransform.position.clone())
newTween.to({
// X-axis flight path (these path points can be freely defined)
x: [
startLoc.x + 1000 + Math.random() * 2000,
startLoc.x + 3000 + Math.random() * 2000,
startLoc.x + 5000 + Math.random() * 2000,
targetLoc.x],
// Y-axis flight path (these path points can be freely defined)
y: [
startLoc.y + 1000 + Math.random() * 2000,
startLoc.y + 3000 + Math.random() * 2000,
startLoc.y + 5000 + Math.random() * 2000,
targetLoc.y],
// Z axis flight path (these path points can be defined freely)
z: [
550 + Math.random() * 1000,
750 + Math.random() * 1000,
950 + Math.random() * 1000,
750 + Math.random() * 1000,
550 + Math.random() * 1000,
targetLoc.z]
// The entire process lasts for 3000 milliseconds
}, 3000)
//Linear interpolation: completely linear, without transitions at turns, straight forward and backward;
Bezier interpolation: Smooth the entire process to form a curve;
CatmullRom interpolation: Smooth turns, only smooth at turns
// Using CatmullRom interpolation
.interpolation(TweenUtil.Interpolation.CatmullRom)
.onUpdate((value) => {
// Assign value (transition value) to tempPos and nowPos for computation in each loop
tempPos.set(value.x, value.y, value.z)
nowPos.set(value.x, value.y, value.z)
// Set the coordinates of the rocket as transition values
this.prefab.worldTransform.position = tempPos
// Calculate the real-time orientation of the rocket according to the position of the previous frame and the position of this frame
this.prefab.worldRotation = nowPos.subtract(lastPos).toRotation()
// Assign this frame value to lastPos for the next operation
lastPos.set(value.x, value.y, value.z)
})
// When Tween finishes playing
newTween.onComplete(() => {
const bombEffectId = "7786";
// Play an explosion effect at the end position
EffectService.playEffectAtLocation(bombEffectId, this.prefab.worldTransform.position, 1)
// Reset the rotation of the rocket
this.prefab.worldTransform.rotation = Rotation.zero
// Return the rocket to the prefab
GameObjPool.despawn(this.prefab)
})
// Wait one second before playing (to wait until the takeoff animation is played)
setTimeout(() => {
newTween.start()
}, 1000);
}
//Takeoff stage animation
private fireReadyAnim() {
let tempRotate: Rotation = Rotation.zero
let startPos: Vector = this.prefab.worldTransform.position.clone()
let tweenA = new mw.Tween({ y: 0 }).to({ y: 60 + Math.random() * 30 }, 1000).onUpdate((value) => {
tempRotate.y = value.y
this.prefab.worldTransform.rotation = tempRotate
}).start().easing(TweenUtil.Easing.Cubic.Out)
let tweenB = new mw.Tween(startPos).to(startPos.clone().add(new mw.Vector(0, 0, Math.random() * 100)), 1000).onUpdate((value) => {
this.prefab.worldTransform.position = value
}).start().easing(TweenUtil.Easing.Cubic.Out)
}
}
@Component
export default class TweenExample extends Script {
protected async onStart(): Promise<void> {
this.useUpdate = true;
if (SystemUtil.isClient()) {
let char = (await Player.asyncGetLocalPlayer()).character;
InputUtil.onKeyDown(Keys.G, async () => {
let daoDan = new DaoDanScript(char.worldTransform.position, new Vector(Math.random() * 1000, Math.random() * 1000, 0));
})
}
}
protected onUpdate(dt: number): void {
TweenUtil.TWEEN.update();
}
}
class DaoDanScript {
//Missile prefab
private prefab: GameObject
//Constructor. When new, a position is passed in for initialization
constructor(bornPos: Vector, toPos: Vector) {
this.init(bornPos, toPos)
}
//Initialize DaoDan
private async init(bornPos: Vector, toPos: Vector) {
// Use object pool to create prefab and set position of prefab
const bombAssetId = "44948";
const fireAssetId = "13404";
const cubeAssetId = "197386";
this.prefab = await GameObjPool.asyncSpawn(cubeAssetId, GameObjPoolSourceType.Asset);
this.prefab.setVisibility(PropertyStatus.Off, false);
GameObject.asyncSpawn({ guid: bombAssetId }).then(obj => {
obj.attachToGameObject(this.prefab);
obj.localTransform.position = new Vector(0, 0, 0);
obj.localTransform.rotation = new Rotation(0, -90, 0);
})
EffectService.playOnGameObject(fireAssetId, this.prefab, {position:new Vector(0, 0, 0), rotation:new Rotation(0, -90, 0), scale: new Vector(2, 2, 2)});
this.prefab.worldTransform.position = bornPos;
this.fireToPos(toPos);
}
//The missile flies towards a coordinate
private async fireToPos(targetPos: Vector) {
// Play the takeoff animation first
this.fireReadyAnim()
// Create a special effect (warning effect) at the target location
EffectService.playAtPosition("155714", targetPos, {loopCount:1});
// Deconstructing the starting point coordinates (to facilitate Tween's transition)
let startLoc = { x: this.prefab.worldTransform.position.x, y: this.prefab.worldTransform.position.y, z: this.prefab.worldTransform.position.z }
// Deconstruct the coordinates of target point (convenient for Tween to transition)
let targetLoc = { x: targetPos.x, y: targetPos.y, z: targetPos.z }
// Previous Frame position
let lastPos: Vector = this.prefab.worldTransform.position.clone()
// Current frame position
let nowPos: Vector = Vector.zero
// Position of middlemen (avoid frequent visits to new Vector)
let tempPos: Vector = Vector.zero
// Create a Tween starting from the position of the missile
const newTween = new mw.Tween(this.prefab.worldTransform.position.clone())
newTween.to({
// X-axis flight path (these path points can be freely defined)
x: [
startLoc.x + 1000 + Math.random() * 2000,
startLoc.x + 3000 + Math.random() * 2000,
startLoc.x + 5000 + Math.random() * 2000,
targetLoc.x],
// Y-axis flight path (these path points can be freely defined)
y: [
startLoc.y + 1000 + Math.random() * 2000,
startLoc.y + 3000 + Math.random() * 2000,
startLoc.y + 5000 + Math.random() * 2000,
targetLoc.y],
// Z axis flight path (these path points can be defined freely)
z: [
550 + Math.random() * 1000,
750 + Math.random() * 1000,
950 + Math.random() * 1000,
750 + Math.random() * 1000,
550 + Math.random() * 1000,
targetLoc.z]
// The entire process lasts for 3000 milliseconds
}, 3000)
//Linear interpolation: completely linear, without transitions at turns, straight forward and backward;
Bezier interpolation: Smooth the entire process to form a curve;
CatmullRom interpolation: Smooth turns, only smooth at turns
// Using CatmullRom interpolation
.interpolation(TweenUtil.Interpolation.CatmullRom)
.onUpdate((value) => {
// Assign value (transition value) to tempPos and nowPos for computation in each loop
tempPos.set(value.x, value.y, value.z)
nowPos.set(value.x, value.y, value.z)
// Set the coordinates of the rocket as transition values
this.prefab.worldTransform.position = tempPos
// Calculate the real-time orientation of the rocket according to the position of the previous frame and the position of this frame
this.prefab.worldRotation = nowPos.subtract(lastPos).toRotation()
// Assign this frame value to lastPos for the next operation
lastPos.set(value.x, value.y, value.z)
})
// When Tween finishes playing
newTween.onComplete(() => {
const bombEffectId = "7786";
// Play an explosion effect at the end position
EffectService.playEffectAtLocation(bombEffectId, this.prefab.worldTransform.position, 1)
// Reset the rotation of the rocket
this.prefab.worldTransform.rotation = Rotation.zero
// Return the rocket to the prefab
GameObjPool.despawn(this.prefab)
})
// Wait one second before playing (to wait until the takeoff animation is played)
setTimeout(() => {
newTween.start()
}, 1000);
}
//Takeoff stage animation
private fireReadyAnim() {
let tempRotate: Rotation = Rotation.zero
let startPos: Vector = this.prefab.worldTransform.position.clone()
let tweenA = new mw.Tween({ y: 0 }).to({ y: 60 + Math.random() * 30 }, 1000).onUpdate((value) => {
tempRotate.y = value.y
this.prefab.worldTransform.rotation = tempRotate
}).start().easing(TweenUtil.Easing.Cubic.Out)
let tweenB = new mw.Tween(startPos).to(startPos.clone().add(new mw.Vector(0, 0, Math.random() * 100)), 1000).onUpdate((value) => {
this.prefab.worldTransform.position = value
}).start().easing(TweenUtil.Easing.Cubic.Out)
}
}
Table of contents
Properties
TWEEN: TweenGroup |
---|
Single case of global supplementary group. When create a mending room, if not specified, it will be added to the mending group by default |
Properties
TWEEN
▪ Static
TWEEN: TweenGroup
Single case of global supplementary group. When create a mending room, if not specified, it will be added to the mending group by default