Rework necromancer, archer, shaman and golem models with procedural animations
This commit is contained in:
+153
-38
@@ -90,21 +90,66 @@ export class Necromancer extends Enemy {
|
||||
this.darkMat = new THREE.MeshToonMaterial({ color: 0x2a1445 });
|
||||
this.glowMat = new THREE.MeshBasicMaterial({ color: 0xbb66ff });
|
||||
this.eyeMat = new THREE.MeshBasicMaterial({ color: 0xdd88ff });
|
||||
const boneMat = new THREE.MeshToonMaterial({ color: 0xf0e6cf });
|
||||
const faceMat = new THREE.MeshBasicMaterial({ color: 0x0a0614 });
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// Robe (umgedrehter Kegel, schwingt beim Schweben)
|
||||
const robe = new THREE.Mesh(new THREE.ConeGeometry(0.9, 1.8, 12, 1, true), this.robeMat);
|
||||
robe.position.y = -0.2;
|
||||
robe.rotation.x = Math.PI;
|
||||
// --- Robe (Blickrichtung +Z, schwebt: unten offen und zerfetzt) ---
|
||||
const robeProfile = [
|
||||
new THREE.Vector2(0.75, -1.15), // Saum
|
||||
new THREE.Vector2(0.62, -0.75),
|
||||
new THREE.Vector2(0.45, -0.2),
|
||||
new THREE.Vector2(0.38, 0.25),
|
||||
new THREE.Vector2(0.5, 0.55), // Schulteransatz
|
||||
new THREE.Vector2(0.32, 0.78), // Hals
|
||||
];
|
||||
const robe = new THREE.Mesh(new THREE.LatheGeometry(robeProfile, 14), this.robeMat);
|
||||
robe.castShadow = true;
|
||||
this.registerFadeMesh(robe);
|
||||
this.model.add(robe);
|
||||
|
||||
// Rocksaum
|
||||
const hem = new THREE.Mesh(new THREE.CircleGeometry(0.95, 12), this.darkMat);
|
||||
hem.rotation.x = -Math.PI / 2;
|
||||
hem.position.y = -1.05;
|
||||
this.registerFadeMesh(hem);
|
||||
this.model.add(hem);
|
||||
// Innenschatten im Saum (von unten sichtbar)
|
||||
const hemInner = new THREE.Mesh(new THREE.CircleGeometry(0.7, 14), this.darkMat);
|
||||
hemInner.rotation.x = Math.PI / 2;
|
||||
hemInner.position.y = -1.08;
|
||||
this.registerFadeMesh(hemInner);
|
||||
this.model.add(hemInner);
|
||||
|
||||
// Zerfetzte Saumfetzen
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const a = (i / 8) * Math.PI * 2;
|
||||
const tatter = new THREE.Mesh(
|
||||
new THREE.ConeGeometry(0.08, 0.4, 4),
|
||||
i % 2 === 0 ? this.robeMat : this.darkMat
|
||||
);
|
||||
tatter.rotation.x = Math.PI;
|
||||
tatter.position.set(Math.sin(a) * 0.68, -1.28, Math.cos(a) * 0.68);
|
||||
this.registerFadeMesh(tatter);
|
||||
this.model.add(tatter);
|
||||
}
|
||||
|
||||
// Gürtelkordel + Quaste
|
||||
const belt = new THREE.Mesh(new THREE.TorusGeometry(0.42, 0.035, 6, 16), this.darkMat);
|
||||
belt.rotation.x = Math.PI / 2;
|
||||
belt.position.y = 0.12;
|
||||
this.registerFadeMesh(belt);
|
||||
this.model.add(belt);
|
||||
const tasselCord = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.28, 5), this.darkMat);
|
||||
tasselCord.position.set(-0.3, -0.05, 0.36);
|
||||
this.registerFadeMesh(tasselCord);
|
||||
this.model.add(tasselCord);
|
||||
const tassel = new THREE.Mesh(new THREE.ConeGeometry(0.05, 0.14, 5), boneMat);
|
||||
tassel.rotation.x = Math.PI;
|
||||
tassel.position.set(-0.3, -0.24, 0.38);
|
||||
registerBone(tassel);
|
||||
this.model.add(tassel);
|
||||
|
||||
// Oberkörper
|
||||
const body = new THREE.Mesh(new THREE.SphereGeometry(0.55, 16, 12), this.robeMat);
|
||||
@@ -114,46 +159,116 @@ export class Necromancer extends Enemy {
|
||||
this.registerFadeMesh(body);
|
||||
this.model.add(body);
|
||||
|
||||
// Kapuze
|
||||
const hood = new THREE.Mesh(new THREE.ConeGeometry(0.42, 0.8, 12, 1, true), this.darkMat);
|
||||
hood.position.set(0, 1.1, 0.1);
|
||||
hood.rotation.x = 0.15;
|
||||
// Kragen
|
||||
const collar = new THREE.Mesh(new THREE.TorusGeometry(0.22, 0.045, 6, 14), this.darkMat);
|
||||
collar.rotation.x = Math.PI / 2 - 0.2;
|
||||
collar.position.set(0, 0.85, 0.08);
|
||||
this.registerFadeMesh(collar);
|
||||
this.model.add(collar);
|
||||
|
||||
// Schulterpanzer mit Spitzen
|
||||
for (const side of [-1, 1]) {
|
||||
const pauldron = new THREE.Mesh(new THREE.SphereGeometry(0.26, 12, 8), this.darkMat);
|
||||
pauldron.scale.set(1.25, 0.65, 1.25);
|
||||
pauldron.position.set(side * 0.45, 0.78, 0);
|
||||
this.registerFadeMesh(pauldron);
|
||||
this.model.add(pauldron);
|
||||
const spike = new THREE.Mesh(new THREE.ConeGeometry(0.06, 0.22, 5), this.darkMat);
|
||||
spike.position.set(side * 0.55, 0.95, 0);
|
||||
spike.rotation.z = side * -0.5;
|
||||
this.registerFadeMesh(spike);
|
||||
this.model.add(spike);
|
||||
}
|
||||
|
||||
// Kapuze mit Krempe und schwarzem Gesichts-Leerraum
|
||||
const hood = new THREE.Mesh(new THREE.ConeGeometry(0.5, 0.95, 12, 1, true), this.darkMat);
|
||||
hood.position.set(0, 1.18, 0.02);
|
||||
hood.rotation.x = 0.22;
|
||||
hood.castShadow = true;
|
||||
this.registerFadeMesh(hood);
|
||||
this.model.add(hood);
|
||||
|
||||
// Kapuzen-Öffnung (+Z, dunkel)
|
||||
const hoodFace = new THREE.Mesh(new THREE.CircleGeometry(0.36, 12), this.darkMat);
|
||||
hoodFace.position.set(0, 1.02, 0.42);
|
||||
hoodFace.rotation.y = Math.PI / 2 - 0.1;
|
||||
hoodFace.rotation.x = -0.35;
|
||||
this.registerFadeMesh(hoodFace);
|
||||
this.model.add(hoodFace);
|
||||
const hoodRim = new THREE.Mesh(new THREE.TorusGeometry(0.34, 0.05, 8, 16), this.darkMat);
|
||||
hoodRim.rotation.x = -0.5;
|
||||
hoodRim.position.set(0, 1.05, 0.34);
|
||||
this.registerFadeMesh(hoodRim);
|
||||
this.model.add(hoodRim);
|
||||
const face = new THREE.Mesh(new THREE.SphereGeometry(0.3, 12, 10), faceMat);
|
||||
face.scale.set(1, 1.1, 0.8);
|
||||
face.position.set(0, 1.04, 0.26);
|
||||
this.registerFadeMesh(face);
|
||||
this.model.add(face);
|
||||
|
||||
// Leuchtende Augen in der Kapuze (+Z)
|
||||
for (const side of [-1, 1]) {
|
||||
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.07, 8, 6), this.eyeMat);
|
||||
eye.position.set(side * 0.13, 1.05, 0.44);
|
||||
const eye = new THREE.Mesh(new THREE.SphereGeometry(0.075, 8, 6), this.eyeMat);
|
||||
eye.position.set(side * 0.12, 1.08, 0.48);
|
||||
this.model.add(eye);
|
||||
}
|
||||
|
||||
// Glühender Amulett-Kern
|
||||
// Amulett + Runen auf der Robe (glühen, färben sich im Enrage)
|
||||
const amulet = new THREE.Mesh(new THREE.SphereGeometry(0.08, 8, 6), this.glowMat);
|
||||
amulet.position.set(0, 0.62, 0.52);
|
||||
amulet.position.set(0, 0.6, 0.5);
|
||||
this.model.add(amulet);
|
||||
const runeSpots: [number, number, number][] = [
|
||||
[0, -0.15, 0.5],
|
||||
[-0.2, -0.5, 0.5],
|
||||
[0.2, -0.5, 0.5],
|
||||
];
|
||||
for (const [x, y, z] of runeSpots) {
|
||||
const rune = new THREE.Mesh(new THREE.OctahedronGeometry(0.06), this.glowMat);
|
||||
rune.scale.set(1, 1.7, 0.5);
|
||||
rune.position.set(x, y, z);
|
||||
this.model.add(rune);
|
||||
}
|
||||
|
||||
// Arm mit Stab
|
||||
const arm = new THREE.Mesh(new THREE.CylinderGeometry(0.09, 0.12, 0.6, 8), this.darkMat);
|
||||
arm.position.set(0.55, 0.7, 0.15);
|
||||
arm.rotation.z = -0.6;
|
||||
this.registerFadeMesh(arm);
|
||||
this.model.add(arm);
|
||||
// Rechter Arm: Ärmel zum Stab
|
||||
const sleeveR = new THREE.Mesh(new THREE.CylinderGeometry(0.09, 0.16, 0.55, 8), this.robeMat);
|
||||
sleeveR.position.set(0.65, 0.84, 0.14);
|
||||
sleeveR.rotation.z = 1.2;
|
||||
this.registerFadeMesh(sleeveR);
|
||||
this.model.add(sleeveR);
|
||||
const handR = new THREE.Mesh(new THREE.SphereGeometry(0.1, 8, 6), boneMat);
|
||||
handR.position.set(0.85, 0.9, 0.23);
|
||||
registerBone(handR);
|
||||
this.model.add(handR);
|
||||
|
||||
const staff = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 2.0, 8), this.darkMat);
|
||||
staff.position.set(0.85, 1.3, 0.25);
|
||||
staff.rotation.z = 0.35;
|
||||
staff.rotation.x = 0.2;
|
||||
// Linker Arm: Ärmel nach vorn (Beschwörungs-Geste) + Energiekugel
|
||||
const sleeveL = new THREE.Mesh(new THREE.CylinderGeometry(0.09, 0.18, 0.6, 8), this.robeMat);
|
||||
sleeveL.position.set(-0.55, 0.75, 0.32);
|
||||
sleeveL.rotation.x = -1.25;
|
||||
sleeveL.rotation.z = -0.35;
|
||||
this.registerFadeMesh(sleeveL);
|
||||
this.model.add(sleeveL);
|
||||
const handL = new THREE.Mesh(new THREE.SphereGeometry(0.1, 8, 6), boneMat);
|
||||
handL.position.set(-0.68, 0.72, 0.62);
|
||||
registerBone(handL);
|
||||
this.model.add(handL);
|
||||
const castOrb = new THREE.Mesh(new THREE.SphereGeometry(0.09, 8, 6), this.glowMat);
|
||||
castOrb.position.set(-0.7, 0.92, 0.68);
|
||||
this.model.add(castOrb);
|
||||
|
||||
// Stab mit Manschetten, Totenkopf und schwebendem Orb
|
||||
const staff = new THREE.Mesh(new THREE.CylinderGeometry(0.045, 0.045, 2.4, 8), this.darkMat);
|
||||
staff.position.set(0.8, 1.2, 0.28);
|
||||
staff.rotation.z = 0.18;
|
||||
staff.rotation.x = 0.15;
|
||||
this.registerFadeMesh(staff);
|
||||
this.model.add(staff);
|
||||
for (const [x, y, z] of [[0.88, 0.76, 0.21], [0.96, 0.31, 0.15]] as const) {
|
||||
const collarM = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.07, 8), this.darkMat);
|
||||
collarM.position.set(x, y, z);
|
||||
this.registerFadeMesh(collarM);
|
||||
this.model.add(collarM);
|
||||
}
|
||||
const skull = new THREE.Mesh(new THREE.SphereGeometry(0.15, 10, 8), boneMat);
|
||||
skull.position.set(0.58, 2.42, 0.47);
|
||||
registerBone(skull);
|
||||
this.model.add(skull);
|
||||
for (const side of [-1, 1]) {
|
||||
const socket = new THREE.Mesh(new THREE.SphereGeometry(0.03, 6, 4), this.darkMat);
|
||||
socket.position.set(0.58 + side * 0.05, 2.44, 0.6);
|
||||
this.model.add(socket);
|
||||
}
|
||||
|
||||
const orbMat = new THREE.MeshBasicMaterial({
|
||||
color: 0xbb66ff,
|
||||
@@ -162,8 +277,8 @@ export class Necromancer extends Enemy {
|
||||
blending: THREE.AdditiveBlending,
|
||||
depthWrite: false,
|
||||
});
|
||||
const orb = new THREE.Mesh(new THREE.SphereGeometry(0.16, 10, 8), orbMat);
|
||||
orb.position.set(1.2, 1.95, 0.35);
|
||||
const orb = new THREE.Mesh(new THREE.SphereGeometry(0.14, 10, 8), orbMat);
|
||||
orb.position.set(0.58, 2.66, 0.47);
|
||||
this.model.add(orb);
|
||||
this.orbMat = orbMat;
|
||||
this.orb = orb;
|
||||
|
||||
Reference in New Issue
Block a user