483 lines
17 KiB
C#
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));
|
|
}
|
|
}
|