Files
ft/Client/Assets/Scripts/UI/PinataPack/PinataPackAct.cs
2026-06-29 21:18:33 +08:00

363 lines
12 KiB
C#

using asap.core;
using asap.core.common;
using Assets.Scripts.UI.PinataPack;
using DG.Tweening;
using Game;
using GameCore;
using RootMotion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
public class PinataPackAct : AGameAct
{
public enum ForceModeType
{
None = 0,
ComplexForce = 1,
SimpleForce = 2,
SphereForce = 3
}
[Header("点击动画")]
[SerializeField]
private List<Animator> hitAnimators;
[Header("点击屏幕特效")]
[SerializeField]
private PinataEffectClickScene clickSceneEffectPrefab;
[Header("点击Pinata特效")]
[SerializeField]
private PinataEffectClickPinata clickPinataEffectPrefab;
[Header("力的模式")]
[SerializeField]// 1-复杂力 2-简单力 3-球体力
private ForceModeType forceMode = ForceModeType.SphereForce;
[SerializeField]
private bool enableAllRigidbodyAddForce = false;
[ShowIf("enableAllRigidbodyAddForce", false)]
[SerializeField]
private Rigidbody[] _rigidbodys;
[SerializeField]
[Tooltip("绳子起始点")]
private List<Transform> originPostionTrans;
[Tooltip("绳子目标点")]
[SerializeField]
private List<Transform> targetPostionTrans;
[ShowIf("forceMode", ForceModeType.ComplexForce)]
[SerializeField]
private float complexForce = 50f;
[ShowIf("forceMode", ForceModeType.ComplexForce)]
[SerializeField]
private float detectionRadius = 2f;
[ShowIf("forceMode", ForceModeType.SimpleForce)]
[SerializeField]
private Vector3 simpleForceV3;
[ShowIf("forceMode", ForceModeType.SphereForce)]
[SerializeField]
private Transform sphereTrans;
[ShowIf("forceMode", ForceModeType.SphereForce)]
[SerializeField]
private Transform targetTrans;
[ShowIf("forceMode", ForceModeType.SphereForce)]
[SerializeField]
[Tooltip("移动花费时间")]
private float sphereMoveTime = 0.5f;
[Header("闪白")]
[SerializeField]
private float flickerTime = 2f;
[SerializeField]
public List<GameObject> notFlickerObjs;
[Header("奖励")]
[SerializeField]
private List<Transform> rewardFlyOriginTrans;
[Header("爆炸")]
[SerializeField]
private List<GameObject> needHideOnBomb;
[SerializeField]
private Transform bombOriginPosTrans;
[SerializeField]
private float bombForce=1f;
[SerializeField]
private float bombDelayTime = 1f;
[SerializeField]
private float bombEndDelayTime = 0.2f;
[SerializeField]
private GameObject bombEffectPrefab;
[SerializeField]
private Transform moveRope;
private HashSet<GameObject> _notFlickerObjs;
private List<Material> _materials = new List<Material>();
private Collider[] hitColliders;
private Material instanceMaterial;
private Vector3 sphereOriginPos;
private IObjectPoolService _objectPoolService;
private IObjectPool _sceneEffectPool;
private IObjectPool _pinataEffectPool;
public override async Task<bool> StartAsync()
{
if (enableAllRigidbodyAddForce)
_rigidbodys = this.transform.GetComponentsInChildren<Rigidbody>();
if (forceMode == ForceModeType.SphereForce)
{
sphereOriginPos = sphereTrans.position;
}
_notFlickerObjs = notFlickerObjs?.ToHashSet() ?? new HashSet<GameObject>();
var meshRenders = this.GetComponentsInChildren<SkinnedMeshRenderer>();
foreach (var meshRender in meshRenders)
{
if (notFlickerObjs.Contains(meshRender.gameObject))
{
var originalMaterial = meshRender.sharedMaterial;
instanceMaterial = new Material(originalMaterial);
meshRender.material = instanceMaterial;
instanceMaterial = null;
continue;
}
_materials.AddRange(meshRender.sharedMaterials);
}
_objectPoolService = GContext.container.Resolve<IObjectPoolService>();
if (clickSceneEffectPrefab != null)
_sceneEffectPool = _objectPoolService.CreatePool(clickSceneEffectPrefab, 1);
if (clickPinataEffectPrefab != null)
_pinataEffectPool = _objectPoolService.CreatePool(clickPinataEffectPrefab, 1);
var packMain = GContext.container.Resolve<EventPinataPackData>()?.EventPinataPackMain;
var uiTypes = UITypes.GetUIType(packMain?.GamePanel ?? nameof(PinataPack_PonySettlementPanel));
await UIManager.Instance.ShowUILoad(uiTypes);
return await base.StartAsync();
}
public async Task MoveRope()
{
var duration = 0.5f;
if(moveRope!=null)
moveRope.DOMoveY(0.5f, duration).SetEase(Ease.OutSine);
await new WaitForSeconds(duration);
}
#region
public void ClickPony(int clickCount)
{
if (_rigidbodys == null) return;
for (int i = 0; i < _rigidbodys.Length; i++)
{
var rigidbody = _rigidbodys[i];
if (rigidbody == null) continue;
// 使用OverlapSphere检测周围表面
if (forceMode == ForceModeType.ComplexForce)
{
Vector3 forceV3 = GetSurfaceNormalWithOverlap(rigidbody) * complexForce;
rigidbody.AddForce(forceV3, ForceMode.Impulse);
}
else if (forceMode == ForceModeType.SimpleForce)
{
rigidbody.AddForce(simpleForceV3, ForceMode.Impulse);
}
else
{
sphereTrans.DOMoveZ(targetTrans.position.z, sphereMoveTime)
.OnComplete(() => sphereTrans.position = sphereOriginPos);
}
}
var index = clickCount < 4 ? clickCount + 1 : clickCount % 2 == 0 ? 3 : 4;
foreach (var animator in hitAnimators)
{
#if UNITY_EDITOR
Debug.Log($"jsd ClickPony Hit0{index}");
#endif
animator.Play($"Hit0{index}", 0, 0f);
}
_ = ShowFlicker();
}
private async Task ShowFlicker()
{
SetFlickerState(true);
await new WaitForSeconds(flickerTime);
SetFlickerState(false);
}
private void SetFlickerState(bool isFlicker)
{
foreach (var material in _materials)
{
if (isFlicker)
material.EnableKeyword("_EMISSION");
else
material.DisableKeyword("_EMISSION");
}
}
// 使用OverlapSphere检测周围表面
private Vector3 GetSurfaceNormalWithOverlap(Rigidbody rb)
{
if (_rigidbodys == null) return default;
if (hitColliders == null)
hitColliders = new Collider[_rigidbodys?.Length ?? 1 * 2];
else
Array.Clear(hitColliders, 0, hitColliders.Length);
Physics.OverlapSphereNonAlloc(rb.position, detectionRadius, hitColliders);
Vector3 averageNormal = Vector3.zero;
int count = 0;
foreach (var hitCollider in hitColliders)
{
if (hitCollider == null) continue;
if (hitCollider.attachedRigidbody == rb) continue;
Vector3 direction = (hitCollider.ClosestPoint(rb.position) - rb.position).normalized;
RaycastHit hit;
if (Physics.Raycast(rb.position, direction, out hit, detectionRadius * 2))
{
averageNormal += hit.normal;
count++;
#if UNITY_EDITOR
Debug.DrawRay(hit.point, hit.normal * 2, Color.yellow, 2f);
#endif
}
}
if (count > 0)
{
return (averageNormal / count).normalized;
}
// 如果没有检测到任何表面,使用默认值
return Vector3.up;
}
#endregion
#region
public async Task PlayClickEffect(Vector3 clickPostion)
{
if (_sceneEffectPool == null) return;
var pinataEffect = _sceneEffectPool.SpawnObject() as PinataEffectClickScene;
Debug.DrawLine(transform.position, clickPostion, Color.red, 2f);
pinataEffect.transform.position = clickPostion;
float time = 0f;
foreach (var effect in pinataEffect.GetParticleSystems())
{
if (effect.gameObject.name == "1") continue;
time = Mathf.Max(time, effect.main.duration);
}
await new WaitForSeconds(time);
_sceneEffectPool.DespawnObject(pinataEffect, time);
}
public async Task PlayClickPinataEffect()
{
if (_pinataEffectPool == null) return;
var pinataEffect = _pinataEffectPool.SpawnObject() as PinataEffectClickPinata;
float time = 0f;
foreach (var effect in pinataEffect.GetParticleSystems())
{
if (effect.gameObject.name == "1") continue;
time = Mathf.Max(time, effect.main.duration);
}
await new WaitForSeconds(time);
_pinataEffectPool.DespawnObject(pinataEffect);
}
public void GetRewardOriginPos(out Vector3 rewardFlyOriginPos1, out Vector3 rewardFlyOriginPos2)
{
if (rewardFlyOriginTrans.Count >= 2)
{
rewardFlyOriginPos1 = rewardFlyOriginTrans[0].position;
rewardFlyOriginPos2 = rewardFlyOriginTrans[1].position;
}
else
rewardFlyOriginPos2 = rewardFlyOriginPos1 = Vector3.zero;
}
#endregion
#region
private GameObject bombEffect;
public async Task BombPony()
{
if (bombEffectPrefab != null)
{
bombEffect = GameObject.Instantiate(bombEffectPrefab, bombOriginPosTrans.position, Quaternion.identity);
bombEffect?.SetActive(true);
}
await new WaitForSeconds(bombDelayTime);
GContext.Publish(new EventUISound($"audio_ui_eventpinatapony_break"));
foreach (var obj in needHideOnBomb)
{
if (obj != null)
obj.SetActive(false);
}
if (_rigidbodys == null) return;
for (int i = _rigidbodys.Length - 1; i >= 0; i--)
{
var rigidbody = _rigidbodys[i];
if (rigidbody == null) continue;
if (rigidbody.TryGetComponent(out Joint joint))
joint.breakForce = 0;
rigidbody.AddExplosionForce(bombForce, bombOriginPosTrans.position, 200f,100, ForceMode.Impulse);
}
await Awaiters.Seconds(bombEndDelayTime);
}
#endregion
#if UNITY_EDITOR
// 可视化检测范围
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
if (_rigidbodys == null) return;
foreach (var rb in _rigidbodys)
{
if (rb == null) return;
Gizmos.DrawWireSphere(rb.position, detectionRadius);
if (bombOriginPosTrans == null) return;
Gizmos.DrawLine(rb.position, bombOriginPosTrans.position);
}
}
#endif
private void FixedUpdate()
{
if (targetPostionTrans == null || targetPostionTrans.Count <= 0) return;
if (originPostionTrans == null || originPostionTrans.Count <= 0) return;
for (int i = 0; i < originPostionTrans.Count && i < targetPostionTrans.Count; i++)
{
var originTrans = originPostionTrans[i];
var targetTrans = targetPostionTrans[i];
if (originTrans == null || targetTrans == null) continue;
targetTrans.position = originTrans.position;
}
}
protected override void OnDestroy()
{
Debug.Log($"Exit AnimalPackAct");
_materials.Clear();
_notFlickerObjs.Clear();
_materials = null;
_notFlickerObjs = null;
_objectPoolService.DestroyPool<PinataEffectClickScene>();
_objectPoolService.DestroyPool<PinataEffectClickPinata>();
if(bombEffect!=null)
Destroy(bombEffect);
}
}