1 using System;
2 using
UnityEngine;
3 using
Random = UnityEngine.Random;
4
5 namespace
UnityStandardAssets.Vehicles.Car
6 {
7     
[RequireComponent(typeof (CarController))]
8     
public class CarAIControl : MonoBehaviour
9     {
10         
public enum BrakeCondition
11         {
12             NeverBrake,
// the car simply accelerates at full throttle all the time.
13             TargetDirectionDifference,
// the car will brake according to the upcoming change in direction of the target. Useful for route-based AI, slowing for corners.
14             TargetDistance,
// the car will brake as it approaches its target, regardless of the target's direction. Useful if you want the car to
15                                         
// head for a stationary target and come to rest when it arrives there.
16         }
17
18         
// This script provides input to the car controller in the same way that the user control script does.
19         
// As such, it is really 'driving' the car, with no special physics or animation tricks to make the car behave properly.
20
21         
// "wandering" is used to give the cars a more human, less robotic feel. They can waver slightly
22         
// in speed and direction while driving towards their target.
23
24         [SerializeField] [Range(
0, 1)] private float m_CautiousSpeedFactor = 0.05f; // percentage of max speed to use when being maximally cautious
25         [SerializeField] [Range(
0, 180)] private float m_CautiousMaxAngle = 50f; // angle of approaching corner to treat as warranting maximum caution
26         [SerializeField]
private float m_CautiousMaxDistance = 100f; // distance at which distance-based cautiousness begins
27         [SerializeField]
private float m_CautiousAngularVelocityFactor = 30f; // how cautious the AI should be when considering its own current angular velocity (i.e. easing off acceleration if spinning!)
28         [SerializeField]
private float m_SteerSensitivity = 0.05f; // how sensitively the AI uses steering input to turn to the desired direction
29         [SerializeField]
private float m_AccelSensitivity = 0.04f; // How sensitively the AI uses the accelerator to reach the current desired speed
30         [SerializeField]
private float m_BrakeSensitivity = 1f; // How sensitively the AI uses the brake to reach the current desired speed
31         [SerializeField]
private float m_LateralWanderDistance = 3f; // how far the car will wander laterally towards its target
32         [SerializeField]
private float m_LateralWanderSpeed = 0.1f; // how fast the lateral wandering will fluctuate
33         [SerializeField] [Range(
0, 1)] private float m_AccelWanderAmount = 0.1f; // how much the cars acceleration will wander
34         [SerializeField]
private float m_AccelWanderSpeed = 0.1f; // how fast the cars acceleration wandering will fluctuate
35         [SerializeField]
private BrakeCondition m_BrakeCondition = BrakeCondition.TargetDistance; // what should the AI consider when accelerating/braking?
36         [SerializeField]
private bool m_Driving; // whether the AI is currently actively driving or stopped.
37         [SerializeField]
private Transform m_Target; // 'target' the target object to aim for.
38         [SerializeField]
private bool m_StopWhenTargetReached; // should we stop driving when we reach the target?
39         [SerializeField]
private float m_ReachTargetThreshold = 2; // proximity to target to consider we 'reached' it, and stop driving.
40
41         
private float m_RandomPerlin; // A random value for the car to base its wander on (so that AI cars don't all wander in the same pattern)
42         
private CarController m_CarController; // Reference to actual car controller we are controlling
43         
private float m_AvoidOtherCarTime; // time until which to avoid the car we recently collided with
44         
private float m_AvoidOtherCarSlowdown; // how much to slow down due to colliding with another car, whilst avoiding
45         
private float m_AvoidPathOffset; // direction (-1 or 1) in which to offset path to avoid other car, whilst avoiding
46         
private Rigidbody m_Rigidbody;
47
48
49         
private void Awake()
50         {
51             
// get the car controller reference
52             m_CarController = GetComponent<CarController>();
53
54             
// give the random perlin a random value
55             m_RandomPerlin = Random.
value*100;
56
57             m_Rigidbody = GetComponent<Rigidbody>();
58         }
59
60
61         
private void FixedUpdate()
62         {
63             
if (m_Target == null || !m_Driving)
64             {
65                 
// Car should not be moving,
66                 
// use handbrake to stop
67                 m_CarController.Move(
0, 0, -1f, 1f);
68             }
69             
else
70             {
71                 Vector3 fwd = transform.forward;
72                 
if (m_Rigidbody.velocity.magnitude > m_CarController.MaxSpeed*0.1f)
73                 {
74                     fwd = m_Rigidbody.velocity;
75                 }
76
77                 
float desiredSpeed = m_CarController.MaxSpeed;
78
79                 
// now it's time to decide if we should be slowing down...
80                 
switch (m_BrakeCondition)
81                 {
82                     
case BrakeCondition.TargetDirectionDifference:
83                         {
84                             
// the car will brake according to the upcoming change in direction of the target. Useful for route-based AI, slowing for corners.
85
86                             
// check out the angle of our target compared to the current direction of the car
87                             
float approachingCornerAngle = Vector3.Angle(m_Target.forward, fwd);
88
89                             
// also consider the current amount we're turning, multiplied up and then compared in the same way as an upcoming corner angle
90                             
float spinningAngle = m_Rigidbody.angularVelocity.magnitude*m_CautiousAngularVelocityFactor;
91
92                             
// if it's different to our current angle, we need to be cautious (i.e. slow down) a certain amount
93                             
float cautiousnessRequired = Mathf.InverseLerp(0, m_CautiousMaxAngle,
94                                                                            Mathf.Max(spinningAngle,
95                                                                                      approachingCornerAngle));
96                             desiredSpeed = Mathf.Lerp(m_CarController.MaxSpeed, m_CarController.MaxSpeed*m_CautiousSpeedFactor,
97                                                       cautiousnessRequired);
98                             
break;
99                         }
100
101                     
case BrakeCondition.TargetDistance:
102                         {
103                             
// the car will brake as it approaches its target, regardless of the target's direction. Useful if you want the car to
104                             
// head for a stationary target and come to rest when it arrives there.
105
106                             
// check out the distance to target
107                             Vector3 delta = m_Target.position - transform.position;
108                             
float distanceCautiousFactor = Mathf.InverseLerp(m_CautiousMaxDistance, 0, delta.magnitude);
109
110                             
// also consider the current amount we're turning, multiplied up and then compared in the same way as an upcoming corner angle
111                             
float spinningAngle = m_Rigidbody.angularVelocity.magnitude*m_CautiousAngularVelocityFactor;
112
113                             
// if it's different to our current angle, we need to be cautious (i.e. slow down) a certain amount
114                             
float cautiousnessRequired = Mathf.Max(
115                                 Mathf.InverseLerp(
0, m_CautiousMaxAngle, spinningAngle), distanceCautiousFactor);
116                             desiredSpeed = Mathf.Lerp(m_CarController.MaxSpeed, m_CarController.MaxSpeed*m_CautiousSpeedFactor,
117                                                       cautiousnessRequired);
118                             
break;
119                         }
120
121                     
case BrakeCondition.NeverBrake:
122                         
break;
123                 }
124
125                 
// Evasive action due to collision with other cars:
126
127                 
// our target position starts off as the 'real' target position
128                 Vector3 offsetTargetPos = m_Target.position;
129
130                 
// if are we currently taking evasive action to prevent being stuck against another car:
131                 
if (Time.time < m_AvoidOtherCarTime)
132                 {
133                     
// slow down if necessary (if we were behind the other car when collision occured)
134                     desiredSpeed *= m_AvoidOtherCarSlowdown;
135
136                     
// and veer towards the side of our path-to-target that is away from the other car
137                     offsetTargetPos += m_Target.right*m_AvoidPathOffset;
138                 }
139                 
else
140                 {
141                     
// no need for evasive action, we can just wander across the path-to-target in a random way,
142                     
// which can help prevent AI from seeming too uniform and robotic in their driving
143                     offsetTargetPos += m_Target.right*
144                                        (Mathf.PerlinNoise(Time.time*m_LateralWanderSpeed, m_RandomPerlin)*
2 - 1)*
145                                        m_LateralWanderDistance;
146                 }
147
148                 
// use different sensitivity depending on whether accelerating or braking:
149                 
float accelBrakeSensitivity = (desiredSpeed < m_CarController.CurrentSpeed)
150                                                   ? m_BrakeSensitivity
151                                                   : m_AccelSensitivity;
152
153                 
// decide the actual amount of accel/brake input to achieve desired speed.
154                 
float accel = Mathf.Clamp((desiredSpeed - m_CarController.CurrentSpeed)*accelBrakeSensitivity, -1, 1);
155
156                 
// add acceleration 'wander', which also prevents AI from seeming too uniform and robotic in their driving
157                 
// i.e. increasing the accel wander amount can introduce jostling and bumps between AI cars in a race
158                 accel *= (
1 - m_AccelWanderAmount) +
159                          (Mathf.PerlinNoise(Time.time*m_AccelWanderSpeed, m_RandomPerlin)*m_AccelWanderAmount);
160
161                 
// calculate the local-relative position of the target, to steer towards
162                 Vector3 localTarget = transform.InverseTransformPoint(offsetTargetPos);
163
164                 
// work out the local angle towards the target
165                 
float targetAngle = Mathf.Atan2(localTarget.x, localTarget.z)*Mathf.Rad2Deg;
166
167                 
// get the amount of steering needed to aim the car towards the target
168                 
float steer = Mathf.Clamp(targetAngle*m_SteerSensitivity, -1, 1)*Mathf.Sign(m_CarController.CurrentSpeed);
169
170                 
// feed input to the car controller.
171                 m_CarController.Move(steer, accel, accel,
0f);
172
173                 
// if appropriate, stop driving when we're close enough to the target.
174                 
if (m_StopWhenTargetReached && localTarget.magnitude < m_ReachTargetThreshold)
175                 {
176                     m_Driving =
false;
177                 }
178             }
179         }
180
181
182         
private void OnCollisionStay(Collision col)
183         {
184             
// detect collision against other cars, so that we can take evasive action
185             
if (col.rigidbody != null)
186             {
187                 
var otherAI = col.rigidbody.GetComponent<CarAIControl>();
188                 
if (otherAI != null)
189                 {
190                     
// we'll take evasive action for 1 second
191                     m_AvoidOtherCarTime = Time.time +
1;
192
193                     
// but who's in front?...
194                     
if (Vector3.Angle(transform.forward, otherAI.transform.position - transform.position) < 90)
195                     {
196                         
// the other ai is in front, so it is only good manners that we ought to brake...
197                         m_AvoidOtherCarSlowdown =
0.5f;
198                     }
199                     
else
200                     {
201                         
// we're in front! ain't slowing down for anybody...
202                         m_AvoidOtherCarSlowdown =
1;
203                     }
204
205                     
// both cars should take evasive action by driving along an offset from the path centre,
206                     
// away from the other car
207                     
var otherCarLocalDelta = transform.InverseTransformPoint(otherAI.transform.position);
208                     
float otherCarAngle = Mathf.Atan2(otherCarLocalDelta.x, otherCarLocalDelta.z);
209                     m_AvoidPathOffset = m_LateralWanderDistance*-Mathf.Sign(otherCarAngle);
210                 }
211             }
212         }
213
214
215         
public void SetTarget(Transform target)
216         {
217             m_Target = target;
218             m_Driving =
true;
219         }
220     }
221 }


Gõ tìm kiếm nhanh...