Add back-mounted shield model tied to shield skill

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-08-18 16:36:16 +02:00
co-authored by Qwen-Coder
parent 9e3e5c70e0
commit 3fde0925c5
2 changed files with 50 additions and 0 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 19 KiB

+50
View File
@@ -44,6 +44,7 @@ export class Player {
mixer!: THREE.AnimationMixer; mixer!: THREE.AnimationMixer;
private clips: Map<string, THREE.AnimationAction> = new Map(); private clips: Map<string, THREE.AnimationAction> = new Map();
private currentAnim = ''; private currentAnim = '';
private backShield: THREE.Group | null = null;
constructor() { constructor() {
this.body = new THREE.Group(); this.body = new THREE.Group();
@@ -88,6 +89,49 @@ export class Player {
shadow.renderOrder = 999; shadow.renderOrder = 999;
this.body.add(shadow); this.body.add(shadow);
// Shield on the back while the shield skill is charged
loadGLB('shield').then((gltf) => {
const shieldModel = gltf.scene;
const shieldTexture = new THREE.TextureLoader().load('./textures/shield.png');
shieldTexture.flipY = false;
shieldTexture.colorSpace = THREE.SRGBColorSpace;
shieldModel.traverse((child) => {
if (child instanceof THREE.Mesh) {
const geo = child.geometry;
// The converted model has no UVs; project them from the
// front plane so the shield texture maps onto the face
if (!geo.attributes.uv) {
geo.computeBoundingBox();
const bb = geo.boundingBox!;
const pos = geo.attributes.position;
const uv = new Float32Array(pos.count * 2);
for (let i = 0; i < pos.count; i++) {
uv[i * 2] = (pos.getX(i) - bb.min.x) / (bb.max.x - bb.min.x);
uv[i * 2 + 1] = (pos.getY(i) - bb.min.y) / (bb.max.y - bb.min.y);
}
geo.setAttribute('uv', new THREE.BufferAttribute(uv, 2));
}
child.castShadow = true;
child.material = new THREE.MeshToonMaterial({ map: shieldTexture });
}
});
// The geometry is offset from the model origin; recenter it so
// rotation and scale act on the shield's center
const box = new THREE.Box3().setFromObject(shieldModel);
const center = box.getCenter(new THREE.Vector3());
shieldModel.position.sub(center);
const shield = new THREE.Group();
shield.add(shieldModel);
shield.scale.set(0.25, 0.25, 0.25);
shield.position.set(0, 0.85, -0.5);
shield.rotation.set(-0.15, Math.PI, 0);
shield.visible = this.stats.shield > 0 && this.shieldHp > 0;
this.backShield = shield;
this.body.add(shield);
}).catch(() => {
// No shield model available
});
// Weapons // Weapons
this.leftHandWeapon = new Staff(); this.leftHandWeapon = new Staff();
this.rightHandWeapon = new Sword(); this.rightHandWeapon = new Sword();
@@ -206,6 +250,9 @@ export class Player {
this.shieldTimer = 0; this.shieldTimer = 0;
} }
} }
if (this.backShield) {
this.backShield.visible = this.stats.shield > 0 && this.shieldHp > 0;
}
} }
applySkillSystem(skills: SkillSystem) { applySkillSystem(skills: SkillSystem) {
@@ -232,6 +279,9 @@ export class Player {
if (this.stats.shield > 0 && this.shieldHp <= 0) { if (this.stats.shield > 0 && this.shieldHp <= 0) {
this.shieldHp = this.stats.shield; this.shieldHp = this.stats.shield;
} }
if (this.backShield) {
this.backShield.visible = this.stats.shield > 0 && this.shieldHp > 0;
}
} }
die() { die() {