chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

View File

@@ -0,0 +1,18 @@
{
"excludePlatforms": [],
"allowUnsafeCode": false,
"precompiledReferences": [],
"name": "BaseGames.Feedback",
"defineConstraints": [],
"noEngineReferences": false,
"versionDefines": [],
"rootNamespace": "BaseGames.Feedback",
"references": [
"BaseGames.Core.Events",
"BaseGames.Combat",
"MoreMountains.Tools"
],
"autoReferenced": true,
"overrideReferences": false,
"includePlatforms": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4847ae647b0781e49b36458792d57902
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using UnityEngine;
namespace BaseGames.Feedback
{
/// <summary>
/// Feedback 系统全局配置:闪白颜色/时长等通用参数。
/// 资产路径Assets/ScriptableObjects/Feedback/FeedbackConfig.asset
/// </summary>
[CreateAssetMenu(menuName = "Feedback/FeedbackConfig")]
public class FeedbackConfigSO : ScriptableObject
{
[Header("受伤闪白")]
[Tooltip("Shader _FlashColor 参数目标颜色。")]
public Color HurtFlashColor = Color.white;
[Tooltip("闪白持续时间(秒)。")]
[Min(0.01f)]
public float HurtFlashDuration = 0.12f;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a13bc45e10182c46a145c59e379bb30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
namespace BaseGames.Feedback
{
/// <summary>
/// 反馈播放器接口:封装所有角色反馈行为,解耦 Character 逻辑与 Feel/MMF_Player 的直接依赖。
/// 由 PlayerFeedback玩家和 EnemyFeedback敌人分别实现
/// NullFeedbackPlayer 用于测试/占位。
/// </summary>
public interface IFeedbackPlayer
{
// ── 命中 ──────────────────────────────────────────────────────────────────
/// <summary>对目标造成命中时播放对应力度的反馈(摄像机震屏 + 控制器振动等)。</summary>
void PlayHit(HitWeight weight);
/// <summary>成功弹反Parry时播放反馈。</summary>
void PlayParrySuccess();
// ── 受伤 / 死亡 ──────────────────────────────────────────────────────────
/// <summary>角色受到伤害时播放反馈(闪白 + 轻微震屏等)。</summary>
void PlayTakeHit();
/// <summary>角色死亡时播放反馈(慢动作 + 震屏 + 音效)。</summary>
void PlayDeath();
// ── 状态恢复 ─────────────────────────────────────────────────────────────
/// <summary>治疗/恢复血量时播放反馈(粒子 + 音效)。</summary>
void PlayHeal();
// ── 移动 / 动作 ──────────────────────────────────────────────────────────
/// <summary>硬着陆时播放地面冲击反馈。</summary>
void PlayLandImpact();
/// <summary>攻击出手时播放破风反馈。</summary>
void PlayAttackWhoosh();
/// <summary>跳跃起跳时播放反馈。</summary>
void PlayJumpLaunch();
// ── 通用 ─────────────────────────────────────────────────────────────────
/// <summary>通过 presetId 触发在 Inspector 中配置的命名预设 MMF_Player。</summary>
void TriggerPreset(string presetId);
/// <summary>通过 sfxId 触发单次音效(不带任何摄像机/振动反馈)。</summary>
void PlaySFXById(string sfxId);
}
/// <summary>命中力度。</summary>
public enum HitWeight { Light, Medium, Heavy }
/// <summary>
/// 空对象模式实现:所有方法均为空操作,用于测试和不需要反馈的实体。
/// </summary>
public class NullFeedbackPlayer : IFeedbackPlayer
{
public static readonly NullFeedbackPlayer Instance = new NullFeedbackPlayer();
public void PlayHit(HitWeight weight) { }
public void PlayParrySuccess() { }
public void PlayTakeHit() { }
public void PlayDeath() { }
public void PlayHeal() { }
public void PlayLandImpact() { }
public void PlayAttackWhoosh() { }
public void PlayJumpLaunch() { }
public void TriggerPreset(string presetId) { }
public void PlaySFXById(string sfxId) { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4baf6cd1a67e25347b18d8d8a13fbe02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,104 @@
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;
[SerializeField] private MMF_Player _onParrySuccess;
[Header("受伤 / 死亡反馈")]
[SerializeField] private MMF_Player _onTakeHit;
[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;
[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)
{
var player = weight switch
{
HitWeight.Light => _onHitLight,
HitWeight.Medium => _onHitMedium,
HitWeight.Heavy => _onHitHeavy,
_ => _onHitLight,
};
player?.PlayFeedbacks();
}
public void PlayParrySuccess() => _onParrySuccess?.PlayFeedbacks();
public void PlayTakeHit() => _onTakeHit?.PlayFeedbacks();
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 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}'。");
}
// ── 辅助 ─────────────────────────────────────────────────────────────────
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;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2865735d009e06d4b88c8f81b149b7d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
// Placeholder to prevent asmdef-no-scripts warning.
namespace BaseGames.Feedback { }

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e48c25f6e427fea4facf869cec33a9e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: