Curved Trajectory for a 2D game in Java

536 Views Asked by At

I'm trying to make a Brick Breaker game in Java and I want my ball to have a curved trajectory, based on paddle's acceleration (no gravity involved). How can I compute the ball position at a given moment? I think I need to compute the velocity first but i can't figure out how to do this.

1

There are 1 best solutions below

1
On

The Spinning ball's path.

Curved trajectory? Do you mean when you hit the ball you add spin to the ball and then that spin causes the ball to curve through the air?

If so then use the following method.

The ball needs the following properties

ball =  {
    x,  // position x, y
    y,
    dx, // delta x and y
    dy,
    dr,  // delta rotation
    radius,  // balls radius
}

And a bat

bat = {
    top, // y pos of top of bat
    dx, // delta x (sideways speed
    // plus what ever else you have
}

When the ball hits the bat do the normal reflection.

ball.dy = -ball.dy;
ball.y = bat.top - ball.radius

To convert the bats sideways movement into ball rotation just needs the ball's radius (for the most simple method) Assuming that the bat is at bottom of screen.

The amount of spin is related to the ball sideways motion relative to the bat, then divided by the radius to give the change in rotation per frame. Add that to the ball's current rotation

ball.dr += (ball.dx - bat.dx) / ball.radius

We are not going to do a full fluid dynamic simulation but rather an approximation.

Magnus effect

When a ball moving through the air, is spinning, one side of the ball moves forward in the air stream (outside), and the other side move in the opposite direction (inside). The imbalance in airflow over the surface and the way the inside spin pulls the air with it causes a low pressure region on the inside in the direction perpendicular to the ball's motion.

The force applied by this low pressure area is spread over the ball's diameter

Force = (airDensity * ballVelocity * Math.pow(2 * PI * ball.radius,2) * ball.dr ) / (2 * ball.radius)

We don't need all of this. Air density is fixed as is the ball radius. Thus the force is a linear relationship between ball velocity and ball spin rate. The rest can be set as a coefficient value we can call spinCof

The force adds acceleration which is dependent on mass. Again this does not change so we can add that to the spinCof value.

So the final formula is

Acceleration = spinCof * ballVelocity * ball.dr;

The value of spinCof is unknown and you will need to experiment to find a value that is playable.

To do this in code

spinCof = ? some value 
var ballVelocity = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
var nx = ball.dx / ballVelocity;  // get normalised vector of motion
var ny = ball.dy / ballVelocity;
var accel = spinCof * ballVelocity * ball.dr; // get the acceleration due to spin
ball.dx -= ny * accel;  // apply acceleration perpendicular to motion
ball.dy += nx * accel;  // 

And that is it.

And quick example of this in practice

var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
// create canvas and add to DOM
var canvas = createImage(512,200);
var ctx = canvas.ctx;
document.body.appendChild(canvas);


var w = canvas.width;
var h = canvas.height;    

const spinCof = 0.01;
const airFriction = 0.001;
const spinFriction = 0.001;
var ball = {
    x : ctx.canvas.width * 2,  // start off screen
    y : 0,
    dx : 1,
    dy : 0,
    ang : 0,
    dr : 0,
    radius : 30,
    holdFrames : 0, // time to wait befor firing ball
    draw(){
        ctx.strokeStyle = "black";
        ctx.lineWidth = 2;
        ctx.setTransform(1,0,0,1,this.x,this.y);
        ctx.rotate(this.ang);
        ctx.beginPath();
        ctx.moveTo(0,0);
        ctx.arc(0,0,this.radius,0,Math.PI * 2);
        ctx.stroke();
        ctx.setTransform(1,0,0,1,0,0);
    }, 
    update(){
        if(this.holdFrames > 0){
            this.holdFrames -= 1;
            this.ang += this.dr;

        }else{

            this.dx *= (1-airFriction);  // add friction due to air
            this.dy *= (1-airFriction);
            this.dr *= (1-spinFriction); // add friction to the balls spin
            var ballVelocity = Math.sqrt(this.dx * this.dx + this.dy * this.dy);
            var nx = this.dx / ballVelocity;  // get normalised vector of motion
            var ny = this.dy / ballVelocity;
            var accel = spinCof * ballVelocity * this.dr; // get the acceleration due to spin
            this.dx -= ny * accel;  // apply acceleration perpendicular to motion
            this.dy += nx * accel;  //  
            this.x += this.dx;
            this.y += this.dy;
            this.ang += this.dr;
            if(this.x > ctx.canvas.width + this.radius){  // ball off canvas so reset
                this.dx = Math.random() * 5 + 4; // set random x speed
                this.dy = 0;
                this.dr = (Math.random() * 0.5) * (Math.random() < 0.5 ? -1 : 1); // set random spin
                this.x = 0;
                this.y = canvas.height / 2;
                this.holdFrames = 60;
            }
        }
        
    }
}



// Main animation loop
function mainLoop1(time){
    ctx.globalCompositeOperation = "destination-out";
    ctx.globalAlpha = 0.1;
    ctx.fillRect(0,0,w,h); // clear da screen
    ctx.globalCompositeOperation = "source-over";
    ctx.globalAlpha = 1;
    ball.update();
    ball.draw();
    requestAnimationFrame(mainLoop1);
}    


// start the animation
requestAnimationFrame(mainLoop1);