Files
2026-05-26 16:15:54 +08:00

483 lines
17 KiB
C#

using asap.core;
using cfg;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.AddressableAssets;
public class FishMovement : AquariumFishBase
{
[SerializeField] private Rigidbody rb;
private Vector3 Orientation => transform.rotation * Vector3.forward;
public int index = 0, groupId = -1;
public Rigidbody Rb => rb;
FishManager fishManager;
GameObject goPrefab;
float startSizeZ = 0;
float avoidRadius = 0;
float maxPitchAngle = 30f;
GameObject leftEye;
GameObject rightEye;
public float AvoidRadius => avoidRadius * transform.localScale.x;
public Vector3 targetPos;
public Transform fishPos;
GameObject fishUnDownloadAlter;
public FishData fishData;
public Fish fish;
List<Collider> colliders = new List<Collider>();
//public int triggerCount;
public bool isResult;
public float mutantScale;
float loadTime;
float startLoadTime;
public void Init(FishManager fishManager, AquariumSlotData slotData, float startSizeZ, FishData fishData, float mutantScale)
{
this.mutantScale = mutantScale;
this.slotData = slotData;
this.fishData = fishData;
this.fishManager = fishManager;
this.startSizeZ = startSizeZ;
rb.velocity = rb.rotation * Vector3.forward;
LoadFishUnDownloadAlter(fishData.Quality - 1);
startLoadTime = Time.time;
}
public void RemoveEye()
{
if (leftEye != null)
{
DestroyImmediate(leftEye);
}
if (rightEye != null)
{
DestroyImmediate(rightEye);
}
}
void LoadFishUnDownloadAlter(int quality)
{
if (fishManager.fishUnDownloadAlter != null && fishManager.fishUnDownloadAlter.ContainsKey(quality))
{
fishUnDownloadAlter = Instantiate(fishManager.fishUnDownloadAlter[quality], transform);
fish = fishUnDownloadAlter.GetComponent<Fish>();
fish.animator.transform.localPosition = Vector3.zero;
fish.EnableCollider();
avoidRadius = fish.GetColliderSize(fishManager.avoidRadius);
fishUnDownloadAlter.transform.localPosition = Vector3.zero;
fishUnDownloadAlter.transform.localEulerAngles = Vector3.up * 180;
fishUnDownloadAlter.transform.localScale = Vector3.one;
fishUnDownloadAlter.name = $"{fishUnDownloadAlter.name}_{index}";
}
}
public async Task LoadFish(Material mutantMaterial, GameObject fxLeftEye, GameObject fxRightEye)
{
goPrefab = await Addressables.InstantiateAsync(fishData.Fbx, transform).Task;
loadTime = Time.time - startLoadTime;
if (rb == null || isResult || loadTime > fishManager.MaxLoadTime)
{
Debug.LogWarning($"LoadFish TimeOut:{loadTime} {fishData.Fbx}");
Addressables.ReleaseInstance(goPrefab);
goPrefab = null;
}
else if (goPrefab != null)
{
//Destroy(fishUnDownloadAlter);
//fishUnDownloadAlter = null;
if (fishUnDownloadAlter != null)
{
fishUnDownloadAlter.SetActive(false);
}
fish = goPrefab.GetComponent<Fish>();
fish.animator.transform.localPosition = Vector3.zero;
fish.EnableCollider();
fish.animator.Play("Swim01", 0, UnityEngine.Random.Range(0, 0.8f));
avoidRadius = fish.GetColliderSize(fishManager.avoidRadius);
goPrefab.transform.localPosition = Vector3.zero;
goPrefab.transform.localEulerAngles = Vector3.up * 180;
goPrefab.transform.localScale = Vector3.one;
goPrefab.name = $"{fishData.Fbx}_{index}";
if (slotData.mutants <= index)
{
return;
}
var _tables = GContext.container.Resolve<Tables>();
float eyeScale = _tables.TbFishMutant.FxAquariumScale[fishData.Quality - 1];
if (fish.LeftEye != null && fxLeftEye != null)
{
leftEye = Instantiate(fxLeftEye);
leftEye.transform.localScale = Vector3.one * eyeScale;
leftEye.transform.parent = fish.LeftEye;
leftEye.transform.localPosition = Vector3.zero;
leftEye.transform.localRotation = Quaternion.identity;
}
if (fish.RightEye != null && fxRightEye != null)
{
rightEye = Instantiate(fxRightEye);
rightEye.transform.localScale = Vector3.one * eyeScale;
rightEye.transform.parent = fish.RightEye;
rightEye.transform.localPosition = Vector3.zero;
rightEye.transform.localRotation = Quaternion.identity;
}
Renderer[] meshRenderer = goPrefab.GetComponentsInChildren<Renderer>();
if (meshRenderer != null && meshRenderer.Length > 0)
{
var render = meshRenderer[0];
var materials = render.materials;
for (int i = 0; i < materials.Length; i++)
{
var material = materials[i];
var material_P = new Material(mutantMaterial);
material_P.SetTexture("_BaseMap", material.GetTexture("_BaseMap"));
material_P.SetTexture("_BumpMap", material.GetTexture("_BumpMap"));
materials[i] = material_P;
}
render.materials = materials;
}
}
}
public void OnShoot(bool isRob, float speed, float time, Vector3 reasonableCenter, Vector3 reasonableLocation, float reasonableDistance)
{
rb.isKinematic = true;
isResult = true;
var pos = transform.position;
var startPos = Vector3.zero;
startPos.z = UnityEngine.Random.Range(-reasonableLocation.z, reasonableLocation.z);
if (pos.x > fishManager.tankCenter.x)
{
startPos.x = UnityEngine.Random.Range(0, reasonableLocation.x);
}
else
{
startPos.x = UnityEngine.Random.Range(-reasonableLocation.x, 0);
}
if (pos.y > fishManager.tankCenter.y)
{
startPos.y = UnityEngine.Random.Range(0, reasonableLocation.y);
}
else
{
startPos.y = UnityEngine.Random.Range(-reasonableLocation.y, 0);
}
transform.position = startPos + reasonableCenter;
targetPos = startPos + reasonableCenter + rb.rotation * Vector3.forward * reasonableDistance;
float l = speed * time;
float t = l / (targetPos - transform.position).magnitude;
if (t < 1)
{
targetPos = Vector3.Lerp(transform.position, targetPos, t);
}
//转弯
//Quaternion rotation1 = Quaternion.LookRotation(targetPos - transform.position);
//transform.DORotateQuaternion(rotation1, time * 0.8f);
// Move the fish along the path
transform.DOMove(targetPos, time).SetEase(Ease.Linear);
SetTargetPos(isRob);
}
void SetTargetPos(bool isRob)
{
fishPos = fish.mouthPos;
if (!isRob)
{
int i = UnityEngine.Random.Range(0, 3);
if (i == 0)
{
fishPos = fish.Fin;
}
else if (i == 1)
{
fishPos = fish.Neck;
}
else if (i == 2)
{
fishPos = fish.Tail;
}
}
if (fishPos != null)
{
targetPos += fishPos.position - transform.position;
}
}
public void AllEscape()
{
if (!isResult)
{
rb.isKinematic = true;
isResult = true;
var ta = transform.position + rb.rotation * Vector3.forward * 50;
transform.DOMove(ta, 3).OnComplete(() => fish.gameObject.SetActive(false)).SetEase(Ease.Linear);
}
}
public void SetFishSpeed(float speed, float time)
{
if (fish != null)
{
float curSpeed = speed;
DOTween.To(() => speed, x => curSpeed = x, 1f, time).OnUpdate(() =>
{
fish.FishAnimSpeed = curSpeed;
});
}
}
public void Shock(float distance, float time, AnimationCurve shockCurve)
{
transform.DOMoveZ(transform.position.z + distance, time).SetEase(shockCurve);
}
public void Capture(float minSpeed, float maxSpeed, float addRotation, float addRotationPeriod)
{
fish.FishAnimSpeed = UnityEngine.Random.Range(minSpeed, maxSpeed);
//鱼的朝向y轴 增加 rotation
float add = UnityEngine.Random.Range(-addRotation, addRotation);
Quaternion rotation = Quaternion.AngleAxis(add, Vector3.up);
rb.rotation = rb.rotation * rotation; //rb.transform.forward;
StartCoroutine(AddRotation(addRotation, addRotationPeriod));
}
IEnumerator AddRotation(float addRotation, float addRotationPeriod)
{
Quaternion startRotation = rb.rotation;
while (enabled)
{
float add = UnityEngine.Random.Range(-addRotation, addRotation);
Quaternion rotation = Quaternion.AngleAxis(add, Vector3.up);
//rb.rotation = startRotation * rotation; //rb.transform.forward;
transform.DORotateQuaternion(startRotation * rotation, addRotationPeriod);
yield return new WaitForSeconds(addRotationPeriod);
}
}
public void Escape(float distance, float duration)
{
transform.DOKill();
rb.isKinematic = true;
isResult = true;
var ta = transform.position + rb.rotation * Vector3.forward * distance;
transform.DOMove(ta, duration).OnComplete(() => fish.gameObject.SetActive(false)).SetEase(Ease.InSine);
}
public void OnShooted(bool isRob)
{
if (fish != null)
{
fish.animator.SetInteger("HurtIndex", isRob ? 2 : 1);
fish.animator.SetTrigger("Hurt");
}
rb.isKinematic = true;
isResult = true;
}
private void OnTriggerEnter(Collider other)
{
colliders.Add(other);
}
private void OnTriggerExit(Collider other)
{
colliders.Remove(other);
}
private Vector3 _dv = Vector3.zero;
private Vector3 _lastDv = Vector3.zero;
private void FixedUpdate()
{
if (fishManager == null || isResult)
{
return;
}
var s = fishManager.tankSize / 2;
float offsetX = fishManager.tankCenter.x;
float offsetY = fishManager.tankCenter.y;
float offsetZ = fishManager.tankCenter.z + startSizeZ;
//区域外
_dv = Vector3.zero;
// Area Constraint
if (rb.position.x > s.x + offsetX)
{
_dv += Vector3.left * (rb.position.x - s.x - offsetX);
}
else if (rb.position.x < -s.x + offsetX)
{
_dv += Vector3.right * (-s.x + offsetX - rb.position.x);
}
if (rb.position.y > s.y + offsetY)
{
_dv += Vector3.down * (rb.position.y - s.y - offsetY);
}
else if (rb.position.y < -s.y + offsetY)
{
_dv += Vector3.up * (-s.y + offsetY - rb.position.y);
}
if (rb.position.z > s.z + offsetZ)
{
_dv += Vector3.back * (rb.position.z - s.z - offsetZ);
}
else if (rb.position.z < -s.z + offsetZ)
{
_dv += Vector3.forward * (-s.z + offsetZ - rb.position.z);
}
_dv *= fishManager.areaConstrain;
// Boids Constraint
var colliderCount = fishManager.allFish.Count;
// var flockMates = new List<Rigidbody>();
var alignVelocity = Vector3.zero;
var avoidPosition = Vector3.zero;
var coherePosition = Vector3.zero;
int alignBoidCount = 0, avoidBoidCount = 0, cohereBoidCount = 0;
for (int i = colliders.Count - 1; i >= 0; i--)
{
if (colliders[i] == null)
{
colliders.RemoveAt(i);
continue;
}
Transform obstacleTransform = colliders[i].transform;
var fromToVector = obstacleTransform.position - transform.position;
var distance = fromToVector.magnitude;
_dv -= fromToVector.normalized / distance * fishManager.trigger;
}
for (int i = 0; i < fishManager.obstacleGo.Count; i++)
{
Transform obstacleTransform = fishManager.obstacleGo[i].transform;
Vector3 localScale = obstacleTransform.localScale / 2;
Vector3 localPos = obstacleTransform.position;
if (transform.position.x < localPos.x - localScale.x)
{
localPos.x -= localScale.x;
}
else if (transform.position.x > localPos.x + localScale.x)
{
localPos.x += localScale.x;
}
if (transform.position.y < localPos.y - localScale.y)
{
localPos.y -= localScale.y;
}
else if (transform.position.y > localPos.y + localScale.y)
{
localPos.y += localScale.y;
}
if (transform.position.z < localPos.z - localScale.z)
{
localPos.z -= localScale.z;
}
else if (transform.position.z > localPos.z + localScale.z)
{
localPos.z += localScale.z;
}
var fromToVector = localPos - transform.position;
var distance = fromToVector.magnitude;
// Obstacle has a higher weight than other boids
if (distance <= fishManager.obstacleDetectionRadius)
{
_dv -= fromToVector.normalized / distance * fishManager.obstacle;
}
}
for (int i = 0; i < colliderCount; i++)
{
var flockMate = fishManager.allFish[i];
var fromToVector = fishManager.allFish[i].transform.position - transform.position;
var distance = fromToVector.magnitude;
if (fishManager.allFish[i].gameObject.name == gameObject.name || flockMate is null)
continue;
if (flockMate.groupId == groupId && distance <= fishManager.alignRadius)
{
alignBoidCount++;
alignVelocity += flockMate.Rb.velocity;
}
if (flockMate.fishData.Quality >= fishData.Quality
&& distance <= (AvoidRadius + flockMate.AvoidRadius))
// && ((triggerCount > 0 && flockMate.groupId == groupId) || flockMate.groupId != groupId)
//)
{
avoidBoidCount++;
avoidPosition += flockMate.Rb.position;
}
if (flockMate.groupId == groupId)
{
cohereBoidCount++;
coherePosition += flockMate.Rb.position;
}
}
if (avoidBoidCount > 0)
_dv += (rb.position - avoidPosition / avoidBoidCount) * fishManager.avoidance;
if (alignBoidCount > 0)
_dv += (alignVelocity / alignBoidCount - rb.velocity) * fishManager.alignment;
if (cohereBoidCount > 0)
_dv += (coherePosition / cohereBoidCount - rb.position) * fishManager.cohesion;
_dv = Vector3.Lerp(_lastDv, _dv, math.saturate(fishManager.dvLerp));
_lastDv = _dv;
var targetVelocity = rb.velocity + _dv * Time.fixedDeltaTime;
var newVelocity = Vector3.RotateTowards(rb.transform.forward * rb.velocity.magnitude, targetVelocity, fishManager.angularAcc,
fishManager.magnitudeAcc);
//仰角限制
float maxPitchAngle = 30f;
float maxY = Mathf.Sin(maxPitchAngle * Mathf.Deg2Rad) * newVelocity.magnitude;
newVelocity.y = Mathf.Clamp(newVelocity.y, -maxY, maxY);
if (newVelocity.magnitude < 0.001)
{
return;
}
rb.rotation = Quaternion.LookRotation(newVelocity);
newVelocity = Vector3.RotateTowards(newVelocity, targetVelocity, fishManager.angularAcc,
fishManager.magnitudeAcc);
maxY = Mathf.Sin(maxPitchAngle * Mathf.Deg2Rad) * newVelocity.magnitude;
newVelocity.y = Mathf.Clamp(newVelocity.y, -maxY, maxY);
float angle = Vector3.Angle(rb.velocity, targetVelocity);
newVelocity *= 1 - (angle / 185);
rb.velocity = newVelocity;
//Max-Min Constraint
if (rb.velocity.magnitude > fishManager.maxVel)
rb.velocity = rb.velocity.normalized * fishManager.maxVel;
else if (rb.velocity.magnitude < fishManager.minVel)
rb.velocity = rb.velocity.normalized * fishManager.minVel;
}
private void OnDestroy()
{
if (goPrefab != null)
{
Addressables.ReleaseInstance(goPrefab);
goPrefab = null;
}
}
private void OnDrawGizmosSelected()
{
// Display the explosion radius when selected
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, AvoidRadius);
Gizmos.color = Color.blue;
Gizmos.DrawLine(transform.position, transform.position + _dv * (5 * Time.fixedDeltaTime));
}
}