Box2D collision detection fails after firing a lot of bullets - kotlin

I am working on a physics game and encountered a strange bug: sometimes, after firing a lot of bullets, collision detection starts to fail.
As can be seen in the following GIF, the collision works only on half the platform, which is very strange. Also, the Box2D debug renderer is enabled and it can also be seen that the platform is a single body.
Here is how I get this bug to happen, as it only happens only after firing lots of bullets (in the beginning everything works fine):
Notes:
- the bullet has the bullet field set to true
- I set the player's bullet field to true, did not make a difference
- the player is 1 meter by 1 meter
- the player and the bullets are DynamicBodies and the platforms are StaticBodies
- the map is near to (0, 0), though it goes a bit in the negatives (-1.5), I doubt it matters
- the categoryBits and maskBits are correct (the collision should happen, and it does happen, but glitches)
- after the bullets disappear, the number of bodies is the same as when the game starts (so they are actually destroyed)
- the World's gravity is (0, -25f)
- the game runs at 60fps
Here is the Box2D timestep code, similar to the stepping code from the libGDX wiki:
companion object {
private const val TIME_STEP = 1f / 300f
private const val VELOCITY_ITERATIONS = 6
private const val POSITION_ITERATIONS = 2
}
private var accumulator = 0f
override fun update(deltaTime: Float) {
accumulator += Math.min(deltaTime, 0.25f)
while (accumulator >= TIME_STEP) {
world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS)
accumulator -= TIME_STEP
}
}
I tried changing:
- TIME_STEP to a lower value, like 1/60f
- VELOCITY_ITERATIONS a bit higher, to 8
- POSITION_ITERATIONS a bit higher, to 6
- VELOCITY_ITERATIONS and POSITION_ITERATIONS to 100
and there was no (obvious) difference.
Concern:
The bugged platform seems to start behaving like a bullet (it doesn't collide with other bullets or with the player), at least halfway. So could its categoryBits and maskBits be changed on-the-fly, after a lot of world.createBody() and world.destroyBody(), maybe due to pooling?
So what should I try for the collision not to fail in this case?

I finally managed to fix it.
The solution I found was to loop through every entity that has a body and call refilter(), which seems to fix it:
engine.getEntitiesFor(Family.one(BodyComponent::class.java).get()).forEach {
it.body.body.fixtureList.first().refilter()
}
In the future, I could call refilter() only when needed (if I can determine when I have to call it) instead of calling it every frame, but it works for now.
It looks like you should call refilter() after you change the filterData of a body (changing its categoryBits or maskBits) outside the FixtureDef, but I don't seem to be doing that (or maybe I am missing something), so it is a bit weird.

Related

How to fix: code not running on certain frames

I have been trying to make my character shoot a projectile on a particular frame of animation. However, sometimes it works and other times it just ignores creating the projectile.
I've tried using alarms instead of checking for the image index but I can't get the timer low enough to get the perfect timing.
I think it may be a problem with the image speed being 0.2 instead of 1.
I'm using a state machine to make it switch between moving and shooting, but I checked and it isn't a problem with state switching over as it changes when I want it to.
Here is relevant code from the shooting state:
if image_index == 2 {
instance_create(x+20*image_xscale,y,obj_projectile);
}
Here is the code that changes the tank over to the shooting state from the main state:
if key_shoot{
state = states.shoot;
image_speed = 0.2;
sprite_index = spr_tankShoot;
}
There is also an animation end event in the object with the following code:
if sprite_index == spr_tankShoot{
state = states.normal;
}
If anyone can see something wrong with the code and/or know what might be going wrong with this, it'd be much appreciated.
I think it may be a problem with the image speed being 0.2 instead of 1.
This is possible - if your animations have different speeds and you don't tend to reset image_index on animation start, you may end up with varying starting indexes (suppose, 0.1) that would not fall right on 2.0 when adding 0.2 to them. Checking that a frame is precisely a number is a not-as-good practice in general though.
You could store image_index at the end of the frame for future reference,
image_index_previous = image_index;
and then check that image_index stepped over 2 since the last frame:
if image_index_previous < 2 && image_index >= 2 {
instance_create(x+20*image_xscale,y,obj_projectile);
}

Variable Jump Height

I have been having great difficulty creating a jumping system whereby the user can tap the jump button for a small jump and hold it down for a higher jump.
I stumbled upon this topic:
https://gamedev.stackexchange.com/questions/13277/variable-height-jumping-in-side-scrollers
Which greatly helped me develop the following code:
PlayerMovementTimer = [NSTimer scheduledTimerWithTimeInterval:0.005 target:self selector:#selector(movePlayer) userInfo:nil repeats:YES];
[JumpButton addTarget:self action:#selector(jumpPlayer:) forControlEvents:UIControlEventTouchDown];
[JumpButton addTarget:self action:#selector(stopJump:) forControlEvents:UIControlEventTouchCancel | UIControlEventTouchUpInside | UIControlEventTouchDragExit];
- (void)movePlayer
{
CGFloat playerY = Player.center.y + PlayerYV;
if(playerY > 264) {
PlayerYV = 0;
playerY = 264;
}
if(playerY < 264) {
PlayerYV += 0.048f - PlayerYD;
}
if(HoldingJump && PlayerYV < 0 && PlayerYD + 0.0018f < 0.048f) {
PlayerYD += 0.0018f;
}
Player.center = CGPointMake(Player.center.x + PlayerXV, playerY);
}
- (IBAction)jumpPlayer:(id)sender
{
if(Player.center.y == 264) {
PlayerYD = 0;
PlayerYV = -2.25;
HoldingJump = true;
}
}
- (IBAction)stopJump:(id)sender
{
HoldingJump = false;
}
The code seems to work (some of the values need a bit of fine tuning but I haven't gotten round to that yet). The only problem is that the movement appears to be slightly jerky (even on the real device) and that when the player is at the top of the jump they accelerate really slowly and no values I put seem to be able to get the jump to look smooth like on Mario games.
Please take a look at the code and see if I am missing something obvious, or if there is a more efficient method of controlling movement than an NSTimer calling a void function. Also, is setting a UIImageView's position to a float value bad?
Thanks.
So there are quite a few things wrong here. First, yes, you should never be setting the origin of an ImageView or any other UI element to a coordinate position that is a fractional pixel. This causes sub-pixelling which will blur your image. To avoid this, all CGFloats should be rounded to the nearest whole number using roundf() or other similar rounding functions.
Another issue I can see is that you're setting Player.center. I hope for your sake that Player is not an ImageView cause you're going to be making your life harder. As mentioned above, when the origin of a frame is not set to a CGFloat that is a round number, you'll get sub-pixelling. When you use the center property, you can easily cause yourself to get on a bad origin value. For example, if I have a 11 by 11 image and set it's center to (11,11), the origin will get set to (5.5,5.5) and will cause sub-pixelling. Easy ways to avoid this is just do the math to place the origin correctly and make sure to round the CGFloats that you feed into it (or use CGRectIntegral on the frame before you set it).
A third issue here is that the timer is being called 0.005 seconds. Let's assume you want this game to run with 60 FPS. 60 FPS translates to about 0.0167 seconds. The timer is calling the method three times more often then it would need to even if you wanted 60 FPS and additionally, calling this method so often could be causing some of your jerky motion.
Now in terms of getting a "Mario" like jump, what you really need to do is look at getting a dedicated physics engine since if you're using the code above, you don't appear to have one. What a physics engine would do is it would apply a constant "gravity" which will help make the player jumps look and act more realistically. You would, when a player presses a button, apply an impulse up on the player character. The use of impulses would also simplify your work as you could apply impulses in different ways depending on how long they hold the button, etc. The code above is simply trying to get around this problem instead of addressing the real issue of you not having a physics engine.
Go investigate cocos2D and Box2D as a possible physics engine you could use. There are a wealth of resources on cocos2D+Box2D and there is a developer who even has made a tutorial on using cocos2D to create a Super Mario clone that should give you some basic understanding of how physics engines work: http://www.raywenderlich.com/15230/how-to-make-a-platform-game-like-super-mario-brothers-part-1

iOS Pong Development, Collision Detection

I am in a late phase of finishing my first usable iOS app.
I am creating a simple Pong game i am using a simple collision detection using CGRectIntersectsRect, but i came up with a problem.
if(CGRectIntersectsRect(mic.frame,plosina_a.frame)) {
if(mic.center.y < (plosina_a.center.y + plosina_a.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
if(CGRectIntersectsRect(mic.frame,plosina_b.frame)) {
if(mic.center.y < (plosina_b.center.y + plosina_b.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
When i use it like this the ball (mic) sort of gets to the paddles (plosina) and starts moving the other way sort of in the middle of the paddle.
My programming teacher managed to fix this problem for one of the paddles (the _b one) by adding the .frame.size.height instead of just .frame which i have used before, but when i did the same thing for the other paddle it didn't work i don't know what's up with that.
Also it creates another problem sometimes there is a situation where the ball get's caught up in the paddle - so I'm looking for a definition of the whole object and not just one side probably?
i hope you can help.
I can see three potential problems here.
The first is that you are waiting until the ball overlaps the paddle before counting it as a touch. It sounds like you really want to start the ball moving in the other direction when the ball touches the paddle, not when it intersects it. The CGRectIntersectsRect waits until they overlap before returning true. If you make either rectangle one pixel larger with a call to CGRectInset, your test will return true as soon as the ball reaches that paddle--by that time, there will be one pixel overlapping the expand rectangle. The test would look like this:
if(CGRectIntersectsRect(CGRectInset(mic.frame, -1, -1),plosina_a.frame)) {
if(mic.center.y < (plosina_a.center.y + plosina_a.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
if(CGRectIntersectsRect(CGRectInset(mic.frame, -1, -1),plosina_b.frame)) {
if(mic.center.y < (plosina_b.center.y + plosina_b.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
The second potential problem has to do with the velocity of the ball. Without seeing all of the code, I don't know if this is a problem or not. If the ball can move more than one pixel at a time, it could easily overlap--or even pass through--the paddle without a hit detection. There are lots of logic changes you can add to take care of this, but the easiest solution may be to just make sure the ball doesn't move more than one pixel at a time.
Finally, if you want to use the hack for both paddles, reverse the sign of the comparison on the other side of the game.
I'm guessing that your pong is played vertically, so that one paddle is at the top of the screen and the other is at the bottom?
If you you need to mirror the logic vertically for the other paddle. Right now you are using the same logic for the top and bottom paddles, but for the bottom paddle you probably need something like the following instead
if(CGRectIntersectsRect(mic.frame,plosina_b.frame)) {
if(mic.center.y > (plosina_b.center.y - plosina_b.frame.size.height)) {
RychlostMice.y = -RychlostMice.y;
}
}
Notice how I'm using the > sign and subtracting the height instead of adding it.

Unity 3D Physics

I'm having trouble with physics in unity 3d. I'm wanting my ball to bounce off of walls and go another direction. When the ball hits the wall it just bounces straight back. I have tried changing the direction to be orthogonal to the direction it hits the wall but it doesn't change direction. Due to this, the ball just keeps hitting the wall and bouncing straight back.
Secondly, sometimes the ball goes through the wall. The walls have box colliders while the ball has a sphere collider. They all have continuous dynamic as the collision detection mode.
Here's a link to a similar thread:
http://forum.unity3d.com/threads/22063-I-shot-an-arrow-up-in-the-air...?highlight=shooting+arrow
Personally, I would code the rotation using LookAt as GargarathSunman suggests in this link, but if you want to do it with physics, you'll probably need to build the javelin in at least a couple of parts, as the others suggest in the link, and add different drag and angular drag values to each part,perhaps density as well. If you threw a javelin in a vacuum, it would never land point down because air drag plays such an important part (all things fall at the same rate regardless of mass, thank you Sir Isaac Newton). It's a difficult simulation for the physics engine to get right.
Maybe try to get the collider point between your sphere and your wall then catch your rigidbody velocity and revert it by the collision point normal.
an example of a script to do that ---> (put this script on a wall with collider )
C# script:
public class WallBumper : MonoBehaviour
{
private Vector3 _revertDirection;
public int speedReflectionVector = 2;
/***********************************************
* name : OnCollisionEnter
* return type : void
* Make every gameObject with a RigidBody bounce againt this platform
* ********************************************/
void OnCollisionEnter(Collision e)
{
ContactPoint cp = e.contacts[0];
_revertDirection = Vector3.Reflect(e.rigidbody.velocity, cp.normal * -1);
e.rigidbody.velocity = (_revertDirection.normalized * speedReflectionVector);
}
}
I recently has an issue with a rocket going through targets due to speed and even with continuous dynamic collision detection I couldn't keep this from happening a lot.
I solved this using a script "DontGoThroughThings" posted in wiki.unity3d.com. This uses raycasting between current and previous positions and then ensures the frame ends with the colliders connected for messages an OnTrigger event. Has worked everytime since and it's just a matter of attaching the script so super easy to use.
I think the physics answer is as others have suggested to use multiple components with different drag although typically I think you only want a single RigidBody on the parent. Instead of direction using transform.LookAt you could try and calculate using Quaternion.LookRotation from the rigidbody.velocity. Then use Vector3.Angle to find out how much are are off. The greater the angle diference the more force should be experienced and then use RigidBody.ApplyTorque. Maybe use the Sin(angleDifference) * a constant so less force is applied to torque as you approach proper rotation.
Here is some code I used on my rocket although you'll have to substitute some things as I was pointing toward a fixed target and you'll want to use your velocity.
var rotationDirection = Quaternion.LookRotation(lockedTarget.transform.position - this.transform.position);
var anglesToGo = Vector3.Angle(this.transform.rotation.eulerAngles, rotationDirection.eulerAngles);
if (anglesToGo > RotationVelocity)
{
var rotationDirectionToMake = (rotationDirection * Quaternion.Inverse(this.transform.rotation)).eulerAngles.normalized * RotationVelocity;
transform.Rotate(rotationDirectionToMake);
}

Code only works if I NSLog some values - but why?

I'm writing a simple QR code generator (just for fun and to learn some Obj-C), and I'm working on tracing the outline of connected "modules" (i.e. the black squares that make up a QR code). This is in order to have nicer vector output than simply making a bunch of rects for each module.
Long story short, my outline-tracing code works - BUT ONLY if I make sure to call NSLog in a specific place! If I remove the NSLog-call, the code loops! I'm literally doing nothing but logging. And it doesn't matter what I log; I just have to call NSLog or things break.
The tracing algorithm is simple enough: Go clockwise around the shape of connected modules. When you hit a corner, turn right until you're back to following the outline of the shape. Stop when you reach the starting point again. The shape can have two modules that share a corner-point. The tracing-loop will thus hit that point twice. This is expected, and the code handles it correctly - if I call NSLog.
Otherwise, the code will say that a certain point is a corner the first time it sees it, and not a corner the second time, which causes the tracing to loop around. Detecting if something's a corner-point is not dependent on anything except the x and the y coordinates of the point and an array of module objects - but neither the modules nor the array changes while the tracing is going on, so given the same x,y you should always get the same result. And it does – if I call NSLog.
Without NSLog, the coordinates – e.g. (10,9) – is corner on moment, and a moment later (10,9) is suddenly not a identified as a corner. But with an NSLog-call, (10,9) is correctly seen as a corner-point every time.
Again: I change absolutely nothing; I just log something - anything! And suddenly it works. It's like it's saying that 2 == 2 is true or false, unless I tell it to log 2 and 2, in which case 2 == 2 is always true, as it should be.
Here's the flaky code. It's hard to understand out of context, but there's a lot of context, so I hope this is enough. Everything is integers (no fuzzy floating point values).
do { // start going around the shape
// If this isn't here or simply commented out, the code loops.
NSLog(#"foobar"); // doesn't matter what I log - I just need to log something
// Branch: Is current x,y a corner-point? This should
// always return the same result given the same X and Y
// values, but it only does if NSLog is there!
if( [self cornerAtX:x Y:y] ) {
// add the point to the path
[path addPoint:NSMakePoint(x, y)];
// rotate the direction clockwise, until
// the direction is following the edge of the
// the shape again.
do {
dt = dx;
dx = -dy;
dy = dt;
} while( ![self boundaryFromX:x Y:y inDirectionX:dx Y:dy] );
}
// continue along direction
x += dx;
y += dy;
} while( !(sx == x && sy == y) ); // we're back to the start of the shape, so stop
If anyone can tell me why NSLog can make code work (or rather: Why not using NSLog makes working code break), I'd be happy to hear it! I hope someone can make sense of it.
Make sure cornerAtX:Y: always returns something—i.e., that there's no code path that fails to return a value.
Otherwise, it may very well “return” whatever the last function you called returns, in which case calling NSLog (which doesn't return a value, but may ultimately call a function that does) causes it to “return” something different, which may always be something that's considered true.
The compiler should warn you if you fail to return a value from a function or method that you declared as doing so. You should listen to it. Turn on all the warnings you can get away with and fix all of them.
You should also turn on the static analyzer (also included in that post), as it, too, may tell you about this bug, and if it does, it will tell you step-by-step how it's happening.
There's not much to go on here, but I'd guess that it's either an uninitialized variable or some sort of memory stomping. NSLog probably uses both stack and heap memory, so it could affect those.
Have you tried replacing NSLog with some other meaningless operation? If that will also work then I suppose problem is linked to [self cornerAtX: x Y: y].
Another possibility is that the problem is time-related. NSLog takes time to execute, so if QR code is loaded in another thread you can see this kind of behavior.