OldBlueWater/BlueWater/Assets/02.Scripts/Boid.cs
NTG 2da1b44a53 Closes #70 #79 #87
+ Layer Boids, Boid로 분리
+ Fish의 로직을 Boids(군집) 알고리즘으로 변경
+ 군집에 이펙트를 통해 낚시 가시성 추가
+ 테스트용 군집 애니메이션 추가
+ Epic Toon EX 에셋의 스크립트 수정 버전
  ㄴ ParticleWeapon(Layer 선택, UnityEvent Hit 델리게이트 기능 추가) 사용
+ Cannon의 x축 회전 고정
+ Cannon이 공격한 Layer에 따른 기능 변경
+ DataManager에 PlayerInventory 추가
  ㄴ 초창기에 사용한 코딩 삭제
+ 초창기에 사용했던 스크립트들 일부 정리
2024-01-03 06:39:53 +09:00

195 lines
7.0 KiB
C#

using System;
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
{
[field: Title("FishInfo")]
[field: SerializeField] public FishInfo FishInfo { get; private set; }
[field: SerializeField] public Vector2 RandomCount { get; private set; } = new(1, 4);
[Title("개체 설정")]
[SerializeField] private float obstacleDistance = 10;
[SerializeField] private float viewAngle = 120;
[SerializeField] private int maxNeighbourCount = 10;
[SerializeField] private float neighbourDistance = 6;
[SerializeField] private float avoidAdditionalSpeed = 10;
[Title("Extensions Data")]
[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 ;
public void Init(Boids boids, float speed)
{
myBoids = boids;
moveSpeed = speed;
hitColliders = new Collider[maxNeighbourCount];
FishInfo.AddCount(Random.Range((int)RandomCount.x, (int)RandomCount.y));
findNeighbourCoroutine ??= StartCoroutine("FindNeighbourCoroutine");
calculateEgoVectorCoroutine ??= StartCoroutine("CalculateEgoVectorCoroutine");
}
private void OnDrawGizmosSelected()
{
foreach (var neighbour in neighbours)
{
if (neighbour == null) continue;
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;
var validNeighbours = 0;
foreach (var neighbour in neighbours.Where(neighbour => neighbour != null))
{
cohesionPos += neighbour.transform.position;
validNeighbours++;
}
if (validNeighbours == 0)
{
return Vector3.zero;
}
cohesionPos /= validNeighbours;
cohesionPos -= transform.position;
cohesionPos.Normalize();
return cohesionPos;
}
private Vector3 CalculateAlignmentPos()
{
var alignmentPos = transform.forward;
var validNeighbours = 0;
foreach (var neighbour in neighbours.Where(neighbour => neighbour != null))
{
alignmentPos += neighbour.transform.forward ;
validNeighbours++;
}
if (validNeighbours == 0)
{
return alignmentPos;
}
alignmentPos /= validNeighbours;
alignmentPos.Normalize();
return alignmentPos;
}
private Vector3 CalculateSeparationPos()
{
var separationPos = Vector3.zero;
var validNeighbours = 0;
foreach (var neighbour in neighbours.Where(neighbour => neighbour != null))
{
separationPos += transform.position - neighbour.transform.position;
validNeighbours++;
}
if (validNeighbours == 0)
{
return separationPos;
}
separationPos /= validNeighbours;
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 = avoidAdditionalSpeed;
}
return obstaclePos;
}
}
}