using System.Collections.Generic;
using System.Reflection;
using Animancer;
using BaseGames.Boss;
using BaseGames.Camera;
using BaseGames.Combat;
using BaseGames.Combat.StatusEffects;
using BaseGames.Dialogue;
using BaseGames.Enemies;
using BaseGames.Enemies.Abilities;
using BaseGames.Enemies.Boss;
using BaseGames.Enemies.Navigation;
using BaseGames.Enemies.Perception;
using BaseGames.Equipment;
using BaseGames.Parry;
using BaseGames.Player;
using BaseGames.Player.States;
using BaseGames.Skills;
using BaseGames.World;
using PathBerserker2d;
using Unity.Cinemachine;
using UnityEditor;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace BaseGames.Editor
{
///
/// 场景对象快速放置工具。
/// 在当前活动场景中生成常用游戏对象(玩家、敌人、机关、存档点、相机等),
/// 并自动挂载基础组件、设置正确的物理层、绑定已有的事件频道资产。
///
/// 菜单:BaseGames → Scene → Place → …
///
/// 所有操作支持 Undo(Ctrl+Z)。生成后选中对象便于立即调整位置。
///
public static class SceneObjectPlacerTool
{
// ══ 菜单入口 ══════════════════════════════════════════════════════════
[MenuItem("BaseGames/Scene/Place/Player", priority = 100)]
public static void PlacePlayer()
{
var report = new List();
// ── Player 根节点(行为+物理+标签三合一)──────────────────────────────
// Rigidbody2D / 所有 MonoBehaviour 集中于此节点。
// HurtBox 作为其子节点,GetComponentInParent() 向上即可找到
// 本节点上的 PlayerController(IDamageable 实现者)。
GameObject root = new GameObject("Player");
Undo.RegisterCreatedObjectUndo(root, "Place Player");
root.transform.position = GetDropPosition();
root.tag = "Player";
SetLayer(root, "Player", report);
// 物理组件(PlayerMovement RequireComponent(Rigidbody2D),必须同节点)
Rigidbody2D rb = GetOrAddComponent(root);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
rb.interpolation = RigidbodyInterpolation2D.Interpolate;
GetOrAddComponent(root);
// 动画组件(AnimancerComponent 需要 Animator 存在;PlayerController
// [RequireComponent(typeof(AnimancerComponent))] 保证其存在)
GetOrAddComponent(root);
AnimancerComponent animancer = GetOrAddComponent(root);
SetupSpriteRenderer(root);
// 核心行为组件
PlayerStats playerStats = GetOrAddComponent(root);
PlayerMovement playerMovement = GetOrAddComponent(root);
PlayerCombat playerCombat = GetOrAddComponent(root);
FormController formController = GetOrAddComponent(root);
WeaponManager weaponManager = GetOrAddComponent(root);
SkillManager skillManager = GetOrAddComponent(root);
SpringSystem springSystem = GetOrAddComponent(root);
ParrySystem parrySystem = GetOrAddComponent(root);
ShieldComponent shield = GetOrAddComponent(root);
PlayerWallDetector wallDetector = GetOrAddComponent(root);
EquipmentManager equipmentManager = GetOrAddComponent(root);
GetOrAddComponent(root);
StatusEffectManager statusEffectManager = GetOrAddComponent(root);
// PlayerController 最后添加:RequireComponent 会拉取上方已加好的组件
PlayerController playerController = GetOrAddComponent(root);
// ── HurtBox 子节点 ───────────────────────────────────────────────────
Transform hurtBoxT = GetOrCreateChild(root.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "PlayerHurtBox", report);
BoxCollider2D hurtCollider = GetOrAddComponent(hurtBoxT.gameObject);
hurtCollider.isTrigger = true;
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
// ── [WeaponSocket] 子节点(WeaponManager 动态实例化武器 HitBox 的挂点)
Transform weaponSocketT = GetOrCreateChild(root.transform, "[WeaponSocket]");
// ── GroundCheck 子节点(地面检测 Transform)────────────────────────
Transform groundCheckT = GetOrCreateChild(root.transform, "GroundCheck");
groundCheckT.localPosition = new Vector3(0f, -0.75f, 0f);
AssignReference(playerMovement, "_groundCheck", groundCheckT, report);
AssignLayerMask(playerMovement, "_groundLayer",
new[] { "Platform", "OneWayPlatform", "MovingOneWayPlatform", "MidHeightOneWayPlatform" },
report);
// ── SkillHitBox_Slot 子节点(技能 HitBox 实例化挂点)────────────────
Transform skillSocketT = GetOrCreateChild(root.transform, "SkillHitBox_Slot");
// ── CameraFollowTarget 子节点(CinemachineCamera.Follow 目标)────────
GetOrCreateChild(root.transform, "CameraFollowTarget");
// ── PlayerController SerializeField 引用赋值 ──────────────────────
AssignReference(playerController, "_combat", playerCombat, report);
AssignReference(playerController, "_formController", formController, report);
AssignReference(playerController, "_weaponManager", weaponManager, report);
AssignReference(playerController, "_skillManager", skillManager, report);
AssignReference(playerController, "_springSystem", springSystem, report);
AssignReference(playerController, "_parrySystem", parrySystem, report);
AssignReference(playerController, "_hurtBox", hurtBox, report);
AssignReference(playerController, "_shield", shield, report);
AssignReference(playerController, "_wallDetector", wallDetector, report);
// ── 其他组件内部引用 ────────────────────────────────────────────────
AssignReference(playerCombat, "_weaponManager", weaponManager, report);
AssignReference(springSystem, "_stats", playerStats, report);
// WeaponManager 内部引用
AssignReference(weaponManager, "_formController", formController, report);
AssignReference(weaponManager, "_weaponSocket", weaponSocketT, report);
// SkillManager 内部引用(技能系统核心依赖)
AssignReference(skillManager, "_stats", playerStats, report);
AssignReference(skillManager, "_animancer", animancer, report);
AssignReference(skillManager, "_formController", formController, report);
AssignReference(skillManager, "_modifiers", GetOrAddComponent(root), report);
AssignReference(skillManager, "_skillSocket", skillSocketT, report);
// PlayerWallDetector 墙壁检测层(Wall + Platform 组合)
{
int wallMask = 0;
int wallL = LayerMask.NameToLayer("Wall");
int groundL = LayerMask.NameToLayer("Platform");
if (wallL != -1) wallMask |= 1 << wallL;
if (groundL != -1) wallMask |= 1 << groundL;
if (wallMask != 0)
{
var wso = new SerializedObject(wallDetector);
var wsp = wso.FindProperty("_wallLayer");
if (wsp != null) { wsp.intValue = wallMask; wso.ApplyModifiedPropertiesWithoutUndo(); }
}
else
report.Add("★ Layer 'Wall'/'Platform' 不存在,PlayerWallDetector._wallLayer 未赋值。");
}
// ── 事件频道(可选,缺失时跳过) ───────────────────────────────────
AssignAsset(playerStats, "_onHPChanged", report, false, "EVT_HPChanged");
AssignAsset(playerStats, "_onMaxHPChanged", report, false, "EVT_MaxHPChanged");
AssignAsset(playerStats, "_onSoulPowerChanged", report, false, "EVT_SoulPowerChanged");
AssignAsset(playerStats, "_onSpiritPowerChanged", report, false, "EVT_SpiritPowerChanged");
AssignAsset(playerStats, "_onSpringChargesChanged", report, false, "EVT_SpringChargesChanged");
AssignAsset(playerStats, "_onLingZhuChanged", report, false, "EVT_LingZhuChanged");
AssignAsset(playerStats, "_onAbilityUnlocked", report, false, "EVT_AbilityUnlocked");
AssignAsset(playerStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(playerController, "_onPlayerDied", report, false, "EVT_PlayerDied");
AssignAsset(playerController, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(springSystem, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(parrySystem, "_onParrySuccess", report, false, "EVT_ParrySuccess");
AssignAsset(formController, "_onFormChanged", report, false, "EVT_FormChanged");
AssignAsset(formController, "_onSkillSetChanged", report, false, "EVT_SkillSetChanged");
AssignAsset(equipmentManager, "_onCharmEquipped", report, false, "EVT_CharmEquipped");
AssignAsset(equipmentManager, "_onCharmUnequipped", report, false, "EVT_CharmUnequipped");
AssignAsset(equipmentManager, "_onEquipmentChanged", report, false, "EVT_EquipmentChanged");
AssignAsset(equipmentManager, "_onAchievementNotchGranted", report, false, "EVT_AchievementNotchGranted");
AssignAsset(statusEffectManager, "_onStatusEffectApplied", report, false, "EVT_StatusEffectApplied");
AssignAsset(statusEffectManager, "_onStatusEffectExpired", report, false, "EVT_StatusEffectExpired");
AssignAsset(shield, "_onShieldBrokenChannel", report, false, "EVT_ShieldBroken");
AssignAsset(shield, "_onShieldRestoredChannel", report, false, "EVT_ShieldRestored");
// ── Config SO 自动查找(资产存在时自动绑定)──────────────────────
Object statsConfig = FindFirstAsset("PLY_PlayerStats");
Object movConfig = FindFirstAsset("PLY_PlayerMovementConfig");
Object formConfig = FindFirstAsset("PLY_FormConfig");
Object parryConfig = FindFirstAsset("PLY_ParryConfig");
Object shieldConfig = FindFirstAsset("PLY_ShieldConfig");
Object inputReader = FindFirstAsset("InputReader");
Object equipmentConfig = FindFirstAsset("PLY_EquipmentConfig");
Object charmCatalog = FindFirstAsset("CHM_Catalog");
Object animConfig = FindFirstAsset("PLY_PlayerAnimationConfig");
if (statsConfig != null) AssignReference(playerStats, "_config", statsConfig, report);
if (movConfig != null)
{
AssignReference(playerController, "_movementConfig", movConfig, report);
AssignReference(playerMovement, "_config", movConfig, report);
AssignReference(wallDetector, "_config", movConfig, report);
}
if (formConfig != null)
{
AssignReference(playerController, "_formConfig", formConfig, report);
AssignReference(formController, "_config", formConfig, report);
}
if (parryConfig != null) AssignReference(parrySystem, "_config", parryConfig, report);
if (shieldConfig != null) AssignReference(shield, "_config", shieldConfig, report);
if (animConfig != null) AssignReference(playerController, "_animConfig", animConfig, report);
if (inputReader != null)
{
AssignReference(playerController, "_inputReader", inputReader, report);
AssignReference(formController, "_input", inputReader, report);
AssignReference(skillManager, "_input", inputReader, report);
}
if (equipmentConfig != null) AssignReference(equipmentManager, "_config", equipmentConfig, report);
if (charmCatalog != null) AssignReference(equipmentManager, "_charmCatalog", charmCatalog, report);
if (animConfig == null) report.Add("★ 需创建并绑定:PlayerController._animConfig(PLY_PlayerAnimationConfig)");
if (statsConfig == null) report.Add("★ 需创建并绑定:PlayerStats._config(PlayerStatsSO)");
if (inputReader == null) report.Add("★ 需手动绑定:PlayerController._inputReader / FormController._input / SkillManager._input(InputReaderSO)");
if (equipmentConfig == null) report.Add("★ 需创建并绑定:EquipmentManager._config(EquipmentConfigSO)");
if (charmCatalog == null) report.Add("★ 需创建并绑定:EquipmentManager._charmCatalog(CharmCatalogSO)");
report.Add("SkillManager._formSkillSets 技能槽 SO 需手动填入。");
Selection.activeGameObject = root;
MarkDirtyAndLog("Player", root, report);
}
[MenuItem("BaseGames/Scene/Place/Player Spawn Point", priority = 105)]
public static void PlacePlayerSpawnPoint()
{
var report = new List();
GameObject go = new GameObject("SpawnPoint");
Undo.RegisterCreatedObjectUndo(go, "Place Player Spawn Point");
go.transform.position = GetDropPosition();
PlayerSpawnPoint spawnPoint = GetOrAddComponent(go);
AssignString(spawnPoint, "_transitionId", "default", report);
AssignInt(spawnPoint, "_facingDirection", 1);
report.Add("修改 _transitionId,使其与对应 RoomTransition._targetTransitionId 匹配。");
report.Add("+1 = 朝右出生,-1 = 朝左出生(_facingDirection)。");
Selection.activeGameObject = go;
MarkDirtyAndLog("Player Spawn Point", go, report);
}
[MenuItem("BaseGames/Scene/Place/Enemy (Basic)", priority = 110)]
public static void PlaceEnemy()
{
var report = new List();
GameObject go = new GameObject("BasicEnemy");
Undo.RegisterCreatedObjectUndo(go, "Place Enemy");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
GetOrAddComponent(go);
GetOrAddComponent(go);
SetupSpriteRenderer(go);
EnemyBase enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
// HurtBox child
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCollider = GetOrAddComponent(hurtBoxT.gameObject);
hurtCollider.isTrigger = true;
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
// Contact-damage HitBox child
Transform hitBodyT = GetOrCreateChild(go.transform, "HitBox_Body");
SetLayer(hitBodyT.gameObject, "EnemyHitBox", report);
CircleCollider2D hitCollider = GetOrAddComponent(hitBodyT.gameObject);
hitCollider.isTrigger = true;
hitCollider.radius = 0.55f;
HitBox hitBox = GetOrAddComponent(hitBodyT.gameObject);
GetOrAddComponent(hitBodyT.gameObject);
// References
AssignReference(enemyBase, "_stats", enemyStats, report);
// DamageSourceSO for body contact (optional — create manually if missing)
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody", "DS_EnemyBody");
if (dmgSrc != null)
AssignReference(hitBox, "_defaultSource", dmgSrc, report);
else
report.Add("未找到 DamageSourceSO,HitBox_Body._defaultSource 未绑定。请按规范创建 CMB_DS_EnemyBody.asset。");
// Event channels
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
// EnemyStatsSO (optional)
Object enemyStatsSO = FindFirstAsset("BasicEnemyStats", "EnemyStatsSO");
if (enemyStatsSO != null)
AssignReference(enemyBase, "_statsSO", enemyStatsSO, report);
else
report.Add("未找到 EnemyStatsSO,EnemyBase._statsSO 未绑定。请在 Data/Enemies/ 创建 ENM_{id}_Stats.asset 后手动指定。");
report.Add("行为树、导航参数(NavAgent)、动画片段需后续手工挂载。");
Selection.activeGameObject = go;
MarkDirtyAndLog("Enemy (Basic)", go, report);
}
[MenuItem("BaseGames/Scene/Place/Boss Enemy", priority = 115)]
public static void PlaceBossEnemy()
{
var report = new List();
GameObject go = new GameObject("BossEnemy");
Undo.RegisterCreatedObjectUndo(go, "Place Boss Enemy");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
rb.interpolation = RigidbodyInterpolation2D.Interpolate;
GetOrAddComponent(go);
GetOrAddComponent(go);
SetupSpriteRenderer(go);
BossBase bossBase = GetOrAddComponent(go);
EnemyStats bossStats = GetOrAddComponent(go);
// HurtBox child
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCollider = GetOrAddComponent(hurtBoxT.gameObject);
hurtCollider.isTrigger = true;
hurtCollider.size = new Vector2(1.5f, 2.5f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
// Contact-damage HitBox child
Transform hitBodyT = GetOrCreateChild(go.transform, "HitBox_Body");
SetLayer(hitBodyT.gameObject, "EnemyHitBox", report);
CircleCollider2D hitCollider = GetOrAddComponent(hitBodyT.gameObject);
hitCollider.isTrigger = true;
hitCollider.radius = 0.9f;
HitBox hitBox = GetOrAddComponent(hitBodyT.gameObject);
GetOrAddComponent(hitBodyT.gameObject);
// References
AssignReference(bossBase, "_stats", bossStats, report);
// DamageSourceSO
Object dmgSrc = FindFirstAsset("CMB_DS_BossBody", "CMB_DS_EnemyBody", "DS_BossBody");
if (dmgSrc != null)
AssignReference(hitBox, "_defaultSource", dmgSrc, report);
else
report.Add("未找到 DamageSourceSO,HitBox_Body._defaultSource 未绑定。请按规范创建 CMB_DS_BossBody.asset。");
// Event channels
AssignAsset(bossBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(bossBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(bossBase, "_onBossFightEnded", report, false, "EVT_BossFightEnded");
AssignAsset(bossBase, "_onBossPhaseChanged", report, false, "EVT_BossPhaseChanged");
AssignAsset(bossStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
report.Add("填写 _bossId。");
report.Add("挂载 BossSkillSequencer 组件并指定技能序列 SO;行为树、NavAgent 需手工添加。");
report.Add("多阶段 Boss 可在此 GameObject 上继续 AddComponent 阶段切换控制器。");
Selection.activeGameObject = go;
MarkDirtyAndLog("Boss Enemy", go, report);
}
// ══ 具体敌人快速放置 ════════════════════════════════════════════════════
[MenuItem("BaseGames/Scene/Place/Enemy E001 (草蛭)", priority = 111)]
public static void PlaceE001_CaoZhi()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place E001 草蛭");
GameObject go = new GameObject("ENM_CaoZhi");
Undo.RegisterCreatedObjectUndo(go, "Place E001");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(0.6f, 0.8f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SpriteRenderer sr1 = SetupSpriteRenderer(go);
EnemyBase enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
EnemyMovement movement = GetOrAddComponent(go);
GetOrAddComponent(go);
GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(0.55f, 0.75f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
Transform contactT = GetOrCreateChild(go.transform, "ContactDamageZone");
SetLayer(contactT.gameObject, "EnemyHitBox", report);
CircleCollider2D contactCol = GetOrAddComponent(contactT.gameObject);
contactCol.isTrigger = true;
contactCol.radius = 0.4f;
HitBox contactHitBox = GetOrAddComponent(contactT.gameObject);
BodyContactDamage bodyContact = GetOrAddComponent(contactT.gameObject);
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform alertT = GetOrCreateChild(abilitiesT, "PlayClipAbility_Alert");
PlayClipAbility alertAbility = GetOrAddComponent(alertT.gameObject);
Transform chaseT = GetOrCreateChild(abilitiesT, "ContactChaseAbility_Chase");
ContactChaseAbility chaseAbility = GetOrAddComponent(chaseT.gameObject);
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(enemyBase, "_statsSO", report, false, "ENM_E001_Stats");
AssignAsset(enemyBase, "_animConfig", report, false, "ENM_E001_AnimConfig");
// Component wiring
AssignReference(enemyBase, "_stats", enemyStats, report);
AssignReference(enemyBase, "_movement", movement, report);
AssignReference(enemyBase, "_animancer", animancer, report);
AssignReference(enemyBase, "_hurtBox", hurtBox, report);
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(movement, "_config", report, false, "ENM_E001_Stats");
AssignAsset(movement, "_animConfig", report, false, "ENM_E001_AnimConfig");
AssignReference(movement, "_animancer", animancer, report);
AssignReference(movement, "_spriteRenderer", sr1, report);
AssignAsset(alertAbility, "_config", report, false, "ABL_E001_Alert");
AssignAsset(chaseAbility, "_config", report, false, "ABL_E001_Chase");
AssignReference(chaseAbility, "_contactDamage", bodyContact, report);
AssignReference(chaseAbility, "_sensorHub", sensorHub, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null) AssignReference(contactHitBox, "_defaultSource", dmgSrc, report);
SetupSensorHubSlotNames(sensorHub, new[] { "aggro", "wall_ahead", "ledge" }, report);
report.Add("★ 在 EnemySensorHub Inspector 中绑定 Sensor 子节点(aggro/wall_ahead/ledge)。");
report.Add("★ 挂载行为树 BehaviorTree 组件,指定 E001_CaoZhi.asset。");
Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go;
MarkDirtyAndLog("E001 草蛭", go, report);
}
[MenuItem("BaseGames/Scene/Place/Enemy E002 (簧蛭)", priority = 112)]
public static void PlaceE002_HuangZhi()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place E002 簧蛭");
GameObject go = new GameObject("ENM_HuangZhi");
Undo.RegisterCreatedObjectUndo(go, "Place E002");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Kinematic;
rb.gravityScale = 0f;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(0.5f, 0.7f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SetupSpriteRenderer(go);
EnemyBase enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
// HurtBox(初始禁用,附着天花板时不受伤)
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(0.45f, 0.65f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
hurtBoxT.gameObject.SetActive(false);
// AttackHitBox(下坠发动时由能力启用)
Transform atkT = GetOrCreateChild(go.transform, "AttackHitBox");
SetLayer(atkT.gameObject, "EnemyHitBox", report);
BoxCollider2D atkCol = GetOrAddComponent(atkT.gameObject);
atkCol.isTrigger = true;
atkCol.size = new Vector2(0.5f, 0.5f);
HitBox atkHitBox = GetOrAddComponent(atkT.gameObject);
atkT.gameObject.SetActive(false);
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform strikeT = GetOrCreateChild(abilitiesT, "CeilingHangStrikeAbility");
CeilingHangStrikeAbility strikeAbility = GetOrAddComponent(strikeT.gameObject);
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(enemyBase, "_statsSO", report, false, "ENM_E002_Stats");
AssignAsset(enemyBase, "_animConfig", report, false, "ENM_E002_AnimConfig");
AssignReference(enemyBase, "_stats", enemyStats, report);
AssignReference(enemyBase, "_animancer", animancer, report);
AssignReference(enemyBase, "_hurtBox", hurtBox, report);
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(strikeAbility, "_config", report, false, "ABL_E002_Strike");
AssignReference(strikeAbility, "_attackHitBox", atkHitBox, report);
AssignReference(strikeAbility, "_hurtBox", hurtBox, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null) AssignReference(atkHitBox, "_defaultSource", dmgSrc, report);
SetupSensorHubSlotNames(sensorHub, new[] { "attack_range" }, report);
report.Add("★ 将此对象放置于天花板,调整位置使 CapsuleCollider 正好贴合天花板底面。");
report.Add("★ 挂载行为树 BehaviorTree 组件,指定 E002_HuangZhi.asset。");
Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go;
MarkDirtyAndLog("E002 簧蛭", go, report);
}
[MenuItem("BaseGames/Scene/Place/Enemy E003 (幼蛭)", priority = 113)]
public static void PlaceE003_YouZhi_Enemy()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place E003 幼蛭");
GameObject go = new GameObject("ENM_YouZhi");
Undo.RegisterCreatedObjectUndo(go, "Place E003");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Kinematic;
rb.gravityScale = 0f;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(0.5f, 0.6f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SpriteRenderer sr3 = SetupSpriteRenderer(go);
E003_YouZhi enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
EnemyMovement movement = GetOrAddComponent(go);
GetOrAddComponent(go);
GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(0.45f, 0.55f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
Transform contactT = GetOrCreateChild(go.transform, "ContactDamageZone");
SetLayer(contactT.gameObject, "EnemyHitBox", report);
CircleCollider2D contactCol = GetOrAddComponent(contactT.gameObject);
contactCol.isTrigger = true;
contactCol.radius = 0.35f;
HitBox contactHitBox = GetOrAddComponent(contactT.gameObject);
BodyContactDamage bodyContact = GetOrAddComponent(contactT.gameObject);
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform fallT = GetOrCreateChild(abilitiesT, "AnimatedCeilingDropAbility");
AnimatedCeilingDropAbility fallAbility = GetOrAddComponent(fallT.gameObject);
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(enemyBase, "_statsSO", report, false, "ENM_E003_Stats");
AssignAsset(enemyBase, "_animConfig", report, false, "ENM_E003_AnimConfig");
AssignReference(enemyBase, "_stats", enemyStats, report);
AssignReference(enemyBase, "_movement", movement, report);
AssignReference(enemyBase, "_animancer", animancer, report);
AssignReference(enemyBase, "_hurtBox", hurtBox, report);
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(movement, "_config", report, false, "ENM_E003_Stats");
AssignAsset(movement, "_animConfig", report, false, "ENM_E003_AnimConfig");
AssignReference(movement, "_animancer", animancer, report);
AssignReference(movement, "_spriteRenderer", sr3, report);
AssignAsset(fallAbility, "_config", report, false, "ABL_E003_Fall");
AssignReference(fallAbility, "_contactDamage", bodyContact, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null) AssignReference(contactHitBox, "_defaultSource", dmgSrc, report);
SetupSensorHubSlotNames(sensorHub, new[] { "aggro" }, report);
report.Add("★ 将此对象放置于天花板下方,E003_YouZhi 会在 OnSpawn/ActivateFromCeiling 时执行下坠。");
report.Add("★ 挂载行为树 BehaviorTree 组件,指定 E003_YouZhi.asset。");
Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go;
MarkDirtyAndLog("E003 幼蛭", go, report);
}
[MenuItem("BaseGames/Scene/Place/Enemy E004 (蛭母)", priority = 114)]
public static void PlaceE004_ZhiMu_Enemy()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place E004 蛭母");
GameObject go = new GameObject("ENM_ZhiMu");
Undo.RegisterCreatedObjectUndo(go, "Place E004");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(0.8f, 1.2f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SpriteRenderer sr4 = SetupSpriteRenderer(go);
E004_ZhiMu enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
EnemyFeedback feedback = GetOrAddComponent(go);
EnemyMovement movement = GetOrAddComponent(go);
GetOrAddComponent(go);
GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(0.75f, 1.1f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
Transform biteT = GetOrCreateChild(go.transform, "BiteHitBox");
SetLayer(biteT.gameObject, "EnemyHitBox", report);
BoxCollider2D biteCol = GetOrAddComponent(biteT.gameObject);
biteCol.isTrigger = true;
biteCol.size = new Vector2(0.6f, 0.4f);
HitBox biteHitBox = GetOrAddComponent(biteT.gameObject);
biteT.gameObject.SetActive(false);
Transform slamT = GetOrCreateChild(go.transform, "SlamHitBox");
SetLayer(slamT.gameObject, "EnemyHitBox", report);
CircleCollider2D slamCol = GetOrAddComponent(slamT.gameObject);
slamCol.isTrigger = true;
slamCol.radius = 0.7f;
HitBox slamHitBox = GetOrAddComponent(slamT.gameObject);
slamT.gameObject.SetActive(false);
Transform chargeHitBoxT = GetOrCreateChild(go.transform, "ChargeHitBox");
SetLayer(chargeHitBoxT.gameObject, "EnemyHitBox", report);
BoxCollider2D chargeHitCol = GetOrAddComponent(chargeHitBoxT.gameObject);
chargeHitCol.isTrigger = true;
chargeHitCol.size = new Vector2(0.9f, 0.8f);
HitBox chargeHitBox = GetOrAddComponent(chargeHitBoxT.gameObject);
chargeHitBoxT.gameObject.SetActive(false);
Transform acidMuzzleT = GetOrCreateChild(go.transform, "AcidMuzzle");
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform biteAblT = GetOrCreateChild(abilitiesT, "MeleeAttackAbility_Bite");
MeleeAttackAbility biteAbl = GetOrAddComponent(biteAblT.gameObject);
Transform slamAblT = GetOrCreateChild(abilitiesT, "RepeatSlamAbility");
RepeatSlamAbility slamAbl = GetOrAddComponent(slamAblT.gameObject);
Transform acidAblT = GetOrCreateChild(abilitiesT, "ProjectileAttackAbility_Acid");
ProjectileAttackAbility acidAbl = GetOrAddComponent(acidAblT.gameObject);
Transform chargeAblT = GetOrCreateChild(abilitiesT, "ChargeAbility");
ChargeAbility chargeAbl = GetOrAddComponent(chargeAblT.gameObject);
Transform chaseAblT = GetOrCreateChild(abilitiesT, "ContactChaseAbility");
ContactChaseAbility chaseAbl = GetOrAddComponent(chaseAblT.gameObject);
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(enemyBase, "_statsSO", report, false, "ENM_E004_Stats");
AssignAsset(enemyBase, "_animConfig", report, false, "ENM_E004_AnimConfig");
AssignReference(enemyBase, "_stats", enemyStats, report);
AssignReference(enemyBase, "_movement", movement, report);
AssignReference(enemyBase, "_animancer", animancer, report);
AssignReference(enemyBase, "_feedback", feedback, report);
AssignReference(enemyBase, "_hurtBox", hurtBox, report);
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(movement, "_config", report, false, "ENM_E004_Stats");
AssignAsset(movement, "_animConfig", report, false, "ENM_E004_AnimConfig");
AssignReference(movement, "_animancer", animancer, report);
AssignReference(movement, "_spriteRenderer", sr4, report);
AssignAsset(biteAbl, "_config", report, false, "ABL_E004_Bite");
AssignAsset(slamAbl, "_config", report, false, "ABL_E004_Slam");
AssignAsset(acidAbl, "_config", report, false, "ABL_E004_Acid");
AssignAsset(chargeAbl, "_config", report, false, "ABL_E004_Charge");
AssignAsset(chaseAbl, "_config", report, false, "ABL_E004_Chase");
AssignMeleeHitBoxSlots(biteAbl, new[] { ("bite", biteHitBox) }, report);
AssignReference(slamAbl, "_hitBox", slamHitBox, report);
AssignReference(acidAbl, "_muzzle", acidMuzzleT, report);
AssignReference(chargeAbl, "_chargeHitBox", chargeHitBox, report);
AssignReference(chaseAbl, "_sensorHub", sensorHub, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null)
{
AssignReference(biteHitBox, "_defaultSource", dmgSrc, report);
AssignReference(slamHitBox, "_defaultSource", dmgSrc, report);
AssignReference(chargeHitBox, "_defaultSource", dmgSrc, report);
}
SetupSensorHubSlotNames(sensorHub, new[] { "aggro", "los" }, report);
report.Add("★ 挂载行为树 BehaviorTree 组件,指定 E004_ZhiMu.asset。");
Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go;
MarkDirtyAndLog("E004 蛭母", go, report);
}
[MenuItem("BaseGames/Scene/Place/Enemy E005 (肥蛭)", priority = 115)]
public static void PlaceE005_FeiZhi_Enemy()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place E005 肥蛭");
GameObject go = new GameObject("ENM_FeiZhi");
Undo.RegisterCreatedObjectUndo(go, "Place E005");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(0.9f, 1.0f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SpriteRenderer sr5 = SetupSpriteRenderer(go);
E005_FeiZhi enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
EnemyMovement movement = GetOrAddComponent(go);
GetOrAddComponent(go);
GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(0.85f, 0.95f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
Transform biteT = GetOrCreateChild(go.transform, "BiteHitBox");
SetLayer(biteT.gameObject, "EnemyHitBox", report);
BoxCollider2D biteCol = GetOrAddComponent(biteT.gameObject);
biteCol.isTrigger = true;
biteCol.size = new Vector2(0.7f, 0.45f);
HitBox biteHitBox = GetOrAddComponent(biteT.gameObject);
biteT.gameObject.SetActive(false);
Transform acidMuzzleT = GetOrCreateChild(go.transform, "AcidMuzzle");
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform biteAblT = GetOrCreateChild(abilitiesT, "MeleeAttackAbility_Bite");
MeleeAttackAbility biteAbl = GetOrAddComponent(biteAblT.gameObject);
Transform acidAblT = GetOrCreateChild(abilitiesT, "ProjectileAttackAbility_Acid");
ProjectileAttackAbility acidAbl = GetOrAddComponent(acidAblT.gameObject);
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(enemyBase, "_statsSO", report, false, "ENM_E005_Stats");
AssignAsset(enemyBase, "_animConfig", report, false, "ENM_E005_AnimConfig");
AssignReference(enemyBase, "_stats", enemyStats, report);
AssignReference(enemyBase, "_movement", movement, report);
AssignReference(enemyBase, "_animancer", animancer, report);
AssignReference(enemyBase, "_hurtBox", hurtBox, report);
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(movement, "_config", report, false, "ENM_E005_Stats");
AssignAsset(movement, "_animConfig", report, false, "ENM_E005_AnimConfig");
AssignReference(movement, "_animancer", animancer, report);
AssignReference(movement, "_spriteRenderer", sr5, report);
AssignAsset(biteAbl, "_config", report, false, "ABL_E005_Bite");
AssignAsset(acidAbl, "_config", report, false, "ABL_E005_Acid");
AssignMeleeHitBoxSlots(biteAbl, new[] { ("bite", biteHitBox) }, report);
AssignReference(acidAbl, "_muzzle", acidMuzzleT, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null) AssignReference(biteHitBox, "_defaultSource", dmgSrc, report);
SetupSensorHubSlotNames(sensorHub, new[] { "aggro" }, report);
report.Add("★ 在 E005_FeiZhi._deathPreClip 上添加 AnimationEvent 调用 SpawnProjectile(\"spawn_e003\")。");
report.Add("★ 挂载行为树 BehaviorTree 组件,指定 E005_FeiZhi.asset。");
Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go;
MarkDirtyAndLog("E005 肥蛭", go, report);
}
[MenuItem("BaseGames/Scene/Place/Enemy E006 (讙)", priority = 116)]
public static void PlaceE006_Huan()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place E006 讙");
GameObject go = new GameObject("ENM_Huan");
Undo.RegisterCreatedObjectUndo(go, "Place E006");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(0.7f, 1.0f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SpriteRenderer sr6 = SetupSpriteRenderer(go);
EnemyBase enemyBase = GetOrAddComponent(go);
EnemyStats enemyStats = GetOrAddComponent(go);
EnemyMovement movement = GetOrAddComponent(go);
GetOrAddComponent(go);
GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(0.65f, 0.95f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
Transform contactT = GetOrCreateChild(go.transform, "ContactDamageZone");
SetLayer(contactT.gameObject, "EnemyHitBox", report);
CircleCollider2D contactCol = GetOrAddComponent(contactT.gameObject);
contactCol.isTrigger = true;
contactCol.radius = 0.4f;
HitBox contactHitBox = GetOrAddComponent(contactT.gameObject);
BodyContactDamage bodyContact = GetOrAddComponent(contactT.gameObject);
Transform landT = GetOrCreateChild(go.transform, "LandingHitBox");
SetLayer(landT.gameObject, "EnemyHitBox", report);
CircleCollider2D landCol = GetOrAddComponent(landT.gameObject);
landCol.isTrigger = true;
landCol.radius = 0.8f;
HitBox landHitBox = GetOrAddComponent(landT.gameObject);
landT.gameObject.SetActive(false);
Transform abilitiesT = GetOrCreateChild(go.transform, "Abilities");
Transform leapT = GetOrCreateChild(abilitiesT, "LeapAttackAbility");
LeapAttackAbility leapAbl = GetOrAddComponent(leapT.gameObject);
Transform chaseT = GetOrCreateChild(abilitiesT, "ContactChaseAbility");
ContactChaseAbility chaseAbl = GetOrAddComponent(chaseT.gameObject);
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(enemyBase, "_statsSO", report, false, "ENM_E006_Stats");
AssignAsset(enemyBase, "_animConfig", report, false, "ENM_E006_AnimConfig");
AssignReference(enemyBase, "_stats", enemyStats, report);
AssignReference(enemyBase, "_movement", movement, report);
AssignReference(enemyBase, "_animancer", animancer, report);
AssignReference(enemyBase, "_hurtBox", hurtBox, report);
AssignAsset(enemyBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(enemyBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(enemyStats, "_onDifficultyChanged", report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(movement, "_config", report, false, "ENM_E006_Stats");
AssignAsset(movement, "_animConfig", report, false, "ENM_E006_AnimConfig");
AssignReference(movement, "_animancer", animancer, report);
AssignReference(movement, "_spriteRenderer", sr6, report);
AssignAsset(leapAbl, "_config", report, false, "ABL_E006_Leap");
AssignAsset(chaseAbl, "_config", report, false, "ABL_E006_Chase");
AssignReference(leapAbl, "_landingHitBox", landHitBox, report);
AssignReference(chaseAbl, "_contactDamage", bodyContact, report);
AssignReference(chaseAbl, "_sensorHub", sensorHub, report);
Object dmgSrc = FindFirstAsset("CMB_DS_EnemyBody");
if (dmgSrc != null)
{
AssignReference(contactHitBox, "_defaultSource", dmgSrc, report);
AssignReference(landHitBox, "_defaultSource", dmgSrc, report);
}
SetupSensorHubSlotNames(sensorHub, new[] { "aggro", "wall_ahead", "ledge" }, report);
report.Add("★ 挂载行为树 BehaviorTree 组件,指定 E006_Huan.asset。");
Undo.CollapseUndoOperations(undoGroup);
Selection.activeGameObject = go;
MarkDirtyAndLog("E006 讙", go, report);
}
[MenuItem("BaseGames/Scene/Place/Boss 嘲风 (ChaoFeng)", priority = 117)]
public static void PlaceChaoFeng()
{
var report = new List();
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Place Boss 嘲风");
GameObject go = new GameObject("ENM_ChaoFeng");
Undo.RegisterCreatedObjectUndo(go, "Place ChaoFeng");
go.transform.position = GetDropPosition();
SetLayer(go, "Enemy", report);
Rigidbody2D rb = GetOrAddComponent(go);
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 2f;
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
rb.interpolation = RigidbodyInterpolation2D.Interpolate;
CapsuleCollider2D body = GetOrAddComponent(go);
body.size = new Vector2(1.2f, 2.0f);
GetOrAddComponent(go);
AnimancerComponent animancer = GetOrAddComponent(go);
SpriteRenderer srBoss = SetupSpriteRenderer(go);
ChaoFengBoss bossBase = GetOrAddComponent(go);
EnemyStats bossStats = GetOrAddComponent(go);
EnemyFeedback feedback = GetOrAddComponent(go);
EnemyMovement movement = GetOrAddComponent(go);
GetOrAddComponent(go);
GetOrAddComponent(go);
BossSkillExecutor skillExec = GetOrAddComponent(go);
ChaoFengFloatController floatCtrl = GetOrAddComponent(go);
ChaoFengKnockdownCounter knockdown = GetOrAddComponent(go);
EnemySensorHub sensorHub = GetOrAddComponent(go);
// HurtBox
Transform hurtBoxT = GetOrCreateChild(go.transform, "HurtBox");
SetLayer(hurtBoxT.gameObject, "EnemyHurtBox", report);
CapsuleCollider2D hurtCap = GetOrAddComponent(hurtBoxT.gameObject);
hurtCap.isTrigger = true;
hurtCap.size = new Vector2(1.1f, 1.9f);
HurtBox hurtBox = GetOrAddComponent(hurtBoxT.gameObject);
// Phase1 attack hitboxes (disabled by default; abilities enable/disable as needed)
HitBox biteHB = CreateDisabledHitBox(go.transform, "Phase1_BiteHitBox", "EnemyHitBox",
true, report, size: new Vector2(0.8f, 0.5f));
HitBox swipeR = CreateDisabledHitBox(go.transform, "Phase1_SwipeHitBox_R","EnemyHitBox",
true, report, size: new Vector2(1.2f, 0.4f));
HitBox swipeL = CreateDisabledHitBox(go.transform, "Phase1_SwipeHitBox_L","EnemyHitBox",
true, report, size: new Vector2(1.2f, 0.4f));
HitBox stompHB = CreateDisabledHitBox(go.transform, "Phase1_StompHitBox", "EnemyHitBox",
false, report, radius: 1.0f);
// Muzzle transforms for Phase 2 skills
GetOrCreateChild(go.transform, "WindBladeMuzzle");
GetOrCreateChild(go.transform, "TornadoMuzzle");
GetOrCreateChild(go.transform, "SummonSpawnPoint");
// SOs — assign first so OnValidate doesn't warn during wiring
AssignAsset(bossBase, "_statsSO", report, false, "ENM_ChaoFeng_Stats");
AssignAsset(bossBase, "_animConfig", report, false, "ENM_ChaoFeng_AnimConfig");
// Component wiring
AssignReference(bossBase, "_stats", bossStats, report);
AssignReference(bossBase, "_movement", movement, report);
AssignReference(bossBase, "_animancer", animancer, report);
AssignReference(bossBase, "_feedback", feedback, report);
AssignReference(bossBase, "_hurtBox", hurtBox, report);
AssignReference(skillExec, "_animancer", animancer, report);
AssignAsset(bossBase, "_onEnemyDied", report, false, "EVT_EnemyDied");
AssignAsset(bossBase, "_onPlayerSpawned", report, false, "EVT_PlayerSpawned");
AssignAsset(bossBase, "_onBossFightEnded", report, false, "EVT_BossFightEnded");
AssignAsset(bossBase, "_onBossPhaseChanged", report, false, "EVT_BossPhaseChanged");
AssignAsset(bossStats, "_onDifficultyChanged",report, false, "EVT_DifficultyChanged");
AssignAsset(hurtBox, "_onDamageDealt", report, false, "EVT_DamageDealt");
AssignAsset(hurtBox, "_onHitConfirmed", report, false, "EVT_HitConfirmed");
AssignAsset(movement, "_config", report, false, "ENM_ChaoFeng_Stats");
AssignAsset(movement, "_animConfig", report, false, "ENM_ChaoFeng_AnimConfig");
AssignReference(movement, "_animancer", animancer, report);
AssignReference(movement, "_spriteRenderer", srBoss, report);
// Collect BossSkillSOs and assign to executor
var skillAssets = new System.Collections.Generic.List