In my game I have a Bullet class, which is in charge of making a new bullet every time the gun is fired. Upon creation the bullet is added to the Bullets class, which is in charge of keeping track of said bullet, along with the rest of the bullets. I have come across a strange behavior:
After killing an enemy, and then shooting once more, the new bullet has the following traits:
The bullet is the same one (as in the same code id) as the bullet
that killed the enemy. (I.E. if the id was:
com.badlogic.gdx.physics.box2d.Body#fc7157f, then it will be the
exact same id.)
The bullet will appear stuck in place, it's sprite not moving, but according to the game it will have a velocity, but the position remains the same. The only visible movement is when you enable the Box2DDebugRenderer, you can see the body move downwards until hitting the ground at which point he "teleports" back up and slowly falls back down.
The number of stuck bullets are equal to the number of enemies killed.
This is the bullet class:
public class Bullet {
private Body bullet;
public Bullet(final float force, final int bulletDmg, final Weapon weapon,
final World world) {
System.out.println("Position " + weapon.getPosition() + ", Angle: "
+ weapon.getAngle());
final BodyDef bulletDef = new BodyDef();
bulletDef.type = BodyDef.BodyType.DynamicBody;
bulletDef.angle = weapon.getAngle();
bulletDef.position.set(
weapon.getPosition().x
+ (float) (2.5 * MathUtils.cos(weapon.getAngle())),
weapon.getPosition().y
+ (float) (2.5 * MathUtils.sin(weapon.getAngle())));
bulletDef.angle = weapon.getAngle();
PolygonShape bulletShape_1 = new PolygonShape();
bulletShape_1.setAsBox(0.34375f, 0.34375f);
CircleShape bulletShape_2 = new CircleShape();
bulletShape_2.setPosition(new Vector2(0.34375f, 0));
bulletShape_2.setRadius(0.34375f);
final FixtureDef bulletFixture_1 = new FixtureDef();
bulletFixture_1.density = 1f;
bulletFixture_1.shape = bulletShape_1;
bulletFixture_1.friction = 0.25f;
bulletFixture_1.restitution = 0.75f;
final FixtureDef bulletFixture_2 = new FixtureDef();
bulletFixture_2.density = 1;
bulletFixture_2.shape = bulletShape_2;
bulletFixture_2.friction = 0.25f;
bulletFixture_2.restitution = 0.75f;
final Timer creationTimer = new Timer();
creationTimer.scheduleTask(new Task() {
#Override
public void run() {
if (!world.isLocked()) {
System.out.println(bullet);
bullet = world.createBody(bulletDef);
bullet.createFixture(bulletFixture_1);
bullet.createFixture(bulletFixture_2);
System.out.println(bullet);
bullet.applyForceToCenter(
force * MathUtils.cos(weapon.getAngle()), force
* MathUtils.sin(weapon.getAngle()), true);
Sprite sprite = new Sprite(new Texture(
"sprites\\Weapon\\bullet_standard.png"));
sprite.setSize(1.03125f, 0.6875f);
sprite.setOrigin((float) (sprite.getWidth() / 2 - 0.12f),
(float) (sprite.getHeight() / 2));
bullet.setUserData(sprite);
Bullets bullets = Bullets.getInstance(world);
bullets.addBullet(bullet);
bullets.setDmg(bulletDmg);
System.out.println("Create bullet number: " + bullet);
creationTimer.stop();
}
}
}, 0, 1);
creationTimer.start();
}
}
I have been facing this for quite some time now and can't figure out the problem, I would love some assistance with this. Thanks in advance!
Update 1:
I do not reuse any of the bullets created.
This is the code that handles collision with the enemy:
public void onCollision(final Body collidedBody, final String bodyHit,
final int index) {
assert instance != null;
final Timer timer = new Timer();
timer.scheduleTask(new Task() {
#Override
public void run() {
if (!world.isLocked()) {
Circles circles = Circles.getInstance();
if (bodyHit.equalsIgnoreCase("ground")) {
if (bulletGroundCollision.get(index) == 5) {
if (bullets.get(index) != null) {
world.destroyBody(bullets.get(index));
bullets.removeIndex(index);
bulletGroundCollision.removeIndex(index);
}
} else
bulletGroundCollision.set(index,
(bulletGroundCollision.get(index) + 1));
} else if (bodyHit.equalsIgnoreCase("enemy")) {
Circle enemy = circles
.findAccordingToCode(collidedBody);
enemy.damaged(bulletDmg);
System.out.println("Hit at: "
+ bullets.get(index).getPosition());
if (bullets.get(index) != null) {
world.destroyBody(bullets.get(index));
bullets.removeIndex(index);
bulletGroundCollision.removeIndex(index);
}
} else if (bodyHit.equalsIgnoreCase("player")) {
if (bullets.get(index) != null) {
world.destroyBody(bullets.get(index));
bullets.removeIndex(index);
bulletGroundCollision.removeIndex(index);
}
Square square = Square.getInstance(world);
square.damaged(bulletDmg);
}
timer.stop();
}
}
}, 0, 1);
timer.start();
}
The code for the bullet creation is already posted as the bullet class.
bullets - is an Array of bodies that are the bullets.
bulletGroundCollision - is an Array of ints which keeps track of how many times a bullet at i position (i.e. index), hit the ground.
Update 2
I had noticed that after the bullet gets stuck, the collision with the bullet does not happen according to the body, instead it is only when there is a collision with the sprite that a collision is triggered.
Your code is somewhat hard to read. However you need to make sure that after your enemy is dead, you need to update your bullet class again with the update method in your bullet class.
boolean enemyDead;
if(damage<=0)
{
bulletClass.updateBulletLocation();
}
I doesn't look like it to me that you are updating your bullet class after you kill the enemy, and because of this, the bullet stays where it is.
Related
Firstly, i am a complete noob at both C# and Java.
So i have been given this assignment to convert a java applet into C#, i have managed to do everything apart from drawing a rectangle on the screen via drag and drop using mouse events.
Whats supposed to happen is when i click and drag my mouse across the screen a rectangle with no fill and white border should appear. The code i have below is just a white screen with a red cross through it, if i comment out the if(action) statement in the form1_Paint then it works but no rectangle so it must be that code that messing it up.
http://gyazo.com/b2506b8c2ea9b304e34172c42ce98aab <-- what it should look like
http://gyazo.com/a8764ac9f5380f0109623d7a7750ddb6 <-- what it actually looks like
[update]
I have now got a rectangle do display but it happens on the MouseUp event rather than creating it as i am dragging my mouse. The obvious next step was to move it to a different mouse event like mouseMove but then it really messes up and created rectangles constantly as i make it bigger. How can i make it constantly resize the rectangle as i drag my mouse and not keep creating rectangles constantly?
The code
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
}
//added load method
private void Form1_Load(object sender, EventArgs e)//runs functions on load
{
init();
start();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (action)
{
xe = e.X;
ye = e.Y;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
action = true;
// e.consume();
xs = xe = e.X;
ys = ye = e.Y; // starting point y
Form1_MouseMove(sender, e);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
Pen pen = new Pen(Color.White);
g.DrawRectangle(pen, xs, ys, Math.Abs(xs - xe), Math.Abs(ys - ye));
}
int z, w;
//e.consume();
//xe = e.X;
//ye = e.Y;
if (xs > xe)
{
z = xs;
xs = xe;
xe = z;
}
if (ys > ye)
{
z = ys;
ys = ye;
ye = z;
}
w = (xe - xs);
z = (ye - ys);
if ((w < 2) && (z < 2)) initvalues();
else
{
if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
else xe = (int)((float)xs + (float)z * xy);
xende = xstart + xzoom * (double)xe;
yende = ystart + yzoom * (double)ye;
xstart += xzoom * (double)xs;
ystart += yzoom * (double)ys;
}
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
this.Invalidate();
//Repaint();
}
The biggest problem in your code is this statement in the Form1_Paint() method:
g1.Dispose();
You should never be disposing the Graphics instance passed to you. It belongs to the framework, not your code. But you should especially never dispose an object that you plan to use later. When you dispose it here, then the Graphics instance isn't valid later on when you try to draw the rectangle.
Note that this is the same as in Java. I hope the original Java code didn't call Graphics.dispose() too!
Some other suggestions:
when creating a new Pen object, add a using statement to ensure the Pen instance you create is disposed properly (you do own that one! :) ). In this case though, you don't need to create a new Pen object...just use the stock Pen provided by .NET. I.e. Pens.White.
you don't appear to be calling Invalidate() in the MouseDown and MouseMove event handlers. You won't get any visual feedback unless you do that, because the Paint event handler won't be called.
Fix the code so it looks like this:
// Little helper method :)
private static void Swap<T>(ref T t1, ref T t2)
{
T temp = t1;
t1 = t2;
t2 = t1;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
if (action)
{
//g.setColor(Color.White);
if (xe < xs)
{
Swap(ref xs, ref xe);
}
if (ye < ys)
{
Swap(ref ys, ref ye);
}
g1.DrawRectangle(Pens.White, xs, ys, (xe - xs), (ye - ys));
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
// e.consume();
if (action)
{
xe = e.X;
ye = e.Y;
Invalidate();
//repaint();
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
action = true;
// e.consume();
if (action)
{
xs = xe = e.X;
ys = ye = e.Y;
Invalidate();
}
}
I've had a look here, this doesn't seem to fix my problem, the Invalidate();'s make it stutter and when I have it in Form1_Paint it doesn't draw correctly, either draws before straight onto the form, straight after I've zoomed but doesn't actually appear when I'm dragging in my zoom!
I got a 3D spaceship that moves forward and backward in the 3D scene correctly but the rightward and leftward movements are not right and what the A and D buttons do seem to vary with the camera position. This is the listener code which works for buttons W (forward) and S (backward) while buttons A and D don't do exactly what they should.
When I start the 3D space scene the steering of the spaceship is working and the buttons A and D move the spaceship left and right but after a changed the camera and rotated the view, the buttons A and D are still opposite directions but not to the left and right but depend on the camera's position.
public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int movement = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals("moveForward")) {
direction.multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveBackward")) {
direction.multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveRight")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveLeft")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("rotateRight") && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals("rotateLeft") && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);
}
if (name.equals("rotateUp") && rotate) {
ufoNode.rotate(0, 0, -1 * tpf);
}
if (name.equals("rotateDown") && rotate) {
ufoNode.rotate(0, 0, 1 * tpf);
}
}
Can you help me and tell me what should be done to fix the right and left movements? The entire code is
public class SpaceStation extends SimpleApplication implements AnalogListener,
ActionListener {
private PlanetAppState planetAppState;
private Geometry mark;
private Node ufoNode;
private Node spaceStationNode;
private Node jumpgateNode;
private Node jumpgateNode2;
private BetterCharacterControl ufoControl;
CameraNode camNode;
boolean rotate = false;
Vector3f direction = new Vector3f();
private BulletAppState bulletAppState;
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.setResolution(1024, 768);
SpaceStation app = new SpaceStation();
app.setSettings(settings);
// app.showSettings = true;
app.start();
}
#Override
public void simpleInitApp() {
// Only show severe errors in log
java.util.logging.Logger.getLogger("com.jme3").setLevel(
java.util.logging.Level.SEVERE);
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
bulletAppState.setDebugEnabled(false);
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(-.1f, 0f, -1f));
sun.setColor(new ColorRGBA(0.75f, 0.75f, 0.75f, 1.0f));
rootNode.addLight(sun);
// Add sky
Node sceneNode = new Node("Scene");
sceneNode.attachChild(Utility.createSkyBox(this.getAssetManager(),
"Textures/blue-glow-1024.dds"));
rootNode.attachChild(sceneNode);
// Create collision test mark
Sphere sphere = new Sphere(30, 30, 5f);
mark = new Geometry("mark", sphere);
Material mark_mat = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
mark_mat.setColor("Color", ColorRGBA.Red);
mark.setMaterial(mark_mat);
// Add planet app state
planetAppState = new PlanetAppState(rootNode, sun);
stateManager.attach(planetAppState);
// Add planet
FractalDataSource planetDataSource = new FractalDataSource(4);
planetDataSource.setHeightScale(900f);
Planet planet = Utility.createEarthLikePlanet(getAssetManager(),
293710.0f, null, planetDataSource);
planetAppState.addPlanet(planet);
rootNode.attachChild(planet);
// Add moon
FractalDataSource moonDataSource = new FractalDataSource(5);
moonDataSource.setHeightScale(300f);
Planet moon = Utility.createMoonLikePlanet(getAssetManager(), 50000,
moonDataSource);
planetAppState.addPlanet(moon);
rootNode.attachChild(moon);
moon.setLocalTranslation(new Vector3f(10f, 10f, 505000f));//-950000f, 0f, 0f);
// add saucer
ufoNode = (Node) assetManager.loadModel("usaucer_v01.j3o");
ufoNode.setLocalScale(100f);
ufoNode.setLocalTranslation((new Vector3f(1000f, -1000f, 328000f)));
jumpgateNode = (Node) assetManager.loadModel("JumpGate.j3o");
jumpgateNode.setLocalScale(10000f);
jumpgateNode.setLocalTranslation((new Vector3f(10f, 10f, 708000f)));
spaceStationNode = (Node) assetManager.loadModel("SpaceStation.j3o");
spaceStationNode.setLocalScale(4000f);
spaceStationNode.setLocalTranslation((new Vector3f(10000f, -10f, 425000f)));
jumpgateNode2 = (Node) assetManager.loadModel("JumpGate.j3o");
jumpgateNode2.setLocalScale(10000f);
jumpgateNode2.setLocalTranslation((new Vector3f(10f, 10f, 798300f)));
/* This quaternion stores a 180 degree rolling rotation */
// Quaternion roll180 = new Quaternion();
// roll180.fromAngleAxis(FastMath.PI, new Vector3f(0, 0, 1));
/* The rotation is applied: The object rolls by 180 degrees. */
// ufoNode.setLocalRotation(roll180);
rootNode.attachChild(jumpgateNode);
rootNode.attachChild(jumpgateNode2);
rootNode.attachChild(spaceStationNode);
// creating the camera Node
camNode = new CameraNode("CamNode", cam);
// Setting the direction to Spatial to camera, this means the camera
// will copy the movements of the Node
camNode.setControlDir(ControlDirection.SpatialToCamera);
// attaching the camNode to the teaNode
ufoNode.attachChild(camNode);
// setting the local translation of the cam node to move it away a bit
camNode.setLocalTranslation(new Vector3f(-40, 0, 0));
// setting the camNode to look at the teaNode
camNode.lookAt(ufoNode.getLocalTranslation(), Vector3f.UNIT_Y);
// disable the default 1st-person flyCam (don't forget this!!)
ufoControl = new BetterCharacterControl(100000f, 80000f, 5000f);// (2, 4, 0.5f);
// radius (meters), height (meters), gravity (mass)
//ufoNode.addControl(ufoControl);
//rootNode.attachChild(ninjaNode);
//bulletAppState.getPhysicsSpace().add(ufoControl);
//getPhysicsSpace().add(ufoControl);
rootNode.attachChild(ufoNode);
flyCam.setEnabled(false);
registerInput();
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}
public void registerInput() {
inputManager.addMapping("moveForward", new KeyTrigger(keyInput.KEY_UP),
new KeyTrigger(keyInput.KEY_W));
inputManager.addMapping("moveBackward", new KeyTrigger(
keyInput.KEY_DOWN), new KeyTrigger(keyInput.KEY_S));
inputManager.addMapping("moveRight",
new KeyTrigger(keyInput.KEY_RIGHT), new KeyTrigger(
keyInput.KEY_D));
inputManager.addMapping("moveLeft", new KeyTrigger(keyInput.KEY_LEFT),
new KeyTrigger(keyInput.KEY_A));
inputManager.addMapping("toggleRotate", new MouseButtonTrigger(
MouseInput.BUTTON_LEFT));
inputManager.addMapping("rotateRight", new MouseAxisTrigger(
MouseInput.AXIS_X, true));
inputManager.addMapping("rotateLeft", new MouseAxisTrigger(
MouseInput.AXIS_X, false));
inputManager.addMapping("rotateUp", new MouseAxisTrigger(
MouseInput.AXIS_Y, true));
inputManager.addMapping("rotateDown", new MouseAxisTrigger(
MouseInput.AXIS_Y, false));
inputManager.addListener(this, "moveForward", "moveBackward",
"moveRight", "moveLeft");
inputManager.addListener(this, "rotateRight", "rotateLeft", "rotateUp",
"rotateDown", "toggleRotate");
// Toggle mouse cursor
inputManager.addMapping("TOGGLE_CURSOR", new MouseButtonTrigger(
MouseInput.BUTTON_LEFT), new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, "TOGGLE_CURSOR");
// Toggle wireframe
inputManager.addMapping("TOGGLE_WIREFRAME", new KeyTrigger(
KeyInput.KEY_T));
inputManager.addListener(actionListener, "TOGGLE_WIREFRAME");
// Collision test
inputManager.addMapping("COLLISION_TEST", new MouseButtonTrigger(
MouseInput.BUTTON_RIGHT));
inputManager.addListener(actionListener, "COLLISION_TEST");
}
public void onAnalog(String name, float value, float tpf) {
// computing the normalized direction of the cam to move the node
int movement = 80000;
int rotation = 1;
direction.set(cam.getDirection()).normalizeLocal();
if (name.equals("moveForward")) {
direction.multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveBackward")) {
direction.multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveRight")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(movement * tpf);
ufoNode.move(direction);
}
if (name.equals("moveLeft")) {
direction.crossLocal(Vector3f.UNIT_Y).multLocal(-movement * tpf);
ufoNode.move(direction);
}
if (name.equals("rotateRight") && rotate) {
ufoNode.rotate(0, 1 * tpf, 0);
}
if (name.equals("rotateLeft") && rotate) {
ufoNode.rotate(0, -1 * tpf, 0);
}
if (name.equals("rotateUp") && rotate) {
ufoNode.rotate(0, 0, -1 * tpf);
}
if (name.equals("rotateDown") && rotate) {
ufoNode.rotate(0, 0, 1 * tpf);
}
}
public void onAction(String name, boolean keyPressed, float tpf) {
// toggling rotation on or off
if (name.equals("toggleRotate") && keyPressed) {
rotate = true;
inputManager.setCursorVisible(false);
}
if (name.equals("toggleRotate") && !keyPressed) {
rotate = false;
inputManager.setCursorVisible(true);
}
if (name.equals("TOGGLE_CURSOR") && !keyPressed) {
if (inputManager.isCursorVisible()) {
inputManager.setCursorVisible(false);
} else {
inputManager.setCursorVisible(true);
}
}
if (name.equals("TOGGLE_WIREFRAME") && !keyPressed) {
for (Planet planet : planetAppState.getPlanets()) {
planet.toogleWireframe();
}
}
if (name.equals("COLLISION_TEST") && !keyPressed) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// Test collision with closest planet's terrain only
planetAppState.getNearestPlanet().getTerrainNode()
.collideWith(ray, results);
System.out.println("----- Collisions? " + results.size() + "-----");
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of
// geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry().getName();
System.out.println("* Collision #" + i);
System.out.println(" You shot " + hit + " at " + pt + ", "
+ dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
rootNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
rootNode.detachChild(mark);
}
}
}
private ActionListener actionListener = new ActionListener() {
public void onAction(String name, boolean pressed, float tpf) {
if (name.equals("TOGGLE_CURSOR") && !pressed) {
if (inputManager.isCursorVisible()) {
inputManager.setCursorVisible(false);
} else {
inputManager.setCursorVisible(true);
}
}
if (name.equals("TOGGLE_WIREFRAME") && !pressed) {
for (Planet planet : planetAppState.getPlanets()) {
planet.toogleWireframe();
}
}
if (name.equals("COLLISION_TEST") && !pressed) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray(cam.getLocation(), cam.getDirection());
// Test collision with closest planet's terrain only
planetAppState.getNearestPlanet().getTerrainNode()
.collideWith(ray, results);
System.out.println("----- Collisions? " + results.size()
+ "-----");
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of
// geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry()
.getName();
System.out.println("* Collision #" + i);
System.out.println(" You shot " + hit + " at " + pt + ", "
+ dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
CollisionResult closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
rootNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
rootNode.detachChild(mark);
}
}
}
};
}
Going off your last comment, I am posting this as an answer (although I'm not exactly sure what to use as a cross vector.
When retrieving the cross vector, we are looking to get a perpendicular to the straight line out the front of the craft, and the vertical line that is perpendicular to the straight line, going vertically through the center of the craft.
I assume that direction is our forward-direction vector, in which case (regardless of view) we want to cross this with the vertical line that goes through the center of the craft. The crossLocal of these two vectors would be a perpendicular line to both, either going out of the left or right of the craft (regardless of camera or craft orientation).
for my code fix, I will assume craftSkewer is an imaginary skewer that runs through the center of the craft, vertically.
direction.crossLocal(craftSkewer.UNIT_Y).multLocal(movement * tpf);
I think the reason this works initially is due to the UNIT_Y returning 0 - But after moving craft or camera, it is recalculated incorrectly?
My code for collision detection works but as soon as I set my players & monsters velocity to more than 1 I get weird results how can I solve this?
with velocity of 1
with velocity of 4
My collision detection:
static boolean collisionDown(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX(), e.getY() + e.getVelocity(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size );
if (player.intersects(block))
{
//e.goUp(1);
return true;
}
}
return false;
}
static boolean collisionUp(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX(), e.getY() - e.getVelocity(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size);
if (player.intersects(block))
{
return true;
}
}
return false;
}
static boolean collisionRight(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX() + e.getVelocity(), e.getY(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size);
if (player.intersects(block))
{
return true;
}
}
return false;
}
static boolean collisionLeft(Entity e)
{
for(Block i : Game.blocks)
{
Rectangle player = new Rectangle(e.getX() - e.getVelocity(), e.getY(), e.getWidth(), e.getHeight());
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size);
if (player.intersects(block))
{
return true;
}
}
return false;
}
Where i use the collision detection
void goUp(int v)
{
if(!Block.collisionUp(this))
y -= v;
}
void goDown(int v)
{
if(!Block.collisionDown(this))
y += v;
}
void goRight(int v)
{
if(!Block.collisionRight(this))
x += v;
}
void goLeft(int v)
{
if(!Block.collisionLeft(this))
x -= v;
}
Thank you
Your collision methods look just fine to me, assuming you are evaluating where the character will be at the next iteration of the game. I'm willing to bet though, that when there will be a collision, you move your character at an offset to move it out of the collision box and back into a valid area to compensate. When the velocity is higher, it is magnifying this little side effect. Double check all the places that call these methods and follow the logic that follows when these methods return true (principally those in the Y direction).
An additional thing to try, increase the velocity of the character to something even higher, or something in between. If your character moves farther way and closer as you manipulate the value, this is very likely what is happening.
On a side note, note this line in all of your methods
Rectangle player = new Rectangle(e.getX(), e.getY() - e.getVelocity(), e.getWidth(), e.getHeight());
This rectangle will not change (assuming you have not implemented threading into your game) during the duration of the method call, so recreating it every iteration of the for loop is just taking up time. Consider modifying the methods to this:
static boolean collisionDown(Entity e)
{
Rectangle player = new Rectangle(e.getX(), e.getY() + e.getVelocity(), e.getWidth(), e.getHeight());
for(Block i : Game.blocks)
{
Rectangle block = new Rectangle(i.getX(), i.getY(), size, size );
if (player.intersects(block))
{
//e.goUp(1);
return true;
}
}
return false;
}
What I did for collision detection is develop a class called CollissionDetector. It has some logic within it for such methods as the following:
public boolean isColliding();
public boolean collided(Rectangular r1, Rectangular r2);
Rectangular is an interface with one method:
public Rectangle getRectangle();
So my CollisionDetector class worked with anything that could implement the interface called Rectangular and return a Rectangle.
The problem with some code that is supposed to detect collissions is that it reports the same collision more than once.
With my collision detector, the collission detector object would keep track of when the collision starting, whether the two objects were still colliding, and when the collision ended.
It seems like I also had a CollissionListener. I know that this is all very complex, but it did work. The CollisionListener had methods such as:
public void CollissionStarted();
public void CollissionEnded();
I wish that I could give you an SSCCE: http://sscce.org but I don't have the code anymore.
What's up everyone,
I am heuristically producing a Pong clone in Box2D via libGDX. The Null Pointer Exception is originating in the ContactListener's beginContact() method in which I am trying to implement score logic.
The Box2D world uses two separate EdgeShapes as sensors for incrementing a score variable upon collision with the Ball (view attached image). The collision logic between the Ball and the two EdgeShapes works, but collision between the Ball and anything else in the Box2D world crashes the program.
The stack trace:
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.ckq3r.Pong.screens.GameScreen$2.beginContact(GameScreen.java:491)
at com.badlogic.gdx.physics.box2d.World.beginContact(World.java:876)
at com.badlogic.gdx.physics.box2d.World.jniStep(Native Method)
at com.badlogic.gdx.physics.box2d.World.step(World.java:602)
at com.ckq3r.Pong.screens.GameScreen.render(GameScreen.java:99)
at com.badlogic.gdx.Game.render(Game.java:46)
at com.ckq3r.Pong.PongGame.render(PongGame.java:236)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:204)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:112)
The problematic code:
/**Box2D contact listener**/
private void createContactListener() {
world.setContactListener(new ContactListener() {
#Override
public void beginContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
Gdx.app.log("beginContact", "between " + fixtureA.toString() + " and " + fixtureB.toString());
if(fixtureA.getBody().getUserData().equals(1) && fixtureB.getBody().getUserData().equals(2) || fixtureA.getBody().getUserData().equals(2) && fixtureB.getBody().getUserData().equals(1)){
Gdx.app.log("HIT", "goal1 contact");
score1++;
score1String = score1 + "";
}
if(fixtureA.getBody().getUserData().equals(1) && fixtureB.getBody().getUserData().equals(3) || fixtureA.getBody().getUserData().equals(3) && fixtureB.getBody().getUserData().equals(1)){
Gdx.app.log("HIT", "goal2 contact");
score2++;
score2String = score2 + "";
}
}
#Override
public void endContact(Contact contact) {
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
Gdx.app.log("endContact", "between " + fixtureA.toString() + " and " + fixtureB.toString());
}
#Override
public void preSolve(Contact contact, Manifold oldManifold) {
}
#Override
public void postSolve(Contact contact, ContactImpulse impulse) {
}
});
}
Notes:
When I comment out the two conditional statements within the beginContact() method, the code runs. When uncommented, the error is reproduced.
The Ball userData is circleBody.setUserData(1);
The goal1 userData is goalBody.setUserData(2);
The goal2 userData is goalBody.setUserData(3);
Set the userdata for each body in the Box2D world so that you can use conditional logic within the ContactListener to assess whether or not the collision should be handled.
With the emphasis being on the line paddleBody.setUserData(4); below. You can pick any number to set to the userdata of the individual object, so long as the number is unique from the userdata of the other objects within your world.
For example:
/**paddle1**/
BodyDef paddleDef = new BodyDef();
paddleDef.type = BodyType.DynamicBody;
paddleDef.position.set(width * 0.2f, height / 2);
paddleDef.fixedRotation = true;
Body paddleBody = world.createBody(paddleDef);
paddleBody.setUserData(4);
PolygonShape paddleShape = new PolygonShape();
paddleShape.setAsBox(3.0f, 15.0f); //half-width and half-height
FixtureDef paddleFD = new FixtureDef();
paddleFD.shape = paddleShape;
paddleFD.density = 10.0f;
paddleFD.friction = 0.0f;
paddleFD.restitution = 0.1f;
paddleBody.createFixture(paddleFD);
paddleShape.dispose();
paddleBody.setLinearVelocity(0.0f, 0.0f);
/**end paddle1**/
/**paddle2**/
BodyDef paddleDef2 = new BodyDef();
paddleDef2.type = BodyType.DynamicBody;
paddleDef2.position.set(width * 0.8f, height / 2);
paddleDef2.fixedRotation = true;
Body paddleBody2 = world.createBody(paddleDef2);
paddleBody2.setUserData(5);
PolygonShape paddleShape2 = new PolygonShape();
paddleShape2.setAsBox(3.0f, 15.0f); //half-width and half-height
FixtureDef paddleFD2 = new FixtureDef();
paddleFD2.shape = paddleShape2;
paddleFD2.density = 10.0f;
paddleFD2.friction = 0.0f;
paddleFD2.restitution = 0.1f;
paddleBody2.createFixture(paddleFD2);
paddleShape2.dispose();
paddleBody2.setLinearVelocity(0.0f, 0.0f); // Move nowhere at a rate of x = 0.0f, y = 0.0f meters per second
/**end paddle2**/
I create a rectangle for my "line", it's a rotating 'laser sight'
public Rectangle getLaserBox() {
float lOriginX = (leon.getPosition().x + 0.27f);
float lOriginY = (leon.getPosition().y + 0.7f);
float lEndX = lOriginX + (float)Math.cos((leonAimLaserSprite.getRotation())/57) * 5f;
float lEndY = lOriginY + (float)Math.sin((leonAimLaserSprite.getRotation())/57) * 5f;
Rectangle laserBox = new Rectangle(lOriginX, lOriginY, lEndX, lEndY);
return laserBox;
}
Then I have a method that checks for overlap of the rectangles and is supposed to shorten the 'laser sight' sprite if overlap is detected. I call the laserCol() method in my render method for now (I know I'm breaking MVC, just trying to get it working), and my laserWidth is applied as the width of the laser sprite.
private float laserWidth;
public void laserCol() {
Vector2 laserOrigin = new Vector2(leon.getPosition().x + 0.55f, leon.getPosition().y + 0.7f);
boolean laserIsCol = false;
for (Tile t : world.getTiles()) { //pulling in tiles as t, from world method getTiles()
laserWidth = laserOrigin.dst(t.getPosition().x + 0.1f, t.getPosition().y + 0.7f);
if (Intersector.overlapRectangles(getLaserBox(), t.getBounds())) {
laserIsCol = true;
}
}
if (laserIsCol) {
for (Tile t : world.getTiles()) { //pulling in tiles as t, from world method getTiles()
laserWidth = laserOrigin.dst(t.getPosition().x, t.getPosition().y + t.getBounds().y);
}
}
if (!laserIsCol) {
laserWidth = 8f;
}
}
But as you can see in the screenshot, the laser does not shorten. I've looked at other examples but can't seem to understand a better way to do this.
So after adding the single object which I called thing in my code, I decided to check my sprite boundingbox I created and it looked like this.
After that, I changed some code, and am trying to use a ray, I've gotten it to work somewhat but it's not as close as I'd like, any suggestions?
public Ray getLaserRay() {
lOriginX = (leon.getPosition().x + 0.27f);
lOriginY = (leon.getPosition().y + 0.7f);
lEndX = lOriginX + (float)Math.cos((leonAimLaserSprite.getRotation())/57) * 10f;
lEndY = lOriginY + (float)Math.sin((leonAimLaserSprite.getRotation())/57) * 10f;
laserO = new Vector3(lOriginX, lOriginY, 0);
laserD = new Vector3(lEndX, lEndY, 0);
Ray laserRay = new Ray(laserO, laserD);
return laserRay;
}
private float laserWidth;
public void laserCol() {
Vector2 laserOrigin = new Vector2(leon.getPosition().x + 0.55f, leon.getPosition().y + 0.7f);
boolean laserIsCol = false;
if (Intersector.intersectRayBoundsFast(getLaserRay(), thing.getBB())) {
laserIsCol = true;
}
if (laserIsCol) {
laserWidth = laserOrigin.dst(thing.getPosition().x, thing.getPosition().y);
}
if (!laserIsCol) {
laserWidth = 10f;
}
}
I ended up using 2 line segments to get the collision detection to work. I made one for the laser sight, and one for my object(enemy), that extended from the lower x point to the top x point. Basically the same code except I used the segment intersector from libgdx.
Anyone have an idea of how to make a bullet or damage happen at laser sight spot?
Without having more information I just can guess, but I assume the problem might be that you're calculating the width using all the tiles in your list/array. Instead, you should just use the tile the laser collides with to calculate the width.
Something like this:
laserWidth = 8f; //default if no collision is found
for (Tile t : world.getTiles()) { //pulling in tiles as t, from world method getTiles()
if (Intersector.overlapRectangles(getLaserBox(), t.getBounds())) {
laserWidth = laserOrigin.dst(t.getPosition().x, t.getPosition().y + t.getBounds().y);
break;
}
}
Please note that I just reused your code, I don't know if this is correct or if there are some wrong calculations in it. As I said in my comment, you should debug the calculations and look where they start to get wrong.