I am getting strange results when using the GhostControls for testing collisions. It seems the accuracy of the control is off a bit. I have tested this using the different Collisions shapes (Box, Sphere, Capsule) and I get the same result:
- While Stationary - the Controls that are physically close (say within 1-2 units, but not touching) are returning a "Overlapping objects" results
- While Moving - the Controls will return random "Overlapping objects" results at greater distances
Now I am more than likely trying to use the control improperly, but to make sure I altered the physics TestWalkingChar example to show how I am trying to use the controls.
[java]
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.GhostControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
import java.util.ArrayList;
import java.util.List;
public class TestWalkingCollision extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener
{
private BulletAppState bulletAppState;
//character
static CharacterControl character;
Node model;
//temp vectors
Vector3f walkDirection = new Vector3f();
//terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
//Materials
Material matRock;
Material matBullet;
//animation
AnimChannel animationChannel;
AnimControl animationControl;
float airTime = 0;
//camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;
//control for ghost testing
private GhostControl ghostControl;
public static void main(String[] args)
{
TestWalkingCollision app = new TestWalkingCollision();
app.start();
}
@Override
public void simpleInitApp()
{
bulletAppState = new BulletAppState();
// bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
// bulletAppState.getPhysicsSpace().setAccuracy(1f / 60f);
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
setupKeys();
createLight();
createSky();
createTerrain();
createCharacter();
createDummyCharacter();
setupChaseCamera();
setupAnimationController();
}
private PhysicsSpace getPhysicsSpace()
{
return bulletAppState.getPhysicsSpace();
}
private void setupKeys()
{
inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
inputManager.addListener(this, "CharLeft");
inputManager.addListener(this, "CharRight");
inputManager.addListener(this, "CharUp");
inputManager.addListener(this, "CharDown");
}
private void createLight()
{
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky()
{
rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
}
private void createTerrain()
{
matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);
AbstractHeightMap heightmap = null;
try
{
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
heightmap.load();
}
catch (Exception e)
{
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createCharacter()
{
ghostControl = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f, 1);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
model.addControl(character);
ghostControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
ghostControl.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);
model.addControl(ghostControl);
character.setPhysicsLocation(new Vector3f(-140, 15, -10));
ghostControl.setPhysicsLocation(new Vector3f(-140, 15, -10));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
getPhysicsSpace().add(ghostControl);
}
private void createDummyCharacter()
{
GhostControl ghostControl1 = new GhostControl(new CapsuleCollisionShape(3f, 4f, 1));
CapsuleCollisionShape tcapsule = new CapsuleCollisionShape(3f, 4f, 1);
CharacterControl tcharacter = new CharacterControl(tcapsule, 1000f);
Node tmodel = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
ghostControl1.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
ghostControl1.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_02);
tmodel.addControl(ghostControl1);
tmodel.addControl(tcharacter);
tcharacter.setPhysicsLocation(new Vector3f(-160, 15, -10));
ghostControl1.setPhysicsLocation(new Vector3f(-160, 15, -10));
rootNode.attachChild(tmodel);
getPhysicsSpace().add(tcharacter);
getPhysicsSpace().add(ghostControl1);
}
private void setupChaseCamera()
{
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}
private void setupAnimationController()
{
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
}
@Override
public void simpleUpdate(float tpf)
{
Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);
Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
camDir.y = 0;
camLeft.y = 0;
walkDirection.set(0, 0, 0);
if (left)
{
walkDirection.addLocal(camLeft);
}
if (right)
{
walkDirection.addLocal(camLeft.negate());
}
if (up)
{
walkDirection.addLocal(camDir);
}
if (down)
{
walkDirection.addLocal(camDir.negate());
}
if (!character.onGround())
{
airTime = airTime + tpf;
}
else
{
airTime = 0;
}
if (walkDirection.length() == 0)
{
if (!"stand".equals(animationChannel.getAnimationName()))
{
animationChannel.setAnim("stand", 1f);
}
}
else
{
character.setViewDirection(walkDirection);
if (airTime > .3f)
{
if (!"stand".equals(animationChannel.getAnimationName()))
{
animationChannel.setAnim("stand");
}
}
else if (!"Walk".equals(animationChannel.getAnimationName()))
{
animationChannel.setAnim("Walk", 0.7f);
}
}
if (!ghostControl.getOverlappingObjects().isEmpty())
{
//System.out.println("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());
fpsText.setText("Overlapping objects: " + ghostControl.getOverlappingObjects().toString());
}
character.setWalkDirection(walkDirection);
}
public void onAction(String binding, boolean value, float tpf)
{
if (binding.equals("CharLeft"))
{
if (value)
{
left = true;
}
else
{
left = false;
}
}
else if (binding.equals("CharRight"))
{
if (value)
{
right = true;
}
else
{
right = false;
}
}
else if (binding.equals("CharUp"))
{
if (value)
{
up = true;
}
else
{
up = false;
}
}
else if (binding.equals("CharDown"))
{
if (value)
{
down = true;
}
else
{
down = false;
}
}
else if (binding.equals("CharSpace"))
{
character.jump();
}
}
public void collision(PhysicsCollisionEvent event)
{
if (event.getObjectA() instanceof GhostControl)
{
System.out.println("character collision");
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)
{
}
public void onAnimChange(AnimControl control, AnimChannel channel, String animName)
{
}
}
[/java]
Thanks for any help/input.