TO SQUASH: shark movement.
This commit is contained in:
@@ -74,10 +74,20 @@ public class BasicBall : BallBase
|
||||
{
|
||||
if (collision.gameObject.CompareTag("Wall") ||
|
||||
collision.gameObject.CompareTag("BasicBall") ||
|
||||
collision.gameObject.CompareTag("FruitBall"))
|
||||
collision.gameObject.CompareTag("FruitBall") ||
|
||||
collision.gameObject.CompareTag("Shark"))
|
||||
{
|
||||
ContactPoint2D contact = collision.GetContact(0);
|
||||
speed = Vector2.Reflect(speed, contact.normal);
|
||||
|
||||
if (collision.gameObject.CompareTag("Shark"))
|
||||
{
|
||||
audioSource.PlayOneShot(bounceSound);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,5 +97,9 @@ public class EyeBall : BallBase
|
||||
ContactPoint2D contact = collision.GetContact(0);
|
||||
speed = Vector2.Reflect(speed, contact.normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,17 +86,28 @@ public class FruitBall : BallBase
|
||||
{
|
||||
if (collision.gameObject.CompareTag("Wall") ||
|
||||
collision.gameObject.CompareTag("BasicBall") ||
|
||||
collision.gameObject.CompareTag("FruitBall"))
|
||||
collision.gameObject.CompareTag("FruitBall") ||
|
||||
collision.gameObject.CompareTag("Shark"))
|
||||
{
|
||||
ContactPoint2D contact = collision.GetContact(0);
|
||||
speed = Vector2.Reflect(speed, contact.normal);
|
||||
|
||||
if (collision.gameObject.CompareTag("BasicBall") ||
|
||||
collision.gameObject.CompareTag("FruitBall"))
|
||||
collision.gameObject.CompareTag("FruitBall") ||
|
||||
collision.gameObject.CompareTag("Shark"))
|
||||
{
|
||||
speedIncreaseTimer = 2.0f; // Reset the timer to 2 seconds
|
||||
speedMultiplier = 20.0f; // Set the speed multiplier to 7x
|
||||
}
|
||||
|
||||
if (collision.gameObject.CompareTag("Shark"))
|
||||
{
|
||||
audioSource.PlayOneShot(bounceSound);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ public class GlassBall : BallBase
|
||||
Damage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void Damage()
|
||||
|
||||
@@ -143,6 +143,10 @@ public class NukeBall : BallBase
|
||||
transform.position = currentPosition;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFallTargetFromCurrentPosition()
|
||||
|
||||
@@ -107,6 +107,10 @@ public class OozeBall : BallBase
|
||||
ContactPoint2D contact = collision.GetContact(0);
|
||||
speed = Vector2.Reflect(speed, contact.normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void Split()
|
||||
|
||||
@@ -39,26 +39,97 @@ public class Shark : MonoBehaviour
|
||||
|
||||
private bool spawnFinished = false;
|
||||
|
||||
[SerializeField]
|
||||
private float normalMaxSpeed = 2.5f;
|
||||
|
||||
[SerializeField]
|
||||
private float turnRateDegreesPerSecond = 180f;
|
||||
|
||||
[SerializeField]
|
||||
private float speedChangeRate = 4f;
|
||||
|
||||
[SerializeField]
|
||||
private float targetReachedDistance = 0.15f;
|
||||
|
||||
[SerializeField]
|
||||
private float alignmentThresholdDegrees = 5f;
|
||||
|
||||
private SpriteRenderer sharkSpriteRenderer;
|
||||
|
||||
private Bounds? movementBounds = null;
|
||||
|
||||
private Vector2 currentTarget;
|
||||
|
||||
private bool hasTarget = false;
|
||||
|
||||
private float currentSpeed = 0f;
|
||||
|
||||
private float currentHeadingAngle = 0f;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
animator = GetComponent<Animator>();
|
||||
sharkSpriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
// Ensure movement transitions stay blocked until spawn animation finishes.
|
||||
animator.SetBool("canMove", false);
|
||||
|
||||
bool spawnLeft = Random.Range(0, 2) == 0;
|
||||
angle = spawnLeft ? 90 : 270;
|
||||
currentHeadingAngle = angle;
|
||||
|
||||
animator.SetBool("spawnLeft", spawnLeft);
|
||||
// Initialize angle before triggering spawn, so post-spawn movement starts in the correct direction.
|
||||
animator.SetInteger("angle", angle);
|
||||
|
||||
// Clear stale trigger state (useful when object is reused) before setting the selected spawn trigger.
|
||||
animator.ResetTrigger("spawnLeft");
|
||||
animator.ResetTrigger("spawnRight");
|
||||
animator.SetTrigger(spawnLeft ? "spawnLeft" : "spawnRight");
|
||||
audioSource.PlayOneShot(spawnSound);
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void FixedUpdate()
|
||||
{
|
||||
// Initialize gameBoardBounds.
|
||||
if (gameBoardSpriteRenderer != null && gameBoardBounds == null)
|
||||
{
|
||||
gameBoardBounds = gameBoardSpriteRenderer.bounds;
|
||||
}
|
||||
|
||||
// Build movement bounds by shrinking the board by 2x the shark's size total (1x on each side).
|
||||
if (gameBoardBounds != null && sharkSpriteRenderer != null && movementBounds == null)
|
||||
{
|
||||
Bounds sharkBounds = sharkSpriteRenderer.bounds;
|
||||
Bounds boardBounds = gameBoardBounds.Value;
|
||||
|
||||
float minX = boardBounds.min.x + sharkBounds.size.x;
|
||||
float maxX = boardBounds.max.x - sharkBounds.size.x;
|
||||
float minY = boardBounds.min.y + sharkBounds.size.y;
|
||||
float maxY = boardBounds.max.y - sharkBounds.size.y;
|
||||
|
||||
// Fallback to board center if the shrunken region collapses.
|
||||
if (minX > maxX)
|
||||
{
|
||||
float centerX = boardBounds.center.x;
|
||||
minX = centerX;
|
||||
maxX = centerX;
|
||||
}
|
||||
|
||||
if (minY > maxY)
|
||||
{
|
||||
float centerY = boardBounds.center.y;
|
||||
minY = centerY;
|
||||
maxY = centerY;
|
||||
}
|
||||
|
||||
Vector3 min = new Vector3(minX, minY, boardBounds.min.z);
|
||||
Vector3 max = new Vector3(maxX, maxY, boardBounds.max.z);
|
||||
movementBounds = new Bounds((min + max) * 0.5f, max - min);
|
||||
}
|
||||
|
||||
// Check if the spawn animation has finished.
|
||||
if (
|
||||
(animator.GetCurrentAnimatorStateInfo(0).IsName("SharkSpawnRight") ||
|
||||
@@ -67,6 +138,7 @@ public class Shark : MonoBehaviour
|
||||
)
|
||||
{
|
||||
spawnFinished = true;
|
||||
animator.SetBool("canMove", true);
|
||||
}
|
||||
|
||||
// Do stuff only if the spawn animation has finished.
|
||||
@@ -79,15 +151,13 @@ public class Shark : MonoBehaviour
|
||||
{
|
||||
animator.SetTrigger("killed");
|
||||
audioSource.PlayOneShot(deathSound);
|
||||
// TODO: award points to the player.
|
||||
player.score += sharkKillPoints;
|
||||
StartCoroutine(DestroyShark());
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Compute movement position and update the shark's speed and angle.
|
||||
// TODO: Apply 2x movement speed if the shark is angry.
|
||||
animator.SetInteger("angle", angle);
|
||||
UpdateMovement();
|
||||
animator.SetInteger("angle", angle);
|
||||
|
||||
if (!isAngry && !isTired && Random.value < angryProbability)
|
||||
{
|
||||
@@ -111,6 +181,84 @@ public class Shark : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMovement()
|
||||
{
|
||||
if (movementBounds == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 currentPosition = transform.position;
|
||||
|
||||
if (!hasTarget)
|
||||
{
|
||||
currentTarget = ChooseRandomTarget();
|
||||
hasTarget = true;
|
||||
}
|
||||
|
||||
Vector2 toTarget = currentTarget - currentPosition;
|
||||
if (toTarget.sqrMagnitude <= targetReachedDistance * targetReachedDistance)
|
||||
{
|
||||
currentTarget = ChooseRandomTarget();
|
||||
toTarget = currentTarget - currentPosition;
|
||||
}
|
||||
|
||||
if (toTarget.sqrMagnitude > 0.0001f)
|
||||
{
|
||||
float desiredAngle = AngleFromDirection(toTarget.normalized);
|
||||
float angleDelta = Mathf.Abs(Mathf.DeltaAngle(currentHeadingAngle, desiredAngle));
|
||||
|
||||
currentHeadingAngle = Mathf.MoveTowardsAngle(
|
||||
currentHeadingAngle,
|
||||
desiredAngle,
|
||||
turnRateDegreesPerSecond * Time.fixedDeltaTime
|
||||
);
|
||||
|
||||
float dynamicMaxSpeed = isAngry && !isTired ? normalMaxSpeed * 2f : normalMaxSpeed;
|
||||
float turningSpeed = normalMaxSpeed * (2f / 3f);
|
||||
float desiredSpeed = angleDelta <= alignmentThresholdDegrees ? dynamicMaxSpeed : turningSpeed;
|
||||
|
||||
currentSpeed = Mathf.MoveTowards(
|
||||
currentSpeed,
|
||||
desiredSpeed,
|
||||
speedChangeRate * Time.fixedDeltaTime
|
||||
);
|
||||
|
||||
Vector2 forward = DirectionFromAngle(currentHeadingAngle);
|
||||
Vector2 nextPosition = currentPosition + forward * (currentSpeed * Time.fixedDeltaTime);
|
||||
transform.position = new Vector3(nextPosition.x, nextPosition.y, transform.position.z);
|
||||
|
||||
angle = QuantizeAngleToTen(currentHeadingAngle);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 ChooseRandomTarget()
|
||||
{
|
||||
Bounds bounds = movementBounds.Value;
|
||||
float targetX = Random.Range(bounds.min.x, bounds.max.x);
|
||||
float targetY = Random.Range(bounds.min.y, bounds.max.y);
|
||||
return new Vector2(targetX, targetY);
|
||||
}
|
||||
|
||||
private static float AngleFromDirection(Vector2 direction)
|
||||
{
|
||||
float raw = Mathf.Atan2(-direction.x, -direction.y) * Mathf.Rad2Deg;
|
||||
return (raw + 360f) % 360f;
|
||||
}
|
||||
|
||||
private static Vector2 DirectionFromAngle(float degrees)
|
||||
{
|
||||
float radians = degrees * Mathf.Deg2Rad;
|
||||
return new Vector2(-Mathf.Sin(radians), -Mathf.Cos(radians));
|
||||
}
|
||||
|
||||
private static int QuantizeAngleToTen(float degrees)
|
||||
{
|
||||
int quantized = Mathf.RoundToInt(degrees / 10f) * 10;
|
||||
quantized = ((quantized % 360) + 360) % 360;
|
||||
return quantized;
|
||||
}
|
||||
|
||||
private IEnumerator DestroyShark()
|
||||
{
|
||||
// Wait two loops of the death animation before destroying the shark object.
|
||||
|
||||
@@ -14,7 +14,7 @@ public class SharkSpawner : MonoBehaviour
|
||||
|
||||
private bool isActive = true;
|
||||
|
||||
private static float spawnProbability = 0.9f;
|
||||
private static float spawnProbability = 0.5f;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
|
||||
Reference in New Issue
Block a user