예제 #1
0
    public override void Start()
    {
        if (CheckErrors() == true)
        {
            return;
        }
        base.Start();

        terminalVel = MFcompute.FindTerminalVelocity(totalThrust, myRigidbody);

        OnValidate();
    }
예제 #2
0
    public override void Awake()
    {
        if (CheckErrors() == true)
        {
            return;
        }
        base.Awake();

        turnSpeed   = turnRate;       // used by FS_Targeting to evaluate time to aim
        terminalVel = MFcompute.FindTerminalVelocity(totalThrust, myRigidbody);

        OnValidate();
    }
예제 #3
0
    public static float SmoothRateChange(float distance, float closureRate, float curRate, float rateAccel, float decelMult, float rateMax)
    {
        int _goalFactor    = distance >= 0 ? 1 : -1;      // which way is goal?
        int _curTurnFactor = curRate >= 0 ? 1 : -1;       // which way currently turning?

        // if goal is within accelleration, reduce accel to match goal. This minimizes jitter at high acceleration speeds
        if ((distance <= rateAccel * _curTurnFactor * Time.deltaTime && distance >= 0) || (distance >= rateAccel * _curTurnFactor * Time.deltaTime && distance <= 0))
        {
            rateAccel = Mathf.Abs(distance) / Time.deltaTime;
        }

        float _rateDecel = rateAccel * decelMult * 2f;         // decelerate faster than accelerating (*2 to make it a little more agressive as default)

        // determine which way to accelerate
        if (_goalFactor * _curTurnFactor > 0)
        {
            // positive: moving towards goal
            if (distance * _goalFactor >= MFcompute.DistanceToStop(closureRate, _rateDecel) * _goalFactor)
            {
                // turn faster
                curRate = Mathf.Clamp(curRate + (rateAccel * _curTurnFactor * Time.deltaTime), -rateMax, rateMax);
            }
            else
            {
                if (distance * _goalFactor <= MFcompute.DistanceToStop(closureRate, _rateDecel) * _goalFactor * .8)
                {
                    //turn slower
                    curRate = Mathf.Clamp(curRate - (_rateDecel * _curTurnFactor * Time.deltaTime), -rateMax, rateMax);
                }                 // else don't change
            }
        }
        else
        {
            // negative: moving away from goal, start heading towards goal
            curRate = Mathf.Clamp(curRate + (_rateDecel * _goalFactor * Time.deltaTime), -rateMax, rateMax);
        }
        return(curRate);
    }
예제 #4
0
    public virtual void FixedUpdate()
    {
        velocity = UnitVelocity();

        if (_navTarget)
        {
            navTargetAim = _navTarget.position;
            if (useIntercept == true)
            {
                if (interceptForPlatform && interceptForPlatform.target == _navTarget)
                {
                    // intercept for this target already computed by platform
                    navTargetAim = interceptForPlatform.targetAimLocation - (interceptForPlatform.weaponMount.position - transform.position);
                }
                else
                {
                    float _vel = velocity.magnitude;                     // use my speed
                    if (interceptForPlatform)
                    {
                        _vel = interceptForPlatform.shotSpeed ?? 0f;
                    }                     // use platform shot speed
                    // _navTarget velocity
                    Vector3 _navVelocity = Vector3.zero;
                    if (navRigidbody)
                    {
                        _navVelocity = navRigidbody.velocity;
                    }
                    else
                    {
                        _navVelocity    = navLastPosition - _navTarget.position;
                        navLastPosition = _navTarget.position;
                    }

                    navTargetAim = MFcompute.Intercept(transform.position, velocity, _vel, _navTarget.position, _navVelocity) ?? _navTarget.position;
                }
            }
        }
    }
    public virtual Vector3 AimLocation()
    {
        // adjust aim location to hit target
        Vector3 _aimLoc = Vector3.zero;

        if (_target)
        {
            _aimLoc = _target.position;

            if (useIntercept == true && shotSpeed != null)
            {
                Vector3 _targetVelocity = Vector3.zero;
                // target velocity
                if (targetRigidbody)                     // if target has a rigidbody, use velocity
                {
                    _targetVelocity = targetRigidbody.velocity;
                }
                else                     // otherwise compute velocity from change in position
                {
                    _targetVelocity    = (_aimLoc - lastTargetPosition) / Time.deltaTime;
                    lastTargetPosition = _aimLoc;
                }

                // point at linear intercept position
                Vector3?interceptAim = MFcompute.Intercept(exitLoc, velocity, (float)shotSpeed, _aimLoc, _targetVelocity);
                if (interceptAim == null)
                {
                    target = null;
                }
                else
                {
                    _aimLoc = (Vector3)interceptAim;
                }
            }
        }
        return(_aimLoc);
    }
예제 #6
0
    public static float?BallisticIteration(Vector3 exitLoc, float?shotSpeed, float aimRad, MFnum.ArcType arc, Transform target,
                                           Vector3 targetVelocity, Vector3 platformVelocity, Vector3 aimLoc_In, out Vector3 aimLoc_Out)
    {
        float?_ballAim = null;

        aimLoc_Out = aimLoc_In;
        // find new flight time
        float?_flightTime         = MFball.BallisticFlightTime(aimLoc_In, exitLoc, (float)shotSpeed, aimRad, arc);
        float _effectiveShotSpeed = Vector3.Distance(exitLoc, aimLoc_In) / (float)_flightTime;
        // find intercept based on new _effectiveShotSpeed
        Vector3?_intAim = MFcompute.Intercept(exitLoc, platformVelocity, _effectiveShotSpeed, target.position, targetVelocity);

        if (_intAim == null)
        {
            _ballAim = null;
        }
        else
        {
            aimLoc_Out = (Vector3)_intAim;             // modify target aim location
            // re-calculate ballistic trajectory based on intercept point
            _ballAim = MFball.BallisticAimAngle(aimLoc_Out, exitLoc, (float)shotSpeed, arc);
        }
        return(_ballAim);
    }
예제 #7
0
	public override Vector3 AimLocation() {
		// intercept and ballistics
		if ( _target ) {
			Vector3 _targetVelocity = Vector3.zero;
			targetLoc = _target.position;
			if ( useIntercept == true && shotSpeed != null ) { 
				// target velocity
				if (targetRigidbody) { // if target has a rigidbody, use velocity
					_targetVelocity = targetRigidbody.velocity;
				} else { // otherwise compute velocity from change in position
					_targetVelocity = ( targetLoc - lastTargetPosition ) / Time.deltaTime;
					lastTargetPosition = targetLoc;
				}
			}
			
			if ( useGravity == true && Physics.gravity.y != 0 && shotSpeed != null ) { // ballistic aim
				int _factor = -Physics.gravity.y > 0 ? 1 : -1;
				// find initial aim angle
				float? _ballAim = MFball.BallisticAimAngle( targetLoc, exitLoc, (float)shotSpeed, curArc );
				if ( _ballAim == null ) {
					target = null;
				} else {
					if ( useIntercept == true && playerControl == false ) { // ballistic + intercept
						// iterate for better ballistic accuracy when also using intercept
						int bi = 0;
						int biMax;
						if ( curArc == MFnum.ArcType.High ) {
							biMax = highArcBallisticIterations;
						} else {
							biMax = ballisticIterations;
						}
						while ( _target && bi++ < biMax ) {
							//							_ballAim = BallisticIteration ( exitLoc, (float)shotSpeed, (float)_ballAim, _targetVelocity );
							_ballAim = MFball.BallisticIteration ( exitLoc, (float)shotSpeed, (float)_ballAim, curArc, _target, _targetVelocity, velocity, targetLoc, out targetLoc );
							if ( _ballAim == null ) { target = null; } // no solution, release target
						}
					}
					if ( _target ) { // target can become null in balistic iteration if no solution
						Vector3 _cross = -Vector3.Cross( ( targetLoc - exitLoc ), Vector3.up );
						Quaternion _eleAngleDir = Quaternion.AngleAxis( _factor * (float)_ballAim * Mathf.Rad2Deg, -_cross );
						targetAimLocation = exitLoc + (( _eleAngleDir * Vector3.Cross( _cross, Vector3.up ) ).normalized * targetRange );
					}
				}
			} else { // no gravity
				if ( useIntercept == true && playerControl == false && shotSpeed != null ) {
					// point at linear intercept position
					Vector3? _interceptAim = MFcompute.Intercept( exitLoc, velocity, (float)shotSpeed, targetLoc, _targetVelocity );
					if ( _interceptAim == null ) {
						target = null;
					} else {
						targetAimLocation = (Vector3)_interceptAim;
					}
				} else { // point at target position
					targetAimLocation = _target.position;
				}
			}
		} else {
			targetAimLocation = Vector3.zero;
		}
		return targetAimLocation;
	}
예제 #8
0
	public override void Update () {
		if (error == true) { return; }
		base.Update(); // AimLocation() called from base

		// find rotation/elevation rates
		float _useTime;
		if (Time.time < .5) { 
//			_useTime = Time.deltaTime; // to avoid smoothDeltaTime returning NaN on the first few frames
			return; // avoids initial movement spurt
		} else {
			_useTime = Time.smoothDeltaTime;
		}
		float _curRotRateEst = Quaternion.Angle( rotator.localRotation, lastRotation ) / _useTime;
		float _curEleRateEst = Quaternion.Angle( elevator.localRotation, lastElevation ) / _useTime;
		// average rotation, then move to lastRotation
		averageRotRateEst = (_curRotRateEst + lastRotRateEst) / 2f;
		lastRotRateEst = _curRotRateEst;
		lastRotation = rotator.localRotation;
		// average elevation, then move to lastElevation
		averageEleRateEst = (_curEleRateEst + lastEleRateEst) / 2f;
		lastEleRateEst = _curEleRateEst;
		lastElevation = elevator.localRotation;
		// find inaccuracy caused by rotation/elevation. (using .03 to minimize influence from spikes)
		if ( averageRotRateEst > rotationAccel * .03f || averageEleRateEst > elevationAccel * .03f ) {
			totalTurnWeapInaccuracy = turningWeapInaccuracy * (averageRotRateEst + averageEleRateEst);
//			totalTurnAimInaccuracy = turningAimInaccuracy * (averageRotRateEst + averageEleRateEst);
		} else { 
			totalTurnWeapInaccuracy = 0f;
//			totalTurnAimInaccuracy = 0f;
		}
		
		// move aim error location
//		if ( aimError + totalTurnAimInaccuracy > 0 ) {
		if ( aimError > 0 ) {
			if ( Time.time >= lastAimError + aimErrorInterval ) {
				systAimError = Random.insideUnitSphere;
				lastAimError = Time.time;
			}
		}

		// find aim locations
		if ( _target ) {
			Vector3 _localTarget;
			timeSinceTarget = Time.time;
			// move apparent location of target due to turret aim error
//			if ( aimError + totalTurnAimInaccuracy > 0 ) {
			if ( aimError > 0 ) {
//				targetAimLocation = targetAimLocation + ((systAimError * (aimError + totalTurnAimInaccuracy) * targetRange) / 57.3f ); // 57.3 is the distance where 1 unit offset appears as 1° offset
				targetAimLocation = targetAimLocation + ((systAimError * aimError * targetRange) / 57.3f ); // 57.3 is the distance where 1 unit offset appears as 1° offset
			}
			
			// find target's location in rotation plane
			_localTarget = rotator.InverseTransformPoint( targetAimLocation ); 
			rotatorPlaneTarget = rotator.TransformPoint( new Vector3(_localTarget.x, 0f, _localTarget.z) );
			// find target's location in elevation plane as if rotator is already facing target, as rotation will eventualy bring it to front. (don't elevate past 90/-90 degrees to reach target)
			Vector3 _cross = Vector3.Cross( rotator.up, targetAimLocation - weaponMount.position );
			Vector3 _level = Vector3.Cross( _cross, rotator.up ); // find direction towards target but level with local plane
			float _angle = Vector3.Angle( _level, targetAimLocation - weaponMount.position ); 
			if ( _localTarget.y < elevator.localPosition.y + weaponMount.localPosition.y ) { _angle *= -1; } // should angle be negative?
			elevatorPlaneTarget = weaponMount.position + (Quaternion.AngleAxis( _angle, -rotator.right ) * rotator.forward * 1000f);

//			Debug.DrawRay( weaponMount.position, _cross, Color.red, .01f );
//			Debug.DrawRay( weaponMount.position, _level, Color.green, .01f );
//			Debug.DrawRay( weaponMount.position, targetAimLocation - weaponMount.position, Color.cyan, .01f );
//			Debug.Log( _angle );

		} else { // no target
			targetAimLocation = Vector3.zero;
			if ( Time.time >= timeSinceTarget + restDelay ) {
				// set rotation and elevation goals to the rest position
				rotatorPlaneTarget = rotator.position + (Quaternion.AngleAxis(restAngle.y, transform.up) * transform.forward * 1000f);
				elevatorPlaneTarget = elevator.position + (Quaternion.AngleAxis(restAngle.x, -rotator.right) * rotator.forward * 1000f);
			}
		}
		
		// turning
		_curRotSeperation = MFmath.AngleSigned(rotator.forward, rotatorPlaneTarget - weaponMount.position, transform.up);
		_curEleSeperation = MFmath.AngleSigned(elevator.forward, elevatorPlaneTarget - weaponMount.position, -rotator.right);
		
		// turn opposite if shortest route is through a gimbal limit
		float _bearing = MFmath.AngleSigned(transform.forward, rotatorPlaneTarget - weaponMount.position, transform.up);
		float _aimAngle = MFmath.AngleSigned(transform.forward, rotator.forward, rotator.up);
		float _modAccel = rotationAccel;
		float _modSeperation = _curRotSeperation;
		if (limitLeft > -180 || limitRight < 180) { // is there a gimbal limit?
			if ( (_curRotSeperation < 0 && _bearing > 0 && _aimAngle < 0)  ||  (_curRotSeperation > 0 && _bearing < 0 && _aimAngle > 0) ) { // is shortest turn angle through a limit?
				// reverse turn
				_modAccel *= -1f; // for constant turn rate mode
				_modSeperation *= -1f; // for smooth turn rate mode
			}
		}

		Quaternion _rot;
		if (constantTurnRate == true) { // no variation in rotation speed. more accurate, lightweight, less realistic.
			// apply rotation
			_rot = Quaternion.LookRotation( rotatorPlaneTarget - rotator.position, transform.up );
			rotator.rotation = Quaternion.RotateTowards( rotator.rotation, _rot, Mathf.Min(_modAccel, rotationRateMax) * Time.deltaTime );
			
			// apply elevation
			_rot = Quaternion.LookRotation( elevatorPlaneTarget - weaponMount.position, rotator.up );
			elevator.rotation = Quaternion.RotateTowards( elevator.rotation, _rot, Mathf.Min(elevationAccel, elevationRateMax) * Time.deltaTime );
			
		} else { // accellerate/decellerate to rotation goal
			// find closure rate of angle to target. 
			float _closureTurnRate = (_curRotSeperation - lastRotSeperation) / Time.deltaTime;
			float _closureEleRate = (_curEleSeperation - lastEleSeperation) / Time.deltaTime;
			// store current rate and seperation to become last rate/seperation for next time
			lastRotSeperation = _curRotSeperation;
			lastEleSeperation = _curEleSeperation;

			// apply rotation
			curRotRate = MFcompute.SmoothRateChange(_modSeperation, _closureTurnRate, curRotRate, rotationAccel, decelerateMult, rotationRateMax);
			rotator.rotation = Quaternion.AngleAxis(curRotRate * Time.deltaTime, transform.up) * rotator.rotation;
			
			// apply elevation
			curEleRate = MFcompute.SmoothRateChange(_curEleSeperation, _closureEleRate, curEleRate, elevationAccel, decelerateMult, elevationRateMax);
			elevator.rotation = Quaternion.AngleAxis(curEleRate * Time.deltaTime, -rotator.right) * elevator.rotation; 
		}
		
		CheckGimbalLimits();
		
		// prevent nonsence
		rotator.localEulerAngles = new Vector3( 0f, rotator.localEulerAngles.y, 0f );
		elevator.localEulerAngles = new Vector3( elevator.localEulerAngles.x, 0f, 0f );

		// turn sounds	
		if (rotatorSound.audioSource) {
			TurnSound( rotatorSound, averageRotRateEst, rotationRateMax );
		}
		if (elevatorSound.audioSource) {
			TurnSound( elevatorSound, averageEleRateEst, elevationRateMax );
		}
	}