Skip to content

Commit

Permalink
Joystick sensitivity for player movement (#11262)
Browse files Browse the repository at this point in the history
This commit deprecates the forward, backward, left, and right binary
inputs currently used for player movement in the PlayerControl struct.
In their place, it adds the movement_speed and movement_direction
values, which represents the player movement is a polar coordinate
system.

movement_speed is a scalar from 0.0 to 1.0. movement_direction is
an angle from 0 to +-Pi:

	       FWD
	        0
	        _
	 LFT   / \   RGT
	-Pi/2 |   | +Pi/2
	       \_/
	       +-Pi
	       BCK

Boolean movement bits will still be set for server telegrams and
Lua script invocations to provide full backward compatibility.
When generating these values from an analog input, a direction is
considered active when it is 22.5 degrees away from either
orthogonal axis.

Co-authored-by: Markus Koch <markus@notsyncing.net>
Co-authored-by: sfan5 <sfan5@live.de>
  • Loading branch information
3 people committed Aug 27, 2021
1 parent 149d8fc commit 1d69a23
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 99 deletions.
7 changes: 3 additions & 4 deletions src/client/content_cao.cpp
Expand Up @@ -998,9 +998,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
const PlayerControl &controls = player->getPlayerControl();

bool walking = false;
if (controls.up || controls.down || controls.left || controls.right ||
controls.forw_move_joystick_axis != 0.f ||
controls.sidew_move_joystick_axis != 0.f)
if (controls.movement_speed > 0.001f)
walking = true;

f32 new_speed = player->local_animation_speed;
Expand All @@ -1015,9 +1013,10 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
g_settings->getBool("free_move") &&
m_client->checkLocalPrivilege("fly"))))
new_speed *= 1.5;
// slowdown speed if sneeking
// slowdown speed if sneaking
if (controls.sneak && walking)
new_speed /= 2;
new_speed *= controls.movement_speed;

if (walking && (controls.dig || controls.place)) {
new_anim = player->local_animations[3];
Expand Down
77 changes: 40 additions & 37 deletions src/client/game.cpp
Expand Up @@ -2460,7 +2460,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)

if (m_cache_enable_joysticks) {
f32 sens_scale = getSensitivityScaleFactor();
f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime * sens_scale;
f32 c = m_cache_joystick_frustum_sensitivity * dtime * sens_scale;
cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c;
cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c;
}
Expand All @@ -2471,41 +2471,29 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)

void Game::updatePlayerControl(const CameraOrientation &cam)
{
//TimeTaker tt("update player control", NULL, PRECISION_NANO);
LocalPlayer *player = client->getEnv().getLocalPlayer();

// DO NOT use the isKeyDown method for the forward, backward, left, right
// buttons, as the code that uses the controls needs to be able to
// distinguish between the two in order to know when to use joysticks.
//TimeTaker tt("update player control", NULL, PRECISION_NANO);

PlayerControl control(
input->isKeyDown(KeyType::FORWARD),
input->isKeyDown(KeyType::BACKWARD),
input->isKeyDown(KeyType::LEFT),
input->isKeyDown(KeyType::RIGHT),
isKeyDown(KeyType::JUMP),
isKeyDown(KeyType::JUMP) || player->getAutojump(),
isKeyDown(KeyType::AUX1),
isKeyDown(KeyType::SNEAK),
isKeyDown(KeyType::ZOOM),
isKeyDown(KeyType::DIG),
isKeyDown(KeyType::PLACE),
cam.camera_pitch,
cam.camera_yaw,
input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE),
input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE)
input->getMovementSpeed(),
input->getMovementDirection()
);

u32 keypress_bits = (
( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) |
( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) |
( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) |
( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) |
( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) |
( (u32)(isKeyDown(KeyType::AUX1) & 0x1) << 5) |
( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) |
( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) |
( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) |
( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9)
);
// autoforward if set: move towards pointed position at maximum speed
if (player->getPlayerSettings().continuous_forward &&
client->activeObjectsReceived() && !player->isDead()) {
control.movement_speed = 1.0f;
control.movement_direction = 0.0f;
}

#ifdef ANDROID
/* For Android, simulate holding down AUX1 (fast move) if the user has
Expand All @@ -2515,23 +2503,38 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
*/
if (m_cache_hold_aux1) {
control.aux1 = control.aux1 ^ true;
keypress_bits ^= ((u32)(1U << 5));
}
#endif

LocalPlayer *player = client->getEnv().getLocalPlayer();

// autojump if set: simulate "jump" key
if (player->getAutojump()) {
control.jump = true;
keypress_bits |= 1U << 4;
}
u32 keypress_bits = (
( (u32)(control.jump & 0x1) << 4) |
( (u32)(control.aux1 & 0x1) << 5) |
( (u32)(control.sneak & 0x1) << 6) |
( (u32)(control.dig & 0x1) << 7) |
( (u32)(control.place & 0x1) << 8) |
( (u32)(control.zoom & 0x1) << 9)
);

// autoforward if set: simulate "up" key
if (player->getPlayerSettings().continuous_forward &&
client->activeObjectsReceived() && !player->isDead()) {
control.up = true;
keypress_bits |= 1U << 0;
// Set direction keys to ensure mod compatibility
if (control.movement_speed > 0.001f) {
float absolute_direction;

// Check in original orientation (absolute value indicates forward / backward)
absolute_direction = abs(control.movement_direction);
if (absolute_direction < (3.0f / 8.0f * M_PI))
keypress_bits |= (u32)(0x1 << 0); // Forward
if (absolute_direction > (5.0f / 8.0f * M_PI))
keypress_bits |= (u32)(0x1 << 1); // Backward

// Rotate entire coordinate system by 90 degrees (absolute value indicates left / right)
absolute_direction = control.movement_direction + M_PI_2;
if (absolute_direction >= M_PI)
absolute_direction -= 2 * M_PI;
absolute_direction = abs(absolute_direction);
if (absolute_direction < (3.0f / 8.0f * M_PI))
keypress_bits |= (u32)(0x1 << 2); // Left
if (absolute_direction > (5.0f / 8.0f * M_PI))
keypress_bits |= (u32)(0x1 << 3); // Right
}

client->setPlayerControl(control);
Expand Down
43 changes: 43 additions & 0 deletions src/client/inputhandler.h
Expand Up @@ -240,6 +240,9 @@ class InputHandler
virtual bool wasKeyReleased(GameKeyType k) = 0;
virtual bool cancelPressed() = 0;

virtual float getMovementSpeed() = 0;
virtual float getMovementDirection() = 0;

virtual void clearWasKeyPressed() {}
virtual void clearWasKeyReleased() {}

Expand Down Expand Up @@ -285,6 +288,44 @@ class RealInputHandler : public InputHandler
{
return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k);
}
virtual float getMovementSpeed()
{
bool f = m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]),
b = m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]),
l = m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]),
r = m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]);
if (f || b || l || r)
{
// if contradictory keys pressed, stay still
if (f && b && l && r)
return 0.0f;
else if (f && b && !l && !r)
return 0.0f;
else if (!f && !b && l && r)
return 0.0f;
return 1.0f; // If there is a keyboard event, assume maximum speed
}
return joystick.getMovementSpeed();
}
virtual float getMovementDirection()
{
float x = 0, z = 0;

/* Check keyboard for input */
if (m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]))
z += 1;
if (m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]))
z -= 1;
if (m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]))
x += 1;
if (m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]))
x -= 1;

if (x != 0 || z != 0) /* If there is a keyboard event, it takes priority */
return atan2(x, z);
else
return joystick.getMovementDirection();
}
virtual bool cancelPressed()
{
return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey);
Expand Down Expand Up @@ -352,6 +393,8 @@ class RandomInputHandler : public InputHandler
virtual bool wasKeyPressed(GameKeyType k) { return false; }
virtual bool wasKeyReleased(GameKeyType k) { return false; }
virtual bool cancelPressed() { return false; }
virtual float getMovementSpeed() {return 0.0f;}
virtual float getMovementDirection() {return 0.0f;}
virtual v2s32 getMousePos() { return mousepos; }
virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }

Expand Down
24 changes: 21 additions & 3 deletions src/client/joystick_controller.cpp
Expand Up @@ -160,6 +160,7 @@ JoystickController::JoystickController() :
for (float &i : m_past_pressed_time) {
i = 0;
}
m_layout.axes_deadzone = 0;
clear();
}

Expand Down Expand Up @@ -251,10 +252,27 @@ void JoystickController::clear()
memset(m_axes_vals, 0, sizeof(m_axes_vals));
}

s16 JoystickController::getAxisWithoutDead(JoystickAxis axis)
float JoystickController::getAxisWithoutDead(JoystickAxis axis)
{
s16 v = m_axes_vals[axis];

if (abs(v) < m_layout.axes_deadzone)
return 0;
return v;
return 0.0f;

v += (v < 0 ? m_layout.axes_deadzone : -m_layout.axes_deadzone);

return (float)v / ((float)(INT16_MAX - m_layout.axes_deadzone));
}

float JoystickController::getMovementDirection()
{
return atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE), -getAxisWithoutDead(JA_FORWARD_MOVE));
}

float JoystickController::getMovementSpeed()
{
float speed = sqrt(pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) + pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
if (speed > 1.0f)
speed = 1.0f;
return speed;
}
5 changes: 4 additions & 1 deletion src/client/joystick_controller.h
Expand Up @@ -144,7 +144,10 @@ class JoystickController {
return m_axes_vals[axis];
}

s16 getAxisWithoutDead(JoystickAxis axis);
float getAxisWithoutDead(JoystickAxis axis);

float getMovementDirection();
float getMovementSpeed();

f32 doubling_dtime;

Expand Down
24 changes: 4 additions & 20 deletions src/client/localplayer.cpp
Expand Up @@ -566,23 +566,7 @@ void LocalPlayer::applyControl(float dtime, Environment *env)
}
}

if (control.up)
speedH += v3f(0.0f, 0.0f, 1.0f);

if (control.down)
speedH -= v3f(0.0f, 0.0f, 1.0f);

if (!control.up && !control.down)
speedH -= v3f(0.0f, 0.0f, 1.0f) * (control.forw_move_joystick_axis / 32767.f);

if (control.left)
speedH += v3f(-1.0f, 0.0f, 0.0f);

if (control.right)
speedH += v3f(1.0f, 0.0f, 0.0f);

if (!control.left && !control.right)
speedH += v3f(1.0f, 0.0f, 0.0f) * (control.sidew_move_joystick_axis / 32767.f);
speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));

if (m_autojump) {
// release autojump after a given time
Expand Down Expand Up @@ -639,6 +623,8 @@ void LocalPlayer::applyControl(float dtime, Environment *env)
else
speedH = speedH.normalize() * movement_speed_walk;

speedH *= control.movement_speed; /* Apply analog input */

// Acceleration increase
f32 incH = 0.0f; // Horizontal (X, Z)
f32 incV = 0.0f; // Vertical (Y)
Expand Down Expand Up @@ -1106,9 +1092,7 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env,
if (m_autojump)
return;

bool control_forward = control.up ||
(!control.up && !control.down &&
control.forw_move_joystick_axis < -0.05f);
bool control_forward = keyPressed & (1 << 0);

bool could_autojump =
m_can_jump && !control.jump && !control.sneak && control_forward;
Expand Down
4 changes: 0 additions & 4 deletions src/network/serverpackethandler.cpp
Expand Up @@ -510,10 +510,6 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
playersao->setWantedRange(wanted_range);

player->keyPressed = keyPressed;
player->control.up = (keyPressed & (0x1 << 0));
player->control.down = (keyPressed & (0x1 << 1));
player->control.left = (keyPressed & (0x1 << 2));
player->control.right = (keyPressed & (0x1 << 3));
player->control.jump = (keyPressed & (0x1 << 4));
player->control.aux1 = (keyPressed & (0x1 << 5));
player->control.sneak = (keyPressed & (0x1 << 6));
Expand Down
25 changes: 7 additions & 18 deletions src/player.h
Expand Up @@ -49,10 +49,6 @@ struct PlayerControl
PlayerControl() = default;

PlayerControl(
bool a_up,
bool a_down,
bool a_left,
bool a_right,
bool a_jump,
bool a_aux1,
bool a_sneak,
Expand All @@ -61,14 +57,10 @@ struct PlayerControl
bool a_place,
float a_pitch,
float a_yaw,
float a_sidew_move_joystick_axis,
float a_forw_move_joystick_axis
float a_movement_speed,
float a_movement_direction
)
{
up = a_up;
down = a_down;
left = a_left;
right = a_right;
jump = a_jump;
aux1 = a_aux1;
sneak = a_sneak;
Expand All @@ -77,13 +69,9 @@ struct PlayerControl
place = a_place;
pitch = a_pitch;
yaw = a_yaw;
sidew_move_joystick_axis = a_sidew_move_joystick_axis;
forw_move_joystick_axis = a_forw_move_joystick_axis;
movement_speed = a_movement_speed;
movement_direction = a_movement_direction;
}
bool up = false;
bool down = false;
bool left = false;
bool right = false;
bool jump = false;
bool aux1 = false;
bool sneak = false;
Expand All @@ -92,8 +80,9 @@ struct PlayerControl
bool place = false;
float pitch = 0.0f;
float yaw = 0.0f;
float sidew_move_joystick_axis = 0.0f;
float forw_move_joystick_axis = 0.0f;
// Note: These two are NOT available on the server
float movement_speed = 0.0f;
float movement_direction = 0.0f;
};

struct PlayerSettings
Expand Down
20 changes: 12 additions & 8 deletions src/script/lua_api/l_localplayer.cpp
Expand Up @@ -223,16 +223,20 @@ int LuaLocalPlayer::l_get_control(lua_State *L)
};

lua_createtable(L, 0, 12);
set("up", c.up);
set("down", c.down);
set("left", c.left);
set("right", c.right);
set("jump", c.jump);
set("aux1", c.aux1);
set("jump", c.jump);
set("aux1", c.aux1);
set("sneak", c.sneak);
set("zoom", c.zoom);
set("dig", c.dig);
set("zoom", c.zoom);
set("dig", c.dig);
set("place", c.place);
// Player movement in polar coordinates and non-binary speed
set("movement_speed", c.movement_speed);
set("movement_direction", c.movement_direction);
// Provide direction keys to ensure compatibility
set("up", player->keyPressed & (1 << 0)); // Up, down, left, and right were removed in favor of
set("down", player->keyPressed & (1 << 1)); // analog direction indicators and are therefore not
set("left", player->keyPressed & (1 << 2)); // available as booleans anymore. The corresponding values
set("right", player->keyPressed & (1 << 3)); // can still be read from the keyPressed bits though.

return 1;
}
Expand Down

0 comments on commit 1d69a23

Please sign in to comment.