private void climb( final CharacterStateEvent state, CharacterMoveInputEvent input, Vector3f desiredVelocity) { if (state.getClimbDirection() == null) { return; } Vector3f tmp; Vector3i climbDir3i = state.getClimbDirection(); Vector3f climbDir3f = climbDir3i.toVector3f(); Quat4f rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), 0, 0); tmp = new Vector3f(0.0f, 0.0f, -1.0f); QuaternionUtil.quatRotate(rotation, tmp, tmp); float angleToClimbDirection = tmp.angle(climbDir3f); boolean clearMovementToDirection = !state.isGrounded(); // facing the ladder or looking down or up if (angleToClimbDirection < Math.PI / 4.0 || Math.abs(input.getPitch()) > 60f) { float pitchAmount = state.isGrounded() ? 45f : 90f; float pitch = input.getPitch() > 30f ? pitchAmount : -pitchAmount; rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), TeraMath.DEG_TO_RAD * pitch, 0); QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity); // looking sidewards from ladder } else if (angleToClimbDirection < Math.PI * 3.0 / 4.0) { float rollAmount = state.isGrounded() ? 45f : 90f; tmp = new Vector3f(); QuaternionUtil.quatRotate(rotation, climbDir3f, tmp); float leftOrRight = tmp.x; float plusOrMinus = (leftOrRight < 0f ? -1.0f : 1.0f) * (climbDir3i.x != 0 ? -1.0f : 1.0f); rotation = new Quat4f( TeraMath.DEG_TO_RAD * input.getYaw(), 0f, TeraMath.DEG_TO_RAD * rollAmount * plusOrMinus); QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity); // facing away from ladder } else { rotation = new Quat4f(TeraMath.DEG_TO_RAD * state.getYaw(), 0, 0); QuaternionUtil.quatRotate(rotation, desiredVelocity, desiredVelocity); clearMovementToDirection = false; } // clear out movement towards or away from the ladder if (clearMovementToDirection) { if (climbDir3i.x != 0) { desiredVelocity.x = 0f; } if (climbDir3i.z != 0) { desiredVelocity.z = 0f; } } }
public void setToInterpolateState( EntityRef entity, CharacterStateEvent a, CharacterStateEvent b, long time) { float t = (float) (time - a.getTime()) / (b.getTime() - a.getTime()); Vector3f newPos = BaseVector3f.lerp(a.getPosition(), b.getPosition(), t); Quat4f newRot = BaseQuat4f.interpolate(a.getRotation(), b.getRotation(), t); LocationComponent location = entity.getComponent(LocationComponent.class); location.setWorldPosition(newPos); location.setWorldRotation(newRot); entity.saveComponent(location); CharacterMovementComponent movementComponent = entity.getComponent(CharacterMovementComponent.class); movementComponent.mode = a.getMode(); movementComponent.setVelocity(a.getVelocity()); movementComponent.grounded = a.isGrounded(); if (b.getFootstepDelta() < a.getFootstepDelta()) { movementComponent.footstepDelta = t * (1 + b.getFootstepDelta() - a.getFootstepDelta()) + a.getFootstepDelta(); if (movementComponent.footstepDelta > 1) { movementComponent.footstepDelta -= 1; } } else { movementComponent.footstepDelta = t * (b.getFootstepDelta() - a.getFootstepDelta()) + a.getFootstepDelta(); } entity.saveComponent(movementComponent); extrapolateCharacterComponent(entity, b); setPhysicsLocation(entity, newPos); }
private void extrapolateCharacterMovementComponent(EntityRef entity, CharacterStateEvent state) { CharacterMovementComponent movementComponent = entity.getComponent(CharacterMovementComponent.class); movementComponent.mode = state.getMode(); movementComponent.setVelocity(state.getVelocity()); movementComponent.grounded = state.isGrounded(); entity.saveComponent(movementComponent); }
/** * Sets the state of the given entity to the state represented by the CharacterStateEvent. The * state of the entity is determined by its LocationComponent (location and orientation of physics * body), CharacterMovementComponent (velocity and various movement state variables) and * CharacterComponent for pitch and yaw (used for the camera). * * @param entity * @param state */ public void setToState(EntityRef entity, CharacterStateEvent state) { LocationComponent location = entity.getComponent(LocationComponent.class); CharacterMovementComponent movementComp = entity.getComponent(CharacterMovementComponent.class); CharacterComponent characterComponent = entity.getComponent(CharacterComponent.class); if (location == null || movementComp == null || characterComponent == null) { return; } location.setWorldPosition(state.getPosition()); location.setWorldRotation(state.getRotation()); entity.saveComponent(location); movementComp.mode = state.getMode(); movementComp.setVelocity(state.getVelocity()); movementComp.grounded = state.isGrounded(); movementComp.footstepDelta = state.getFootstepDelta(); entity.saveComponent(movementComp); characterComponent.pitch = state.getPitch(); characterComponent.yaw = state.getYaw(); entity.saveComponent(characterComponent); setPhysicsLocation(entity, state.getPosition()); }
public static void setToExtrapolateState(EntityRef entity, CharacterStateEvent state, long time) { float t = (time - state.getTime()) * 0.0001f; Vector3f newPos = new Vector3f(state.getVelocity()); newPos.scale(t); newPos.add(state.getPosition()); LocationComponent location = entity.getComponent(LocationComponent.class); location.setWorldPosition(newPos); location.setWorldRotation(state.getRotation()); entity.saveComponent(location); CharacterMovementComponent movementComponent = entity.getComponent(CharacterMovementComponent.class); movementComponent.mode = state.getMode(); movementComponent.setVelocity(state.getVelocity()); movementComponent.grounded = state.isGrounded(); entity.saveComponent(movementComponent); CharacterComponent characterComponent = entity.getComponent(CharacterComponent.class); characterComponent.pitch = state.pitch; characterComponent.yaw = state.yaw; entity.saveComponent(characterComponent); setPhysicsLocation(entity, newPos); }
private void walk( final CharacterMovementComponent movementComp, final CharacterStateEvent state, CharacterMoveInputEvent input, EntityRef entity) { Vector3f desiredVelocity = new Vector3f(input.getMovementDirection()); float lengthSquared = desiredVelocity.lengthSquared(); // If the length of desired movement is > 1, normalise it to prevent movement being faster than // allowed. // (Desired velocity < 1 is allowed, as the character may wish to walk/crawl/otherwise move // slowly) if (lengthSquared > 1) { desiredVelocity.normalize(); } desiredVelocity.scale(movementComp.speedMultiplier); float maxSpeed = getMaxSpeed(entity, movementComp); if (input.isRunning()) { maxSpeed *= movementComp.runFactor; } // As we can't use it, remove the y component of desired movement while maintaining speed. if (movementComp.grounded && desiredVelocity.y != 0) { float speed = desiredVelocity.length(); desiredVelocity.y = 0; if (desiredVelocity.x != 0 || desiredVelocity.z != 0) { desiredVelocity.normalize(); desiredVelocity.scale(speed); } } desiredVelocity.scale(maxSpeed); if (movementComp.mode == MovementMode.CLIMBING) { climb(state, input, desiredVelocity); } // Modify velocity towards desired, up to the maximum rate determined by friction Vector3f velocityDiff = new Vector3f(desiredVelocity); velocityDiff.sub(state.getVelocity()); velocityDiff.scale(Math.min(movementComp.mode.scaleInertia * input.getDelta(), 1.0f)); Vector3f endVelocity = new Vector3f(state.getVelocity()); endVelocity.x += velocityDiff.x; endVelocity.z += velocityDiff.z; if (movementComp.mode.scaleGravity == 0) { // apply the velocity without gravity endVelocity.y += velocityDiff.y; } else if (movementComp.mode.applyInertiaToVertical) { endVelocity.y += Math.max( -TERMINAL_VELOCITY, velocityDiff.y - (GRAVITY * movementComp.mode.scaleGravity) * input.getDelta()); } else { endVelocity.y = Math.max( -TERMINAL_VELOCITY, state.getVelocity().y - (GRAVITY * movementComp.mode.scaleGravity) * input.getDelta()); } Vector3f moveDelta = new Vector3f(endVelocity); moveDelta.scale(input.getDelta()); CharacterCollider collider = movementComp.mode.useCollision ? physics.getCharacterCollider(entity) : null; MoveResult moveResult = move( state.getPosition(), moveDelta, (state.getMode() != MovementMode.CLIMBING && state.isGrounded() && movementComp.mode.canBeGrounded) ? movementComp.stepHeight : 0, movementComp.slopeFactor, collider); Vector3f distanceMoved = new Vector3f(moveResult.getFinalPosition()); distanceMoved.sub(state.getPosition()); state.getPosition().set(moveResult.getFinalPosition()); if (input.isFirstRun() && distanceMoved.length() > 0) { entity.send(new MovedEvent(distanceMoved, state.getPosition())); } if (moveResult.isBottomHit()) { if (!state.isGrounded() && movementComp.mode.canBeGrounded) { if (input.isFirstRun()) { Vector3f landVelocity = new Vector3f(state.getVelocity()); landVelocity.y += (distanceMoved.y / moveDelta.y) * (endVelocity.y - state.getVelocity().y); logger.debug("Landed at " + landVelocity); entity.send(new VerticalCollisionEvent(state.getPosition(), landVelocity)); } state.setGrounded(true); } endVelocity.y = 0; // Jumping is only possible, if the entity is standing on ground if (input.isJumpRequested()) { state.setGrounded(false); endVelocity.y += movementComp.jumpSpeed; if (input.isFirstRun()) { entity.send(new JumpEvent()); } } } else { if (moveResult.isTopHit() && endVelocity.y > 0) { endVelocity.y = -0.5f * endVelocity.y; } state.setGrounded(false); } state.getVelocity().set(endVelocity); if (input.isFirstRun() && moveResult.isHorizontalHit()) { entity.send(new HorizontalCollisionEvent(state.getPosition(), state.getVelocity())); } if (state.isGrounded() || movementComp.mode == MovementMode.SWIMMING || movementComp.mode == MovementMode.DIVING) { state.setFootstepDelta( state.getFootstepDelta() + distanceMoved.length() / movementComp.distanceBetweenFootsteps); if (state.getFootstepDelta() > 1) { state.setFootstepDelta(state.getFootstepDelta() - 1); if (input.isFirstRun()) { switch (movementComp.mode) { case WALKING: entity.send(new FootstepEvent()); break; case DIVING: case SWIMMING: entity.send(new SwimStrokeEvent(worldProvider.getBlock(state.getPosition()))); break; } } } } }