2

I am trying to get to grips and understand how to use and create colliding balls with HTML5 canvas,examples I have looked at have a lot of JavaScript, but I need to break it down into much smaller chunks to get a better understanding of what's going on.

In my example what I understand so far is that I am redrawing the circles every 40 milliseconds onto the canvas, and calling the animate function each time. Every time this is called the position of the circle changes as I am changing it with

circles[0].x+=1;
circles[0].y+=-1.5;

So my circle objects are in an array, and there are 2 things I would like to achieve:

1) not to let the balls escape the canvas area

2) if the balls collide then bounce off each other and reverse in direction.

What I want to tackle first though is not letting the balls escape the canvas and how I would go about working that out.

I have access to the window.width and window.height, so it's a case of understanding how to get the position of each ball in the array, and ensure that it does not cross those boundaries.

I don't want to just have it work, would much prefer to understand what is happening.

halfer
  • 19,824
  • 17
  • 99
  • 186
Richlewis
  • 15,070
  • 37
  • 122
  • 283
  • [This question](http://stackoverflow.com/questions/20885297/collision-detection-in-html5-canvas) can show you how to detect the collisions between the balls and the container, and [this one](http://stackoverflow.com/questions/8331243/circle-collision-javascript) between the balls themselves. Then on collision you should change the direction of the ball, to create the bounce back. – undefined Jul 26 '14 at 13:25
  • And [here's a complete tutorial](http://www.exeneva.com/2012/06/multiple-balls-bouncing-and-colliding-example/). – undefined Jul 26 '14 at 13:34

3 Answers3

3

This will check collisions on the bounds of the canvas. I updated your objects to store vx and vy (velocity) and the draw() function to move based on these properties. I added checkBounds() which reverses the velocity when the circle goes outside the bounds.

EDIT: modified so that it takes into account the radius of the circles too.

Doing a collision detect between the circles could follow a similar pattern

http://jsfiddle.net/3tfUN/5/

    var canvas = document.getElementById('ball-canvas');
    var context = canvas.getContext('2d')
    var radius = 50;
    var strokewidth = 2;
    var strokestyle = '#666';
    var frameCount = 0;
    var w = canvas.width;
    var h = canvas.height;

    // Circle Objects
    var yellowCircle = {
        x: 50,
        y: h / 2,
        radius: radius,
        color: 'yellow',
        vx: 1,
        vy: 1.5
    }

    var redCircle = {
        x: 450,
        y: h / 2,
        radius: radius,
        color: 'red',
        vx: 1,
        vy: -1
    }

    var blueCircle = {
        x: 850,
        y: h / 2,
        radius: radius,
        color: 'blue',
        vx: -1,
        vy: -1.5
    }

    // Create empty array and then push cirlce objects into array
    var circles = [];
    circles.push(yellowCircle, blueCircle, redCircle);

    function checkBounds() {
        for (var i = 0; i < circles.length; i++) {
            var c = circles[i];
            if (c.x > w - c.radius || c.x < c.radius) {
                c.vx = -c.vx;
            }
            if (c.y > h - c.radius || c.y < c.radius) {
                c.vy = -c.vy;
            }
        }
    }

    // Clear last circle and draw again
    function draw() {
        context.clearRect(0, 0, canvas.width, canvas.height); // Clear the circle from the from page
        for (var i = 0; i < circles.length; i++) {
            var c = circles[i];
            context.beginPath();
            context.fillStyle = c.color // Set the color of the circle using key:valuecontext.fill();
            context.lineWidth = strokewidth;
            context.strokeStyle = strokestyle;
            context.stroke();
            context.arc(c.x, c.y, c.radius, 0, Math.PI * 2); // X-axis Position, y-axis Position, radius, % of fill, ?
            context.closePath();
            context.fill();
        }
    }

    function animate() {
        for (i = 0; i <= 2; i++) {
            circles[i].x += circles[i].vx;
            circles[i].y += circles[i].vy;
        }
        checkBounds();
        draw();
    }

    var canvas = document.getElementById('ball-canvas');
    var context = canvas.getContext('2d')
    var radius = 50;
    setInterval(animate, 40);
geedubb
  • 4,048
  • 4
  • 27
  • 38
2
circles[0].x+=1;
circles[0].y+=-1.5;

That's pretty tough to maintain. Instead, I'd suggest you have properties for X and Y speeds (I used moveX and moveY in the example).

Next, you need to check whether the position of the ball + the radius compensation is touching the canvas edges, and if so, reverse the speed value. So, for example, the X speed of the ball is 4 and now it hits the left or the right canvas egde, the X speed now becomes -4.

This is it, in a nutshell:

            var c = circles[i];

            // check rebounds
            if (c.x - c.radius <= 0 || c.x + c.radius  >= canvas.width)
                c.moveX = -c.moveX; // flip the horizontal speed component
            if (c.y - c.radius <= 0 || c.y + c.radius >= canvas.height)
                c.moveY = -c.moveY; // flip the vertical speed component

            // Yellow Circle
            c.x += c.moveX; // here we don't have to worry
            c.y += c.moveY; // about directions anymore

See my example here: http://jsfiddle.net/3tfUN/8/

The same principle applies for collisions between balls. I'm assuming you want to do simple collisions without angle changes.

But if you wish to simulate real ball collisions, that would require some more serious trigonometry to calculate when exactly the pixel-perfect collision happens, and to calculate the new X and Y speed components.


UPDATE

An example featuring slightly improved collision detection and speed transfer between balls: http://jsfiddle.net/3tfUN/12/

Shomz
  • 37,421
  • 4
  • 57
  • 85
  • thank you for your answer, yes i will be coming onto simple collisions but first i think ill digest whats happening here and then move onto that – Richlewis Jul 26 '14 at 13:55
  • 2
    Also you don't need to do radius/2 - should just be radius – geedubb Jul 26 '14 at 13:56
  • You're welcome, I'm just expanding that last fiddle to include simple collisions. But basically, it will be the same thing as detecting canvas egde collisions. – Shomz Jul 26 '14 at 13:56
  • so the check collisions could go in the animate function? – Richlewis Jul 26 '14 at 14:03
  • Yes, I think it would be the most natural place for it. Another interesting thing might be to implement the speed translation (a fast ball hits the slow ball and "gives" it its speed, so now the fast ball is slower than the slow ball). – Shomz Jul 26 '14 at 14:04
  • that sounds a bit fancy at the moment :-) So just to clarify, when detecting the ball collision, rather than detecting the canvas edge, i am looking to dectect the edge of the circle, defined as c in this example.. – Richlewis Jul 26 '14 at 14:14
  • Hehe, once you start, you just keep adding things. :) Yes, the collision happens when coordinates of c are within the double radius range of c2 (technically, it should be within the radius of c + the radius of c2, but they have the same values in your example). – Shomz Jul 26 '14 at 14:22
  • im going to ask something big here but do you think you could show me an example please..Trying to grasp it – Richlewis Jul 26 '14 at 14:36
  • Sure, let me sketch something quickly. – Shomz Jul 26 '14 at 14:47
  • thank you so much, however on a lighter note i have just added some logic that will change the ball color when it touches edge of the canvas...haha – Richlewis Jul 26 '14 at 14:56
  • Haha, I've also added some "fancy" stuff :D Here you go, I'll add it to my answer as well: http://jsfiddle.net/3tfUN/9/ It's the simplest collision detection with the speed translations I've mentioned above. – Shomz Jul 26 '14 at 15:01
  • 1
    @geedubb Haha, you're still watching this thread? :) Yeah, there's so much that can be done, even with a few number of variables we have at this point (imagine blending colours AND shrinking the radius, for example). – Shomz Jul 26 '14 at 15:26
  • 1
    I'm watching you write this dude's whole application for him ;) – geedubb Jul 26 '14 at 15:27
  • 1
    @geedubb And I still get downvoted, imagine that! :) – Shomz Jul 26 '14 at 15:32
  • thank you for your help @Shomz it is appreciated and its not about you showing me what to do, its about me taking each part and understanding whats going on so that i can apply these techniques/logical thinking to something else – Richlewis Jul 26 '14 at 15:42
  • You're welcome! Right, that's why I was trying to explain every step and keep it very simple. Seeing an understandable code can be more useful than dozens of paragraphs describing how something works. At least for some people. :) – Shomz Jul 26 '14 at 15:45
  • Not sure what you mean. You put the circles in the array, not me. And I used only that array and an additional loop to compare all the possible combinations. I think it would be the best to work from my last example: http://jsfiddle.net/3tfUN/12/ – Shomz Jul 26 '14 at 15:59
  • sorry, got confused there, thats why i deleted comment...the circle objects are in an array (yes thats me) i guess im just not sure what var c2 = circles[u]; is doing . is it taking just two of the circles and applying the collision rule? what if all the balls could collide? does that make it a lot more complicated – Richlewis Jul 26 '14 at 16:02
  • Oh that, that's just for readability. You can use `circles[u]` instead of `c2`. Is that what confused you or the whole other loop (with `u` as iterator)? – Shomz Jul 26 '14 at 16:04
  • no applying c2 as a var is fine, but out of the four balls in your example only 2 are colliding? is that right? the red and the blue – Richlewis Jul 26 '14 at 16:06
  • No, all four. The outer loop goes over balls A, B, C and D (let's call them like that), and the inner goes over B, C and D when the outer is A; over C and D when the outer is B; and just over D when the outer is C (hope this makes sense, if not, try outputting the values of i and u to see what's going on). Basically, that's how I go through all the combinations. – Shomz Jul 26 '14 at 16:08
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/58064/discussion-between-richlewis-and-shomz). – Richlewis Jul 26 '14 at 16:09
1

The canvas is just a "canvas" where you draw the circles. What you need to accomplish what you want is to model a "world" where the circles are object with width and height dimensions and their current position, and where the bounds are well defined. Once you have the width and height of each circle and their position, you can calculate where they are in respect to the bounds you set and see if you need to change direction or keep going.

Collisions stem from the same principle but are somewhat harder to model if you want them to be "realistic" (in the bounds problem you are only interested in the width and height of the circles because the bounding area is box shaped and the circle will always collide in the furthest point from its center, while when two circles collide you should take into account the radius of each circle instead of the "bounding box" around them.

I don't have time right now to show you this concepts with examples, but hopefully I sent you in the right track :).

Pablo Mescher
  • 26,057
  • 6
  • 30
  • 33