Initial commit: Tiny Tackle Heroes Unity project

This commit is contained in:
2026-09-15 17:37:42 +09:00
commit 591fa9a826
1623 changed files with 182420 additions and 0 deletions
@@ -0,0 +1,32 @@
using BumpCombat.Player;
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Artifacts")]
public sealed class ArtifactConstants : ScriptableObject
{
[Header("공유 게이지")]
[Min(1f)] public float MaximumGauge = 100f;
[Min(0f)] public float StartingGauge;
[Min(0f)] public float NormalBumpGaugeGain = 4f;
[Min(0f)] public float MovementGaugeGainPerSecond = 2f;
[Header("보유 제한")]
[Tooltip("플레이어가 보유할 수 있는 아티팩트 총량. 초기 선택 수도 남은 슬롯에 맞춰 자동으로 줄어듭니다."), Min(1)]
public int MaximumOwnedArtifacts = 3;
[Min(1)] public int MaximumArtifactsPerColor = 1;
[Header("공통 효과")]
[Tooltip("Pulse Ring 기본기의 기절시간 (초)"), Min(0f)] public float PulseNormalStunDuration = 0.3f;
[Tooltip("Pulse Ring 충전기의 기절시간 (초)"), Min(0f)] public float PulseChargedStunDuration = 0.6f;
[Tooltip("Pulse Ring 경계에서 안전하게 멈추는 거리 (유닛)"), Min(0f)] public float PulseBoundaryClearance = 0.1f;
[Tooltip("Chain Lightning 투사체 가속 배율"), Min(0f)] public float ProjectileAccelerationStrength = 2f;
[Tooltip("대시 아티팩트 기본기 이동 지속시간 (초)"), Min(0.01f)] public float DashDuration = 0.1f;
[Tooltip("대시 아티팩트 충전기 이동 지속시간 (초)"), Min(0.01f)] public float ChargedDashDuration = 0.14f;
[Header("아티팩트 정의")]
[Tooltip("실행 가능 아티팩트의 정의 에셋")]
public ActiveArtifactDefinition[] Definitions = System.Array.Empty<ActiveArtifactDefinition>();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d182399864ec4f88a905bcdf5dd248c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Combat")]
public sealed class CombatConstants : ScriptableObject
{
[Header("몸통박치기 방향 판정")]
[Tooltip("적 정면으로 분류하는 방향 내적 기준"), Range(-1f, 1f)] public float FrontThreshold = 0.707f;
[Tooltip("적 후방으로 분류하는 방향 내적 기준"), Range(-1f, 1f)] public float BackThreshold = -0.5f;
[Tooltip("측면 또는 정면 비껴치기 피해 배율"), Min(0f)] public float PositionDamageMultiplier = 1.2f;
[Tooltip("후방 몸통박치기 피해 배율"), Min(0f)] public float BackDamageMultiplier = 1.5f;
[Header("몸통박치기 피해")]
[Min(0f)] public float BaseDamage = 10f;
[Tooltip("대시 아티팩트 정의가 피해량을 지정하지 않을 때 사용하는 대체 피해량"), Min(0f)]
public float DashDamage = 20f;
[Tooltip("충전 대시 아티팩트 정의가 피해량을 지정하지 않을 때 사용하는 대체 피해량"), Min(0f)]
public float StrongDashDamage = 30f;
[Min(0f)] public float MinimumAttackSpeed = 0.5f;
[Range(-1f, 1f)] public float MinimumApproachDot = 0.25f;
[Header("넉백과 반동")]
[Min(0f)] public float KnockbackForce = 1f;
[Min(0f)] public float DashKnockbackForce = 3f;
[Min(0f)] public float StrongDashKnockbackForce = 4f;
[Min(0f)] public float StrongDashLaunchHeight = 0.65f;
[Min(0.01f)] public float StrongDashLaunchDuration = 0.32f;
[Min(0f)] public float PlayerImpactRecoilDistance = 0.08f;
[Min(1f)] public float FrontKnockbackMultiplier = 1.5f;
[Min(0f)] public float FrontPlayerImpactRecoilDistance = 0.24f;
[Min(0f)] public float EventBackPlayerImpactRecoilDistance = 0.12f;
[Min(0f)] public float ShieldedPlayerImpactRecoilDistance = 0.75f;
[Header("판정 간격과 범위")]
[Min(0f)] public float MinimumOffsetDistance = 0.16f;
[Min(0f)] public float NormalHitInterval = 0.25f;
[Min(0f)] public float DashContactSkin = 0.02f;
[Min(0f)] public float DashFallbackCastRadius = 0.22f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db153e77cef645c49a74705c9d542ea5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,49 @@
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Combat Feedback")]
public sealed class CombatFeedbackSettings : ScriptableObject
{
[Header("히트스톱 (초)")]
[Min(0f)] public float NormalHitStopDuration = 0.045f;
[Min(0f)] public float BackHitStopDuration = 0.06f;
[Min(0f)] public float DashHitStopDuration = 0.055f;
[Min(0f)] public float StrongDashHitStopDuration = 0.075f;
[Min(0f)] public float DamageHitStopDuration = 0.04f;
[Header("타격·피격 표시 (초)")]
[Min(0f)] public float FlashDuration = 0.07f;
[Min(0f)] public float DamageFlashDuration = 0.12f;
[Min(0f)] public float ImpactDuration = 0.14f;
[Min(0f)] public float SlashDuration = 0.11f;
[Min(0f)] public float AfterimageDuration = 0.16f;
[Min(0f)] public float InvulnerabilityBlinkInterval = 0.06f;
[Range(0f, 1f)] public float InvulnerabilityBlinkAlpha = 0.35f;
[Header("카메라 흔들림")]
[Tooltip("흔들림 지속시간 (초)"), Min(0f)] public float CameraShakeDuration = 0.12f;
[Tooltip("일반 흔들림 크기 (픽셀)"), Min(0f)] public float CameraShakePixels = 3f;
[Tooltip("강한 흔들림 크기 (픽셀)"), Min(0f)] public float HeavyCameraShakePixels = 5f;
[Tooltip("강한 피격으로 표시하는 넉백 거리 (유닛)"), Min(0f)] public float HeavyKnockbackThreshold = 1f;
[Header("무적 적 몸통박치기 표시")]
[Tooltip("유지시간 (초)"), Min(0f)] public float BlockedBumpHoldDuration = 0.08f;
[Tooltip("사라지는 시간 (초)"), Min(0f)] public float BlockedBumpFadeDuration = 0.04f;
[Tooltip("수직 오프셋 (유닛)")] public float BlockedBumpVerticalOffset = 0.35f;
[Header("가드 차단 불꽃")]
[Tooltip("유지시간 (초)"), Min(0f)] public float GuardBlockImpactHoldDuration = 0.08f;
[Tooltip("사라지는 시간 (초)"), Min(0f)] public float GuardBlockImpactFadeDuration = 0.1f;
[Tooltip("최소 생성 간격 (초)"), Min(0f)] public float GuardBlockImpactInterval = 0.08f;
[Tooltip("공격자 방향 오프셋 (유닛)"), Min(0f)] public float GuardBlockImpactOffset = 0.35f;
[Header("동시 표시 제한")]
[Tooltip("무거운 일회성 효과의 최대 동시 개수"), Min(1)] public int MaxConcurrentHeavyEffects = 12;
[Header("회오리 이동 버프 잔상")]
[Tooltip("최소 생성 간격 (초)"), Min(0.001f)] public float CycloneAfterimageInterval = 0.06f;
[Tooltip("잔상 유지시간 (초)"), Min(0f)] public float CycloneAfterimageDuration = 0.22f;
[Tooltip("최대 동시 잔상 수"), Min(1)] public int MaxCycloneAfterimages = 4;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 65792944731040cbae0b4c6ff5fef348
@@ -0,0 +1,60 @@
using BumpCombat.Enemies;
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Enemies")]
public sealed class EnemyConstants : ScriptableObject
{
[Header("행동 판정")]
[Tooltip("원뿔 공격 판정의 방향 내적 기준"), Range(0f, 1f)] public float ConeDirectionThreshold = 0.5f;
[Tooltip("원형 범위 공격의 최소 경고시간 (초)"), Min(0f)] public float MinimumTargetCircleWarningDuration = 1.8f;
[Tooltip("적 분리 방향을 다시 계산하는 간격 (초)"), Min(0f)] public float SeparationRefreshInterval = 0.1f;
[Min(0f)] public float DefaultSeparationRadius = 0.55f;
[Min(0f)] public float DefaultSeparationStrength = 0.6f;
[Tooltip("Animator에 사망 클립이 없을 때의 대체 사망시간 (초)"), Min(0f)] public float DefaultDeathDuration = 0.5f;
[Tooltip("사망 클립 종료 뒤 오브젝트를 정리하기 전 여유시간 (초)"), Min(0f)] public float DeathRemovalPadding = 0.05f;
[Tooltip("몸 공격 상태에서 적이 전진하는 속도 (유닛/초)"), Min(0f)] public float BodyContactSpeed = 3f;
[Tooltip("적 넉백 속도가 줄어드는 비율 (유닛/초)"), Min(0f)] public float KnockbackDeceleration = 8f;
[Tooltip("군중형이 플레이어를 둘러싸며 접근하는 기준점 개수"), Min(1)] public int CrowdApproachSlotCount = 24;
[Tooltip("군중형이 접근 기준점에 배치되는 동심원 개수"), Min(1)] public int CrowdApproachRingCount = 3;
[Tooltip("군중형 접근 기준점의 가장 안쪽 반경 (유닛)"), Min(0f)] public float CrowdApproachBaseRadius = 0.75f;
[Tooltip("군중형 접근 기준점 동심원 간 간격 (유닛)"), Min(0f)] public float CrowdApproachRingSpacing = 0.15f;
[Min(0f)] public float SummonedEnemyNoAnimationLockDuration = 0.15f;
[Header("적과 플레이어 사이 거리")]
[Tooltip("일반 적이 주는 플레이어 넉백 거리 (유닛)"), Min(0f)] public float NormalPlayerKnockbackDistance = 0.75f;
[Tooltip("대형 무기 적이 주는 플레이어 넉백 거리 (유닛)"), Min(0f)] public float HeavyPlayerKnockbackDistance = 1.1f;
[Tooltip("원거리 적 공격 허용 범위 안쪽 여백 (유닛)"), Min(0f)] public float RangedAttackArenaInset = 0.75f;
[Tooltip("화면 안쪽에서 원거리 공격을 시작하기 위한 여백 (유닛)"), Min(0f)] public float RangedAttackCameraInset = 0.5f;
[Tooltip("원거리 적 접근 목표의 전장 가장자리 여백 (유닛)"), Min(0f)] public float RangedApproachArenaInset = 0.8f;
[Tooltip("원거리 적 접근 목표의 화면 여백 (유닛)"), Min(0f)] public float RangedApproachCameraInset = 0.55f;
[Header("엘리트 이상 그로기")]
[Min(0f)] public float EliteGroggyDuration = 5f;
[Min(0f)] public float MidBossGroggyDuration = 7f;
[Min(0f)] public float FinalBossGroggyDuration = 10f;
[Tooltip("엘리트 실드를 깰 때 필요한 일치색 적중 횟수"), Min(1)] public int EliteGroggyRequiredMatchingHits = 1;
[Tooltip("중간보스 실드를 깰 때 필요한 일치색 적중 횟수"), Min(1)] public int MidBossGroggyRequiredMatchingHits = 2;
[Tooltip("최종보스 실드를 깰 때 필요한 일치색 적중 횟수"), Min(1)] public int FinalBossGroggyRequiredMatchingHits = 4;
[Header("네크로맨서")]
[Min(0.5f)] public float GeneralSummonCooldown = 8f;
[Min(0.5f)] public float CrowdSummonCooldown = 12f;
[Min(0.5f)] public float MultiAoeCooldown = 4f;
[Tooltip("소환 예고와 소환 중 행동 잠금시간 (초)"), Min(0.5f)] public float SummonVisualDuration = 0.875f;
[Range(0.01f, 0.2f)] public float DesperationHealthFraction = 0.05f;
[Header("공격 토큰")]
[Min(1)] public int MaxMeleeAttackers = 4;
[Min(1)] public int MaxRangedAttackers = 3;
[Min(1)] public int MaxEarlyCrowdAttackers = 1;
[Min(1)] public int MaxLateCrowdAttackers = 2;
[Min(0f)] public float CrowdAttackExpansionTime = 180f;
[Min(0f)] public float CrowdAttackGrantInterval = 0.9f;
[Header("적 정의")]
[Tooltip("각 적의 능력치·공격 패턴 정의. 적 프리팹은 여기에 있는 에셋을 참조합니다.")]
public EnemyDefinition[] Definitions = System.Array.Empty<EnemyDefinition>();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 049a9353a64f4c50a4c7b9649bee023a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,99 @@
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Gameplay Catalog")]
public sealed class GameplayConstants : ScriptableObject
{
private const string ResourcePath = "GameplayConstants";
private static GameplayConstants current;
private static PlayerConstants fallbackPlayer;
private static CombatConstants fallbackCombat;
private static EnemyConstants fallbackEnemies;
private static ItemConstants fallbackItems;
private static LevelUpConstants fallbackLevelUp;
private static ArtifactConstants fallbackArtifacts;
private static RunConstants fallbackRun;
private static CombatFeedbackSettings fallbackCombatFeedback;
[Header("설정 분류")]
[SerializeField, Tooltip("플레이어 기본 능력치, 피격, 가드 설정")]
private PlayerConstants player;
[SerializeField, Tooltip("몸통박치기 판정과 피해 설정")]
private CombatConstants combat;
[SerializeField, Tooltip("적 행동과 적 공통 설정")]
private EnemyConstants enemies;
[SerializeField, Tooltip("아이템과 경험치 구슬 설정")]
private ItemConstants items;
[SerializeField, Tooltip("경험치, 레벨업, 미션 설정")]
private LevelUpConstants levelUp;
[SerializeField, Tooltip("아티팩트 게이지와 공통 실행 설정")]
private ArtifactConstants artifacts;
[SerializeField, Tooltip("런 시간표, 스폰, 전장 설정")]
private RunConstants run;
[SerializeField, Tooltip("타격, 피격, 가드 효과의 표시 설정")]
private CombatFeedbackSettings combatFeedback;
public static GameplayConstants Current
{
get
{
if (current == null)
{
current = Resources.Load<GameplayConstants>(ResourcePath);
if (current == null)
{
current = CreateInstance<GameplayConstants>();
}
}
return current;
}
}
public PlayerConstants Player => Resolve(player, ref fallbackPlayer);
public CombatConstants Combat => Resolve(combat, ref fallbackCombat);
public EnemyConstants Enemies => Resolve(enemies, ref fallbackEnemies);
public ItemConstants Items => Resolve(items, ref fallbackItems);
public LevelUpConstants LevelUp => Resolve(levelUp, ref fallbackLevelUp);
public ArtifactConstants Artifacts => Resolve(artifacts, ref fallbackArtifacts);
public RunConstants Run => Resolve(run, ref fallbackRun);
public CombatFeedbackSettings CombatFeedback =>
Resolve(combatFeedback, ref fallbackCombatFeedback);
#if UNITY_EDITOR
public static void SetCurrentForTests(GameplayConstants value)
{
current = value;
}
public static void ResetCurrentForTests()
{
current = null;
fallbackPlayer = null;
fallbackCombat = null;
fallbackEnemies = null;
fallbackItems = null;
fallbackLevelUp = null;
fallbackArtifacts = null;
fallbackRun = null;
fallbackCombatFeedback = null;
}
#endif
private static T Resolve<T>(T value, ref T fallback) where T : ScriptableObject
{
if (value != null)
{
return value;
}
if (fallback == null)
{
fallback = CreateInstance<T>();
}
return fallback;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a161f5df6854cfb8b5aad874b423db9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Items")]
public sealed class ItemConstants : ScriptableObject
{
[Header("경험치 구슬")]
[Tooltip("플레이어가 구슬을 습득하는 반경 (유닛)"), Min(0f)] public float ExperienceOrbPickupRadius = 0.35f;
[Tooltip("구슬이 플레이어를 따라오기 시작하는 반경 (유닛)"), Min(0f)] public float ExperienceOrbMagnetRadius = 2f;
[Tooltip("끌려오는 구슬의 이동속도 (유닛/초)"), Min(0f)] public float ExperienceOrbMagnetSpeed = 5f;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: badc477d7b434262b0f032cfc3da0ee6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,73 @@
using System;
using BumpCombat.Progression;
using UnityEngine;
namespace BumpCombat.Constants
{
[Serializable]
public struct MissionTuning
{
[Tooltip("미션 목록에 표시할 이름")]
public string DisplayName;
[Tooltip("목표가 포함된 조건 설명. {0}은 목표 수, {1}은 측정 시간창입니다.")]
public string ConditionFormat;
[Min(1)] public int Target;
[Min(0)] public int ExperienceReward;
public bool RequiresGuardUnlock;
public bool RequiresChargeUnlock;
public MissionTuning(
string displayName,
string conditionFormat,
int target,
int experienceReward,
bool requiresGuardUnlock = false,
bool requiresChargeUnlock = false)
{
DisplayName = displayName;
ConditionFormat = conditionFormat;
Target = target;
ExperienceReward = experienceReward;
RequiresGuardUnlock = requiresGuardUnlock;
RequiresChargeUnlock = requiresChargeUnlock;
}
}
[CreateAssetMenu(menuName = "BumpCombat/Constants/Level Up")]
public sealed class LevelUpConstants : ScriptableObject
{
[Header("경험치")]
[Tooltip("레벨 1에서 다음 레벨까지 필요한 경험치"), Min(1)] public int BaseExperienceToNextLevel = 10;
[Tooltip("레벨이 하나 오를 때마다 늘어나는 필요 경험치"), Min(0)] public int AdditionalExperiencePerLevel = 5;
[Header("레벨업 선택")]
[Tooltip("레벨업 때 표시할 모디파이어 후보 수 (UI 지원 범위: 1~3)"), Range(1, 3)] public int OptionCount = 3;
[Tooltip("레벨업 후보 모디파이어 정의 에셋")]
public ModifierDefinition[] ModifierDefinitions = Array.Empty<ModifierDefinition>();
[Header("미션")]
[Tooltip("파죽지세의 측정 시간창 (전투초)"), Min(0.1f)] public float RampageWindowSeconds = 3f;
[Tooltip("순서: 일반 적 처치, XP 구슬, 범핑, 아티팩트, 후방 범핑, 다중 적중, 공격 취소, 가드, 실드, 강화기, 파죽지세")]
public MissionTuning[] Missions =
{
new("첫 소탕", "소환수가 아닌 일반 몬스터 {0}마리 처치", 100, 10),
new("성장의 발판", "경험치 구슬 {0}개 실제 습득", 50, 10),
new("몸으로 돌파", "실제 피해가 발생한 몸통박치기 {0}회", 60, 10),
new("아티팩트 활용", "서로 다른 아티팩트 발동 {0}회에서 각각 유효 적중", 10, 10),
new("등을 노려라", "서로 다른 적 {0}마리에게 후방 몸통박치기 성공", 15, 15),
new("한 번에 몰아서", "아티팩트 한 번의 발동으로 서로 다른 적 {0}마리 유효 적중", 5, 15),
new("빈틈 차단", "일반 적의 준비/활성 공격을 후방 몸통박치기로 실제 취소 {0}회", 8, 15),
new("정확한 방어", "서로 다른 가드 발동 {0}회에서 실제 공격 차단", 3, 15, true),
new("색을 맞춰라", "맞는 색 아티팩트로 실드의 남은 요구 횟수 실제 차감 {0}회", 6, 15),
new("강화의 순간", "서로 다른 충전 강화기 발동 {0}회에서 각각 유효 적중", 3, 15, false, true),
new("파죽지세", "연속된 {1}전투초 안에 소환수가 아닌 적 {0}마리 처치", 10, 20),
};
public MissionTuning GetMission(int index, MissionTuning fallback)
{
return Missions != null && index >= 0 && index < Missions.Length
? Missions[index]
: fallback;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 031fdbfbffe244b5aae9675d923637dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,40 @@
using UnityEngine;
namespace BumpCombat.Constants
{
[CreateAssetMenu(menuName = "BumpCombat/Constants/Player")]
public sealed class PlayerConstants : ScriptableObject
{
[Header("기본 능력치")]
[Tooltip("플레이어 기본 이동 속도 (유닛/초)"), Min(0f)] public float BaseMoveSpeed = 3f;
[Tooltip("플레이어 기본 최대 체력"), Min(1f)] public float BaseMaxHealth = 100f;
[Header("피격")]
[Tooltip("피격 뒤 무적시간 (초)"), Min(0f)] public float InvulnerabilityDuration = 1f;
[Tooltip("피격 넉백 지속시간 (초)"), Min(0f)] public float KnockbackDuration = 0.14f;
[Tooltip("Hurt 이동 잠금시간 (초)"), Min(0f)] public float HurtMovementLockDuration = 0.5f;
[Tooltip("Hurt 종료 뒤 무적에 더하는 여유시간 (초)"), Min(0f)] public float HurtRecoveryPadding = 0.5f;
[Header("연속 처치 이속 보너스")]
[Tooltip("연속 처치 인정 시간창 (게임 초)"), Min(0f)]
public float PressureStreakWindowSeconds = 3f;
[Tooltip("보너스 발동에 필요한 몸통박치기 처치 수"), Min(1)]
public int PressureStreakKillTarget = 3;
[Tooltip("연속 처치 보너스 이동속도 증가율"), Range(0f, 1f)]
public float PressureStreakMoveSpeedIncrease = 0.2f;
[Tooltip("연속 처치 보너스 지속시간 (게임 초)"), Min(0f)]
public float PressureStreakDuration = 3f;
[Header("가드")]
[Tooltip("가드 재사용 대기시간 (초)"), Min(0f)] public float GuardCooldownDuration = 10f;
[Tooltip("가드 무적 지속시간 (초)"), Min(0f)] public float GuardDuration = 1f;
[Tooltip("가드가 실제 공격을 막았을 때 회복하는 쿨타임 비율 (%)"), Range(0f, 100f)]
public float GuardSuccessCooldownRechargePercent = 70f;
[Header("전장 가장자리 fallback")]
[Tooltip("ArenaBounds가 없는 테스트 장면에서 사용하는 전장 반너비/반높이")]
public Vector2 ArenaHalfExtents = new(15f, 8.4375f);
[Tooltip("ArenaBounds가 없는 테스트 장면의 플레이어 가장자리 여백")]
public Vector2 ArenaPadding = new(0.5f, 0.625f);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2643d1587a2745d0b584eadd6b34978b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,155 @@
using System;
using BumpCombat.Core;
using BumpCombat.Enemies;
using UnityEngine;
namespace BumpCombat.Constants
{
[Serializable]
public struct SpawnDensityPhase
{
[Tooltip("다음 단계로 넘어가는 진행시간 (초). 마지막 단계는 무시됩니다.")]
public float EndTimeSeconds;
[Min(0)] public int CrowdTarget;
[Min(0)] public int NormalTarget;
public SpawnDensityPhase(float endTimeSeconds, int crowdTarget, int normalTarget)
{
EndTimeSeconds = endTimeSeconds;
CrowdTarget = crowdTarget;
NormalTarget = normalTarget;
}
}
[CreateAssetMenu(menuName = "BumpCombat/Constants/Run and Spawning")]
public sealed class RunConstants : ScriptableObject
{
[Header("런 이벤트 시간표 (초)")]
public float[] DevelopmentEliteEventTimes = { 36f, 72f, 108f, 144f, 180f, 216f };
public float[] FormalEliteEventTimes = { 180f, 360f, 540f, 720f, 900f, 1080f };
public float[] ProductionEliteEventTimes = { 180f, 360f, 540f, 720f };
[Min(0f)] public float DevelopmentProgressionMultiplier = 5f;
[Min(0f)] public float DevelopmentMidBossEventTime = 120f;
[Min(0f)] public float DevelopmentFinalBossEventTime = 240f;
[Min(0f)] public float FormalMidBossEventTime = 600f;
[Min(0f)] public float FormalFinalBossEventTime = 1200f;
[Min(0f)] public float ProductionMidBossEventTime = 600f;
[Min(0f)] public float ProductionFinalBossEventTime = 900f;
[Min(0)] public int DevelopmentStartupArtifactSelectionCount = 3;
[Min(0)] public int ProductionStartupArtifactSelectionCount = 1;
[Min(0)] public int LegacyStartupArtifactSelectionCount = 3;
[Header("스폰 밀도 단계")]
[Tooltip("순서대로 시작시점, 1분, 3분, 5분, 10분 이후의 군중형/일반형 목표 수")]
public SpawnDensityPhase[] SpawnDensity =
{
new(60f, 3, 3), new(180f, 6, 4), new(300f, 12, 4),
new(600f, 19, 5), new(float.PositiveInfinity, 27, 5),
};
[Min(0)] public int TimedEventCrowdTarget = 5;
[Min(0)] public int TimedEventNormalTarget = 3;
[Min(0)] public int FinalBossMaximumCrowdTarget = 2;
[Header("웨이브")]
[Min(1)] public int MaximumAliveEnemies = 100;
[Min(1)] public int MaximumBossEncounterEnemies = 8;
[Min(0.1f)] public float WaveInterval = 5f;
[Min(0.1f)] public float TimedEventWaveInterval = 12f;
[Min(1)] public int MaximumWaveSize = 3;
[Min(1)] public int MaximumTimedEventWaveSize = 1;
[Min(0f)] public float InitialWaveDelay = 0.5f;
[Header("실제 플레이 유입량·습격")]
[Min(0)] public int ProductionWaveCrowdSize = 2;
[Min(0)] public int ProductionWaveNormalSize = 1;
[Min(0)] public int ProductionTimedEventCrowdSize = 1;
[Min(0f)] public float ProductionSwarmInterval = 90f;
[Min(0f)] public float ProductionSwarmBossExclusionWindow = 30f;
[Min(0f)] public float ProductionSwarmEarlyEndTime = 300f;
[Min(0f)] public float ProductionSwarmMidEndTime = 600f;
[Min(0)] public int ProductionSwarmEarlyCount = 16;
[Min(0)] public int ProductionSwarmMidCount = 20;
[Min(0)] public int ProductionSwarmLateCount = 24;
[Min(1)] public int ProductionSwarmBatchSize = 4;
[Min(0.1f)] public float ProductionSwarmBatchInterval = 2f;
[Header("스폰 위치")]
[Min(0f)] public float SpawnEdgeInset = 0.25f;
[Min(0f)] public float PlayerSafeSpawnDistance = 4f;
[Min(0f)] public float SpawnCameraMargin = 0.5f;
[Min(0f)] public float SummonSpawnRadius = 2.5f;
[Min(0f)] public float SummonSpawnMinimumSeparation = 1.5f;
[Min(0f)] public float SummonSpawnPlayerClearance = 1.25f;
public Vector2 FallbackSpawnHalfExtents = new(14.5f, 7.8125f);
[Tooltip("시간 중간보스의 후보 프리팹 종류")]
public EnemyKind[] MidBossKinds = { EnemyKind.GreatswordSkeleton, EnemyKind.NecroGolem };
[Header("이벤트 적 기본 튜닝")]
public RunEventEnemyTuning EliteTuning = RunEventEnemyTuning.Create(
EnemyKind.Lancer, 3f, 30, 1.25f, Color.white, 1.05f, 1.1f, 0.85f, 1f,
0.7f, 1f, 1.1f, 1.15f, 1f, 2, 0.7f, 0.35f, 2, 0.65f, true);
public RunEventEnemyTuning MidBossTuning = RunEventEnemyTuning.Create(
EnemyKind.GreatswordSkeleton, 8f, 100, 1.6f, new Color(1f, 0.35f, 0.3f),
0.9f, 1.25f, 0.95f, 1f, 0.8f, 1.15f, 1f, 1f, 1.35f, 2, 0.7f,
0.4f, 3, 0.3f, false);
public RunEventEnemyTuning FinalBossTuning = RunEventEnemyTuning.Create(
EnemyKind.Warlock, 15f, 0, 2f, new Color(0.75f, 0.35f, 1f), 0.85f,
1.25f, 0.9f, 1f, 0.75f, 1f, 1f, 1f, 1.25f, 3, 0.75f, 0.3f,
4, 0.15f, false);
[Min(1f)] public float ProductionMidBossHealthMultiplier = 7f;
[Min(1f)] public float ProductionFinalBossHealthMultiplier = 12f;
[Tooltip("순서대로 첫 번째, 두 번째, 세 번째, 이후 시간 엘리트 체력 배율")]
public float[] ProductionEliteHealthMultipliers = { 2.5f, 3.5f, 3.5f, 5f };
[Tooltip("순서대로 첫 번째, 두 번째, 세 번째, 이후 시간 엘리트 공격 배율")]
public float[] ProductionEliteAttackDamageMultipliers = { 1f, 1f, 1.1f, 1.2f };
[Tooltip("순서대로 첫 번째, 두 번째, 세 번째, 이후 시간 엘리트 연속 공격 수")]
public int[] ProductionEliteAttacksPerSequence = { 1, 2, 3, 2 };
[Tooltip("엘리트 순환에 사용할 적 종류")]
public EnemyKind[] EliteKinds =
{
EnemyKind.ArmoredSkeleton,
EnemyKind.Werewolf,
EnemyKind.Werebear,
};
[Tooltip("개발 런에서 순서대로 적용할 엘리트 연속 공격 수")]
public int[] DevelopmentEliteAttacksPerSequence = { 2, 2, 3 };
[Tooltip("생산 런에서 순서대로 적용할 엘리트 경고시간 배율")]
public float[] ProductionEliteWarningDurationMultipliers = { 1f, 0.85f, 0.85f, 0.85f };
[Tooltip("생산 런에서 순서대로 적용할 엘리트 회복시간 배율")]
public float[] ProductionEliteRecoveryDurationMultipliers = { 1f, 0.7f, 0.7f, 0.7f };
[Header("전장")]
public Vector2 ArenaBackgroundHalfExtents = new(15f, 8.4375f);
public Vector2 ArenaPlayableHalfExtents = new(15f, 8.4375f);
public Vector2 ArenaPlayerPadding = new(0.5f, 0.625f);
public Vector2 ArenaCameraViewHalfExtents = new(10f, 5.625f);
public Vector2 ArenaCameraDeadZone = new(0.5f, 0.3f);
[Min(0f)] public float ArenaCameraSmoothTime = 0.15f;
public float[] EliteTimes(bool development, bool production)
{
float[] times = production
? ProductionEliteEventTimes
: development ? DevelopmentEliteEventTimes : FormalEliteEventTimes;
return times ?? Array.Empty<float>();
}
public SpawnDensityPhase GetSpawnDensity(float elapsedTime)
{
if (SpawnDensity == null || SpawnDensity.Length == 0)
{
return new SpawnDensityPhase(float.PositiveInfinity, 27, 5);
}
for (int i = 0; i < SpawnDensity.Length - 1; i++)
{
if (elapsedTime < SpawnDensity[i].EndTimeSeconds)
{
return SpawnDensity[i];
}
}
return SpawnDensity[SpawnDensity.Length - 1];
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 42e84bae00be47f387921d3e618ed6a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: