Exemplo n.º 1
0
    public void CalculateBends()
    {
        if (!IsCircular)
        {
            return;
        }
        Bends = new CircleList <Bend>();
        Bend           _bend      = new Bend();
        int            BendId     = 0;
        MovingAvgFloat AngleQueue = new MovingAvgFloat(5);

        _bez = BezierLine.Instance;
        int      XSecCount = XSecs.Count();
        Vector3  PrevMid;
        Vector3  ThisMid;
        Vector3  NextMid;
        BendType _bt            = BendType.Unknown;
        BendType _prevbt        = BendType.Unknown;
        int      Incr           = 1;
        float    AngleThreshold = 1.34f;

        foreach (XSec X in XSecs)
        {
            PrevMid = XSecs.Prev(X).MidPt;
            ThisMid = X.MidPt;
            NextMid = XSecs.Next(X).MidPt;
            float Angle    = VectGeom.SignedAngle(ThisMid + ThisMid - PrevMid, ThisMid, NextMid, Vector3.up);
            float _segDist = Vector3.Distance(ThisMid, NextMid);
            AngleQueue.Push(Angle);
            if (Mathf.Abs(AngleQueue.Avg) < AngleThreshold)
            {
                _bt = BendType.Straight;
            }
            else
            {
                _bt = AngleQueue.Avg > 0 ? BendType.Right : BendType.Left;
            }

            if (_prevbt == _bt)
            {
                _bend.Angle += Angle;
            }
            else
            {
                if (_prevbt == BendType.Left || _prevbt == BendType.Right)
                {  //Finish off the previous bend
                    if (Mathf.Abs(_bend.Angle) < 15)
                    {
                        //ignore small bends
                        Bends.Remove(_bend);
                        BendId--;
                    }
                    else
                    {
                        //take 2 off cos of moving avg
                        _bend.EndXSec    = XSecs[X.Idx - 2];
                        _bend.EndSegIdx  = _bend.EndXSec.Idx;
                        _bend.ApexXSec   = XSecs[_bend.StartSegIdx + Mathf.RoundToInt(XSecs.Diff(_bend.StartXSec, _bend.EndXSec) / 2)];
                        _bend.ApexSegIdx = _bend.ApexXSec.Idx;
                        if (_bend.Type == BendType.Right)
                        {
                            _bend.ApexPos = _bend.ApexXSec.KerbR + (_bend.ApexXSec.KerbL - _bend.ApexXSec.KerbR).normalized; _bend.MinTurninPos = _bend.StartXSec.KerbR + (_bend.StartXSec.KerbL - _bend.StartXSec.KerbR).normalized * (_bend.Concatenated ? 4 : 2);
                        }
                        if (_bend.Type == BendType.Left)
                        {
                            _bend.ApexPos = _bend.ApexXSec.KerbL + (_bend.ApexXSec.KerbR - _bend.ApexXSec.KerbL).normalized; _bend.MinTurninPos = _bend.StartXSec.KerbL + (_bend.StartXSec.KerbR - _bend.StartXSec.KerbL).normalized * (_bend.Concatenated ? 4 : 2);
                        }
                        //Fmax = mv^2/r
                        float c = (_bend.EndSegIdx - _bend.StartSegIdx) * 360 / _bend.Angle;
                        float r = Mathf.Abs(c) / Mathf.PI / 2f;
                        //Debug.Log("Bend" + _bend.BendId + " radius = " + r);
                        _bend.SqrtRad = Mathf.Sqrt(r);
                    }
                }
                else
                {   //We might be starting a new bend or carrying n the previous one
                    if (_bt == BendType.Left || _bt == BendType.Right)
                    {
                        bool StartNewBend = true;

                        if (BendId > 0 && X.Idx - Bends[BendId - 1].EndSegIdx < 15 && Bends[BendId - 1].Type == _bt) ///bugbugbug circle bug
                        {                                                                                            //if the bend we've just finished is close to and the same sign as one before
                          //We just carry on with this bend and dont create a new one
                            StartNewBend       = false;
                            _bend.Concatenated = true;
                            GameObject.Destroy(GameObject.Find("Turnin" + _bend.BendId));
                            GameObject.Destroy(GameObject.Find("Apex" + _bend.BendId));
                            GameObject.Destroy(GameObject.Find("BendStart" + _bend.BendId));
                        }

                        if (StartNewBend)
                        {
                            //Create a new Bend
                            _bend             = new Bend();
                            _bend.Type        = _bt;
                            _bend.Sign        = _bt == BendType.Right ? (Int16)1 : (Int16)(-1);
                            _bend.StartXSec   = XSecs[X.Idx - 2];//-2 cos of moving avg
                            _bend.StartSegIdx = _bend.StartXSec.Idx;
                            _bend.Angle       = Angle;
                            _bend.BendId      = BendId;
                            Bends.Add(_bend);
                            BendId++;
                        }
                    }
                }
                //******************************************************************************************
            }
            _prevbt = _bt;
        }
        //This is the last bend. NOt a brilliant bit but it doesn't crash
        if (_bt == BendType.Straight)
        {
            goto Finalise;
        }
        _bend.EndSegIdx = XSecs.Count - 1;
        _bend.EndXSec   = XSecs[_bend.EndSegIdx];
        if (_bend.EndSegIdx <= _bend.StartSegIdx && Bends.Count() > 0)
        {
            Bends.Remove(_bend); Bends.Last().EndSegIdx = XSecs.Count;
        }
        else
        {
            _bend.ApexSegIdx = Mathf.RoundToInt((_bend.EndSegIdx + _bend.StartSegIdx) / 2);
            _bend.ApexXSec   = XSecs[_bend.ApexSegIdx];
            if (_bend.Type == BendType.Right)
            {
                _bend.ApexPos = _bend.ApexXSec.KerbR + (_bend.ApexXSec.KerbL - _bend.ApexXSec.KerbR).normalized;
            }
            if (_bend.Type == BendType.Left)
            {
                _bend.ApexPos = _bend.ApexXSec.KerbL + (_bend.ApexXSec.KerbR - _bend.ApexXSec.KerbL).normalized;
            }
        }
Finalise:
        CalculateTurninAndExitPoints();
        PopulateXSecCurrBends();
    }
Exemplo n.º 2
0
    internal void CalcTurnin()
    {
        //GameObject goCirc;

        //CapsuleCollider Circ;
        //goCirc = GameObject.CreatePrimitive(PrimitiveType.Cube);
        //goCirc.GetComponent<BoxCollider>().enabled = false;
        //Circ = goCirc.AddComponent<CapsuleCollider>();
        //goCirc.name = "CircColl" + BendId;
        //Circ.height = 100;
        Radius = SqrtRad * SqrtRad;
        Vector3        Centre         = new Vector3(0, 0, 0);
        Vector3        C              = new Vector3(0, 0, 0);
        Vector3        AP             = VectGeom.Convert2d(ApexPos);
        Vector3        TP             = new Vector3(0, 0, 0);
        Vector3        EP             = new Vector3(0, 0, 0);
        int            e              = 0;
        float          ExitFrac       = 1;//1 is a wide turn, 0 is tight
        float          SmallestRadErr = 1000;
        PossBendCircle bbc            = new PossBendCircle();
        Vector3        ApexPos2d      = VectGeom.Convert2d(ApexPos);
        float          NxtBndDist     = Rd.XSecs.Diff(ApexXSec, NextBend.ApexXSec);

        //Method:
        //Try a range of turnin segs
        //For each Turnin seg, draw a line perpendicular to the fwd direction
        //and find the turninPt (TP)
        //Find the midpoint (MP) of the line from apex to turninPoint and draw a line perpendicular to this (MPerp)
        //Where MPerp crosses TPerp is the centre of the circle, C
        /// <image url="$(SolutionDir)\CommonImages\CalcTurninAlgorithm.png" scale="1.2"/>
        if (Type == BendType.Right)
        {
            if (NextBend.Type == BendType.Left && NxtBndDist < 200)
            {
                ExitFrac = NxtBndDist / 200;
            }
            for (XSec tx = Rd.XSecs[StartSegIdx - 70]; tx.IsBefore(Rd.XSecs[StartSegIdx]); tx = Rd.XSecs.Next(tx))    //removed circlebug
            {
                Vector2 TPerp = VectGeom.Convert2d(Vector3.Cross(Vector3.up, tx.Forward));
                TP = VectGeom.Convert2d(tx.KerbL + (tx.KerbR - tx.KerbL).normalized * TurninGap);
                Vector2 MP    = (ApexPos2d + TP) / 2;
                Vector2 MPerp = new Vector2((ApexPos2d - TP).y, -(ApexPos2d - TP).x);
                if (VectGeom.LineLineIntersection(out C, TP, TPerp, MP, MPerp)) //these vars are all Vector3(x,0,y)
                {
                    float          biggestCos = 0;
                    float          R          = Vector2.Distance(TP, C);
                    PossBendCircle pbc        = new PossBendCircle();
                    float          RadErr     = 1000;
                    for (e = EndXSec.Idx + 70; e > EndXSec.Idx; e--) //bugbugbug circlebug
                    {
                        Vector2 WideExitPt     = VectGeom.Convert2d(Rd.XSecs[e].KerbL + (Rd.XSecs[e].KerbR - Rd.XSecs[e].KerbL).normalized * 2);
                        Vector2 TightExitPoint = VectGeom.Convert2d(Rd.XSecs[e].KerbR + (Rd.XSecs[e].KerbL - Rd.XSecs[e].KerbR).normalized * 2);
                        EP = Vector2.Lerp(TightExitPoint, WideExitPt, ExitFrac);
                        Vector3 EPerp = VectGeom.Convert2d(-Rd.XSecs[e].Right).normalized;
                        float   cos   = Mathf.Abs(Vector2.Dot((EP - C).normalized, EPerp));
                        if (cos > biggestCos)
                        {
                            RadErr = Mathf.Abs(Vector3.Distance(EP, C) - R);
                            pbc    = new PossBendCircle {
                                C = C, RSq = 0, RadErr = RadErr, EP = EP, EX = Rd.XSecs[e], TP = TP, TX = tx
                            };
                            biggestCos = cos;
                        }
                    }
                    if (RadErr < SmallestRadErr)
                    {
                        bbc = new PossBendCircle {
                            C = pbc.C, RSq = 0, RadErr = pbc.RadErr, EP = pbc.EP, EX = pbc.EX, TP = pbc.TP, TX = pbc.TX
                        };
                        SmallestRadErr = pbc.RadErr;
                    }
                }
            }
            Centre       = new Vector3(bbc.C.x, ApexPos.y, bbc.C.y);
            Radius       = Vector2.Distance(bbc.TP, bbc.C);
            TurninXSec   = bbc.TX;
            TurninSegIdx = TurninXSec.Idx;
            TurninPos    = new Vector3(bbc.TP.x, ApexPos.y, bbc.TP.y);
            ExitXSec     = bbc.EX;
            ExitPos      = new Vector3(bbc.EP.x, ApexPos.y, bbc.EP.y);
            goto FoundCentre;
            //Not found Centre
            Debug.Log("No Centre for Bend" + BendId);
            ExitXSec = Rd.XSecs[e];
            Debug.Break();
        }
        if (Type == BendType.Left)
        {
            if (NextBend.Type == BendType.Right && NxtBndDist < 200)
            {
                ExitFrac = NxtBndDist / 200;
            }
            for (int t = StartSegIdx - 70; t < StartSegIdx; t++)    //bugbugbug circlebug
            {
                Vector2 TPerp = VectGeom.Convert2d(Vector3.Cross(Vector3.up, Rd.XSecs[t].Forward));
                TP = VectGeom.Convert2d(Rd.XSecs[t].KerbR + (Rd.XSecs[t].KerbL - Rd.XSecs[t].KerbR).normalized * TurninGap);
                Vector2 MP    = (ApexPos2d + TP) / 2;
                Vector2 MPerp = new Vector2((ApexPos2d - TP).y, -(ApexPos2d - TP).x);
                if (VectGeom.LineLineIntersection(out C, TP, TPerp, MP, MPerp)) //these vars are all Vector3(x,0,y)
                {
                    float          biggestCos = 0;
                    float          R          = Vector2.Distance(TP, C);
                    PossBendCircle pbc        = new PossBendCircle();
                    float          RadErr     = 1000;
                    for (e = EndXSec.Idx + 70; e > EndXSec.Idx; e--) //bugbugbug circlebug
                    {
                        Vector2 WideExitPt     = VectGeom.Convert2d(Rd.XSecs[e].KerbR + (Rd.XSecs[e].KerbL - Rd.XSecs[e].KerbR).normalized * 2);
                        Vector2 TightExitPoint = VectGeom.Convert2d(Rd.XSecs[e].KerbL + (Rd.XSecs[e].KerbR - Rd.XSecs[e].KerbL).normalized * 2);
                        EP = Vector2.Lerp(TightExitPoint, WideExitPt, ExitFrac);
                        Vector3 EPerp = VectGeom.Convert2d(Rd.XSecs[e].Right).normalized;
                        float   cos   = Mathf.Abs(Vector2.Dot((EP - C).normalized, EPerp));
                        if (cos > biggestCos)
                        {
                            RadErr = Mathf.Abs(Vector3.Distance(EP, C) - R);
                            pbc    = new PossBendCircle {
                                C = C, RSq = 0, RadErr = RadErr, EP = EP, EX = Rd.XSecs[e], TP = TP, TX = Rd.XSecs[t]
                            };
                            biggestCos = cos;
                        }
                    }
                    if (RadErr < SmallestRadErr)
                    {
                        bbc = new PossBendCircle {
                            C = pbc.C, RSq = 0, RadErr = pbc.RadErr, EP = pbc.EP, EX = pbc.EX, TP = pbc.TP, TX = pbc.TX
                        };
                        SmallestRadErr = pbc.RadErr;
                    }
                }
            }
            Centre       = new Vector3(bbc.C.x, ApexPos.y, bbc.C.y);
            Radius       = Vector2.Distance(bbc.TP, bbc.C);
            TurninXSec   = bbc.TX;
            TurninSegIdx = TurninXSec.Idx;
            TurninPos    = new Vector3(bbc.TP.x, ApexPos.y, bbc.TP.y);
            ExitXSec     = bbc.EX;
            ExitPos      = new Vector3(bbc.EP.x, ApexPos.y, bbc.EP.y);
            goto FoundCentre;
            //Not found Centre
            Debug.Log("No Centre for Bend" + BendId);
            ExitXSec = Rd.XSecs[e];
            Debug.Break();
        }
FoundCentre:
        //goCirc.transform.position = Centre;
        //Circ.radius = Radius;
        Angle = VectGeom.SignedAngle(TurninXSec.Forward, ExitXSec.Forward);
        float RacelineSegCount = Rd.XSecs.Diff(TurninXSec, ExitXSec);;

        RacelineAnglePerSeg = Angle / RacelineSegCount;
        RacelineSegLength   = Angle * Mathf.Deg2Rad * Radius / RacelineSegCount;
        CalculateSpeed();
        //Circ.enabled = false; //cos the gameobject doesnt get destroyed till end of frame
        //GameObject.Destroy(goCirc);
        return;

NoTurnin:
        if (TurninXSec == null)
        {
            TurninXSec = Rd.XSecs[ApexSegIdx - 50]; TurninSegIdx = TurninXSec.Idx;
            ExitXSec   = Rd.XSecs[ApexSegIdx + (ApexSegIdx - TurninSegIdx)];
            if (Type == BendType.Right)
            {
                TurninPos = TurninXSec.KerbL + (TurninXSec.KerbR - TurninXSec.KerbL).normalized * 4f;
            }
            else
            {
                TurninPos = TurninXSec.KerbR + (TurninXSec.KerbL - TurninXSec.KerbR).normalized * 4f;
            }
            Radius = 100;
            AnalyseTurnin();
            CalculateSpeed();
        }
        //Circ.enabled = false; //cos the gameobject doesnt get destroyed till end of frame
        //GameObject.Destroy(goCirc);
    }
Exemplo n.º 3
0
    void Update()
    {
        _speed     = _gps.Speed;
        _currXSec  = _gps.CurrXSec;
        PrevSegIdx = SegIdx;
        SegIdx     = _currXSec.Idx;
        if (CR_Running)
        {
            return;
        }
        if (Timer == 0)
        {
            Timer = 5;
        }
        Timer--;
        if (Timer != 4)
        {
            goto DoThisEveryFrame;
        }
        //This bit only runs every 5 frames
        _currBend         = _gps.CurrBend;
        _nextBend         = _gps.NextBend;
        _gpsNextBendAngle = _nextBend.Angle;
        SegFwdDir         = _currXSec.Forward;
        Vector3 Heading = transform.forward;

        HeadingAngle = Vector3.Angle(Heading, SegFwdDir);
        Vector3 cross = Vector3.Cross(Heading, SegFwdDir);

        if (cross.y > 0)
        {
            HeadingAngle = -HeadingAngle;
        }
        //Postition across the cross sec
        Vector3 L = Road.Instance.XSecs[SegIdx].KerbL;
        Vector3 R = Road.Instance.XSecs[SegIdx].KerbR;

        DistFromLKerb = Vector3.Distance(_currXSec.KerbL, transform.position);
        DistFromRKerb = Vector3.Distance(_currXSec.KerbR, transform.position);

        _bendPhase = BendPhase.Straight;

        int  ApproachSegs    = Mathf.RoundToInt(0.5f * _speed * _speed + _speed / 2);
        XSec _approachXSec   = Rd.XSecs[SegIdx + ApproachSegs];
        XSec _decisionXSec   = Rd.XSecs[SegIdx + Mathf.RoundToInt(_speed * 2)];
        bool FirstHalfOfBend = _currBend != null && _currXSec.IsBefore(_currBend.ApexXSec);

        if (!FirstHalfOfBend)
        {
            _approachBend = _nextBend;

            if (_approachBend.TurninXSec.IsBefore(_approachXSec))
            {
                _bendPhase = BendPhase.Approach;
                SpeedAdj   = 1f;
                DriftAngle = _approachBend.DriftAngle * Mathf.Pow(TurnInSpeed / _approachBend.Speed, 2.8f) * _approachBend.Sign; //this is cool

                //Slide the Adjusted turnin point along the gate
                SlideFrac = Mathf.Sqrt(Mathf.Clamp01(TurnInSpeed / _nextBend.Speed)); // - Mathf.Abs(Mathf.Sin(DriftAngle)) * 1.5f); //SlideFrac=1 for a big swingout
                //Debug.Log("SlideFrac=" + TurnInSpeed  + "/" + _nextBend.Speed + "=" + SlideFrac);
                AdjustedTurninPt   = Vector3.Lerp(_nextBend.MinTurninPos, _nextBend.TurninPos, SlideFrac);
                AdjustedTurninXSec = Rd.XSecs[Mathf.RoundToInt(Mathf.Lerp(_nextBend.StartSegIdx, _nextBend.TurninSegIdx, SlideFrac))];

                CalcBrakePoint();

                //Have we reached the decision point? We only decide the flickSegs once we reach the decision point
                if ((_approachBend.TurninXSec.IsBefore(_decisionXSec) && FlickXSec == null) || (FlickXSec != null && _currXSec.IsOnOrBefore(FlickXSec)))
                {
                    FlickSegs       = 0;
                    ReachedFlickEnd = false;
                    //This is the basic flickseg pre-calculation = 150/Radius*FlickSegMultiplier
                    FlickSegs = _approachBend.FlickSegs;
                    //Are we approaching at an angle or spinning?
                    float FlickSegAdj = Rd.XSecs.Diff(AdjustedTurninXSec, _approachBend.ApexXSec) * (TurnInSpeed - _approachBend.Speed) / _approachBend.Speed; // (HeadingAngle + _rb.angularVelocity.y * 20) * _approachBend.FlickSegsMultiplier * -_approachBend.Sign;
                    //if (_approachBend.Type == BendType.Right) FlickSegAdj = -FlickSegAdj;
                    //Debug.Log("FlickSegs = " + FlickSegs + "   FlickSegAdj=(" + Rd.XSecs.Diff(AdjustedTurninXSec, _approachBend.ApexXSec) + " * " + (TurnInSpeed - _approachBend.Speed) / _approachBend.Speed + " = " + FlickSegAdj + "\nTotal=" + (FlickSegs + FlickSegAdj));
                    FlickSegs += FlickSegAdj;
                    //Too slow, the flick will be too harsh
                    //Debug.Log("FlickSegs*=" + TurnInSpeed + "^2/225=" + FlickSegs);
                    //Wrong siede of the road
                    if (_approachBend.Type == BendType.Right ? (DistFromLKerb > DistFromRKerb) : (DistFromRKerb > DistFromLKerb))
                    {
                        FlickSegs = 0;
                    }
                    //Is there a long curve on the turnin?
                    //Debug.Log("TurninCurve=" + Vector3.Angle(_approachBend.StartXSec.Forward, _approachBend.TurninXSec.Forward) + "\nFlickSegs=" + FlickSegs);
                    //RequiredRotation worked ok but didnt take into account the initial rotation
                    //float RequiredRotation = Vector3.Angle(transform.forward, _approachBend.ApexXSec.Forward);
                    //FlickXSec = Rd.XSecs[Mathf.RoundToInt(_approachBend.TurninSegIdx - Mathf.Abs(RequiredRotation) / 2.8f)];
                    FlickXSec = Rd.XSecs[AdjustedTurninXSec.Idx - Mathf.RoundToInt(FlickSegs)];
                } //End of Decision

                if (_currXSec.IsAfter(BrakeXSec) && _currXSec.IsBefore(FlickXSec))
                {
                    _bendPhase = BendPhase.Brake;
                }
                if (FlickXSec != null && _currXSec.IsAfter(FlickXSec))
                {
                    _bendPhase = BendPhase.Turnin;
                }

                PrevFrac = SlideFrac; //used in the turnin
            }//end if Approach
        }                             //end if !FirstHalfOfBend
        else
        {                             //if FIrstHalfOfBend
            _approachBend = _currBend;
            _bendPhase    = BendPhase.Turnin;
        }
        if (_currBend != null && !FirstHalfOfBend)
        {
            if (_bendPhase != BendPhase.Approach)
            {
                _bendPhase = BendPhase.Exit; FlickXSec = null;
            }
        }

        if (_bendPhase == BendPhase.Straight)
        {
            Vector3 _apprXSecFwd = _approachXSec.Forward;
            float   _curveAngle  = Vector3.Angle(SegFwdDir, _apprXSecFwd);
            if (Vector3.Cross(SegFwdDir, _apprXSecFwd).y > 0)
            {
                _curveAngle = -_curveAngle;
            }
            if (_curveAngle > 15)
            {
                _bendPhase = BendPhase.LCurve;
            }
            if (_curveAngle < -15)
            {
                _bendPhase = BendPhase.RCurve;
            }
        }
        //************************************************************************************************
DoThisEveryFrame:

        AimPoint      = Rd.XSecs[SegIdx + 20].MidPt;
        SteerOverride = null;

        if (_bendPhase == BendPhase.Approach)
        {
            Accel = 1; BrakeForce = 0;
            float AvgApprSpeed  = (_speed + LiftOffSpeed) / 2;
            float TimeToBrakePt = DistToBrakePt / AvgApprSpeed;

            //Aim for half the dist to the turnin
            XSec    AimXSec = Rd.XSecs[_currXSec.Idx + Rd.XSecs.Diff(_currXSec, AdjustedTurninXSec) / 2];
            Vector3 T       = (AimXSec.KerbR - AimXSec.KerbL).normalized * _approachBend.TurninGap;

            if (_approachBend.Type == BendType.Right)
            {
                if (TimeToBrakePt < 1)
                {
                    SlideFrac = DistFromRKerb / (DistFromLKerb + DistFromRKerb - _approachBend.TurninGap);
                }
                AimPoint = Vector3.Lerp(AimXSec.KerbR, AimXSec.KerbL + T, SlideFrac);
            }
            else
            {
                if (TimeToBrakePt < 1)
                {
                    SlideFrac = DistFromLKerb / (DistFromLKerb + DistFromRKerb - _approachBend.TurninGap);
                }
                AimPoint = Vector3.Lerp(AimXSec.KerbL, AimXSec.KerbR - T, SlideFrac);
            }

            //I abandoned this because it never had time to straighten up
            /// <image url="$(SolutionDir)\CommonImages\SCurve.png" scale="0.7"/>
        }

        if (_bendPhase == BendPhase.Brake)
        {
            Accel    = 0; BrakeForce = 1;
            AimPoint = (AdjustedTurninPt + transform.position + transform.forward) / 2;
        }

        int   SegsToApex = 0;
        float diff       = 0;


        if (_bendPhase == BendPhase.Turnin)
        {
            float TotSegsToApex = Rd.XSecs.Diff(AdjustedTurninXSec, _approachBend.ApexXSec);
            float SegFrac       = 1 - SegsToApex / TotSegsToApex;
            //SegFrac starts at 0 at the turninPt and ends at 1 at the apex
            float TotalRequiredRotation = VectGeom.SignedAngle(AdjustedTurninXSec.Forward, _approachBend.ApexXSec.Forward) + DriftAngle;
            float CurrRotation          = VectGeom.SignedAngle(AdjustedTurninXSec.Forward, transform.forward);
            float da = (Mathf.Abs(DriftAngle) < 20 && SegFrac > 0.5f) ? DriftAngle / 2 : DriftAngle;
            float RequiredRotation = VectGeom.SignedAngle(transform.forward, _approachBend.ApexXSec.Forward) + DriftAngle;
            SegsToApex = Rd.XSecs.Diff(_currXSec, _approachBend.ApexXSec);
            float TimeToApex         = SegsToApex / _gps.SegsPerSec;
            float ProjectedRotation  = _rb.angularVelocity.y * 57.3f * TimeToApex;
            float ProjectedRotation2 = CurrRotation / SegFrac;
            diff = ProjectedRotation - RequiredRotation;

            Accel = 1; BrakeForce = 0;
            if (_speed < 8)
            {
                AimPoint = _approachBend.Type == BendType.Right ? Rd.XSecs[SegIdx + 10].MidRight : Rd.XSecs[SegIdx + 10].MidLeft; BrakeForce = 0;
            }
            else
            {
                AimPoint = _approachBend.ApexPos;

                //Debug.Log("rotdiff=" + ProjectedRotation + " - " + RequiredRotation + " = " + diff);
                if (_approachBend.Type == BendType.Right)
                {
                    /*
                     * //Manage the Fractional Distance
                     * if (PrevSegIdx != SegIdx)
                     * {
                     *  FracPerSeg = (Frac - PrevFrac) / (SegIdx - PrevSegIdx);
                     *  ProjectedFrac = Frac + FracPerSeg * SegsToApex;
                     *  //Debug.Log("ProjectedFrac=" + ProjectedFrac);
                     *  PrevFrac = Frac;
                     * }
                     */

                    //Stop understeer
                    if (diff < 0)
                    {
                        BrakeForce = Mathf.Clamp01(-diff / 80); Accel = 1 - BrakeForce; SteerOverride = 40;
                    }
                    if (diff < -40)
                    {
                        BrakeForce = Mathf.Clamp01(-diff / 40); Accel = 1;
                    }
                    if (_rb.angularVelocity.y < 0)
                    {
                        BrakeForce = 0; AimPoint = _approachBend.ApexPos;
                    }                                                                                    //stop the backskid
                }
                if (_approachBend.Type == BendType.Left)
                {
                    if (diff > 0)
                    {
                        BrakeForce = Mathf.Clamp01(diff / 80); Accel = 1 - BrakeForce; SteerOverride = -40;
                    }
                    if (diff > 40)
                    {
                        BrakeForce = Mathf.Clamp01(diff / 40); Accel = 1;
                    }
                    if (_rb.angularVelocity.y > 0)
                    {
                        BrakeForce = 0; AimPoint = _approachBend.ApexPos;
                    }                                                                                    //stop the backskid
                    //if (SegsToApex > 40) { BrakeForce = 0; Accel = 1; AimPoint = Rd.XSecs[SegIdx + 10].MidPt; }  //Stop the serious oversteer
                }
            }
        }
        if (_bendPhase == BendPhase.Exit)
        {
            Accel = 1;
            Vector3 _transverse   = (_currXSec.KerbR - _currXSec.KerbL).normalized;
            float   SpeedTwdRKerb = Vector3.Dot(_rb.velocity, _transverse);
            //Debug.Log("SpeedTwdKerb=" + SpeedTwdRKerb);
            AimPoint = _currBend.ExitPos; BrakeForce = 0;
            if (_currBend.Type == BendType.Right)
            {
                if (Rd.XSecs.Diff(_currXSec, _currBend.ExitXSec) > 5)
                {
                    AimPoint = _currBend.ExitXSec.MidRight;
                }
            }
            if (_currBend.Type == BendType.Left)
            {
                if (Rd.XSecs.Diff(_currXSec, _currBend.ExitXSec) > 5)
                {
                    AimPoint = _currBend.ExitXSec.MidLeft;
                }
            }
        }

        XSec    LongAimXSec  = Rd.XSecs[SegIdx + 40];
        Vector3 LongAimPoint = LongAimXSec.MidPt;

        if (_bendPhase == BendPhase.Straight)
        {
            Accel = 1; BrakeForce = 0;
            if (_nextBend == null)
            {
                Debug.Log("nbnull");
            }
            if (_nextBend.Type == BendType.Right)
            {
                AimPoint = Rd.XSecs[SegIdx + 20].MidLeft;
            }
            else
            {
                AimPoint = Rd.XSecs[SegIdx + 20].MidRight;
            }
        }
        if (_bendPhase == BendPhase.RCurve)
        {
            Accel = 1; BrakeForce = 0; AimPoint = Rd.XSecs[SegIdx + 20].MidRight;
        }
        if (_bendPhase == BendPhase.LCurve)
        {
            Accel = 1; BrakeForce = 0; AimPoint = Rd.XSecs[SegIdx + 20].MidLeft;
        }

        //steer round the car in front
        Vector3 FrontOfCar = transform.position + transform.up * 0.5f + transform.forward * 2.5f;

        CollisionRay = new Ray(FrontOfCar, AimPoint + Vector3.up - FrontOfCar);
        int layerMask = (1 << 8);

        if (Physics.Raycast(CollisionRay, out CollisionHit, 50, layerMask))
        {
            if (CollisionHit.collider.name != this.name && CollisionHit.collider.name != "ColldrF")
            {
                if (CollisionHit.rigidbody.velocity.sqrMagnitude < 1)
                {
                    StartCoroutine(ThreePointTurn("R"));
                }
                else
                {
                    AimPoint = Rd.XSecs[SegIdx + 10].MidRight;
                }
            }
        }
        //Debug.DrawRay(FrontOfCar, AimPoint + Vector3.up - FrontOfCar, Color.red);

        //**************************************************************************************
        //Coming up to a hill, put your foot down
        if (LongAimPoint.y - SegFwdDir.y > 0)
        {
            //Accel = ((LongAimPoint.y - SegFwdDir.y) * 0.005f) + 0.9f;
        }

        //coming up to a hill bend slow down
        if (Mathf.Abs(_gpsNextBendAngle) > 10 && _gps.NextHill > 2 && _gps.Speed > 10)
        {
            // Accel = 0; BrakeForce = 0.5f;
        }

        //***********************************************************************************


        // Pointing towards kerb - do a 3 point turn
        if (HeadingAngle > -100 && HeadingAngle < -80 && DistFromLKerb < 3)
        {
            StartCoroutine(ThreePointTurn("L"));
            return;
        }
        if (HeadingAngle < 100 && HeadingAngle > 80 && DistFromRKerb < 3)
        {
            StartCoroutine(ThreePointTurn("R"));
            return;
        }

        if (SteerOverride == null)
        {
            //Steer towards the aim point
            float   DesiredHeadingAngle = Vector3.Angle(AimPoint - transform.position, SegFwdDir);
            Vector3 crossDes            = Vector3.Cross(AimPoint - transform.position, SegFwdDir);
            if (crossDes.y > 0)
            {
                DesiredHeadingAngle = -DesiredHeadingAngle;
            }

            //Positive steer = right
            Steer = (DesiredHeadingAngle - HeadingAngle);
        }
        else
        {
            Steer = (float)SteerOverride;
        }

        //if (_bendPhase == BendPhase.Flick) Steer = _gpsNextBendAngle > 0 ? 40 : -40;
        if (_opponentCollisionDirec == CollisionDirection.Left && Steer < 0)
        {
            Steer = 10;
        }
        if (_opponentCollisionDirec == CollisionDirection.Right && Steer > 0)
        {
            Steer = -10;
        }

        if (_playerCollision)
        {
            if (Time.time - _playerCollisionTime > 3)
            {
                if (_playerGPS.IsOnRoad)
                {
                    if (!_gps.IsOnRoad)
                    {
                        Main.Instance.PopupMsg("Road Rage Bonus\n$20", Color.red);
                        Race.Current.HogBonus += 20;
                        UserDataManager.Instance.Data.Coins += 10;
                    }
                    else if (_playerGPS.CurrSegIdx - _gps.CurrSegIdx > 12 && _playerGPS.CurrSegIdx - _gps.CurrSegIdx < 100)
                    {
                        int pts = (_playerGPS.CurrSegIdx - _gps.CurrSegIdx) / 6;
                        Race.Current.HogBonus += pts;
                        Main.Instance.PopupMsg("Road Hog Bonus\n$" + pts.ToString(), Color.red);
                        UserDataManager.Instance.Data.Coins += pts;
                    }
                }
                _playerCollision = false;
            }
        }

        _inputBuffer.RecordInput(Accel, BrakeForce, Steer, Time.time);
    }