OldBlueWater/BlueWater/Assets/02.Scripts/Boid.cs

176 lines
6.4 KiB
C#
Raw Normal View History

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
using Random = UnityEngine.Random;
// ReSharper disable once CheckNamespace
namespace BlueWaterProject
{
public class Boid : MonoBehaviour
{
[Title("개체 설정")]
[SerializeField] private float obstacleDistance = 10;
[SerializeField] private float viewAngle = 120;
[SerializeField] private int maxNeighbourCount = 10;
[SerializeField] private float neighbourDistance = 6;
[Title("ETC")]
[SerializeField] private LayerMask boidUnitLayer;
[SerializeField] private LayerMask obstacleLayer;
private Boids myBoids;
private List<Boid> neighbours = new();
private Collider[] hitColliders;
private Coroutine findNeighbourCoroutine;
private Coroutine calculateEgoVectorCoroutine;
private Vector3 targetPos;
private Vector3 egoVector;
private float moveSpeed;
private float additionalSpeed = 0;
public void Init(Boids boids, float speed)
{
myBoids = boids;
moveSpeed = speed;
hitColliders = new Collider[maxNeighbourCount];
findNeighbourCoroutine = StartCoroutine("FindNeighbourCoroutine");
calculateEgoVectorCoroutine = StartCoroutine("CalculateEgoVectorCoroutine");
}
private void OnDrawGizmosSelected()
{
foreach (var neighbour in neighbours)
{
var myPos = transform.position;
Gizmos.color = Color.red;
Gizmos.DrawLine(myPos, neighbour.transform.position);
Gizmos.color = Color.blue;
Gizmos.DrawLine(myPos, myPos + targetPos);
}
}
private void Update()
{
if (additionalSpeed > 0)
additionalSpeed -= Time.deltaTime;
var cohesionPos = CalculateCohesionPos() * myBoids.CohesionWeight;
var alignmentPos = CalculateAlignmentPos() * myBoids.AlignmentWeight;
var separationPos = CalculateSeparationPos() * myBoids.SeparationWeight;
var boundsPos = CalculateBoundsVector() * myBoids.BoundsWeight;
var obstaclePos = CalculateObstacleVector() * myBoids.ObstacleWeight;
var egoPos = egoVector * myBoids.EgoWeight;
targetPos = cohesionPos + alignmentPos + separationPos + boundsPos + obstaclePos + egoPos;
targetPos = Vector3.Lerp(transform.forward, targetPos, Time.deltaTime);
targetPos = targetPos.normalized;
if (targetPos == Vector3.zero)
targetPos = egoVector;
transform.rotation = Quaternion.LookRotation(targetPos);
transform.position += targetPos * ((moveSpeed + additionalSpeed) * Time.deltaTime);
}
private IEnumerator CalculateEgoVectorCoroutine()
{
moveSpeed = Random.Range(myBoids.RandomSpeedRange.x, myBoids.RandomSpeedRange.y);
egoVector = Random.insideUnitSphere;
yield return new WaitForSeconds(Random.Range(1, 3f));
calculateEgoVectorCoroutine = StartCoroutine("CalculateEgoVectorCoroutine");
}
private IEnumerator FindNeighbourCoroutine()
{
neighbours.Clear();
var size = Physics.OverlapSphereNonAlloc(transform.position, neighbourDistance, hitColliders, boidUnitLayer);
for (var i = 0; i < size; i++)
{
if (Vector3.Angle(transform.forward, hitColliders[i].transform.position - transform.position) <= viewAngle)
{
neighbours.Add(hitColliders[i].GetComponent<Boid>());
}
}
yield return new WaitForSeconds(Random.Range(0.5f, 2f));
findNeighbourCoroutine = StartCoroutine("FindNeighbourCoroutine");
}
private Vector3 CalculateCohesionPos()
{
var cohesionPos = Vector3.zero;
if (neighbours.Count > 0)
{
cohesionPos = neighbours.Aggregate(cohesionPos, (current, neighbour) => current + neighbour.transform.position);
}
else
{
return cohesionPos;
}
cohesionPos /= neighbours.Count;
cohesionPos -= transform.position;
cohesionPos.Normalize();
return cohesionPos;
}
private Vector3 CalculateAlignmentPos()
{
var alignmentPos = transform.forward;
if (neighbours.Count > 0)
{
alignmentPos = neighbours.Aggregate(alignmentPos, (current, neighbour) => current + neighbour.transform.forward);
}
else
{
return alignmentPos;
}
alignmentPos /= neighbours.Count;
alignmentPos.Normalize();
return alignmentPos;
}
private Vector3 CalculateSeparationPos()
{
var separationPos = Vector3.zero;
if (neighbours.Count > 0)
{
separationPos = neighbours.Aggregate(separationPos, (current, neighbour) => current + (transform.position - neighbour.transform.position));
}
else
{
return separationPos;
}
separationPos /= neighbours.Count;
separationPos.Normalize();
return separationPos;
}
private Vector3 CalculateBoundsVector()
{
var myPos = transform.position;
var offsetToCenter = myBoids.BoundMeshRenderer.transform.position - myPos;
var insideBounds = myBoids.BoundMeshRenderer.bounds.Contains(myPos);
return insideBounds ? Vector3.zero : offsetToCenter.normalized;
}
private Vector3 CalculateObstacleVector()
{
var obstaclePos = Vector3.zero;
if (Physics.Raycast(transform.position,transform.forward, out var hit, obstacleDistance, obstacleLayer))
{
Debug.DrawLine(transform.position, hit.point, Color.black);
obstaclePos = hit.normal;
additionalSpeed = 10;
}
return obstaclePos;
}
}
}