Wikipedia: “An elastic collision is a collision in which the total kinetic energy of the colliding bodies after collision is equal to their total kinetic energy before collision.”
In this case ball collision by manipulating DOM elements with CSS animations. This is a basic game mechanic used in many games like Snooker or Hunderds.
Although this tutorial is written in javascript, you should be able to use the same techniques and concepts in almost any development environment. Based on a flash tutorial Managing ball vs ball collision with Flash by Emanuele Feronato.
add the balls
In the first function we call, we add the balls to the scene. Just creating some DIV elements, give them some properties and push them to an array to access them later on. We also start the render if the last element is added. To keep the DIV elements in style we set some CSS properties like “border-radius”.
var ballsArray =[]; var maxBalls = 5; function initBalls() { //ADD maxBalls for (var i=0;i<maxBalls;i++) { var ball = document.createElement("div"); ball.className ="ball"; //RANDOM START POSITION ON THE SCREEN ball.left =Math.round(Math.random()*window.innerWidth-30); ball.top = Math.round(Math.random()*390); ball.collision = 0; ball.mass = 1; //RANDOM START SPEED ball.xspeed = Math.round(Math.random()*8-4); ball.yspeed = Math.round(Math.random()*8-4); document.getElementById('wrapper').appendChild(ball); ballsArray.push(ball); if(ballsArray.length == maxBalls){tick();} } }
move the balls
In this function we loop trough the Array which stores the balls an animate them by changing some CSS properties. First we check if any ball reaches the end of the screen. If so we inverse speed for the x and y direction. Next we get the speed value for the actual ball and apply it to the current position. This function is called in the render tick so it is executed on every frame.
function moveBall() { //for each ball in the array for (var i=0;i<maxBalls;i++) { //Check for a collision with boundaries if (ballsArray[i].left<0) {ballsArray[i].left = 0;ballsArray[i].xspeed *= -1;} // BOUNDRIES Left if (ballsArray[i].left>window.innerWidth-30) {ballsArray[i].left = window.innerWidth-30;ballsArray[i].xspeed *= -1;} // BOUNDRIES Right if (ballsArray[i].top<0) {ballsArray[i].top = 0;ballsArray[i].yspeed *= -1;} // BOUNDRIES Top if (ballsArray[i].top>window.innerHeight-30) {ballsArray[i].top = innerHeight-30;ballsArray[i].yspeed *= -1;} // BOUNDRIES Bottom //GET THE NEW POSITION ballsArray[i].left += ballsArray[i].xspeed; ballsArray[i].top += ballsArray[i].yspeed; //APPLY THE NEW POSITION //ballsArray[i].style.WebkitTransform = "translate("+ballsArray[i].left+"px,"+ballsArray[i].top+"px)";//2D Transform ballsArray[i].style.WebkitTransform = "translate3D("+ballsArray[i].left+"px,"+ballsArray[i].top+"px,0px)";//3D Transform fo better Performance?? -> "testing" ballsArray[i].style.MozTransform = "translate3D("+ballsArray[i].left+"px,"+ballsArray[i].top+"px,0px)"; } //ball.style.WebkitTransform = "translate("+ball.dx+"px,"+ball.dy+"px)"; }
ball collision
There is a whole lot of math in this function. It gets called every time two balls collide together, ball and ball2. If you want to understand how this works, you could read these articles “The Physics of an Elastic Collision” also “real-world-physics-problems“.
function manage_bounce(ball, ball2) { dx = ball.left-ball2.left; dy = ball.top-ball2.top; collisionision_angle = Math.atan2(dy, dx); magnitude_1 = Math.sqrt(ball.xspeed*ball.xspeed+ball.yspeed*ball.yspeed); magnitude_2 = Math.sqrt(ball2.xspeed*ball2.xspeed+ball2.yspeed*ball2.yspeed); direction_1 = Math.atan2(ball.yspeed, ball.xspeed); direction_2 = Math.atan2(ball2.yspeed, ball2.xspeed); new_xspeed_1 = magnitude_1*Math.cos(direction_1-collisionision_angle); new_yspeed_1 = magnitude_1*Math.sin(direction_1-collisionision_angle); new_xspeed_2 = magnitude_2*Math.cos(direction_2-collisionision_angle); new_yspeed_2 = magnitude_2*Math.sin(direction_2-collisionision_angle); final_xspeed_1 = ((ball.mass-ball2.mass)*new_xspeed_1+(ball2.mass+ball2.mass)*new_xspeed_2)/(ball.mass+ball2.mass); final_xspeed_2 = ((ball.mass+ball.mass)*new_xspeed_1+(ball2.mass-ball.mass)*new_xspeed_2)/(ball.mass+ball2.mass); final_yspeed_1 = new_yspeed_1; final_yspeed_2 = new_yspeed_2; ball.xspeed = Math.cos(collisionision_angle)*final_xspeed_1+Math.cos(collisionision_angle+Math.PI/2)*final_yspeed_1; ball.yspeed = Math.sin(collisionision_angle)*final_xspeed_1+Math.sin(collisionision_angle+Math.PI/2)*final_yspeed_1; ball2.xspeed = Math.cos(collisionision_angle)*final_xspeed_2+Math.cos(collisionision_angle+Math.PI/2)*final_yspeed_2; ball2.yspeed = Math.sin(collisionision_angle)*final_xspeed_2+Math.sin(collisionision_angle+Math.PI/2)*final_yspeed_2; }
render tick
This function is called every frame to animate all the Objects on the screen. The function check first if the distance between any balls is greater than the diameter. If not it collides, calls the ball collision function and passes the two colliding balls over. We also call the moveBall & tick function to keep the animation running.
function tick() { for (x=0; x<=maxBalls; x++) { for (y=x+1; y<maxBalls; y++) { distance_x = Math.abs(ballsArray[x].left-ballsArray[y].left); distance_y = Math.abs(ballsArray[x].top-ballsArray[y].top); distance = Math.sqrt(distance_x*distance_x+distance_y*distance_y); if (distance<=31 && (ballsArray[x].collision == 0 || ballsArray[y].collision == 0)) { ballsArray[x].collision = 1; ballsArray[y].collision = 1; manage_bounce(ballsArray[x], ballsArray[y]); } else if (distance>31) { ballsArray[x].collision = 0; ballsArray[y].collision = 0; } //window.console.log(distance) } } moveBall(); requestAnimFrame( tick );//RUN THE NEXT TICK }
conclusion
If your game mechanic bases on elastic collision this is a great way of doing it. Full control over the code, light weight, fast and simple.