Kampfsystem: NPC verhalten weiter angepasst, einige Bugs gefixed

This commit is contained in:
2026-09-27 20:57:04 +02:00
parent da8e5e99f1
commit 0a7c7bc6d1
26 changed files with 678 additions and 38 deletions

View File

@@ -663,6 +663,18 @@ public class AnimPreviewState extends BaseAppState {
// j3o-Quelle: Clip bereits gespeichert – nur Preview, kein erneutes Speichern.
// snap passiert ausschließlich zur Laufzeit im Spiel via AnimationLibrary.
boolean isJ3oSource = animAssetPath.matches("(?i).*\\.j3o$");
// FBX-Import erfordert ein geladenes Charakter-Modell.
// Ohne Ziel-Armatur kann normalizeAndRetargetClip die $AssimpFbx$_Translation-Orphan-Joints
// nicht in den Hips-Track falten → Hips-Translation geht verloren → Charakter schrumpft.
if (!isJ3oSource && srcArm != null && dstArm == null) {
input.animPreviewStatus =
"Fehler: FBX-Import erfordert ein geladenes Charakter-Modell. " +
"Bitte zuerst das Modell laden, dann die Animation importieren.";
LOG.warn("[AnimPreview] FBX-Import ohne Charakter-Modell abgebrochen: {}", animAssetPath);
return;
}
com.jme3.anim.Armature snapArm = dstArm != null ? dstArm : srcArm;
int saved = 0;
@@ -1248,6 +1260,32 @@ public class AnimPreviewState extends BaseAppState {
if (armature != null) holder.addControl(new SkinningControl(armature));
java.nio.file.Files.createDirectories(outFile.getParent());
BinaryExporter.getInstance().save(holder, outFile.toFile());
syncClipToMirrors(outFile);
}
private void syncClipToMirrors(java.nio.file.Path savedFile) {
java.nio.file.Path rel;
try {
rel = ASSET_ROOT.relativize(savedFile);
} catch (Exception e) {
return;
}
java.nio.file.Path projectRoot = de.blight.editor.ProjectRoot.PATH;
java.nio.file.Path[] mirrors = {
projectRoot.resolve("blight-assets").resolve("bin").resolve("main").resolve(rel),
projectRoot.resolve("blight-assets").resolve("build").resolve("resources").resolve("main").resolve(rel),
};
for (java.nio.file.Path mirror : mirrors) {
try {
if (java.nio.file.Files.exists(mirror.getParent())) {
java.nio.file.Files.copy(savedFile, mirror,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
LOG.info("[AnimPreview] Spiegel aktualisiert: {}", mirror);
}
} catch (Exception e) {
LOG.warn("[AnimPreview] Spiegel-Update fehlgeschlagen ({}): {}", mirror, e.getMessage());
}
}
}
// ── FBX-Import ───────────────────────────────────────────────────────────
@@ -1924,12 +1962,27 @@ public class AnimPreviewState extends BaseAppState {
}
translations = retargeted;
}
// Scale-Tracks entfernen – FBX-Skalierung (Mixamo cm, Rig-Scale) schrumpft sonst den Charakter.
newTracks.add(new com.jme3.anim.TransformTrack(dstJoint, tt.getTimes(),
translations, tt.getRotations(), tt.getScales()));
translations, tt.getRotations(), null));
}
if (newTracks.isEmpty()) return null;
AnimClip result = new AnimClip(src.getName());
result.setTracks(newTracks.toArray(new com.jme3.anim.AnimTrack[0]));
// Diagnose: Hips-Translation im ersten Frame ausgeben
for (com.jme3.anim.AnimTrack<?> t : newTracks) {
if (t instanceof com.jme3.anim.TransformTrack tt
&& tt.getTarget() instanceof com.jme3.anim.Joint j
&& j.getName().contains("Hips") && !j.getName().contains("$AssimpFbx$")
&& tt.getTranslations() != null && tt.getTranslations().length > 0) {
com.jme3.math.Vector3f tr0 = tt.getTranslations()[0];
LOG.info("[AnimPreview] normalizeAndRetarget '{}': Hips[0]=({},{},{}) tracks={} orphansFolded={}",
src.getName(),
String.format("%.3f", tr0.x), String.format("%.3f", tr0.y), String.format("%.3f", tr0.z),
newTracks.size(), orphanByParent.size());
break;
}
}
LOG.debug("[AnimPreview] normalizeAndRetarget '{}': {} Tracks, length={}", src.getName(), newTracks.size(), result.getLength());
return result;
}