Handle friendly fire collision detection - libgdx

I want to create a friendly-fire mechanism. Essentially, my cubes fire repeatedly a bullet to hit enemies. I also want to detect the hit between the bullet and another cube (friendly fire). In order to avoid that the bullet's boundaries overlap with the cube that fired it, in my "Cube" class i overrided equals. Each Cube has a unique id, initialized as soon as the Cube is created.
The issue is that i have a very bad bug. When i add the first Cube all behaves normally, but as soon as i add the second one, a collision is detected for both the cubes' bullets, instantly, even if they are not firing to each other. This happens even if i add more cubes. I checked the boundaries and they seems fine.
I put some code:
collision detection
// check collisions between bullets and cubes (friendly fire)
for(BaseBullet bullet : bullets) {
for(BaseSquare square : squares) {
// exclude collision between bullet and its own Cube
if (!bullet.getBaseSquare().equals(square)) {
// check if bounds overlap
if (square.getBounds().overlaps(bullet.getBounds())) {
System.out.println("collision between bullet and qube");
// propagate the event to anyone is interested in it
EventsManager.qubeHitByQube(bullet, square);
bullet.remove();
bulletsToDestroy.add(bullet);
}
}
}
}
cube's equals method
#Override
public boolean equals(Object o) {
if(!(o instanceof BaseSquare) || o == null) {
return false;
}
BaseSquare baseSquare = (BaseSquare)o;
return baseSquare.getUniqueID().equals(getUniqueID());
}
NOTE
With "friendly fire" i mean when your objects hit themselves and not the enemies.

I would suspect the problem is more likely coming from 'if (square.getBounds().overlaps(bullet.getBounds()))' than the square's equals() method.
Are 'BaseBullet' and 'BaseSquare' extending LibGDX's Sprite class? Should you not be calling getBoundingRectangle()?

Related

Detect collision between Actors

I'm trying to implement a collision detector between my Player (Actor) and my Obstacle (Actor too), and I wonder what's the best way to perform a collision detection between them. I see some users saying that they create a Rectangle object in the class and update its bounds every frame, but I don't know if this is the best way to perform this (I'm also trying to make this, but my collision detector method triggers before my Player touches the Obstacle).
This is what I'm trying to check:
public boolean touchedWall() {
// Loop for the obstacles
for (Obstacle obstacle : this.world.getObstacles()) {
// Check if player collided with the wall
if (this.bounds.overlaps(obstacle.getBounds())) {
Gdx.app.log(Configuration.TAG, "collided with " + obstacle.getName());
return true;
}
}
return false;
}
And this is where the method triggers (it should trigger when the Player bounds hit the wall):
I figured out! Instead of using this.bounds.set(x, y, width, height), I was using this.bounds.set(x, y, height, width) like this:
this.bounds.set(this.getX(), this.getY(), this.getHeight(), this.getWidth());
Sorry for my mistake.

What can I do to stop a character's movement upon hitting an object?

I want my controlled character to completely stop moving when he hits a specific object (and not regain control until he answers the question correctly.) But I can't seem to figure out how to make him stop and remove control from the user, removing the key functions that allow character movement only works for the first instance of this and afterwards only stops the character if he falls onto the object rather than touch it (basically keeps moving the character if he touches the object horizontally in every instance afterwards). The current code for the hitTest is below if it helps.
addEventListener(Event.ENTER_FRAME, information);
function information(e:Event)
{
if (player.hitTestObject(folder))
{
removeChild(folder);
myInfo.alpha=100;
myInfo.gotoAndStop("folder1");
myInfo.x=player.x;
myInfo.y=player.y-50;
stage.addEventListener(KeyboardEvent.KEY_DOWN,kU);
//stops player movement
myInfo.myA.addEventListener(MouseEvent.CLICK, answerA);
function answerA(event:MouseEvent)
{
trace("answer A");
myInfo.alpha=0;
stage.removeEventListener(KeyboardEvent.KEY_DOWN, kU);
}
the "kU" function is the function that checks whether a key is not being pressed and doesn't seem to work when i switch it with the "key down function" I have. the code for movement is below.
function loop(e:Event)
{
if (kLeft)
{
speedX=-15;
}
if (kRight)
{
speedX=15;
}
else
{
speedX*=0.5;
}
You can use a boolean value and set it to true when the object hits the specific object you want , then if the boolean value is true you disable the movement code , but when it becomes say false you can activate it again ...
So basically the movement code depends on the boolean value ...
Regards
Nullifying the "kLeft" variable and the "kRight" variable effectively stops the character upon the hitTest. The reason nothing else would work is because the Movement is in a seperate function than the hitTest (so i guess it would constantly detect movement and count that instead). Anyways, the code i used was
kLeft = null;
kRight = null;

How to make a stick man running when pressing a key?

I created a movieclip named stickman. In that, I created an animation by drawing a sequence of move in everyframe, so that stickman can run. Now what I want is that when I press a key, the stick man will run from left to right and when I release the key, it will stop. This is my code:
RunningMan.stop();
stage.addEventListener(KeyboardEvent.KEY_DOWN,keypresseddown);
function keypresseddown(event:KeyboardEvent):void
{
var key:uint = event.keyCode;
switch (key) {
case Keyboard.LEFT :
{
RunningMan.play();
RunningMan.x-=10;
RunningMan.scaleX=-1;
if(RunningMan.x<=0)
{
RunningMan.x=0;
}
};
case Keyboard.RIGHT :
{
RunningMan.play(); //play animated run
RunningMan.x+=10;
RunningMan.scaleX=1;
if(RunningMan.x>=stage.width)
{
RunningMan.x=stage.width;
}
};
default: RunningMan.stop();
}
}
However, when I pressed and held a key, it moved from left to right without animated run.
How can I fix it?
Thanks in advance.
EDIT:
I have a movieclip called character containing 3 movieclip named: standing, running and jumping, respectively. When I pressed up arrow key, it would jump, but if I released the key right away, it did not jump high as the jump movieclip could not finish its frames. This is the code:
if (key.isDown(Keyboard.LEFT))
{
gotoAndStop("running");
BGround.x+=speed;
scaleX=-1;
if(BGround.x>=stage.stageWidth)
BGround.x=stage.stageWidth;
}
else if (key.isDown(Keyboard.RIGHT))
{
gotoAndStop("running");
BGround.x -= speed;
scaleX=1;
}
else
if (key.isDown(Keyboard.UP))
{
gotoAndStop("jumping");
}
else
gotoAndStop("standing");
How can I fix that?
First of all, I hope RunningMan is an instance of the class, not the class itself. And if it is an instance, you should really follow common naming conventions for when you share your code with others (like you are doing now) so it would be runningMan.
So 1st, make the 1st frame of the runnigMan's timeline a picture of the man standing still and name it "still" or something. then name the second "running" and extend that like 20 frames or however long your animation is. at the last frame you will have to use timeline code. just one line of gotoAndPlay("running") will cause those frames of 2 to 20 (or whatever) to loop. When you tell the timeline to go to frame 1 from outside the timeline code, it wont loop anymore and will stay on the frame of the man standing still. So from outside when you want the loop to start:
runningMan.gotoAndPlay("running"); // frame 2
To stop:
runningMan.gotoAndStop("still"); // frame 1
Or you could do it from inside the RunningMan class
public function startRunAnimation():void{
this.gotoAndPlay("running");
}
public function stopRunAnimation():void{
this.gotoAndStop("still");
}
And you could use them just by replacing these function names with the ones you have if your code ex( instead of play() it would be startRunAnimation() )
EDIT
What you could do for this problem is to have a boolean variable for when your character is in the air (somewhere in your code where you do collision detection with the ground or where you handle gravity - however it is set up) so that this part of your code know when your character is in the air. And then you could simple test for this however way you need it.
...
if (key.isDown(Keyboard.UP) || this.inAir==true)
{
gotoAndStop("jumping");
}
else
gotoAndStop("standing");
Although if your character does not inheirt from a collidable object that has gravity, friction etc... then you would have to make the inAir property of whatever other class, public or make getter function for it - so that you can access it here. I hope this helps.

Input detection for a Group with overlapping transparent Images

I use a Group to store some Images and draw them to a SpriteBatch. Now I want to detect which Image has been clicked. For this reason I add an InputListener to the Group to get an event on touch down. The incoming InputEvent got an method (getTarget) that returns an reference to the clicked Actor.
If I click on a transparent area of an Actor I want to ignore the incoming event. And if there is an Actor behind it I want to use this instead. I thought about something like this:
myGroup.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Actor targetActor = event.getTarget();
// is the touched pixel transparent: return false
// else: use targetActor to handle the event and return true
};
});
Is this the right way to do it? I thought returning false for the method touchDown would continue propagation for the event and let me also receive touchDown events for other Actors at the same position. But this seems to be a misunderstanding...
UPDATE
P.T.s answer solves the problem of getting the right Event. Now I have got the problem to detect if the hit pixel is transparent. It seems to me that I need the Image as a Pixmap to get access. But I do not know how to convert an Image to a Pixmap. I also wonder if this is a good solution in terms of performance and memory usage..
I think you want to override the Actor.hit() method. See the scene2d Hit Detection wiki.
To do this, subclass Image, and put your hit specialization in there. Something like:
public Actor hit(float x, float y, boolean touchable) {
Actor result = super.hit(x, y, touchable);
if (result != null) { // x,y is within bounding box of this Actor
// Test if actor is really hit (do nothing) or it was missed (set result = null)
}
return result;
}
I believe you cannot accomplish this within the touchDown listener because the Stage will have skipped the Actors "behind" this one (only "parent" actors will get the touchDown event if you return false here).

Flixel - FlxWeapon not showing any bullet

I tried to make some bullets(actually swords/daggers) using FTP FlxWeapon, but some unexpected results was given.
This is how I set up the FlxWeapon:
sword = new BasicSword("Sword", player, "x", "y");
sword.makeImageBullet(15, BasicSword.SWORD_GIF,5);
sword.setBulletDirection(FlxWeapon.BULLET_RIGHT, 400);
sword.setPreFireCallback(bulletDirectionCallback);
//load FlxControl
if(FlxG.getPlugin(FlxControl) == null){
FlxG.addPlugin(new FlxControl);
}
FlxControl.player1.setFireButton("X", FlxControlHandler.KEYMODE_PRESSED, 100, sword.fire);
}
/*A callback function, to set a proper direction before we fire the sword*/
public function bulletDirectionCallback():void{
if(player.facing == FlxObject.RIGHT){
sword.setBulletDirection(0, 400); //make the bullet move to the right side with velocity of 400.
}
else{ //if player is facing the left direction.
sword.setBulletDirection(180, 400); //make the bullet move to the left side with the velocity of 400.
}
}
Important notes:
BasicSword.as extends Sword.as and Sword.as extends FlxWeapon.
There's nothing important in BasicSword & Sword , actually Sword has only the constructer and BasicSword has only SWORD_GIF.
-What I think is working:
1-The callback function.
2-some methods and variables, like: setFireRate() and bulletsFired.
3-The fire button(FlxControl.player1.setFireButton).
-What is not working:
1-The sword is not showing up.
I solved my bug by putting this codeFlxG.worldBounds.make(0,0,level.width, level.height); after the level is loaded itself because earlier(when I had the bug) I put this code after creating the level object BUT before loading it. so the value of level.width and level.height was 0 and 0.