Files
zeling_v2/Assets/_Game/Scripts/Feedback/PlayerFeedback.cs
Joywayer 09bdda970a feat(feedback): 命中/受击反馈支持固定位置与击中位置两种生成模式
DamageInfo 新增运行时字段 HitPoint:HitBox 结算时写入精确接触点
(ClosestPoint),HurtBox.ReceiveDamage 以解析后命中点兜底盖戳,
陷阱/环境等直调路径同样可用。IFeedbackPlayer.PlayHit/PlayTakeHit
增加命中点参数,全链路(武器实例→PlayerCombat→HurtState→
EnemyFeedback)透传。各 Feedback 组件命中/受击槽位新增
FeedbackPositionMode(Fixed=反馈链编排的固定位置 /
HitPoint=传入命中点,启用位置选项的模块在该点生成),由编排
反馈链的开发者在 Inspector 按表现选择;武器命中默认 HitPoint
(火花贴点),角色级默认 Fixed(震屏/振动与位置无关)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:48:37 +08:00

129 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEngine;
using MoreMountains.Feedbacks;
namespace BaseGames.Feedback
{
/// <summary>
/// 玩家反馈播放器:实现 IFeedbackPlayer将行为语义映射到 MMF_Player 实例。
/// 挂在玩家 GameObject 上(或子对象 [Feedback] 上)。
/// 所有 MMF_Player 字段均在 Inspector 中配置 Feel 反馈链。
/// </summary>
public class PlayerFeedback : MonoBehaviour, IFeedbackPlayer
{
[Header("命中反馈")]
[SerializeField] private MMF_Player _onHitLight;
[SerializeField] private MMF_Player _onHitMedium;
[SerializeField] private MMF_Player _onHitHeavy;
[Tooltip("命中反馈生成位置Fixed = 反馈链编排的固定位置HitPoint = 在命中点生成")]
[SerializeField] private FeedbackPositionMode _hitPositionMode = FeedbackPositionMode.Fixed;
[SerializeField] private MMF_Player _onParrySuccess;
[Header("受伤 / 死亡反馈")]
[SerializeField] private MMF_Player _onTakeHit;
[Tooltip("受击反馈生成位置Fixed = 反馈链编排的固定位置HitPoint = 在受击点生成")]
[SerializeField] private FeedbackPositionMode _takeHitPositionMode = FeedbackPositionMode.Fixed;
[SerializeField] private MMF_Player _onDeath;
[Header("恢复反馈")]
[SerializeField] private MMF_Player _onHeal;
[Header("移动 / 动作反馈")]
[SerializeField] private MMF_Player _onLandImpact;
[SerializeField] private MMF_Player _onAttackWhoosh;
[SerializeField] private MMF_Player _onJumpLaunch;
[SerializeField] private MMF_Player _onFootstep;
[Header("脚步声材质检测")]
[SerializeField] private BaseGames.Audio.FootstepSoundPlayer _footstepSoundPlayer;
[Header("形态切换反馈")]
[Tooltip("索引与 FormType 枚举值对应0=天魂, 1=地魂, 2=命魂")]
[SerializeField] private MMF_Player[] _onFormSwitch = new MMF_Player[3];
[Header("命名预设(可选)")]
[SerializeField] private NamedFeedback[] _namedPresets;
[Header("音效预设(可选)")]
[SerializeField] private NamedFeedback[] _sfxPresets;
private Dictionary<string, MMF_Player> _presetMap;
private Dictionary<string, MMF_Player> _sfxMap;
private void Awake()
{
_presetMap = BuildMap(_namedPresets);
_sfxMap = BuildMap(_sfxPresets);
}
// ── IFeedbackPlayer ──────────────────────────────────────────────────────
public void PlayHit(HitWeight weight, Vector3 hitPoint)
{
var player = weight switch
{
HitWeight.Light => _onHitLight,
HitWeight.Medium => _onHitMedium,
HitWeight.Heavy => _onHitHeavy,
_ => _onHitLight,
};
FeedbackPlayback.Play(player, _hitPositionMode, hitPoint);
}
public void PlayParrySuccess() => _onParrySuccess?.PlayFeedbacks();
public void PlayTakeHit(Vector3 hitPoint)
=> FeedbackPlayback.Play(_onTakeHit, _takeHitPositionMode, hitPoint);
public void PlayDeath() => _onDeath?.PlayFeedbacks();
public void PlayHeal() => _onHeal?.PlayFeedbacks();
public void PlayLandImpact() => _onLandImpact?.PlayFeedbacks();
public void PlayAttackWhoosh() => _onAttackWhoosh?.PlayFeedbacks();
public void PlayJumpLaunch() => _onJumpLaunch?.PlayFeedbacks();
public void PlayFootstep()
{
_onFootstep?.PlayFeedbacks();
_footstepSoundPlayer?.Play();
}
public void TriggerPreset(string presetId)
{
if (_presetMap.TryGetValue(presetId, out var p))
p?.PlayFeedbacks();
else
Debug.LogWarning($"[PlayerFeedback] 未找到预设 '{presetId}'。");
}
public void PlaySFXById(string sfxId)
{
if (_sfxMap.TryGetValue(sfxId, out var p))
p?.PlayFeedbacks();
else
Debug.LogWarning($"[PlayerFeedback] 未找到 SFX 预设 '{sfxId}'。");
}
public void PlayFormSwitch(int formIndex)
{
if (_onFormSwitch != null && formIndex >= 0 && formIndex < _onFormSwitch.Length)
_onFormSwitch[formIndex]?.PlayFeedbacks();
}
// ── 辅助 ─────────────────────────────────────────────────────────────────
private static Dictionary<string, MMF_Player> BuildMap(NamedFeedback[] entries)
{
var map = new Dictionary<string, MMF_Player>();
if (entries == null) return map;
foreach (var e in entries)
{
if (!string.IsNullOrEmpty(e.id))
map[e.id] = e.player;
}
return map;
}
}
[System.Serializable]
public struct NamedFeedback
{
public string id;
public MMF_Player player;
}
}