2023-12-28 07:29:52 +00:00
|
|
|
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;
|
2023-12-29 05:59:04 +00:00
|
|
|
[SerializeField] private float avoidAdditionalSpeed = 10;
|
2023-12-28 07:29:52 +00:00
|
|
|
|
|
|
|
[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;
|
2023-12-29 05:59:04 +00:00
|
|
|
additionalSpeed = avoidAdditionalSpeed;
|
2023-12-28 07:29:52 +00:00
|
|
|
}
|
|
|
|
return obstaclePos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|