Improve character controls and add idle breathing

This commit is contained in:
2026-11-02 11:56:28 +09:00
parent 0d58170211
commit 21b0bb7e98
15 changed files with 458 additions and 65 deletions
+22 -2
View File
@@ -9,6 +9,26 @@ import { createFlashlight } from '../src/scene/flashlight.ts';
import { FLASHLIGHT } from '../src/constants/flashlight.ts';
import { Vector3 } from 'three';
test('idle breathing carries the coat and hood without lifting the feet or changing the umbrella grip', async () => {
const [model, umbrella, raincoat] = await Promise.all(['red-sheep', 'umbrella', 'raincoat'].map(asset));
const sheep = createSheep(model, { umbrella, raincoat });
sheep.setEquipment(true, true, true);
const body = model.getObjectByName('Body')!, head = model.getObjectByName('Head')!;
const hood = model.getObjectByName('CoatHood')!, arm = model.getObjectByName('ArmRight')!;
const bodyScale = body.scale.clone(), coatScale = raincoat.scale.clone();
const foot = model.getObjectByName('LegRight')!, footPosition = foot.getWorldPosition(new Vector3());
sheep.pose('idle', 0, false, 0);
sheep.pose('idle', 0, false, 1);
const grip = arm.quaternion.clone();
assert.ok(Math.abs(body.scale.y / bodyScale.y - raincoat.scale.y / coatScale.y) < 1e-8);
assert.ok(Math.abs(body.scale.z / bodyScale.z - raincoat.scale.z / coatScale.z) < 1e-8);
assert.ok(hood.getWorldPosition(new Vector3()).distanceTo(head.getWorldPosition(new Vector3())) < 1e-8);
sheep.pose('idle', 0, false, 2);
assert.ok(arm.quaternion.angleTo(grip) < 1e-8);
assert.ok(foot.getWorldPosition(new Vector3()).distanceTo(footPosition) < 1e-8);
sheep.dispose();
});
test('authored flashlight stays in the left grip and uses lumen power only when switched on', async () => {
const [model, umbrella, raincoat, torch] = await Promise.all(['red-sheep', 'umbrella', 'raincoat', 'flashlight'].map(asset));
const sheep = createSheep(model, { umbrella, raincoat, flashlight: torch });
@@ -60,7 +80,7 @@ test('flashlight raises and lowers smoothly, retaining the prop until the arm is
assert.equal(sheep.pose('idle', 0, false, 2), true, 'the final lowered frame is rendered');
assert.ok(arm.quaternion.angleTo(rest) < 1e-6);
assert.equal(torch.visible, false);
assert.equal(sheep.pose('idle', 0, false, 2.1), false, 'idle rendering can stop after the transition');
assert.equal(sheep.pose('idle', 0, false, 2.1), true, 'breathing continues after the equipment transition');
sheep.dispose();
});
@@ -171,6 +191,6 @@ test('umbrella follows the right hand through raising, reversal and lowering bef
assert.equal(sheep.pose('idle', 0, false, 1.2), true);
assert.ok(arm.quaternion.angleTo(rest) < 1e-6);
assert.equal(umbrella.visible, false);
assert.equal(sheep.pose('idle', 0, false, 1.3), false);
assert.equal(sheep.pose('idle', 0, false, 1.3), true);
sheep.dispose();
});