# babylon.js 學習筆記(6)

```//輪子轉動
const wheelAnimation = (scene, wheels) => {
//定義一個動畫，每秒30幀，繞y軸轉動
const animWheel = new BABYLON.Animation("wheelAnimation", "rotation.y",
30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);

//動畫關鍵幀
const wheelKeys = [];

//起始幀
wheelKeys.push({
frame: 0,
value: 0
});

//截止幀(即：第30幀，轉到360度)
wheelKeys.push({
frame: 30,
value: 2 * Math.PI
});

//設置關鍵幀
animWheel.setKeys(wheelKeys);

let result = [];

for (let i = 0; i < wheels.length; i++) {
//將wheel與動畫關聯
wheels[i].animations = [];
wheels[i].animations.push(animWheel);

//開始動畫,最後的true表示循環播放
result.push(scene.beginAnimation(wheels[i], 0, 30, true));
}

return result;
}
```

```//car運動
const carAnimation = (scene, car) => {
const animCar = new BABYLON.Animation("carAnimation", "position.x",
30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);

//動畫關鍵幀
const carKeys = [];

//起始幀
carKeys.push({
frame: 0,
value: -1
});

carKeys.push({
frame: 30,
value: -0.9
});

//截止幀(即：第120幀，走到x=8)
carKeys.push({
frame: 120,
value: 0.9
});

carKeys.push({
frame: 150,
value: 1
});

//設置關鍵幀
animCar.setKeys(carKeys);

car.animations = [];
car.animations.push(animCar);

//開始動畫
return scene.beginAnimation(car, 0, 150, true);

}
```

• pause()
• restart()
• stop()
• reset()

• ALT+鼠標單擊，暫停/繼續播放 汽車運動；
• SHIFT+鼠標單擊，暫停/繼續播放 車輪轉動
```//聲明2個變量，方便控制動畫
let wheelAnimatables, carAnimatable;

const createScene = () => {
const scene = new BABYLON.Scene(engine);

...

wheelAnimatables = wheelAnimation(scene, wheels);
carAnimatable = carAnimation(scene, car);

...

return scene;
}

...
if (e.altKey) {
if (carAnimatable._paused) {
carAnimatable.restart();
}
else {
carAnimatable.pause();
}
}

if (e.shiftKey) {
let first = wheelAnimatables[0];
if (first._paused) {
wheelAnimatables.forEach(element => {
element.restart();
});
}
else {
wheelAnimatables.forEach(element => {
element.pause();
});
}
}
});
```

```const createScene = () => {
const scene = new BABYLON.Scene(engine);

const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 2, new BABYLON.Vector3(0, 0, 0));
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));

const car = buildCar();
const wheels = buildWheels(car);
car.rotation.x = -Math.PI / 2;
wheelAnimation(scene, wheels);

return scene;
}

const buildCar = () => {
//base
const outline = [
new BABYLON.Vector3(-0.3, 0, -0.1),
new BABYLON.Vector3(0.2, 0, -0.1),
]

//curved front
for (let i = 0; i < 20; i++) {
outline.push(new BABYLON.Vector3(0.2 * Math.cos(i * Math.PI / 40), 0, 0.2 * Math.sin(i * Math.PI / 40) - 0.1));
}

//top
outline.push(new BABYLON.Vector3(0, 0, 0.1));
outline.push(new BABYLON.Vector3(-0.3, 0, 0.1));

//car face UVs
const faceUV = [];
faceUV[0] = new BABYLON.Vector4(0, 0.5, 0.38, 1);
faceUV[1] = new BABYLON.Vector4(0, 0, 1, 0.5);
faceUV[2] = new BABYLON.Vector4(0.38, 1, 0, 0.5);

//car material
const carMat = new BABYLON.StandardMaterial("carMat");
carMat.diffuseTexture = new BABYLON.Texture("https://yjmyzz.github.io/babylon_js_study/assets/img/car.png");

//back formed automatically
const car = BABYLON.MeshBuilder.ExtrudePolygon("car", { shape: outline, depth: 0.2, faceUV: faceUV, wrap: true });
car.material = carMat;

return car;
}

const buildWheels = (car) => {
//wheel face UVs
const wheelUV = [];
wheelUV[0] = new BABYLON.Vector4(0, 0, 1, 1);
wheelUV[1] = new BABYLON.Vector4(0, 0.5, 0, 0.5);
wheelUV[2] = new BABYLON.Vector4(0, 0, 1, 1);

//car material
const wheelMat = new BABYLON.StandardMaterial("wheelMat");
wheelMat.diffuseTexture = new BABYLON.Texture("https://yjmyzz.github.io/babylon_js_study/assets/img/wheel.png");

const wheelRB = BABYLON.MeshBuilder.CreateCylinder("wheelRB", { diameter: 0.125, height: 0.05, faceUV: wheelUV })
wheelRB.material = wheelMat;

wheelRB.parent = car;
wheelRB.position.z = -0.1;
wheelRB.position.x = -0.2;
wheelRB.position.y = 0.035;

const wheelRF = wheelRB.clone("wheelRF");
wheelRF.position.x = 0.1;

const wheelLB = wheelRB.clone("wheelLB");
wheelLB.position.y = -0.2 - 0.035;

const wheelLF = wheelRF.clone("wheelLF");
wheelLF.position.y = -0.2 - 0.035;

const wheels = [];
wheels.push(wheelRB);
wheels.push(wheelRF);
wheels.push(wheelLB);
wheels.push(wheelLF);

return wheels;
}

//輪子轉動
const wheelAnimation = (scene, wheels) => {
//定義一個動畫，每秒30幀，繞y軸轉動
const animWheel = new BABYLON.Animation("wheelAnimation", "rotation.y",
30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);

//動畫關鍵幀
const wheelKeys = [];

//起始幀
wheelKeys.push({
frame: 0,
value: 0
});

//截止幀(即：第30幀，轉到360度)
wheelKeys.push({
frame: 30,
value: 2 * Math.PI
});

//設置關鍵幀
animWheel.setKeys(wheelKeys);

for (let i = 0; i < wheels.length; i++) {
//將wheel與動畫關聯
wheels[i].animations = [];
wheels[i].animations.push(animWheel);

//開始動畫,最後的true表示循環播放
scene.beginAnimation(wheels[i], 0, 30, true);
}
}
```

2.1 理解movePOV

babylon.js提供了movePOV(rightSpeed, upSpeed, forwardSpeed)方法，可以讓對象 朝右(x軸負方向)朝上(z軸正方向)朝前(z軸負方向) 運動。

```const createScene = () => {
const scene = new BABYLON.Scene(engine);

const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON.Vector3(0, 0, 0));
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));

var dude = result.meshes[0];
dude.scaling = new BABYLON.Vector3(0.02, 0.02, 0.02);
scene.beginAnimation(result.skeletons[0], 0, 100, true, 1.0);

//向前運動
const rightSpeed = 0, upSpeed = 0, forwardSpeed = 0.01;
let distance = 0;

//控制下一幀的行爲
//讓人物動起來
dude.movePOV(rightSpeed, upSpeed, forwardSpeed);
distance += 0.002;
if (distance > 1) {
//走的距離>1地，復位
distance = 0;
dude.position = BABYLON.Vector3.Zero();
}

});
});

showAxis(4, scene);
return scene;
}
```

向右(x軸負方向)：

```//向右運動
const rightSpeed = 0.01, upSpeed = 0, forwardSpeed = 0.00;
```

```//向上運動
const rightSpeed = 0, upSpeed = 0.01, forwardSpeed = 0.00;
```

2.2 理解rotate

```BABYLON.SceneLoader.ImportMeshAsync("him", "../assets/glb/", "dude.babylon").then((result) => {
var dude = result.meshes[0];
dude.scaling = new BABYLON.Vector3(0.02, 0.02, 0.02);
scene.beginAnimation(result.skeletons[0], 0, 100, true, 1.0);

//向前運動
const rightSpeed = 0, upSpeed = 0, forwardSpeed = 0.01;

let distance = 0, turnFlag = false;

//控制下一幀的行爲
//讓人物動起來
dude.movePOV(rightSpeed, upSpeed, forwardSpeed);
distance += 0.002;

//走到0.5距離時，右轉90度
if (!turnFlag && Math.abs(distance - 0.5) <= 0.000001) {
dude.rotate(BABYLON.Axis.Y, Math.PI / 2, BABYLON.Space.LOCAL);
turnFlag = true;
}

if (distance > 1) {
//走的距離>1地，復位
distance = 0;
dude.position = BABYLON.Vector3.Zero();
dude.rotation = BABYLON.Vector3.Zero();
turnFlag = false;
}

});
});
```

3 生成複雜運動軌跡

```const createScene = () => {
const scene = new BABYLON.Scene(engine);

const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON.Vector3(0, 0, 0));
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));

//畫1個正方形(輔助觀察人物運動路徑)
const squarePoints = [new BABYLON.Vector3(-2, 0, 2),
new BABYLON.Vector3(2, 0, 2),
new BABYLON.Vector3(2, 0, -2),
new BABYLON.Vector3(-2, 0, -2),
new BABYLON.Vector3(-2, 0, 2)];
const square = BABYLON.MeshBuilder.CreateLines("square", { points: squarePoints });

//走距離dist後，轉方向trun(角度)
const trunPoint = function (turn, dist) {
this.turn = turn;
this.dist = dist;
}

//運動軌跡(關鍵轉向點)
//注：因本例中的人物原型太大，加載後縮小處理了，所以這裏的距離也要回應縮小（即：*0.2)
const track = [
new trunPoint(Math.PI / 2, 4 * 0.2),
new trunPoint(Math.PI / 2, 8 * 0.2),
new trunPoint(Math.PI / 2, 12 * 0.2),
new trunPoint(Math.PI / 2, 16 * 0.2)
]

var dude = result.meshes[0];
dude.scaling = new BABYLON.Vector3(0.02, 0.02, 0.02);
dude.position = new BABYLON.Vector3(2, 0, 2);
scene.beginAnimation(result.skeletons[0], 0, 100, true, 1.0);

//向前運動
const rightSpeed = 0.00, upSpeed = 0.00, forwardSpeed = 0.01;

//distance記錄走過的距離，p爲track中轉向點的下標(即：第幾個轉向點)
let distance = 0, p = 0;

//控制下一幀的行爲
//讓人物動起來
dude.movePOV(rightSpeed, upSpeed, forwardSpeed);
distance += 0.002;
//判斷關鍵轉向點
console.log(distance);
if (distance > track[p].dist) {
dude.rotate(BABYLON.Axis.Y, track[p].turn, BABYLON.Space.LOCAL);
p += 1;
p %= track.length;

if (p === 0) {
//走到最開始的位置時，復位
distance = 0;
dude.position = new BABYLON.Vector3(2, 0, 2);
dude.rotation = BABYLON.Vector3.Zero();
}
}
});
});

showAxis(4, scene);
return scene;
}
```

4、碰撞檢測

當2個對象碰撞到時，只要這2個立體空間有重疊即爲發生了碰撞，對應的方法爲 intersectsMesh，下面給1個簡單的演示：

```BABYLON.SceneLoader.ImportMeshAsync("", "../assets/glb/", "car.babylon").then((result) => {
var car1 = scene.getMeshByName("car");
car1.position = new BABYLON.Vector3(-6, 0, 0);
car1.scaling = new BABYLON.Vector3(4, 4, 4);

const wheelRB = scene.getMeshByName("wheelRB");
const wheelRF = scene.getMeshByName("wheelRF");
const wheelLB = scene.getMeshByName("wheelLB");
const wheelLF = scene.getMeshByName("wheelLF");
let wheels = [wheelRB, wheelRF, wheelLB, wheelLF];
let wheelAnimatables = [];
for (let i = 0; i < wheels.length; i++) {
wheelAnimatables.push(scene.beginAnimation(wheels[i], 0, 30, true));
}

const animCar = new BABYLON.Animation("carAnimation", "position.x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
const carKeys = [];
carKeys.push({
frame: 0,
value: -6
});
carKeys.push({
frame: 120,
value: 6
});

animCar.setKeys(carKeys);

car1.animations = [];
car1.animations.push(animCar);

let carAnimable = scene.beginAnimation(car1, 0, 120, true);

//複製出第2輛車，擋在路上
var car2 = car1.clone("car2");
car2.position = new BABYLON.Vector3(2, 0, 0.4);
car2.rotation.y = Math.PI;
car2.scaling = car1.scaling;

//控制下一幀的行爲
//發生碰撞
if (car1.intersectsMesh(car2)) {
return;
}

//停止動畫
carAnimable.stop();
//模擬第1輛車，被撞飛
car1.rotate(BABYLON.Axis.Y, Math.PI*0.4, BABYLON.Space.LOCAL);
car1.position.x -= 0.2;
car1.position.y += 0.5;
return;

}

}

});
});
```

https://doc.babylonjs.com/features/introductionToFeatures/chap3/walkpath

https://doc.babylonjs.com/features/introductionToFeatures/chap4/mesh_intersect