Laufen und Rollen verbessert
This commit is contained in:
@@ -115,6 +115,7 @@
|
||||
"Sword And Shield Block Walk Backwards",
|
||||
"Sword And Shield Strafe Left",
|
||||
"Sword And Shield Strafe Right",
|
||||
"Sword And Shield Heavy Attack"
|
||||
"Sword And Shield Heavy Attack",
|
||||
"Roll"
|
||||
]
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class KeyBindings {
|
||||
public int drawMeleeWeapon = KeyInput.KEY_1;
|
||||
public int drawRangedWeapon = KeyInput.KEY_2;
|
||||
public int sheathWeapon = KeyInput.KEY_X;
|
||||
public int roll = KeyInput.KEY_LCONTROL;
|
||||
|
||||
/** Metadaten für die Config-UI: Feldname im Objekt + Anzeigename. */
|
||||
public static final String[][] ENTRIES = {
|
||||
@@ -42,6 +43,7 @@ public class KeyBindings {
|
||||
{"drawMeleeWeapon", "Nahkampfwaffe ziehen"},
|
||||
{"drawRangedWeapon", "Fernkampfwaffe ziehen"},
|
||||
{"sheathWeapon", "Waffe wegstecken"},
|
||||
{"roll", "Rolle"},
|
||||
};
|
||||
|
||||
public int get(String fieldName) {
|
||||
|
||||
@@ -7,9 +7,16 @@ import com.jme3.anim.tween.action.BaseAction;
|
||||
import com.jme3.bullet.control.CharacterControl;
|
||||
import com.jme3.input.InputManager;
|
||||
import com.jme3.input.MouseInput;
|
||||
import com.jme3.input.RawInputListener;
|
||||
import com.jme3.input.controls.ActionListener;
|
||||
import com.jme3.input.controls.KeyTrigger;
|
||||
import com.jme3.input.controls.MouseButtonTrigger;
|
||||
import com.jme3.input.event.JoyAxisEvent;
|
||||
import com.jme3.input.event.JoyButtonEvent;
|
||||
import com.jme3.input.event.KeyInputEvent;
|
||||
import com.jme3.input.event.MouseButtonEvent;
|
||||
import com.jme3.input.event.MouseMotionEvent;
|
||||
import com.jme3.input.event.TouchEvent;
|
||||
import de.blight.common.model.ItemSubCategory;
|
||||
import de.blight.common.model.MainCharacter;
|
||||
import com.jme3.math.Quaternion;
|
||||
@@ -64,8 +71,8 @@ public class PlayerInputControl {
|
||||
private static final int STAMINA_ATTACK5_COST = 20;
|
||||
private static final int STAMINA_HEAVY_COST = 30;
|
||||
private static final int STAMINA_BLOCK_HIT_COST = 30;
|
||||
private static final int STAMINA_ROLL_COST = 20;
|
||||
private static final float ROLL_DOUBLE_TAP_WINDOW = 0.3f;
|
||||
private static final int STAMINA_ROLL_COST = 20;
|
||||
private static final float ROLL_AIRBORNE_THRESHOLD = 0.90f; // Hips-Y unter 90% Baseline → Luftphase
|
||||
/** Dauer der Ziehen/Wegstecken-Animation in Sekunden. */
|
||||
private static final float DRAW_DURATION = 0.85f;
|
||||
// Ziel-Quaternions für die Ziehen-Animation: aus Heavy-Attack-Wind-Up bei t=0.933s gelesen
|
||||
@@ -95,10 +102,12 @@ public class PlayerInputControl {
|
||||
private static final String ACT_SHEATH = "SheathWeapon";
|
||||
private static final String ACT_COMBAT_ATK = "CombatAttack";
|
||||
private static final String ACT_COMBAT_ALT = "CombatAltAction";
|
||||
private static final String ACT_ROLL = "Roll";
|
||||
|
||||
private static final String[] ACTION_NAMES = {
|
||||
"Forward", "Backward", "Left", "Right", "Jump", "Sprint", "Walk",
|
||||
ACT_DRAW_MELEE, ACT_DRAW_RANGED, ACT_SHEATH, ACT_COMBAT_ATK, ACT_COMBAT_ALT
|
||||
ACT_DRAW_MELEE, ACT_DRAW_RANGED, ACT_SHEATH, ACT_COMBAT_ATK, ACT_COMBAT_ALT,
|
||||
ACT_ROLL
|
||||
};
|
||||
|
||||
private final InputManager inputManager;
|
||||
@@ -107,6 +116,8 @@ public class PlayerInputControl {
|
||||
private CharacterControl physicsChar;
|
||||
private Spatial visual;
|
||||
|
||||
private int sprintKey = -1;
|
||||
private RawInputListener sprintRawListener = null;
|
||||
private boolean forward, backward, left, right, sprint, walk;
|
||||
private boolean paused = false;
|
||||
|
||||
@@ -263,15 +274,13 @@ public class PlayerInputControl {
|
||||
private Vector3f autopilotDir = null;
|
||||
|
||||
// ── Dodge Roll ────────────────────────────────────────────────────────────
|
||||
// Double-tap-Timer pro Richtung: -1 = kein erster Tap ausstehend
|
||||
private float rollTapFwd = -1f;
|
||||
private float rollTapBwd = -1f;
|
||||
private float rollTapLeft = -1f;
|
||||
private float rollTapRight = -1f;
|
||||
private int pendingRollDir = 0; // 1=vor, 2=zurück, 3=links, 4=rechts
|
||||
private boolean rollActive = false;
|
||||
private float rollRemaining = 0f;
|
||||
private Vector3f rollDir = null;
|
||||
private boolean rollActive = false;
|
||||
private float rollRemaining = 0f;
|
||||
private float rollInitRemaining = 0f;
|
||||
private boolean rollBaselineCaptured = false;
|
||||
private float rollHipsBaseY = 0f;
|
||||
private boolean rollAirborne = false;
|
||||
private Vector3f rollDir = null;
|
||||
|
||||
// Pre-allokierte Scratch-Objekte — keine Heap-Allokation pro Frame in update()
|
||||
private final Vector3f scratchCamDir = new Vector3f();
|
||||
@@ -285,11 +294,11 @@ public class PlayerInputControl {
|
||||
private final ActionListener actionListener = (name, isPressed, tpf) -> {
|
||||
if (paused) return;
|
||||
switch (name) {
|
||||
case "Forward" -> { if (isPressed) detectRollTap(1); forward = isPressed; }
|
||||
case "Backward" -> { if (isPressed) detectRollTap(2); backward = isPressed; }
|
||||
case "Left" -> { if (isPressed) detectRollTap(3); left = isPressed; }
|
||||
case "Right" -> { if (isPressed) detectRollTap(4); right = isPressed; }
|
||||
case "Sprint" -> sprint = isPressed;
|
||||
case "Forward" -> forward = isPressed;
|
||||
case "Backward" -> backward = isPressed;
|
||||
case "Left" -> left = isPressed;
|
||||
case "Right" -> right = isPressed;
|
||||
case "Sprint" -> sprint = isPressed;
|
||||
case "Walk" -> walk = isPressed;
|
||||
case "Jump" -> { if (isPressed && physicsChar != null) { physicsChar.jump(); jumpFrames = 12; } }
|
||||
case ACT_DRAW_MELEE -> { if (isPressed) handleDrawMelee(); }
|
||||
@@ -324,6 +333,7 @@ public class PlayerInputControl {
|
||||
}
|
||||
}
|
||||
case ACT_COMBAT_ALT -> handleCombatAlt(isPressed);
|
||||
case ACT_ROLL -> { if (isPressed) triggerRollFromInput(); }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -680,12 +690,28 @@ public class PlayerInputControl {
|
||||
inputManager.addMapping("Right", new KeyTrigger(kb.right));
|
||||
inputManager.addMapping("Jump", new KeyTrigger(kb.jump));
|
||||
inputManager.addMapping("Sprint", new KeyTrigger(kb.sprint));
|
||||
if (sprintRawListener != null) inputManager.removeRawInputListener(sprintRawListener);
|
||||
sprintKey = kb.sprint;
|
||||
sprintRawListener = new RawInputListener() {
|
||||
public void onKeyEvent(KeyInputEvent e) {
|
||||
if (e.getKeyCode() == sprintKey) sprint = !e.isReleased();
|
||||
}
|
||||
public void beginInput() {}
|
||||
public void endInput() {}
|
||||
public void onJoyAxisEvent(JoyAxisEvent e) {}
|
||||
public void onJoyButtonEvent(JoyButtonEvent e) {}
|
||||
public void onMouseMotionEvent(MouseMotionEvent e) {}
|
||||
public void onMouseButtonEvent(MouseButtonEvent e) {}
|
||||
public void onTouchEvent(TouchEvent e) {}
|
||||
};
|
||||
inputManager.addRawInputListener(sprintRawListener);
|
||||
inputManager.addMapping("Walk", new KeyTrigger(kb.walk));
|
||||
inputManager.addMapping(ACT_DRAW_MELEE, new KeyTrigger(kb.drawMeleeWeapon));
|
||||
inputManager.addMapping(ACT_DRAW_RANGED, new KeyTrigger(kb.drawRangedWeapon));
|
||||
inputManager.addMapping(ACT_SHEATH, new KeyTrigger(kb.sheathWeapon));
|
||||
inputManager.addMapping(ACT_COMBAT_ATK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
|
||||
inputManager.addMapping(ACT_COMBAT_ALT, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
|
||||
inputManager.addMapping(ACT_ROLL, new KeyTrigger(kb.roll));
|
||||
inputManager.addListener(actionListener, ACTION_NAMES);
|
||||
}
|
||||
|
||||
@@ -716,29 +742,6 @@ public class PlayerInputControl {
|
||||
return;
|
||||
}
|
||||
|
||||
// Double-tap-Timer ablaufen lassen
|
||||
if (rollTapFwd > 0f) { rollTapFwd -= tpf; if (rollTapFwd < 0f) rollTapFwd = -1f; }
|
||||
if (rollTapBwd > 0f) { rollTapBwd -= tpf; if (rollTapBwd < 0f) rollTapBwd = -1f; }
|
||||
if (rollTapLeft > 0f) { rollTapLeft -= tpf; if (rollTapLeft < 0f) rollTapLeft = -1f; }
|
||||
if (rollTapRight > 0f) { rollTapRight -= tpf; if (rollTapRight < 0f) rollTapRight = -1f; }
|
||||
|
||||
// Doppeltippen → Rolle einleiten (nicht während Kampf, Block, Pickup usw.)
|
||||
if (pendingRollDir != 0) {
|
||||
if (!rollActive && !pickupActive && !blockingAnimActive && !turnActive
|
||||
&& !lockedInPlace && !combatAnimActive && !isBlocking()) {
|
||||
cam.getDirection(scratchCamDir).setY(0).normalizeLocal();
|
||||
cam.getLeft(scratchCamLeft).setY(0).normalizeLocal();
|
||||
Vector3f dir = switch (pendingRollDir) {
|
||||
case 1 -> scratchCamDir.clone();
|
||||
case 2 -> scratchCamDir.clone().negateLocal();
|
||||
case 3 -> scratchCamLeft.clone();
|
||||
default -> scratchCamLeft.clone().negateLocal();
|
||||
};
|
||||
startRoll(dir);
|
||||
}
|
||||
pendingRollDir = 0;
|
||||
}
|
||||
|
||||
// Navigator: hat Vorrang vor allem außer Pause
|
||||
if (navigator != null && navigator.isActive()) {
|
||||
navigator.update(tpf);
|
||||
@@ -763,11 +766,42 @@ public class PlayerInputControl {
|
||||
rollActive = false;
|
||||
rollDir = null;
|
||||
currentAnim = null;
|
||||
if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
} else {
|
||||
if (physicsChar != null) {
|
||||
physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
boolean rollEndedEarly = false;
|
||||
if (rollInitRemaining > 0f) {
|
||||
rollInitRemaining -= tpf;
|
||||
if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
} else if (!rollBaselineCaptured && hipsJoint != null) {
|
||||
rollBaselineCaptured = true;
|
||||
rollHipsBaseY = hipsJoint.getModelTransform().getTranslation().y;
|
||||
rollAirborne = false;
|
||||
log.info("[ROLL] Baseline hipsY={}", rollHipsBaseY);
|
||||
if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
} else if (rollBaselineCaptured && hipsJoint != null && physicsChar != null) {
|
||||
float hipsY = hipsJoint.getModelTransform().getTranslation().y;
|
||||
boolean wasAirborne = rollAirborne;
|
||||
rollAirborne = hipsY < rollHipsBaseY * ROLL_AIRBORNE_THRESHOLD;
|
||||
if (rollAirborne != wasAirborne) {
|
||||
log.info("[ROLL] hipsY={} baseY={} → {}", hipsY, rollHipsBaseY,
|
||||
rollAirborne ? "AIRBORNE" : "GROUNDED");
|
||||
}
|
||||
// Gelandet + Bewegungstaste → Rolle beenden, direkt in Lauf-Logik fallen
|
||||
if (wasAirborne && !rollAirborne && (forward || backward || left || right)) {
|
||||
rollActive = false;
|
||||
rollDir = null;
|
||||
currentAnim = null;
|
||||
rollEndedEarly = true;
|
||||
physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
} else if (rollAirborne && rollDir != null) {
|
||||
physicsChar.setWalkDirection(scratchWalk.set(rollDir).multLocal(MOVE_SPEED));
|
||||
} else {
|
||||
physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
}
|
||||
} else {
|
||||
if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
}
|
||||
return;
|
||||
if (!rollEndedEarly) return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1331,32 +1365,28 @@ public class PlayerInputControl {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void detectRollTap(int dir) {
|
||||
float cur = switch (dir) {
|
||||
case 1 -> rollTapFwd;
|
||||
case 2 -> rollTapBwd;
|
||||
case 3 -> rollTapLeft;
|
||||
default -> rollTapRight;
|
||||
};
|
||||
if (cur > 0f) {
|
||||
pendingRollDir = dir;
|
||||
} else {
|
||||
switch (dir) {
|
||||
case 1 -> rollTapFwd = ROLL_DOUBLE_TAP_WINDOW;
|
||||
case 2 -> rollTapBwd = ROLL_DOUBLE_TAP_WINDOW;
|
||||
case 3 -> rollTapLeft = ROLL_DOUBLE_TAP_WINDOW;
|
||||
default -> rollTapRight = ROLL_DOUBLE_TAP_WINDOW;
|
||||
}
|
||||
}
|
||||
private void triggerRollFromInput() {
|
||||
if (rollActive || pickupActive || blockingAnimActive || turnActive
|
||||
|| lockedInPlace || combatAnimActive || isBlocking()) return;
|
||||
cam.getDirection(scratchCamDir).setY(0).normalizeLocal();
|
||||
cam.getLeft(scratchCamLeft).setY(0).normalizeLocal();
|
||||
Vector3f rollDirVec;
|
||||
if (backward && !forward) rollDirVec = scratchCamDir.clone().negateLocal();
|
||||
else if (left && !right) rollDirVec = scratchCamLeft.clone();
|
||||
else if (right && !left) rollDirVec = scratchCamLeft.clone().negateLocal();
|
||||
else rollDirVec = scratchCamDir.clone();
|
||||
startRoll(rollDirVec);
|
||||
}
|
||||
|
||||
private void startRoll(Vector3f dir) {
|
||||
if (!spendStamina(STAMINA_ROLL_COST)) return;
|
||||
nextTransitionLength = 0.1f;
|
||||
rollActive = true;
|
||||
rollRemaining = resolveClipLength(AnimationAction.ROLL, 0.7f);
|
||||
rollDir = dir;
|
||||
forward = backward = left = right = false;
|
||||
rollActive = true;
|
||||
rollRemaining = resolveClipLength(AnimationAction.ROLL, 0.7f);
|
||||
rollInitRemaining = 0.12f;
|
||||
rollBaselineCaptured = false;
|
||||
rollAirborne = false;
|
||||
rollDir = dir;
|
||||
if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||
if (visual != null) {
|
||||
scratchRot.lookAt(dir, Vector3f.UNIT_Y);
|
||||
|
||||
Reference in New Issue
Block a user