I created a basic turning coroutine that lerps the ships rotation towards the desired quaternion while the ship is in a corner, you can now that with a tag or adding some sort of tag (like a bool, enum, string), to the Tile script.
The ship has two turning animations that roll the ship in the direction it is supposed to, to know which animation to play I used dot product. There’s an issue with this whole approach, the ships won’t move while in a corner, this means ships can get on top of one another, I fixed this, I explain how below. Here’s the entire original coroutine.
private IEnumerator RotateShip(Tile tile)
{
Quaternion startRotation = transform.rotation;
Vector3 lookTarget = tile.transform.position - transform.position;
lookTarget.y = 0;
Quaternion lookDirection = Quaternion.LookRotation(lookTarget, Vector3.up);
if (Vector3.Dot(transform.right, lookTarget) < 0) { animator.SetTrigger("TurningLeft"); }
else { animator.SetTrigger("TurningRight"); }
float rotationPercent = 0f;
while (rotationPercent < 1f)
{
rotationPercent += Time.deltaTime * rotationSpeed;
transform.rotation = Quaternion.Lerp(startRotation, lookDirection, rotationPercent);
yield return new WaitForEndOfFrame();
}
}
To fix the issue of ships getting on top of one another I make them move while turning.
private void MoveShip()
{
Vector3 positionWithHeight = path[objectToMoveTo].position;
positionWithHeight.y = transform.position.y;
transform.position = Vector3.MoveTowards(transform.position, positionWithHeight, speed * Time.deltaTime);
CheckRotationType(positionWithHeight);
if (objectToMoveTo + 1 < path.Count) { SetNextTile((transform.position - positionWithHeight).sqrMagnitude); }
else if (!missileFired) { Shoot(); }
}
private void CheckRotationType(Vector3 positionWithHeight)
{
if (path[objectToMoveTo].CompareTag("Corner")) { RotateSmoothly(); }
else { transform.LookAt(positionWithHeight, Vector3.up); }
}
private void RotateSmoothly()
{
Vector3 normalizedTilePosition = path[objectToMoveTo + 1].position - transform.position;
normalizedTilePosition.y = transform.position.y;
Quaternion rot = Quaternion.LookRotation(normalizedTilePosition);
if (!rotating) { StartCoroutine(AnimateTurn(normalizedTilePosition)); }
transform.rotation = Quaternion.Lerp(transform.rotation, rot, Time.deltaTime * rotationSpeed);
}
private IEnumerator AnimateTurn(Vector3 normalizedTilePosition)
{
rotating = true;
string animationToPlay = Vector3.Dot(transform.right, normalizedTilePosition) < 0 ? "TurningLeft" : "TurningRight";
anm.SetTrigger(animationToPlay);
yield return new WaitForSeconds(1.1f);
rotating = false;
}
The main difference is that the ship now needs to know if the next tile is a corner so it can start rotating before hand and that the moving doesn’t wait until the turn finishes.
Instead of caching the transform rotation before the turn, I’m using the transforms actual rotation, this makes the ship turn even if the t
value in the lerp function never reaches 1. This has a disadvantage, as the ship gets closer to the b
value the turn slows down and it never actually reaches the desired quaternion, so I still have to set it to the final desired rotation to avoid weirdly rotated ships, the advantage of this is that it looks slightly more natural.
I’m still using dot product to calculate which animation should be played, I’m just using the ternary operator instead of an if/then
statement.