So i have sprites that are should be un-walkable for my character.
The problem is im not sure how to do this.
Here is what i am trying. I am having the sprites added to a Linked List when they are loaded from the level.
Then i iterate through the list to detect if the character is colliding with any of the unwalkable sprites/tiles.
I do this on the engines update thread which runs about every second.
Here is how i am doing this:
mScene.registerUpdateHandler(new IUpdateHandler(){
#Override
public void onUpdate(float pSecondsElapsed) {
Iterator<Sprite> wall_collision = unwalkable_Sprites.iterator();
while(wall_collision.hasNext()){
Sprite sprite = wall_collision.next();
if(character_sprite.collidesWith(sprite)){
}
}
}
#Override
public void reset() {
// TODO Auto-generated method stub
}
});
So the question is how do i position the character when it collides with the unwalkable tile so the character doesnt even cross over into the tile..To the user it should seem like a boundary.
Does anyone have any suggestions or know how i can do this?
Look into this example
use the same code for you un-walkable tiles as "ground" in example. You should use static bodies:
this.mPhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false);
final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0.5f, 0.5f);
Here you can use Shape as in example or for example Sprite
//final Shape yourTile = new Rectangle(0, CAMERA_HEIGHT - 2, CAMERA_WIDTH, 2);
Sprite yourTile = new Sprite(...);
PhysicsFactory.createBoxBody(this.mPhysicsWorld, yourTile, BodyType.StaticBody, wallFixtureDef);
Here you can find box2dExtension for AndEngine (it need for using Bodies)
Sorry for my English :) Hope this will help you.
P.S. and you don't need to check collisions for this issue anymore.
Related
I'm trying to make a simple bit of code that will detect whether a model was clicked on. So far the best method I've seen is to create some sort of rectangle around the mesh and detect with Gdx.input.justTouched() to get the x,y coordinates, and then check if the rectangle contains the coordinates returned by justTouched().
I have no idea if there's a better way to do this, some kind of mesh onClick listener or something that LibGDX has in place that I'm unaware of (I've been scouring Google and the javadocs but I can't seem to find anything). I don't really need to deal with the z-axis coordinate, at least I don't think so. I only have the one PerspectiveCamera and it's not going to be moving around that much (not sure if this matters?)
Anyways, in my render() method I have:
if (Gdx.input.justTouched()) {
//this returns the correct values relative to the screen size
Vector2 pos = new Vector2(Gdx.input.getX(), Gdx.input.getY());
//I'm not sure how to get the correct rectangle to see what the
//width and height are for the model relative to the screen?
Rectangle modelBounds = new Rectangle(<<not sure what to put here>>);
if (modelBounds.contains(pos.x, pos.y) {
System.out.println("Model is being touched at: " + pos.x + ", " + pos.y);
}
}
I'm really not sure if this is the correct way to do this. I can get the position of the model with:
modelInstance.getNode("Node1").globalTransform.getTranslation(new Vector3());
but I'm not sure how to get the width and height as a rectangle relative to the screen size, if it's even possible.
I'm also unsure if this would cause massive lag, as I'm going to have about 7 nodes total that I need to detect if they're clicked on or not.
Is there a better way to do this? If not, is there a way to get the model width & height relative to the screensize (or camera, maybe)?
EDIT: Read about using Bounding Boxes, seems like what I need. Not quite sure how to implement it properly, however. I've changed my code to such:
public ModelInstance modelInstance;
public BoundingBox modelBounds;
#Override
public void create() {
...
//omitted irrelevant bits of code
modelInstance = new ModelInstance(heatExchangerModel);
modelBounds = modelInstance.calculateBoundingBox(new BoundingBox());
}
#Override
public void render() {
...
if (Gdx.input.justTouched()) {
Vector3 pos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
System.out.println(pos);
if (modelBounds.contains(pos)) {
System.out.println("Touching the model");
}
}
}
I'm not really sure what the output of BoundingBox is supposed to be, or how the numbers it gives me correlates to the position in a 2d space. Hmm..
EDIT2: Think I'm getting closer.. Read about Rays and the .getPickRay method for my PerspectiveCamera. .getPickRay seems to return completely unusable numbers though, like really tiny numbers. I think I need to do something like:
if (Gdx.input.justTouched()) {
Vector3 intersection = new Vector3();
Ray pickRay = perspectiveCamera.getPickRay(Gdx.input.getX(), Gdx.input.getY());
Intersector.intersectRayBounds(pickRay, modelBounds, intersection);
}
and then intersection should give me the point where they overlap. It appears to be not working, however, giving me really small numbers like (4.8066642E-5, 2.9180354E-5, 1.0) .. hmmm..
I am trying to implement the method beginContact contactlistener class , but I have some doubts about it .
I'm new to libgdx and I can not figure out how to " leave" the method that controls the contacts .
in my project I have a Stage that manages the actors .
I looked online and found that I have to add World .
but I can not understand the concept , why :)
Some good soul can help me? Thank You
public void create() {
stage = new Stage();
Gdx.input.setInputProcessor(stage);
world = new World(new Vector2(0,-10), true);
world.setContactListener(contactlistener);
//meteora
meteora = new Image(new TextureRegion(new Texture(Gdx.files.internal("planet.png")), 128, 128));
meteora.setOrigin(meteora.getWidth() / 2, meteora.getHeight() / 2);
meteora.setPosition(Gdx.graphics.getWidth() / 2, 100);
meteora.setZIndex(3);
group = new Group();
group.addActor(spaceShip);
group.addActor(flagShip);
group.setZIndex(3);
group.setWidth(98);
group.setHeight(128);
group.setOrigin(group.getWidth() / 2, group.getHeight() / 2);
group.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
stage.addActor(group);
stage.addActor(meteora);
}
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
/*....other code....*/
}
ContactListener contactlistener = new ContactListener() {
#Override
public void beginContact(Contact contact) {
final Fixture x1 = contact.getFixtureA();
final Fixture x2 = contact.getFixtureB();
System.out.println(x1.getUserData());
System.out.println(x2.getUserData());
if ( (x1.equals("gruppo") && x2.equals("planet")) || (x1.equals("planet") && x2.equals("gruppo")) )
System.out.println("shot");
}
#Override
public void endContact(Contact contact) {
}
#Override
public void preSolve(Contact contact, Manifold oldManifold) {
}
#Override
public void postSolve(Contact contact, ContactImpulse impulse) {
}
};
The problem is, that Actor and subclasses are classes from Scene2D, the Libgdx scenegraph, while World and ContactListener are form Box2D, a physics engine.
The Box2D engine can't work with Actors, it needs Bodys and Fixtures to work.
So if you want to use both concepts together, you need to create a Box2DActor, which creates a Body inside it's constructor and adds it to the physics World.
The in your render(delta) you need to update the World using it's step method and then you need to update the Box2DActors positions, using the positions of their Bodys.
I suggest you to read those articles in the libgdx wiki:
Scen2D
Box2D
It won't be a bad idea to read the rest of the wiki or at least the for you relevant parts.
Also i suggest to start with much simpler games, which does not need a physics engine like Box2D, to get some basic knowledge of libgdx.
Starting with complex games will just frustrate you, as you will face many problems down the road and many of them will be hard to solve.
EDIT:
The collission detection itself depends on the shapes you are using.
If your objects are circles, then they collide, if the distance between them is smaller then the sum of their radii.
For Rectangles you could use the overlaps method.
The Intersector class does also contain a few methods to detect collisions/overlaps of different objects.
The management is another thing. If you don't have to many and to complex objects, i guess it would be enough to keep them in a list and iterate through it in a nested for loop, checking every object pair.
A little optimization would be to do a distance check before the actual collision check, as most object will be sorted out, before doing some complex overlap checks.
I am the programmer for my FRC Team 4468 and we are using mecanum wheels this year. We are trying to control the robot with two joysticks, one for moving in a direction (mecStick), and another for rotation (rotStick) using this line of code.
myDrive.mecanumDrive_Cartesian(mecStick.getX(), mecStick.getX(), rotStick.getY(), 0);
The robot will move in the Y direction (fowards, backwards), and will rotate but won't move in the X axis. Could someone tell me what i'm doing wrong please.
Thanks :-)
public class RobotTemplate extends SimpleRobot {
RobotDrive myDrive = new RobotDrive(1,2,3,4);
Joystick mecStick = new Joystick(1);
Joystick rotStick = new Joystick(2);
public void robotInit() {
}
public void autonomous() {
}
public void operatorControl() {
//myDrive.setSafetyEnabled(true);
myDrive.mecanumDrive_Cartesian(mecStick.getX(), mecStick.getX(), rotStick.getY(), 0);
Timer.delay(0.01);
}
}
Looks like you pass mecStick.getX() twice, one should probably be mecStick.getY(). I'm not familiar with the RobotDrive class, so I'm not sure which should be switched. The WPILib Javadoc is your friend, you can find a copy hosted by team 2168 at http://team2168.org/javadoc/. Look for RobotDrive on the left bottom list, and check there.
Best of luck from 1902, Exploding Bacon!
I've got a few years experience in Java and Slick2D, and I'm attempting to port a game over to libgdx due to the fact that it is a much better library. However, I'm having a simple issue with moving a sprite on the screen. There must be some paradigm I don't understand with this API. I've distilled my code into the following. It recognizes input and runs it through my entire entity and networking systems both to and from the server, and yet the sprite stays at the same location. Any help would be wonderful.
My create() method:
public void create() {
//Check to see if we are using external server or localhost
ClientNetworkManager.setLocalhostAsHost(Constant.useLocalhost, this);
//Begin application setup
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(1280,900);
batch = new SpriteBatch();
//Load assets into memory
texture = new Texture(Gdx.files.internal("Sprites/Ash.png"));
//texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
sprite = new Sprite(texture);
sprite.setSize(256,256);
sprite.setPosition(44, 666);
//Begin external network setup
setPlayerPointer(null);
this.connection = new ClientConnectionThread(this);
getClientConnectionThread().setCurrentPing(0);
}
My render() method:
public void render() {
//Render logic
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
if (connection.isActive() && !Constant.performEssentialTasksOnly) {
InputManager.handlePlayerInput(this);
batch.setProjectionMatrix(camera.combined);
camera.update();
batch.begin();
batch.draw(sprite,playerPointer.getCurrentX(),playerPointer.getCurrentY());
System.out.println(playerPointer.getCurrentX());
batch.end();
}
}
The values for the getCurrentX/Y methods are all reporting correctly, so that's not the issue. I feel it must be something obvious.
You aren't using the Sprite position to render it, you are actually using it like if it was a regular TextureRegion.
batch.draw(sprite,playerPointer.getCurrentX(),playerPointer.getCurrentY());
System.out.println(playerPointer.getCurrentX());
Where are you changing this playerPointer?
Do it more like this:
sprite.setPosition(playerPointer.getCurrentX(),playerPointer.getCurrentY())
sprite.draw(batch);
But again, the problem is that playerPointer doesnt have the correct values (they aren't changing).
You can call
sprite.setX(xpos);
to change the actual position.
If you are going to use a tiledmap, don't forget to push in the right SpriteBatch, which should be returned by the renderer.getSpriteBatch() method.
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