|
|
@@ -1,22 +1,18 @@
|
|
|
using System.Collections;
|
|
|
using System.Collections.Generic;
|
|
|
-using Unity.VisualScripting;
|
|
|
+using Sirenix.OdinInspector;
|
|
|
using UnityEngine;
|
|
|
-using Spine;
|
|
|
-using Spine.Unity;
|
|
|
|
|
|
public class Polliwog : Enemy
|
|
|
{
|
|
|
public Transform playerTarget;
|
|
|
- public PolliwogShot shooter;
|
|
|
- private BoxCollider _bodyCollider;
|
|
|
public int damage;
|
|
|
[Header("鱼群行为参数")]
|
|
|
- public float separationWeight = 1.5f; //分离权重
|
|
|
- public float cohesionWeight = 1.0f; //聚集权重
|
|
|
- public float alignmentWeight = 1.0f; //对齐权重
|
|
|
- public float flockInfluence = 0.5f; // 群体行为对最终方向的影响程度
|
|
|
- public int correctionProbability;
|
|
|
+ [LabelText("分离权重")] public float separationWeight = 1.5f;
|
|
|
+ [LabelText("聚集权重")] public float cohesionWeight = 1.0f;
|
|
|
+ [LabelText("对齐权重")] public float alignmentWeight = 1.0f;
|
|
|
+ [LabelText("群体行为对最终方向的影响程度")] public float flockInfluence = 0.5f;
|
|
|
+ [LabelText("每次循环应用群体行为的概率")] public int correctionProbability;
|
|
|
|
|
|
[Header("移动参数")]
|
|
|
public float baseMoveSpeed = 5f;
|
|
|
@@ -25,41 +21,38 @@ public class Polliwog : Enemy
|
|
|
private float rotationSpeed;
|
|
|
public float speedDeviation;
|
|
|
public float rotationSpeedDeviation;
|
|
|
-
|
|
|
- public float squareAvoidanceRadius;
|
|
|
[Header("旋转平滑参数")]
|
|
|
- public float rotationSmoothing = 5f; // 旋转平滑系数(越大越平滑)
|
|
|
- private float currentAngleVelocity; // 当前角度变化速度(用于SmoothDamp)
|
|
|
- public bool isInGroup = true;
|
|
|
+ [LabelText("旋转平滑系数(越大越平滑)")] public float rotationSmoothing = 5f;
|
|
|
+ private float currentAngleVelocity;
|
|
|
|
|
|
public Vector2 currentVelocity { get; private set; }
|
|
|
private int updateCounter = 0;
|
|
|
- public int updateInterval = 2;
|
|
|
+ [LabelText("多少次循环更新一次")] public int updateInterval = 2;
|
|
|
+
|
|
|
+ private List<Polliwog> nearbyFish = new List<Polliwog>();
|
|
|
+ private float lastUpdateNearbyFishTime;
|
|
|
|
|
|
public override void Awake()
|
|
|
{
|
|
|
base.Awake();
|
|
|
- _bodyCollider = bodyCollider.GetComponent<BoxCollider>();
|
|
|
}
|
|
|
protected override void OnEnable()
|
|
|
{
|
|
|
base.OnEnable();
|
|
|
isDie = false;
|
|
|
- _bodyCollider.isTrigger = true;
|
|
|
- //rb.useGravity = false;
|
|
|
currentVelocity = Vector2.zero;
|
|
|
moveSpeed = baseMoveSpeed + Random.Range(-speedDeviation, speedDeviation);
|
|
|
rotationSpeed = baseRotationSpeed + Random.Range(-rotationSpeedDeviation, rotationSpeedDeviation);
|
|
|
ChangeState(CharacterState.Run);
|
|
|
+ if (PlayersInput.instance.Length > 0) playerTarget = PlayersInput.instance[0].transform;
|
|
|
}
|
|
|
private void Start()
|
|
|
{
|
|
|
attackController.attackTrigger.attackInfo.damage = damage;
|
|
|
}
|
|
|
|
|
|
- public void Init(PolliwogShot _shooter, int _damage)
|
|
|
+ public void Init(int _damage)
|
|
|
{
|
|
|
- shooter = _shooter;
|
|
|
damage = _damage;
|
|
|
}
|
|
|
|
|
|
@@ -79,36 +72,14 @@ public class Polliwog : Enemy
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
- switch (state)
|
|
|
- {
|
|
|
- case CharacterState.Run:
|
|
|
- if (attackController != null)
|
|
|
- {
|
|
|
- attackController.isAttackTriggerOn = false;
|
|
|
- attackController.attackTrigger.gameObject.SetActive(false);
|
|
|
- }
|
|
|
- //rb.useGravity = true;
|
|
|
- isInGroup = false;
|
|
|
- _bodyCollider.isTrigger = false;
|
|
|
- if (shooter != null)
|
|
|
- {
|
|
|
- if (shooter.list.Contains(this)) shooter.list.Remove(this);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- Debug.Log("从" + state + "切换到" + newState);
|
|
|
state = newState;
|
|
|
switch (newState)
|
|
|
{
|
|
|
case CharacterState.Idle:
|
|
|
ani.Play("idle", 0, 0);
|
|
|
- //rb.velocity = Vector3.zero;
|
|
|
currentVelocity = Vector3.zero;
|
|
|
break;
|
|
|
case CharacterState.Run:
|
|
|
- //rb.useGravity = false;
|
|
|
- _bodyCollider.isTrigger = true;
|
|
|
- attackController.isAttackTriggerOn = true;
|
|
|
ani.Play("walk", 0, 0);
|
|
|
break;
|
|
|
case CharacterState.Die:
|
|
|
@@ -117,23 +88,15 @@ public class Polliwog : Enemy
|
|
|
dieKeepTime = totalDieKeepTime;
|
|
|
DropSouls();
|
|
|
break;
|
|
|
- case CharacterState.HitStun:
|
|
|
- canNotShotDown = true;
|
|
|
- break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
public override void FixedUpdate()
|
|
|
{
|
|
|
- //updateCounter++;
|
|
|
- //if (updateCounter > updateInterval) return;
|
|
|
- //updateCounter = 0;
|
|
|
- if (PlayersInput.instance.Length > 0)
|
|
|
- {
|
|
|
- playerTarget = PlayersInput.instance[0].transform;
|
|
|
- }
|
|
|
- else return;
|
|
|
+ updateCounter++;
|
|
|
+ if (updateCounter > updateInterval) return;
|
|
|
+ updateCounter = 0;
|
|
|
OnState();
|
|
|
}
|
|
|
|
|
|
@@ -160,7 +123,6 @@ public class Polliwog : Enemy
|
|
|
pastAttackTime += Time.deltaTime;
|
|
|
Vector3 leftDir = GetMoveDir();
|
|
|
bool isAttack = GetAttack();
|
|
|
- //Vector3 velocity = rb.velocity;
|
|
|
Quaternion targetQt = Quaternion.Euler(Vector3.zero);
|
|
|
switch (state)
|
|
|
{
|
|
|
@@ -168,63 +130,41 @@ public class Polliwog : Enemy
|
|
|
ChangeState(CharacterState.Run);
|
|
|
break;
|
|
|
case CharacterState.Run:
|
|
|
- if (isInGroup)
|
|
|
- {
|
|
|
- Vector2 toPlayer = playerTarget.position - transform.position;
|
|
|
- float targetAngle = Vector2.SignedAngle(Vector2.left, toPlayer);;
|
|
|
- float desiredAngle = targetAngle;
|
|
|
- bool applyFlock = Random.Range(0, 100) <= correctionProbability;
|
|
|
- if (applyFlock)
|
|
|
- {
|
|
|
- Vector2 separation = CalculateMove1();
|
|
|
- Vector2 alignment = CalculateMove3();
|
|
|
- Vector2 cohesion = CalculateMove2();
|
|
|
-
|
|
|
- Vector2 combinedMove = (separation * separationWeight +
|
|
|
- cohesion * cohesionWeight +
|
|
|
- alignment * alignmentWeight);
|
|
|
-
|
|
|
- float flockAngle = combinedMove != Vector2.zero ?
|
|
|
- Vector2.SignedAngle(Vector2.left, combinedMove) : targetAngle;
|
|
|
- desiredAngle = Mathf.LerpAngle(targetAngle, flockAngle, flockInfluence);
|
|
|
- }
|
|
|
- float newAngle = Mathf.SmoothDampAngle(
|
|
|
- transform.eulerAngles.z,
|
|
|
- desiredAngle,
|
|
|
- ref currentAngleVelocity,
|
|
|
- 1f / rotationSmoothing,
|
|
|
- rotationSpeed * 2f,
|
|
|
- Time.fixedDeltaTime
|
|
|
- );
|
|
|
-
|
|
|
- transform.rotation = Quaternion.Euler(0, 0, newAngle);
|
|
|
- currentVelocity = -transform.right * moveSpeed;
|
|
|
- //rb.velocity = -transform.right * moveSpeed;
|
|
|
-
|
|
|
- }
|
|
|
- else
|
|
|
+ Vector2 toPlayer = playerTarget.position - transform.position;
|
|
|
+ float targetAngle = Vector2.SignedAngle(Vector2.left, toPlayer); ;
|
|
|
+ float desiredAngle = targetAngle;
|
|
|
+ bool applyFlock = Random.Range(0, 100) <= correctionProbability;
|
|
|
+ if (applyFlock)
|
|
|
{
|
|
|
- if (!gameObject.activeSelf) return;
|
|
|
- Vector2 toPlayer = shooter.UpdateGroupCenter() - transform.position;
|
|
|
- float targetAngle = Vector2.SignedAngle(Vector2.left, toPlayer);
|
|
|
- transform.rotation = Quaternion.Euler(0, 0, targetAngle + 360);
|
|
|
- currentVelocity = -transform.right * moveSpeed;
|
|
|
- //rb.velocity = -transform.right * moveSpeed;
|
|
|
- if (toPlayer.magnitude < squareAvoidanceRadius && shooter != null)
|
|
|
- {
|
|
|
- shooter.list.Add(this);
|
|
|
- isInGroup = true;
|
|
|
- }
|
|
|
+ UpdateNearbyFish();
|
|
|
+ Vector2 separation = CalculateMove1();
|
|
|
+ Vector2 alignment = CalculateMove3();
|
|
|
+ Vector2 cohesion = CalculateMove2();
|
|
|
+
|
|
|
+ Vector2 combinedMove = (separation * separationWeight +
|
|
|
+ cohesion * cohesionWeight +
|
|
|
+ alignment * alignmentWeight);
|
|
|
+
|
|
|
+ float flockAngle = combinedMove != Vector2.zero ?
|
|
|
+ Vector2.SignedAngle(Vector2.left, combinedMove) : targetAngle;
|
|
|
+ desiredAngle = Mathf.LerpAngle(targetAngle, flockAngle, flockInfluence);
|
|
|
}
|
|
|
+ float newAngle = Mathf.SmoothDampAngle(
|
|
|
+ transform.eulerAngles.z,
|
|
|
+ desiredAngle,
|
|
|
+ ref currentAngleVelocity,
|
|
|
+ 1f / rotationSmoothing,
|
|
|
+ rotationSpeed * 2f,
|
|
|
+ Time.fixedDeltaTime
|
|
|
+ );
|
|
|
+
|
|
|
+ transform.rotation = Quaternion.Euler(0, 0, newAngle);
|
|
|
+ currentVelocity = -transform.right * moveSpeed;
|
|
|
if (!attackController.attackTrigger.gameObject.activeSelf)
|
|
|
{
|
|
|
attackController.attackTrigger.gameObject.SetActive(true);
|
|
|
}
|
|
|
break;
|
|
|
- case CharacterState.HitStun:
|
|
|
- currentVelocity = Vector2.zero;
|
|
|
- hitFeedbackSystem.HitStunUpdate();
|
|
|
- break;
|
|
|
case CharacterState.Die:
|
|
|
currentVelocity = Vector2.zero;
|
|
|
if (dieKeepTime <= 0)
|
|
|
@@ -237,63 +177,68 @@ public class Polliwog : Enemy
|
|
|
break;
|
|
|
}
|
|
|
break;
|
|
|
- case CharacterState.SpecialStatus_Float:
|
|
|
- attributeStatus.SpecialStateEffect(SpecialState.FloatState);
|
|
|
- break;
|
|
|
- case CharacterState.SpecialStatus_BlowUp:
|
|
|
- attributeStatus.SpecialStateEffect(SpecialState.BlownUp);
|
|
|
- break;
|
|
|
- case CharacterState.SpecialStatus_ShotDown:
|
|
|
- currentVelocity = Vector2.zero;
|
|
|
- attributeStatus.SpecialStateEffect(SpecialState.ShotDown);
|
|
|
- break;
|
|
|
- case CharacterState.SpecialStatus_Weak:
|
|
|
- attributeStatus.SpecialStateEffect(SpecialState.Weak);
|
|
|
- break;
|
|
|
}
|
|
|
transform.position += (Vector3)currentVelocity * Time.fixedDeltaTime;
|
|
|
}
|
|
|
+
|
|
|
public Vector2 CalculateMove1()
|
|
|
{
|
|
|
- if (shooter == null || shooter.list.Count == 0)
|
|
|
+ if (nearbyFish.Count == 0)
|
|
|
{
|
|
|
return Vector2.zero;
|
|
|
}
|
|
|
+
|
|
|
Vector2 avoidDanceMove = Vector2.zero;
|
|
|
- int nAvoid = 0;
|
|
|
- foreach (Polliwog fish in shooter.list)
|
|
|
- {
|
|
|
- if ((fish.transform.position - transform.position).sqrMagnitude < squareAvoidanceRadius)
|
|
|
- {
|
|
|
- nAvoid++;
|
|
|
- avoidDanceMove += (Vector2)(transform.transform.position - fish.transform.position);
|
|
|
- }
|
|
|
- }
|
|
|
- if (nAvoid > 0)
|
|
|
+
|
|
|
+ foreach (Polliwog fish in nearbyFish)
|
|
|
{
|
|
|
- avoidDanceMove /= nAvoid;
|
|
|
+ avoidDanceMove += (Vector2)(transform.transform.position - fish.transform.position) / Vector2.Distance(transform.transform.position, fish.transform.position);
|
|
|
}
|
|
|
- return avoidDanceMove;
|
|
|
+
|
|
|
+ return avoidDanceMove.normalized;
|
|
|
}
|
|
|
+
|
|
|
public Vector2 CalculateMove2()
|
|
|
{
|
|
|
- if (shooter == null || shooter.list.Count == 0)
|
|
|
+ if (nearbyFish.Count == 0)
|
|
|
{
|
|
|
return Vector2.zero;
|
|
|
}
|
|
|
- Vector2 cohesionMove = shooter.UpdateGroupCenter();
|
|
|
-
|
|
|
+ Vector2 cohesionMove = Vector2.zero;
|
|
|
+ foreach (Polliwog fish in nearbyFish)
|
|
|
+ {
|
|
|
+ cohesionMove += (Vector2)(fish.transform.transform.position);
|
|
|
+ }
|
|
|
+ cohesionMove /= nearbyFish.Count;
|
|
|
cohesionMove -= (Vector2)transform.position;
|
|
|
- return cohesionMove;
|
|
|
+ return cohesionMove.normalized;
|
|
|
}
|
|
|
+
|
|
|
public Vector2 CalculateMove3()
|
|
|
{
|
|
|
- if (shooter == null || shooter.list.Count == 0)
|
|
|
+ if (nearbyFish.Count == 0)
|
|
|
{
|
|
|
return Vector2.zero;
|
|
|
}
|
|
|
- Vector2 alignmentMove = Vector2.zero;
|
|
|
+ Vector2 groupVelocity = Vector2.zero;
|
|
|
+ foreach (Polliwog fish in nearbyFish)
|
|
|
+ {
|
|
|
+ groupVelocity += fish.currentVelocity;
|
|
|
+ }
|
|
|
+ groupVelocity /= nearbyFish.Count;
|
|
|
+ return groupVelocity.normalized;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void OnDisable()
|
|
|
+ {
|
|
|
+ EnemyCreater.instance.OnEnemyRecycle(this);
|
|
|
+ PolliwogManager.GetInstance().RemovePolliwog(this);
|
|
|
+ }
|
|
|
|
|
|
- return shooter.UpdateGroupVelocity();
|
|
|
+ private void UpdateNearbyFish()
|
|
|
+ {
|
|
|
+ if(Time.time - lastUpdateNearbyFishTime < 0.1) return;
|
|
|
+ lastUpdateNearbyFishTime = Time.time;
|
|
|
+ nearbyFish = PolliwogManager.GetInstance().GetNearbyFish(transform.position, 6);
|
|
|
}
|
|
|
}
|