In unity, i have a racket which is supposed to hit a ball, and the racket is controlled directly by the mouse, i.e the bat is being moved by the mouse using mouse axes and the using transform.translate() function to move the racket.
I expected that Unity3d's physics will not properly translate the racket's movement directly by mouse and impact the ball accordingly, and i would have to write something custom, and it turned out to be true.
But the ball's collision is not being detected properly when the racket is moving. When it is still, everything is fine, and the ball behaves as i like.
Now i went as far as writing a custom physics script (i use C# for scripting) in which i attached 4 raycasts of length 0.6F to the ball, and after doing some complex vector calculations, calculate the velocity of the ball after hitting the racket, and apply it directly to the velocity of the ball using rigidbody.velocity = calculateVelocity(). Now it is again working fine when the racket is not moving, but not when i move the racket. The exact (symptoms of the) problem is:
Using built-in Physics and collision detection: When the racket is moving, the ball sometimes pass straight through the racket, and sometimes, it slows down (to unbelievable levels).
Using my script to calculate velocity: The problem is the same, but it lets me identify what is wrong when i print the normal of the collider (the racket). It sometimes give the right normal, and sometime gives the negative of the normal vector, which means that it is going straight through the top surface and detecting the hit with the bottom side of the collider (racket).
The things i have tried:
Increasing the size of the collider (it works with the wider box collider on the racket, but then obviously the ball moves from quite a distance away from the racket, and my own script works here, default physics give strange results when racket is moved), in short i don't get the reality that i want.
Decreasing the fixed timestamp to 0.001, which significantly improved things, but still very very far away from the result i want, and the ball is again quite often picking the wrong side of the ball.
Changing collision detection to continuous dynamic. Which didn't improve things either.
And in addition to the wrong side picked at collision, another problem i have observed is that after bouncing off the racket, the ball is moves but the racket is moved faster, it instead of moving in a complete arc or line, somehow appears in front of the ball, resulting in two hits. It is a conjecture based on what is visible.
Also it is clear that the "movement" aspect of the racket is not being read by Unity3d's built in physics, resulting in strange behavior when racket is moving using mouse hits the ball.
I am stuck, i have no idea where to move from here. Please tell me what is it that i am doing wrong.
As others have pointed out, the problem is that the ball goes from being on a side of the pad in one frame to being on the other side in the next. Fast moving objects tend to do that if the barriers are too slim.
There are three very simple solutions for this problem:
- Increase the size of the pad or the ball, which is what happened when you changed the collider size.
- Establish a maximum speed for the ball, so that it can never move fast enough to go through the pads.
- Increase the frequency with which Unity makes its physics calculations. It can be changed in Time Manager, reducing the value of Fixed Timestep. Beware of reducing this too much, or the physics engine will be unable to finish a call before the next round is supposed to start and it will never be able to catch up with the game.
Setting a maximum speed for moving objects is something that must always be done. You cannot risk having an important object skyrocket during the game and leave everything in an uncontrolled state.
What I think is happening is that each interval that happens the ball/racquet are moved and then checked for a collision. The issue is that the ball/racquet move to far in a single interval and miss the collision.
1) Ball before racquet
2) Ball after racquet
not
1) Ball before racquet
2) Ball touching racquet
So what you have to do is in your FixedUpdate() method of your ball GameObject is cast a ray from the current ball location to the previous ball location. If there is anything between those two points that should have been hit (ie the racquet) move the ball back to the reported hit point on the ray. This will trigger your existing collision detection stuff.
The reason increasing the size of the collider works as well is because the ball isn't skipping the lager collider. This has the drawbacks you mentioned in your question. The ray casting avoids this issue and allows the ball/racquet to move as fast or slow as they need to.
This is not a complete answer, but I remember dealing with a problem like this many years ago on slower machines.
The problem was using sprite-based collision detection; relying on pixels for the sprite and obstacle being rendered at the same coordinates. If the frame rate is so low that the sprite moves more than the obstacle's size in one frame, you will get into situations where it is (for example) left of the obstacle in one frame and right of the obstacle in the next frame without ever occupying the same pixels.
In this case sprite-based collisions do not work, you have to base collisions on vectors. Instead of checking each sprite pixel for collisions, record the position and convex hull of the sprite. After each frame, calculate a vector from the old position to the new position and intersect it with the convex hull of each obstacle.
There are multiple shortcuts you can make, like comparing only against bounding boxes at first and calculating the hull only if the vector intersects a bounding box, but this is the basic idea. Good luck.
I have been working on a 3d pong game as well, and have run into the exact same problems. I'm going to try enlarging everything like you guys did. As for the paddle adding speed and spin to the ball, I was baffled by this until I realized that changing the position of the paddle does not change its velocity. If the paddle was at zero velocity before it was moved, it will be at zero when the physics engine looks at it next frame. Un checking is kenimatic and controlling the paddle directly through the velocity property fixed the problem. It caused the paddle to jitter when hitting walls, but I fixed that by removing the walls from the paddle layer and handling boundaries manually in LateUpdate. Also, when you update the velocity, first store the new desired velocity in Update so that the controls work smoothly, then commit the changes in FixedUpdate.