Client LuaCsForBarotrauma
SteeringManager.cs
1 using FarseerPhysics.Dynamics;
2 using Microsoft.Xna.Framework;
3 using System;
4 
5 namespace Barotrauma
6 {
8  {
9  protected const float CircleDistance = 2.5f;
10  protected const float CircleRadius = 0.3f;
11 
12  protected const float RayCastInterval = 0.5f;
13 
14  protected ISteerable host;
15 
16  protected Vector2 steering;
17 
18  private float lastRayCastTime;
19 
20  private bool avoidRayCastHit;
21 
22  public Vector2 AvoidDir { get; private set; }
23  public Vector2 AvoidRayCastHitPosition { get; private set; }
24  public Vector2 AvoidLookAheadPos { get; private set; }
25 
26  private float wanderAngle;
27 
28  public float WanderAngle
29  {
30  get { return wanderAngle; }
31  set { wanderAngle = value; }
32  }
33 
35  {
36  this.host = host;
37 
38  wanderAngle = Rand.Range(0.0f, MathHelper.TwoPi);
39  }
40 
41  public void SteeringSeek(Vector2 targetSimPos, float weight = 1)
42  {
43  steering += DoSteeringSeek(targetSimPos, weight);
44  }
45 
46  public void SteeringWander(float weight = 1, bool avoidWanderingOutsideLevel = false)
47  {
48  steering += DoSteeringWander(weight, avoidWanderingOutsideLevel);
49  }
50 
51  public void SteeringAvoid(float deltaTime, float lookAheadDistance, float weight = 1)
52  {
53  steering += DoSteeringAvoid(deltaTime, lookAheadDistance, weight);
54  }
55 
56  public void SteeringManual(float deltaTime, Vector2 velocity)
57  {
58  if (MathUtils.IsValid(velocity))
59  {
60  steering += velocity;
61  }
62  }
63 
64  public void Reset()
65  {
66  steering = Vector2.Zero;
67  }
68 
69  public void ResetX()
70  {
71  steering.X = 0.0f;
72  }
73 
74  public void ResetY()
75  {
76  steering.Y = 0.0f;
77  }
78 
80  public virtual void Update(float speed)
81  {
82  if (steering == Vector2.Zero || !MathUtils.IsValid(steering))
83  {
84  steering = Vector2.Zero;
85  host.Steering = Vector2.Zero;
86  return;
87  }
88  if (steering.LengthSquared() > speed * speed)
89  {
90  // Can't steer faster than the max speed.
91  steering = Vector2.Normalize(steering) * Math.Abs(speed);
92  }
93  if (host is AIController aiController && aiController?.Character.CharacterHealth.GetAfflictionOfType("invertcontrols".ToIdentifier()) != null)
94  {
95  steering = -steering;
96  }
98  }
99 
100  protected virtual Vector2 DoSteeringSeek(Vector2 target, float weight)
101  {
102  Vector2 targetVel = target - host.SimPosition;
103 
104  if (targetVel.LengthSquared() < 0.00001f) { return Vector2.Zero; }
105 
106  targetVel = Vector2.Normalize(targetVel) * weight;
107  // TODO: the code below doesn't quite work as it should, and I'm not sure what the purpose of it is/was.
108  // So, we'll just return the targetVel for now, as it produces smooth results.
109  return targetVel;
110 
111  //Vector2 newSteering = targetVel - host.Steering;
112 
113  //if (newSteering == Vector2.Zero) return Vector2.Zero;
114 
115  //float steeringSpeed = (newSteering + host.Steering).Length();
116  //if (steeringSpeed > Math.Abs(weight))
117  //{
118  // newSteering = Vector2.Normalize(newSteering) * Math.Abs(weight);
119  //}
120 
121  //return newSteering;
122  }
123 
124  protected virtual Vector2 DoSteeringWander(float weight, bool avoidWanderingOutsideLevel)
125  {
126  Vector2 circleCenter = (host.Steering == Vector2.Zero) ? Vector2.UnitY : host.Steering;
127  circleCenter = Vector2.Normalize(circleCenter) * CircleDistance;
128 
129  Vector2 displacement = new Vector2(
130  (float)Math.Cos(wanderAngle),
131  (float)Math.Sin(wanderAngle));
132  displacement *= CircleRadius;
133 
134  float angleChange = 1.5f;
135 
136  wanderAngle += Rand.Range(0.0f, 1.0f) * angleChange - angleChange * 0.5f;
137 
138  Vector2 newSteering = circleCenter + displacement;
139  if (avoidWanderingOutsideLevel && Level.Loaded != null)
140  {
141  float margin = 5000.0f;
142  if (host.WorldPosition.X < -margin)
143  {
144  // Too far left
145  newSteering.X += (-margin - host.WorldPosition.X) * weight / margin;
146  }
147  else if (host.WorldPosition.X > Level.Loaded.Size.X - margin)
148  {
149  // Too far right
150  newSteering.X -= (host.WorldPosition.X - (Level.Loaded.Size.X - margin)) * weight / margin;
151  }
152  }
153 
154  float steeringSpeed = (newSteering + host.Steering).Length();
155  if (steeringSpeed > weight)
156  {
157  newSteering = Vector2.Normalize(newSteering) * weight;
158  }
159 
160 
161  return newSteering;
162  }
163 
164  protected virtual Vector2 DoSteeringAvoid(float deltaTime, float lookAheadDistance, float weight, Vector2? heading = null)
165  {
166  if (steering == Vector2.Zero || host.Steering == Vector2.Zero)
167  {
168  return Vector2.Zero;
169  }
170 
171  float maxDistance = lookAheadDistance;
172  if (Timing.TotalTime >= lastRayCastTime + RayCastInterval)
173  {
174  avoidRayCastHit = false;
175  AvoidLookAheadPos = host.SimPosition + Vector2.Normalize(host.Steering) * maxDistance;
176  lastRayCastTime = (float)Timing.TotalTime;
178  if (closestBody != null)
179  {
180  avoidRayCastHit = true;
183  //add a bit of randomness
184  AvoidDir = MathUtils.RotatePoint(AvoidDir, Rand.Range(-0.15f, 0.15f));
185  //wait a bit longer for the next raycast
186  lastRayCastTime += RayCastInterval;
187  }
188  }
189 
190  if (AvoidDir.LengthSquared() < 0.0001f) { return Vector2.Zero; }
191 
192  //if raycast hit nothing, lerp avoid dir to zero
193  if (!avoidRayCastHit)
194  {
195  AvoidDir -= Vector2.Normalize(AvoidDir) * deltaTime * 0.5f;
196  }
197 
198  Vector2 diff = AvoidRayCastHitPosition - host.SimPosition;
199  float dist = diff.Length();
200 
201  //> 0 when heading in the same direction as the obstacle, < 0 when away from it
202  float dot = MathHelper.Clamp(Vector2.Dot(diff / dist, host.Steering), 0.0f, 1.0f);
203  if (dot < 0) { return Vector2.Zero; }
204 
205  return AvoidDir * dot * weight * MathHelper.Clamp(1.0f - dist / lookAheadDistance, 0.0f, 1.0f);
206  }
207  }
208 }
Affliction GetAfflictionOfType(Identifier afflictionType, bool allowLimbAfflictions=true)
virtual Vector2 DoSteeringWander(float weight, bool avoidWanderingOutsideLevel)
void SteeringManual(float deltaTime, Vector2 velocity)
void SteeringSeek(Vector2 targetSimPos, float weight=1)
void SteeringWander(float weight=1, bool avoidWanderingOutsideLevel=false)
virtual Vector2 DoSteeringAvoid(float deltaTime, float lookAheadDistance, float weight, Vector2? heading=null)
void SteeringAvoid(float deltaTime, float lookAheadDistance, float weight=1)
virtual void Update(float speed)
Update speed for the steering. Should normally match the characters current animation speed.
SteeringManager(ISteerable host)
virtual Vector2 DoSteeringSeek(Vector2 target, float weight)
static Body CheckVisibility(Vector2 rayStart, Vector2 rayEnd, bool ignoreLevel=false, bool ignoreSubs=false, bool ignoreSensors=true, bool ignoreDisabledWalls=true, bool ignoreBranches=true, Predicate< Fixture > blocksVisibilityPredicate=null)
Check visibility between two points (in sim units).