Are there any functions that can be improved? what features would you like to add?
Write here your ideas and your code to expand the project and make it useful and interesting for all developers.
Archer-Game-Template is very cool! Thank you for sharing your work.
Here are my questions/ideas:
I’m struggling with the keyboard controls. Are they documented anywhere?
The bow and the arrows should be made visible.
I’d like it if releasing the R key (at full draw) fired an arrow. Having to press a separate button feels awkward.
It seems like arrows in flight aren’t affected by gravity. If so, they should be.
It would be more exciting if there were something to shoot at besides sliding crates. Perhaps something destructible and/or something that moves independently and/or something that fights back.
It would be nice if they game kept score and kept track of elapsed time.
For physics, you should use Minie in place of jme3-bullet. At this point, the changes required to switch would be about 7 lines of code.
Thank you so much @sgold for your feedback. Let’s analyze the list by points:
I have updated the README.md file with the explanation of the keyboard controls.
I already have a test case, I’m simplifying the code to make it simple and reusable with other weapons.
I reconfigured the keyboard controls to aim (E key) and change arrow type (R key) by placing them closer together.
I hope this new configuration is less stressful on the wrist and easier to use.
The aim and fire buttons are separate like in most third-person video games. They are very cool when used with the L1 and R1 buttons on the joystick. This is just my opinion, the controls are still easy to change (Archer-Game-Template/GInputAppState.java at main · capdevon/Archer-Game-Template · GitHub).
You are absolutely right, the arrow is not affected by gravity. For simplicity I opted for an instant shot using a physics raycast to identify the affected surface (see line Archer-Game-Template/Physics.java at 1beec43d0cd3537e45b4c9619c77c52b46531ace · capdevon/Archer-Game-Template · GitHub). It is no coincidence that the origin of the ray is above the character’s head to avoid it being on the trajectory, and is oriented with the direction of the camera. I’d like to implement your idea, but haven’t been able to make a shoulder camera prototype yet.
I could use this example (jmonkeyengine/BombControl.java at master · jMonkeyEngine/jmonkeyengine · GitHub)
to handle bullet physics and object impacts, still don’t have an artistic idea of how to harmoniously integrate this mechanic with third-person camera and weapons.
Any suggestions with images or example code would help me a lot.
I agree with you, it would be cooler, I already have protoypes with zombies too. If you have specific references in mind send me the links.
like above
I’d like to use your new library with all the new features, it’s been on my list for a while now. If you send me some PR on github on how to modify the build.gradle file and the 7 lines of code I am happy to accept them.
I’d like to turn this template into a free sample project that contains most of the basic mechanics used in third-person video games, and at the same time shows how to use all the jMonkeyEngine tools like Unity did with FPS Microgame - Unity Learn
Thanks for you thoughtful responses to my feedback.
#4: A single raycast is fine if your weapon is a laser, but it seems strange given that the avatar’s body motions are clearly those of an archer. Once the arrow is released, its path should not depend on camera position. For the aim indicator, I suggest using a series of raycasts to estimate the impact point. I’ll send you some code soon. #7: I’ll submit a PR for switching to MInie ASAP!
One other concern: I hear that you want this to become a template for creating FPS games. I’m not a serious gamer, but to me, “first-person” implies that the player’s avatar is not visible, except possibly for hands/weapons. In this game, the controls are FPS-like, but the camera looks over the avatar’s shoulder from a distance. So is this actually an FPS?
Of course it is not an FPS. What I would like to do is a Unity-like tutorial but in third person. At the moment I’m trying to improve the movement and aiming mechanics. I already have some cool prototypes for melee attacks too. I will be a little busy at work in the next few days, but I will publish all the material I have collected over time creating some interesting discussions. I await your feedback!
thank you, I’m getting the gist of this, cool, but with a working example everything would become clear immediately. Would you like to add this functionality to the project? It could become a nice study project for community programmers.
I downloaded and compiled the MinieExamples project. The idea is very interesting, I calmly study the solution. If in doubt I will let you know. Thanks
I’m improving the archer demo by adding physical arrows. At the moment they are just spheres, but I will replace them with the model of an arrow in the final version.
To test the collisions I built a scenario that includes:
A city scene with static RigidBodyControl and mass 0
Simple cubes with mass greater than 0
Sinbad to which I added the ‘DynamicAnimControl’ controller to identify the part of the body hit by the arrow.
The goal I would like to achieve is to attach the arrow to the hit surface or body parts of animated characters.
The algorithm I wrote seems to work correctly for cubes, while with Sinbad the arrows (balls) sometimes correctly follow the position of the moving bone, other times they don’t.
(see method ArrowControl.stick(PhysicsCollisionObject other, Vector3f hitPoint) )
Could you give me some suggestions on how to improve the solution?
Here is an example video.
Here is the code.
public class SinbadRagdollPrefab extends PrefabComponent {
public SinbadRagdollPrefab(Application app) {
super(app);
}
@Override
public Spatial getAssetModel() {
return app.getAssetManager().loadModel("Models/Sinbad/Sinbad.mesh.xml");
}
@Override
public Spatial instantiate(Vector3f position, Quaternion rotation, Node parent) {
Spatial model = getAssetModel();
model.setLocalTranslation(position);
model.setLocalRotation(rotation);
parent.attachChild(model);
AnimComposer composer = model.getControl(AnimComposer.class);
composer.setCurrentAction("Dance");
DynamicAnimControl ragdoll = new DynamicAnimControl();
setupSinbad(ragdoll);
model.addControl(ragdoll);
PhysicsSpace.getPhysicsSpace().add(ragdoll);
return model;
}
private void setupSinbad(DynamicAnimControl ragdoll) {
ragdoll.link("Waist", 1f, new RangeOfMotion(1f, -0.4f, 0.8f, -0.8f, 0.4f, -0.4f));
ragdoll.link("Chest", 1f, new RangeOfMotion(0.4f, 0f, 0.4f));
ragdoll.link("Neck", 1f, new RangeOfMotion(0.5f, 1f, 0.7f));
ragdoll.link("Clavicle.R", 1f, new RangeOfMotion(0.3f, -0.6f, 0f, 0f, 0.4f, -0.4f));
ragdoll.link("Humerus.R", 1f, new RangeOfMotion(1.6f, -0.8f, 1f, -1f, 1.6f, -1f));
ragdoll.link("Ulna.R", 1f, new RangeOfMotion(0f, 0f, 1f, -1f, 0f, -2f));
ragdoll.link("Hand.R", 1f, new RangeOfMotion(0.8f, 0f, 0.2f));
ragdoll.link("Clavicle.L", 1f, new RangeOfMotion(0.6f, -0.3f, 0f, 0f, 0.4f, -0.4f));
ragdoll.link("Humerus.L", 1f, new RangeOfMotion(0.8f, -1.6f, 1f, -1f, 1f, -1.6f));
ragdoll.link("Ulna.L", 1f, new RangeOfMotion(0f, 0f, 1f, -1f, 2f, 0f));
ragdoll.link("Hand.L", 1f, new RangeOfMotion(0.8f, 0f, 0.2f));
ragdoll.link("Thigh.R", 1f, new RangeOfMotion(0.4f, -1f, 0.4f, -0.4f, 1f, -0.5f));
ragdoll.link("Calf.R", 1f, new RangeOfMotion(2f, 0f, 0f, 0f, 0f, 0f));
ragdoll.link("Foot.R", 1f, new RangeOfMotion(0.3f, 0.5f, 0f));
ragdoll.link("Thigh.L", 1f, new RangeOfMotion(0.4f, -1f, 0.4f, -0.4f, 0.5f, -1f));
ragdoll.link("Calf.L", 1f, new RangeOfMotion(2f, 0f, 0f, 0f, 0f, 0f));
ragdoll.link("Foot.L", 1f, new RangeOfMotion(0.3f, 0.5f, 0f));
}
}
public class ArrowPrefab extends RangedBullet {
public ArrowPrefab(Application app, String name) {
super(app);
mass = 6f;
this.name = name;
}
@Override
public Spatial getAssetModel() {
// TODO Auto-generated method stub
Mesh mesh = new Sphere(16, 16, 0.05f);
Geometry geo = new Geometry("Arrow", mesh);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Green.clone());
geo.setMaterial(mat);
return geo;
}
@Override
public Spatial instantiate(Vector3f position, Quaternion rotation, Node parent) {
Spatial model = getAssetModel();
model.setName(name + "-" + nextSeqId());
model.setLocalTranslation(position);
model.setLocalRotation(rotation);
parent.attachChild(model);
// Add Physics.
RigidBodyControl rgb = new RigidBodyControl(mass);
model.addControl(rgb);
PhysicsSpace.getPhysicsSpace().add(rgb);
rgb.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
rgb.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_01);
rgb.setCcdMotionThreshold(0.001f);
ArrowControl arrow = new ArrowControl();
model.addControl(arrow);
return model;
}
}
public class ArrowControl extends AdapterControl implements PhysicsCollisionListener {
private static final Logger logger = Logger.getLogger(ArrowControl.class.getName());
private RigidBodyControl rigidBody;
private PhysicsSpace m_PhysicsSpace;
private boolean hasCollided;
private float maxFlyingTime = 10f;
private float timer = 0;
@Override
public void setSpatial(Spatial sp) {
super.setSpatial(sp);
if (spatial != null) {
rigidBody = getComponent(RigidBodyControl.class);
m_PhysicsSpace = rigidBody.getPhysicsSpace();
m_PhysicsSpace.addCollisionListener(this);
}
}
@Override
protected void controlUpdate(float tpf) {
// TODO Auto-generated method stub
timer += tpf;
if (!hasCollided) {
// this is to cleanup old bullets that hasn't collided yet (lived more than maxTime)
if (timer > maxFlyingTime) {
destroy();
}
}
}
@Override
public void collision(PhysicsCollisionEvent event) {
if (hasCollided) {
return;
}
if ( event.getObjectA() == rigidBody || event.getObjectB() == rigidBody ) {
hasCollided = true;
// Stop the rigidBody in position
rigidBody.setEnabled(false);
PhysicsCollisionObject other;
Vector3f hitPoint;
if (event.getObjectA() == rigidBody) {
other = event.getObjectB();
hitPoint = event.getPositionWorldOnB();
} else {
other = event.getObjectA();
hitPoint = event.getPositionWorldOnA();
}
logger.log(Level.INFO, "Collided with: {0}", other.getUserObject().toString());
stick(other, hitPoint);
spatial.addControl(new TimerControl(15f) {
@Override
public void onTrigger() {
destroy();
}
});
}
}
/**
*
* @param other
* @param hitPoint
*/
private void stick(PhysicsCollisionObject other, Vector3f hitPoint) {
if (other.getUserObject() instanceof Node) {
Node gameObject = (Node) other.getUserObject();
gameObject.worldToLocal(hitPoint, hitPoint);
gameObject.attachChild(spatial);
spatial.setLocalTranslation(hitPoint);
} else if (other.getUserObject() instanceof BoneLink) {
BoneLink bone = (BoneLink) other.getUserObject();
Spatial animRoot = bone.getControl().getSpatial();
Node attachNode = animRoot.getControl(SkinningControl.class).getAttachmentsNode(bone.boneName());
System.out.println(bone.boneName() + " " + animRoot + "; " + attachNode);
attachNode.worldToLocal(hitPoint, hitPoint);
attachNode.attachChild(spatial);
spatial.setLocalTranslation(hitPoint);
} else {
logger.log(Level.WARNING, "Unable to attach the arrow to the hit object: " + other.getUserObject());
}
}
// Destroy everything
private void destroy() {
m_PhysicsSpace.removeCollisionListener(this);
spatial.removeFromParent();
logger.log(Level.INFO, "Object Destroyed: {0}", spatial);
}
}
Let me know if you need more information on the code I wrote.
I’ll post the demo on github with the final code.
@capdevon I thought it can be done easily through removal of the arrows from physical space onCollision & donot remove the arrows Spatials from the rootNode after collision , am I right ?
EDIT :
This is a great part , I tried sticking objs before but on non-movable surfaces , so this was the trick for movable or animated objects good part
To connect 2 physics bodies (such as an arrow and a target), the natural solution is to add a PhysicsJoint such as New6Dof. For bodies that penetrate other bodies, you can disable collisions using setCollisionBetweenLinkedBodies(false).
Sticking based on spatials doesn’t seem like a good plan. Spatials are for rendering, not for representing game objects.
Putting rigid bodies in different collision groups doesn’t seem like a good plan, because it prevents arrows from colliding with other arrows.
thank you @Pavl_G, I always publish my code to give everyone the opportunity to learn something, even from my mistakes. It seems to work even without the worldToLocal statement.
Thanks @sgold for your patience. Could you please tell me how to use a PhysicsJoint / New6Dof in the stick method? It would help me understand better.
private void stick(PhysicsCollisionObject other, Vector3f hitPoint) {
if (other.getUserObject() instanceof Node) {
Node gameObject = (Node) other.getUserObject();
gameObject.attachChild(spatial);
spatial.setLocalTranslation(hitPoint);
} else if (other.getUserObject() instanceof BoneLink) {
BoneLink bone = (BoneLink) other.getUserObject();
Spatial animRoot = bone.getControl().getSpatial();
Node attachNode = animRoot.getControl(SkinningControl.class).getAttachmentsNode(bone.boneName());
System.out.println(bone.boneName() + " " + animRoot + "; " + attachNode);
attachNode.attachChild(spatial);
spatial.setLocalTranslation(hitPoint);
} else {
logger.log(Level.WARNING, "Unable to attach the arrow to the hit object: " + other.getUserObject());
}
}
I didn’t quite understand what you mean.
I have deliberately chosen to use a separate group for the arrows because in this scenario I don’t need collisions with other arrows.
To create a PhysicsJoint, you’ll need access to both rigid bodies. I don’t know how to rewrite the stick() method because it apparently has access to only one of them.
Suppose rigid body of the arrow is mainBody, the rigid body of the target is closestBody, and the point of impact is at pivotLocation in physics-space coordinates. Then the essential code looks like this:
/*
* Create a constraint, its pivot located at the point of impact
* and its axes aligned with those of the arrow...
*/
// transform the POI to the arrow's local coordinates
Matrix3f closestMatrix = closestBody.getPhysicsRotationMatrix(null);
closestMatrix.invertLocal();
Vector3f closestOffset = closestBody.getPhysicsLocation(null);
closestOffset.negateLocal();
closestOffset.addLocal(pivotLocation);
closestMatrix.mult(closestOffset, closestOffset);
// locate the POI in the target's local coordinates
Matrix3f mainMatrix = mainBody.getPhysicsRotationMatrix(null);
closestMatrix.multLocal(mainMatrix);
float penetrationFraction = 0.35f; // for 35% penetration
Vector3f mainOffset = tipOffset.mult(1f - 2f * penetrationFraction);
New6Dof constraint = new New6Dof(mainBody, closestBody,
mainOffset, closestOffset, Matrix3f.IDENTITY, closestMatrix, RotationOrder.XYZ);
// disable all 3 rotational axes
for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
RotationMotor rMotor = constraint.getRotationMotor(axisIndex);
rMotor.setSpringEnabled(true); // necessary, but not sure why!
int dofIndex = axisIndex + 3;
constraint.set(MotorParam.UpperLimit, dofIndex, 0f);
constraint.set(MotorParam.LowerLimit, dofIndex, 0f);
}
constraint.setCollisionBetweenLinkedBodies(false);
physicsSpace.addJoint(constraint);
Game objects describe the state of the game. Spatials are objects JMonkeyEngine uses to organize a 3-D scene for rendering purposes.
In very simple games, one can get away with representing each game object as a Spatial and storing game state in userdata or scene-graph controls. But for games of moderate to high complexity, it’s prudent to create data structures outside the scene graph to keep track of game state.
To clarify what’s a game object, imagine writing a networked game where 3-D rendering and user interface are handled entirely by the client, while interactions between players and their environment are simulated entirely by the server. In this scenario, game objects are data maintained by the server. Since the server doesn’t do any 3-D rendering, there would be no scene graph on the server and hence no spatials.
For another example, consider a simple chess game. The game state for chess basically consists of (1) whose turn it is and (2) the locations of the 32 pieces (65 possible locations for each). [In practice a bit more is needed for castling, pawn promotion, and stalemate detection.] The point is, it would be silly to embed this information in a scene graph.
A third way to think about game objects is in terms of save/restore. Game objects represent information written to stable storage at each save point. Sky textures, no. Inventory, yes.
To speed your project along, here are some tricks I developed for simulating arrow dynamics:
Before each physics tick, apply torque to turn the long axis of the arrow toward its direction of travel. This simulates an effect of fletching and helps the motion look realistic. It also increases the likelihood that the point of impact will be the arrow’s head (not its shaft or fletchings or nock). So software can focus on the motion of the head, paying less attention to the rest.
Before each physics tick, project the motion of the arrowhead during the upcoming timestep. (Use a sweep test with a small sphere representing the arrowhead.) This allows software to anticipate penetrations before any physics collision occurs. That’s when it’s easiest to simulate penetration.
If an arrowhead is predicted to collide with a penetrable object, reduce the arrow’s speed to keep it from colliding during the current timestep. An opportunity to transfer some of its momentum to the target.
Bullet’s continuous collision detection (CCD) doesn’t work well for long, skinny objects. If an arrowhead is predicted to collide with an impenetrable object, reflect the arrow’s velocity against the object’s surface and diminish the speed by 50% or so. This gives a convincing simulation of a ricochet without relying on Bullet’s CCD, which would need a large swept sphere.
I’d like to learn, but managing the physical components isn’t easy. An example code would help me and everyone who reads the topic to understand something. I have tried to include your suggestions in ArrowControl, but I don’t think I really understand what to do. Here is my latest attempt:
public class ArrowControl extends AbstractControl implements PhysicsCollisionListener, PhysicsTickListener {
private static final Logger logger = Logger.getLogger(ArrowControl.class.getName());
// the rigid body of the arrow.
private RigidBodyControl rigidBody;
private PhysicsSpace m_PhysicsSpace;
private boolean hasCollided;
private float maxFlyingTime = 10f;
private float timer = 0;
@Override
public void setSpatial(Spatial sp) {
super.setSpatial(sp);
if (spatial != null) {
rigidBody = spatial.getControl(RigidBodyControl.class);
m_PhysicsSpace = rigidBody.getPhysicsSpace();
m_PhysicsSpace.addCollisionListener(this);
m_PhysicsSpace.addTickListener(this);
}
}
@Override
protected void controlUpdate(float tpf) {
// TODO Auto-generated method stub
timer += tpf;
if (!hasCollided) {
// this is to cleanup old bullets that hasn't collided yet (lived more than maxTime)
if (timer > maxFlyingTime) {
destroy();
}
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
//To change body of generated methods, choose Tools | Templates.
}
@Override
public void collision(PhysicsCollisionEvent event) {
if (hasCollided) {
return;
}
if ( event.getObjectA() == rigidBody || event.getObjectB() == rigidBody ) {
hasCollided = true;
// Stop the rigidBody in position
// rigidBody.setEnabled(false);
PhysicsCollisionObject other;
Vector3f hitPoint;
if (event.getObjectA() == rigidBody) {
other = event.getObjectB();
hitPoint = event.getPositionWorldOnB();
} else {
other = event.getObjectA();
hitPoint = event.getPositionWorldOnA();
}
logger.log(Level.INFO, "Collided with: {0}", other.getUserObject().toString());
stick(other, hitPoint);
}
}
/**
*
* @param closestBody
* @param pivotLocation
*/
private void stick(PhysicsCollisionObject closestBody, Vector3f pivotLocation) {
if (closestBody.getUserObject() instanceof Node) {
// transform the POI to the arrow's local coordinates
Matrix3f closestMatrix = closestBody.getPhysicsRotationMatrix(null);
closestMatrix.invertLocal();
Vector3f closestOffset = closestBody.getPhysicsLocation(null);
closestOffset.negateLocal();
closestOffset.addLocal(pivotLocation);
closestMatrix.mult(closestOffset, closestOffset);
// locate the POI in the target's local coordinates
Matrix3f mainMatrix = rigidBody.getPhysicsRotationMatrix(null);
closestMatrix.multLocal(mainMatrix);
float penetrationFraction = 0.35f; // for 35% penetration
Vector3f mainOffset = ??? // tipOffset.mult(1f - 2f * penetrationFraction); //tipOffset is not defined in the snippet code
New6Dof constraint = new New6Dof(rigidBody, (PhysicsRigidBody) closestBody,
mainOffset, closestOffset, Matrix3f.IDENTITY, closestMatrix, RotationOrder.XYZ);
// disable all 3 rotational axes
for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
RotationMotor rMotor = constraint.getRotationMotor(axisIndex);
rMotor.setSpringEnabled(true); // necessary, but not sure why!
int dofIndex = axisIndex + 3;
constraint.set(MotorParam.UpperLimit, dofIndex, 0f);
constraint.set(MotorParam.LowerLimit, dofIndex, 0f);
}
constraint.setCollisionBetweenLinkedBodies(false);
m_PhysicsSpace.addJoint(constraint);
// Node gameObject = (Node) other.getUserObject();
// gameObject.worldToLocal(hitPoint, hitPoint);
// gameObject.attachChild(spatial);
// spatial.setLocalTranslation(hitPoint);
// } else if (other.getUserObject() instanceof BoneLink) {
// BoneLink link = (BoneLink) other.getUserObject();
// Spatial animRoot = link.getControl().getSpatial();
// Node attachNode = animRoot.getControl(SkinningControl.class).getAttachmentsNode(link.boneName());
// System.out.println(link.boneName() + " " + animRoot + "; " + attachNode);
//
// attachNode.worldToLocal(hitPoint, hitPoint);
// attachNode.attachChild(spatial);
// spatial.setLocalTranslation(hitPoint);
//
} else {
logger.log(Level.WARNING, "Unable to attach the arrow to the hit object: " + closestBody.getUserObject());
}
}
// Destroy everything
private void destroy() {
rigidBody.setEnabled(false);
m_PhysicsSpace.removeCollisionListener(this);
m_PhysicsSpace.removeTickListener(this);
spatial.removeFromParent();
logger.log(Level.INFO, "Physics Object Destroyed: {0}", spatial);
}
@Override
public void prePhysicsTick(PhysicsSpace space, float timeStep) {
// TODO Auto-generated method stub
if (hasCollided) {
m_PhysicsSpace.removeCollisionListener(this);
return;
}
Transform start = new Transform(rigidBody.getPhysicsLocation());
Transform end = new Transform(rigidBody.getPhysicsLocation().add(rigidBody.getLinearVelocity().multLocal(timeStep)));
List<PhysicsSweepTestResult> sweepTest = m_PhysicsSpace.sweepTest((ConvexShape) rigidBody.getCollisionShape(), start, end);
boolean collision = false;
for (PhysicsSweepTestResult result : sweepTest) {
PhysicsCollisionObject pco = result.getCollisionObject();
if (pco.getCollisionShape() != rigidBody.getCollisionShape()) {
System.out.println("Colliding with: " + pco.getUserObject().toString());
collision = true;
}
}
if (collision) {
rigidBody.setLinearVelocity(rigidBody.getLinearVelocity().multLocal(0.9f));
}
}
@Override
public void physicsTick(PhysicsSpace space, float timeStep) {
// TODO Auto-generated method stub
if (hasCollided) {
m_PhysicsSpace.removeTickListener(this);
}
}
}
I can guess with another thousand unsuccessful attempts, but if you have a ready-made example you would help the whole community of newbies like me on this topic to understand how physics update loops work, how to use sweepTest and New6Dof correctly. Thanks as always for your time
I agree it would be nice to have a complete, open-source example of arrow dynamics. That’s one of the many reasons I’m excited about your project!
Currently I’m focused on bringing More Advanced Vehicles to a release. When that’s accomplished, I have a slew of JME issues and PRs to catch up on. I’m unsure when I’ll return to the Minie Project or my tactical simulation.
While the tactical sim was never intended to be open-source, I expect to eventually extract open-source library code and examples from it. Until then, all I can provide are snippets and general advice. If you’re stuck on how to implement something, by all means ask. But don’t expect snippets pasted from another (differently organized) project to do exactly what you want.
Some excel at debugging code by visual inspection—not me! I hate reading code in a webpage, out of context. I want to browse it an an editor, execute it, and step through it in a debugger. If you want debugging help, please describe the issue, then point me to a Git hash I can clone, build, and run myself.
Regarding how physics update loops work, it seems you already know about PhysicsTickListener. I’m unsure what more there is to know!
Regarding how to use sweep tests, there’s an example in TestRbc.
Sorry for my last post, maybe I was a little tired. Programming video games is difficult, sometimes there are moments of frustration, but if you share your passions with someone, you will never lose enthusiasm. I know you are working on a lot of JME improvements so I try not to waste your time with silly questions. I appreciate all your feedbacks and thanks for the reference material, I try to understand more. I will publish the code with all the new evolutions on github after doing some cleaning.
Questions are generally welcome. I assume that for every person asking questions at the Forum, there are 9 shy people lurking to see the answers, if any.