Refactored movement controls
- Movement control code is now in its own class - Movement direction controls are now more bug-resistant - No longer event-driven; uses InputTracker instead - Temporarily? function with menus open - Other movement controls are now registered local Controls - Removed flying and sprinting indicators from F3 layer - TestPlayerControls is now a singleton - resetInstance() now resets the instance's fields
This commit is contained in:
parent
b4ff5114bd
commit
c93f0df30d
@ -143,20 +143,6 @@ public class ControlTriggers {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
//
|
|
||||||
///
|
|
||||||
///
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
public static ControlTriggerInputBased localOf(
|
public static ControlTriggerInputBased localOf(
|
||||||
String id,
|
String id,
|
||||||
Consumer<InputEvent> action,
|
Consumer<InputEvent> action,
|
||||||
|
@ -58,22 +58,6 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
|
|
||||||
TestPlayerControls tpc = TestPlayerControls.getInstance();
|
TestPlayerControls tpc = TestPlayerControls.getInstance();
|
||||||
|
|
||||||
group.addChild(
|
|
||||||
new Label(
|
|
||||||
"IsFlyingDisplay",
|
|
||||||
font,
|
|
||||||
tmp_dynFormat("LayerTestGUI.IsFlyingDisplay", tpc::isFlying)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
group.addChild(
|
|
||||||
new Label(
|
|
||||||
"IsSprintingDisplay",
|
|
||||||
font,
|
|
||||||
tmp_dynFormat("LayerTestGUI.IsSprintingDisplay", tpc::isSprinting)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
group.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"CameraModeDisplay",
|
"CameraModeDisplay",
|
||||||
|
@ -29,12 +29,16 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.ClientState;
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.audio.Sound;
|
import ru.windcorp.progressia.client.audio.Sound;
|
||||||
import ru.windcorp.progressia.client.comms.controls.*;
|
import ru.windcorp.progressia.client.comms.controls.*;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.world.Selection;
|
import ru.windcorp.progressia.client.graphics.world.Selection;
|
||||||
import ru.windcorp.progressia.client.world.block.*;
|
import ru.windcorp.progressia.client.world.block.*;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
||||||
@ -287,6 +291,8 @@ public class TestContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void regsiterControls() {
|
private static void regsiterControls() {
|
||||||
|
TestPlayerControls.getInstance().registerControls();
|
||||||
|
|
||||||
ControlDataRegistry data = ControlDataRegistry.getInstance();
|
ControlDataRegistry data = ControlDataRegistry.getInstance();
|
||||||
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();
|
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();
|
||||||
ControlLogicRegistry logic = ControlLogicRegistry.getInstance();
|
ControlLogicRegistry logic = ControlLogicRegistry.getInstance();
|
||||||
@ -328,6 +334,33 @@ public class TestContent {
|
|||||||
);
|
);
|
||||||
logic.register(ControlLogic.of("Test:PlaceTile", TestContent::onTilePlaceReceived));
|
logic.register(ControlLogic.of("Test:PlaceTile", TestContent::onTilePlaceReceived));
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:SwitchPlacingModeMMB",
|
||||||
|
KeyEvent.class,
|
||||||
|
() -> TestPlayerControls.getInstance().switchPlacingMode(),
|
||||||
|
KeyMatcher.MMB::matches
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:SwitchPlacingModeWheel",
|
||||||
|
WheelScrollEvent.class,
|
||||||
|
() -> TestPlayerControls.getInstance().switchPlacingMode(),
|
||||||
|
e -> e.hasHorizontalMovement() || InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:SelectNextBlockOrTile",
|
||||||
|
WheelScrollEvent.class,
|
||||||
|
e -> TestPlayerControls.getInstance().selectNextBlockOrTile(e),
|
||||||
|
e -> !e.hasHorizontalMovement() && !InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
triggers.register(
|
triggers.register(
|
||||||
ControlTriggers.localOf(
|
ControlTriggers.localOf(
|
||||||
"Test:StartNextMusic",
|
"Test:StartNextMusic",
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
package ru.windcorp.progressia.test;
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
import glm.Glm;
|
import glm.Glm;
|
||||||
import glm.mat._3.Mat3;
|
|
||||||
import glm.mat._4.Mat4;
|
import glm.mat._4.Mat4;
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
@ -27,61 +26,29 @@ import ru.windcorp.progressia.client.ClientState;
|
|||||||
import ru.windcorp.progressia.client.graphics.GUI;
|
import ru.windcorp.progressia.client.graphics.GUI;
|
||||||
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;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
|
||||||
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
|
import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
|
import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
|
||||||
import ru.windcorp.progressia.client.localization.Localizer;
|
import ru.windcorp.progressia.client.localization.Localizer;
|
||||||
import ru.windcorp.progressia.common.Units;
|
|
||||||
import ru.windcorp.progressia.common.util.Matrices;
|
import ru.windcorp.progressia.common.util.Matrices;
|
||||||
import ru.windcorp.progressia.common.util.VectorUtil;
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.test.controls.MovementControls;
|
||||||
|
|
||||||
public class TestPlayerControls {
|
public class TestPlayerControls {
|
||||||
|
|
||||||
private static TestPlayerControls instance = new TestPlayerControls();
|
private static final TestPlayerControls INSTANCE = new TestPlayerControls();
|
||||||
|
|
||||||
public static TestPlayerControls getInstance() {
|
public static TestPlayerControls getInstance() {
|
||||||
return instance;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final double MODE_SWITCH_MAX_DELAY = 300 * Units.MILLISECONDS;
|
private final MovementControls movementControls = new MovementControls();
|
||||||
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
|
|
||||||
private static final float FLYING_SPEED = Units.get("6 m/s");
|
|
||||||
|
|
||||||
// (0; 1], 1 is instant change, 0 is no control authority
|
|
||||||
private static final float FLYING_CONTROL_AUTHORITY = Units.get("2 1/s");
|
|
||||||
|
|
||||||
// Horizontal max control speed when walking
|
|
||||||
private static final float WALKING_SPEED = Units.get("4 m/s");
|
|
||||||
|
|
||||||
// Horizontal max control speed when sprinting
|
|
||||||
private static final float SPRINTING_SPEED = Units.get("6 m/s");
|
|
||||||
|
|
||||||
// (0; 1], 1 is instant change, 0 is no control authority
|
|
||||||
private static final float WALKING_CONTROL_AUTHORITY = Units.get("15 1/s");
|
|
||||||
|
|
||||||
// Vertical velocity instantly added to player when they jump
|
|
||||||
private static final float JUMP_VELOCITY = 5f * Units.METERS_PER_SECOND;
|
|
||||||
|
|
||||||
private boolean isFlying = true;
|
|
||||||
private boolean isSprinting = false;
|
|
||||||
|
|
||||||
private int movementForward = 0;
|
|
||||||
private int movementRight = 0;
|
|
||||||
private int movementUp = 0;
|
|
||||||
|
|
||||||
private double lastSpacePress = Double.NEGATIVE_INFINITY;
|
|
||||||
private double lastSprintPress = Double.NEGATIVE_INFINITY;
|
|
||||||
|
|
||||||
private int selectedBlock = 0;
|
private int selectedBlock = 0;
|
||||||
private int selectedTile = 0;
|
private int selectedTile = 0;
|
||||||
@ -90,75 +57,30 @@ public class TestPlayerControls {
|
|||||||
private LayerTestGUI debugLayer = null;
|
private LayerTestGUI debugLayer = null;
|
||||||
private Runnable updateCallback = null;
|
private Runnable updateCallback = null;
|
||||||
|
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
public static void resetInstance() {
|
public static void resetInstance() {
|
||||||
instance = new TestPlayerControls();
|
INSTANCE.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
movementControls.reset();
|
||||||
|
|
||||||
|
debugLayer = null;
|
||||||
|
updateCallback = null;
|
||||||
|
selectedBlock = 0;
|
||||||
|
selectedTile = 0;
|
||||||
|
isBlockSelected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyPlayerControls() {
|
public void applyPlayerControls() {
|
||||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
movementControls.applyPlayerControls();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityData player = getEntity();
|
|
||||||
|
|
||||||
final float speed, authority;
|
|
||||||
|
|
||||||
if (isFlying) {
|
|
||||||
speed = FLYING_SPEED;
|
|
||||||
authority = FLYING_CONTROL_AUTHORITY;
|
|
||||||
} else {
|
|
||||||
speed = isSprinting ? SPRINTING_SPEED : WALKING_SPEED;
|
|
||||||
authority = WALKING_CONTROL_AUTHORITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mat3 movementTransform = getMovementTransform(player, null);
|
|
||||||
Vec3 desiredVelocity = new Vec3(movementForward, movementRight, 0);
|
|
||||||
if (movementForward != 0 && movementRight != 0) {
|
|
||||||
desiredVelocity.normalize();
|
|
||||||
}
|
|
||||||
desiredVelocity.z = movementUp;
|
|
||||||
movementTransform.mul_(desiredVelocity); // bug in jglm, .mul() and .mul_() are
|
|
||||||
// swapped
|
|
||||||
desiredVelocity.mul(speed);
|
|
||||||
|
|
||||||
Vec3 newVelocity = new Vec3()
|
|
||||||
.set(desiredVelocity)
|
|
||||||
.sub(player.getVelocity())
|
|
||||||
.mul((float) Math.exp(-authority * GraphicsInterface.getFrameLength()))
|
|
||||||
.negate()
|
|
||||||
.add(desiredVelocity);
|
|
||||||
|
|
||||||
if (!isFlying) {
|
|
||||||
Vec3 up = player.getUpVector();
|
|
||||||
Vec3 wantedVertical = VectorUtil.projectOnVector(player.getVelocity(), up, null);
|
|
||||||
VectorUtil.projectOnSurface(newVelocity, up).add(wantedVertical);
|
|
||||||
}
|
|
||||||
|
|
||||||
player.getVelocity().set(newVelocity);
|
|
||||||
|
|
||||||
// THIS IS TERRIBLE TEST
|
|
||||||
EntityData serverEntity = ServerState.getInstance().getWorld().getData()
|
|
||||||
.getEntity(TestContent.PLAYER_ENTITY_ID);
|
|
||||||
if (serverEntity != null) {
|
|
||||||
serverEntity.setPosition(player.getPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mat3 getMovementTransform(EntityData player, Mat3 mat) {
|
public void registerControls() {
|
||||||
if (mat == null) {
|
movementControls.registerControls();
|
||||||
mat = new Mat3();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 f = player.getForwardVector(null);
|
|
||||||
Vec3 u = player.getUpVector();
|
|
||||||
Vec3 s = u.cross_(f);
|
|
||||||
|
|
||||||
return mat.set(
|
|
||||||
+f.x, +f.y, +f.z,
|
|
||||||
-s.x, -s.y, -s.z,
|
|
||||||
+u.x, +u.y, +u.z
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleInput(InputEvent event) {
|
public void handleInput(InputEvent event) {
|
||||||
@ -169,81 +91,39 @@ public class TestPlayerControls {
|
|||||||
} else if (event instanceof CursorMoveEvent) {
|
} else if (event instanceof CursorMoveEvent) {
|
||||||
onMouseMoved((CursorMoveEvent) event);
|
onMouseMoved((CursorMoveEvent) event);
|
||||||
event.consume();
|
event.consume();
|
||||||
} else if (event instanceof WheelScrollEvent) {
|
|
||||||
onWheelScroll((WheelScrollEvent) event);
|
|
||||||
event.consume();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onKeyEvent(KeyEvent event) {
|
private boolean onKeyEvent(KeyEvent event) {
|
||||||
if (event.isRepeat())
|
if (!event.isPress())
|
||||||
return false;
|
return false;
|
||||||
int multiplier = event.isPress() ? 1 : -1;
|
|
||||||
switch (event.getKey()) {
|
switch (event.getKey()) {
|
||||||
case GLFW.GLFW_KEY_W:
|
|
||||||
movementForward += +1 * multiplier;
|
|
||||||
handleSprint(event);
|
|
||||||
break;
|
|
||||||
case GLFW.GLFW_KEY_S:
|
|
||||||
movementForward += -1 * multiplier;
|
|
||||||
break;
|
|
||||||
case GLFW.GLFW_KEY_A:
|
|
||||||
movementRight += -1 * multiplier;
|
|
||||||
break;
|
|
||||||
case GLFW.GLFW_KEY_D:
|
|
||||||
movementRight += +1 * multiplier;
|
|
||||||
break;
|
|
||||||
case GLFW.GLFW_KEY_SPACE:
|
|
||||||
handleSpace(multiplier);
|
|
||||||
break;
|
|
||||||
case GLFW.GLFW_KEY_LEFT_SHIFT:
|
|
||||||
handleShift(multiplier);
|
|
||||||
break;
|
|
||||||
case GLFW.GLFW_KEY_ESCAPE:
|
case GLFW.GLFW_KEY_ESCAPE:
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
handleEscape();
|
handleEscape();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_F11:
|
case GLFW.GLFW_KEY_F11:
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
GraphicsInterface.makeFullscreen(!GraphicsBackend.isFullscreen());
|
GraphicsInterface.makeFullscreen(!GraphicsBackend.isFullscreen());
|
||||||
updateGUI();
|
updateGUI();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_F12:
|
case GLFW.GLFW_KEY_F12:
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
GraphicsBackend.setVSyncEnabled(!GraphicsBackend.isVSyncEnabled());
|
GraphicsBackend.setVSyncEnabled(!GraphicsBackend.isVSyncEnabled());
|
||||||
updateGUI();
|
updateGUI();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_F3:
|
case GLFW.GLFW_KEY_F3:
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
handleDebugLayerSwitch();
|
handleDebugLayerSwitch();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_F5:
|
case GLFW.GLFW_KEY_F5:
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
handleCameraMode();
|
handleCameraMode();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_L:
|
case GLFW.GLFW_KEY_L:
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
handleLanguageSwitch();
|
handleLanguageSwitch();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_MOUSE_BUTTON_3:
|
|
||||||
if (!event.isPress())
|
|
||||||
return false;
|
|
||||||
switchPlacingMode();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -251,70 +131,7 @@ public class TestPlayerControls {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSpace(int multiplier) {
|
|
||||||
boolean isPressed = multiplier > 0;
|
|
||||||
|
|
||||||
double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSpacePress;
|
|
||||||
|
|
||||||
if (isPressed && timeSinceLastSpacePress < MODE_SWITCH_MAX_DELAY) {
|
|
||||||
isSprinting = false;
|
|
||||||
isFlying = !isFlying;
|
|
||||||
updateGUI();
|
|
||||||
movementUp = +1;
|
|
||||||
} else {
|
|
||||||
if (isFlying) {
|
|
||||||
movementUp += +1 * multiplier;
|
|
||||||
} else {
|
|
||||||
if (isPressed && timeSinceLastSpacePress > MIN_JUMP_DELAY) {
|
|
||||||
jump();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSpacePress = GraphicsInterface.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSprint(KeyEvent event) {
|
|
||||||
|
|
||||||
double timeSinceLastSpacePress = GraphicsInterface.getTime() - lastSprintPress;
|
|
||||||
|
|
||||||
if (event.isPress() && timeSinceLastSpacePress < MODE_SPRINT_SWITCH_MAX_DELAY && !isFlying) {
|
|
||||||
isSprinting = !isSprinting;
|
|
||||||
updateGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSprintPress = GraphicsInterface.getTime();
|
|
||||||
|
|
||||||
if (isSprinting && event.isRelease()) {
|
|
||||||
isSprinting = false;
|
|
||||||
updateGUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void jump() {
|
|
||||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec3 up = getEntity().getUpVector();
|
|
||||||
|
|
||||||
getEntity().getVelocity().add(
|
|
||||||
up.x * JUMP_VELOCITY,
|
|
||||||
up.y * JUMP_VELOCITY,
|
|
||||||
up.z * JUMP_VELOCITY
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleShift(int multiplier) {
|
|
||||||
if (isFlying) {
|
|
||||||
movementUp += -1 * multiplier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleEscape() {
|
private void handleEscape() {
|
||||||
movementForward = 0;
|
|
||||||
movementRight = 0;
|
|
||||||
movementUp = 0;
|
|
||||||
GUI.addTopLayer(new LayerButtonTest());
|
GUI.addTopLayer(new LayerButtonTest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,15 +207,12 @@ public class TestPlayerControls {
|
|||||||
Matrices.release(mat);
|
Matrices.release(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onWheelScroll(WheelScrollEvent event) {
|
public void switchPlacingMode() {
|
||||||
if (event.hasHorizontalMovement()) {
|
isBlockSelected = !isBlockSelected;
|
||||||
switchPlacingMode();
|
updateGUI();
|
||||||
}
|
}
|
||||||
if (InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)) {
|
|
||||||
switchPlacingMode();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public void selectNextBlockOrTile(WheelScrollEvent event) {
|
||||||
if (isBlockSelected) {
|
if (isBlockSelected) {
|
||||||
selectedBlock += event.isUp() ? +1 : -1;
|
selectedBlock += event.isUp() ? +1 : -1;
|
||||||
|
|
||||||
@ -424,11 +238,6 @@ public class TestPlayerControls {
|
|||||||
updateGUI();
|
updateGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchPlacingMode() {
|
|
||||||
isBlockSelected = !isBlockSelected;
|
|
||||||
updateGUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityData getEntity() {
|
public EntityData getEntity() {
|
||||||
return getPlayer().getEntity();
|
return getPlayer().getEntity();
|
||||||
}
|
}
|
||||||
@ -443,12 +252,8 @@ public class TestPlayerControls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFlying() {
|
public MovementControls getMovementControls() {
|
||||||
return isFlying;
|
return movementControls;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSprinting() {
|
|
||||||
return isSprinting;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockData getSelectedBlock() {
|
public BlockData getSelectedBlock() {
|
||||||
@ -463,4 +268,12 @@ public class TestPlayerControls {
|
|||||||
return isBlockSelected;
|
return isBlockSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFlying() {
|
||||||
|
return movementControls.isFlying();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSprinting() {
|
||||||
|
return movementControls.isSprinting();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.test.controls;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import glm.mat._3.Mat3;
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
|
import ru.windcorp.progressia.client.comms.controls.ControlTriggerRegistry;
|
||||||
|
import ru.windcorp.progressia.client.comms.controls.ControlTriggers;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
|
import ru.windcorp.progressia.common.Units;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
|
||||||
|
public class MovementControls {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max delay between space presses that can toggle flying
|
||||||
|
*/
|
||||||
|
private static final double FLYING_SWITCH_MAX_DELAY = Units.get("300 ms");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max delay between W presses that can toggle sprinting
|
||||||
|
*/
|
||||||
|
private static final double SPRINTING_SWITCH_MAX_DELAY = Units.get("300 ms");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Min delay between jumps
|
||||||
|
*/
|
||||||
|
private static final double JUMP_MIN_DELAY = Units.get("300 ms");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal and vertical max control speed when flying
|
||||||
|
*/
|
||||||
|
private static final float FLYING_SPEED = Units.get("6 m/s");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (0; 1], 1 is instant change, 0 is no control authority
|
||||||
|
*/
|
||||||
|
private static final float FLYING_CONTROL_AUTHORITY = Units.get("2 1/s");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal max control speed when walking
|
||||||
|
*/
|
||||||
|
private static final float WALKING_SPEED = Units.get("4 m/s");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal max control speed when sprinting
|
||||||
|
*/
|
||||||
|
private static final float SPRINTING_SPEED = Units.get("6 m/s");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (0; 1], 1 is instant change, 0 is no control authority
|
||||||
|
*/
|
||||||
|
private static final float WALKING_CONTROL_AUTHORITY = Units.get("15 1/s");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical velocity instantly added to player when they jump
|
||||||
|
*/
|
||||||
|
private static final float JUMP_VELOCITY = Units.get("5 m/s");
|
||||||
|
|
||||||
|
private boolean isFlying;
|
||||||
|
private boolean isSprinting;
|
||||||
|
|
||||||
|
private double lastSpacePress;
|
||||||
|
private double lastSprintPress;
|
||||||
|
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyPlayerControls() {
|
||||||
|
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityData player = ClientState.getInstance().getLocalPlayer().getEntity();
|
||||||
|
assert player != null;
|
||||||
|
|
||||||
|
final float speed, authority;
|
||||||
|
|
||||||
|
if (isFlying) {
|
||||||
|
speed = FLYING_SPEED;
|
||||||
|
authority = FLYING_CONTROL_AUTHORITY;
|
||||||
|
} else {
|
||||||
|
speed = isSprinting ? SPRINTING_SPEED : WALKING_SPEED;
|
||||||
|
authority = WALKING_CONTROL_AUTHORITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat3 movementTransform = getMovementTransform(player, null);
|
||||||
|
Vec3 desiredVelocity = getDesiredVelocity(movementTransform, speed);
|
||||||
|
Vec3 newVelocity = getNewVelocity(desiredVelocity, player.getVelocity(), authority, player.getUpVector());
|
||||||
|
player.getVelocity().set(newVelocity);
|
||||||
|
|
||||||
|
tmp_syncServerEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tmp_syncServerEntity() {
|
||||||
|
// THIS IS TERRIBLE TEST
|
||||||
|
EntityData serverEntity = ServerState.getInstance().getWorld().getData()
|
||||||
|
.getEntity(TestContent.PLAYER_ENTITY_ID);
|
||||||
|
if (serverEntity != null) {
|
||||||
|
ClientState.getInstance().getLocalPlayer().getEntity().copy(serverEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mat3 getMovementTransform(EntityData player, Mat3 mat) {
|
||||||
|
if (mat == null) {
|
||||||
|
mat = new Mat3();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 f = player.getForwardVector(null);
|
||||||
|
Vec3 u = player.getUpVector();
|
||||||
|
Vec3 s = f.cross_(u);
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
return mat.set(
|
||||||
|
f.x, f.y, f.z,
|
||||||
|
s.x, s.y, s.z,
|
||||||
|
u.x, u.y, u.z
|
||||||
|
);
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vec3 getDesiredVelocity(Mat3 movementTransform, float speed) {
|
||||||
|
int forward = 0;
|
||||||
|
int right = 0;
|
||||||
|
int up = 0;
|
||||||
|
|
||||||
|
forward += InputTracker.isKeyPressed(GLFW.GLFW_KEY_W) ? 1 : 0;
|
||||||
|
forward -= InputTracker.isKeyPressed(GLFW.GLFW_KEY_S) ? 1 : 0;
|
||||||
|
|
||||||
|
right += InputTracker.isKeyPressed(GLFW.GLFW_KEY_D) ? 1 : 0;
|
||||||
|
right -= InputTracker.isKeyPressed(GLFW.GLFW_KEY_A) ? 1 : 0;
|
||||||
|
|
||||||
|
if (isFlying) {
|
||||||
|
up += InputTracker.isKeyPressed(GLFW.GLFW_KEY_SPACE) ? 1 : 0;
|
||||||
|
up -= InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_SHIFT) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 desiredVelocity = new Vec3(forward, right, 0);
|
||||||
|
if (forward != 0 && right != 0) {
|
||||||
|
desiredVelocity.normalize();
|
||||||
|
}
|
||||||
|
desiredVelocity.z = up;
|
||||||
|
|
||||||
|
// bug in jglm, .mul() and .mul_() are swapped
|
||||||
|
movementTransform.mul_(desiredVelocity);
|
||||||
|
desiredVelocity.mul(speed);
|
||||||
|
|
||||||
|
return desiredVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vec3 getNewVelocity(Vec3 desiredVelocity, Vec3 oldVelocity, float authority, Vec3 up) {
|
||||||
|
|
||||||
|
// newVelocity = oldVelocity + small change toward desiredVelocity
|
||||||
|
Vec3 newVelocity = new Vec3()
|
||||||
|
.set(desiredVelocity)
|
||||||
|
.sub(oldVelocity)
|
||||||
|
.mul((float) Math.exp(-authority * GraphicsInterface.getFrameLength()))
|
||||||
|
.negate()
|
||||||
|
.add(desiredVelocity);
|
||||||
|
|
||||||
|
// If we aren't flying, don't change vertical component
|
||||||
|
if (!isFlying) {
|
||||||
|
Vec3 wantedVertical = VectorUtil.projectOnVector(oldVelocity, up, null);
|
||||||
|
VectorUtil.projectOnSurface(newVelocity, up);
|
||||||
|
newVelocity.add(wantedVertical);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
isFlying = true;
|
||||||
|
isSprinting = false;
|
||||||
|
lastSpacePress = Double.NEGATIVE_INFINITY;
|
||||||
|
lastSprintPress = Double.NEGATIVE_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerControls() {
|
||||||
|
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:JumpOrToggleFlight",
|
||||||
|
KeyEvent.class,
|
||||||
|
this::handleSpacePress,
|
||||||
|
new KeyMatcher("Space")::matches
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:ToggleSprint",
|
||||||
|
KeyEvent.class,
|
||||||
|
this::toggleSprint,
|
||||||
|
|
||||||
|
new KeyMatcher("W")::matches,
|
||||||
|
e -> !isFlying
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:DisableSprint",
|
||||||
|
KeyEvent.class,
|
||||||
|
this::disableSprint,
|
||||||
|
|
||||||
|
new KeyMatcher("W")::matchesIgnoringAction,
|
||||||
|
KeyEvent::isRelease
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSpacePress(KeyEvent e) {
|
||||||
|
double timeSinceLastSpacePress = e.getTime() - lastSpacePress;
|
||||||
|
|
||||||
|
if (timeSinceLastSpacePress < FLYING_SWITCH_MAX_DELAY) {
|
||||||
|
isSprinting = false;
|
||||||
|
isFlying = !isFlying;
|
||||||
|
} else if (!isFlying && timeSinceLastSpacePress >= JUMP_MIN_DELAY) {
|
||||||
|
jump();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSpacePress = e.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void jump() {
|
||||||
|
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityData player = ClientState.getInstance().getLocalPlayer().getEntity();
|
||||||
|
assert player != null;
|
||||||
|
|
||||||
|
Vec3 up = player.getUpVector();
|
||||||
|
|
||||||
|
player.getVelocity().add(
|
||||||
|
up.x * JUMP_VELOCITY,
|
||||||
|
up.y * JUMP_VELOCITY,
|
||||||
|
up.z * JUMP_VELOCITY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleSprint(KeyEvent e) {
|
||||||
|
if (e.getTime() - lastSprintPress < SPRINTING_SWITCH_MAX_DELAY) {
|
||||||
|
isSprinting = !isSprinting;
|
||||||
|
}
|
||||||
|
lastSprintPress = e.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableSprint(KeyEvent e) {
|
||||||
|
isSprinting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFlying() {
|
||||||
|
return isFlying;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSprinting() {
|
||||||
|
return isSprinting;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,8 +4,6 @@ LayerAbout.Title = Progressia
|
|||||||
LayerAbout.Version = Version: %s
|
LayerAbout.Version = Version: %s
|
||||||
LayerAbout.DebugHint = Debug GUI: F3
|
LayerAbout.DebugHint = Debug GUI: F3
|
||||||
|
|
||||||
LayerTestGUI.IsFlyingDisplay = Flying: %5s (Space bar x2)
|
|
||||||
LayerTestGUI.IsSprintingDisplay = Sprinting: %5s (W x2)
|
|
||||||
LayerTestGUI.CameraModeDisplay = Camera mode: %5d (F5)
|
LayerTestGUI.CameraModeDisplay = Camera mode: %5d (F5)
|
||||||
LayerTestGUI.LanguageDisplay = Language: %5s (L)
|
LayerTestGUI.LanguageDisplay = Language: %5s (L)
|
||||||
LayerTestGUI.FPSDisplay = FPS:
|
LayerTestGUI.FPSDisplay = FPS:
|
||||||
|
@ -4,8 +4,6 @@ LayerAbout.Title = Прогрессия
|
|||||||
LayerAbout.Version = Версия: %s
|
LayerAbout.Version = Версия: %s
|
||||||
LayerAbout.DebugHint = Отладочный GUI: F3
|
LayerAbout.DebugHint = Отладочный GUI: F3
|
||||||
|
|
||||||
LayerTestGUI.IsFlyingDisplay = Полёт: %5s (Пробел x2)
|
|
||||||
LayerTestGUI.IsSprintingDisplay = Бег: %5s (W x2)
|
|
||||||
LayerTestGUI.CameraModeDisplay = Камера: %5d (F5)
|
LayerTestGUI.CameraModeDisplay = Камера: %5d (F5)
|
||||||
LayerTestGUI.LanguageDisplay = Язык: %5s (L)
|
LayerTestGUI.LanguageDisplay = Язык: %5s (L)
|
||||||
LayerTestGUI.FPSDisplay = FPS:
|
LayerTestGUI.FPSDisplay = FPS:
|
||||||
|
Reference in New Issue
Block a user