Skip to content
Open
14 changes: 14 additions & 0 deletions docs/general_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ Note: The sound played when `referenceTargetPlayFireSound` is set to `true` is t
|--------------------|------|------------------------------------------------------------------------------------|
|`moveRate` |m/s | The rate of player motion, set this parameter to `0` to disable player motion |
|`moveScale` |`Vector2`| A scaler for X/Y player-space motion (set to 0 to lock forward/back, strafe motion)|
|`accelerationEnabled` |`bool`| Whether or not acceleration is enabled. |
|`movementAcceleration` |m/s^2 | Player acceleration during movement (defaults to 3.0) |
|`movementDeceleration` |m/s^2 | Player deceleration while stopping (defaults to 7.0) |
|`sprintMultiplier` |`float`| Multiplies with players move rate when player is sprinting (set 1 to turn off) |
|`playerAxisLock` |`Array<bool>`| Axis aligned motion lock for player |
|`turnScale` |`Vector2`| A scaler for horizontal/vertical player mouse motion (set to 0 to disable) |
|`playerHeight` |m | The height of the player above the ground when "standing" |
Expand All @@ -282,6 +286,9 @@ Note: The sound played when `referenceTargetPlayFireSound` is set to `true` is t
|`playerGravity` |m/s^2| The graivty vector that impacts the player |
|`disablePlayerMotionBetweenTrials`|`bool`|Don't allow the player to move when not in a trial? |
|`resetPlayerPositionBetweenTrials`|`bool`|Respawn the player to their original position between trials? |
|`headBobEnabled` |`bool` | Whether or not the headbob effect is enabled. |
|`headBobAmplitude` |m | This is additive to the crouch/playerHeight, Camera Y will bob within headBobAmplitude/2 and -headBobAmplitude/2.|
|`headBobFrequency` |`float`| How fast the headBob will happen. scales up with movement speed. |

```
"moveRate": 0.0, // Player move rate (0 for no motion)
Expand All @@ -294,6 +301,13 @@ Note: The sound played when `referenceTargetPlayFireSound` is set to `true` is t
"jumpInterval": 0.5, // Minimum jump interval
"jumpTouch": true, // Require touch for jump
"playerGravity": Vector3(0.0, -10.0, 0.0), // Player gravity
"sprintMultiplier": 1, // Player spring is disabled by default. (1 for no multiplied movement speed)
"accelerationEnabled": false, // Acceleration is disabled by default
"movementAcceleration": 3.0f // Acceleration during movement
"movementDeceleration": 7.0f // Deceleration while stopping
"headBobEnabled": false, // HeadBob is disabled by default
"headBobAmplitude": 0.1f, // HeadBob Amplitude
"headBobFrequency": 1.0f, // HeadBob Frequency
"disablePlayerMotionBetweenTrials": false, // Don't allow the player to move in between trials
"resetPlayerPositionBetweenTrials": false, // Respawn the player in the starting location between trials
```
Expand Down
15 changes: 15 additions & 0 deletions source/FPSciApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,13 @@ void FPSciApp::initPlayer(bool setSpawnPosition) {

// Set player values from session config
player->moveRate = &sessConfig->player.moveRate;
player->sprintMultiplier = &sessConfig->player.sprintMultiplier;
player->headBobEnabled = &sessConfig->player.headBobEnabled;
player->headBobAmplitude = &sessConfig->player.headBobAmplitude;
player->headBobFrequency = &sessConfig->player.headBobFrequency;
player->accelerationEnabled = &sessConfig->player.accelerationEnabled;
player->movementAcceleration = &sessConfig->player.movementAcceleration;
player->movementDeceleration = &sessConfig->player.movementDeceleration;
player->moveScale = &sessConfig->player.moveScale;
player->axisLock = &sessConfig->player.axisLock;
player->jumpVelocity = &sessConfig->player.jumpVelocity;
Expand Down Expand Up @@ -1137,6 +1144,10 @@ bool FPSciApp::onEvent(const GEvent& event) {
scene()->typedEntity<PlayerEntity>("player")->setJumpPressed(true);
foundKey = true;
}
else if (keyMap.map["sprint"].contains(ksym)) {
scene()->typedEntity<PlayerEntity>("player")->setSprintPressed(true);
foundKey = true;
}
}
}
else if ((event.type == GEventType::KEY_UP)) {
Expand All @@ -1145,6 +1156,10 @@ bool FPSciApp::onEvent(const GEvent& event) {
scene()->typedEntity<PlayerEntity>("player")->setCrouched(false);
foundKey = true;
}
else if (keyMap.map["sprint"].contains(ksym)) {
scene()->typedEntity<PlayerEntity>("player")->setSprintPressed(false);
foundKey = true;
}
}
}
if (foundKey) {
Expand Down
7 changes: 7 additions & 0 deletions source/FpsConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ void PlayerConfig::load(FPSciAnyTableReader reader, int settingsVersion) {
case 1:
reader.getIfPresent("moveRate", moveRate);
reader.getIfPresent("moveScale", moveScale);
reader.getIfPresent("sprintMultiplier", sprintMultiplier);
reader.getIfPresent("headBobEnabled", headBobEnabled);
reader.getIfPresent("headBobAmplitude", headBobAmplitude);
reader.getIfPresent("headBobFrequency", headBobFrequency);
reader.getIfPresent("accelerationEnabled", accelerationEnabled);
reader.getIfPresent("movementAcceleration", movementAcceleration);
reader.getIfPresent("movementDeceleration", movementDeceleration);
reader.getIfPresent("turnScale", turnScale);
reader.getIfPresent("playerHeight", height);
reader.getIfPresent("crouchHeight", crouchHeight);
Expand Down
7 changes: 7 additions & 0 deletions source/FpsConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ class PlayerConfig {
Array<bool> axisLock = { false, false, false }; ///< World-space player motion axis lock
bool stillBetweenTrials = false; ///< Disable player motion between trials?
bool resetPositionPerTrial = false; ///< Reset the player's position on a per trial basis (to scene default)
float sprintMultiplier = 1.0f; ///< Multiplies with players move rate when player is sprinting (defaults to 1x (off))
bool headBobEnabled = false; ///< Enables/Disables the HeadBob functionality (default is false (disabled))
float headBobAmplitude = 0.17f; ///< Determines how high/low players head will oscillate during movement (defaults to 0.17)
float headBobFrequency = 0.7f; ///< Determines how fast players head will oscillate during movement (defaults to 0.7)
bool accelerationEnabled = false; ///< Enables/Disables the acceleration/deceleration functionality (default is false (disabled))
float movementAcceleration = 12.0f; ///< Player acceleration during movement (defaults to 12.0)
float movementDeceleration = 22.0f; ///< Player deceleration while stopping (defaults to 12.0)

void load(FPSciAnyTableReader reader, int settingsVersion = 1);
Any addToAny(Any a, bool forceAll = false) const;
Expand Down
35 changes: 35 additions & 0 deletions source/GuiElements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,41 @@ PlayerControls::PlayerControls(SessionConfig& config, std::function<void()> expo
c->setCaptionWidth(width / 2);
c->setWidth(width*0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addCheckBox("Use Acceleration?", &(config.player.accelerationEnabled));
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addNumberBox("Movement Acceleration", &(config.player.movementAcceleration), "m/s^2", GuiTheme::LINEAR_SLIDER, 0.001f, 100.0f);
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addNumberBox("Movement Deceleration", &(config.player.movementDeceleration), "m/s^2", GuiTheme::LINEAR_SLIDER, 0.001f, 100.0f);
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addNumberBox("Sprint Multiplier", &(config.player.sprintMultiplier), "x", GuiTheme::LINEAR_SLIDER, 1.0f, 10.0f);
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addCheckBox("Use Headbob?", &(config.player.headBobEnabled));
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addNumberBox("Headbob Amplitude", &(config.player.headBobAmplitude), "m", GuiTheme::LINEAR_SLIDER, 0.0f, 5.0f);
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addNumberBox("Headbob Frequency", &(config.player.headBobFrequency), "x", GuiTheme::LINEAR_SLIDER, 0.0f, 10.0f);
c->setCaptionWidth(width / 2);
c->setWidth(width * 0.95f);
}movePane->endRow();
movePane->beginRow(); {
auto c = movePane->addNumberBox("Jump Velocity", &(config.player.jumpVelocity), "m/s", GuiTheme::LINEAR_SLIDER, 0.0f, 50.0f, 0.1f);
c->setCaptionWidth(width / 2);
Expand Down
1 change: 1 addition & 0 deletions source/KeyMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ KeyMapping::KeyMapping() {
map.set("strafeLeft", Array<GKey>{ (GKey)'a', GKey::LEFT });
map.set("moveBackward", Array<GKey>{ (GKey)'s', GKey::DOWN });
map.set("strafeRight", Array<GKey>{ (GKey)'d', GKey::RIGHT });
map.set("sprint", Array<GKey>{ GKey::LSHIFT });
map.set("openMenu", Array<GKey>{ GKey::ESCAPE });
map.set("quit", Array<GKey>{ GKey::KP_MINUS, GKey::PAUSE });
map.set("crouch", Array<GKey>{ GKey::LCTRL });
Expand Down
84 changes: 77 additions & 7 deletions source/PlayerEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,30 @@ void PlayerEntity::onPose(Array<shared_ptr<Surface> >& surfaceArray) {
}

void PlayerEntity::updateFromInput(UserInput* ui) {

m_walkSpeed = 0;

const float walkSpeed = *moveRate * units::meters() / units::seconds();
// Check if player is sprinting or not
if (!m_sprinting) {
m_walkSpeed = *moveRate * units::meters() / units::seconds();
}
else {
m_walkSpeed = *moveRate * *sprintMultiplier * units::meters() / units::seconds();
}

// Get walking speed here (and normalize if necessary)
Vector3 linear = Vector3(ui->getX()*moveScale->x, 0, -ui->getY()*moveScale->y);
if (linear.magnitude() > 0) {
linear = linear.direction() * walkSpeed;
m_linearVector = Vector3(ui->getX()*moveScale->x, 0, -ui->getY()*moveScale->y);
if (m_linearVector.magnitude() > 0) {
m_gettingMovementInput = true;
}

// Add jump here (if needed)
RealTime timeSinceLastJump = System::time() - m_lastJumpTime;
if (m_jumpPressed && timeSinceLastJump > *jumpInterval) {
// Allow jumping if jumpTouch = False or if jumpTouch = True and the player is in contact w/ the map
if (!(*jumpTouch) || m_inContact) {
const Vector3 jv(0, *jumpVelocity * units::meters() / units::seconds(), 0);
linear += jv;
m_linearVector += jv;
m_lastJumpTime = System::time();
}
}
Expand All @@ -115,8 +124,7 @@ void PlayerEntity::updateFromInput(UserInput* ui) {
float yaw = mouseRotate.x;
float pitch = mouseRotate.y;

// Set the player translation/view velocities
setDesiredOSVelocity(linear);
// Set the player view velocity
setDesiredAngularVelocity(yaw, pitch);
}

Expand Down Expand Up @@ -147,6 +155,68 @@ void PlayerEntity::onSimulation(SimTime absoluteTime, SimTime deltaTime) {
respawn();
}
}

if (!m_gettingMovementInput) {

if (accelerationEnabled!= nullptr && *accelerationEnabled) {
m_acceleratedVelocity = max(m_acceleratedVelocity - *movementDeceleration * deltaTime, 0.0f);
}
else {
m_acceleratedVelocity = 0;
}

if (m_acceleratedVelocity > 0) {
m_linearVector = m_acceleratedVelocity * m_lastDirection;
}
}
else {

if (*accelerationEnabled) {
m_acceleratedVelocity = min(m_acceleratedVelocity + *movementAcceleration * deltaTime, m_walkSpeed);
}
else {
m_acceleratedVelocity = m_walkSpeed;
}

m_linearVector = m_linearVector.direction() * m_acceleratedVelocity;
m_lastDirection = m_linearVector.direction();
}
/** The HeadBob uses lerp to achieve the "Bobbing" effect. The lerp function tries to reach
the designated amplitude, but as Lerp can never reach final value, we change the polarity
when it reaches half of the designated amplitude. The lerp function will then try to reach
to the negative(-) of amplitude value. The polarity will again be changed when it reaches
half of (-amplitude), thus achieving the effect.

The frequency (how fast the HeadBob will happen) is controlled by the headBobFrequency value
as well as the players current movement speed. So the faster the player moves, faster the
effect will be. Its also tied to deltaTime, so FPS will not effect how fast/slow HeadBob
happens.
*/
if (headBobEnabled != nullptr && *headBobEnabled && m_acceleratedVelocity > 0) {
if (!m_headBobPolarity) {
m_headBobCurrentHeight = lerp(m_headBobCurrentHeight, *headBobAmplitude, *headBobFrequency * m_acceleratedVelocity * deltaTime);

if (m_headBobCurrentHeight >= *headBobAmplitude / 2) {
m_headBobPolarity = !m_headBobPolarity;
}
}
else {
m_headBobCurrentHeight = lerp(m_headBobCurrentHeight, -*headBobAmplitude, *headBobFrequency * m_acceleratedVelocity * deltaTime);

if (m_headBobCurrentHeight <= -*headBobAmplitude / 2) {
m_headBobPolarity = !m_headBobPolarity;
}
}
}
// Height of the camera will reset to the original position once the player has stopped moving
else if(headBobEnabled != nullptr && *headBobEnabled && m_acceleratedVelocity <= 0){
m_headBobCurrentHeight = lerp(m_headBobCurrentHeight, 0, *headBobFrequency * deltaTime);
}

//Set Players Translation velocity
setDesiredOSVelocity(m_linearVector);

m_gettingMovementInput = false;
}

void PlayerEntity::getConservativeCollisionTris(Array<Tri>& triArray, const Vector3& velocity, float deltaTime) const {
Expand Down
33 changes: 30 additions & 3 deletions source/PlayerEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,22 @@ class PlayerEntity : public VisibleEntity {
float m_lastJumpVelocity;
float m_health = 1.0f; ///< Player health storage

float m_walkSpeed; ///< Players movement speed

bool m_gettingMovementInput; ///< Is getting movement input from user?
bool m_headBobPolarity; ///< Is head moving up/down?
float m_headBobCurrentHeight; ///< Headbob current that gets added to camera y

bool m_inContact = false; ///< Is the player in contact w/ anything?
bool m_motionEnable = true; ///< Flag to disable player motion
bool m_jumpPressed = false; ///< Indicates whether jump buton was pressed

bool m_sprinting = false; ///< Is the player sprinting?

Vector3 m_linearVector; ///< Vector for movement
Vector3 m_lastDirection; ///< Holds players last heading
float m_acceleratedVelocity; ///< Velocity after adding acceleration

PlayerEntity() {}

#ifdef G3D_OSX
Expand All @@ -50,14 +62,24 @@ class PlayerEntity : public VisibleEntity {
float* moveRate = nullptr; ///< Player movement rate (m/s)
Vector2* moveScale = nullptr; ///< Player X/Y movement scale vector (interpreted as unit vector)
Array<bool>* axisLock = nullptr; ///< World-space axis lock

bool* accelerationEnabled = nullptr; ///< Checks if acceleration/deceleration is enabled or not
float* movementAcceleration = nullptr; ///< Players rate of acceletion during movement
float* movementDeceleration = nullptr; ///< Players rate of deceleration while stopping movement

float* sprintMultiplier = nullptr; ///< Sprint speed multiplier

float* jumpVelocity = nullptr; ///< Player vertical (+Y) jump velocity
float* jumpInterval = nullptr; ///< Player minimum jump interval limit
bool* jumpTouch = nullptr; ///< Require contact for jump?
bool* jumpTouch = nullptr; ///< Require contact for jump?

float* height = nullptr; ///< Player height when standing
float* crouchHeight = nullptr; ///< Player height when crouched

bool* headBobEnabled = nullptr; ///< Checks if headbob is enabled or not
float* headBobAmplitude = nullptr; ///< Players headbob motion amplitude
float* headBobFrequency = nullptr; ///< Players headbob motion frequency

/** \brief Computes all triangles that could be hit during a
slideMove with the current \a velocity, allowing that the
velocity may be decreased along some axes during movement.
Expand Down Expand Up @@ -98,15 +120,20 @@ class PlayerEntity : public VisibleEntity {
const CFrame getCameraFrame() const {
CFrame f = frame();
if (notNull(height)) {
f.translation += Point3(0.0f, heightOffset(m_crouched ? *crouchHeight : *height), 0.0f);
if (*headBobEnabled) {
f.translation += Point3(0.0f, heightOffset(m_crouched ? *crouchHeight : *height) + m_headBobCurrentHeight, 0.0f);
}
else {
f.translation += Point3(0.0f, heightOffset(m_crouched ? *crouchHeight : *height), 0.0f);
}
}
return f;
}

void setCrouched(bool crouched) { m_crouched = crouched; };
void setJumpPressed(bool pressed=true) { m_jumpPressed = pressed; }
void setMoveEnable(bool enabled) { m_motionEnable = enabled; }

void setSprintPressed(bool sprinting) { m_sprinting = sprinting; };
void setRespawnPosition(Point3 pos) { m_respawnPosition = pos; }
void setRespawnHeadingDegrees(float headingDeg) { m_spawnHeadingRadians = pif() / 180.f * headingDeg; }
void setRespawnHeight(float height) { m_respawnHeight = height; }
Expand Down