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 wingL!: THREE.Group;
|
||||||
private wingR!: THREE.Group;
|
private wingR!: THREE.Group;
|
||||||
private jaw!: THREE.Group;
|
private jaw!: THREE.Group;
|
||||||
|
private tailGroup!: THREE.Group;
|
||||||
private breathGroup!: THREE.Group;
|
private breathGroup!: THREE.Group;
|
||||||
private breathMat!: THREE.MeshBasicMaterial;
|
private breathMat!: THREE.MeshBasicMaterial;
|
||||||
private telegraphArc!: THREE.Mesh;
|
private telegraphArc!: THREE.Mesh;
|
||||||
@@ -108,131 +109,316 @@ export class Dragon extends Enemy {
|
|||||||
this.skinMat = new THREE.MeshToonMaterial({ color: 0xbb4433 });
|
this.skinMat = new THREE.MeshToonMaterial({ color: 0xbb4433 });
|
||||||
this.darkMat = new THREE.MeshToonMaterial({ color: 0x772a1e });
|
this.darkMat = new THREE.MeshToonMaterial({ color: 0x772a1e });
|
||||||
this.bellyMat = new THREE.MeshToonMaterial({ color: 0xe8c890 });
|
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 });
|
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) ---
|
// --- 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);
|
const torso = new THREE.Mesh(new THREE.SphereGeometry(1.0, 20, 14), this.skinMat);
|
||||||
torso.scale.set(1, 0.85, 1.7);
|
torso.scale.set(1, 0.85, 1.5);
|
||||||
torso.position.y = 1.4;
|
torso.position.set(0, 1.5, -0.1);
|
||||||
torso.castShadow = true;
|
torso.castShadow = true;
|
||||||
this.registerFadeMesh(torso);
|
this.registerFadeMesh(torso);
|
||||||
this.model.add(torso);
|
this.model.add(torso);
|
||||||
|
|
||||||
// Bauch (hell, unten)
|
const hips = new THREE.Mesh(new THREE.SphereGeometry(0.85, 18, 12), this.skinMat);
|
||||||
const belly = new THREE.Mesh(new THREE.SphereGeometry(0.75, 16, 10), this.bellyMat);
|
hips.scale.set(1.0, 0.9, 1.1);
|
||||||
belly.scale.set(1, 0.55, 1.6);
|
hips.position.set(0, 1.45, -0.85);
|
||||||
belly.position.set(0, 1.05, 0.1);
|
hips.castShadow = true;
|
||||||
this.registerFadeMesh(belly);
|
this.registerFadeMesh(hips);
|
||||||
this.model.add(belly);
|
this.model.add(hips);
|
||||||
|
|
||||||
// Schwanz (-Z, verjüngt)
|
// Bauchplatten (hell, segmentiert)
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
const seg = new THREE.Mesh(
|
const plate = new THREE.Mesh(
|
||||||
new THREE.SphereGeometry(0.5 - i * 0.08, 10, 8),
|
new THREE.SphereGeometry(0.58 - i * 0.06, 14, 8),
|
||||||
i % 2 === 0 ? this.skinMat : this.darkMat
|
this.bellyMat
|
||||||
);
|
);
|
||||||
seg.position.set(0, 1.4 - i * 0.06, -1.7 - i * 0.6);
|
plate.scale.set(0.95, 0.5, 0.8);
|
||||||
this.registerFadeMesh(seg);
|
plate.position.set(0, 1.02 - i * 0.02, 1.15 - i * 0.62);
|
||||||
this.model.add(seg);
|
this.registerFadeMesh(plate);
|
||||||
|
this.model.add(plate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schwanzspitze (Pfeil)
|
// Schwanz (geschwungen, mit Zacken und Spadel-Spitze)
|
||||||
const tailTip = new THREE.Mesh(new THREE.ConeGeometry(0.2, 0.8, 6), this.darkMat);
|
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.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.registerFadeMesh(tailTip);
|
||||||
this.model.add(tailTip);
|
this.tailGroup.add(tailTip);
|
||||||
|
|
||||||
// Kopf (+Z)
|
// Hals (S-Kurve nach oben)
|
||||||
const head = new THREE.Mesh(new THREE.SphereGeometry(0.7, 16, 12), this.skinMat);
|
for (const [y, z, r] of [[1.95, 1.3, 0.46], [2.2, 1.55, 0.4]]) {
|
||||||
head.scale.set(1, 0.9, 1.2);
|
const neck = new THREE.Mesh(new THREE.SphereGeometry(r, 14, 10), this.skinMat);
|
||||||
head.position.set(0, 2.0, 1.7);
|
neck.scale.set(0.85, 0.95, 1.1);
|
||||||
head.castShadow = true;
|
neck.position.set(0, y, z);
|
||||||
this.registerFadeMesh(head);
|
this.registerFadeMesh(neck);
|
||||||
this.model.add(head);
|
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)
|
// Schnauze (+Z)
|
||||||
const snout = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.35, 0.6), this.skinMat);
|
const snout = new THREE.Mesh(new THREE.BoxGeometry(0.44, 0.32, 0.7), this.skinMat);
|
||||||
snout.position.set(0, 1.85, 2.5);
|
snout.position.set(0, -0.1, 0.72);
|
||||||
this.registerFadeMesh(snout);
|
this.registerFadeMesh(snout);
|
||||||
this.model.add(snout);
|
headGroup.add(snout);
|
||||||
|
|
||||||
// Kiefer (beweglich, öffnet sich beim Feuerspeer)
|
// Stirnleiste (grimmiger Blick)
|
||||||
this.jaw = new THREE.Group();
|
const brow = new THREE.Mesh(new THREE.BoxGeometry(0.62, 0.14, 0.26), this.darkMat);
|
||||||
const jawMesh = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.22, 0.55), this.darkMat);
|
brow.position.set(0, 0.3, 0.36);
|
||||||
jawMesh.position.set(0, -0.1, 0.22);
|
brow.rotation.x = 0.12;
|
||||||
this.registerFadeMesh(jawMesh);
|
this.registerFadeMesh(brow);
|
||||||
this.jaw.add(jawMesh);
|
headGroup.add(brow);
|
||||||
this.jaw.position.set(0, 1.75, 2.4);
|
|
||||||
this.jaw.rotation.x = 0.15;
|
|
||||||
this.model.add(this.jaw);
|
|
||||||
|
|
||||||
// Augen (+Z, ragen aus dem Kopf heraus)
|
// Augen (+Z, ragen aus dem Kopf heraus)
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.13, 8, 6), this.eyeMat);
|
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), this.eyeMat);
|
||||||
eye.position.set(side * 0.32, 2.3, 2.42);
|
eye.position.set(side * 0.3, 0.2, 0.48);
|
||||||
this.model.add(eye);
|
headGroup.add(eye);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hörner
|
// Hörner (nach hinten geschwungen, zwei Paare)
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
const horn = new THREE.Mesh(new THREE.ConeGeometry(0.09, 0.6, 6), this.darkMat);
|
const horn = new THREE.Mesh(new THREE.ConeGeometry(0.09, 0.75, 6), this.darkMat);
|
||||||
horn.position.set(side * 0.28, 2.6, 1.4);
|
horn.position.set(side * 0.28, 0.42, -0.12);
|
||||||
horn.rotation.set(-0.9, 0, side * 0.5);
|
horn.rotation.set(-1.15, 0, side * 0.35);
|
||||||
this.registerFadeMesh(horn);
|
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
|
// Ohr-Fransen (seitlich nach hinten)
|
||||||
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)
|
|
||||||
for (const side of [-1, 1]) {
|
for (const side of [-1, 1]) {
|
||||||
for (const back of [true, false]) {
|
const frill = new THREE.Mesh(new THREE.ConeGeometry(0.12, 0.45, 5), this.darkMat);
|
||||||
const leg = new THREE.Mesh(
|
frill.position.set(side * 0.48, 0.1, -0.18);
|
||||||
new THREE.CylinderGeometry(0.16, 0.22, 0.7, 8),
|
frill.rotation.set(0.35, 0, side * -1.75);
|
||||||
this.darkMat
|
frill.scale.set(1, 1, 0.5);
|
||||||
);
|
this.registerFadeMesh(frill);
|
||||||
leg.position.set(side * 0.65, 0.35, back ? -0.8 : 0.7);
|
headGroup.add(frill);
|
||||||
this.registerFadeMesh(leg);
|
}
|
||||||
this.model.add(leg);
|
|
||||||
const claw = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.14, 0.5), this.skinMat);
|
// Nüstern
|
||||||
claw.position.set(side * 0.65, 0.08, back ? -0.8 : 0.7);
|
for (const side of [-1, 1]) {
|
||||||
this.registerFadeMesh(claw);
|
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);
|
this.model.add(claw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Flügel (animiertes Flattern) ---
|
// --- Flügel (Fledermaus-Silhouette mit Membran und Fingerknochen) ---
|
||||||
const wingGeom = new THREE.BoxGeometry(0.1, 2.4, 1.5);
|
const wingShape = new THREE.Shape();
|
||||||
this.wingL = new THREE.Group();
|
wingShape.moveTo(0.2, 0.1);
|
||||||
this.wingL.position.set(-0.7, 2.2, -0.2);
|
wingShape.quadraticCurveTo(0.8, -0.12, 1.5, 0.0); // Vorderkante zum Handgelenk
|
||||||
const wingLmesh = new THREE.Mesh(wingGeom, this.wingMat);
|
wingShape.quadraticCurveTo(2.5, 0.0, 3.05, 0.95); // zur Flügelspitze
|
||||||
wingLmesh.position.set(-1.6, 0, 0);
|
// Hinterkante: Fingerspitzen mit konkaven Bögen dazwischen
|
||||||
wingLmesh.rotation.z = Math.PI / 2;
|
wingShape.quadraticCurveTo(2.2, 1.0, 2.7, 1.65);
|
||||||
wingLmesh.castShadow = true;
|
wingShape.quadraticCurveTo(1.7, 1.45, 2.0, 2.1);
|
||||||
this.registerFadeMesh(wingLmesh);
|
wingShape.quadraticCurveTo(1.05, 1.6, 1.1, 2.25);
|
||||||
this.wingL.add(wingLmesh);
|
wingShape.quadraticCurveTo(0.4, 1.2, 0.15, 1.5);
|
||||||
this.model.add(this.wingL);
|
wingShape.closePath();
|
||||||
|
const wingGeom = new THREE.ShapeGeometry(wingShape, 10);
|
||||||
|
|
||||||
this.wingR = new THREE.Group();
|
const buildWing = (): THREE.Group => {
|
||||||
this.wingR.position.set(0.7, 2.2, -0.2);
|
const wing = new THREE.Group();
|
||||||
const wingRmesh = new THREE.Mesh(wingGeom, this.wingMat);
|
const membrane = new THREE.Mesh(wingGeom, this.wingMat);
|
||||||
wingRmesh.position.set(1.6, 0, 0);
|
membrane.rotation.x = -Math.PI / 2;
|
||||||
wingRmesh.rotation.z = -Math.PI / 2;
|
membrane.castShadow = true;
|
||||||
wingRmesh.castShadow = true;
|
this.registerFadeMesh(membrane);
|
||||||
this.registerFadeMesh(wingRmesh);
|
wing.add(membrane);
|
||||||
this.wingR.add(wingRmesh);
|
|
||||||
|
// 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.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 ---
|
// --- Effekte ---
|
||||||
|
|
||||||
// Boden-Schatten (folgt dem Drachen)
|
// 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);
|
const flame = new THREE.Mesh(new THREE.ConeGeometry(1.8, BREATH_RANGE, 16, 1, true), this.breathMat);
|
||||||
flame.rotation.x = Math.PI / 2;
|
flame.rotation.x = Math.PI / 2;
|
||||||
flame.position.z = BREATH_RANGE / 2;
|
flame.position.z = BREATH_RANGE / 2;
|
||||||
flame.position.y = 1.2;
|
|
||||||
this.breathGroup.add(flame);
|
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.breathGroup.visible = false;
|
||||||
this.body.add(this.breathGroup);
|
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);
|
const flap = Math.sin(this.waveTime * flapSpeed) * (this.state === 'fly' ? 0.7 : 0.2);
|
||||||
this.wingL.rotation.z = flap;
|
this.wingL.rotation.z = flap;
|
||||||
this.wingR.rotation.z = -flap;
|
this.wingR.rotation.z = -flap;
|
||||||
// Flügel-Einstellwinkel
|
// Flügel-Einstellwinkel (wegen scale.x-Spiegelung auf beiden Seiten gleich)
|
||||||
this.wingL.rotation.x = -0.3;
|
this.wingL.rotation.x = 0.3;
|
||||||
this.wingR.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
|
// Schatten bleibt am Boden
|
||||||
this.shadow.position.y = 0.06 - this.body.position.y;
|
this.shadow.position.y = 0.06 - this.body.position.y;
|
||||||
const shadowScale = 0.6 + this.body.position.y * 0.15;
|
const shadowScale = 0.6 + this.body.position.y * 0.15;
|
||||||
@@ -559,7 +752,7 @@ export class Dragon extends Enemy {
|
|||||||
this.telegraphArc.visible = false;
|
this.telegraphArc.visible = false;
|
||||||
this.breathGroup.visible = true;
|
this.breathGroup.visible = true;
|
||||||
this.breathActive = true;
|
this.breathActive = true;
|
||||||
this.jaw.rotation.x = -0.5;
|
this.jaw.rotation.x = 0.55;
|
||||||
}
|
}
|
||||||
} else if (this.breathPhase === 'active') {
|
} else if (this.breathPhase === 'active') {
|
||||||
this.breathTime -= dt;
|
this.breathTime -= dt;
|
||||||
@@ -589,7 +782,7 @@ export class Dragon extends Enemy {
|
|||||||
this.breathPhase = 'done';
|
this.breathPhase = 'done';
|
||||||
this.breathGroup.visible = false;
|
this.breathGroup.visible = false;
|
||||||
this.breathMat.opacity = 0;
|
this.breathMat.opacity = 0;
|
||||||
this.jaw.rotation.x = 0.15;
|
this.jaw.rotation.x = 0.12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user