2.5d test

This commit is contained in:
2026-09-27 21:08:05 +09:00
parent 84dd12414a
commit abd2da2057
201 changed files with 24297 additions and 295 deletions
+39 -16
View File
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using BumpCombat.Constants;
using BumpCombat.Defense;
using BumpCombat.Player;
using UnityEngine;
@@ -25,6 +26,8 @@ namespace BumpCombat.Enemies
private float necrofireRayLength;
private bool meleeAttackActive;
private bool meleeHitConfirmed;
private bool defenseSummonHitConfirmed;
private bool checkingDefenseSummonGeometry;
private StageEnemyEffectVisual necrofireRayVisual;
private readonly List<StageEnemyEffectVisual> stageAoeVisuals = new();
private readonly List<AttackWarningVisual> stageMultiWarnings = new();
@@ -202,6 +205,7 @@ namespace BumpCombat.Enemies
attackOrigin = transform.position;
meleeAttackActive = false;
meleeHitConfirmed = false;
defenseSummonHitConfirmed = false;
if (IsLancerAttack)
{
EnsureLancerVisual();
@@ -440,6 +444,7 @@ namespace BumpCombat.Enemies
public void TickActive(float activeElapsed)
{
TryApplyDefenseSummonHit(activeElapsed);
if (necrofireRayActive)
{
TickNecrofireRay();
@@ -495,6 +500,7 @@ namespace BumpCombat.Enemies
necrofireRayHitConfirmed = false;
meleeAttackActive = false;
meleeHitConfirmed = false;
defenseSummonHitConfirmed = false;
if (necrofireRayVisual != null)
{
Destroy(necrofireRayVisual.gameObject);
@@ -796,6 +802,28 @@ namespace BumpCombat.Enemies
|| controller.AttackShape == EnemyAttackShape.Cone;
}
private void TryApplyDefenseSummonHit(float activeElapsed)
{
if (defenseSummonHitConfirmed || !controller.CurrentAttackPattern.IsContactWindowActive(activeElapsed, controller.ActiveDuration)) return;
DefenseSummonUnit target = controller.DefenseCombatTarget;
if (target == null || target.BodyCollider == null) return;
bool hit;
if (controller.AttackShape == EnemyAttackShape.Body)
{
ColliderDistance2D distance = controller.BodyCollider.Distance(target.BodyCollider);
hit = distance.isOverlapped || distance.distance <= 0.03f;
}
else
{
checkingDefenseSummonGeometry = true;
hit = ContainsPoint(target.BodyCollider.bounds.center);
checkingDefenseSummonGeometry = false;
}
if (!hit) return;
Vector2 direction = (Vector2)target.transform.position - (Vector2)transform.position;
if (direction.sqrMagnitude <= 0.0001f) direction = lockedDirection;
defenseSummonHitConfirmed = target.TryTakeDamage(controller.AttackDamage, direction.normalized);
}
private void TryApplyMeleeHit(float activeElapsed)
{
if (!meleeAttackActive || meleeHitConfirmed)
@@ -943,31 +971,26 @@ namespace BumpCombat.Enemies
private bool ContainsPlayerCollider(Collider2D[] hits)
{
Collider2D playerCollider = GetPlayerCollider();
if (playerCollider == null || hits == null)
{
return false;
}
Collider2D targetCollider = GetPlayerCollider();
if (targetCollider == null || hits == null) return false;
for (int i = 0; i < hits.Length; i++)
{
if (hits[i] == playerCollider
|| hits[i].GetComponentInParent<PlayerHealth>() == playerHealth)
{
return true;
}
if (hits[i] == targetCollider) return true;
if (!checkingDefenseSummonGeometry
&& hits[i].GetComponentInParent<PlayerHealth>() == playerHealth) return true;
}
return false;
}
private Collider2D GetPlayerCollider()
{
return playerHealth != null
? playerHealth.GetComponent<Collider2D>()
: null;
if (checkingDefenseSummonGeometry)
{
DefenseSummonUnit target = controller != null ? controller.DefenseCombatTarget : null;
return target != null ? target.BodyCollider : null;
}
return playerHealth != null ? playerHealth.GetComponent<Collider2D>() : null;
}
private void TickNecrofireRay()
{
if (!necrofireRayActive || necrofireRayHitConfirmed)
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using BumpCombat.Combat;
using BumpCombat.Constants;
using BumpCombat.Core;
using BumpCombat.Defense;
using BumpCombat.Player;
using BumpCombat.Progression;
using BumpCombat.Stage;
@@ -70,6 +71,7 @@ namespace BumpCombat.Enemies
private static readonly int HurtParameter = Animator.StringToHash("Hurt");
private static readonly int DeathState = Animator.StringToHash("Base Layer.Death");
private static readonly Collider2D[] SeparationBuffer = new Collider2D[16];
private static readonly RaycastHit2D[] RouteCollisionBuffer = new RaycastHit2D[16];
private static readonly Dictionary<int, HashSet<EnemyController>> EncounterGroups = new();
private static Sprite launchWhiteSprite;
private static int nextSpawnLifetimeIdentity;
@@ -94,6 +96,7 @@ namespace BumpCombat.Enemies
private ActiveArtifactController activeArtifactController;
private Transform player;
private PlayerHealth playerHealth;
private DefenseSummonUnit defenseCombatTarget;
private Vector2 lockedDirection;
private Vector2 capturedTarget;
private Vector2 knockbackVelocity;
@@ -112,6 +115,7 @@ namespace BumpCombat.Enemies
private float vulnerabilityIncrease;
private bool ownsAttackToken;
private bool registeredAlive;
private bool suppressTowerTestDummyRewards;
private float nextSeparationRefreshTime;
private Vector2 cachedSeparation;
private RunTimedEvent? runEventRole;
@@ -259,6 +263,7 @@ namespace BumpCombat.Enemies
public EnemyController SummonOwner => summonOwner;
public bool IsProtectedFromCleanup => runEventRole.HasValue || phaseSummon;
public bool HasContactDamage => definition != null && definition.HasContactDamage;
public DefenseSummonUnit DefenseCombatTarget => defenseCombatTarget != null && defenseCombatTarget.IsOperational ? defenseCombatTarget : null;
public bool IsFlying => definition != null && definition.IsFlying;
public Collider2D BodyCollider => bodyCollider != null ? bodyCollider : (bodyCollider = GetComponent<Collider2D>());
public bool HasDetectedPlayer => hasDetectedPlayer;
@@ -405,6 +410,16 @@ namespace BumpCombat.Enemies
}
#endif
public void ConfigureTowerTestDummy()
{
suppressTowerTestDummyRewards = true;
if (body != null)
{
body.linearVelocity = Vector2.zero;
body.constraints = RigidbodyConstraints2D.FreezeAll;
}
}
public void ConfigureEncounterGroup(int groupId, Vector2 anchor)
{
UnregisterEncounterGroup();
@@ -1588,9 +1603,29 @@ namespace BumpCombat.Enemies
return false;
}
Vector2 destination = ClampEnemyPosition(position);
Vector2 delta = destination - body.position;
if (DefensePrototypeController.Instance != null && DefensePrototypeController.Instance.IsActive
&& delta.sqrMagnitude > 0.000001f)
{
ContactFilter2D filter = ContactFilter2D.noFilter;
filter.useTriggers = false;
int hitCount = bodyCollider.Cast(delta.normalized, filter,
RouteCollisionBuffer, delta.magnitude);
for (int i = 0; i < hitCount; i++)
{
Collider2D hit = RouteCollisionBuffer[i].collider;
DefenseSummonUnit summon = hit != null
? hit.GetComponentInParent<DefenseSummonUnit>() : null;
if (summon == null || !summon.IsOperational) continue;
if (RouteCollisionBuffer[i].distance <= 0.001f
&& Vector2.Dot(delta.normalized, body.position - (Vector2)hit.bounds.center) >= 0f) continue;
return false;
}
}
knockbackVelocity = Vector2.zero;
body.linearVelocity = Vector2.zero;
body.position = ClampEnemyPosition(position);
body.position = destination;
transform.position = body.position;
return true;
}
@@ -1793,7 +1828,7 @@ namespace BumpCombat.Enemies
SetFacing(direction);
float repeatWarning = WarningDuration
* runEventTuning.RepeatWarningDurationMultiplier;
BeginAttackWarning(direction, player.position, repeatWarning);
BeginAttackWarning(direction, attackTarget, repeatWarning);
}
private void BeginAttackWarning(
@@ -1953,7 +1988,7 @@ namespace BumpCombat.Enemies
CurrentAttackPatternIndex);
completedAttacksInSequence = 0;
remainingBackHitsToInterrupt = BackHitsToInterrupt;
BeginAttackWarning(direction, player.position, WarningDuration);
BeginAttackWarning(direction, attackTarget, WarningDuration);
return;
}
@@ -2181,6 +2216,11 @@ namespace BumpCombat.Enemies
private Vector2 GetAttackTargetPosition()
{
DefenseSummonUnit summon = DefenseCombatTarget;
if (summon != null && summon.BodyCollider != null)
{
return summon.BodyCollider.bounds.center;
}
if (UsesHorizontalRootAttackVisual && playerHealth != null)
{
Collider2D targetCollider =
@@ -2201,20 +2241,18 @@ namespace BumpCombat.Enemies
return true;
}
float targetMinY = player.position.y;
float targetMaxY = player.position.y;
if (playerHealth != null)
Collider2D targetCollider = DefenseCombatTarget != null
? DefenseCombatTarget.BodyCollider
: playerHealth != null ? playerHealth.GetComponent<Collider2D>() : null;
float targetMinY = DefenseCombatTarget != null
? DefenseCombatTarget.transform.position.y : player.position.y;
float targetMaxY = targetMinY;
if (targetCollider != null)
{
Collider2D targetCollider =
playerHealth.GetComponent<Collider2D>();
if (targetCollider != null)
{
Bounds targetBounds = targetCollider.bounds;
targetMinY = targetBounds.min.y;
targetMaxY = targetBounds.max.y;
}
Bounds targetBounds = targetCollider.bounds;
targetMinY = targetBounds.min.y;
targetMaxY = targetBounds.max.y;
}
// Match the same world-space horizontal attack band used by
// EnemyAttack's OverlapBox. Collider bounds include the player
// foot offset, so alignment does not infer the body from its
@@ -2318,6 +2356,10 @@ namespace BumpCombat.Enemies
|| (state == EnemyState.Warning && attackAnimationStarted));
}
public void SetDefenseCombatTarget(DefenseSummonUnit target)
{
defenseCombatTarget = target != null && target.IsOperational ? target : null;
}
public void Alert()
{
MarkPlayerDetected(false);
@@ -2390,7 +2432,7 @@ namespace BumpCombat.Enemies
private bool RefreshChaseAwareness()
{
if (IsEventEnemy)
if (IsEventEnemy || DefenseCombatTarget != null)
{
return true;
}
@@ -2930,16 +2972,19 @@ namespace BumpCombat.Enemies
animator.Play(DeathState, 0, 0f);
}
if (experienceOrbPrefab != null)
if (!suppressTowerTestDummyRewards)
{
ExperienceOrb orb = Instantiate(experienceOrbPrefab, transform.position, Quaternion.identity);
orb.Initialize(
experienceValueOverride >= 0
? experienceValueOverride
: definition.ExperienceValue);
}
if (experienceOrbPrefab != null)
{
ExperienceOrb orb = Instantiate(experienceOrbPrefab, transform.position, Quaternion.identity);
orb.Initialize(
experienceValueOverride >= 0
? experienceValueOverride
: definition.ExperienceValue);
}
CombatEvents.RaiseEnemyDied(gameObject);
CombatEvents.RaiseEnemyDied(gameObject);
}
OnDied?.Invoke(this);
Destroy(
gameObject,