Rework dragon model with bat wings, detailed head and curved tail
This commit is contained in:
+285
-92
@@ -66,6 +66,7 @@ export class Dragon extends Enemy {
|
||||
private wingL!: THREE.Group;
|
||||
private wingR!: THREE.Group;
|
||||
private jaw!: THREE.Group;
|
||||
private tailGroup!: THREE.Group;
|
||||
private breathGroup!: THREE.Group;
|
||||
private breathMat!: THREE.MeshBasicMaterial;
|
||||
private telegraphArc!: THREE.Mesh;
|
||||
@@ -108,131 +109,316 @@ export class Dragon extends Enemy {
|
||||
this.skinMat = new THREE.MeshToonMaterial({ color: 0xbb4433 });
|
||||
this.darkMat = new THREE.MeshToonMaterial({ color: 0x772a1e });
|
||||
this.bellyMat = new THREE.MeshToonMaterial({ color: 0xe8c890 });
|
||||
this.wingMat = new THREE.MeshToonMaterial({ color: 0x993322 });
|
||||
this.wingMat = new THREE.MeshToonMaterial({ color: 0x993322, side: THREE.DoubleSide });
|
||||
this.eyeMat = new THREE.MeshBasicMaterial({ color: 0xffcc33 });
|
||||
const boneMat = new THREE.MeshToonMaterial({ color: 0xf0e6cf });
|
||||
let boneFadeRegistered = false;
|
||||
const registerBone = (mesh: THREE.Mesh) => {
|
||||
// Knochen-Material einmal fürs Fade-Death registrieren (wird geteilt)
|
||||
if (!boneFadeRegistered) {
|
||||
this.registerFadeMesh(mesh);
|
||||
boneFadeRegistered = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Hilfspunkt: Strebe/Knochen zwischen zwei Punkten
|
||||
const strut = (
|
||||
a: THREE.Vector3, b: THREE.Vector3, r1: number, r2: number
|
||||
): THREE.Mesh => {
|
||||
const dir = new THREE.Vector3().subVectors(b, a);
|
||||
const len = dir.length();
|
||||
const mesh = new THREE.Mesh(new THREE.CylinderGeometry(r2, r1, len, 6), this.darkMat);
|
||||
mesh.position.copy(a).addScaledVector(dir, 0.5);
|
||||
mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.normalize());
|
||||
return mesh;
|
||||
};
|
||||
|
||||
// --- Körper (Blickrichtung +Z) ---
|
||||
|
||||
// Rumpf: gestreckte Kugel
|
||||
// Brust, Rumpfmitte, Hüfte (überlappende Kugeln für organische Silhouette)
|
||||
const chest = new THREE.Mesh(new THREE.SphereGeometry(0.8, 18, 12), this.skinMat);
|
||||
chest.scale.set(0.95, 0.92, 1.05);
|
||||
chest.position.set(0, 1.4, 0.85);
|
||||
chest.castShadow = true;
|
||||
this.registerFadeMesh(chest);
|
||||
this.model.add(chest);
|
||||
|
||||
const torso = new THREE.Mesh(new THREE.SphereGeometry(1.0, 20, 14), this.skinMat);
|
||||
torso.scale.set(1, 0.85, 1.7);
|
||||
torso.position.y = 1.4;
|
||||
torso.scale.set(1, 0.85, 1.5);
|
||||
torso.position.set(0, 1.5, -0.1);
|
||||
torso.castShadow = true;
|
||||
this.registerFadeMesh(torso);
|
||||
this.model.add(torso);
|
||||
|
||||
// Bauch (hell, unten)
|
||||
const belly = new THREE.Mesh(new THREE.SphereGeometry(0.75, 16, 10), this.bellyMat);
|
||||
belly.scale.set(1, 0.55, 1.6);
|
||||
belly.position.set(0, 1.05, 0.1);
|
||||
this.registerFadeMesh(belly);
|
||||
this.model.add(belly);
|
||||
const hips = new THREE.Mesh(new THREE.SphereGeometry(0.85, 18, 12), this.skinMat);
|
||||
hips.scale.set(1.0, 0.9, 1.1);
|
||||
hips.position.set(0, 1.45, -0.85);
|
||||
hips.castShadow = true;
|
||||
this.registerFadeMesh(hips);
|
||||
this.model.add(hips);
|
||||
|
||||
// Schwanz (-Z, verjüngt)
|
||||
// Bauchplatten (hell, segmentiert)
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const seg = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.5 - i * 0.08, 10, 8),
|
||||
i % 2 === 0 ? this.skinMat : this.darkMat
|
||||
const plate = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(0.58 - i * 0.06, 14, 8),
|
||||
this.bellyMat
|
||||
);
|
||||
seg.position.set(0, 1.4 - i * 0.06, -1.7 - i * 0.6);
|
||||
this.registerFadeMesh(seg);
|
||||
this.model.add(seg);
|
||||
plate.scale.set(0.95, 0.5, 0.8);
|
||||
plate.position.set(0, 1.02 - i * 0.02, 1.15 - i * 0.62);
|
||||
this.registerFadeMesh(plate);
|
||||
this.model.add(plate);
|
||||
}
|
||||
|
||||
// Schwanzspitze (Pfeil)
|
||||
const tailTip = new THREE.Mesh(new THREE.ConeGeometry(0.2, 0.8, 6), this.darkMat);
|
||||
// Schwanz (geschwungen, mit Zacken und Spadel-Spitze)
|
||||
this.tailGroup = new THREE.Group();
|
||||
this.tailGroup.position.set(0, 1.35, -1.5);
|
||||
this.model.add(this.tailGroup);
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const radius = 0.46 - i * 0.055;
|
||||
const seg = new THREE.Mesh(
|
||||
new THREE.SphereGeometry(radius, 12, 8),
|
||||
i % 2 === 0 ? this.skinMat : this.darkMat
|
||||
);
|
||||
const y = -0.13 * i + Math.max(0, i - 3) * 0.18;
|
||||
seg.position.set(0, y, -0.15 - i * 0.45);
|
||||
seg.scale.set(1, 0.92, 1.15);
|
||||
this.registerFadeMesh(seg);
|
||||
this.tailGroup.add(seg);
|
||||
if (i % 2 === 0 && i < 5) {
|
||||
const spike = new THREE.Mesh(
|
||||
new THREE.ConeGeometry(0.09 - i * 0.012, 0.32 - i * 0.03, 5),
|
||||
this.darkMat
|
||||
);
|
||||
spike.position.set(0, y + radius * 0.9 + 0.1, -0.15 - i * 0.45);
|
||||
spike.rotation.x = -0.3;
|
||||
this.registerFadeMesh(spike);
|
||||
this.tailGroup.add(spike);
|
||||
}
|
||||
}
|
||||
const tailTip = new THREE.Mesh(new THREE.ConeGeometry(0.3, 0.9, 4), this.darkMat);
|
||||
tailTip.rotation.x = -Math.PI / 2;
|
||||
tailTip.position.set(0, 1.1, -4.8);
|
||||
tailTip.scale.x = 0.4;
|
||||
tailTip.position.set(0, -0.35, -3.3);
|
||||
this.registerFadeMesh(tailTip);
|
||||
this.model.add(tailTip);
|
||||
this.tailGroup.add(tailTip);
|
||||
|
||||
// Kopf (+Z)
|
||||
const head = new THREE.Mesh(new THREE.SphereGeometry(0.7, 16, 12), this.skinMat);
|
||||
head.scale.set(1, 0.9, 1.2);
|
||||
head.position.set(0, 2.0, 1.7);
|
||||
head.castShadow = true;
|
||||
this.registerFadeMesh(head);
|
||||
this.model.add(head);
|
||||
// Hals (S-Kurve nach oben)
|
||||
for (const [y, z, r] of [[1.95, 1.3, 0.46], [2.2, 1.55, 0.4]]) {
|
||||
const neck = new THREE.Mesh(new THREE.SphereGeometry(r, 14, 10), this.skinMat);
|
||||
neck.scale.set(0.85, 0.95, 1.1);
|
||||
neck.position.set(0, y, z);
|
||||
this.registerFadeMesh(neck);
|
||||
this.model.add(neck);
|
||||
}
|
||||
|
||||
// Kopf-Gruppe (+Z)
|
||||
const headGroup = new THREE.Group();
|
||||
headGroup.position.set(0, 2.4, 1.8);
|
||||
headGroup.rotation.x = 0.06;
|
||||
this.model.add(headGroup);
|
||||
|
||||
const skull = new THREE.Mesh(new THREE.SphereGeometry(0.55, 16, 12), this.skinMat);
|
||||
skull.scale.set(0.95, 0.9, 1.15);
|
||||
skull.castShadow = true;
|
||||
this.registerFadeMesh(skull);
|
||||
headGroup.add(skull);
|
||||
|
||||
// Schnauze (+Z)
|
||||
const snout = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.35, 0.6), this.skinMat);
|
||||
snout.position.set(0, 1.85, 2.5);
|
||||
const snout = new THREE.Mesh(new THREE.BoxGeometry(0.44, 0.32, 0.7), this.skinMat);
|
||||
snout.position.set(0, -0.1, 0.72);
|
||||
this.registerFadeMesh(snout);
|
||||
this.model.add(snout);
|
||||
headGroup.add(snout);
|
||||
|
||||
// Kiefer (beweglich, öffnet sich beim Feuerspeer)
|
||||
this.jaw = new THREE.Group();
|
||||
const jawMesh = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.22, 0.55), this.darkMat);
|
||||
jawMesh.position.set(0, -0.1, 0.22);
|
||||
this.registerFadeMesh(jawMesh);
|
||||
this.jaw.add(jawMesh);
|
||||
this.jaw.position.set(0, 1.75, 2.4);
|
||||
this.jaw.rotation.x = 0.15;
|
||||
this.model.add(this.jaw);
|
||||
// Stirnleiste (grimmiger Blick)
|
||||
const brow = new THREE.Mesh(new THREE.BoxGeometry(0.62, 0.14, 0.26), this.darkMat);
|
||||
brow.position.set(0, 0.3, 0.36);
|
||||
brow.rotation.x = 0.12;
|
||||
this.registerFadeMesh(brow);
|
||||
headGroup.add(brow);
|
||||
|
||||
// Augen (+Z, ragen aus dem Kopf heraus)
|
||||
for (const side of [-1, 1]) {
|
||||
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.13, 8, 6), this.eyeMat);
|
||||
eye.position.set(side * 0.32, 2.3, 2.42);
|
||||
this.model.add(eye);
|
||||
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), this.eyeMat);
|
||||
eye.position.set(side * 0.3, 0.2, 0.48);
|
||||
headGroup.add(eye);
|
||||
}
|
||||
|
||||
// Hörner
|
||||
// Hörner (nach hinten geschwungen, zwei Paare)
|
||||
for (const side of [-1, 1]) {
|
||||
const horn = new THREE.Mesh(new THREE.ConeGeometry(0.09, 0.6, 6), this.darkMat);
|
||||
horn.position.set(side * 0.28, 2.6, 1.4);
|
||||
horn.rotation.set(-0.9, 0, side * 0.5);
|
||||
const horn = new THREE.Mesh(new THREE.ConeGeometry(0.09, 0.75, 6), this.darkMat);
|
||||
horn.position.set(side * 0.28, 0.42, -0.12);
|
||||
horn.rotation.set(-1.15, 0, side * 0.35);
|
||||
this.registerFadeMesh(horn);
|
||||
this.model.add(horn);
|
||||
headGroup.add(horn);
|
||||
const horn2 = new THREE.Mesh(new THREE.ConeGeometry(0.06, 0.4, 6), this.darkMat);
|
||||
horn2.position.set(side * 0.17, 0.3, -0.34);
|
||||
horn2.rotation.set(-1.3, 0, side * 0.25);
|
||||
this.registerFadeMesh(horn2);
|
||||
headGroup.add(horn2);
|
||||
}
|
||||
|
||||
// Rückenstacheln
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const spike = new THREE.Mesh(new THREE.ConeGeometry(0.14, 0.5, 5), this.darkMat);
|
||||
spike.position.set(0, 1.9, 0.8 - i * 0.9);
|
||||
this.registerFadeMesh(spike);
|
||||
this.model.add(spike);
|
||||
}
|
||||
|
||||
// Beine (4, unter dem Rumpf)
|
||||
// Ohr-Fransen (seitlich nach hinten)
|
||||
for (const side of [-1, 1]) {
|
||||
for (const back of [true, false]) {
|
||||
const leg = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0.16, 0.22, 0.7, 8),
|
||||
this.darkMat
|
||||
);
|
||||
leg.position.set(side * 0.65, 0.35, back ? -0.8 : 0.7);
|
||||
this.registerFadeMesh(leg);
|
||||
this.model.add(leg);
|
||||
const claw = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.14, 0.5), this.skinMat);
|
||||
claw.position.set(side * 0.65, 0.08, back ? -0.8 : 0.7);
|
||||
this.registerFadeMesh(claw);
|
||||
const frill = new THREE.Mesh(new THREE.ConeGeometry(0.12, 0.45, 5), this.darkMat);
|
||||
frill.position.set(side * 0.48, 0.1, -0.18);
|
||||
frill.rotation.set(0.35, 0, side * -1.75);
|
||||
frill.scale.set(1, 1, 0.5);
|
||||
this.registerFadeMesh(frill);
|
||||
headGroup.add(frill);
|
||||
}
|
||||
|
||||
// Nüstern
|
||||
for (const side of [-1, 1]) {
|
||||
const nostril = new THREE.Mesh(new THREE.SphereGeometry(0.04, 6, 4), this.darkMat);
|
||||
nostril.position.set(side * 0.1, 0.06, 1.0);
|
||||
headGroup.add(nostril);
|
||||
}
|
||||
|
||||
// Zähne im Oberkiefer (zeigen nach unten)
|
||||
for (const side of [-1, 1]) {
|
||||
for (const z of [0.72, 0.88, 1.02]) {
|
||||
const tooth = new THREE.Mesh(new THREE.ConeGeometry(0.032, 0.11, 5), boneMat);
|
||||
tooth.rotation.x = Math.PI;
|
||||
tooth.position.set(side * 0.15, -0.3, z);
|
||||
registerBone(tooth);
|
||||
headGroup.add(tooth);
|
||||
}
|
||||
}
|
||||
|
||||
// Kiefer (beweglich, öffnet sich beim Feuerspeer)
|
||||
this.jaw = new THREE.Group();
|
||||
this.jaw.position.set(0, -0.28, 0.28);
|
||||
const jawMesh = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.16, 0.62), this.darkMat);
|
||||
jawMesh.position.set(0, -0.05, 0.3);
|
||||
this.registerFadeMesh(jawMesh);
|
||||
this.jaw.add(jawMesh);
|
||||
for (const side of [-1, 1]) {
|
||||
for (const z of [0.42, 0.56]) {
|
||||
const tooth = new THREE.Mesh(new THREE.ConeGeometry(0.028, 0.09, 5), boneMat);
|
||||
tooth.position.set(side * 0.11, 0.06, z);
|
||||
registerBone(tooth);
|
||||
this.jaw.add(tooth);
|
||||
}
|
||||
}
|
||||
this.jaw.rotation.x = 0.12;
|
||||
headGroup.add(this.jaw);
|
||||
|
||||
// Rückenplatten (vom Nacken bis in den Schwanz)
|
||||
const backPlates: [number, number, number, number][] = [
|
||||
[2.25, 0.95, 0.15, 0.48],
|
||||
[2.38, 0.35, 0.19, 0.6],
|
||||
[2.4, -0.25, 0.2, 0.64],
|
||||
[2.32, -0.85, 0.17, 0.52],
|
||||
[1.9, -1.5, 0.14, 0.44],
|
||||
[1.62, -2.4, 0.12, 0.38],
|
||||
[1.45, -3.3, 0.1, 0.32],
|
||||
];
|
||||
for (const [y, z, r, h] of backPlates) {
|
||||
const plate = new THREE.Mesh(new THREE.ConeGeometry(r, h, 5), this.darkMat);
|
||||
plate.position.set(0, y, z);
|
||||
plate.rotation.x = -0.22;
|
||||
this.registerFadeMesh(plate);
|
||||
this.model.add(plate);
|
||||
}
|
||||
|
||||
// Beine (Hinterbeine kräftiger, Vorderbeine schlanker)
|
||||
for (const side of [-1, 1]) {
|
||||
// Hinterbein
|
||||
const thigh = new THREE.Mesh(new THREE.SphereGeometry(0.5, 12, 10), this.skinMat);
|
||||
thigh.scale.set(0.75, 1.1, 0.95);
|
||||
thigh.position.set(side * 0.72, 1.0, -0.8);
|
||||
this.registerFadeMesh(thigh);
|
||||
this.model.add(thigh);
|
||||
const shin = new THREE.Mesh(new THREE.CylinderGeometry(0.13, 0.19, 0.75, 8), this.darkMat);
|
||||
shin.position.set(side * 0.8, 0.45, -0.9);
|
||||
shin.rotation.x = 0.12;
|
||||
this.registerFadeMesh(shin);
|
||||
this.model.add(shin);
|
||||
const hindFoot = new THREE.Mesh(new THREE.BoxGeometry(0.42, 0.16, 0.6), this.skinMat);
|
||||
hindFoot.position.set(side * 0.8, 0.09, -0.78);
|
||||
this.registerFadeMesh(hindFoot);
|
||||
this.model.add(hindFoot);
|
||||
for (const dx of [-0.14, 0, 0.14]) {
|
||||
const claw = new THREE.Mesh(new THREE.ConeGeometry(0.05, 0.2, 5), boneMat);
|
||||
claw.rotation.x = Math.PI / 2;
|
||||
claw.position.set(side * 0.8 + dx, 0.09, -0.44);
|
||||
registerBone(claw);
|
||||
this.model.add(claw);
|
||||
}
|
||||
|
||||
// Vorderbein
|
||||
const upperArm = new THREE.Mesh(new THREE.SphereGeometry(0.3, 12, 8), this.skinMat);
|
||||
upperArm.scale.set(0.8, 1.15, 0.9);
|
||||
upperArm.position.set(side * 0.62, 1.05, 0.8);
|
||||
this.registerFadeMesh(upperArm);
|
||||
this.model.add(upperArm);
|
||||
const foreArm = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.15, 0.7, 8), this.darkMat);
|
||||
foreArm.position.set(side * 0.6, 0.5, 0.85);
|
||||
this.registerFadeMesh(foreArm);
|
||||
this.model.add(foreArm);
|
||||
const foreFoot = new THREE.Mesh(new THREE.BoxGeometry(0.34, 0.14, 0.5), this.skinMat);
|
||||
foreFoot.position.set(side * 0.6, 0.08, 0.95);
|
||||
this.registerFadeMesh(foreFoot);
|
||||
this.model.add(foreFoot);
|
||||
for (const dx of [-0.11, 0, 0.11]) {
|
||||
const claw = new THREE.Mesh(new THREE.ConeGeometry(0.045, 0.18, 5), boneMat);
|
||||
claw.rotation.x = Math.PI / 2;
|
||||
claw.position.set(side * 0.6 + dx, 0.08, 1.24);
|
||||
registerBone(claw);
|
||||
this.model.add(claw);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Flügel (animiertes Flattern) ---
|
||||
const wingGeom = new THREE.BoxGeometry(0.1, 2.4, 1.5);
|
||||
this.wingL = new THREE.Group();
|
||||
this.wingL.position.set(-0.7, 2.2, -0.2);
|
||||
const wingLmesh = new THREE.Mesh(wingGeom, this.wingMat);
|
||||
wingLmesh.position.set(-1.6, 0, 0);
|
||||
wingLmesh.rotation.z = Math.PI / 2;
|
||||
wingLmesh.castShadow = true;
|
||||
this.registerFadeMesh(wingLmesh);
|
||||
this.wingL.add(wingLmesh);
|
||||
this.model.add(this.wingL);
|
||||
// --- Flügel (Fledermaus-Silhouette mit Membran und Fingerknochen) ---
|
||||
const wingShape = new THREE.Shape();
|
||||
wingShape.moveTo(0.2, 0.1);
|
||||
wingShape.quadraticCurveTo(0.8, -0.12, 1.5, 0.0); // Vorderkante zum Handgelenk
|
||||
wingShape.quadraticCurveTo(2.5, 0.0, 3.05, 0.95); // zur Flügelspitze
|
||||
// Hinterkante: Fingerspitzen mit konkaven Bögen dazwischen
|
||||
wingShape.quadraticCurveTo(2.2, 1.0, 2.7, 1.65);
|
||||
wingShape.quadraticCurveTo(1.7, 1.45, 2.0, 2.1);
|
||||
wingShape.quadraticCurveTo(1.05, 1.6, 1.1, 2.25);
|
||||
wingShape.quadraticCurveTo(0.4, 1.2, 0.15, 1.5);
|
||||
wingShape.closePath();
|
||||
const wingGeom = new THREE.ShapeGeometry(wingShape, 10);
|
||||
|
||||
this.wingR = new THREE.Group();
|
||||
this.wingR.position.set(0.7, 2.2, -0.2);
|
||||
const wingRmesh = new THREE.Mesh(wingGeom, this.wingMat);
|
||||
wingRmesh.position.set(1.6, 0, 0);
|
||||
wingRmesh.rotation.z = -Math.PI / 2;
|
||||
wingRmesh.castShadow = true;
|
||||
this.registerFadeMesh(wingRmesh);
|
||||
this.wingR.add(wingRmesh);
|
||||
const buildWing = (): THREE.Group => {
|
||||
const wing = new THREE.Group();
|
||||
const membrane = new THREE.Mesh(wingGeom, this.wingMat);
|
||||
membrane.rotation.x = -Math.PI / 2;
|
||||
membrane.castShadow = true;
|
||||
this.registerFadeMesh(membrane);
|
||||
wing.add(membrane);
|
||||
|
||||
// Flügelknochen: Arm, Außenkante, ein Fingerstrahl
|
||||
const bones = [
|
||||
strut(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1.5, 0.06, 0), 0.07, 0.05),
|
||||
strut(new THREE.Vector3(1.5, 0.06, 0), new THREE.Vector3(3.05, 0.1, -0.95), 0.05, 0.028),
|
||||
strut(new THREE.Vector3(1.3, 0.03, -0.25), new THREE.Vector3(2.0, 0.05, -2.1), 0.04, 0.02),
|
||||
];
|
||||
for (const bone of bones) {
|
||||
this.registerFadeMesh(bone);
|
||||
wing.add(bone);
|
||||
}
|
||||
|
||||
// Daumenkralle am Handgelenk
|
||||
const thumb = new THREE.Mesh(new THREE.ConeGeometry(0.045, 0.22, 5), boneMat);
|
||||
thumb.rotation.x = Math.PI / 2;
|
||||
thumb.position.set(1.55, 0.06, 0.14);
|
||||
registerBone(thumb);
|
||||
wing.add(thumb);
|
||||
return wing;
|
||||
};
|
||||
|
||||
this.wingR = buildWing();
|
||||
this.wingR.position.set(0.7, 2.25, -0.2);
|
||||
this.model.add(this.wingR);
|
||||
|
||||
this.wingL = buildWing();
|
||||
this.wingL.position.set(-0.7, 2.25, -0.2);
|
||||
this.wingL.scale.x = -1; // spiegeln für linke Seite
|
||||
this.model.add(this.wingL);
|
||||
|
||||
// --- Effekte ---
|
||||
|
||||
// Boden-Schatten (folgt dem Drachen)
|
||||
@@ -289,8 +475,10 @@ export class Dragon extends Enemy {
|
||||
const flame = new THREE.Mesh(new THREE.ConeGeometry(1.8, BREATH_RANGE, 16, 1, true), this.breathMat);
|
||||
flame.rotation.x = Math.PI / 2;
|
||||
flame.position.z = BREATH_RANGE / 2;
|
||||
flame.position.y = 1.2;
|
||||
this.breathGroup.add(flame);
|
||||
// auf Maulhöhe und leicht nach unten geneigt
|
||||
this.breathGroup.position.y = 3.3;
|
||||
this.breathGroup.rotation.x = 0.3;
|
||||
this.breathGroup.visible = false;
|
||||
this.body.add(this.breathGroup);
|
||||
}
|
||||
@@ -332,10 +520,15 @@ export class Dragon extends Enemy {
|
||||
const flap = Math.sin(this.waveTime * flapSpeed) * (this.state === 'fly' ? 0.7 : 0.2);
|
||||
this.wingL.rotation.z = flap;
|
||||
this.wingR.rotation.z = -flap;
|
||||
// Flügel-Einstellwinkel
|
||||
this.wingL.rotation.x = -0.3;
|
||||
// Flügel-Einstellwinkel (wegen scale.x-Spiegelung auf beiden Seiten gleich)
|
||||
this.wingL.rotation.x = 0.3;
|
||||
this.wingR.rotation.x = 0.3;
|
||||
|
||||
// Schwebe-Wippen im Flug und sanftes Schwanz-Schwenken
|
||||
const airborne = this.state === 'fly' || this.state === 'takeoff';
|
||||
this.model.position.y = airborne ? Math.sin(this.waveTime * 2.3) * 0.15 : 0;
|
||||
this.tailGroup.rotation.y = Math.sin(this.waveTime * 1.7) * 0.12;
|
||||
|
||||
// Schatten bleibt am Boden
|
||||
this.shadow.position.y = 0.06 - this.body.position.y;
|
||||
const shadowScale = 0.6 + this.body.position.y * 0.15;
|
||||
@@ -559,7 +752,7 @@ export class Dragon extends Enemy {
|
||||
this.telegraphArc.visible = false;
|
||||
this.breathGroup.visible = true;
|
||||
this.breathActive = true;
|
||||
this.jaw.rotation.x = -0.5;
|
||||
this.jaw.rotation.x = 0.55;
|
||||
}
|
||||
} else if (this.breathPhase === 'active') {
|
||||
this.breathTime -= dt;
|
||||
@@ -589,7 +782,7 @@ export class Dragon extends Enemy {
|
||||
this.breathPhase = 'done';
|
||||
this.breathGroup.visible = false;
|
||||
this.breathMat.opacity = 0;
|
||||
this.jaw.rotation.x = 0.15;
|
||||
this.jaw.rotation.x = 0.12;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user