LÖVE • Community Blogs - new
http://blogs.love2d.org/blog-terms/new
enCircle Collisions
http://blogs.love2d.org/content/circle-collisions
<div class="field field-name-field-blog-term field-type-taxonomy-term-reference field-label-inline clearfix"><h3 class="field-label">Blog Terms: </h3><ul class="links inline"><li class="taxonomy-term-reference-0"><a href="/blog-terms/circle">circle</a></li><li class="taxonomy-term-reference-1"><a href="/blog-terms/collisions">collisions</a></li><li class="taxonomy-term-reference-2"><a href="/blog-terms/reaction">reaction</a></li><li class="taxonomy-term-reference-3"><a href="/blog-terms/resolution">resolution</a></li><li class="taxonomy-term-reference-4"><a href="/blog-terms/collision">collision</a></li><li class="taxonomy-term-reference-5"><a href="/blog-terms/detection">detection</a></li><li class="taxonomy-term-reference-6"><a href="/blog-terms/physics">physics</a></li><li class="taxonomy-term-reference-7"><a href="/blog-terms/new">new</a></li><li class="taxonomy-term-reference-8"><a href="/blog-terms/ball">ball</a></li></ul></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><br />
Hello I'm Phoenix Enero, and in this article (also my first) we will talk about Circular Collisions. But first, let me introduce myself.</p>
<p>I am a programmer in the Philippines. I originally programmed in a programming language called ActionScript 3.0, the programming language used in Flash, and made some pretty cool stuff with it, like a plotting program. After that, I stumbled on <span style="font-size: 14px; line-height: 21px;">LÖVE through a ROBLOX (google it) group called "Love2D Fans". It was a great game engine, partly because it uses a simple programming language called Lua, and I made some games that I, erm, never finished. Okay, I think that's all I need to say about that. So anyways, let's get on with it!</span></p>
<p><span style="font-size: 14px; line-height: 21px;">And, before you comment here about that the code is not working, notice that I fixed most of my careless typos. If you have some problems, re-copy-and-paste the code.</span></p>
<!--break-->
<h1>Detecting Circular Collisions</h1>
<p>First we need to find a way to detect circular collisions. The best way is to use distance checking. It checks if the distance between two circles' centers are less than the sum of their radii (plural of radius). The code will look like this.</p>
<pre class="brush: lua">
function checkDistColl(circleA, circleB)
local dist = math.sqrt((circleA.x - circleB.x)^2 + (circleA.x - circleB.x)^2)
return dist <= circleA.radius + circleB.radius
end</pre>
<p>However, if you want to optimize your code, you can square both sides, thus getting rid of the math.sqrt function on the left side, and squaring the sum of the radii on the right side.</p>
<pre class="brush: lua">
function checkDistColl(circleA, circleB)
local dist = (circleA.x - circleB.x)^2 + (circleA.y - circleB.y)^2
return dist <= (circleA.radius + circleB.radius)^2
end</pre>
<h1>Collision Resolution</h1>
<p>Now we want to know what will happen if they actually collide. But first, let's take a detour and find where the collisions occur.</p>
<h2>Calculating the point of collision</h2>
<p>This isn't really required for the collisions, but it can have some uses, for example, you can add an explosion effect at the point of the collision.</p>
<p>There are two ways to find the point, the right and the wrong way.</p>
<p>The wrong way, which many tutorials use, is averaging the two center points.</p>
<pre class="brush: lua">
collPointX = (circleA.x + circleB.x)/2
collPointY = (circleA.y + circleB.y)/2</pre>
<p>That works <strong>only</strong> if the circles have the same radius.</p>
<p>The right way that we want to use is a bit more complicated, but it works for circles of any radius:</p>
<pre class="brush: lua">
collPointX ((circleA.x * circleB.radius) + (circleB.x * circleA.radius))/(circleA.radius + circleB.radius)
collPointY ((circleA.y * circleB.radius) + (circleB.y * circleA.radius))/(circleA.radius + circleB.radius)</pre>
<p>This gives the true x/y coordinates for the collision point. If you don't bother finding the collision point, try finding the midpoint of both circles, as it will be needed later on.</p>
<h2>Bouncing Apart</h2>
<p>Now, we know when our circles collide into each other, and we know their velocity and their x/y coordinates. How do we work out where they travel next?</p>
<p>There are two ways to do collisions; <em>elastic collisions</em> and <em>inelastic collisions</em>. Elastic collisions, in layman's terms, are collisions in which the objects do not "stick". Think billard balls colliding. Inelastic collisions are when they "stick". Think a bullet penetrating a block of wood. We will do elastic collisions here.</p>
<p>Explaining it in words can be difficult, so I'll add a gif image from Wikipedia. If you would like to learn, in detail, how the formula is derived, click the link in the caption of the image.</p>
<p><img alt="Wikipedia illustration of 2 coins colliding" src="http://gamedevtuts.s3.amazonaws.com/031_whenWorldsCollide/wikigif.gif" style="width: 350px; height: 200px;" /></p>
<address class="rteindent1">Image taken from <a href="http://en.wikipedia.org/wiki/Elastic_collision">http://en.wikipedia.org/wiki/Elastic_Collision</a>, was created by<font color="#000000" face="sans-serif"><span style="line-height: 19.1875px;"> Simon Steinmann</span></font></address>
<address> </address>
<p>Usually, there are a lot of factors that influence collision, such as spin, mass, mass distribution, etc. but in this collision reaction, we will only use the mass factor. If you don't need that much realism, you can substitute mass for the radius of the circle.</p>
<p>The formula has something to do with the <em>conservation of momentum</em>. Momentum is expressed as this:</p>
<p><img alt="momentum = mass times velocity" src="http://upload.wikimedia.org/math/c/a/2/ca2b01dfa919a34a0c9364110797aa36.png" style="width: 68px; height: 13px;" /></p>
<p>In words: momentum is mass times the velocity. The conservation of momentum says that, the sum of the momentum of 2 objects before the collision is equal to the sum of the momentum of the 2 objects after the collision. Assuming that the initial velocities are <em>u</em>, and the final velocities are <em>v</em>, the equation of the conservation of momentum is this:</p>
<p><img alt="conservation of momentum" src="http://upload.wikimedia.org/math/d/c/c/dccfa0969fdbd08e06981e85f2813970.png" style="width: 251px; height: 16px;" /></p>
<p>Now you have an equation with two unknowns, <em>v<sub>1</sub></em> and <em>v<sub>2</sub></em>. To solve an equation with two unknowns, you need to find another equation with the same two unknowns in it. That happens to be dealing with <em>kinetic energy</em>. Kinetic energy is half of the mass times the magnitude of the velocity squared.</p>
<p><img alt="formula for kinetic energy" src="http://upload.wikimedia.org/math/1/1/e/11e6fc84bb2641d36b09c5a6359f7c08.png" style="width: 94px; height: 25px;" /></p>
<p>Now, it just so happens that, the sum of the kinetic energies of 2 objects before and after the collision remains the same. I have seen no equation-style image for that yet, so I'll just express it in code.</p>
<pre class="brush: lua">
KE1 + KE3 = KE1Final + KE2Final</pre>
<p>Expanding it, and factoring out the 1/2's in the kinetic energies (by multiplying both sides by 2) yelds:</p>
<pre class="brush: lua">
(m1 * v1^2) + (m1 * v2^2) =
(m1 * v1Final^2) + (m1 * v2Final^2)
</pre>
<p>Now you have a different equation with the same two unknowns. If you like algebra quite a bit, I invite you to sit down and try to solve both equations for both unknowns. In the mean time, I'll add Newton's Cradle here.</p>
<p><img alt="" src="http://upload.wikimedia.org/wikipedia/commons/d/d3/Newtons_cradle_animation_book_2.gif" style="width: 480px; height: 360px;" /></p>
<address>Incidentally, that represents the conservation of momentum. Haven't I hypnotized you yet?</address>
<p> </p>
<p>You done? The equations, solved for both unknowns, is this:</p>
<p><img alt="Final Formula 1" src="http://upload.wikimedia.org/math/2/f/9/2f9b6fcd36cd25f6e4f563a7c08e9f54.png" style="width: 317px; height: 45px;" /></p>
<p><img alt="Final formula 2" src="http://upload.wikimedia.org/math/e/3/b/e3b6a72306f7ccbfc078034ce8e833d0.png" style="width: 324px; height: 45px;" /></p>
<p>In code, that will look like this, after adding both fractions in each equation:</p>
<pre class="brush: lua">
local newVelX1 = (circleA.vx * (circleA.mass - circleB.mass) + (2 * circleB.mass * circleB.vx)) / (circleA.mass + circleB.mass)
local newVelY1 = (circleA.vy * (circleA.mass - circleB.mass) + (2 * circleB.mass * circleB.vy)) / (circleA.mass + circleB.mass)
local newVelX2 = (circleB.vx * (circleB.mass - circleA.mass) + (2 * circleA.mass * circleA.vx)) / (circleA.mass + circleB.mass)
local newVelY2 = (circleB.vy * (circleB.mass - circleA.mass) + (2 * circleA.mass * circleA.vy)) / (circleA.mass + circleB.mass)</pre>
<p>But, if you have seen it, those values for newVelX1/Y1 and newVelX2/Y2, almost have the same fractions. Is there some way to optimize it? Yes there is. I won't explain the solution in detail, but basically, you find the total velocities of both objects. After getting the final velocity of objectA, you then add the total velocity and the final velocity of objectA to find the final velocity of objectB.</p>
<p>You can actually find the total velocity by <em>subtracting</em> both of the initial velocities. That may seem weird but think of it in the viewpoint of the system. Suppose you have a 40kph and 50 kph cars in a freeway, moving in the same direction. Depending on which car you are at, you can see one going at 10kph, or going at -10kph. In other words, it's either going away from you, or falling behind you.</p>
<p>So first, you find the total velocity <em>before</em> the collision. In code, for both axes, it will look like this:</p>
<pre class="brush: lua">
local vxTotal = circleA.vx - circleB.vx
local vyTotal = circleA.vy - circleB.vy
</pre>
<p>Then, you find the final velocity of circleA:</p>
<pre class="brush: lua">
local newVelX1 = (circleA.vx * (circleA.mass - circleB.mass) + (2 * circleB.mass * circleB.vx)) / (circleA.mass + circleB.mass)
local newVelY1 = (circleA.vy * (circleA.mass - circleB.mass) + (2 * circleB.mass * circleB.vy)) / (circleA.mass + circleB.mass)</pre>
<p>Now, you find the final velocity of circleB, using the method I told earlier:</p>
<pre class="brush: lua">
local newVelX2 = vxTotal + newVelX1
local newVelY2 = vyTotal + newVelY1</pre>
<p>Well that's better than the double formula. You can actually make this more optimized by using the normal vector, but I won't cover it here.</p>
<p>So now, are we done yet? Hmm, not quite.</p>
<p>Earlier, we made some position updates to our circles and <em>then</em> we check for collisions. Because of that, there is a chance that the two overlap and the velocity is not enough to seperate the two. A solution to that is to do some more math.</p>
<p>Find the midpoint of the 2 circles (or the collision point, if both circles have the same radius). Now set the centers of the two circles to be radius R away from the midpoint, but along the "line of collision". The code will look like this:</p>
<pre class="brush: lua">
midpointX = (circleA.x + circleB.x)/2
midpointY = (circleA.y + circleA.y)/2
dist = math.sqrt((circleA.x - circleB.x)^2 + (circleA.y - circleB.y)^2)
circleA.x = midpointX + circleA.radius * (circleA.x - circleB.x)/dist
circleA.y = midpointY + circleA.radius * (circleA.y - circleB.y)/dist
circleB.x = midpointX + circleB.radius * (circleB.x - circleA.x)/dist
circleB.y = midpointY + circleB.radius * (circleB.y - circleA.y)/dist</pre>
<p>That (circleA.x - circleB.x)/dist there is a "normal" vector, that is, a vector with a direction, but with a magnitude of 1, and that is used here to move the circles along the line of collision.</p>
<p>Now, our complete code for the collision resolution function is this:</p>
<pre class="brush: lua">
function circleResolution(circleA, circleB)
if checkCircleColl(circleA, circleB) then
-- Find the new velocities
local vxTotal = circleA.vx - circleB.vx
local vyTotal = circleA.vy - circleB.vy
local newVelX1 = (circleA.vx * (circleA.mass - circleB.mass) + (2 * circleB.mass * circleB.vx)) / (circleA.mass + circleB.mass)
local newVelY1 = (circleA.vy * (circleA.mass - circleB.mass) + (2 * circleB.mass * circleB.vy)) / (circleA.mass + circleB.mass)
local newVelX2 = vxTotal + newVelX1
local newVelY2 = vyTotal + newVelY1
-- Move the circles so that they don't overlap
local midpointX = (circleA.x + circleB.x)/2
local midpointY = (circleA.y + circleA.y)/2
local dist = math.sqrt((circleA.x - circleB.x)^2 + (circleA.y - circleB.y)^2)
circleA.x = midpointX + circleA.radius * (circleA.x - circleB.x)/dist
circleA.y = midpointY + circleA.radius * (circleA.y - circleB.y)/dist
circleB.x = midpointX + circleB.radius * (circleB.x - circleA.x)/dist
circleB.y = midpointY + circleB.radius * (circleB.y - circleA.y)/dist
-- Update the velocities
circleA.vx = newVelX1
circleA.vy = newVelY1
circleB.vx = newVelX2
circleB.vy = newVelY2
end
end</pre>
<h1>More than Two</h1>
<p>That's good enough for two circles, but what about 3 or more? You need to use a double for-loop for that. The unoptimized way would be this:</p>
<pre class="brush: lua">
for i=1, #objects do
local partA = objects[i]
for j=1, #objects do
local partB = objects[j]
if i ~= j then
-- check collision between partA and partB
end
end
end</pre>
<p>I said that's unoptimized because, it checks for collisions that are already checked. Here's what it will look like for 4 objects.</p>
<pre class="brush: lua">
object1 to object2, object3, object4
object2 to object1, object3, object4
object3 to object1, object2, object4
object4 to object1, object2, object3</pre>
<p>If you look carefully, the first statement checks for collisions for object1 and object2. But in the next statement, you are checking for object2 and object1! Certainly, if object1 doesn't collide with the object2, then object2 is also not colliding to object1. A better, more optimized loop, will look like this:</p>
<pre class="brush: lua">
for i=1, #objects-1 do
local partA = objects[i]
for j=i+1, #objects do
local partB = objects[j]
-- check collision between partA and partB
end
end</pre>
<p>Now what the collision checking for 4 objects will look like this:</p>
<pre class="brush: lua">
object1 to object2, object3, object4
object2 to object3, object4
object3 to object4
object4 to none</pre>
<p>Object4 (or the last object in an object array) checks to none, because all of the others are checking for it. So you see that if you use the unoptimized way, it turns out that you checking the collisions 2 times as much.</p>
<p>Download the .love file (typos fixed by the way): <a href="https://dl.dropbox.com/u/105405645/Blog/LOVE2D%20Files/circle-collision.love">circle-collision.love</a>. As always, you can view the source code by either changing the extension to .zip and opening it, or opening it in an archive program like 7-zip.</p>
<p><span style="color: rgb(0, 0, 0); font-size: 1.357em; line-height: 1.5;">And we are done!</span></p>
<p>Alright, now we are done with the collision resolution. I hope you learned something here, and if you have any questions or suggestions, please comment below!</p>
<p>Also, a credit goes to vrld for posting some suggestions too :).</p>
</div></div></div><div class="field field-name-field-addthiswidget field-type-addthis field-label-hidden"><div class="field-items"><div class="field-item even"><div class="addthis_toolbox addthis_default_style addthis_32x32_style " addthis:title="" addthis:url="http://blogs.love2d.org/content/circle-collisions"><a href="http://www.addthis.com/bookmark.php?v=300" class="addthis_button_google_plusone_share"></a>
<a href="http://www.addthis.com/bookmark.php?v=300" class="addthis_button_facebook"></a>
<a href="http://www.addthis.com/bookmark.php?v=300" class="addthis_button_hackernews"></a>
<a href="http://www.addthis.com/bookmark.php?v=300" class="addthis_button_reddit"></a>
<a href="http://www.addthis.com/bookmark.php?v=300" class="addthis_button_twitter"></a>
<a href="http://www.addthis.com/bookmark.php?v=300" class="addthis_button_"></a>
</div>
</div></div></div>Mon, 11 Mar 2013 06:11:13 +0000substitute54116 at http://blogs.love2d.orghttp://blogs.love2d.org/content/circle-collisions#comments