Added the ability to sprint

When you double-tap the W key, the character starts moving faster
This commit is contained in:
Sergey Karmanov 2021-01-11 00:17:31 +03:00
parent 9d305890d1
commit a67d3ba1bb
2 changed files with 354 additions and 320 deletions

View File

@ -65,6 +65,11 @@ public class LayerTestGUI extends GUILayer {
() -> String.format("Flying: %5s (Space bar x2)", TestPlayerControls.getInstance().isFlying()) () -> String.format("Flying: %5s (Space bar x2)", TestPlayerControls.getInstance().isFlying())
)); ));
panel.addChild(new Label(
"IsSprintDisplay", font,
() -> String.format("Sprint: %5s (W x2)", TestPlayerControls.getInstance().isSprint())
));
panel.addChild(new Label( panel.addChild(new Label(
"IsMouseCapturedDisplay", font, "IsMouseCapturedDisplay", font,
() -> String.format("Mouse captured: %5s (esc)", TestPlayerControls.getInstance().isMouseCaptured()) () -> String.format("Mouse captured: %5s (esc)", TestPlayerControls.getInstance().isMouseCaptured())

View File

@ -1,11 +1,10 @@
package ru.windcorp.progressia.test; package ru.windcorp.progressia.test;
import org.lwjgl.glfw.GLFW;
import glm.Glm; import glm.Glm;
import glm.mat._3.Mat3; import glm.mat._3.Mat3;
import glm.vec._2.Vec2; import glm.vec._2.Vec2;
import glm.vec._3.Vec3; import glm.vec._3.Vec3;
import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
@ -25,333 +24,363 @@ import ru.windcorp.progressia.server.ServerState;
public class TestPlayerControls { public class TestPlayerControls {
private static final TestPlayerControls INSTANCE = new TestPlayerControls(); private static final TestPlayerControls INSTANCE = new TestPlayerControls();
public static TestPlayerControls getInstance() { public static TestPlayerControls getInstance() {
return INSTANCE; return INSTANCE;
} }
private TestPlayerControls() {} private TestPlayerControls() {
}
private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS; private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS;
private static final double MIN_JUMP_DELAY = 300 * Units.MILLISECONDS; private static final double MODE_SPRINT_SWITCH_MAX_DELAY = 100 * Units.MILLISECONDS;
private static final double MIN_JUMP_DELAY = 300 * Units.MILLISECONDS;
// Horizontal and vertical max control speed when flying // Horizontal and vertical max control speed when flying
private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND; private static final float FLYING_SPEED = 6.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority // (0; 1], 1 is instant change, 0 is no control authority
private static final float FLYING_CONTROL_AUTHORITY = Units.get("2 1/s"); private static final float FLYING_CONTROL_AUTHORITY = Units.get("2 1/s");
// Horizontal and vertical max control speed when walking // Horizontal and vertical max control speed when walking
private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND; private static final float WALKING_SPEED = 4.0f * Units.METERS_PER_SECOND;
// (0; 1], 1 is instant change, 0 is no control authority // Horizontal and vertical max control speed when run
private static final float WALKING_CONTROL_AUTHORITY = Units.get("15 1/s"); private static final float RUN_SPEED = 6.0f * Units.METERS_PER_SECOND;
// Vertical velocity instantly add to player when they jump // (0; 1], 1 is instant change, 0 is no control authority
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND; private static final float WALKING_CONTROL_AUTHORITY = Units.get("15 1/s");
private boolean isFlying = true; // Vertical velocity instantly add to player when they jump
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND;
private int movementForward = 0;
private int movementRight = 0; private boolean isFlying = true;
private int movementUp = 0; private boolean isSprint = false;
private double lastSpacePress = Double.NEGATIVE_INFINITY; private int movementForward = 0;
private int movementRight = 0;
private boolean captureMouse = true; private int movementUp = 0;
private boolean useMinecraftGravity = false;
private double lastSpacePress = Double.NEGATIVE_INFINITY;
private int selectedBlock = 0; private double lastSprintPress = Double.NEGATIVE_INFINITY;
private int selectedTile = 0;
private boolean isBlockSelected = true; private boolean captureMouse = true;
private boolean useMinecraftGravity = false;
private Runnable updateCallback = null;
private int selectedBlock = 0;
public void applyPlayerControls() { private int selectedTile = 0;
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { private boolean isBlockSelected = true;
return;
} private Runnable updateCallback = null;
EntityData player = getEntity(); public void applyPlayerControls() {
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
final float speed, authority; return;
}
if (isFlying) {
speed = FLYING_SPEED; EntityData player = getEntity();
authority = FLYING_CONTROL_AUTHORITY;
} else { final float speed, authority;
speed = WALKING_SPEED;
authority = WALKING_CONTROL_AUTHORITY; if (isFlying) {
} speed = FLYING_SPEED;
authority = FLYING_CONTROL_AUTHORITY;
Mat3 angMat = new Mat3().identity().rotateZ(player.getYaw()); } else if (isSprint) {
Vec3 desiredVelocity = new Vec3(movementForward, -movementRight, 0); speed = RUN_SPEED;
authority = WALKING_CONTROL_AUTHORITY;
if (movementForward != 0 && movementRight != 0) desiredVelocity.normalize(); } else {
angMat.mul_(desiredVelocity); // bug in jglm, .mul() and mul_() are swapped speed = WALKING_SPEED;
desiredVelocity.z = movementUp; authority = WALKING_CONTROL_AUTHORITY;
desiredVelocity.mul(speed); }
Vec3 change = new Vec3() Mat3 angMat = new Mat3().identity().rotateZ(player.getYaw());
.set(desiredVelocity) Vec3 desiredVelocity = new Vec3(movementForward, -movementRight, 0);
.sub(player.getVelocity())
.mul((float) Math.exp(-authority * GraphicsInterface.getFrameLength())) if (movementForward != 0 && movementRight != 0) desiredVelocity.normalize();
.negate() angMat.mul_(desiredVelocity); // bug in jglm, .mul() and mul_() are swapped
.add(desiredVelocity); desiredVelocity.z = movementUp;
desiredVelocity.mul(speed);
if (!isFlying) {
change.z = player.getVelocity().z; Vec3 change = new Vec3()
} .set(desiredVelocity)
.sub(player.getVelocity())
player.getVelocity().set(change); .mul((float) Math.exp(-authority * GraphicsInterface.getFrameLength()))
.negate()
// THIS IS TERRIBLE TEST .add(desiredVelocity);
EntityData serverEntity = ServerState.getInstance().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID);
if (serverEntity != null) { if (!isFlying) {
serverEntity.setPosition(player.getPosition()); change.z = player.getVelocity().z;
} }
} player.getVelocity().set(change);
public void handleInput(Input input) { // THIS IS TERRIBLE TEST
InputEvent event = input.getEvent(); EntityData serverEntity = ServerState.getInstance().getWorld().getData().getEntity(TestContent.PLAYER_ENTITY_ID);
if (serverEntity != null) {
if (event instanceof KeyEvent) { serverEntity.setPosition(player.getPosition());
if (onKeyEvent((KeyEvent) event)) { }
input.consume();
} }
} else if (event instanceof CursorMoveEvent) {
onMouseMoved((CursorMoveEvent) event); public void handleInput(Input input) {
input.consume(); InputEvent event = input.getEvent();
} else if (event instanceof WheelScrollEvent) {
onWheelScroll((WheelScrollEvent) event); if (event instanceof KeyEvent) {
input.consume(); if (onKeyEvent((KeyEvent) event)) {
} input.consume();
} }
} else if (event instanceof CursorMoveEvent) {
private boolean onKeyEvent(KeyEvent event) { onMouseMoved((CursorMoveEvent) event);
if (event.isRepeat()) return false; input.consume();
} else if (event instanceof WheelScrollEvent) {
int multiplier = event.isPress() ? 1 : -1; onWheelScroll((WheelScrollEvent) event);
input.consume();
switch (event.getKey()) { }
case GLFW.GLFW_KEY_W: }
movementForward += +1 * multiplier;
break; private boolean onKeyEvent(KeyEvent event) {
case GLFW.GLFW_KEY_S: if (event.isRepeat()) return false;
movementForward += -1 * multiplier; int multiplier = event.isPress() ? 1 : -1;
break; switch (event.getKey()) {
case GLFW.GLFW_KEY_A: case GLFW.GLFW_KEY_W:
movementRight += -1 * multiplier; movementForward += +1 * multiplier;
break; handleSprint(event);
case GLFW.GLFW_KEY_D: break;
movementRight += +1 * multiplier; case GLFW.GLFW_KEY_S:
break; movementForward += -1 * multiplier;
case GLFW.GLFW_KEY_SPACE: break;
handleSpace(multiplier); case GLFW.GLFW_KEY_A:
break; movementRight += -1 * multiplier;
case GLFW.GLFW_KEY_LEFT_SHIFT: break;
handleShift(multiplier); case GLFW.GLFW_KEY_D:
break; movementRight += +1 * multiplier;
break;
case GLFW.GLFW_KEY_ESCAPE: case GLFW.GLFW_KEY_SPACE:
if (!event.isPress()) return false; handleSpace(multiplier);
handleEscape(); break;
break; case GLFW.GLFW_KEY_LEFT_SHIFT:
handleShift(multiplier);
case GLFW.GLFW_KEY_F5: break;
if (!event.isPress()) return false; case GLFW.GLFW_KEY_ESCAPE:
handleCameraMode(); if (!event.isPress()) return false;
break; handleEscape();
break;
case GLFW.GLFW_KEY_G:
if (!event.isPress()) return false; case GLFW.GLFW_KEY_F5:
handleGravitySwitch(); if (!event.isPress()) return false;
break; handleCameraMode();
break;
case GLFW.GLFW_MOUSE_BUTTON_3:
if (!event.isPress()) return false; case GLFW.GLFW_KEY_G:
switchPlacingMode(); if (!event.isPress()) return false;
break; handleGravitySwitch();
break;
default:
return false; case GLFW.GLFW_MOUSE_BUTTON_3:
} if (!event.isPress()) return false;
switchPlacingMode();
return true; break;
}
default:
private void handleSpace(int multiplier) { return false;
boolean isPressed = multiplier > 0; }
double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSpacePress; return true;
}
if (isPressed && timeSinceLastSpacePress < MODE_SWITCH_MAX_DELAY) {
isFlying = !isFlying; private void handleSpace(int multiplier) {
updateGUI(); boolean isPressed = multiplier > 0;
movementUp = +1;
} else { double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSpacePress;
if (isFlying) {
movementUp += +1 * multiplier; if (isPressed && timeSinceLastSpacePress < MODE_SWITCH_MAX_DELAY) {
} else { isSprint = false;
if (isPressed && timeSinceLastSpacePress > MIN_JUMP_DELAY) { isFlying = !isFlying;
jump(); updateGUI();
} movementUp = +1;
} } else {
} if (isFlying) {
movementUp += +1 * multiplier;
lastSpacePress = GraphicsInterface.getTime(); } else {
} if (isPressed && timeSinceLastSpacePress > MIN_JUMP_DELAY) {
jump();
private void jump() { }
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { }
return; }
}
lastSpacePress = GraphicsInterface.getTime();
getEntity().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1)); }
}
private void handleSprint(KeyEvent event) {
private void handleShift(int multiplier) {
if (isFlying) { double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSprintPress;
movementUp += -1 * multiplier;
} if (event.isPress() && timeSinceLastSpacePress < MODE_SPRINT_SWITCH_MAX_DELAY && !isFlying) {
} isSprint = !isSprint;
updateGUI();
private void handleEscape() { }
if (captureMouse) {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); lastSprintPress = GraphicsInterface.getTime();
} else {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); if (isSprint && event.isRelease()) {
} isSprint = false;
updateGUI();
captureMouse = !captureMouse; }
updateGUI(); }
}
private void jump() {
private void handleCameraMode() { if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) { return;
return; }
}
getEntity().getVelocity().add(0, 0, JUMP_VELOCITY * (useMinecraftGravity ? 2 : 1));
if (ClientState.getInstance().getCamera().hasAnchor()) { }
ClientState.getInstance().getCamera().selectNextMode();
updateGUI(); private void handleShift(int multiplier) {
} if (isFlying) {
} movementUp += -1 * multiplier;
}
private void handleGravitySwitch() { }
useMinecraftGravity = !useMinecraftGravity;
updateGUI(); private void handleEscape() {
} if (captureMouse) {
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
private void onMouseMoved(CursorMoveEvent event) { } else {
if (!captureMouse) return; GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
}
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
return; captureMouse = !captureMouse;
} updateGUI();
}
final float yawScale = -0.002f;
final float pitchScale = yawScale; private void handleCameraMode() {
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
EntityData player = getEntity(); return;
}
normalizeAngles(player.getDirection().add(
(float) (event.getChangeX() * yawScale), if (ClientState.getInstance().getCamera().hasAnchor()) {
(float) (event.getChangeY() * pitchScale) ClientState.getInstance().getCamera().selectNextMode();
)); updateGUI();
} }
}
private void normalizeAngles(Vec2 dir) {
// Normalize yaw private void handleGravitySwitch() {
dir.x = FloatMathUtils.normalizeAngle(dir.x); useMinecraftGravity = !useMinecraftGravity;
updateGUI();
// Clamp pitch }
dir.y = Glm.clamp(
dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2 private void onMouseMoved(CursorMoveEvent event) {
); if (!captureMouse) return;
}
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
private void onWheelScroll(WheelScrollEvent event) { return;
if (event.hasHorizontalMovement()) { }
switchPlacingMode();
} final float yawScale = -0.002f;
if (InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)) { final float pitchScale = yawScale;
switchPlacingMode();
return; EntityData player = getEntity();
}
normalizeAngles(player.getDirection().add(
if (isBlockSelected) { (float) (event.getChangeX() * yawScale),
selectedBlock += event.isUp() ? +1 : -1; (float) (event.getChangeY() * pitchScale)
));
int size = TestContent.PLACEABLE_BLOCKS.size(); }
if (selectedBlock < 0) { private void normalizeAngles(Vec2 dir) {
selectedBlock = size - 1; // Normalize yaw
} else if (selectedBlock >= size) { dir.x = FloatMathUtils.normalizeAngle(dir.x);
selectedBlock = 0;
} // Clamp pitch
} else { dir.y = Glm.clamp(
selectedTile += event.isUp() ? +1 : -1; dir.y, -FloatMathUtils.PI_F / 2, +FloatMathUtils.PI_F / 2
);
int size = TestContent.PLACEABLE_TILES.size(); }
if (selectedTile < 0) { private void onWheelScroll(WheelScrollEvent event) {
selectedTile = size - 1; if (event.hasHorizontalMovement()) {
} else if (selectedTile >= size) { switchPlacingMode();
selectedTile = 0; }
} if (InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)) {
} switchPlacingMode();
return;
updateGUI(); }
}
if (isBlockSelected) {
private void switchPlacingMode() { selectedBlock += event.isUp() ? +1 : -1;
isBlockSelected = !isBlockSelected;
updateGUI(); int size = TestContent.PLACEABLE_BLOCKS.size();
}
if (selectedBlock < 0) {
public EntityData getEntity() { selectedBlock = size - 1;
return getPlayer().getEntity(); } else if (selectedBlock >= size) {
} selectedBlock = 0;
}
public LocalPlayer getPlayer() { } else {
return ClientState.getInstance().getLocalPlayer(); selectedTile += event.isUp() ? +1 : -1;
}
int size = TestContent.PLACEABLE_TILES.size();
public void setUpdateCallback(Runnable updateCallback) {
this.updateCallback = updateCallback; if (selectedTile < 0) {
} selectedTile = size - 1;
} else if (selectedTile >= size) {
private void updateGUI() { selectedTile = 0;
if (this.updateCallback != null) { }
this.updateCallback.run(); }
}
} updateGUI();
}
public boolean isFlying() {
return isFlying; private void switchPlacingMode() {
} isBlockSelected = !isBlockSelected;
updateGUI();
public boolean isMouseCaptured() { }
return captureMouse;
} public EntityData getEntity() {
return getPlayer().getEntity();
public boolean useMinecraftGravity() { }
return useMinecraftGravity;
} public LocalPlayer getPlayer() {
return ClientState.getInstance().getLocalPlayer();
public BlockData getSelectedBlock() { }
return TestContent.PLACEABLE_BLOCKS.get(selectedBlock);
} public void setUpdateCallback(Runnable updateCallback) {
this.updateCallback = updateCallback;
public TileData getSelectedTile() { }
return TestContent.PLACEABLE_TILES.get(selectedTile);
} private void updateGUI() {
if (this.updateCallback != null) {
public boolean isBlockSelected() { this.updateCallback.run();
return isBlockSelected; }
} }
public boolean isFlying() {
return isFlying;
}
public boolean isSprint() {
return isSprint;
}
public boolean isMouseCaptured() {
return captureMouse;
}
public boolean useMinecraftGravity() {
return useMinecraftGravity;
}
public BlockData getSelectedBlock() {
return TestContent.PLACEABLE_BLOCKS.get(selectedBlock);
}
public TileData getSelectedTile() {
return TestContent.PLACEABLE_TILES.get(selectedTile);
}
public boolean isBlockSelected() {
return isBlockSelected;
}
} }