private void followToParent(final CharacterStateEvent state, EntityRef entity) { LocationComponent locationComponent = entity.getComponent(LocationComponent.class); if (!locationComponent.getParent().equals(EntityRef.NULL)) { Vector3f velocity = new Vector3f(locationComponent.getWorldPosition()); velocity.sub(state.getPosition()); state.getVelocity().set(velocity); state.getPosition().set(locationComponent.getWorldPosition()); } }
private void updateFocalDistance(HitResult hitInfo, float delta) { float focusRate = 4.0f; // how fast the focus distance is updated // if the hit result from a trace has a recorded a hit if (hitInfo.isHit()) { Vector3f playerToTargetRay = new Vector3f(); // calculate the distance from the player to the hit point playerToTargetRay.sub(hitInfo.getHitPoint(), localPlayer.getPosition()); // gradually adjust focalDistance from it's current value to the hit point distance focalDistance = TeraMath.lerp(focalDistance, playerToTargetRay.length(), delta * focusRate); // if nothing was hit, gradually adjust the focusDistance to the maximum length of the update // function trace } else { focalDistance = TeraMath.lerp(focalDistance, targetDistance, delta * focusRate); } }
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; } } } } }
private boolean moveDown( float dist, float slopeFactor, CharacterCollider collider, Vector3f position) { if (collider == null) { position.y += dist; return false; } float remainingDist = -dist; Vector3f targetPos = new Vector3f(position); targetPos.y -= remainingDist + VERTICAL_PENETRATION_LEEWAY; Vector3f normalizedDir = new Vector3f(0, -1, 0); boolean hit = false; int iteration = 0; while (remainingDist > physics.getEpsilon() && iteration++ < 10) { SweepCallback callback = collider.sweep(position, targetPos, VERTICAL_PENETRATION, -1.0f); float actualDist = Math.max( 0, (remainingDist + VERTICAL_PENETRATION_LEEWAY) * callback.getClosestHitFraction() - VERTICAL_PENETRATION_LEEWAY); Vector3f expectedMove = new Vector3f(targetPos); expectedMove.sub(position); if (expectedMove.lengthSquared() > physics.getEpsilon()) { expectedMove.normalize(); expectedMove.scale(actualDist); position.add(expectedMove); } remainingDist -= actualDist; if (remainingDist < physics.getEpsilon()) { break; } if (callback.hasHit()) { float originalSlope = callback.getHitNormalWorld().dot(new Vector3f(0, 1, 0)); if (originalSlope < slopeFactor) { float slope = callback.calculateAverageSlope(originalSlope, CHECK_FORWARD_DIST); if (slope < slopeFactor) { remainingDist -= actualDist; expectedMove.set(targetPos); expectedMove.sub(position); extractResidualMovement(callback.getHitNormalWorld(), expectedMove); float sqrDist = expectedMove.lengthSquared(); if (sqrDist > physics.getEpsilon()) { expectedMove.normalize(); if (expectedMove.dot(normalizedDir) <= 0.0f) { hit = true; break; } } else { hit = true; break; } if (expectedMove.y > -physics.getEpsilon()) { hit = true; break; } normalizedDir.set(expectedMove); expectedMove.scale(-remainingDist / expectedMove.y + HORIZONTAL_PENETRATION_LEEWAY); targetPos.set(position); targetPos.add(expectedMove); } else { hit = true; break; } } else { hit = true; break; } } else { break; } } if (iteration >= 10) { hit = true; } return hit; }