I can see everybody posting here their improvements to the BetterCharacterControl so I would like to share my tweaks too. I solved (at least is working for me ) two issues with this control:
- Stucking over stairs and any kind of slopes.
- Stucking when it goes over a wall (just changing the character friction when isn’t in ground).
Walking over stairs:
protected void checkSlope() {
TempVars vars = TempVars.get();
Vector3f rayFrom = vars.vect1;
Vector3f rayTo = vars.vect2;
Vector3f feetLoc = vars.vect3;
Vector3f aux = vars.vect4;
feetLoc.set(location);
rayFrom.set(feetLoc).addLocal(aux.set(localUp).multLocal(slopeDepth));//.addLocal(localUp.mult(0.05f));
rayTo.set(rayFrom).addLocal(aux.set(getWalkDirection()).normalizeLocal().multLocal(getFinalRadius())).addLocal(aux.set(localUp).mult(maxAcclivity));
List<PhysicsRayTestResult> results = space.rayTest(rayFrom, rayTo);
for (PhysicsRayTestResult physicsRayTestResult : results) {
if (!physicsRayTestResult.getCollisionObject().equals(rigidBody)) {
rayTo.set(rayFrom.addLocal(rayTo.subtractLocal(rayFrom).multLocal(physicsRayTestResult.getHitFraction())));
rayTo.addLocal(aux.set(getWalkDirection()).normalizeLocal().multLocal(slopeDepth));
results = space.rayTest(rayFrom.set(rayTo).addLocal(aux.set(localUp).multLocal(maxStepHeight)), rayTo);
if(!results.isEmpty()) {
rayFrom.addLocal(rayTo.subtractLocal(rayFrom).multLocal(results.get(0).getHitFraction()));
warp(location.setY(rayFrom.getY()));
}
vars.release();
return;
}
}
vars.release();
}
The character can only go over a slope if there is no obstacle just over that slope so a “checkSomethingInFront” must be done:
protected void checkSomethingInFront() {
TempVars vars = TempVars.get();
Vector3f rayFrom = vars.vect1;
Vector3f rayTo = vars.vect2;
Vector3f aux = vars.vect3;
Vector3f aux2 = vars.vect4;
aux.set(location).addLocal(aux2.set(localUp).multLocal(getFinalHeight()));
rayFrom.set(aux);
rayTo.set(aux.addLocal(rayTo.set(walkDirection).normalizeLocal().multLocal(getFinalRadius() + 0.05f)));
List<PhysicsRayTestResult> results = space.rayTest(rayFrom, rayTo);
for (PhysicsRayTestResult physicsRayTestResult : results) {
if (!physicsRayTestResult.getCollisionObject().equals(rigidBody)) {
somethingInFront = true;
vars.release();
return;
}
}
rayFrom.set(aux);
rayTo.set(rayFrom).subtractLocal(aux.set(localUp).multLocal(getFinalHeight() - maxStepHeight));
results = space.rayTest(rayFrom, rayTo);
vars.release();
for (PhysicsRayTestResult physicsRayTestResult : results) {
if (!physicsRayTestResult.getCollisionObject().equals(rigidBody)) {
somethingInFront = true;
return;
}
}
somethingInFront = false;
}
To use it just:
@Override
public void prePhysicsTick(PhysicsSpace space, float tpf) {
super.prePhysicsTick(space, tpf);
checkSomethingInFront(); // Checks if something is in front of the character
if(!somethingInFront) checkSlope(); // If not, checks if there is a slope and goes up it
// This is a "botched fast fix" for the character getting stuck on a wall
if(onGround) { if(rigidBody.getFriction() != friction) rigidBody.setFriction(friction); }
else if(rigidBody.getFriction() != 0) rigidBody.setFriction(0f);
}
The initial values of the variables used on the checks are:
float maxStepHeight = 0.5f;
float maxAcclivity = 0.13f;
float slopeDepth = 0.05f;
The acclivity is used to avoid unnecessary relocations on ramps (kind of false slope positives).