mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 05:47:13 +08:00
(client) feat:支持定义实体的碰撞体大小和偏移;建筑支持定义实体建筑和瓦片建筑,建筑支持指定按钮回调;添加存档管理器;Dev支持设置是否暂停;实体允许定义事件组;添加基地界面 (#57)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/57
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
using Base;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EventWorkClass
|
||||
{
|
||||
public abstract class EventWorkClassBase
|
||||
{
|
||||
public abstract void Init(string value);
|
||||
|
||||
public abstract void Run(string dimensionID);
|
||||
public abstract void Run(string dimensionID, Entity.Entity initiator = null);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,8 @@ namespace EventWorkClass
|
||||
/// 运行实体生成器事件,在指定维度生成实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionID">要生成实体的维度ID。</param>
|
||||
public override void Run(string dimensionID)
|
||||
/// <param name="initiator">发起者</param>
|
||||
public override void Run(string dimensionID,Entity.Entity initiator = null)
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
@@ -190,6 +191,7 @@ namespace EventWorkClass
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < _config.Count; i++)
|
||||
{
|
||||
// 随机选择一个实体定义
|
||||
@@ -205,13 +207,13 @@ namespace EventWorkClass
|
||||
|
||||
if (selectedEntityDef is CharacterDef characterDef)
|
||||
{
|
||||
EntityManage.Instance.GenerateEntity(dimensionID, characterDef, position);
|
||||
EntityManager.Instance.GenerateEntity(dimensionID, characterDef, position);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (selectedEntityDef is MonsterDef monsterDef)
|
||||
{
|
||||
EntityManage.Instance.GenerateMonsterEntity(dimensionID, monsterDef, position);
|
||||
EntityManager.Instance.GenerateMonsterEntity(dimensionID, monsterDef, position);
|
||||
continue;
|
||||
}
|
||||
Debug.LogWarning($"目标实体 '{selectedEntityDef.defName}' (类型: {selectedEntityDef.GetType().Name}) 既不是 CharacterDef 也不是 MonsterDef。" +
|
||||
@@ -237,7 +239,7 @@ namespace EventWorkClass
|
||||
Debug.LogError($"未找到维度 '{dimensionID}'。无法确定生成位置。");
|
||||
return Vector3.zero;
|
||||
}
|
||||
var mapGenerator = dimension.mapGenerator;
|
||||
var mapGenerator = dimension.landform;
|
||||
if (mapGenerator == null)
|
||||
{
|
||||
Debug.LogError($"维度 '{dimensionID}' 的地图生成器为空。无法确定生成位置。");
|
||||
@@ -249,32 +251,32 @@ namespace EventWorkClass
|
||||
{
|
||||
var size = mapGenerator.GetSize();
|
||||
var dir = Random.Range(0, 4); // 0:上, 1:下, 2:左, 3:右
|
||||
Vector2Int gridCoord; // 地图网格坐标
|
||||
Vector3Int gridCoord; // 地图网格坐标
|
||||
Vector2 worldCoord2D; // GetWorldCoordinates 返回的 Vector2Int
|
||||
Vector3 worldPos3D; // 最终的 Vector3 世界坐标
|
||||
var offset = _config.OffMapOffset;
|
||||
switch (dir)
|
||||
{
|
||||
case 0: // Top border (max Y)
|
||||
gridCoord = new Vector2Int(Random.Range(0, size.x), size.y);
|
||||
gridCoord = new Vector3Int(Random.Range(0, size.x), size.y);
|
||||
worldCoord2D = mapGenerator.GetWorldCoordinates(gridCoord);
|
||||
worldPos3D = new Vector3(worldCoord2D.x, worldCoord2D.y, 0f); // 转换为 Vector3
|
||||
worldPos3D.y += offset;
|
||||
break;
|
||||
case 1: // Bottom border (min Y)
|
||||
gridCoord = new Vector2Int(Random.Range(0, size.x), 0);
|
||||
gridCoord = new Vector3Int(Random.Range(0, size.x), 0);
|
||||
worldCoord2D = mapGenerator.GetWorldCoordinates(gridCoord);
|
||||
worldPos3D = new Vector3(worldCoord2D.x, worldCoord2D.y, 0f); // 转换为 Vector3
|
||||
worldPos3D.y -= offset;
|
||||
break;
|
||||
case 2: // Left border (min X)
|
||||
gridCoord = new Vector2Int(0, Random.Range(0, size.y));
|
||||
gridCoord = new Vector3Int(0, Random.Range(0, size.y));
|
||||
worldCoord2D = mapGenerator.GetWorldCoordinates(gridCoord);
|
||||
worldPos3D = new Vector3(worldCoord2D.x, worldCoord2D.y, 0f); // 转换为 Vector3
|
||||
worldPos3D.x -= offset;
|
||||
break;
|
||||
case 3: // Right border (max X)
|
||||
gridCoord = new Vector2Int(size.x, Random.Range(0, size.y));
|
||||
gridCoord = new Vector3Int(size.x, Random.Range(0, size.y));
|
||||
worldCoord2D = mapGenerator.GetWorldCoordinates(gridCoord);
|
||||
worldPos3D = new Vector3(worldCoord2D.x, worldCoord2D.y, 0f); // 转换为 Vector3
|
||||
worldPos3D.x += offset;
|
||||
@@ -301,7 +303,7 @@ namespace EventWorkClass
|
||||
Debug.LogWarning($"配置了 'AroundTargetEntity',但 'TargetFactionDefName' 为空或null。无法找到派系实体。将在原点生成。");
|
||||
return Vector3.zero;
|
||||
}
|
||||
var factionEntities = EntityManage.Instance.FindEntitiesByFaction(dimensionID, _config.TargetFactionDefName);
|
||||
var factionEntities = EntityManager.Instance.FindEntitiesByFaction(dimensionID, _config.TargetFactionDefName);
|
||||
if (factionEntities == null || factionEntities.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"在维度 '{dimensionID}' 中未找到派系 '{_config.TargetFactionDefName}' 的任何实体。将在原点生成。");
|
||||
@@ -324,7 +326,7 @@ namespace EventWorkClass
|
||||
case EntitySpawnLocationType.RandomlyOnMap:
|
||||
{
|
||||
var size = mapGenerator.GetSize();
|
||||
var randomGridPos = new Vector2Int(Random.Range(0, size.x), Random.Range(0, size.y));
|
||||
var randomGridPos = new Vector3Int(Random.Range(0, size.x), Random.Range(0, size.y));
|
||||
|
||||
var worldCoord2D = mapGenerator.GetWorldCoordinates(randomGridPos);
|
||||
var worldPos3D = new Vector3(worldCoord2D.x, worldCoord2D.y, 0f);
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using Entity;
|
||||
using Map; // 用于 LINQ
|
||||
|
||||
namespace EventWorkClass
|
||||
{
|
||||
/// <summary>
|
||||
/// Event_GiveHediff 事件的配置数据结构。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class GiveHediffConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 要添加或移除的 Hediff 的定义名 (defName)。
|
||||
/// </summary>
|
||||
public string HediffDefName;
|
||||
/// <summary>
|
||||
/// 要添加或移除的 Hediff 定义的具体类型名,例如 "HediffDef" 或其子类名。
|
||||
/// 用于 DefineManager 的严格类型查找。
|
||||
/// </summary>
|
||||
public string HediffDefTypeName;
|
||||
/// <summary>
|
||||
/// 要执行的操作类型:添加或移除 Hediff。
|
||||
/// </summary>
|
||||
public HediffActionType ActionType;
|
||||
/// <summary>
|
||||
/// 目标实体选择类型。
|
||||
/// </summary>
|
||||
public TargetEntitySelectionType TargetSelectionType;
|
||||
// 以下是各种 TargetSelectionType 可能需要的配置参数
|
||||
|
||||
/// <summary>
|
||||
/// 用于 SpecificEntityById 类型:目标实体的唯一ID。
|
||||
/// </summary>
|
||||
public string TargetEntityId;
|
||||
/// <summary>
|
||||
/// 用于 AllEntitiesInFaction 或 TargetSpecificFactionLeader 类型:目标派系的定义名 (factionDefName)。
|
||||
/// </summary>
|
||||
public string TargetFactionDefName;
|
||||
/// <summary>
|
||||
/// 用于 AroundSpecificCoordinates 类型:生成中心坐标。
|
||||
/// </summary>
|
||||
public Vector3 CenterCoordinates;
|
||||
/// <summary>
|
||||
/// 用于 AroundSpecificCoordinates 类型:搜索半径。
|
||||
/// </summary>
|
||||
public float Radius = 10f; // 默认半径
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定义针对 Hediff 的操作类型。
|
||||
/// </summary>
|
||||
public enum HediffActionType
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加 Hediff。
|
||||
/// </summary>
|
||||
Add,
|
||||
/// <summary>
|
||||
/// 移除 Hediff。
|
||||
/// </summary>
|
||||
Remove
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定义目标实体选择的类型。
|
||||
/// </summary>
|
||||
public enum TargetEntitySelectionType
|
||||
{
|
||||
/// <summary>
|
||||
/// 未指定或无效的目标选择类型。
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// 目标是当前玩家(如果存在)。
|
||||
/// </summary>
|
||||
Player,
|
||||
/// <summary>
|
||||
/// 目标是指定派系的所有活动实体。
|
||||
/// </summary>
|
||||
AllEntitiesInFaction,
|
||||
/// <summary>
|
||||
/// 目标是指定坐标周围半径内的所有生物。
|
||||
/// </summary>
|
||||
AroundSpecificCoordinates
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event_GiveHediff 事件类:用于给予或移除健康状态 (Hediff)。
|
||||
/// </summary>
|
||||
public class Event_GiveHediff : EventWorkClassBase
|
||||
{
|
||||
private GiveHediffConfig _config;
|
||||
private HediffDef _aimHediffDef;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 Hediff 给予/移除事件。
|
||||
/// </summary>
|
||||
/// <param name="value">包含事件配置的JSON字符串。</param>
|
||||
public override void Init(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Debug.LogError("Event_GiveHediff: 初始化值为空或null。请提供一个JSON配置字符串。");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_config = JsonUtility.FromJson<GiveHediffConfig>(value);
|
||||
if (_config == null)
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: 无法解析配置JSON: {value}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_config.HediffDefTypeName) || string.IsNullOrEmpty(_config.HediffDefName))
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: Hediff定义类型名或名称为空或null (名称: '{_config.HediffDefName}', 类型: '{_config.HediffDefTypeName}')。无法查找Hediff定义。");
|
||||
return;
|
||||
}
|
||||
|
||||
_aimHediffDef = (HediffDef)DefineManager.Instance.FindDefine(_config.HediffDefTypeName, _config.HediffDefName);
|
||||
if (_aimHediffDef == null)
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: 未找到Hediff定义 (名称: '{_config.HediffDefName}', 类型: '{_config.HediffDefTypeName}')。请检查配置。");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: 解析配置JSON时出错: {value}。异常信息: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行 Hediff 给予/移除事件。
|
||||
/// </summary>
|
||||
/// <param name="dimensionID">目标实体所在的维度ID。</param>
|
||||
public override void Run(string dimensionID)
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
Debug.LogError("Event_GiveHediff: 事件配置(_config)为空。Init()可能失败了。");
|
||||
return;
|
||||
}
|
||||
if (_aimHediffDef == null)
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: 目标Hediff定义为空 (名称: {_config.HediffDefName}, 类型: {_config.HediffDefTypeName})。无法执行操作。");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetEntities = GetTargetEntities(dimensionID);
|
||||
if (targetEntities == null || targetEntities.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"Event_GiveHediff: 在维度 '{dimensionID}' 中未找到符合条件的目标实体 (选择类型: {_config.TargetSelectionType})。");
|
||||
return;
|
||||
}
|
||||
|
||||
var successCount = 0;
|
||||
var failedCount = 0;
|
||||
var hediffInstance = new Hediff(_aimHediffDef); // 创建 Hediff 实例
|
||||
|
||||
foreach (var entity in targetEntities)
|
||||
{
|
||||
if (entity is LivingEntity livingEntity)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.ActionType == HediffActionType.Add)
|
||||
{
|
||||
livingEntity.AddHediff(hediffInstance);
|
||||
Debug.Log($"Event_GiveHediff: 已向实体 '{livingEntity.name}' 添加 Hediff '{_aimHediffDef.defName}'。");
|
||||
successCount++;
|
||||
}
|
||||
else if (_config.ActionType == HediffActionType.Remove)
|
||||
{
|
||||
livingEntity.RemoveHediff(hediffInstance);
|
||||
Debug.Log($"Event_GiveHediff: 已从实体 '{livingEntity.name}' 移除 Hediff '{_aimHediffDef.defName}'。");
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: 对实体 '{entity.name}' 执行 Hediff 操作时出错: {ex.Message}");
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Event_GiveHediff: 实体 '{entity.name}' (类型: {entity.GetType().Name}) 不是 LivingEntity,无法添加/移除 Hediff。");
|
||||
failedCount++;
|
||||
}
|
||||
}
|
||||
Debug.Log($"Event_GiveHediff: 完成操作。成功: {successCount},失败: {failedCount} (Hediff: {_aimHediffDef.defName}, 操作: {_config.ActionType})。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据配置获取目标实体列表。
|
||||
/// </summary>
|
||||
/// <param name="dimensionID">维度ID。</param>
|
||||
/// <returns>符合条件的目标实体列表。</returns>
|
||||
private List<Entity.Entity> GetTargetEntities(string dimensionID)
|
||||
{
|
||||
var entities = new List<Entity.Entity>();
|
||||
var dimension = Program.Instance.GetDimension(dimensionID);
|
||||
|
||||
if (dimension == null)
|
||||
{
|
||||
Debug.LogError($"Event_GiveHediff: 未找到维度 '{dimensionID}'。无法获取目标实体。");
|
||||
return entities;
|
||||
}
|
||||
|
||||
switch (_config.TargetSelectionType)
|
||||
{
|
||||
case TargetEntitySelectionType.Player:
|
||||
// 假设 PlayerManager.Instance.GetPlayerEntity(dimensionID) 返回 LivingEntity
|
||||
// 或者你的玩家实体本身就是 LivingEntity
|
||||
var player = dimension.focusEntity as LivingEntity; // 假设玩家是维度的一部分且是LivingEntity
|
||||
if (player != null)
|
||||
{
|
||||
entities.Add(player);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Event_GiveHediff: 未能找到维度 '{dimensionID}' 中的玩家实体或玩家不是 LivingEntity。");
|
||||
}
|
||||
break;
|
||||
|
||||
case TargetEntitySelectionType.AllEntitiesInFaction:
|
||||
if (string.IsNullOrEmpty(_config.TargetFactionDefName))
|
||||
{
|
||||
Debug.LogWarning("Event_GiveHediff: 配置了 'AllEntitiesInFaction',但 'TargetFactionDefName' 为空。");
|
||||
return entities;
|
||||
}
|
||||
|
||||
entities.AddRange(EntityManage.Instance
|
||||
.FindEntitiesByFaction(dimensionID, _config.TargetFactionDefName)
|
||||
.Select(item => item.entity));
|
||||
break;
|
||||
|
||||
case TargetEntitySelectionType.AroundSpecificCoordinates:
|
||||
// 假设 EntityManage 提供了根据位置和半径查找实体的方法
|
||||
// 或者可以直接通过场景查询(例如 Physics.OverlapSphere 或者自定义管理系统)
|
||||
// 为了简化,我将假设 EntityManage 提供 FindEntitiesAroundRadius 方法
|
||||
var allEntitiesInDimension = EntityManage.Instance.GetAllEntities(dimensionID)
|
||||
.Select(item => item.entity);
|
||||
var center = _config.CenterCoordinates;
|
||||
var radius = _config.Radius;
|
||||
|
||||
foreach (var ent in allEntitiesInDimension)
|
||||
{
|
||||
// 确保实体有 Transform 且在半径内
|
||||
if (ent != null && ent.transform != null && Vector3.Distance(ent.transform.position, center) <= radius)
|
||||
{
|
||||
entities.Add(ent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TargetEntitySelectionType.None:
|
||||
default:
|
||||
Debug.LogWarning($"Event_GiveHediff: 未知或不支持的目标实体选择类型: {_config.TargetSelectionType}。");
|
||||
break;
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
Client/Assets/Scripts/EventWorkClass/Event_OpenUI.cs
Normal file
74
Client/Assets/Scripts/EventWorkClass/Event_OpenUI.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using Base;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace EventWorkClass
|
||||
{
|
||||
public class Event_OpenUI : EventWorkClassBase
|
||||
{
|
||||
// 新增:私有数据类,用于JSON反序列化
|
||||
private class OpenUIEventData
|
||||
{
|
||||
public string uiName { get; set; } // 对应JSON中的 "uiName" 字段
|
||||
}
|
||||
|
||||
// 新增:私有字段,用于存储解析出的UI名称
|
||||
private string _uiName;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化事件,解析JSON字符串获取UI名称。
|
||||
/// </summary>
|
||||
/// <param name="value">包含UI名称的JSON字符串,例如: {"uiName": "MainMenuUI"}</param>
|
||||
/// <exception cref="System.ArgumentException">当value为null或空时抛出。</exception>
|
||||
/// <exception cref="System.FormatException">当JSON格式无效或缺少'uiName'属性时抛出。</exception>
|
||||
/// <exception cref="System.InvalidOperationException">当发生其他意外错误时抛出。</exception>
|
||||
public override void Init(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
throw new System.ArgumentException(
|
||||
"Event_OpenUI Init: value cannot be null or empty. It must contain the UI configuration JSON.",
|
||||
nameof(value));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 反序列化JSON字符串为OpenUIEventData对象
|
||||
OpenUIEventData eventData = JsonConvert.DeserializeObject<OpenUIEventData>(value);
|
||||
// 检查反序列化结果是否有效
|
||||
if (eventData == null || string.IsNullOrEmpty(eventData.uiName))
|
||||
{
|
||||
throw new System.FormatException(
|
||||
$"Event_OpenUI Init: Invalid JSON format or missing 'uiName' property in value: '{value}'");
|
||||
}
|
||||
|
||||
// 存储UI名称
|
||||
_uiName = eventData.uiName;
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonSerializationException ex)
|
||||
{
|
||||
// JSON反序列化失败的特定处理
|
||||
throw new System.FormatException(
|
||||
$"Event_OpenUI Init: Failed to deserialize JSON for value: '{value}'. Please ensure it is a valid JSON string with a 'uiName' property. Error: {ex.Message}",
|
||||
ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 捕获其他所有潜在异常,提供更通用的错误信息
|
||||
throw new System.InvalidOperationException(
|
||||
$"Event_OpenUI Init: An unexpected error occurred during initialization for value: '{value}'. Error: {ex.Message}",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行事件,打开UI界面。
|
||||
/// </summary>
|
||||
/// <param name="dimensionID">维度ID (当前未被使用,但保留接口)。</param>
|
||||
/// <param name="initiator">触发此事件的实体 (当前未被使用,但保留接口)。</param>
|
||||
public override void Run(string dimensionID, Entity.Entity initiator = null)
|
||||
{
|
||||
UIInputControl.Instance.Show(_uiName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83d4eabcba674ad6b8120b84cb71de7a
|
||||
timeCreated: 1758949174
|
||||
161
Client/Assets/Scripts/EventWorkClass/Event_PlayAudio.cs
Normal file
161
Client/Assets/Scripts/EventWorkClass/Event_PlayAudio.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using Managers;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EventWorkClass
|
||||
{
|
||||
/// <summary>
|
||||
/// 音频播放事件的配置类。
|
||||
/// 通过JSON字符串进行初始化。
|
||||
/// </summary>
|
||||
public class AudioEventConfig
|
||||
{
|
||||
[Tooltip("要播放的音频定义名,通过 AudioManager 获取 AudioClip。")]
|
||||
public string audioClipName;
|
||||
[Tooltip("播放音量。0.0 到 1.0。")]
|
||||
[Range(0f, 1f)]
|
||||
public float volume = 1.0f;
|
||||
[Tooltip("是否循环播放。")]
|
||||
public bool loop = false;
|
||||
[Tooltip("空间混合。0.0 为完全2D,1.0 为完全3D。")]
|
||||
[Range(0f, 1f)]
|
||||
public float spatialBlend = 0.0f; // 默认为2D音效
|
||||
[Tooltip("如果存在发起者,是否将音频播放到发起者(initiator)的 AudioSource 上。")]
|
||||
public bool attachToInitiator = true;
|
||||
[Tooltip("播放前的延迟秒数。")]
|
||||
public float delay = 0.0f;
|
||||
[Tooltip("如果是非循环播放且是临时创建的 AudioSource,是否在播放结束后自动销毁。")]
|
||||
public bool destroyAfterPlay = true;
|
||||
|
||||
/// <summary>
|
||||
/// 验证配置是否有效。
|
||||
/// </summary>
|
||||
public bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrEmpty(audioClipName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实现一个音频播放事件。
|
||||
/// 配置通过JSON字符串传入,可指定音频名、音量、循环、空间混合、是否依附于发起者等。
|
||||
/// </summary>
|
||||
public class Event_PlayAudio : EventWorkClassBase
|
||||
{
|
||||
private AudioEventConfig _config;
|
||||
private bool _isInitialized = false;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化音频播放事件,解析JSON配置。
|
||||
/// </summary>
|
||||
/// <param name="value">包含音频配置的JSON字符串。</param>
|
||||
public override void Init(string value)
|
||||
{
|
||||
_isInitialized = false;
|
||||
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Debug.LogError("EventPlayAudio 初始化错误:配置 JSON 值为 null 或空。跳过初始化。");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
// 使用 Newtonsoft.Json 进行反序列化
|
||||
_config = JsonConvert.DeserializeObject<AudioEventConfig>(value);
|
||||
if (_config == null || !_config.IsValid())
|
||||
{
|
||||
Debug.LogError("EventPlayAudio 初始化错误:反序列化的配置无效或为空。音频剪辑名称可能缺失。跳过初始化。");
|
||||
}
|
||||
else
|
||||
{
|
||||
_isInitialized = true;
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex) // 可以更具体地捕获 Newtonsoft.Json.JsonException
|
||||
{
|
||||
Debug.LogError($"EventPlayAudio 初始化错误:使用 Newtonsoft.Json 解析 JSON 配置失败:{ex.Message}\nJSON: {value}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行音频播放事件。
|
||||
/// </summary>
|
||||
/// <param name="dimensionID">维度ID(此事件中暂未使用,保留扩展性)。</param>
|
||||
/// <param name="initiator">事件发起者实体,可能包含一个AudioSource。</param>
|
||||
public override void Run(string dimensionID, Entity.Entity initiator = null)
|
||||
{
|
||||
if (!_isInitialized)
|
||||
{
|
||||
Debug.LogError("EventPlayAudio 运行错误:事件未初始化或配置无效。跳过音频播放。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取音频片段
|
||||
var audioClip = AudioManager.Instance.GetAudioClip(_config.audioClipName);
|
||||
if (!audioClip)
|
||||
{
|
||||
Debug.LogError($"EventPlayAudio 运行错误:AudioManager 中未找到音频剪辑 '{_config.audioClipName}'。跳过播放。");
|
||||
return;
|
||||
}
|
||||
|
||||
AudioSource audioSourceToUse = null;
|
||||
var isTemporaryAudioSource = false;
|
||||
|
||||
// 尝试使用发起者的AudioSource
|
||||
if (_config.attachToInitiator && initiator != null && initiator.Audio != null)
|
||||
{
|
||||
audioSourceToUse = initiator.Audio;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有发起者AudioSource或不依附于发起者,则创建临时的GameObject和AudioSource
|
||||
var tempAudioGO = new GameObject($"TempAudio__{_config.audioClipName}");
|
||||
audioSourceToUse = tempAudioGO.AddComponent<AudioSource>();
|
||||
isTemporaryAudioSource = true;
|
||||
|
||||
if (_config.spatialBlend > 0f)
|
||||
{
|
||||
if (initiator != null)
|
||||
{
|
||||
tempAudioGO.transform.position = initiator.transform.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempAudioGO.transform.position = Vector3.zero; // 默认位置
|
||||
}
|
||||
}
|
||||
// 如果 spatialBlend 为 0,则无需设置位置,它将保持默认的 Vector3.zero。
|
||||
}
|
||||
|
||||
// 配置AudioSource参数
|
||||
audioSourceToUse.clip = audioClip;
|
||||
audioSourceToUse.volume = _config.volume;
|
||||
audioSourceToUse.loop = _config.loop;
|
||||
audioSourceToUse.spatialBlend = _config.spatialBlend;
|
||||
audioSourceToUse.playOnAwake = false; // 由Run方法显式控制播放
|
||||
|
||||
// 播放音频
|
||||
if (_config.delay > 0)
|
||||
{
|
||||
audioSourceToUse.PlayDelayed(_config.delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
audioSourceToUse.Play();
|
||||
}
|
||||
|
||||
// 如果是临时AudioSource且不循环播放,且配置为播放后销毁,则在播放结束后自动销毁
|
||||
if (isTemporaryAudioSource && !_config.loop && _config.destroyAfterPlay)
|
||||
{
|
||||
// 计算销毁时间:延迟 + 音频长度
|
||||
var destroyTime = _config.delay + audioClip.length;
|
||||
Object.Destroy(audioSourceToUse.gameObject, destroyTime);
|
||||
}
|
||||
// 如果是循环播放的临时AudioSource,不自动销毁,需要外部机制停止/销毁
|
||||
else if (isTemporaryAudioSource && _config.loop)
|
||||
{
|
||||
Debug.LogWarning($"EventPlayAudio:为 '{_config.audioClipName}' 创建了循环临时 AudioSource。它将不会被自动销毁。需要外部管理来停止和销毁它。");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4babb896d7a4ce3a3fc68604379bc58
|
||||
timeCreated: 1758536607
|
||||
Reference in New Issue
Block a user