OldBlueWater/BlueWater/Assets/02.Scripts/Boid.cs
NTG 03dd793340 Closes #92 #133 #134 #135
+ 물고기를 잡으면 드랍 가능한 형태의 오브젝트와 아이콘이 생성되게 변경되었습니다.
+ 플레이어(배)와 아이콘의 위치가 가까워지면 플레이어는 아이콘을 흡수하면서 아이템을 얻게됩니다.
+ 아이템 획득 시에 효과음을 추가하였습니다.
+ 아이템 획득 시에 좌측 중단에 UI를 통해서 어떠한 아이템을 얻었는지 확인할 수 있게 표시합니다.
+ 물고기 스팟 이펙트를 2가지 합치는 방식으로 변경하였습니다.
+ 물고기 스팟을 기준으로 플레이어와의 거리를 체크해 물고기 스팟이 도망치는 방식으로 변경되었습니다. (기존에는 Bound의 중심)
+ 물고기 스팟의 크기를 원하는 크기로 변경할 수 있도록 변경했습니다.
+ 인터페이스 IItem을 추가하였습니다.
+ Item,FishItem 로직을 변경했습니다.
2024-01-22 02:42:31 +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 FishItem FishItem { 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];
FishItem.ItemCount = 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;
}
}
}