I am currently making a Pong clone in gamemaker (My first stand alone project in gamemaker).
When the ball collides with the paddle, I reverse the x_speed (reversing its direction by taking it * -1). However, when the ball hits the top or bottom of the paddle, the ball becomes stuck inside, constantly changing its x_speed but never getting out of the paddle.
My question is not so much as what is the problem, but is there a practical way of getting around this problem?
I have tried (but failed) to implement the place_meeting method and I have tried a couple of other methods to detect if the ball has hit the top or bottom of the paddle so that I can adjust it's x and y positions accordingly.
If anyone has ideas (I don't necessarily need the code for the solution, just the idea so that I can implement it in my game. Here is what I have so far.
I have tried other solutions but none of them even came close, so no point in showing them here. If you need any other snippets of code from my program, just ask.
Step for the ball:
//Checking if it touches the borders of the play area
if (x <= 0) {
x_speed *= -1;
obj_score_left.gameScore += 1;
}
if (x + width >= room_width) {
x_speed *= -1;
obj_score_right.gameScore += 1;
}
if (y <= 0) y_speed *= -1;
if (y + height >= room_height) y_speed *= -1;
//Adds the x and y speeds to x and y
x += x_speed;
y += y_speed;
Change your y_speed and x_speed from negative to positive and visa versa each time it hits the external wall of the game by making it negative. If you need to adjust the speed just change the individual variable. It requires less code and is more code efficient overall.
// Create event
x_speed = 20; // Whatever you want
y_speed = 20; // Whatever you want
speed = 1;
// Step event - Checking if it touches the borders of the play area
if (x <= 0) || (x >= room_width/*for the right side*/) {
x_speed = -(x_speed);
}
if (y <= 0) || (y >= room_height) y_speed = -(y_speed);
//Adds the x and y speeds to x and y
x += x_speed;
y += y_speed;
It would be usefull to have the script for your paddle too, since the collision which is causing your problem isn't shown here but I'll try wide-range solutions you should try implementing.
The first step that should help a lot no matter what your situation is would be to force the ball at the right distance from your paddle when it collides, since it shouldn't be inside of it anyway. Something like this
On collision:
ball.x = paddle.x+paddle.width+ball.width
Something that might help too would be to make sure the x_speed can only ever be in the opposite direction from the paddle, either by using point_direction() or abs()
On collision:
direction = point_direction(paddle.x,paddle.y,ball.x,ball.y)
//would require using native direction and speed variables rather than x_speed and y_speed
or:
x_speed = abs(xspeed) //for the left paddle
x_speed = -abs(xspeed) //for the right paddle
Without knowing more about your actual code this is all I can suggest.
Related
I have three object, obj_player, obj_enemy, obj_wall. Now, I have this code from gmc forums.
shoot_cooldown -= 1; //Lower the shooting cooldown timer
if (shoot_cooldown < 0) then shoot_cooldown = 0 //Prevents timer from counting down further than 0.
target_distance = distance_to_object(obj_player); //Distance to the player from enemy.
if (target_distance < 64) //If player within the range of enemy
{
//image_angle = point_direction(x,y,obj_player.x,obj_player.y); //Enemy faces the player.
if (shoot_cooldown == 0) //If enemy can shoot (cooldown ready)
{
bullet = instance_create(x,y,obj_bullet); //Create a bullet relative to enemy
bullet.direction = point_direction(x,y,obj_player.x,obj_player.y); //Shoot it towards player
bullet.speed = 3; //Give it speed
shoot_cooldown = 50; //Set the new cooldown time between low and high thresholds.
}
}
This works perfectly. However, the obj_player and obj_enemy have the dimensions of 64x64. I have in my code that if the player's distance between the enemy is equal or below 64pix, enemy will fire a bullet. Now the obj_wall has the dimension of 32x32. If the player is on the other side of the wall, the enemy must not fire a bullet since enemy "can't detect" the player. But the enemy still fires a bullet because the player is within 64 pix. I wonder if there is a workaround in making the enemy stop firing if there is a wall between them. Thanks for those who will reply. I know there is gmc forums I'm hoping someone can also help me here.
Use collision_line between obj_enemy and obj_player, like
if target_distance < 64 and collision_line(x, y, obj_player.x, obj_player.y, obj_wall, false, true) == noone
{
// shoot
}
for more perfect you can check line between enemy and every corner of the player
I'm making a game where there should be a robot throwing ball-shaped objects at another robot.
The balls thrown should fly in the shape of a symmetrical arc. Pretty sure the math-word for this is a parabola.
Both robots are on the x axis.
How can I implement such a thing in my game? I tried different approaches, none worked.
The current system of moving things in my game, is like so: Every object has x and y co-ordinates (variables), and dx and dy variables.
Every object has a move() method, that get's called every cycle of the game-loop. It simply adds dx to x and dy to y.
How can I implement what I described, into this system?
If there is a lot of math involved, please try to explain in a simply way, because I'm not great with math.
My situation:
Thanks a lot
You should add velocity to your missiles.
Velocity is a vector, which means it says how fast the missile moves in x-axis and how fast in y-axis. Now, instead of using Move() use something like Update(). Something like this:
void Update()
{
position.X += velocity.X;
position.Y += velocity.Y;
}
Now let's think, what happens to the missile, once it is shot:
In the beginning it has some start velocity. For example somebody shot the missile with speed of 1 m/s in x, and -0.5 m/s in y. Then as it files, the missile will be pulled to the ground - it's Y velocity will be growing towards ground.
void Update()
{
velocity.Y += gravity;
position.X += velocity.X;
position.Y += velocity.Y;
}
This will make your missile move accordingly to physics (excluding air resistance) and will generate a nice-looking parabola.
Edit:
You might ask how to calculate the initial velocity. Let's assume we have a given angle of shot (between line of shot and the ground), and the initial speed (we may know how fast the missiles after the shot are, just don't know the X and Y values). Then:
velocity.X = cos(angle) * speed;
velocity.Y = sin(angle) * speed;
Adding to Michal's answer, to make sure the missile hits the robot (if you want it to track the robot), you need to adjust its x velocity.
void Update()
{
ball.dy += gravity; // gravity = -9.8 or whatever makes sense in your game
ball.dx = (target.x - ball.x); // this needs to be normalized.
double ballNorm = sqrt(ball.dx^2 + ball.dy^2);
ball.dx /= ballNorm;
ball.x += ball.dx;
ball.y += ball.dy
}
This will cause the missile to track your target. Normalizing the x component of your vector ensures that it will never go above a velocity of one. It's nor fully "normalizing" the vector because normally you would have to do this to the y component too. If we didn't normalize here, we would end up with a ball that jumps all the way to your target on the first update. If you want to make your missile travel faster, just multiply ballNorm by some amount.
you can get every thing you need from these few equations
for the max height to time to distance.
g = gravity
v = start vorticity M/S
a = start angle deg
g = KG of object * 9.81
time = v*2 * sin(a) / g
range = v^2 * sin(a * 2) / g
height = v^2 * sin(a)^2 / 2*g
I am using a cosine curve to apply a force on an object between the range [0, pi]. By my calculations, that should give me a sine curve for the velocity which, at t=pi/2 should have a velocity of 1.0f
However, for the simplest of examples, I get a top speed of 0.753.
Now if this is a floating point issue, that is fine, but that is a very significant error so I am having trouble accepting that it is (and if it is, why is there such a huge error computing these values).
Some code:
// the function that gives the force to apply (totalTime = pi, maxForce = 1.0 in this example)
return ((Mathf.Cos(time * (Mathf.PI / totalTime)) * maxForce));
// the engine stores this value and in the next fixed update applies it to the rigidbody
// the mass is 1 so isn't affecting the result
engine.ApplyAccelerateForce(applyingForce * ship.rigidbody2D.mass);
Update
There is no gravity being applied to the object, no other objects in the world for it to interact with and no drag. I'm also using a RigidBody2D so the object is only moving on the plane.
Update 2
Ok have tried a super simple example and I get the result I am expecting so there must be something in my code. Will update once I have isolated what is different.
For the record, super simple code:
float forceThisFrame;
float startTime;
// Use this for initialization
void Start () {
forceThisFrame = 0.0f;
startTime = Time.fixedTime;
}
// Update is called once per frame
void Update () {
float time = Time.fixedTime - startTime;
if(time <= Mathf.PI)
{
forceThisFrame = Mathf.Cos (time);
if(time >= (Mathf.PI /2.0f)- 0.01f && time <= (Mathf.PI /2.0f) + 0.01f)
{
print ("Speed: " + rigidbody2D.velocity);
}
}
else
{
forceThisFrame = 0.0f;
}
}
void FixedUpdate()
{
rigidbody2D.AddForce(forceThisFrame * Vector2.up);
}
Update 3
I have changed my original code to match the above example as near as I can (remaining differences listed below) and I still get the discrepancy.
Here are my results of velocity against time. Neither of them make sense to me, with a constant force of 1N, that should result in a linear velocity function v(t) = t but that isn't quite what is produced by either example.
Remaining differences:
The code that is "calculating" the force (now just returning 1) is being run via a non-unity DLL, though the code itself resides within a Unity DLL (can explain more but can't believe this is relevant!)
The behaviour that is applying the force to the rigid body is a separate behaviour.
One is moving a cube in an empty enviroment, the other is moving a Model3D and there is a plane nearby - tried a cube with same code in broken project, same problem
Other than that, I can't see any difference and I certainly can't see why any of those things would affect it. They both apply a force of 1 on an object every fixed update.
For the cosine case this isn't a floating point issue, per se, it's an integration issue.
[In your 'fixed' acceleration case there are clearly also minor floating point issues].
Obviously acceleration is proportional to force (F = ma) but you can't just simply add the acceleration to get the velocity, especially if the time interval between frames is not constant.
Simplifying things by assuming that the inter-frame acceleration is constant, and therefore following v = u + at (or alternately ∂v = a.∂t) you need to scale the effect of the acceleration in proportion to the time elapsed since the last frame. It follows that the smaller ∂t is, the more accurate your integration.
This was a multi-part problem that started with me not fully understanding Update vs. FixedUpdate in Unity, see this question on GameDev.SE for more info on that part.
My "fix" from that was advancing a timer that went with the fixed update so as to not apply the force wrong. The problem, as demonstrated by Eric Postpischil was because the FixedUpdate, despite its name, is not called every 0.02s but instead at most every 0.02s. The fix for this was, in my update to apply some scaling to the force to apply to accomodate for missed fixed updates. My code ended up looking something like:
Called From Update
float oldTime = time;
time = Time.fixedTime - startTime;
float variableFixedDeltaTime = time - oldTime;
float fixedRatio = variableFixedDeltaTime / Time.fixedDeltaTime;
if(time <= totalTime)
{
applyingForce = forceFunction.GetValue(time) * fixedRatio;
Vector2 currentVelocity = ship.rigidbody2D.velocity;
Vector2 direction = new Vector2(ship.transform.right.x, ship.transform.right.y);
float velocityAlongDir = Vector2.Dot(currentVelocity, direction);
float velocityPrediction = velocityAlongDir + (applyingForce * lDeltaTime);
if(time > 0.0f && // we are not interested if we are just starting
((velocityPrediction < 0.0f && velocityAlongDir > 0.0f ) ||
(velocityPrediction > 0.0f && velocityAlongDir < 0.0f ) ))
{
float ratio = Mathf.Abs((velocityAlongDir / (applyingForce * lDeltaTime)));
applyingForce = applyingForce * ratio;
// We have reversed the direction so we must have arrived
Deactivate();
}
engine.ApplyAccelerateForce(applyingForce);
}
Where ApplyAccelerateForce does:
public void ApplyAccelerateForce(float requestedForce)
{
forceToApply += requestedForce;
}
Called from FixedUpdate
rigidbody2D.AddForce(forceToApply * new Vector2(transform.right.x, transform.right.y));
forceToApply = 0.0f;
I'm trying to implement simple continuous collision detection for my pong game however i'm not sure i'm implementing or understand this right. AFAIR continuous collision detection is used for fast moving objects that may pass through another object circumventing normal collision detection.
So what I tried was that because the only fast moving object I have is a ball I would just need the position of the ball, its move speed, and the position of the object we are comparing to.
From this I figured it would be best that for example if the ball's move speed indicated it was moving left, I would compare it's left-most bound to the right-most bound of the other object. From this I would step through by adding the move speed to the left-most bound of the ball and compare to make sure it's greater than the other objects right bound. This would show that there is no left right collision.
I have something somewhat working, but unfortunately, the ball starts bouncing normally for a while then it acts as if it hits a paddle when nothing is there.
I'm a bit lost, any help would be appreciated!
static bool CheckContinuousCollision(PActor ball, PRect ballRect, PActor other, PRect otherRect)
{
PVector ballMoveSpeed;
int ballXLimit;
int ballYLimit;
ballMoveSpeed = ball.moveSpeed;
// We are moving left
if ( sgn(ball.moveSpeed.x) < 0 )
{
ballXLimit = std.math.abs(ballMoveSpeed.x) / 2;
for ( int i = 0; i <= ballXLimit; i++ )
{
if ( ballRect.Left < otherRect.Right && otherRect.Left < ballRect.Left)
{
return true;
}
ballRect.Left -= i;
}
}
//We are moving right
if ( sgn(ball.moveSpeed.x) > 0)
{
ballXLimit = std.math.abs(ballMoveSpeed.x) / 2;
for ( int i = 0; i < ballXLimit; i ++ )
{
if ( ballRect.Right > otherRect.Left && ballRect.Right < otherRect.Right )
{
return true;
}
ballRect.Right += i;
}
}
// we are not moving
if ( sgn(ball.moveSpeed.x) == 0)
{
return false;
}
}
You seem to be checking the collision of only one dimension, i.e the X dimension of your ball versus your Other.
What you probably want is to compare whether the two objects collide in 2d space. This can be easily done by adjusting each objects Bounding Rectangle and checking whether the rectangles overlap. Then in your for loop you can adjust your Ball rectangle accordingly
I have been implementing various forms of simple collision detection with varying results. I have a fairly good working version of collision detection, but there are some odd behaviors that I can't work out.
Just for a reference, i'm making a simple pong game, and trying to refine the collision. The problems I get are when the ball collides with the paddle on either the top or bottom side. In those cases, the ball hovers above (or below) the paddle and does not move. I'm guessing this is because of how i'm checking for collision and how i'm altering the movespeed of the ball.
I would like to implement a way I differentiate between top/bottom and left/right collision but this is the only method that works decently:
static void CheckCollision(PActor object1, PActor object2, PInput pinput)
{
if ( CheckObjectCollision( object1, object2 ) )
{
AdjustMoveSpeed( object1, object2, pinput );
}
}
static bool CheckObjectCollision(PActor object1, PActor object2)
{
int object1LeftBound = object1.position.x;
int object1RightBound = object1.position.x + object1.actorTextureXSize;
int object1TopBound = object1.position.y;
int object1BottomBound = object1.position.y + object1.actorTextureYSize;
int object2LeftBound = object2.position.x;
int object2RightBound = object2.position.x + object1.actorTextureXSize;
int object2TopBound = object2.position.y;
int object2BottomBound = object2.position.y + object2.actorTextureYSize;
if ( object1RightBound < object2LeftBound )
return false;
if ( object1LeftBound > object2RightBound )
return false;
if ( object1BottomBound < object2TopBound )
return false;
if ( object1TopBound > object2BottomBound )
return false;
return true;
}
I am guessing that the root of some of the problems i'm having is the function AdjustMoveSpeed, here it is:
static void AdjustMoveSpeed(PActor object1, PActor object2, PInput pinput)
{
PVector prevMouseLocation = pinput.GetPrevMouseLocation();
PVector currMouseLocation = pinput.GetCurrMouseLocation();
int currentMoveSpeed;
int nextMoveSpeed;
if (typeid(object1) == typeid(PBall))
{
object1.moveSpeed.x *= -1;
if ( typeid(object2) == typeid(PPlayer) )
{
currentMoveSpeed = object1.moveSpeed.y;
nextMoveSpeed = prevMouseLocation.y - currMouseLocation.y;
object1.moveSpeed.y = (prevMouseLocation.y - currMouseLocation.y) * -1;
}
else
{
if (object1.moveSpeed.y > 0)
object1.moveSpeed.y *= -1;
}
}
else if (typeid(object2) == typeid(PBall))
{
object2.moveSpeed.x *= -1;
if ( typeid(object1) == typeid(PPlayer) )
{
currentMoveSpeed = object1.moveSpeed.y;
nextMoveSpeed = prevMouseLocation.y - currMouseLocation.y;
object2.moveSpeed.y = (prevMouseLocation.y - currMouseLocation.y) * -1;
}
else
{
if (object2.moveSpeed.y > 0)
object2.moveSpeed.y *= -1;
}
}
}
What I was attempting to do with AdjustMoveSpeed, is first check to see which object is the ball, after this, multiply the x move speed by -1 to reverse its direction. After this, I check to see if the other object is a player, if so I set the y move speed to the difference between the previous mouse location and current mouse location. This is here to give the player option to change the balls y speed, or add spin.
I've tried checking for intersection between objects so that I can get a specific side, and the result is the ball just flying in the middle of the screen without actually hitting either paddle.
How do I properly check for collision detection on two objects that are squares?
How can I fix AdjustMoveSpeed so that it works properly with collision detection?
Lastly, how do I keep the momentum of the ball of its current speed is greater than the difference of the mouse location before and after the hit?
I've tried taking comparing the absolute value of currentMoveSpeed and nextMoveSpeed but then the ball doesn't change y speed. Something like this:
if ( abs(currentMoveSpeed) < abs(nextMoveSpeed )
object1.moveSpeed.y = (prevMouseLocation.y - currMouseLocation.y) * -1;
else
object1.moveSpeed.y *= -1
Pong is simple enough that, rather than moving the ball each frame and checking for a collision with a paddle, you can actually solve the equation for when the paddle and ball will collide - if that time is less than one frame, there is a collision.
This completely eliminates the issue of the ball moving so fast it moves through the paddle, an issue that plagues many pong-clones that use the naive method of collision-detection.
This solution is called continuous collision detection - see this answer for more information.
If the ball gets stuck on the paddle instead of bouncing it is probably because it keeps changing direction back and forth. The ball should only bounce if it is heading towards the paddle.
if (sgn(object1.moveSpeed.x) == sgn(object1.x - object2.x)) {
// Ball is already moving away from the paddle, don't bounce!
}
else {
// Ok to bounce!
object1.moveSpeed.x *= -1;
}