Ejemplo n.º 1
0
    public static void KeepInCube(Transform cube, float radius, Transform sphere)
    {
        if (!sphereInCube(sphere.position, radius, cube))
        {
            Vector3 _ballLocalPosition = VectorOperator.getLocalPosition(cube, sphere.position);
            float   displacementX1     = _ballLocalPosition.x + radius - 0.5f * cube.localScale.x;
            float   displacementX2     = _ballLocalPosition.x - radius + 0.5f * cube.localScale.x;

            if (displacementX1 > 0.0f)
            {
                sphere.position -= displacementX1 * cube.right;
            }
            else
            if (displacementX2 < 0.0f)
            {
                sphere.position -= displacementX2 * cube.right;
            }

            float displacementZ1 = _ballLocalPosition.z + radius - 0.5f * cube.localScale.z;
            float displacementZ2 = _ballLocalPosition.z - radius + 0.5f * cube.localScale.z;

            if (displacementZ1 > 0.0f)
            {
                sphere.position -= displacementZ1 * cube.forward;
            }
            else
            if (displacementZ2 < 0.0f)
            {
                sphere.position -= displacementZ2 * cube.forward;
            }
        }
    }
Ejemplo n.º 2
0
        void OnUpdade()
        {
            #if UNITY_EDITOR
            if (Application.isPlaying)
            {
            #endif
            if (connectedBody)
            {
                wheelPivot.position = VectorOperator.getWordPosition(absorber, localPosition);
                if (turnBody)
                {
                    if (lineralDamper)
                    {
                        wheelPivot.transform.LookAt(wheelPivot.transform.position + steering.transform.up, -steering.transform.forward);
                    }
                    else
                    {
                        wheelPivot.LookAt(wheelPivot.position + Vector3.ProjectOnPlane(turnBody.transform.forward, root.up).normalized, root.up);
                    }
                }

                float angularVelocityX = Vector3.Dot(body.angularVelocity, wheelPivot.right);

                if (Mathf.Abs(angularVelocityX) > 0.9f)
                {
                    transform.RotateAround(wheelPivot.position, wheelPivot.right, (180.0f / Mathf.PI) * angularVelocityX * Time.deltaTime);
                }
            }
                #if UNITY_EDITOR
        }
                #endif
        }
Ejemplo n.º 3
0
    public static void MoveBallInQuad(Transform cube, float radius, Vector3 hitPoint, ref Vector3 position)
    {
        if (sphereInCube(hitPoint, radius, cube))
        {
            position = hitPoint;
        }
        else
        {
            Vector3 _ballMoveLocalPosition = VectorOperator.getLocalPosition(cube, hitPoint);

            if (!(Mathf.Abs(_ballMoveLocalPosition.x) <= -radius + 0.5f * cube.localScale.x))
            {
                if (Mathf.Abs(_ballMoveLocalPosition.z) <= -radius + 0.5f * cube.localScale.z)
                {
                    Vector3 ballMovePositionX = _ballMoveLocalPosition.x > 0.0f? VectorOperator.getWordPosition(cube, (-radius + 0.5f * cube.localScale.x) * Vector3.right): VectorOperator.getWordPosition(cube, (radius - 0.5f * cube.localScale.x) * Vector3.right);
                    position = new Vector3(ballMovePositionX.x, position.y, hitPoint.z);
                }
            }
            else
            {
                if (Mathf.Abs(_ballMoveLocalPosition.x) <= -radius + 0.5f * cube.localScale.x)
                {
                    Vector3 ballMovePositionZ = _ballMoveLocalPosition.z > 0.0f? VectorOperator.getWordPosition(cube, (-radius + 0.5f * cube.localScale.z) * Vector3.forward):
                                                VectorOperator.getWordPosition(cube, (radius - 0.5f * cube.localScale.z) * Vector3.forward);
                    position = new Vector3(hitPoint.x, position.y, ballMovePositionZ.z);
                }
            }
        }
    }
Ejemplo n.º 4
0
    public bool OnButton(RaycastHit curentHit)
    {
        if (!isSelected)
        {
            return(false);
        }

        if (curentHit.collider == GetComponent <Collider>() && Vector3.Distance(curentHit.point, strPosition) < radius)
        {
            touch.position = curentHit.point;
        }
        else
        {
            Vector3 displacement = curentHit.point - strPosition;

            touch.position = radius * (displacement - Vector3.Project(displacement, cueCamera.transform.forward)).normalized + strPosition;
        }

        displacementX = Mathf.Clamp((1.0f / radius) * VectorOperator.getLocalPosition(transform, touch.position).x, -1.0f, 1.0f);
        displacementZ = Mathf.Clamp((1.0f / radius) * VectorOperator.getLocalPosition(transform, touch.position).z, -1.0f, 1.0f);

        if (CircularSliderPress != null)
        {
            CircularSliderPress(this);
        }

        return(true);
    }
Ejemplo n.º 5
0
    void Update()
    {
        if (controller.NewPoseName == "Rotate left" || controller.NewPoseName == "Rotate right" || controller.NewPoseName == "From Rotate left" || controller.NewPoseName == "From Rotate right" ||
            controller.NewPoseName == "Left turn" || controller.NewPoseName == "Right turn")
        {
            position = VectorOperator.getWordPosition(aviator, localPosition);
        }
        else
        {
            position = deltaPosition + aviator.position;
        }
        //position = VectorOperator.getWordPosition(aviator, localPosition);
        if (controller.inAnimate)
        {
            _transform.position = Vector3.Lerp(_transform.position, position, 1.0f * damp * Time.deltaTime);
        }
        else
        {
            _transform.position = Vector3.Lerp(_transform.position, position, damp * Time.deltaTime);
        }


        aviatorPosition = Vector3.Lerp(aviatorPosition, aviator.position, 2.0f * damp * Time.deltaTime);
        _transform.LookAt(aviatorPosition);
    }
Ejemplo n.º 6
0
 void Awake()
 {
     _transform      = transform;
     deltaPosition   = _transform.position - aviator.position;
     localPosition   = VectorOperator.getLocalPosition(aviator, _transform.position);
     aviatorPosition = aviator.position;
 }
Ejemplo n.º 7
0
 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);
 }
Ejemplo n.º 8
0
        void LateUpdate()
        {
            if (!downDetailParent)
            {
                enabled = false;
                return;
            }
            springDownDetail.position = VectorOperator.getWordPosition(downDetailParent, localPosition);
            springDownDetail.LookAt(springUpDetail.position);
            springUpDetail.LookAt(springDownDetail.position);

            float deltaDistance = Vector3.Distance(downPivot.position, upPivot.position) / detailsStartDistance;

            spring.localScale = new Vector3(springStartScale.x, springStartScale.y, springStartScale.z * deltaDistance);
        }
Ejemplo n.º 9
0
        public void SetParent(Transform upDetailParent, Transform downDetailParent)
        {
            if (!springDownDetail || !springUpDetail || !spring || !downPivot || !upPivot)
            {
                enabled = false;
                return;
            }
            springUpDetail.parent   = upDetailParent;
            springDownDetail.parent = upDetailParent;
            this.downDetailParent   = downDetailParent;
            if (!downDetailParent)
            {
                enabled = false;
                return;
            }

            localPosition = VectorOperator.getLocalPosition(downDetailParent, springDownDetail.position);
        }
Ejemplo n.º 10
0
        void OnEnable()
        {
            vehicle = transform.GetComponentInParent <Vehicle>();
            root    = vehicle.transform;

            wheelPivot = transform.parent;
            #if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                if (!body)
                {
                    GameObject wheel     = new GameObject(name + "_Joint");
                    GameObject wheelTurn = null;
                    if (wheelPivot.name.Contains("WheelTurn"))
                    {
                        wheelTurn = new GameObject(name + "_Turn_Joint");
                        steering  = root.GetComponentInChildren <Steering>();
                    }


                    Transform physics = root.Find("Physics");
                    if (!physics)
                    {
                        physics                      = new GameObject("Physics").transform;
                        physics.parent               = root.transform;
                        physics.position             = root.position;
                        physics.transform.localScale = Vector3.one;
                    }

                    wheel.transform.parent   = physics;
                    wheel.transform.position = transform.position;
                    wheel.transform.rotation = root.rotation;

                    if (wheelTurn)
                    {
                        wheelTurn.transform.parent   = physics;
                        wheelTurn.transform.position = wheelPivot.position;
                        lineralDamper = GetComponentInParent <Absorber>();
                        if (!lineralDamper.name.Contains("LineralAbsorber"))
                        {
                            lineralDamper = null;
                        }
                        if (lineralDamper)
                        {
                            wheelTurn.transform.LookAt(wheelTurn.transform.position + steering.transform.up, -steering.transform.forward);
                        }
                        else
                        {
                            wheelTurn.transform.rotation = root.rotation;
                        }
                    }

                    joint        = wheel.AddComponent <HingeJoint>();
                    bodyCollider = wheel.AddComponent <MeshCollider>();
                    body         = wheel.GetComponent <Rigidbody>();

                    PhysicMaterial physicMaterial = (PhysicMaterial)AssetDatabase.LoadAssetAtPath("Assets/Vehicles/WheelSystem/PhysicsMaterials/Wheel.physicMaterial", typeof(PhysicMaterial));

                    parameters   = new Parameters(name, true, true, 2.0f, 0.0f, 0.0f, physicMaterial);
                    joint.anchor = Vector3.zero;
                    joint.axis   = Vector3.right;

                    if (wheelTurn)
                    {
                        turnJoint        = wheelTurn.AddComponent <HingeJoint>();
                        turnBody         = wheelTurn.GetComponent <Rigidbody>();
                        turnBody.mass    = parameters.mass;
                        turnJoint.anchor = Vector3.zero;
                        turnJoint.axis   = Vector3.up;

                        UpdateSteeringParameters();
                    }

                    if (!turnJoint)
                    {
                        Absorber aAbsorber = transform.GetComponentInParent <Absorber>();
                        if (aAbsorber)
                        {
                            connectedBody = aAbsorber.body;
                            absorber      = aAbsorber.transform;
                        }
                        else
                        {
                            connectedBody = root.GetComponent <Rigidbody>();
                            absorber      = root;
                        }
                    }
                    else
                    {
                        Absorber aAbsorber = transform.GetComponentInParent <Absorber>();
                        if (aAbsorber)
                        {
                            turnJoint.connectedBody = aAbsorber.body;
                            absorber = aAbsorber.transform;
                        }
                        else
                        {
                            turnJoint.connectedBody = root.GetComponent <Rigidbody>();
                            absorber = root;
                        }
                        connectedBody = turnBody;
                    }

                    joint.connectedBody = connectedBody;

                    //absorber = connectedBody.transform;

                    horsepower           = 10.0f;
                    maxVelocityMPS       = 55.0f;
                    newtonMeterPerSecond = 746.0f * horsepower;
                    tractionForce        = newtonMeterPerSecond / maxVelocityMPS;
                    CalculateCollider();
                }
            }
            else
            {
            #endif
            if (!absorber)
            {
                enabled = false;
                return;
            }
            newtonMeterPerSecond = 746.0f * horsepower;
            tractionForce        = newtonMeterPerSecond / maxVelocityMPS;
            radius = 0.5f * body.transform.lossyScale.y;
            body.maxAngularVelocity = maxVelocityMPS / radius;

            JointMotor motor = new JointMotor();
            motor.targetVelocity = (180.0f / Mathf.PI) * velocityMPS / radius;
            motor.force          = tractionForce * radius;;
            joint.motor          = motor;

            Absorber aAbsorber = transform.GetComponentInParent <Absorber>();

            if (aAbsorber)
            {
                aAbsorber.OnUpdade += OnUpdade;
            }
            else if (vehicle)
            {
                vehicle.OnUpdade += OnUpdade;
            }

            if (connectedBody)
            {
                localPosition     = VectorOperator.getLocalPosition(absorber, wheelPivot.position);
                wheelPivot.parent = root;
            }
            foreach (var item in root.GetComponentsInChildren <Collider>())
            {
                Physics.IgnoreCollision(bodyCollider, item);
            }

                #if UNITY_EDITOR
        }
                #endif
        }
Ejemplo n.º 11
0
    // 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;
                }
            }
        }
    }