Bob Dylan

Calculating direction after circle-circle collision

Recommended Posts

I know how to calculate the scalar of the velocity vector after a collision with 2 circles (as per this link: https://gamedevelopment.tutsplus.com/tutorials/how-to-create-a-custom-2d-physics-engine-the-basics-and-impulse-resolution--gamedev-6331)

These circles cannot rotate and do not have friction but can have different masses, however I cannot seem to find out any way to find the unit vector that I need to multiply the scalar of velocity by to get the new velocity of the particles after the collision.

I also know how to check if 2 circles are colliding.

Also, I am only dealing with this in a purely "maths-sense" (ie. the circles have a center and a radius), and would like to know how I can represent these circles on the screen in python 3.0.

The vector class:

class Vector():
def __init__(self,x,y):
    self.x = x
    self.y = y

def add(self, newVector):
    return Vector(self.x+newVector.x, self.y+newVector.y)

def subtract(self,newVector):
    return Vector(self.x-newVector.x, self.y-newVector.y)

def equals(self, newVector):
    return Vector(newVector.x,newVector.y)

def scalarMult(self, scalar):
    return Vector(self.x*scalar, self.y*scalar)

def dotProduct(self, newVector):
    return (self.x*newVector.x)+(self.y*newVector.y

def distance(self):
    return math.sqrt((self.x)**2 +(self.y)**2)

The circle class:

class Particles():
def __init__(self,currentPos, oldPos, accel, dt,mass, center, radius):
    self.currentPos = currentPos
    self.oldPos = oldPos
    self.accel = accel
    self.dt = dt
    self.mass = mass
    self.center = center
    self.radius = radius

def doVerletPosition(currentPos, oldPos, accel, dt):
    a = currentPos.subtract(oldPos)
    b = currentPos.add(a)
    c = accel.scalarMult(dt)
    d = c.scalarMult(dt)
    return d.add(b)

def doVerletVelocity(currentPos, oldPos, dt):
    deltaD = (currentPos.subtract(oldPos))
    return deltaD.scalarMult(1/dt)

def collisionDetection(self, center, radius):
    xCenter = (self.radius).xComponent()
    yCenter = (self.radius).yComponent()
    xOther = radius.xComponent()
    yOther = radius.yComponent()
    if ((xCenter - xOther)**2 + (yCenter-yOther)**2 < (self.radius + radius)**2):
        return True
    else:
        return False

I do know about AABBs, but I am only using around 10 particles for now, and AABBs are not necessary now.

Share this post


Link to post
Share on other sites

Ah that is somehow easy Its not that hard:

- You take the difference between the two centers in world space: R = B - A

- Normalize that and you have your direction: N = R / |R|

- Calculate the penetration distance by subtracting the summed radius of both circles from the projected difference by the direction (btw: This distance is the length of the difference): d = (RadiusA+RadiusB) - Dot(R, N)

- Calculate relative velocity: VAB = VA - VB

- Calculate impulse strength: j = -(1 + e) * Dot(VAB, N) / (1/MassA + 1/MassB);

- Calculate and apply impulse on velocities:

VA -= j * (1 / MassA) * N

VB += j * (1 / MassB) * N

 

Thats one step. Next step is to resolve penetration. This is handled in the "how to create a custom 2D physics engine" series as well - so check that out.

Edited by Finalspace

Share this post


Link to post
Share on other sites
34 minutes ago, Dirk Gregorius said:

This obviously has not the dimensions of an impulse.

Right, its not an impulse at all! My bad. Its a minimum translation vector!

An impulse would be at the relative velocity projected along the direction divided by the sum of their masses.

Edited by Finalspace

Share this post


Link to post
Share on other sites
Quote

An impulse would be at the relative velocity projected along the direction divided by the sum of their masses.

Nope, impulse = mass * velocity. Not velocity divided by mass. You are also not taking the rotational effects into account. E.g. the inertia contribution seen by the impulse at the application point in the direction of the normal. This is sometimes referred to as effective mass.

float EffectiveMassInv = 1/m1 + 1/m2 + dot(n, InvI1 * n) + dot(n, InvI2 * n);
float EffectiveMass = 1 / EffectiveMassInv;

Then you can compute equal and opposite impulses on each body like

float Lambda = EffectiveMass * -RelVelocity;
Body1->ApplyImpulseAt( -n * Lambda, ContactPoint );
Body2->ApplyImpulseAt( n, Lambda, ContactPoint );

 

 

Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites
On 17.10.2017 at 7:56 PM, Dirk Gregorius said:

Nope, impulse = mass * velocity. Not velocity divided by mass. You are also not taking the rotational effects into account. E.g. the inertia contribution seen by the impulse at the application point in the direction of the normal. This is sometimes referred to as effective mass.


float EffectiveMassInv = 1/m1 + 1/m2 + dot(n, InvI1 * n) + dot(n, InvI2 * n);
float EffectiveMass = 1 / EffectiveMassInv;

Then you can compute equal and opposite impulses on each body like


float Lambda = EffectiveMass * -RelVelocity;
Body1->ApplyImpulseAt( -n * Lambda, ContactPoint );
Body2->ApplyImpulseAt( n, Lambda, ContactPoint );

 

 

The OP dont care about rotations, so i havent included it. 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Announcements

  • Forum Statistics

    • Total Topics
      628330
    • Total Posts
      2982112
  • Similar Content

    • By Connor Rust
      I am currently attempting to make a navigation mesh for our 2D top down game, which is a multiplayer game using Node.js as the server communication. At the moment, I have implemented A* over an obstacle hardnessmap, which is awfully slow and laggy at times when we test our game on Heroku. I have been trying to find an algorithm to automatically generate the navmesh after map creation, instead of me having to do this manually. I am currently attempting to use Delaunay's Triangulation Divide and Conquer algorithm, but I am running into some issues. I have already asked a question on StackOverflow and am not getting many suggestions and help from it, so I figured I would come here. Is there another algorithm that might be better to use for the navmesh generation in comparison to Deluanay's Triangulation? My current implementation seems extremely buggy during the merge step and I cannot find the error. I have checked over the code countless times, comparing it to the description of the algorithm from http://www.geom.uiuc.edu/~samuelp/del_project.html. 
      My current code is this:
      class MapNode { constructor(x, y) { this.position = new Vector(x, y); this.neighbors = []; } distance(n) { return this.position.distance(n.position); } inNeighbor(n) { for (let i = 0; i < this.neighbors.length; i++) { if (this.neighbors[i] === n) return true; } return false; } addNeighbor(n) { this.neighbors = this.neighbors.filter((node) => node != n); this.neighbors.push(n); } addNeighbors(arr) { let self = this; arr.forEach((n) => self.neighbors.push(n)); } removeNeighbor(n) { this.neighbors = this.neighbors.filter((neighbor) => neighbor != n); } } class Triangle { constructor(p1, p2, p3) { this.p1 = p1; this.p2 = p2; this.p3 = p3; this.neighbors = []; } addNeighbors(n) { this.neighbors.push(n); } } function genSubMat(matrix, ignoreCol) { let r = []; for (let i = 0; i < matrix.length - 1; i++) { r.push([]); for (let j = 0; j < matrix[0].length; j++) { if (j != ignoreCol) r[i].push(matrix[i + 1][j]); } } return r; } function determinantSqMat(matrix) { if (matrix.length != matrix[0].length) return false; if (matrix.length === 2) return matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]; let det = 0; for (let i = 0; i < matrix.length; i++) { let r = genSubMat(matrix, i); let tmp = matrix[0][i] * determinantSqMat(r); if (i % 2 == 0) det += tmp; else det -= tmp; } return -det; } // if d is in the circle formed by points a, b, and c, return > 0 // d is on circle, return 0 // d is outside of circle, return < 0 function inCircle(a, b, c, d) { let arr = [a, b, c, d]; let mat = [ [], [], [], [] ]; for (let i = 0; i < arr.length; i++) { mat[i][0] = 1; mat[i][1] = arr[i].position.x; mat[i][2] = arr[i].position.y; mat[i][3] = arr[i].position.x * arr[i].position.x + arr[i].position.y * arr[i].position.y; } return determinantSqMat(mat); } function walkable(from, to, hardnessMap) { let diff = new Vector(to.x - from.x, to.y - from.y); if (Math.abs(diff.x) > Math.abs(diff.y)) diff.scale(Math.abs(1 / diff.x)); else diff.scale(Math.abs(1 / diff.y)); let current = new Vector(from.x + diff.x, from.y + diff.y); while (Math.round(current.x) != to.x || Math.round(current.y) != to.y) { if (hardnessMap[Math.floor(current.y)][Math.floor(current.x)] === 1) return false; current.x += diff.x; current.y += diff.y; } return true; } function getLowest(nodes) { let lowest = nodes[0]; for (let i = 1; i < nodes.length; i++) { if (nodes[i].position.y < lowest.position.y) lowest = nodes[i]; } return lowest; } // returns the angle between 2 vectors, if cw is true, then return clockwise angle between, // else return the ccw angle between. b is the "hinge" point function angleBetween(a, b, c, cw) { let ba = new Vector(a.position.x - b.position.x, a.position.y - b.position.y); let bc = new Vector(c.position.x - b.position.x, c.position.y - b.position.y); let v0 = new Vector(0, 1); let angleBA = v0.angleBetween(ba) * 180 / Math.PI; if (angleBA < 0) angleBA += 360; let angleBC = v0.angleBetween(bc) * 180 / Math.PI; if (angleBC < 0) angleBC += 360; let smallest = Math.min(angleBA, angleBC); let largest = Math.max(angleBA, angleBC); let angle = largest - smallest; return (cw) ? angle : 360 - angle; } function sortSmallestAngle(a, b, list, cw) { list.sort((m, n) => { let vab = new Vector(a.position.x - b.position.x, a.position.y - b.position.y); let vmb = new Vector(m.position.x - b.position.x, m.position.y - b.position.y); let vnb = new Vector(n.position.x - b.position.x, n.position.y - b.position.y); if (cw) return vab.angleBetween(vmb, cw) - vab.angleBetween(vnb, cw); else return vab.angleBetween(vnb, cw) - vab.angleBetween(vmb, cw); }); } // a is in list, b is in the other list function getPotential(a, b, list, cw) { sortSmallestAngle(b, a, list, cw); for (let i = 0; i < list.length - 1; i++) { let angle = angleBetween(b, a, list[i], cw); if (angle > 180) return false; else if (inCircle(a, b, list[i], list[i + 1]) <= 0) return list[i]; else { a.removeNeighbor(list[i]); list[i].removeNeighbor(a); } } let potential = list[list.length - 1]; if (potential) { let angle = angleBetween(a, b, potential, cw); if (angle > 180) return false; return potential; } return false; } function merge(leftNodes, rightNodes, leftBase, rightBase, hardnessMap) { leftBase.addNeighbor(rightBase); rightBase.addNeighbor(leftBase); let newLeft = leftNodes.filter((n) => n != leftBase); let newRight = rightNodes.filter((n) => n != rightBase); let potentialLeft = getPotential(leftBase, rightBase, newLeft, false); let potentialRight = getPotential(rightBase, leftBase, newRight, true); if (!potentialLeft && !potentialRight) return; else if (potentialLeft && !potentialRight) merge(newLeft, newRight, potentialLeft, rightBase, hardnessMap); else if (potentialRight && !potentialLeft) merge(newLeft, newRight, leftBase, potentialRight, hardnessMap); else { if (inCircle(leftBase, rightBase, potentialLeft, potentialRight) <= 0) merge(newLeft, newRight, potentialLeft, rightBase, hardnessMap); if (inCircle(leftBase, rightBase, potentialRight, potentialLeft) <= 0) merge(newLeft, newRight, leftBase, potentialRight, hardnessMap); } } // divide and conquer algorithm function delaunay(nodes, hardnessMap) { if (nodes.length <= 3) { for (let i = 0; i < nodes.length; i++) for (let j = 0; j < nodes.length; j++) if (i != j) nodes[i].addNeighbor(nodes[j]); return nodes; } else { nodes.sort((a, b) => { let tmp = a.position.x - b.position.x; if (tmp === 0) return b.position.y - a.position.y; return tmp; }); let l = nodes.length; let leftNodes; let rightNodes; if (l === 4) { leftNodes = delaunay(nodes.slice(0, 3), hardnessMap); rightNodes = delaunay(nodes.slice(3, 4), hardnessMap); } else { leftNodes = delaunay(nodes.slice(0, Math.floor(nodes.length / 2)), hardnessMap); rightNodes = delaunay(nodes.slice(Math.floor(nodes.length / 2), nodes.length), hardnessMap); } let leftBase = getLowest(leftNodes); let rightBase = getLowest(rightNodes); merge(leftNodes, rightNodes, leftBase, rightBase, hardnessMap); console.log("=============================MergeComplete================================"); return nodes; } }  
    • By Hilster
      Hello 2D Artists,
      I've started making a 2D Puzzle Adventure game for mobile and I'm looking for someone who would want in on creating assets for the game. The core of the programming is pretty much complete, you can walk within the grid laid out and push boxes, when there is an object on top of a pressure pad it will activate the linked objects or if there is one object with multiple linked pressure pads it requires you to activate all points for the object to become active. 

      The level iteration for the game is quick and simple, a Photoshop file that is made of individual pixels that represents objects is put into the game and it creates the level out of those pixels with the assigned objects.
      The objects that need sprites created so far is the character, box, pressure pad, door, trap door, the walls, the stairs and the tiled background.
      I intend to add more objects so the amount I'd like to add will be extended.
      My motivations for posting here is to have something that looks nice to be able to display on my portfolio, so if you're looking for a working game that you can place your art into and improve the look of your portfolio then we're in business.
      Please reply with a few past examples of your art below and I'll be in touch!
    • By suliman
      Hi!
      My game is coming along nicely and I would love some feedback.
      You play as one (or two in co-op) survivor that must travel the land and survive the infected hordes, looners and bandits. You stop in locations but are always pressed as the hordes will start pouring in. Collect resources (food, fuel, medical supplies and ammo) and weapons and head for the goal!
      Tips
      Always quickly switch to a melee weapon if running out of ammo Loot everything if you have time to loot, including cartrunks Choose locations that have the loot you need (such as gas station for fuel) Try to avoid running out of fuel or having your car break down. Walking is dangerous! Download (50 MB, works with windows only, you DON'T need dropbox to download):
      Damnation road (beta 2)




    • By MeeMaster
      I am a beginner in the Game Dev business, however I plan to build a futuristic MMO with some interesting mechanics.
      However, I have some doubts about shooting mechanics that I chose for this game and would like to know your opinion on this. The mechanic goes as follows:
      - Each gun would have it's damage-per-shot value
      - Each gun would have it's shots-per-second value
      - Each gun would have it's accuracy rating
      Now the question is: how to calculate the output damage? I have three available options:
      1) Calculate the chance of each shot hitting the target (per-shot accuracy)
      2) Multiply the damage output of a weapon by it's accuracy rating (weapon with 50% accuracy deals 50% of it's base damage)
      3) Don't use accuracy at all and just adjust the weapon damage output
      Which of these three mechanics would you like to see in a game? Mind, this will be an MMO game, so it will have lock-on targets, AoE effects and all that jazz.
    • By Vityou
      I am thinking of making a game like screeps, where you use actual programming to control the game, but I'm not sure about the best way to do this.  I'm probably going to use lua or javascript for the language, and I would like to represent units in the game as objects that the user can command and modify, with restraints based on the actual game, like how screeps does it.  However, I am not sure how to get started.  I think I have to use C or C++ and then embed the languages, but I'm not sure where to go from there, for example, having objects in lua actually correspond to units in the game.  Are there any resources that explain how to do this?  Also, I'm not concerned about the graphics of the game whatsoever, it might as well just be a text adventure as far as I'm concerned, I just want the user to be able to control the game through scripting.
  • Popular Now