public Vector3 GetForce(Steering steering) { if (steering.GetVelocity().sqrMagnitude == 0f) { float initialAngle = Random.Range(0f, 2 * Mathf.PI); float sinAngle = Mathf.Sin(initialAngle); wanderPoint = 2.4f * new Vector3(Mathf.Cos(initialAngle), Steering.YMult * sinAngle, Steering.ZMult * sinAngle); } if (steering.GetVelocity().sqrMagnitude > 0f && !hasMoved) { hasMoved = true; wanderPoint = SteeringUtilities.scaledVector(2.4f, steering.GetVelocity()); } float xNoise = Time.fixedDeltaTime * wanderNoise * Random.Range(-1f, 1f); float yzNoise = Time.fixedDeltaTime * wanderNoise * Random.Range(-1f, 1f); wanderPoint += new Vector3(xNoise, Steering.YMult * yzNoise, Steering.ZMult * yzNoise); Vector3 forwardPoint = steering.GetPosition() + SteeringUtilities.scaledVector(1.41f, steering.GetVelocity()); // Constrain the wander point to the unit circle in front of the player. wanderPoint = forwardPoint + (wanderPoint - forwardPoint).normalized; //return SteeringUtilities.getForceForDirection(steering, wanderDirection); //SteeringUtilities.drawDebugCircle(forwardPoint, 1f, Color.black, 32); SteeringUtilities.drawDebugPoint(wanderPoint, Color.red); return(SteeringUtilities.getSeekForce(steering, wanderPoint)); }
public Vector3 GetForce(Steering steering) { if (isResponsibleForNeighbourUpdate) { // TODO: figure out how to add or remove neighbours automatically here or in neighbours neighbours.Update(); } /* * Avoid collisions by determining for each neighbor when their paths will be closest to each other * and then steer laterally to avoid collision. * https://www.red3d.com/cwr/steer/Unaligned.html */ float distanceToBeginReacting = 4f * (steering.GetSize() + steering.GetStoppingDistance()); //Debug.Log(doubleStopDistance); foreach (Neighbour <Steering> neighbour in neighbours) { if (neighbour.dd > distanceToBeginReacting * distanceToBeginReacting) { break; } Steering otherUnit = neighbour.obj; Vector3 offset = otherUnit.GetPosition() - steering.GetPosition(); Vector3 relativeVelocity = steering.GetVelocity() - otherUnit.GetVelocity(); // Decrease the timeToCollision so that closestOffset is nonZero. float combinedSize = steering.GetSize() + otherUnit.GetSize(); float timeToCollision = (offset.magnitude - combinedSize) / SteeringUtilities.parallelComponent(relativeVelocity, offset).magnitude; if (timeToCollision > 2 * steering.GetStoppingTime()) { continue; } Vector3 closestOffset = (offset - (timeToCollision * relativeVelocity)); float preferredDistance = 1.5f * combinedSize; if (closestOffset.sqrMagnitude > preferredDistance * preferredDistance) { continue; } SteeringUtilities.drawDebugVector(steering, timeToCollision * steering.GetVelocity(), Color.cyan); SteeringUtilities.drawDebugPoint(steering.GetPosition() + timeToCollision * steering.GetVelocity(), Color.cyan); SteeringUtilities.drawDebugVector(otherUnit, timeToCollision * otherUnit.GetVelocity(), Color.cyan); SteeringUtilities.drawDebugPoint(otherUnit.GetPosition() + timeToCollision * otherUnit.GetVelocity(), Color.cyan); // TODO: for head-on collisions steer to the right // Steer in the direction of the component of the collision normal that is perpindicular to the current velocity. // This way the unit will turn instead of just slowing down. // TODO: use an amount of acceleration proportionate to the time until collision and the severity of the collision return(SteeringUtilities.scaledVector(steering.GetAcceleration(), SteeringUtilities.perpindicularComponent(-closestOffset, steering.GetVelocity()))); //return SteeringUtilities.getForceForDirection(steering, -closestOffset); } return(Vector3.zero); }
public static Vector3 getForceForDesiredVelocity(Steering steering, Vector3 desiredVelocity) { // The velocity will be changed by force * deltaTime, so the desired force is deltaV/deltaTime. Vector3 desiredForce = (desiredVelocity - steering.GetVelocity()) / Time.fixedDeltaTime; return(scaledDownVector(steering.GetAcceleration(), desiredForce)); }
public Vector3 GetForce(Steering steering) { float raycastDistance = 2f * (2f * steering.GetSize()) + 2f * (steering.GetMaxSpeed() * steering.GetMaxSpeed() / steering.GetAcceleration()); // TODO: send out 3 rays and define static quaternions to determine the rotated direction vectors. Vector3 directionVector = steering.GetVelocity(); // TODO: update the side raycast lengths based on steering's size, and the center raycast based on speed. // TODO: constrain hit normals to the relevant plane in case the collider has a complicated shape Vector3 hitNormal1 = RaycastNormal(steering, (Steering.UseXZ ? xzRotateLeft : xyRotateLeft) * directionVector, raycastDistance * 0.5f); Vector3 hitNormal2 = RaycastNormal(steering, directionVector, raycastDistance); Vector3 hitNormal3 = RaycastNormal(steering, (Steering.UseXZ ? xzRotateRight : xyRotateRight) * directionVector, raycastDistance * 0.5f); // If multiple raycasts hit, sum the normals. // TODO: weight the normals differently based on which collision point is closest. Vector3 combinedNormal = SteeringUtilities.scaledDownVector(1f, hitNormal1 + hitNormal2 + hitNormal3); if (combinedNormal.sqrMagnitude > 0f) { // For the normal of the wall ahead, steer to have a velocity that is perpindicular to it. Vector3 leftVector = new Vector3(combinedNormal.y + combinedNormal.z, -Steering.YMult * combinedNormal.x, -Steering.ZMult * combinedNormal.x); Vector3 rightVector = new Vector3((-combinedNormal.y) - combinedNormal.z, Steering.YMult * combinedNormal.x, Steering.ZMult * combinedNormal.x); float rayLeftDistance = RaycastDistance(steering, leftVector, raycastDistance); float rayRightDistance = RaycastDistance(steering, rightVector, raycastDistance); // TODO: Consider updating the logic when approaching a corner. // Currently the unit kind of turns toward the side that they are coming from even if the other turn is shorter. // Case 1: Wall in front, wall on left, wall on right. if (rayLeftDistance < raycastDistance && rayRightDistance < raycastDistance) { // Move towards the left/right wall that is closer. That should be moving away from the corner point. if (rayLeftDistance < rayRightDistance) { return(SteeringUtilities.getForceForDirection(steering, leftVector)); } else // rayRightDistance < rayLeftDistance { return(SteeringUtilities.getForceForDirection(steering, rightVector)); } } // Case 2: Wall in front, wall on left else if (rayLeftDistance < raycastDistance) { return(SteeringUtilities.getForceForDirection(steering, rightVector)); } // Case 3: Wall in front, wall on right else if (rayRightDistance < raycastDistance) { return(SteeringUtilities.getForceForDirection(steering, leftVector)); } // Case 4: Wall in front else { // Move towards whichever of left or right is closer to the current velocity Vector3 perpindicularVector = SteeringUtilities.perpindicularComponent(combinedNormal, steering.GetVelocity()); // Edge case: the normal is parallel to velocity. In this case, pick one of the two perpindicular vectors at random. if (perpindicularVector == Vector3.zero) { Debug.Log("Exact perpindicular!"); int randomSign = Random.value < .5 ? 1 : -1; perpindicularVector = new Vector3((-combinedNormal.y) - combinedNormal.z, Steering.YMult * combinedNormal.x * randomSign, Steering.ZMult * combinedNormal.x * randomSign); } return(SteeringUtilities.getForceForDirection(steering, perpindicularVector)); } } return(Vector3.zero); }
public Vector3 GetForce(Steering steering) { Vector3 currentOffset = target.GetPosition() - steering.GetPosition(); float dist = currentOffset.magnitude; Vector3 unitV = steering.GetVelocity().normalized; float parallelness = Vector3.Dot(unitV, target.GetVelocity().normalized); float forwardness = Vector3.Dot(unitV, currentOffset / dist); float halfsqrt2 = 0.707f; int f = SteeringUtilities.intervalComp(forwardness, -halfsqrt2, halfsqrt2); int p = SteeringUtilities.intervalComp(parallelness, -halfsqrt2, halfsqrt2); // approximate how far to lead the target float timeFactor = 1f; // case logic based on (ahead, aside, behind) X (parallel, perp, anti-parallel) switch (f) { case 1: //target is ahead switch (p) { case 1: timeFactor = 4f; break; case 0: timeFactor = 1.8f; break; case -1: timeFactor = 0.85f; break; } break; case 0: //target is aside switch (p) { case 1: timeFactor = 1f; break; case 0: timeFactor = 0.8f; break; case -1: timeFactor = 4f; break; } break; case -1: //target is behind switch (p) { case 1: timeFactor = 0.5f; break; case 0: timeFactor = 2f; break; case -1: timeFactor = 2f; break; } break; } // Multiply the timeToArrive by some approximate constants based on how similar the two velocities are. float approximateArrivalTime = dist / steering.GetMaxSpeed(); float improvedArrivalTimeEstimate = Mathf.Min(MAX_PREDICTION_TIME, approximateArrivalTime * timeFactor); Vector3 newTargetPosition = (Vector3)target.GetPosition() + improvedArrivalTimeEstimate * target.GetVelocity(); SteeringUtilities.drawDebugVector(target, newTargetPosition - target.GetPosition(), Color.white); SteeringUtilities.drawDebugVector(steering, newTargetPosition - steering.GetPosition(), Color.magenta); return(SteeringUtilities.getForceForDirection(steering, newTargetPosition - steering.GetPosition())); }