import eu.svjatoslav.sixth.e3d.geometry.Point3D;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+
public class Avatar implements ViewUpdateListener {
public static final double SPEED_LIMIT = 30;
* are updated within the world, avatar orientation relative to the world is
* taken into account.
*/
- private final Point3D movementDirection = new Point3D();
+ private final Point3D movementVector = new Point3D();
public double avatarAcceleration = 0.1;
/**
* Avatar location within the 3D world.
*/
private double orientationYZ;
+ /**
+ * Determines amount of friction user experiences every millisecond while moving around in space.
+ */
+ private static final double MILLISECOND_FRICTION = 1.005;
+
public Avatar() {
}
}
@Override
- public boolean beforeViewUpdate(final ViewContext viewContext,
- final int millisecondsSinceLastFrame) {
+ public boolean beforeViewUpdate(final ViewContext viewContext, final int millisecondsSinceLastFrame) {
final Point3D locationBeforeUpdate = new Point3D(location);
- updateLocation(millisecondsSinceLastFrame);
-
- final double distanceMoved = location
- .getDistanceTo(locationBeforeUpdate);
+ translateAvatarLocation(millisecondsSinceLastFrame);
+ applyFrictionToUserMovement(millisecondsSinceLastFrame);
+ return isFrameRepaintNeeded(locationBeforeUpdate);
+ }
+ private boolean isFrameRepaintNeeded(Point3D locationBeforeUpdate) {
+ final double distanceMoved = location.getDistanceTo(locationBeforeUpdate);
return distanceMoved > 0.03;
-
}
public void enforceSpeedLimit() {
- final double currentSpeed = movementDirection
+ final double currentSpeed = movementVector
.getDistanceTo(Point3D.ZERO);
if (currentSpeed <= SPEED_LIMIT)
return;
- movementDirection.scaleDown(currentSpeed / SPEED_LIMIT);
+ movementVector.scaleDown(currentSpeed / SPEED_LIMIT);
}
public double getAngleXZ() {
this.location = location;
}
- public Point3D getMovementDirection() {
- return movementDirection;
+ public Point3D getMovementVector() {
+ return movementVector;
}
public double getMovementSpeed() {
- return movementDirection.getDistanceTo(Point3D.ZERO);
+ return movementVector.getDistanceTo(Point3D.ZERO);
+ }
+
+ private void applyFrictionToUserMovement(int millisecondsPassedSinceLastFrame) {
+ for (int i = 0; i < millisecondsPassedSinceLastFrame; i++)
+ applyMillisecondFrictionToUserMovementVector();
+ }
+
+ private void applyMillisecondFrictionToUserMovementVector() {
+ getMovementVector().x /= MILLISECOND_FRICTION;
+ getMovementVector().y /= MILLISECOND_FRICTION;
+ getMovementVector().z /= MILLISECOND_FRICTION;
}
/**
- * Update camera location based on current speed
+ * Translate coordinates based on avatar movement vector and avatar orientation in the world.
+ *
+ * @param millisecondsPassedSinceLastFrame We want avatar movement to be independent of framerate.
+ * Therefore we take frame rendering time into account when translating
+ * avatar between consecutive frames.
*/
- public void updateLocation(final int millisecondsPassedSinceLastFrame) {
-
- // translate user coordinates based on avatar movement speed, and avatar
- // orientation in the world
- {
- location.x -= (float) Math.sin(getAngleXZ())
- * getMovementDirection().z * SPEED_MULTIPLIER
- * millisecondsPassedSinceLastFrame;
- location.z += (float) Math.cos(getAngleXZ())
- * getMovementDirection().z * SPEED_MULTIPLIER
- * millisecondsPassedSinceLastFrame;
-
- location.x += (float) Math.cos(getAngleXZ())
- * getMovementDirection().x * SPEED_MULTIPLIER
- * millisecondsPassedSinceLastFrame;
- location.z += (float) Math.sin(getAngleXZ())
- * getMovementDirection().x * SPEED_MULTIPLIER
- * millisecondsPassedSinceLastFrame;
-
- location.y += getMovementDirection().y * SPEED_MULTIPLIER
- * millisecondsPassedSinceLastFrame;
- }
-
- final double millisecondFriction = 1.005;
- // apply friction to progressively slow movement
- for (int i = 0; i < millisecondsPassedSinceLastFrame; i++) {
- getMovementDirection().x = getMovementDirection().x
- / millisecondFriction;
- getMovementDirection().y = getMovementDirection().y
- / millisecondFriction;
- getMovementDirection().z = getMovementDirection().z
- / millisecondFriction;
- }
+ private void translateAvatarLocation(int millisecondsPassedSinceLastFrame) {
+ location.x -= (float) sin(getAngleXZ())
+ * getMovementVector().z * SPEED_MULTIPLIER
+ * millisecondsPassedSinceLastFrame;
+ location.z += (float) cos(getAngleXZ())
+ * getMovementVector().z * SPEED_MULTIPLIER
+ * millisecondsPassedSinceLastFrame;
+
+ location.x += (float) cos(getAngleXZ())
+ * getMovementVector().x * SPEED_MULTIPLIER
+ * millisecondsPassedSinceLastFrame;
+ location.z += (float) sin(getAngleXZ())
+ * getMovementVector().x * SPEED_MULTIPLIER
+ * millisecondsPassedSinceLastFrame;
+
+ location.y += getMovementVector().y * SPEED_MULTIPLIER
+ * millisecondsPassedSinceLastFrame;
}
}
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
public class RenderAggregator {
- ArrayList<AbstractCoordinateShape> shapes = new ArrayList<>();
- ShapesComparator comparator = new ShapesComparator();
+ private ArrayList<AbstractCoordinateShape> shapes = new ArrayList<>();
+ private ShapesZIndexComparator comparator = new ShapesZIndexComparator();
public void paint(final RenderingContext renderBuffer) {
-
- Collections.sort(shapes, comparator);
-
- for (final AbstractCoordinateShape shape : shapes)
- shape.paint(renderBuffer);
-
+ shapes.sort(comparator);
+ shapes.forEach(shape -> shape.paint(renderBuffer));
}
public void queueShapeForRendering(final AbstractCoordinateShape shape) {
shapes.clear();
}
- static class ShapesComparator implements Comparator<AbstractCoordinateShape>, Serializable {
+ static class ShapesZIndexComparator implements Comparator<AbstractCoordinateShape>, Serializable {
@Override
public int compare(final AbstractCoordinateShape o1, final AbstractCoordinateShape o2) {