//calculate the vertical velocity at the moment of impact private float Y_ImpactVelocity() {// (V)^2 = (V°)^2 - 2 * g * X° float Y_impactVelSquared = velocityPreviousFrame.getY() * velocityPreviousFrame.getY() - 2 * gravity.getY() * (position.DistanceFromPlane(ground) - radius); if (Y_impactVelSquared < 0) { Y_impactVelSquared = 0; } return(Mathf.Sqrt(Y_impactVelSquared)); }
private void FixedUpdate() { //simulation starts upon pressing the spacebar if (Input.GetKeyDown(KeyCode.Space) == true) { startSimulation = true; } if (!startSimulation) { return; } float t = Time.fixedDeltaTime; MyVector3 previousLinearMomentum = linearMomentum; MyVector3 linearMomentumVariation = linearMomentum.UnitVector().Scale( -SnookerBall.NormalForce * SnookerBall.frictionCoeff * t); if (linearMomentumVariation.Magnitude() >= linearMomentum.Magnitude()) {//the decrease in linear momentum is bigger than the linear momentum itself linearMomentum = MyVector3.Zero(); } else { linearMomentum = MyVector3.Add(linearMomentum, linearMomentumVariation); } MyVector3 avgLinearMomentum = MyVector3.Add(linearMomentum, previousLinearMomentum).Scale(0.5f); MyVector3 avgVelocity = avgLinearMomentum.Scale(1 / SnookerBall.mass); MyVector3 newPosition = MyVector3.Add(position, avgVelocity.Scale(t)); foreach (Plane plane in cushions) { float distanceFromPlane = newPosition.DistanceFromPlane(plane) - SnookerBall.radius; if (distanceFromPlane < 0) {//the ball has gone beyond the boundary //collision detected: the collision has happened anytime between the previous frame and this one. //find how much time has past since the collision float impactTime = ImpactTime( MyVector3.Dot(previousLinearMomentum, plane.Normal()), -MyVector3.Dot(avgLinearMomentum.UnitVector(), plane.Normal()) * SnookerBall.NormalForce * SnookerBall.frictionCoeff, position.DistanceFromPlane(plane) - SnookerBall.radius); linearMomentumVariation = previousLinearMomentum.UnitVector().Scale( -SnookerBall.NormalForce * SnookerBall.frictionCoeff * impactTime); //reflect the linear momentum along the normal of the plane linearMomentum = MyVector3.Add(previousLinearMomentum, linearMomentumVariation).Reflect(plane.Normal()); //position ball just within the boundaries newPosition = MyVector3.Add(newPosition, plane.Normal().Scale(-distanceFromPlane)); } } position = newPosition; MoveGameObject(); }
//draw rays in the scene private void Update() { int lines = 36; MyVector3 normal = plane.Normal(); MyVector3 p = MyVector3.Subtract(vector, normal.Scale(vector.DistanceFromPlane(plane))); for (int i = 0; i < lines; i++) { Vector3 v = new Vector3((float)Math.Cos(Math.PI * 2 * i / lines), 0, (float)Math.Sin(Math.PI * 2 * i / lines)); v = Quaternion.FromToRotation(Vector3.up, new Vector3(normal.getX(), normal.getY(), normal.getZ())) * v; Debug.DrawRay(new Vector3(p.getX(), p.getY(), p.getZ()), 1000 * v, Color.magenta); } Debug.DrawRay(Vector3.zero, new Vector3(vector.getX(), vector.getY(), vector.getZ()), Color.black); }
public void Calculate() { try {//parse strings into floating point numbers float v_x = Single.Parse(vect_inputs[0].text); float v_y = Single.Parse(vect_inputs[1].text); float v_z = Single.Parse(vect_inputs[2].text); float plane_a = Single.Parse(plane_inputs[0].text); float plane_b = Single.Parse(plane_inputs[1].text); float plane_c = Single.Parse(plane_inputs[2].text); float plane_d = Single.Parse(plane_inputs[3].text); if (plane_a == 0 && plane_b == 0 && plane_c == 0) { throw new Exception("Coefficients cannot all be zero!"); } float num = Single.Parse(scalar_input.text); if (num < 0) { throw new Exception("Only positive tolerance allowed!"); } vector = new MyVector3(v_x, v_y, v_z); plane = new Plane(plane_a, plane_b, plane_c, plane_d); tolerance = num; } catch (Exception e) {//parsing failed Debug.LogError(e.Message); vect_inputs[0].text = ""; vect_inputs[1].text = ""; vect_inputs[2].text = ""; plane_inputs[0].text = ""; plane_inputs[1].text = ""; plane_inputs[2].text = ""; plane_inputs[3].text = ""; scalar_input.text = ""; return; } //printe vectors to the scene distanceFromPlane.text = vector.DistanceFromPlane(plane).ToString("0.000"); isOnPlane.text = vector.IsOnPlane(plane, tolerance).ToString(); }
private void FixedUpdate() { //simulation start upon press of the spacebar if (Input.GetKeyDown(KeyCode.Space) == true) { startSimulation = true; } if (!startSimulation) { return; } if (fullStop) { return; //simulation ended } float t = Time.fixedDeltaTime; if (stopBouncing) { if (pureRolling) {//case 1: the ball is rolling on the floor without sliding float dumping = 1 - (5 - 4 / (1 + mass)) * (floorFriction / (4 * floorFriction + 6)) * (1 + 49 / (1 + 10 * Mathf.Abs(velocityPreviousFrame.Magnitude()))) * t; //arbitrary dumping factor. No real physical meaning. velocityCurrentFrame = velocityPreviousFrame.Scale(dumping); if (velocityCurrentFrame.Magnitude() < 0.01f) {//the velocity is low enough to be neglectable velocityCurrentFrame = MyVector3.Zero(); fullStop = true; } //calculate the avarage velocity between two consecutive frames and use it to calculate the new position MyVector3 avgVelocity = MyVector3.Lerp(velocityPreviousFrame, velocityCurrentFrame, 0.5f); MyVector3 displacement = avgVelocity.Scale(t); position = MyVector3.Add(position, displacement); MoveGameObject(); angularMomentumCurrentFrame = MyVector3.Cross(velocityCurrentFrame, new MyVector3(0, -1, 0)).Scale(momentOfInertia / radius); RotateGameObject(t); velocityPreviousFrame = velocityCurrentFrame; angularMomentumPreviousFrame = angularMomentumCurrentFrame; } else {//case 2: the ball is rolling on the floor with sliding velocityCurrentFrame = HorizontalVelocityAfterSliding(t); //calculate the avarage velocity between two consecutive frames and use it to calculate the new position MyVector3 avgVelocity = MyVector3.Lerp(velocityPreviousFrame, velocityCurrentFrame, 0.5f); MyVector3 displacement = avgVelocity.Scale(t); position = MyVector3.Add(position, displacement); MoveGameObject(); //angularMomentumCurrentFrame set in the X_VelocityAfterSliding() function RotateGameObject(t); velocityPreviousFrame = velocityCurrentFrame; angularMomentumPreviousFrame = angularMomentumCurrentFrame; } } else {//the ball is still bouncing MyVector3 deltaVel = gravity.Scale(t); velocityCurrentFrame = MyVector3.Add(velocityPreviousFrame, deltaVel); //calculate the avarage velocity between two consecutive frames and use it to calculate the new position MyVector3 avgVelocity = MyVector3.Lerp(velocityPreviousFrame, velocityCurrentFrame, 0.5f); MyVector3 displacement = avgVelocity.Scale(t); MyVector3 newPosition = MyVector3.Add(position, displacement); float distanceFromGround = newPosition.DistanceFromPlane(ground) - radius; if (distanceFromGround < 0) { //case 3: the ball is bouncing and has partially fallen balow the plane of the ground float impactVel_y = Y_ImpactVelocity(); float velocityCurrentFrame_y = impactVel_y * restitution; //upwards speed after bounce if (velocityCurrentFrame_y < tresholdStopBouncing) { //the speed after the bounce is small enough to be ignored stopBouncing = true; velocityCurrentFrame_y = 0; } MyVector3 horzVelocityCurrentFrame = HorizontalVelocityAfterSliding(t + 0.001f * impactVel_y + 8 * t * (1 - 1 / Mathf.Sqrt(1 + mass))); //position ball on top of the ground position = MyVector3.Add(newPosition, ground.Normal().Scale(-distanceFromGround)); MoveGameObject(); //angularMomentumCurrentFrame set in the X_VelocityAfterSliding() function RotateGameObject(t); angularMomentumPreviousFrame = angularMomentumCurrentFrame; velocityPreviousFrame = new MyVector3(horzVelocityCurrentFrame.getX(), velocityCurrentFrame_y, horzVelocityCurrentFrame.getZ()); } else {//case 4: the ball is bouncing and is in mid-air velocityPreviousFrame = velocityCurrentFrame; position = newPosition; MoveGameObject(); RotateGameObject(t); } } }