Hello everybody,
I wanted to share with you the results of some experiments on moving platforms.
Let’s talk about it after the video:
I tried to model a test level in Blender, I’m not good yet but I do my best.
Below is the list of features visible in the video:
- Physics with Minie.
- third-person view demo with automatic camera obstruction detection.
- the elevator can move up and down between floors.
- the position of the floors and the speed of the lift are configurable in the controller.
- the lift doors can be opened by interacting with the call panel, which is activated with a simple proximity trigger.
- when the player can open the doors, a message appears at the top left.
- the doors close automatically when the player moves too far from the call panel.
- all the geometries that are inside the elevator node have a rigid body in kinematic mode (Minie wants a mass > 0).
I have simplified some physics problems that I have not been able to solve. For example when the elevator moves, I disable the BetterCharacterControl
and move the player along with the platform with the spatial.move()
method. This means that the player cannot move during the ascent and descent of the elevator, while the camera remains free to move at will.
In some Unity tutorials, to resolve this limitation, the player is temporarily attached to the elevator node during movement and then reattached to the scene node. I was unable to imitate this approach in jMonkey.
Here’s the script of the elevator.
public class ElevatorControl extends AbstractControl implements ActionListener, TriggerListener {
public BitmapText interactionUI;
public Node player;
public Spatial platform;
public AudioNode elevatorPanelSFX;
public AudioNode elevatorStopSFX, elevatorMoveSFX;
public List<Spatial> lstDoors;
public float ySpeed = 2f;
public Vector3f[] floors = {
new Vector3f(0, 0, 0),
new Vector3f(0, 14, 0),
new Vector3f(0, 28, 0)
};
/* Constants */
private int floorNumber = 0;
private boolean callElevator;
private boolean onPlatform;
private boolean isMoving;
boolean isMoved;
private String dirName;
private final String MOVE_PLATFORM_UP = KeyMapping.KEY_1;
private final String MOVE_PLATFORM_DOWN = KeyMapping.KEY_2;
private final String OPEN_DOORS = KeyMapping.KEY_3;
@Override
public void onAction(String action, boolean isPressed, float tpf) {
if (callElevator && !isPressed) {
if (action.equals(OPEN_DOORS)) {
elevatorPanelSFX.play();
openDoors();
}
}
if (onPlatform && !isMoving && !isPressed) {
if (action.equals(MOVE_PLATFORM_UP)) {
isMoving = true;
dirName = "ElevatorUp";
ySpeed = Math.abs(ySpeed);
floorNumber++;
} else if (action.equals(MOVE_PLATFORM_DOWN)) {
isMoving = true;
dirName = "ElevatorDown";
ySpeed = -1 * Math.abs(ySpeed);
floorNumber--;
}
}
}
@Override
public void controlUpdate(float tpf) {
if (isMoving) {
floorNumber = MathUtils.clamp(floorNumber, 0, floors.length - 1);
Vector3f floor = floors[floorNumber];
boolean moveUp = dirName.equals("ElevatorUp") && platform.getWorldTranslation().getY() < floor.y;
boolean moveDown = dirName.equals("ElevatorDown") && platform.getWorldTranslation().getY() > floor.y;
if (moveUp || moveDown) {
setPlayerInputEnabled(false);
//platform.attachChild(player); //this approach doesn't work
player.move(0f, ySpeed * tpf, 0f);
platform.move(0f, ySpeed * tpf, 0f);
closeDoors();
} else {
isMoving = false;
Vector3f v = platform.getWorldTranslation().setY(floor.y);
platform.setLocalTranslation(v);
//rootNode.attachChild(player); //this approach doesn't work
setPlayerInputEnabled(true);
openDoors();
}
if (isMoving && !isMoved) {
elevatorMoveSFX.play();
isMoved = true;
}
if (!isMoving && isMoved) {
elevatorMoveSFX.stop();
elevatorStopSFX.play();
isMoved = false;
}
}
if (!onPlatform && FVector.distance(platform, player) > 6f) {
closeDoors();
}
}
private void setPlayerInputEnabled(boolean enable) {
player.getControl(BetterCharacterControl.class).setEnabled(enable);
}
private void openDoors() {
for (Spatial sp : lstDoors) {
sp.getControl(FlipFlopControl.class).setStatus(FlipFlopControl.Status.FLIP);
}
}
private void closeDoors() {
for (Spatial sp : lstDoors) {
sp.getControl(FlipFlopControl.class).setStatus(FlipFlopControl.Status.FLOP);
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
//To change body of generated methods, choose Tools | Templates.
}
@Override
public void onTriggerEnter(EnterableTrigger trigger) {
if (trigger.getTagName().equals("Elevator-Platform")) {
onPlatform = true;
}
if (trigger.getTagName().equals("Elevator-Button")) {
callElevator = true;
interactionUI.setText("Press SPACE to call the elevator");
}
}
@Override
public void onTriggerExit(EnterableTrigger trigger) {
if (trigger.getTagName().equals("Elevator-Platform")) {
onPlatform = false;
}
if (trigger.getTagName().equals("Elevator-Button")) {
callElevator = false;
interactionUI.setText("");
}
}
}
Let me know if you liked the demo, write your suggestions and ideas.
Greetings to all, I hope to see your projects soon.