void Update() { if (parachuteIsOpened) { if (parachute.localScale.magnitude < parachuteStrSqale.magnitude) { parachute.localScale *= 1.0f + 5.0f * Time.deltaTime; for (i = 5; i < 11; i++) { joints [i].frequency = 0f; } } else { parachute.localScale = parachuteStrSqale; } } time += Time.deltaTime; if (time > 100000.0f) { time = 0.0f; } for (int i = 0; i < hipSuitPoints.Length; i++) { hipSuitPoints [i].localRotation = Quaternion.Euler(hipSuitRotations [i] + suitMagnitude * Mathf.Sin(suitFrequency * time + ((float)i)) * (Mathf.PI / 2.0f) * Vector3.right); } for (int i = 0; i < armSuits.Length; i++) { armSuits [i].localRotation = Quaternion.Euler(armSuitRotations [i] + 0.75f * suitMagnitude * Mathf.Sin(suitFrequency * time + ((float)i)) * (Mathf.PI / 2.0f) * Vector3.right); } if (!posController.inAnimate) { foreach (var item in joints) { item.AnimateJoint(); } } VelocityControl(); if (posController.NewPoseName == "Salto" || posController.NewPoseName == "From Salto") { velocity = velocityY * Vector3.up + velocityZ * VectorOperator.getProjectXZ(velocity.normalized, true); } else if (posController.NewPoseName == "Open parachute" || (parachuteIsOpened && posController.NewPoseName == "ParachuteDown") || (parachuteIsOpened && posController.NewPoseName == "ParachuteUp") || (parachuteIsOpened && posController.NewPoseName == "Parachute right") || (parachuteIsOpened && posController.NewPoseName == "Parachute left")) { velocity = velocityY * Vector3.up - velocityZ * root.up; if (posController.NewPoseName == "ParachuteDown") { velocity = velocityY * Vector3.up - velocityZ * root.up * 2f; } } else { velocity = velocityY * Vector3.up + velocityZ * root.forward; } velocity *= 9.0f; transform.position += velocity * Time.deltaTime; transform.Rotate(rotationY * Time.deltaTime * Vector3.up); }
// this function is doing awefully little, only works some sounds and work with the main ball // in here, we use reserve velocity all we want but NEVER set it void OnCollisionEnter(Collision collision) { // dajiang physics 2 // we need angular velocity and real velocity for both items before. // we need frictions of the 2 contact surfaces (this is already known through the layerName). // reserveVelocity stuff is the backup, which I will NOT change in this function but only change in fixedupdate // we need to make sure the exit velocities of the 2 balls are perpendicular to each other // you can and do have an initial angular velocity, and in some cases it immediately matches with your real velocity // we can set a flag so we know if the real rigidbody.velocity is modified upon impact or not at each frame // if it has not been impacted, we will swap in the new values // if it has been impacted, we will apply the new value on top of it // velocity of the rigidbody right now is already calculated by unity for after the collision // if we want to update it any further (or in case of wall, not updated), we can do our calculation here. // angular is immediately reconciled for wall collisions and should be reconciles quickly for normal hits except for skidding (which can last some time). string layerName = LayerMask.LayerToName(collision.collider.gameObject.layer); // An important pre-condition // If we get a stationary ball to ball or ball to wall collision due to initial game setting, we need to ignore those collisions because they do crazy things if ((layerName == "Wall" || layerName == "Ball" || layerName == "MainBall") && collision.relativeVelocity.magnitude > 0.01f) { if (layerName == "Ball" || layerName == "MainBall") // ballllzzz { BallController otherBall = collision.collider.gameObject.GetComponent <BallController>(); if (otherBall && otherBall.id < id) { // sound portion for ball on ball float b_b_volume = Mathf.Clamp01(collision.relativeVelocity.magnitude / cueController.ballMaxVelocity); int b_b_index = 0; if (b_b_volume > 0.66f) { b_b_index = Random.Range((int)MusicClip.B_B_Hard_0, (int)MusicClip.B_B_Hard_2 + 1); } else if (b_b_volume > 0.33f) { b_b_index = Random.Range((int)MusicClip.B_B_Mid_0, (int)MusicClip.B_B_Mid_1 + 1); } else { b_b_index = Random.Range((int)MusicClip.B_B_Weak_0, (int)MusicClip.B_B_Weak_1 + 1); } GameManager_script.Instance().PlaySound(b_b_index, false, b_b_volume); } // real physics stuff starts here if (inMove && cueController && collision.collider.GetComponent <Rigidbody>()) { Vector3 exitVelocity = Vector3.zero; float energyRetained = 0.0f; // object ball with a spline attached (risky but necessary for a successful game) // has to be the first contact if (otherBall.isMain && cueController.CurrentBallID == id && cueController.TargetPocketDirection != Vector3.zero) { // assign collision normal exitVelocity = cueController.TargetPocketDirection; // zero out stuff so there is no next use cueController.TargetPocketDirection = Vector3.zero; cueController.CurrentBallID = -1; // calculate energy retained energyRetained = Vector3.Dot((cueController.currentHitBallController.reserveVelocity + cueController.cueBallController.reserveVelocity).normalized, exitVelocity.normalized); // the following are all copied from ball controller, make them into a function if (energyRetained < 0.0f) { energyRetained += 1; } if (!cueController.currentHitBallController.GetComponent <Rigidbody>().isKinematic) { cueController.currentHitBallController.GetComponent <Rigidbody>().velocity = exitVelocity.normalized * energyRetained * (cueController.cueBallController.reserveVelocity.magnitude + cueController.currentHitBallController.reserveVelocity.magnitude); } } // cue ball with its spline, this will always check out else if (isMain && cueController.CurrentBallID == otherBall.id && cueController.CueBallBounceDirection != Vector3.zero) { // assign collision normal exitVelocity = cueController.CueBallBounceDirection; // zero out the spline cueController.CueBallBounceDirection = Vector3.zero; // calculate energy retained energyRetained = Vector3.Dot((cueController.currentHitBallController.reserveVelocity + cueController.cueBallController.reserveVelocity).normalized, exitVelocity.normalized); // the following are all copied from ball controller, make them into a function if (energyRetained < 0.0f) { energyRetained += 1; } if (!cueController.cueBallController.GetComponent <Rigidbody>().isKinematic) { cueController.cueBallController.GetComponent <Rigidbody>().velocity = exitVelocity.normalized * energyRetained * (cueController.cueBallController.reserveVelocity.magnitude + cueController.currentHitBallController.reserveVelocity.magnitude); } } // all other cases of ball to ball collision else { // we use unity built in engine for this purpose exitVelocity = GetComponent <Rigidbody>().velocity; // zero out stuff so there is no next use cueController.TargetPocketDirection = Vector3.zero; cueController.CurrentBallID = -1; cueController.CueBallBounceDirection = Vector3.zero; // calculate energy retained energyRetained = exitVelocity.magnitude / (reserveVelocity.magnitude + otherBall.reserveVelocity.magnitude); // guard against negative numbers if (energyRetained < 0.0f) { energyRetained += 1; } if (!GetComponent <Rigidbody>().isKinematic) { GetComponent <Rigidbody>().velocity = exitVelocity.normalized * energyRetained * (reserveVelocity.magnitude + otherBall.reserveVelocity.magnitude); } } } // some game rules, you can only first hit the current first ball if (cueController.firstBallBallCollisionSinceShot && otherBall != null) { if (isMain || otherBall.isMain) { int tempId = 0; if (isMain) { tempId = otherBall.id; } else { tempId = id; } if (cueController.currentBallControllers.Count > 1) { if (tempId != cueController.currentBallControllers[1].id) // first ball remaining { cueController.hittingTheRightFirstBall = false; } else { cueController.hittingTheRightFirstBall = true; } } cueController.firstBallBallCollisionSinceShot = false; } } cueController.contactedAtLeastOneRealBall = true; } if (layerName == "Wall" && !GetComponent <Rigidbody>().isKinematic&& !ballIsPocketed) // walllllz, should include pocket walls { // choose sound float b_w_volume = Mathf.Clamp01(collision.relativeVelocity.magnitude / cueController.ballMaxVelocity); int b_w_index = Random.Range((int)MusicClip.B_W_0, (int)MusicClip.B_W_1 + 1); // play sound GameManager_script.Instance().PlaySound(b_w_index, false, b_w_volume); // take half a ball back to avoid the risk of running into the other side of the wall before our wall, vectoroperator has things that does this as well Ray ray = new Ray(GetComponent <Rigidbody>().position - cueController.ballRadius * reserveVelocity.normalized * 1.25f, reserveVelocity.normalized); RaycastHit hit; // use a common angular velocity bullshit Vector3 reserveXZVelocity = VectorOperator.getProjectXZ(reserveVelocity, false); Vector3 reserveXZAngularVelocity = VectorOperator.getProjectXZ(reserveAngularVelocity, false); if (Physics.SphereCast(ray, cueController.ballRadius, out hit, 20.0f * cueController.ballRadius, cueController.wallMask)) { // make sure we only have a bounce but no pocket direction, meaning it is not a ball hit if (isMain && cueController.CueBallBounceDirection != Vector3.zero && cueController.TargetPocketDirection == Vector3.zero) { // set the straight velocity up GetComponent <Rigidbody>().velocity = Vector3.Magnitude(reserveVelocity) * Vector3.Normalize(cueController.CueBallBounceDirection); // reset everything just to be sure cueController.TargetPocketDirection = Vector3.zero; cueController.CurrentBallID = -1; cueController.CueBallBounceDirection = Vector3.zero; } else { // calculate linear velocity GetComponent <Rigidbody>().velocity = reserveXZVelocity - 2.0f * Vector3.Project(reserveXZVelocity, VectorOperator.CleanYAxis(-hit.normal)); GetComponent <Rigidbody>().velocity = VectorOperator.getProjectXZ(GetComponent <Rigidbody>().velocity, true); } if (true) { // calculate angular velocity Vector3 XZHitNormal = VectorOperator.CleanYAxis(-hit.normal); // this declaration is f*****g retarded float XAngular = reserveXZAngularVelocity.x; float ZAngular = reserveXZAngularVelocity.z; // z is NOT flat, so x needs to be re-calculated. Since reserve velocity x is always negative of the reserve angular z, so we add. if (Mathf.Abs(XZHitNormal.x) > 0.01f) { ZAngular += Mathf.Abs(XZHitNormal.x) * reserveVelocity.x * 2.0f * 2.0f; // 2.0f for twice the angular, 2.0f for negation ZAngular = Mathf.Clamp(ZAngular, Mathf.Abs(reserveXZAngularVelocity.z) * -1.0f, Mathf.Abs(reserveXZAngularVelocity.z) * 1.0f); // dajiang hack, i omitted the other component, make sure ZAngular doesn't exceed the total magnitude it previously had } // x is NOT flat, so z needs to be re-calculated. Since reserve velocity z is always in sync with reserve angluar x, so we subtract. if (Mathf.Abs(XZHitNormal.z) > 0.01f) { XAngular -= Mathf.Abs(XZHitNormal.z) * reserveVelocity.z * 2.0f * 2.0f; // 2.0f for twice the angular, 2.0f for negation XAngular = Mathf.Clamp(ZAngular, Mathf.Abs(reserveXZAngularVelocity.x) * -1.0f, Mathf.Abs(reserveXZAngularVelocity.x) * 1.0f); // dajiang hack, i omitted the other component, make sure XAngular doesn't exceed the total magnitude it previously had } GetComponent <Rigidbody>().angularVelocity = new Vector3(XAngular, 0.0f, ZAngular); } // add Y component of angular velocity to linear velocity and subtract itself from the angular component, dajiang hack? Donno what this is no more Vector3 YAngularVelocityOnly = new Vector3(0.0f, reserveAngularVelocity.y, 0.0f); // actually add and subtract angular velocities GetComponent <Rigidbody>().velocity += 0.36f * Vector3.Magnitude(YAngularVelocityOnly * 0.5f) * Vector3.Cross(VectorOperator.CleanYAxis(-hit.normal), YAngularVelocityOnly.normalized); GetComponent <Rigidbody>().angularVelocity -= new Vector3(0.0f, 0.36f * reserveAngularVelocity.y, 0.0f); // this line checks out // calculate restitution cooef, 1 is straight, 0 is slice float impactAngle = Mathf.Clamp01(Vector3.Dot(reserveXZVelocity.normalized, VectorOperator.CleanYAxis(-hit.normal).normalized)); // restitution, choose wisely GetComponent <Rigidbody>().velocity = (0.96f - impactAngle * 0.24f) * GetComponent <Rigidbody>().velocity; GetComponent <Rigidbody>().angularVelocity = (0.96f - impactAngle * 0.24f) * GetComponent <Rigidbody>().angularVelocity; } // only start counting the wall hits after a good ball hit, otherwise all wall hits are pointless if (cueController.contactedAtLeastOneRealBall) { cueController.wallHitCount += 1; } } } }