But you should change this line in toInPlaceAnimation
for (int i = 0; i < transformTrack.getLength(); i++) {
to
for (int i = 0; i < transformTrack.getTranslations().length; i++) {
else it will throw
java.lang.AssertionError
at com.jme3.anim.TransformTrack.setKeyframesTranslation(TransformTrack.java:149)
at com.capdevon.demo.Test_Climbing$PlayerControl.toInPlaceAnimation(Test_Climbing.java:352)
at com.capdevon.demo.Test_Climbing$PlayerControl.setSpatial(Test_Climbing.java:310)
at com.jme3.scene.Spatial.addControl(Spatial.java:777)
at com.capdevon.demo.Test_Climbing.setupPlayer(Test_Climbing.java:203)
at com.capdevon.demo.Test_Climbing.simpleInitApp(Test_Climbing.java:104)
at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:240)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:139)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:221)
at java.base/java.lang.Thread.run(Thread.java:835)
transformTrack.getLength() will return the duration (in seconds) of the track.
I see the collision capsule is not moving with the character. For a generic root motion solution that needs to be done as well
Also, I see only translations are taking account for root motion. Root motion also contains rotations. For example a turn left animation. Preferably you would be able to run the root motion animation without applying manual rotations to the spatial.
How can I tell bullet to get the transform from the spatial?
Edit:
If I try to enable Kinematic mode it will throw an assertion error:
java.lang.AssertionError
at com.jme3.bullet.objects.PhysicsRigidBody.getLinearVelocity(PhysicsRigidBody.java:487)
at com.jme3.bullet.control.BetterCharacterControl.physicsTick(BetterCharacterControl.java:665)
at com.jme3.bullet.PhysicsSpace.postTick_native(PhysicsSpace.java:1197)
at com.jme3.bullet.PhysicsSpace.stepSimulation(Native Method)
at com.jme3.bullet.PhysicsSpace.update(PhysicsSpace.java:825)
at com.jme3.bullet.BulletAppState.render(BulletAppState.java:802)
at com.jme3.app.state.AppStateManager.render(AppStateManager.java:388)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:270)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:160)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:201)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:242)
at java.base/java.lang.Thread.run(Thread.java:835)
Strangely it works in your example, I think it is because physics is overwriting the location. If you disable BCC when the climb starts you will see character will fly to the air
Edit:
Thatâs why in RootMotion class I get the startLoc at the beginning and keep adding to it instead.
Yes I had noticed it too, but I did not understand why. How do we solve it? Meanwhile, I added the extraction of rotations from the animation to apply them to the character node.
private void toInPlaceAnimation(AnimClip clip, int jointId) {
AnimTrack[] tracks = clip.getTracks();
for (AnimTrack track : tracks) {
if (track instanceof TransformTrack) {
TransformTrack transformTrack = (TransformTrack) track;
HasLocalTransform target = transformTrack.getTarget();
if (target instanceof Joint) {
Joint joint = (Joint) target;
if (jointId == joint.getId()) {
cleanTranslation(transformTrack);
cleanRotation(transformTrack);
}
}
}
}
}
/**
* Convert it to an in-place animation by removing translations data
* @param transformTrack
*/
private void cleanTranslation(TransformTrack transformTrack) {
CompactVector3Array translation = new CompactVector3Array();
for (int i = 0; i < transformTrack.getTranslations().length; i++) {
translation.add(Vector3f.ZERO.clone());
}
transformTrack.setKeyframesTranslation(translation.toObjectArray());
}
/**
* Convert it to an in-place animation by removing rotations data
* @param transformTrack
*/
private void cleanRotation(TransformTrack transformTrack) {
CompactQuaternionArray rotation = new CompactQuaternionArray();
for (int i = 0; i < transformTrack.getRotations().length; i++) {
rotation.add(Quaternion.IDENTITY.clone());
}
transformTrack.setKeyframesRotation(rotation.toObjectArray());
}
I reapply the rotation taking into account the orientation of the character.
@Override
protected void controlUpdate(float tpf) {
if (!isClimbingMode) {
// Player is in NORMAL state
updateMovement(tpf);
} else {
// Player is in CLIMBING state
if (startClimb && !isClimbingAnimDone) {
// align with wall
//spatial.getWorldRotation().slerp(helper.getRotation(), tpf * 5);
hipsTrack.getDataAtTime(animComposer.getTime(), rootMotion);
// Vector3f vec = animComposer.getSpatial().localToWorld(rootMotion.getTranslation(), null);
// rootBoneRef.setLocalTranslation(vec);
// rootBoneRef.setLocalRotation(rootMotion.getRotation());
spatial.setLocalTranslation(rootMotion.getTranslation()); // ??? doesn't work
spatial.getLocalRotation().multLocal(rootMotion.getRotation());
} else if (isClimbingAnimDone) {
isClimbingMode = false;
startClimb = false;
//spatial.setLocalTranslation(goalPosition);
// bcc.getRigidBody().setKinematic(false);
// bcc.setEnabled(true);
bcc.warp(goalPosition);
}
}
}
I mean that is how scene-graph is working. If a node moves, everything inside it will move also so while their local translation is the same but their world translation will change.
Physics objects can only be properly moved using physics functions afaik. setPhysicsRotation, setPhysicsLocation, setLinearVelocity, etc.
One caveat, bettercharactercontrol will also update your physics, and will cause trouble probably.
Best to write your own ClimbableEvenBetterCharacterControl
Great job, we can do a little optimization to stabilize the camera. Hooking the ChaseCamera to the model instead of the player node the result is more fluid and natural. However, a slight flickering of the image remains to be eliminated, probably caused by the final teleportation of the bcc with the warp method
// setup third person camera
setupChaseCamera(model);
A question about this though, while I use mixamo to build my own animations I donât or almost never use them in their vanilla state, I have also been actively negating the root motions to create âin placeâ animations for all my stuff because I think itâs safer for me to manage all locomotion in jme3 itself would this still work without root motion, also how are the animations built/managed climb loop > pull up/vaulting separately or is in one animation file.
additional question how would u handle max climb distance and situations that would stop a climb, I am interested in this except that I want to d a vertical wall run thing since I mostly want the hero character to maintain a certain momentum as much as possible
Hi @smith,
the approach we are using is this: extract the Root Motion of the character animation and reapply the same motion to the characterâs collision capsule or in our case to the character node. This article explains really well what we are talking about.
All animations are in-place except the climbing one. I downloaded the animations from the Mixamo site in fbx format and then I included all the tracks in a single gltf file with Blender.
Given the difficulty of the problem, I have simplified some conditions to give an achievable goal and write a first basic solution with you. For this reason I assumed that the height of the obstacles is fixed and determined by the height of the climbing animation. As soon as we have a stable solution, we will try to do further experiments.
To identify which obstacles are scalable and which are not there are a lot of techniques. In this video they use Raycast to identify the surfaces along which the character can move. https://www.youtube.com/watch?v=O91pjnCWOl0
The code, the animations and all the files of this game-jam is at your complete disposal. You can study it, modify it, add your ideas and share them with us if you like.
Hi @ia97lies, weâre still working on it. For sure, if it were integrated into jme3, it would be a very useful feature and would give access to many new possibilities for video game development.
small somewhat unrelated note you can already kinda climb in jme, well more like teleport up slopes based on a tolerance, I set a high step height on CharacterController and a 90 degree max slope,
literally walked âsort ofâ âall overâ Town.zip
character stepHeight of 50+ and
character.setMaxSlope(1.5708f); 90 degrees jmephysics
character.getCharacter().setMaxSlope(1.570796f) minie
l
Notes this is with CC not BCC, something similar might make it possible to not rely on root motion for climbing
Edit: the Teleport seems to only occur on slopes that are 90 degrees did not test for tolerance 80s and such otherwise
maybe a solution can be found somewhere between CC and BCC
Well I am not the best coder, never truly dedicated my myself to the craft, but I have been looking into cobbling together a controller that does everything I need or as much as possible, fact is, other than the crouch, I donât think that BCC is actually âBetterâ than CC, try getting up or over the smallest obstacle with that. I am trying to build rigid body control with some CC features and the useful bit of BCC, will see how that goesâŚjust thought you guys should be aware of thisâŚif you werenât already, I hit upon it while looking at the code to get tips for my ideas