//this function will adjust the position of the 2 points connected by this Link to keep the distance between them //as the restingDistance (constraint of this link) public void solve() { float diffX = point1.getPosition().x - point2.getPosition().x; float diffY = point1.getPosition().y - point2.getPosition().y; //get the current distance between those 2 points float distance = CollisionCalculation.getDistance(point1.getPosition().x, point1.getPosition().y, point2.getPosition().x, point2.getPosition().y); //find the ratio of how far between the actual distance and the resting distance float ratioDiff = (restingDistance - distance) / distance; //an multiplier to make the adjustment more natural float stiffness = 1f; float scalarP1 = (0.5f) * stiffness; // mass1/(mass1+mass2) = 0.5, since for our task point have same mass float scalarP2 = stiffness - scalarP1; //move the point1 toward point2 to attain the resting distance constraint float newPosX = point1.getPosition().x + scalarP1 * ratioDiff * diffX; float newPosY = point1.getPosition().y + scalarP1 * ratioDiff * diffY; point1.setPosition(new Vector3(newPosX, newPosY)); //move the point2 toward point1 to attain the resting distance constraint newPosX = point2.getPosition().x - scalarP2 * ratioDiff * diffX; newPosY = point2.getPosition().y - scalarP2 * ratioDiff * diffY; point2.setPosition(new Vector3(newPosX, newPosY)); }
//function for changing the string motion after it's collided with cannon ball (the string shouldn't intersect will cannon ball) //the paramters are the cannon ball and the balloon whose string is collided with, and also the position of the two end of the line segment of the string void movingAfterCannonBallCollision(CannonBall ball, Balloon balloon, Vector3 startPoint, Vector3 endPoint) { //find the 2 Point objects of the balloon string according to the startPoint and endPoint //Balloon.Point point1 = null; Balloon.Point point2 = null; ArrayList strPoints = balloon.getStringPoints(); for (int i = 0; i < strPoints.Count; i++) { /* * if( ((Balloon.Point) strPoints[i]).getPosition().Equals(startPoint)) * { * point1 = (Balloon.Point) strPoints[i]; * }*/ if (((Balloon.Point)strPoints[i]).getPosition().Equals(endPoint)) { point2 = (Balloon.Point)strPoints[i]; } } Vector2 unitNormal = CollisionCalculation.getNormal(startPoint.x, startPoint.y, point2.getPosition().x, point2.getPosition().y); //calculate the unit normal of the line float epsilon = 0.1f; //the coefficient of restitution (0~1, when it's 1 we have perfect bouncing) float mass = 1f; //mass of the string float VnCollided = unitNormal.x * ball.getVelocity().x + unitNormal.y * ball.getVelocity().y; //Vn-: the normal component of collided velocity = dot product of normal and velocity float j = -(1 + epsilon) * mass * VnCollided; //the impulse scalar j = -(1+epsilon) * m * Vn- Vector2 impulse = -j * unitNormal; //the force will apply to the line, not the cannon ball, so we use negative of the impulse point2.setAcceleration((impulse / mass)); //update the acceleration based on the impulse }
//this function will handle the collision between terrain and ballon void handleBalloonTerrainCollision(Balloon b, Vector3[] terrainLinePoints) { Vector3[] bodyPositions = b.getBodyLinePositions(); Vector3[] stringPositions = b.getStringLinePositions(); for (int j = 0; j < terrainLinePoints.Length - 1; j++) { //iterate through the Points to check if the cannon ball collided any line segment of the balloon body for (int i = 0; i < bodyPositions.Length - 1; i++) { //check if the Point of balloon is collided with any terrain line segment //if the balloon is collided with terrain //if (CollisionCalculation.isPointOnLine(bodyPositions[i].x, bodyPositions[i].y, terrainCollisionPoints[j].x, terrainCollisionPoints[j].y, terrainCollisionPoints[j + 1].x, terrainCollisionPoints[j + 1].y)) if (CollisionCalculation.isCircleCollidesLine(terrainCollisionPoints[j].x, terrainCollisionPoints[j].y, terrainCollisionPoints[j + 1].x, terrainCollisionPoints[j + 1].y, bodyPositions[i].x, bodyPositions[i].y, 0.05f)) { //handle the collision movingAfterLineCollision(b, bodyPositions[i], terrainCollisionPoints[j], terrainCollisionPoints[j + 1]); } } //check if the Point of string is collided with any terrain line segment for (int i = 0; i < stringPositions.Length - 1; i++) { //if the string is collided with terrain if (CollisionCalculation.isCircleCollidesLine(terrainCollisionPoints[j].x, terrainCollisionPoints[j].y, terrainCollisionPoints[j + 1].x, terrainCollisionPoints[j + 1].y, stringPositions[i].x, stringPositions[i].y, 0.05f)) { //handle the collision movingAfterLineCollision(b, stringPositions[i], terrainCollisionPoints[j], terrainCollisionPoints[j + 1]); } } } }
//function for changing the balloon point motion after it's collided with line (the balloon Point shouldn't intersect will terrain) //the paramters are the balloon and the points of a line which the balloon is collided with, and also the position of the two end of the line segment void movingAfterLineCollision(Balloon balloon, Vector3 collidedPoint, Vector3 lineStart, Vector3 lineEnd) { //find the 2 Point objects of the balloon string according to the startPoint and endPoint //Balloon.Point point1 = null; Balloon.Point cPoint = null; ArrayList strPoints = balloon.getStringPoints(); ArrayList bodyPoints = balloon.getBodyPoints(); //iterate to find the contact point for (int i = 0; i < strPoints.Count; i++) { if (((Balloon.Point)strPoints[i]).getPosition().Equals(collidedPoint)) { cPoint = (Balloon.Point)strPoints[i]; } } for (int i = 0; i < bodyPoints.Count; i++) { if (((Balloon.Point)bodyPoints[i]).getPosition().Equals(collidedPoint)) { cPoint = (Balloon.Point)bodyPoints[i]; } } Vector2 unitNormal = CollisionCalculation.getNormal(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y); //calculate the unit normal of the line segment float epsilon = 1f; //the coefficient of restitution (0~1, when it's 1 we have perfect bouncing) float mass = 0.1f; //mass of the string float VnCollided = unitNormal.x * cPoint.getMovingDirection().x + unitNormal.y * cPoint.getMovingDirection().y; //Vn-: the normal component of collided velocity = dot product of normal and velocity float multiplier = 30f; float j = -(1 + epsilon) * mass * -VnCollided * multiplier; //the impulse scalar j = -(1+epsilon) * m * Vn- Vector2 impulse = -j * unitNormal; //the force will apply to the line, not the cannon ball, so we use negative of the impulse foreach (Balloon.Point p in bodyPoints) { p.setAcceleration((impulse / mass)); //update the acceleration based on the impulse } foreach (Balloon.Point p in strPoints) { p.setAcceleration((impulse / mass)); //update the acceleration based on the impulse } //cPoint.setAcceleration((impulse / mass)); //update the acceleration based on the impulse }
//function for the bouncing solution of collison, taking a ball and a line represented by two points //as parameters void bouncingAfterTerrainCollision(CannonBall ball, float x1, float y1, float x2, float y2) { //calculate bouncing velocity using the formula V+ = V- + dotProduct(j,n)/m, //where V- is the velocity when colliding, V+ is the velocity after colliding, //dotProduct(j,n) = J which is the impulse, and j = -(1+epsilon) * m * Vn- where //Vn- = dotProduct(V-,n). Vector2 unitNormal = CollisionCalculation.getNormal(x1, y1, x2, y2); //calculate the unit normal of the line float epsilon = 0.55f; //the coefficient of restitution (0~1, when it's 1 we have perfect bouncing) float mass = 1f; //mass of the ball float VnCollided = unitNormal.x * ball.getVelocity().x + unitNormal.y * ball.getVelocity().y; //Vn-: the normal component of collided velocity = dot product of normal and velocity float j = -(1 + epsilon) * mass * VnCollided; //the impulse scalar j = -(1+epsilon) * m * Vn- Vector2 impulse = j * unitNormal; //J ball.addVelocity(impulse / mass); //update the velocity based on the impulse (V+ = V- + impulse/mass) }
//function to detect and handle collision bewtween cannon ball and collision lines (water, terrain and balloons) //taking one cannon ball and an array of points of the line (line with corners) as paramters, detect the collsion void handleCannonBallCollision(CannonBall ball, Vector3[] linePoints, ColliderType type) { GameObject ballObject = ball.GetGameObject(); //check that if any line segment of the line is collided with the cannon ball for (int i = 0; i < linePoints.Length - 1; i++) { //if the line which the ball is colliding with is terrain if (type == ColliderType.TERRAIN) { // if the cannon ball is collided with the line if (CollisionCalculation.isCircleCollidesLine(linePoints[i].x, linePoints[i].y, linePoints[i + 1].x, linePoints[i + 1].y, ballObject.transform.position.x, ballObject.transform.position.y, ball.getRadius())) { //if the ball is colliding with horizontal ground if (linePoints[i].y - linePoints[i + 1].y == 0) { //apply an inverse acceleration to the gravity to cancel out the gravity ball.updateVelocity(Time.deltaTime, new Vector2(0, -gravity)); //apply a friction to the ball when rolling on horizontal ground applyFrictionOfGround(ball); } else //when the ball is not colliding with the horizontal terrain, push it back by a small amount to avoid triggering continuous collision detection { //push the ball away from the terrain line in its normal vector direction by a small //amount to avoid penetration Vector2 unitNormal = CollisionCalculation.getNormal(linePoints[i].x, linePoints[i].y, linePoints[i + 1].x, linePoints[i + 1].y); ball.translatePosition(Time.deltaTime, unitNormal * 1); } //handle the collision on terrain bouncingAfterTerrainCollision(ball, linePoints[i].x, linePoints[i].y, linePoints[i + 1].x, linePoints[i + 1].y); } } else //if the line which the ball is colliding with is water { // if the cannon ball is collided with the line if (CollisionCalculation.isCircleCollidesLine(linePoints[i].x, linePoints[i].y, linePoints[i + 1].x, linePoints[i + 1].y, ballObject.transform.position.x, ballObject.transform.position.y, ball.getRadius())) { //handle the collision on water disappearAfterWaterCollision(ball, cannonBallsList); } } } }
//this function will handle the collision between cannon ball and any ballon body void handleBalloonCollision(CannonBall cb, ArrayList balloonList) { //vectors that store the position of points that construct the balloons Vector3[] bodyPositions; Vector3[] stringPositions; //get every ballon as line segment for (int k = 0; k < balloonsList.Count; k++) { bodyPositions = ((Balloon)balloonsList[k]).getBodyLinePositions(); stringPositions = ((Balloon)balloonsList[k]).getStringLinePositions(); //iterate through the Points to check if the cannon ball collided any line segment of the balloon string for (int i = 0; i < stringPositions.Length - 1; i++) { // if the cannon ball is collided with the line segment of a balloon string if (CollisionCalculation.isCircleCollidesLine(stringPositions[i].x, stringPositions[i].y, stringPositions[i + 1].x, stringPositions[i + 1].y, cb.GetGameObject().transform.position.x, cb.GetGameObject().transform.position.y, cb.getRadius())) { //handle the collision by moving the string to avoid intersecting with the cannon ball movingAfterCannonBallCollision(cb, ((Balloon)balloonsList[k]), stringPositions[i], stringPositions[i + 1]); } } //iterate through the Points to check if the cannon ball collided any line segment of the balloon body for (int i = 0; i < bodyPositions.Length - 1; i++) { // if the cannon ball is collided with the line segment of a balloon body if (CollisionCalculation.isCircleCollidesLine(bodyPositions[i].x, bodyPositions[i].y, bodyPositions[i + 1].x, bodyPositions[i + 1].y, cb.GetGameObject().transform.position.x, cb.GetGameObject().transform.position.y, cb.getRadius())) { //handle the collision by destroy the balloon which has been collided destroyAfterCannonBallCollision(((Balloon)balloonsList[k]), balloonList); break; //break since the balloon with current positions array is destroyed, so no further check has to be made } } } }
//the Balloon class will take a Line gameObject and a spawnPosition for the balloon as parameter public Balloon(GameObject balloonBodyLine, GameObject balloonStringLine, Vector3 spawnPosition) { balloonBody = balloonBodyLine; balloonString = balloonStringLine; //our balloon will have six points for the body (one point is connecting to the string) //and 4 points for the string (one point is the body-string connecting point) //The spawnPosition will be the position of the top motorPoint of this balloon. // w1=w2 // | // v // . // h1-> / \ // h2-> | | // h1-> \ / // | // | // | // // The invisible Links between points to keep the balloon in shape are designed in a separate PDF file //Create Points: //define some distance for the balloon shape float h1 = 0.125f; //0.0625f 0.125f 0.10525f float h2 = 0.25f; float w1 = 0.2105f; float strLen = 0.15f; //balloon body points Point topP = new Point(spawnPosition); motorPoint = topP; Point leftP1 = new Point(new Vector3(spawnPosition.x - w1, spawnPosition.y - h1)); Point rightP1 = new Point(new Vector3(spawnPosition.x + w1, spawnPosition.y - h1)); Point leftP2 = new Point(new Vector3(leftP1.getPosition().x, leftP1.getPosition().y - h2)); Point rightP2 = new Point(new Vector3(rightP1.getPosition().x, rightP1.getPosition().y - h2)); Point botP = new Point(new Vector3(spawnPosition.x, spawnPosition.y - 2 * h1 - h2)); //string points Point str1 = new Point(new Vector3(spawnPosition.x, botP.getPosition().y - strLen)); Point str2 = new Point(new Vector3(spawnPosition.x, str1.getPosition().y - strLen)); Point str3 = new Point(new Vector3(spawnPosition.x, str2.getPosition().y - strLen)); Point str4 = new Point(new Vector3(spawnPosition.x, str3.getPosition().y - strLen)); //Add all body points into array list in loop order, first element will connected with the last element later //the first element is the connecting point between balloon body and the string balloonBodyPoints.Add(botP); balloonBodyPoints.Add(leftP2); balloonBodyPoints.Add(leftP1); balloonBodyPoints.Add(topP); balloonBodyPoints.Add(rightP1); balloonBodyPoints.Add(rightP2); //Add all string points into array list, the first point in the arrayList will be connected to the first point in the //balloon body arrayList balloonStringPoints.Add(str1); balloonStringPoints.Add(str2); balloonStringPoints.Add(str3); balloonStringPoints.Add(str4); //Use Line object to draw the ballon shape: Vector3[] bodyPoints = new Vector3[6]; for (int i = 0; i < balloonBodyPoints.Count; i++) { bodyPoints[i] = ((Point)balloonBodyPoints[i]).getPosition(); } //we have to connect the last point with the fist point //bodyPoints[6] = ((Point)balloonBodyPoints[0]).getPosition(); bodyLineRenderer = balloonBodyLine.GetComponent <LineRenderer>(); bodyLineRenderer.positionCount = bodyPoints.Length; //set the number of vertices this line have, including two end of the line bodyLineRenderer.numCornerVertices = 6; //higher value will smooth the corner //draw the balloon body bodyLineRenderer.SetPositions(bodyPoints); //Use Line object to draw the string shape attached to the balloon: Vector3[] stringPoints = new Vector3[5]; //the first point of the string is the connecting point to the balloon body stringPoints[0] = ((Point)balloonBodyPoints[0]).getPosition(); for (int i = 1; i < stringPoints.Length; i++) { stringPoints[i] = ((Point)balloonStringPoints[i - 1]).getPosition(); } stringLineRenderer = balloonStringLine.GetComponent <LineRenderer>(); stringLineRenderer.positionCount = stringPoints.Length; //set the number of vertices this line have, including two end of the line stringLineRenderer.numCornerVertices = 6; //higher value will smooth the corner //draw the string stringLineRenderer.SetPositions(stringPoints); //add constraints as links to keep the hexagon convex float crossLength = h2 / Mathf.Sin(30 * Mathf.PI / 180); //the length between two opposite angles of this hexagon leftP1.addLink(rightP2, crossLength); leftP2.addLink(rightP1, crossLength); topP.addLink(botP, crossLength); botP.addLink(topP, crossLength); rightP2.addLink(leftP1, crossLength); rightP1.addLink(leftP2, crossLength); //add constraints as links for the length between each vertices of the balloon (hexagon) topP.addLink(leftP1, h2); leftP1.addLink(topP, h2); leftP1.addLink(leftP2, h2); leftP2.addLink(leftP1, h2); leftP2.addLink(botP, h2); botP.addLink(leftP2, h2); botP.addLink(rightP2, h2); rightP2.addLink(botP, h2); rightP2.addLink(rightP1, h2); rightP1.addLink(rightP2, h2); rightP1.addLink(topP, h2); topP.addLink(rightP1, h2); //add constraints as Links for the points of this balloon string (constraints are shown in the separate PDF file) float str_balloonAngle = 120 * Mathf.PI / 180; //str1.addLink(leftP2, CollisionCalculation.cosineFormula(str1.getPosition(), botP.getPosition(), leftP2.getPosition(), str_balloonAngle)); botP.addLink(str1, strLen); str1.addLink(botP, strLen); str1.addLink(str2, strLen); str2.addLink(str1, strLen); str2.addLink(str3, strLen); str3.addLink(str2, strLen); str3.addLink(str4, strLen); str4.addLink(str3, strLen); //botP.addLink(str2, 2*strLen); //str2.addLink(botP, 2*strLen); str2.addLink(leftP2, CollisionCalculation.cosineFormula(str2.getPosition(), botP.getPosition(), leftP2.getPosition(), str_balloonAngle)); leftP2.addLink(str2, CollisionCalculation.cosineFormula(str2.getPosition(), botP.getPosition(), leftP2.getPosition(), str_balloonAngle)); str2.addLink(rightP2, CollisionCalculation.cosineFormula(str2.getPosition(), botP.getPosition(), rightP2.getPosition(), str_balloonAngle)); rightP2.addLink(str2, CollisionCalculation.cosineFormula(str2.getPosition(), botP.getPosition(), rightP2.getPosition(), str_balloonAngle)); }