I'm just now picking up jMonkeyEngine and I've encountered an issue I can't seem to solve.
In the simpleInitApp method in the main class, I can use the following code to successfully render a box:
Box playerBase = new Box(Vector3f.ZERO,1f,1f,1f);
Geometry playerBaseGeom = new Geometry("playerBase", playerBase);
Transform fixBaseHeight = new Transform(
new Vector3f(0f,(0.5f * 2f),0f));
playerBaseGeom.setLocalTransform(fixBaseHeight);
Material playerBaseMaterial = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
playerBaseMaterial.setColor("Color", ColorRGBA.Yellow);
playerBaseGeom.setMaterial(playerBaseMaterial);
rootNode.attachChild(playerBaseGeom);
I tried to use a class called Tower to be able to spawn several boxes representing towers (for a simple tower defense game). The tower class looks like this:
public class Tower {
private static final float HEIGHT = 0.5f;
private static final float WIDTH = 0.2f;
private static final float DEPTH = 0.2f;
private Geometry towerGeom;
private Material towerMaterial;
private Box tower;
public Tower(AssetManager assetManager, float x_coord, float z_coord) {
tower = new Box();
towerGeom = new Geometry("tower", tower);
towerMaterial = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
towerMaterial.setColor("Color", ColorRGBA.Green);
towerGeom.setMaterial(towerMaterial);
towerGeom.setLocalTranslation(x_coord, (0.5f * .5f),z_coord);
towerGeom.setLocalScale(WIDTH, HEIGHT, DEPTH);
}
public Geometry getGeometry() {
return towerGeom;
}
}
In the main class, in the simpleInitApp method, I tried to use my new Tower class like this:
List <Tower> towers = new ArrayList<Tower>();
towers.add(new Tower(assetManager, 10f,8f));
for(Tower t:towers) {
rootNode.attachChild(t.getGeometry());
}
However, no cube is rendered. Why? I used the exact same procedure shown in the beginning, which worked.
The Box() constructor is meant for serialization only and doesn't initialize the mesh. The constructor in your upper example is deprecated. Use:
tower = new Box(0.5f, 0.5f, 0.5f);
This will create a cube of the size 1x1x1 centered at [0, 0, 0].
Also, make sure you look at the tower. With the default camera position and the tower at [10, 0, 8], it will be placed behind you.
getCamera().lookAt( new Vector3f(10f, 0, 8f), Vector3f.UNIT_Y );
I recommend consulting the jME source code for this kind of problem so you can be sure whats going on.
Related
In netbeans, there is an option under samples called CubeSystem3D. I was wondering if there was a way to convert all of the cubes to spheres so I can eventually create a solar system type of thing. I can send the actual code if you are unable to retrieve it off netbeans because I can not put the code of this post as it says there will be too much code compared with text.
Use below Ellipse class instead of Cube class in that neabeans sample code.
public class Ellipse extends Group {
final Rotate rx = new Rotate(0, Rotate.X_AXIS);
final Rotate ry = new Rotate(0, Rotate.Y_AXIS);
final Rotate rz = new Rotate(0, Rotate.Z_AXIS);
public Ellipse(double size, Color color, double shade) {
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(color);
material.setSpecularColor(Color.BLACK);
getTransforms().addAll(rz, ry, rx);
Sphere s = new Sphere(size);
s.setMaterial(material);
getChildren().addAll(s);
}
}
You will get something like this,
I'm using LibGDX with Scene2D for my java game. I know my issue is connected to Scene2D, because I used the EXACT same class passing it normally to SpriteBatch (not through Stage instance) and it worked as expected.
I let Stage manage all of my drawables entities which are actors. It draws everything using implementor of Batch; SpriteBatch is the default. And it was working until I wanted to draw a polygon, which has to be drawn by PolygonSpriteBatch, not SpriteBatch.. So during one Stage.draw() call I need to use them both.
I made a CheckedPolygon class which is basically two PolygonSprites drawn on top of each other (one is semi-transparent). SpriteBatch passed in draw() method is temporarily ended to enable PolygonSpriteBatch for a moment, draw the polygon and disable it.
And the output is empty screen, I get nothing. Again, it worked when I wasn't using Stage class.
Here's the class, so you get a better understanding.
NOTE: I know this is bad in terms of performance, because I don't dispose of Texture and keep Batch for one object but it's for the sake of simplicity.
NOTE2: Yes, I passed it properly to stage, the code is executed, I checked through debugging.
public class CheckedPolygon extends Actor {
private final PolygonSprite mBackground, mCross;
private final PolygonSpriteBatch mPolygonSpriteBatch;
private final Camera mCamera;
public CheckedPolygon(Camera camera, float[] vertices, short[] triangles) {
super();
mCamera = camera;
mPolygonSpriteBatch = new PolygonSpriteBatch();
Texture textureBack = new Texture(Gdx.files.internal("source/back.png"));
textureBack.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
PolygonRegion regionBack = new PolygonRegion(new TextureRegion(textureBack), vertices, triangles);
mBackground = new PolygonSprite(regionBack);
Texture textureCross = new Texture(Gdx.files.internal("source/cross.png"));
textureCross.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
PolygonRegion regionCross = new PolygonRegion(new TextureRegion(textureCross), vertices, triangles);
mCross = new PolygonSprite(regionCross);
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.end();
mPolygonSpriteBatch.setProjectionMatrix(mCamera.combined);
mPolygonSpriteBatch.begin();
mBackground.draw(mPolygonSpriteBatch);
mCross.draw(mPolygonSpriteBatch);
mPolygonSpriteBatch.end();
batch.begin();
}
}
And I use it like this:
CheckedPolygon polygon = new CheckedPolygon(mStage.getCamera(), mTextureAtlas, new float[]{0, 500, 0, 0, 0, 500}, new short[]{0, 1, 2});
mStage.addActor(polygon);
I checked values from methods PolygonSprite.getVertices() and PolygonSprite.getBoundingRectangle() and got some weird outputs...
You create an invalid polygon with 0,500 being the first and last point.
So you need to chose valid vertices, for example:
new float[] {0, 0, 0, 500, 500, 500}
I created a small image to visualize it. The line on top is your "polygon", the triangle below is a polygon with the vertices from above:
The vertices you get back from the polygon sprite are otherwise ok, since they contain x, y, color, u, v.
I am using Box2d for a game, and although I use large constants to set angular velocity, the fastest speed I can get is 1 revolution at 3.86 seconds.
I had checked my source code in the following thread and everything is the same with what I have been suggested from both users in here and in tutorials:
setAngularVelocity rotates really slowly
However than I noticed the following unresolved thread:
http://www.reddit.com/r/libgdx/comments/1qr2m3/the_strangest_libgdxbox2d_behaviour/
and noticed that might actually be the problem. Here is my dispose method
public void dispose() {
//Get Rid of Everything!
Assets.Clear();
GameEngine.Clear();
BallMap.clear();
PlayerMap.clear();
shapeRenderer.dispose();
debugRenderer.dispose();
world.dispose();
batch.dispose();
font.dispose();
}
They are all reinitialized on the beginning as follows:
this.game = game;
this.cameraWidth = cameraWidth*pixelRatio;
this.cameraHeight = cameraHeight*pixelRatio;
batch = new SpriteBatch();
shapeRenderer = new ShapeRenderer();
stateTime = 0F;
Scores = new Integer[]{0, 0};
debugRenderer = new Box2DDebugRenderer();
world = new World(new Vector2(0, 0), true); //Create a world with no gravity
GameEngine.setContactListener(world);
I navigate through screens with the following code:
public void create () {
scene_menu = new MainMenuScreen(this, cameraWidth, cameraHeight);
setScreen(scene_menu);
}
public void swtogame(){
scene_menu.dispose();
scene_game = new MatchScreen(this, cameraWidth, cameraHeight);
setScreen(scene_game);
}
public void swtomenu(){
scene_game.dispose();
scene_menu = new MainMenuScreen(this, cameraWidth, cameraHeight);
setScreen(scene_menu);
}
the way i initialize objects:
public Object(World world, short category, short mask, float x, float y, float radius, Sprite image,
float maxSpeed, float frictionStrength, float linearDamping, float angularDamping, boolean movable,
float elasticity, float mass){
this.world = world;
this.category = category;
this.mask = mask;
// We set our body type
this.bodyDef = new BodyDef();
if(movable==true){bodyDef.type = BodyType.DynamicBody;}else{bodyDef.type = BodyType.StaticBody;}
// Set body's starting position in the world
bodyDef.position.set(x, y);
bodyDef.linearDamping = linearDamping;
bodyDef.angularDamping = angularDamping;
// Create our body in the world using our body definition
this.body = world.createBody(bodyDef);
// Create a circle shape and set its radius
CircleShape circle = new CircleShape();
circle.setRadius(radius);
// Create a fixture definition to apply our shape to
fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = (float) (mass/(Math.PI*radius*radius));
fixtureDef.friction = frictionStrength;
fixtureDef.restitution = elasticity;
fixtureDef.filter.categoryBits = category;
fixtureDef.filter.maskBits = mask;
// Create our fixture and attach it to the body
this.fixture = body.createFixture(fixtureDef);
// BodyDef and FixtureDef don't need disposing, but shapes do.
circle.dispose();
... unrelated functions after that
}
Am I disposing correctly? Is this a bug? Is there any way to get around it and use the setAngularVelocity properly?
Because you haven't shown much code, I can I'm not 100% sure that I'm right, but I think that you are hitting the built in maximum movement limit of 2.0 units per time step. This means that at a typical framerate of 60Hz a body covering 2 units per timestep is moving at 120 m/s or 432 km/h (270 mph). Unfortunately it seems that there is no direct way to change this limit in Java, because this limit seems to be defined in the native C++ librarys.
But I think that the real problem is that you have a wrong scale. Box2D uses MKS (meters, kilograms, and seconds). And you may have used pixels instead of meters. The FAQ of Box2D suggests to use
objects [that are] between 0.1 - 10 meters
otherwise you can get strange situations.
See http://www.iforce2d.net/b2dtut/gotchas#speedlimit
and https://code.google.com/p/box2d/wiki/FAQ
I just found the problem, and it was pretty simple. Im just going to post this here for future googlers:
Object was actually rotating properly, the problem was in my drawing method, I didn't use conversion between radians to degrees in my batch.draw, and it interpreted everything in radians. I know such an amateur mistake! Thanks a lot for your time.
I want to draw some (filled) polygons with libGDX. It shoudn't be filled with a graphic/texture. I have only the vertices of the polygon (closed path) and tried to visualize with meshes but at some point this is not the best solution, I think.
My code for an rectangle is:
private Mesh mesh;
#Override
public void create() {
if (mesh == null) {
mesh = new Mesh(
true, 4, 0,
new VertexAttribute(Usage.Position, 3, "a_position")
);
mesh.setVertices(new float[] {
-0.5f, -0.5f, 0
0.5f, -0.5f, 0,
-0.5f, 0.5f, 0,
0.5f, 0.5f, 0
});
}
}
// ...
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
mesh.render(GL10.GL_TRIANGLE_STRIP, 0, 4);
}
is there a function or something to draw filled polygons in an easier way?
Since recent updates of LibGDX, #Rus answer is using deprecated functions. However, I give him/her credits for the new updated version below:
PolygonSprite poly;
PolygonSpriteBatch polyBatch = new PolygonSpriteBatch(); // To assign at the beginning
Texture textureSolid;
// Creating the color filling (but textures would work the same way)
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0xDEADBEFF); // DE is red, AD is green and BE is blue.
pix.fill();
textureSolid = new Texture(pix);
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] { // Four vertices
0, 0, // Vertex 0 3--2
100, 0, // Vertex 1 | /|
100, 100, // Vertex 2 |/ |
0, 100 // Vertex 3 0--1
}, new short[] {
0, 1, 2, // Two triangles using vertex indices.
0, 2, 3 // Take care of the counter-clockwise direction.
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
For good triangulating algorithms if your polygon is not convex, see the almost-linear earclipping algorithm from Toussaint (1991)
Efficient triangulation of simple polygons, Godfried Toussaint, 1991
Here is a libGDX example which draws a 2D concave polygon.
Define class members for PolygonSprite PolygonSpriteBatch
PolygonSprite poly;
PolygonSpriteBatch polyBatch;
Texture textureSolid;
Create instances, 1x1 size texture used with red pixel as workaround. An array of coordinates (x, y) is used for initialization of the polygon.
ctor() {
textureSolid = makeTextureBox(1, 0xFFFF0000, 0, 0);
float a = 100;
float b = 100;
PolygonRegion polyReg = new PolygonRegion(new TextureRegion(textureSolid),
new float[] {
a*0, b*0,
a*0, b*2,
a*3, b*2,
a*3, b*0,
a*2, b*0,
a*2, b*1,
a*1, b*1,
a*1, b*0,
});
poly = new PolygonSprite(polyReg);
poly.setOrigin(a, b);
polyBatch = new PolygonSpriteBatch();
}
Draw and rotate polygon
void draw() {
super.draw();
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
poly.rotate(1.1f);
}
I believe the ShapeRenderer class now has a polygon method for vertex defined polygons:
ShapeRenderer.polygon()
You can use the ShapeRenderer API to draw simple, solid-color shapes with Libgdx.
The code you've given is a reasonable way to draw solid color polygons too. Its much more flexible than ShapeRenderer, but is a good bit more complicated. You'll need to use glColor4f to set the color, or add a Usage.Color attribute to each vertex. See the SubMeshColorTest example for more details on the first approach and the MeshColorTexture example for details on the second approach.
Another option to think about is using sprite textures. If you're only interested in simple solid colors objects, you can use very simple 1x1 textures of a single color and let the system stretch that across the sprite. Much of Libgdx and the underlying hardware are really optimized for rendering textures, so you may find it easier to use even if you're not really taking advantage of the texture contents. (You can even use a 1x1 white texture, and then use a SpriteBatch with setColor and draw()
to draw different color rectangles easily.)
You can also mix and match the various approaches, too.
Use triangulation algorithm and then draw all triangles as GL_TRIANGLE_STRIP
http://www.personal.psu.edu/cxc11/AERSP560/DELAUNEY/13_Two_algorithms_Delauney.pdf
just wanted to share my related solution with you, namely for implementing and drawing a walkZone with scene2d. I basically had to put together the different suggestions of the others' posts:
1) The WalkZone:
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.EarClippingTriangulator;
import com.badlogic.gdx.math.Polygon;
import com.mygdx.game.MyGame;
public class WalkZone extends Polygon {
private PolygonRegion polygonRegion = null;
public WalkZone(float[] vertices) {
super(vertices);
if (MyGame.DEBUG) {
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(0x00FF00AA);
pix.fill();
polygonRegion = new PolygonRegion(new TextureRegion(new Texture(pix)),
vertices, new EarClippingTriangulator().computeTriangles(vertices).toArray());
}
}
public PolygonRegion getPolygonRegion() {
return polygonRegion;
}
}
2) The Screen:
you can then add a listener in the desired Stage:
myStage.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
if (walkZone.contains(x, y)) player.walkTo(x, y);
// or even directly: player.addAction(moveTo ...
return super.touchDown(event, x, y, pointer, button);
}
});
3) The implementation:
The array passed to te WZ constructor is a set of x,y,x,y... points. If you put them counter-clockwise, it works (I didn't check the other way, nor know how it exactly works); for example this generates a 100x100 square:
yourScreen.walkZone = new WalkZone(new int[]{0, 0, 100, 0, 100, 100, 0, 100});
In my project it works like a charm, even with very intricated polygons. Hope it helps!!
Most answers suggest triangulation, which is fine, but you can also do it using the stencil buffer. It handles both convex and concave polygons. This may be a better solution if your polygon changes a lot, since otherwise you'd have to do triangulation every frame. Also, this solution properly handles self intersecting polygons, which EarClippingTriangulator does not.
FloatArray vertices = ... // The polygon x,y pairs.
Color color = ... // The color to draw the polygon.
ShapeRenderer shapes = ...
ImmediateModeRenderer renderer = shapes.getRenderer();
Gdx.gl.glClearStencil(0);
Gdx.gl.glClear(GL20.GL_STENCIL_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl.glStencilFunc(GL20.GL_NEVER, 0, 1);
Gdx.gl.glStencilOp(GL20.GL_INVERT, GL20.GL_INVERT, GL20.GL_INVERT);
Gdx.gl.glColorMask(false, false, false, false);
renderer.begin(shapes.getProjectionMatrix(), GL20.GL_TRIANGLE_FAN);
renderer.vertex(vertices.get(0), vertices.get(1), 0);
for (int i = 2, n = vertices.size; i < n; i += 2)
renderer.vertex(vertices.get(i), vertices.get(i + 1), 0);
renderer.end();
Gdx.gl.glColorMask(true, true, true, true);
Gdx.gl.glStencilOp(GL20.GL_ZERO, GL20.GL_ZERO, GL20.GL_ZERO);
Gdx.gl.glStencilFunc(GL20.GL_EQUAL, 1, 1);
Gdx.gl.glEnable(GL20.GL_BLEND);
shapes.setColor(color);
shapes.begin(ShapeType.Filled);
shapes.rect(-9999999, -9999999, 9999999 * 2, 9999999 * 2);
shapes.end();
Gdx.gl.glDisable(GL20.GL_STENCIL_TEST);
To use the stencil buffer, you must specify the number of bits for the stencil buffer when your app starts. For example, here is how to do that using the LWJGL2 backend:
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.stencil = 8;
new LwjglApplication(new YourApp(), config);
For more information on this technique, try one of these links:
http://commaexcess.com/articles/7/concave-polygon-triangulation-shortcut
http://glprogramming.com/red/chapter14.html#name13
http://what-when-how.com/opengl-programming-guide/drawing-filled-concave-polygons-using-the-stencil-buffer-opengl-programming/
I am new to Jmonkey programming and I would like to ask a question about collision interaction as my code seems to finds collisions possibly from the terrain and i do not know how to solve this out. My goal is player as a first person to be detected if he collides with an enemie's ghost control to display a message as an output. My code displays a continues collision and then it crashes...
package test;
//imports...
public class test extends SimpleApplication
implements ActionListener,PhysicsTickListener{
private MotionPath path;
private MotionPath path2;
private MotionTrack motionTrack;
private MotionTrack motionTrack2;
private AnimChannel channel2;
private AnimControl control2;
private AnimControl control3;
private AnimChannel channel3;
private BulletAppState bulletAppState;
private RigidBodyControl landscape;
private CharacterControl player;
private Vector3f walkDirection = new Vector3f();
private boolean left = false, right = false, up = false, down = false;
private TerrainQuad terrain;
private Material mat_terrain;
private GhostControl ghost;
static test app;
Material matMarker;
public static void main(String[] args) {
app = new test();
app.start();
}
float displacement=60;
int score = 0;
int robotHealth=0;
Geometry mark;
Node shootables;
Node pickUpObject1;
BitmapText hudText;
#Override
public void simpleInitApp() {
createScene();
enemies();
pickUptype1();
initCrossHairs(); // a "+" in the middle of the screen to help aiming
initKeys(); // load custom key mappings
initMark(); // a red sphere to mark the hit
hudText = new BitmapText(guiFont, false);
hudText.setSize(guiFont.getCharSet().getRenderedSize()); // font size
hudText.setColor(ColorRGBA.Red); // font color
hudText.setLocalTranslation(600, 700, 0); // position
guiNode.attachChild(hudText);
DirectionalLight sun2 = new DirectionalLight();
sun2.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
int width = settings.getWidth(); //width is the width of the gui
int height = settings.getHeight(); //height is the height of the gui
}
protected Geometry makeCube(String name, float x, float y, float z) {
Box box = new Box(new Vector3f(x, y, z), 3f, 3f, 3f);
Geometry cube = new Geometry(name, box);
Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Texture tex_ml = assetManager.loadTexture("Interface/Logo/Monkey.jpg");
mat1.setTexture("ColorMap", tex_ml);
cube.setMaterial(mat1);
return cube;
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}
/**
* This is the main event loop--walking happens here.
* We check in which direction the player is walking by interpreting
* the camera direction forward (camDir) and to the side (camLeft).
* The setWalkDirection() command is what lets a physics-controlled player walk.
* We also make sure here that the camera moves with player.
*/
#Override
public void simpleUpdate(float tpf) {
hudText.setText("SCORE \n" + " " + score);// the text
Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
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()); }
player.setWalkDirection(walkDirection);
cam.setLocation(player.getPhysicsLocation());
path.setCycle(true); // Make path a complete circuit
path2.setCycle(true);
motionTrack.setLoopMode(LoopMode.Loop);
motionTrack2.setLoopMode(LoopMode.Loop);
}
public Node robot(){
Node monster = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
monster.scale(1.5f, 1.5f, 1.5f);
monster.rotate(0.0f, -3.0f, 0.0f);
// Create a appropriate physical shape for it
return monster;
}
public void createScene(){
/** Set up Physics */
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
//bulletAppState.getPhysicsSpace().enableDebug(assetManager);
flyCam.setMoveSpeed(100);
setUpKeys();
terrain = new TerrainQuad("my terrain", 65, 513, heightmap.getHeightMap());
/** 6. Add physics: */
// We set up collision detection for the scene by creating a
// compound collision shape and a static RigidBodyControl with mass zero.*/
CollisionShape terrainShape =
CollisionShapeFactory.createMeshShape((Node) terrain);
landscape = new RigidBodyControl(terrainShape, 0);
terrain.addControl(landscape);
CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
player = new CharacterControl(capsuleShape, 0.05f);
player.setJumpSpeed(20);
player.setFallSpeed(30);
player.setGravity(30);
player.setPhysicsLocation(new Vector3f(145f, -28f, 10f));
player.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_01);
player.addCollideWithGroup(PhysicsCollisionObject.COLLISION_GROUP_01);
setUpLight();
rootNode.attachChild(SkyFactory.createSky( assetManager,
"Textures/Sky/Bright/BrightSky.dds", false));
}
public void enemies(){
shootables = new Node("Shootables");
rootNode.attachChild(shootables);
Node Robot1 = robot();
Node Robot2 = robot();
CapsuleCollisionShape capsule = new CapsuleCollisionShape(4f, 10f);
RigidBodyControl robot1Cap = new RigidBodyControl(capsule, 0.01f);
Robot1.addControl(robot1Cap);
getPhysicsSpace().add(robot1Cap);
bulletAppState.getPhysicsSpace().add(robot1Cap);
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
robot1Cap.setMass(100f);
robot1Cap.setKinematic(true);
CapsuleCollisionShape capsule2 = new CapsuleCollisionShape(4f, 10f);
RigidBodyControl robot2Cap = new RigidBodyControl(capsule, 0.01f);
Robot2.addControl(robot2Cap);
getPhysicsSpace().add(robot2Cap);
bulletAppState.getPhysicsSpace().add(robot2Cap);
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
robot2Cap.setMass(100f);
robot2Cap.setKinematic(true);
ghost = new GhostControl(
new BoxCollisionShape(new Vector3f(8f,8f,8f))); // a box-shaped ghost
Robot1.addControl(ghost);
ghost.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_01);
ghost.setCollideWithGroups(PhysicsCollisionObject.COLLISION_GROUP_01);
getPhysicsSpace().add(ghost);
getPhysicsSpace().addTickListener(this);
control2 = Robot1.getControl(AnimControl.class);
channel2 = control2.createChannel();
channel2.setAnim("Walk");
control3 = Robot2.getControl(AnimControl.class);
channel3 = control3.createChannel();
channel3.setAnim("Walk");
path = new MotionPath();
path.addWayPoint(new Vector3f(500f,-83f,3f));
path.addWayPoint(new Vector3f(350f,-79f, 3f));
path.enableDebugShape(assetManager,rootNode);
// Initialize our motionTrack object
motionTrack = new MotionTrack(Robot1, path);
motionTrack.setDirectionType(MotionTrack.Direction.Path);
// Enable the motionTrack
motionTrack.setEnabled(true);
path2 = new MotionPath();
path2.addWayPoint(new Vector3f(180f,-50f,-100f));
path2.addWayPoint(new Vector3f(200f, -55f, -30f));
path2.enableDebugShape(assetManager,rootNode);
// Initialize our motionTrack object
motionTrack2 = new MotionTrack(Robot2, path2);
motionTrack2.setDirectionType(MotionTrack.Direction.Path);
// Enable the motionTrack
motionTrack2.setEnabled(true);
shootables.attachChild(Robot1);
shootables.attachChild(Robot2);
}
public void physicsTick(PhysicsSpace space, float f) {
if (ghost.getOverlappingObjects().size() > 0) {
final Vector3f bPoint = ghost.getPhysicsLocation();
try {
app.enqueue(new Callable<Boolean>() {
public Boolean call() throws Exception {
app.addMarker(bPoint);
return true;
}
});
} catch (Exception ex) {
}
}
}
public void pickUptype1(){
pickUpObject1 = new Node("pickUpObject1");
rootNode.attachChild(pickUpObject1);
Node cube1 = new Node();
cube1.attachChild(makeCube("the Deputy", 220f, -63f, -150f));
Node cube2 = new Node();
cube2.attachChild(makeCube("the Deputy2", 410f, -89f, -270f));
RigidBodyControl floor_phy = new RigidBodyControl(0.0f);
cube1.addControl(floor_phy);
RigidBodyControl floor_phy2 = new RigidBodyControl(0.0f);
cube2.addControl(floor_phy2);
bulletAppState.getPhysicsSpace().add(floor_phy);
bulletAppState.getPhysicsSpace().add(floor_phy2);
pickUpObject1.attachChild(cube1);
pickUpObject1.attachChild(cube2);
}
}
You include a lot of unnecessary code and judging from the style you are new to programming in general, I edited your question to make it readable so people could try and help (didn't fix the indenting though that would have taken far too much patience. The code should be stripped down to include only the physics code, member variables and setup for context. If your ghost control didn't rest on top of the ground it would fall through and leave your robot so its going to be constantly colliding with the ground or falling through the void after an initial pass through the ground.
You should be using getOverlappingObjects() to get the list of objects it's colliding with and then checking that list for the characters representation in the physics space.
Also this below is bad form. If your method is going to throw an exception you should know what it can throw and throw those exact exceptions and handle each one. Otherwise you're basically strapping on body armour and running around with your eyes closed because believe you won't get hurt.
public Boolean call() throws Exception
Also your enemies function is poorly designed. Functions should preferably be small, and should definitely only fulfill one purpose, whereas your function creates a node with shootables, creates two enemies and sets up controls.
Creating the node and attaching it to the root node should be in the constructor or an initialise function that is called near the start. And then you should have a function called something like addEnemy that adds a single enemy. For example:
/*
* adds an enemy and returns a reference to that enemy
*/
Spatial addEnemy()
{
//Characters are spatials typically in JMonkey
Spatial enemy=new Spatial();
CapsuleCollisionShape collisionShape=new CapsuleCollisionShape(4.0f, 10.0f);
CharacterControl characterControl = new CharacterControl(collisionShape, stepHeight);
enemy.addControl(characterControl);
getPhysicsSpaceState(characterControl);
shootables.attachChild(enemy);
}
Now you notice this is really different right?
Well in JMonkey Characters use Spatials (a class derived from node) and also they don't use rigid body physics, things like terrain are too bumpy, instead they use character controls and these keep them facing upright so they don't topple and allow you to set walk direction and view direction. The character control also gives access to a collision shape that you can run collides with on to get when character controls collide with each other.
For controlling your Spatial you'll want to subclass AbstractControl, using that you can get access to other controls and update your Spatial every so often. In my game all character are Spatials with different skins set the difference is in the controls.
The player has:
CharacterControl
//to send keyboard input to the character controller to move it
KeyboardControl
GunControl
Whereas the AI has
CharacterControl
//does path planning to get a route and steers character control along it
RouteController
GunControl
//AI to determine where/if I want to walk, what to shoot at, where to aim
BehaviourControl
You should be splitting a lot of this functionality into a path to make it easier to maintain, learning more about how the JMonkey API works by looking at examples and also learning how Object Oriented design works and how to structure things so they are easy to read and easy to maintain. If you're interested the book clean code by Robert C Martin is a great guide on a clean maintainable coding style :)
Any questions feel free to ask