<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>시리우스의 개발일지</title>
    <link>https://siriussquare.tistory.com/</link>
    <description>siriussquare 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 14 Jul 2026 16:51:34 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>siriussquare</managingEditor>
    <image>
      <title>시리우스의 개발일지</title>
      <url>https://tistory1.daumcdn.net/tistory/8126599/attach/0b5054753bd34be68b818e2eff6080c8</url>
      <link>https://siriussquare.tistory.com</link>
    </image>
    <item>
      <title>&amp;Alpha;&amp;Beta;&amp;Rho;&amp;Alpha;&amp;Xi;&amp;Alpha;&amp;Sigma; - 개발 후기 1편</title>
      <link>https://siriussquare.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본론으로 가기 전에 먼저&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 블로그를 찾아주신 수많은 분들에게 감사를 표합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;아브락사스 - 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아브락사스는 로그라이트 액션&amp;nbsp; 게임으로 2025년 1학년 1학기 경기게임마이스터고 개인프로젝트 수행평가에서 우수상을 받은 작품입니다(실제로 그만큼 우수한지는 잘 모르겠지만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 본격적으로 공개한 첫 작품이기도 하고 이 작품을 만들면서 상당히 고생한 만큼. 저도 꽤나 애착이 가기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나. 한편으로는 매우 아쉽습니다. 제가 욕심을 조금만 버리고 조금만 더 능력이 있었다면 더 좋은 작품이 나올 수 있었을 텐데 말이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 게시글은 제가 저의 게임을 돌아보면서 부족했던 점들을 찾고 비판하는 일기의 느낌으로 봐주시면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 편에서는 스텟 상승 아이템 시스템, 인챈트 효과 발생 시스템, 넣지 못한 방들에 대해 이야기하도록 하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스텟 상승 아이템 시스템&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dF41ok/btsPOsFdfCY/Jpuk2hbVv2OYMD2vbkv2LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dF41ok/btsPOsFdfCY/Jpuk2hbVv2OYMD2vbkv2LK/img.png&quot; data-alt=&quot;스텟 상승 아이템을 파는 상점에 서있는 플레이어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dF41ok/btsPOsFdfCY/Jpuk2hbVv2OYMD2vbkv2LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdF41ok%2FbtsPOsFdfCY%2FJpuk2hbVv2OYMD2vbkv2LK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;353&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스텟 상승 아이템을 파는 상점에 서있는 플레이어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작중의 메인 시스템중 하나이자 그나마 운빨에 기대지 않을 수 있는 확정적인 스텟 상승의 수단입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구매하기 위해선 일정량의 돈이 필요하며. 위쪽 화살표 키(가드 키와 동일)를 누르고 있는 상태에서 접촉할 시 먹으면 스텟을 증가시키는 아이템이 드롭됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 스크립트&lt;/p&gt;
&lt;pre id=&quot;code_1754825808010&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using NUnit.Framework;
using System;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public interface IItem
{
    float TargetDir { get; set; }
    float Speed { get; set; }
    LayerMask TargetLayer { get; set; }
    void UseItem(Entity entity);
}

public enum StatType
{
    MaxHealth,
    HealthRecoverPower,
    MaxStamina,
    StaminaRecoverPower,
    MaxMana,
    ManaRecoverPower,
    AttackPower,
    AttackPowerMultiplier,
    AttackSpeed,
    Defense,
    CriticalChance,
    CriticalDamageMultiplier,
    CriticalGardPenetration,
    KnockbackResistance,
    DefGardLevel,
    MoveSpeed,
    JumpForce,
    CooldownTime,
    MaxJumpCount,
    Gold,
    ArrowPoint,
    CurrentHealth,
    CurrentStamina,
    CurrentMana,
    CurrentShield,
    
}

[Serializable]
public struct StatIncreaseInfo
{
    public StatType stat;
    public float amount;
    public Color color;
}

public class CoinItemScript : MonoBehaviour, IItem, IPoolable
{
    [SerializeField] private string itemName;

    [SerializeField] private float targetDir = 2f;
    [SerializeField] private float speed = 2f;
    [SerializeField] private LayerMask targetLayer;
    [SerializeField] private float firstenableTime = 0.5f;
    [SerializeField] public List&amp;lt;StatIncreaseInfo&amp;gt; statIncreases;

    public string ItemName =&amp;gt; itemName;
    public GameObject GameObject =&amp;gt; gameObject;

    public float TargetDir { get =&amp;gt; targetDir; set =&amp;gt; targetDir = value; }
    public float Speed { get =&amp;gt; speed; set =&amp;gt; speed = value; }

    public LayerMask TargetLayer { get =&amp;gt; targetLayer; set =&amp;gt; targetLayer = value; }

    public Rigidbody2D rb;

    private void Awake()
    {
        rb = GetComponent&amp;lt;Rigidbody2D&amp;gt;();
    }

    private void Update()
    {
        if (firstenableTime &amp;gt; 0)
        {
            firstenableTime -= Time.deltaTime;
            rb.linearVelocityX = Mathf.Lerp(rb.linearVelocity.x, 0, 2f * Time.deltaTime);
        }
        if (firstenableTime &amp;lt; 0)
        {
            Collider2D hit = Physics2D.OverlapCircle(transform.position, targetDir, targetLayer);
            Collider2D hit2 = Physics2D.OverlapCircle(transform.position, 0.5f, targetLayer);
            if (hit != null &amp;amp;&amp;amp; hit.TryGetComponent&amp;lt;Entity&amp;gt;(out Entity entity))
            {
                Vector2 direction = (entity.transform.position - transform.position);

                rb.AddForce(direction * speed, ForceMode2D.Impulse);
                rb.gravityScale = 0f;

            }
            else
            {
                rb.linearVelocity = new Vector2(Mathf.Lerp(rb.linearVelocity.x, 0, 2f * Time.deltaTime), Mathf.Lerp(rb.linearVelocity.y, 0, 2f * Time.deltaTime));
                
                rb.gravityScale = 2.5f;
            }

            if (hit2 != null &amp;amp;&amp;amp; hit2.TryGetComponent&amp;lt;Entity&amp;gt;(out entity))
            {
                UseItem(entity);
                PoolManager.Instance.Push(this);
            }
        }
        
    }

    public void UseItem(Entity entity)
    {
        if (entity.isDead)
        {
            return;
        }
        foreach (var info in statIncreases)
        {
            if (info.amount == 0) continue;

            ApplyStatIncrease(entity.statObject, info.stat, info.amount);
            entity.HPBarSystem?.SetHP();
            entity.HPBarSystem?.SetMana();

            if (info.stat != StatType.CurrentHealth || info.stat != StatType.CurrentShield)
            {
                IPoolable statText = PoolManager.Instance.Pop(&quot;ItemText&quot;);
                if (statText.GameObject != null)
                {

                    ItemTextScript itemTextScript = statText.GameObject.GetComponent&amp;lt;ItemTextScript&amp;gt;();
                    if (itemTextScript != null)
                    {

                        itemTextScript.SettingText(info.amount, info.stat.ToString(), info.color);
                        statText.GameObject.transform.position = (Vector2)transform.position + new Vector2(UnityEngine.Random.Range(-0.5f, 0.5f), UnityEngine.Random.Range(-0.5f, 0.5f));
                        statText.GameObject.SetActive(true);
                    }
                }
            }
            IPoolable statEffect = PoolManager.Instance.Pop(&quot;ItemCollectEffect&quot;);
            if (statEffect.GameObject != null)
            {
                statEffect.GameObject.transform.position = transform.position;
                statEffect.GameObject.GetComponent&amp;lt;Effect&amp;gt;().SetColor(info.color);
                statEffect.GameObject.SetActive(true);
            }

        }
        
            var itemEventHolder = GetComponent&amp;lt;EntityEventHolder&amp;gt;();
            if (itemEventHolder != null &amp;amp;&amp;amp; entity.entityEventHolder != null)
            {
                foreach (var itemEvent in itemEventHolder.pendingEvents)
                {
                    entity.entityEventHolder.AddEvent(itemEvent);

                    
                    string eventText = SettigTextsa(itemEvent);
                    string eventLogText = SettigTextnonum(itemEvent);
                    FindAnyObjectByType&amp;lt;MenuScript&amp;gt;().ResumeTextPlus(eventLogText);
                    IPoolable statText = PoolManager.Instance.Pop(&quot;ItemText&quot;);
                    if (statText.GameObject != null)
                    {
                        ItemTextScript itemTextScript = statText.GameObject.GetComponent&amp;lt;ItemTextScript&amp;gt;();
                        if (itemTextScript != null)
                        {
                            itemTextScript.SettingText(0, eventText, Color.white,true);
                            statText.GameObject.transform.position = (Vector2)transform.position + new Vector2(UnityEngine.Random.Range(-0.5f, 0.5f), UnityEngine.Random.Range(-0.5f, 0.5f));
                            statText.GameObject.SetActive(true);
                        }
                    }
                
            }
        }
    }
    private string SettigTextsa(PendingEvent pe)
    {
        string returnstring = &quot;&quot;;


        if (EventManager.GameEventNames.TryGetValue(pe.eventType, out var eventName))
            returnstring += $&quot;{eventName}\n&quot;;


        if (EventManager.GameConditionNames.TryGetValue(pe.condition.conditionType, out var cond1))
        {
            if (cond1.Length == 1 || pe.condition.conditionType == EventCondition.ConditionType.FloorBetween)
                returnstring += $&quot;{cond1[0]}\n&quot;;
            else
                returnstring += $&quot;{cond1[0]} {pe.condition.threshold.ToString(&quot;F1&quot;)} {cond1[1]}\n&quot;;
        }


        if (pe.secondaryConditions != null &amp;amp;&amp;amp; pe.secondaryConditions.Length &amp;gt; 0)
        {
            var secondCondition = pe.secondaryConditions[0];
            if (EventManager.GameSecondaryConditionNames.TryGetValue(secondCondition.conditionType, out var cond))
            {
                if (cond.Length == 1)
                    returnstring += $&quot;{secondCondition.parameters[0].ToString(&quot;F1&quot;)}% {cond[0]}\n&quot;;
                else
                    returnstring += $&quot;{cond[0]} {secondCondition.parameters[0].ToString(&quot;F1&quot;)} {cond[1]}\n&quot;;
            }
        }

        if (EventManager.EffectTypeNames.TryGetValue(pe.effect.effectType, out var effectNames))
        {
            var effectParams = pe.effect.parameters;

            for (int i = 1; i &amp;lt; effectNames.Length; ++i)
            {
                if (i - 1 &amp;lt; effectParams.Count)
                {
                    returnstring += $&quot;{effectNames[i]} {effectParams[i - 1].ToString(&quot;F2&quot;)} &quot;;
                }
            }

            returnstring += $&quot;{effectNames[0]}\n&quot;;
        }

        return returnstring.TrimEnd();
    }

    private string SettigTextnonum(PendingEvent pe)
    {
        string returnstring = &quot;&quot;;

        if (EventManager.GameEventNames.TryGetValue(pe.eventType, out var eventName))
        {
            returnstring += $&quot;{eventName}&quot;;
        }


        if (EventManager.GameConditionNames.TryGetValue(pe.condition.conditionType, out var cond1))
        {
            if (cond1.Length == 1 || pe.condition.conditionType == EventCondition.ConditionType.FloorBetween)
                returnstring += $&quot;{cond1[0]}&quot;;
            else
                returnstring += $&quot;{cond1[0]} {pe.condition.threshold} {cond1[1]}&quot;;
        }


        if (EventManager.GameSecondaryConditionNames.TryGetValue(pe.secondaryConditions[0].conditionType, out var cond))
        {
            if (cond.Length == 1)
                returnstring += $&quot;{pe.secondaryConditions[0].parameters[0]}% {cond[0]}&quot;;
            else
                returnstring += $&quot;{cond[0]} {pe.secondaryConditions[0].parameters[0]} {cond[1]}&quot;;
        }


        if (EventManager.EffectTypeNames.TryGetValue(pe.effect.effectType, out var effectNames))
        {
            var effectParams = pe.effect.parameters;

            for (int i = 1; i &amp;lt; effectNames.Length; ++i)
            {

                if (i - 1 &amp;lt; effectParams.Count)
                {
                    returnstring += $&quot;{effectNames[i]} {effectParams[i - 1]} &quot;;
                }
            }


            returnstring += $&quot;{effectNames[0]}&quot;;
        }

        return returnstring.TrimEnd();
    }
    private void OnDestroy()
    {
        if (PoolManager.HasInstance)
        {
            PoolManager.Instance.Remove(this);
        }
    }

    private void ApplyStatIncrease(StatManager stats, StatType stat, float amount)
    {
        switch (stat)
        {
            case StatType.MaxHealth:
                stats.MaxHealth += amount;
                break;
            case StatType.HealthRecoverPower:
                stats.HealthRecoverPower += amount;
                break;
            case StatType.MaxStamina:
                stats.MaxStamina += amount;
                break;
            case StatType.StaminaRecoverPower:
                stats.StaminaRecoverPower += amount;
                break;
            case StatType.MaxMana:
                stats.MaxMana += amount;
                break;
            case StatType.ManaRecoverPower:
                stats.ManaRecoverPower += amount;
                break;
            case StatType.AttackPower:
                stats.AttackPower += amount;
                break;
            case StatType.AttackPowerMultiplier:
                stats.AttackPowerMultiplier += amount;
                break;
            case StatType.AttackSpeed:
                stats.AttackSpeed += amount;
                break;
            case StatType.Defense:
                stats.Defense += amount;
                break;
            case StatType.CriticalChance:
                stats.CriticalChance += amount;
                break;
            case StatType.CriticalDamageMultiplier:
                stats.CriticalDamageMultiplier += amount;
                break;
            case StatType.CriticalGardPenetration:
                stats.CriticalGardPenetration += (int)amount;
                break;
            case StatType.KnockbackResistance:
                stats.KnockbackResistance += amount;
                break;
            case StatType.DefGardLevel:
                stats.DefGardLevel += (int)amount;
                break;
            case StatType.MoveSpeed:
                stats.MoveSpeed += amount;
                break;
            case StatType.JumpForce:
                stats.JumpForce += amount;
                break;
            case StatType.CooldownTime:
                stats.CooldownTime -= amount;
                if (stats.CooldownTime &amp;lt; 0.01f) stats.CooldownTime = 0.01f;
                break;
            case StatType.MaxJumpCount:
                stats.MaxJumpCount += (int)amount;
                break;
            case StatType.Gold:
                stats.Gold += (int)amount;
                break;
            case StatType.ArrowPoint:
                stats.ArrowPoint += (int)amount;
                break;
            case StatType.CurrentHealth:
                stats.gameObject.GetComponent&amp;lt;Entity&amp;gt;().Heal(amount);
                break;
            case StatType.CurrentStamina:
                stats.gameObject.GetComponent&amp;lt;Entity&amp;gt;().Stamina += amount;
                break;
            case StatType.CurrentMana:
                stats.gameObject.GetComponent&amp;lt;Entity&amp;gt;().Mana += amount;
                break;
            case StatType.CurrentShield:
                stats.gameObject.GetComponent&amp;lt;Entity&amp;gt;().AddShield(amount);
                break;
        }
    }


    public void ResetItem()
    {
        statIncreases.Clear();
        firstenableTime = 0.5f;
        rb.linearVelocity = Vector2.zero;
        rb.angularVelocity = 0f;
        rb.gravityScale = 2.5f;
    }

#if UNITY_EDITOR
    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(transform.position, targetDir);
    }
#endif
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트의 이름은 CoinItemScript이지만 실상은 그냥 아이템스크립트의 역할을 하는 기적을 보유하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 당시에 나는 새로 스크립트를 생성할 시 생기는 로딩 시간이 길어 매우 귀찮았기 때문에 인터페이스, enum, 스텟 상승 정보들이 한 곳에 적혀있는 기괴한 장면을 볼 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(당연히 이렇게 짜시는 것은 절대로 추천드리지 않습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 구조 요약 시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업데이트에선 생성되고 몇 초 동안은 중력과 기존의 힘만 작용하다가 몇 초가 지난 후엔 근처에 접근한 플레이어에게 자동으로 따라오고 접촉한 엔티티에게 statIncreases에 저장된 StatIncreaseInfo들의 정보에 따라 차례대로 그에 맞는 스텟증감 이밴트를 실행하고 이팩트와 올라간 스텟을 표시하는 텍스트 오브젝트를 호출하는 것으로 요약할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시면 알겠지만 딱히 플레이어인지 아닌지 검증하는 부분은 따로 존재하지 않는 걸 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱히 레이어 말고는 플레이어를 검증할 것이 따로 없었기도 하고. (실질적으로 플레이어와 적의 차이는 조종, 레이어 외엔 존재하지 않았습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀랍게도 개발 초기에는 적들도 스텟 증가 아이템을 먹을 수 있었습니다.(심지어 돈, 후술 할 인챈트 아이템 또한 획득하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 진행 도중 이 시스템은 넣기에 부적절하다고 판단하여 이 시스템은 제거되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인챈트 시스템&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 스크립&lt;/p&gt;
&lt;pre id=&quot;code_1754825376917&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.SceneManagement;
using static SecondaryCondition;
#region Serializable Event Structures

[Serializable]
public class EventCondition
{
    public enum ConditionType
    {
        HealthAbove, HealthBelow,
        StaminaAbove,
        ManaAbove,
        FloorAbove, FloorBetween, FloorBelow,
 Always
    }

    public ConditionType conditionType;
    public float threshold;
    public float multiplier = 1f;

}

[Serializable]
public class SecondaryCondition
{
    public enum SecondaryConditionType
    {
        None, RandomChance,
         PlayerDamageAbove,
         PlayerHitDamageAbove
    }

    public SecondaryConditionType conditionType;
    public List&amp;lt;float&amp;gt; parameters = new(2);
}

[Serializable]
public class EventEffect
{
    public enum EffectType
    {
        Bleed, Stun, Burn, Frost, Drench, Poison, Shock,
        AttackUp, DefenseUp, Shield,
        Impact, Explosion, HeatWave, Vampirism, Wave
    }

    public EffectType effectType;
    public List&amp;lt;float&amp;gt; parameters = new(5);
}

[Serializable]
public class CustomEventData
{
    public Entity target;
    public Transform source;
    public List&amp;lt;EventCondition&amp;gt; conditions = new();
    public List&amp;lt;SecondaryCondition&amp;gt; secondaryConditions = new();
    public List&amp;lt;EventEffect&amp;gt; effects = new();
    public float recentDamageDealt;
    public float recentDamageTaken;
}

#endregion

public enum GameEventType
{

    PlayerAttack,
    PlayerAttackHit,
    PlayerBasicAttack,
    PlayerBasicAttackHit,
    PlayerPierce,
    PlayerPierceHit,
    PlayerTriSlash,
    PlayerTriSlashHit,
    PlayerUppercut,
    PlayerUppercutHit,
    PlayerSpinSlash,
    PlayerSpinSlashHit,
    PlayerCounter,
    PlayerCounterHit,
    PlayerHorizontal,
    PlayerHorizontalHit,
    EquinoxMultiPierce,
    EquinoxMultiPierceHit,
    EquinoxMultiCut,
    EquinoxMultiCutHit,
    EquinoxBigCut,
    EquinoxBigCutHit,

    PlayerGuard,
    PlayerHit,
}


public class EventManager : MonoBehaviour
{
    [SerializeField] private GameObject impecteffect;
    [SerializeField] private GameObject explosion;
    [SerializeField] private GameObject magiceffect;
    [SerializeField] private CinemachineImpulseSource impulseSource;
    public static EventManager Instance { get; private set; }

    private static readonly HashSet&amp;lt;EventEffect.EffectType&amp;gt; IgnoreInVectorEffectTypes = new()
    {
        EventEffect.EffectType.Bleed, EventEffect.EffectType.Stun, EventEffect.EffectType.Burn,
        EventEffect.EffectType.Frost, EventEffect.EffectType.Drench,
        EventEffect.EffectType.Poison, EventEffect.EffectType.Shock,
        EventEffect.EffectType.AttackUp, EventEffect.EffectType.DefenseUp,
        EventEffect.EffectType.Shield, EventEffect.EffectType.Impact
    };

    private static readonly HashSet&amp;lt;SecondaryCondition.SecondaryConditionType&amp;gt; IgnoreInVectorSecondaryTypes = new()
    {

        SecondaryCondition.SecondaryConditionType.PlayerDamageAbove,

        SecondaryCondition.SecondaryConditionType.PlayerHitDamageAbove
    };

    public static readonly Dictionary&amp;lt;GameEventType, float&amp;gt; GameEventMultipliers = new()
    {
        { GameEventType.PlayerAttack, 0.25f },
        { GameEventType.PlayerAttackHit, 0.15f },
        { GameEventType.PlayerBasicAttack, 1f },
        { GameEventType.PlayerBasicAttackHit, 0.5f },
        { GameEventType.PlayerPierce, 1.5f },
        { GameEventType.PlayerPierceHit, 0.75f },
        { GameEventType.PlayerTriSlash, 2f },
        { GameEventType.PlayerTriSlashHit, 1f },
        { GameEventType.PlayerUppercut, 3f },
        { GameEventType.PlayerUppercutHit, 1.5f },
        { GameEventType.PlayerSpinSlash, 4f },
        { GameEventType.PlayerSpinSlashHit, 0.25f },
        { GameEventType.PlayerCounter, 5f },
        { GameEventType.PlayerCounterHit, 1f },
        { GameEventType.PlayerHorizontal, 2f },
        { GameEventType.PlayerHorizontalHit, 1f },
        { GameEventType.EquinoxMultiPierce, 5f },
        { GameEventType.EquinoxMultiPierceHit, 2f },
        { GameEventType.EquinoxMultiCut, 7f },
        { GameEventType.EquinoxMultiCutHit, 1f },
        { GameEventType.EquinoxBigCut, 10f },
        { GameEventType.EquinoxBigCutHit, 3f },
        { GameEventType.PlayerGuard, 1f },
        { GameEventType.PlayerHit, 1f }
    };
    public static readonly Dictionary&amp;lt;EventCondition.ConditionType, string[]&amp;gt; GameConditionNames = new()
{
    { EventCondition.ConditionType.HealthBelow, new[] { &quot;체력이&quot;, &quot;% 이하일 때 &quot; } },
    { EventCondition.ConditionType.HealthAbove, new[] { &quot;체력이&quot;, &quot;% 이상일 때 &quot; } },

    { EventCondition.ConditionType.StaminaAbove, new[] { &quot;스태미나가&quot;, &quot;% 이상일 때 &quot; } },

    { EventCondition.ConditionType.ManaAbove, new[] { &quot;마나가&quot;, &quot;% 이상일 때 &quot; } },
    { EventCondition.ConditionType.FloorBelow, new[] { &quot;플로어가&quot;, &quot;층 이하일 때 &quot; } },
    { EventCondition.ConditionType.FloorAbove, new[] { &quot;플로어가&quot;, &quot;층 이상일 때 &quot; } },
    { EventCondition.ConditionType.FloorBetween, new[] { &quot;플로어가 10의 배수일 때 &quot;, } },
    { EventCondition.ConditionType.Always, new[] { &quot;항상 &quot; } }
};
    public static readonly Dictionary&amp;lt;SecondaryCondition.SecondaryConditionType, string[]&amp;gt; GameSecondaryConditionNames = new()
{
    { SecondaryCondition.SecondaryConditionType.RandomChance, new[] { &quot;확률로 &quot; } },
        { SecondaryCondition.SecondaryConditionType.PlayerDamageAbove, new[] { &quot;데미지가&quot;,&quot; 이상일 시 &quot; } },
        { SecondaryCondition.SecondaryConditionType.PlayerHitDamageAbove, new[] { &quot;데미지가&quot;, &quot; 이상일 시 &quot; } },

    };
    public static readonly Dictionary&amp;lt;GameEventType, string&amp;gt; GameEventNames = new()
{
    { GameEventType.PlayerAttack, &quot;플레이어 공격 시 &quot; },
    { GameEventType.PlayerAttackHit, &quot;플레이어 공격 적중 시 &quot; },
    { GameEventType.PlayerBasicAttack, &quot;플레이어 기본 공격 사용 시 &quot; },
    { GameEventType.PlayerBasicAttackHit, &quot;플레이어 기본 공격 적중 시 &quot; },
    { GameEventType.PlayerPierce, &quot;찌르기 공격 시 &quot; },
    { GameEventType.PlayerPierceHit, &quot;찌르기 적중 시 &quot; },
    { GameEventType.PlayerTriSlash, &quot;트라이슬래시 사용 시 &quot; },
    { GameEventType.PlayerTriSlashHit, &quot;트라이슬래시 적중 시 &quot; },
    { GameEventType.PlayerUppercut, &quot;올려치기 사용 시 &quot; },
    { GameEventType.PlayerUppercutHit, &quot;올려치기 적중 시 &quot; },
    { GameEventType.PlayerSpinSlash, &quot;회전베기 사용 시 &quot; },
    { GameEventType.PlayerSpinSlashHit, &quot;회전베기 적중 시 &quot; },
    { GameEventType.PlayerCounter, &quot;스톰블레이드 사용 시 &quot; },
    { GameEventType.PlayerCounterHit, &quot;스톰블레이드 적중 시 &quot; },
    { GameEventType.PlayerHorizontal, &quot;가로베기 사용 시 &quot; },
    { GameEventType.PlayerHorizontalHit, &quot;가로배기 적중 시 &quot; },
    { GameEventType.EquinoxMultiPierce, &quot;이퀴녹스 공간절단 사용 시 &quot; },
    { GameEventType.EquinoxMultiPierceHit, &quot;이퀴녹스 공간절단 적중 시 &quot;  },
    { GameEventType.EquinoxMultiCut, &quot;이퀴녹스 절단난무 사용 시 &quot; },
    { GameEventType.EquinoxMultiCutHit, &quot;이퀴녹스 절단난무 적중 시 &quot; },
    { GameEventType.EquinoxBigCut, &quot;이퀴녹스 대절단 사용 시 &quot; },
    { GameEventType.EquinoxBigCutHit, &quot;이퀴녹스 대절단 적중 시 &quot; },
    { GameEventType.PlayerGuard, &quot;플레이어 가드 시 &quot; },
    { GameEventType.PlayerHit, &quot;플레이어 피격 시 &quot; }
};
    public static readonly Dictionary&amp;lt;EventEffect.EffectType, string[]&amp;gt; EffectTypeNames = new()
{
        {EventEffect.EffectType.Bleed, new[] { &quot;스택만큼 출혈 부과 &quot;, &quot; &quot;}},
        {EventEffect.EffectType.Stun, new[] { &quot;스택만큼 기절 부과 &quot;, &quot; &quot;}},
        {EventEffect.EffectType.Burn, new[] { &quot;스택만큼 화상 부과 &quot;, &quot; &quot;}},
        {EventEffect.EffectType.Frost, new[] { &quot;스택만큼 서리 부과 &quot;, &quot; &quot;}},
        {EventEffect.EffectType.Drench, new[] { &quot;스택만큼 침수 부과 &quot;, &quot; &quot; }},
        {EventEffect.EffectType.Poison, new[] { &quot;스택만큼 독 부과 &quot;, &quot;&quot;}},
        {EventEffect.EffectType.Shock, new[] { &quot;스택만큼 감전 부과 &quot;, &quot;&quot;}},
         {EventEffect.EffectType.AttackUp, new[] {&quot;스택 공격력 버프 &quot;, &quot;&quot;}},
        {EventEffect.EffectType.DefenseUp, new[] {&quot;스택 방어력 버프 &quot;, &quot;&quot;}},
        {EventEffect.EffectType.Shield, new[] {&quot;스택 쉴드 생성 &quot;, &quot;&quot;}},
        {EventEffect.EffectType.Impact, new[] {&quot;충격 발생 &quot;, &quot;피해레벨 &quot;, &quot;기절량 &quot;}},
        {EventEffect.EffectType.Explosion, new[] {&quot;폭파 발생 &quot;, &quot;피해레벨 &quot;, &quot;범위레벨 &quot;}},
        {EventEffect.EffectType.Wave, new[] {&quot;파동 발생 &quot;, &quot;피해레벨 &quot;, &quot;생성 간격 &quot;, &quot;생성 횟수 &quot;}},
        {EventEffect.EffectType.HeatWave, new[] {&quot;열파 발생 &quot;, &quot;피해레벨 &quot;, &quot;지속 시간 &quot;, &quot;생성 범위 오차 &quot;}},
        {EventEffect.EffectType.Vampirism, new[] { &quot;%만큼 흡혈 &quot;, &quot;&quot;}}

};

    private void Awake()
    {
        if (Instance != null &amp;amp;&amp;amp; Instance != this) { Destroy(gameObject); return; }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void EventMain(Entity owner, Entity target, EventCondition ec, SecondaryCondition[] sec, EventEffect effect, Statuseffect[] se, GameEventType eventType)
    {
        Debug.Log($&quot;이밴트 실행&amp;gt; {owner},{target},{ec},{sec[0]},{effect},{eventType}&quot;);
        if (!CheckPrimaryCondition(owner, ec, se)) return;
        float multiplier = GameEventMultipliers.TryGetValue(eventType, out var baseMultiplier)
                        ? baseMultiplier
                        : 1f;
        Debug.Log($&quot;이밴트 실행&amp;gt; {owner},{target},{ec},{sec[0]},{effect},{eventType}&quot;);
        if (!CheckSecondaryCondition(owner, target, sec[0])) return;
        ApplyEffectWithMultiplier(owner, target, effect, multiplier, sec[0]);
    }

    public void EventMain(Entity owner, Vector2 targetPos, EventCondition ec, SecondaryCondition[] sec, EventEffect effect, Statuseffect[] se, GameEventType eventType)
    {
        Debug.Log($&quot;이밴트 실행&amp;gt; {owner},{targetPos},{ec},{sec[0]},{effect},{eventType}&quot;);
        if (!CheckPrimaryCondition(owner, ec, se)) return;
        float multiplier = GameEventMultipliers.TryGetValue(eventType, out var baseMultiplier)
                        ? baseMultiplier
                        : 1f;
        Debug.Log($&quot;이밴트 실행&amp;gt; {owner},{targetPos},{ec},{sec[0]},{effect},{eventType}&quot;);
        if (!CheckSecondaryCondition(owner, sec[0])) return;
        ApplyEffectWithMultiplier(owner, targetPos, effect, multiplier);

    }

    private bool CheckPrimaryCondition(Entity entity, EventCondition ec, Statuseffect[] se)
    {
        Debug.Log($&quot;CheckPrimaryCondition이밴트 실행&amp;gt; {entity},{ec}&quot;);
        return ec.conditionType switch
        {
            EventCondition.ConditionType.HealthAbove =&amp;gt; (entity.Health / entity.MaxHealth) * 100 &amp;gt; ec.threshold,
            EventCondition.ConditionType.HealthBelow =&amp;gt; (entity.Health / entity.MaxHealth) * 100 &amp;lt; ec.threshold,
            EventCondition.ConditionType.StaminaAbove =&amp;gt; (entity.Stamina / entity.MaxStamina) * 100 &amp;gt; ec.threshold,

            EventCondition.ConditionType.ManaAbove =&amp;gt; (entity.Mana / entity.MaxMana) * 100 &amp;gt; ec.threshold,

            EventCondition.ConditionType.FloorAbove =&amp;gt; StageManager.Instance.Floor &amp;gt; ec.threshold,
            EventCondition.ConditionType.FloorBelow =&amp;gt; StageManager.Instance.Floor &amp;lt; ec.threshold,
            EventCondition.ConditionType.FloorBetween =&amp;gt; StageManager.Instance.Floor % 10 == 0,
            EventCondition.ConditionType.Always =&amp;gt; true,
            _ =&amp;gt; false,
        };
    }

    private bool CheckSecondaryCondition(Entity owner, Entity target, SecondaryCondition sc)
    {
        Debug.Log($&quot;CheckSecondaryCondition이밴트 실행&amp;gt; {owner},{target},{sc}&quot;);
        return sc.conditionType switch
        {
            SecondaryCondition.SecondaryConditionType.None =&amp;gt; true,
            SecondaryCondition.SecondaryConditionType.RandomChance =&amp;gt; UnityEngine.Random.Range(0f,100f) &amp;lt;= (sc.parameters.Count &amp;gt; 0 ? sc.parameters[0] : 1f),

            SecondaryCondition.SecondaryConditionType.PlayerDamageAbove =&amp;gt;
                owner != null &amp;amp;&amp;amp; sc.parameters.Count &amp;gt; 0 &amp;amp;&amp;amp; sc.parameters[1] &amp;gt; sc.parameters[0],

            SecondaryCondition.SecondaryConditionType.PlayerHitDamageAbove =&amp;gt;
                owner != null &amp;amp;&amp;amp; target != null &amp;amp;&amp;amp; sc.parameters.Count &amp;gt; 0 &amp;amp;&amp;amp; sc.parameters[1] &amp;gt; sc.parameters[0],
            _ =&amp;gt; true,
        };
    }
    private bool CheckSecondaryCondition(Entity owner, SecondaryCondition sc)
    {
        return sc.conditionType switch
        {
            SecondaryCondition.SecondaryConditionType.None =&amp;gt; true,
            SecondaryCondition.SecondaryConditionType.RandomChance =&amp;gt; UnityEngine.Random.Range(0f, 100f) &amp;lt;= (sc.parameters.Count &amp;gt; 0 ? sc.parameters[0] : 1f),
            _ =&amp;gt; true,
        };
    }

    public void ApplyEffectWithMultiplier(Entity owner, Entity target, EventEffect effect, float multiplier, SecondaryCondition sc)
    {
        Debug.Log($&quot;이팩트 부여,{owner},{target},{effect},{multiplier}&quot;);
        switch (effect.effectType)
        {
            case EventEffect.EffectType.Bleed:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Bleed,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Stun:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Stun,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Frost:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Frost,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Burn:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Burn,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Drench:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Drench,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Poison:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Poison,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Shock:
                {
                    target.entityStatusManager.ApplyEffect(StatusEffectType.Shock,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.AttackUp:
                {
                    owner.entityStatusManager.ApplyEffect(StatusEffectType.AttackUp,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.DefenseUp:
                {
                    owner.entityStatusManager.ApplyEffect(StatusEffectType.DefenseUp,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Shield:
                owner.AddShield((effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] : 50f) * multiplier);
                break;
            case EventEffect.EffectType.Impact:
                float dmg = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] * owner.statObject.FinalAttackPower : 10f) * multiplier;
                float stun = (effect.parameters.Count &amp;gt; 1 ? effect.parameters[1] : 1f) * multiplier;
                IPoolable impectEffect = PoolManager.Instance.Pop(&quot;ImpectEffect&quot;);
                impectEffect.GameObject.transform.position = target.transform.position;
                target.TakeDamage(dmg * owner.statObject.FinalAttackPower * 0.5f, 5, Vector2.up * 5, AttackType.Impect, owner, target.transform, impecteffect);
                target.ApplyStun(stun);
                impulseSource.GenerateImpulse(stun / 3.5f);
                impectEffect.GameObject.SetActive(true);
                break;
            case EventEffect.EffectType.Explosion:
                float hDmga = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] * owner.statObject.FinalAttackPower : 5f) * multiplier;
                float hRada = (effect.parameters.Count &amp;gt; 2 ? effect.parameters[1] : 1f);
                IPoolable hb = PoolManager.Instance.Pop(&quot;AttackHitBox&quot;);

                hb.GameObject.GetComponent&amp;lt;AttackTriggerScript&amp;gt;().ChangeSetting(hDmga,3,new Vector2(10,10),magiceffect,5f,AttackType.Magic,AttackElement.Null,owner,0.5f);
                Vector3 basePosEx = target.transform.position;
                hb.GameObject.transform.position = new Vector3(basePosEx.x, basePosEx.y, basePosEx.z);
                bool isPlayerOwnera = (owner.Faction == &quot;Player&quot;);
                string waveKeya = isPlayerOwnera ? &quot;Explosion&quot; : &quot;EnemyExplosion&quot;;
                IPoolable meff = PoolManager.Instance.Pop(waveKeya);
                meff.GameObject.transform.position = new Vector3(basePosEx.x, basePosEx.y, basePosEx.z);
                meff.GameObject.transform.localScale = new Vector3(hRada, hRada, hRada);
                hb.GameObject.transform.localScale = new Vector3(hRada, hRada, hRada);
                hb.GameObject.SetActive(true);
                meff.GameObject.SetActive(true);
                impulseSource.GenerateImpulse(hRada / 7);
                break;
            case EventEffect.EffectType.HeatWave:
                {
                    Vector2 spawnPos;
                    if (TryGetGroundPosition((Vector2)target.transform.position + Vector2.up * 1f, out spawnPos))
                    {
                        float hDmg = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] * owner.statObject.FinalAttackPower : 0.2f) * multiplier;
                        float hDur = (effect.parameters.Count &amp;gt; 1 ? effect.parameters[1] : 0.2f);
                        float hRange = (effect.parameters.Count &amp;gt; 2 ? effect.parameters[2] : 1f);
                        IPoolable heatWave;
                        bool isPlayerOwner = (owner.Faction == &quot;Player&quot;);
                        string waveKey = isPlayerOwner ? &quot;HeatWave&quot; : &quot;EnemyHeatWave&quot;;
                        heatWave = PoolManager.Instance.Pop(waveKey);
                        heatWave.GameObject.GetComponent&amp;lt;HeatWave&amp;gt;().OnSetting(hDmg, hDur, owner,magiceffect);
                        heatWave.GameObject.transform.position = spawnPos + new Vector2(UnityEngine.Random.Range(-hRange,hRange),0);
                        
                        heatWave.GameObject.SetActive(true);
                    }
                    break;
                }

            case EventEffect.EffectType.Wave:
                {
                    Vector2 spawnPos;
                    if (TryGetGroundPosition((Vector2)target.transform.position + Vector2.up * 1f, out spawnPos))
                    {
                        float wDmg = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0]  * owner.statObject.FinalAttackPower : 1f) * multiplier;
                        float wIntervalTime = (effect.parameters.Count &amp;gt; 1 ? effect.parameters[1] : 0.3f);
                        int wCount = (effect.parameters.Count &amp;gt; 2 ? (int)effect.parameters[2] : 5);
                        float wSpeed = (effect.parameters.Count &amp;gt; 3 ? effect.parameters[3] : 1f);

                        StartCoroutine(SpawnWave(owner, spawnPos, wDmg, wIntervalTime, wCount, wSpeed));
                    }
                    break;
                }
            case EventEffect.EffectType.Vampirism:
                float vamp = (sc.parameters[1] / (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] : 50f) * multiplier);
                owner.Heal(vamp);
                break;

        }
    }
    private bool TryGetGroundPosition(Vector2 origin, out Vector2 groundPos)
    {
        RaycastHit2D hit = Physics2D.Raycast(origin, Vector2.down, 10f, LayerMask.GetMask(&quot;Ground&quot;));
        if (hit.collider != null)
        {
            groundPos = hit.point;
            return true;
        }
        groundPos = Vector2.zero;
        return false;
    }
    private IEnumerator SpawnWave(Entity owner, Vector2 centerPosition, float damage, float intervalTime, int count, float speed)
    {
        bool isPlayerOwner = (owner.Faction == &quot;Player&quot;);
        string waveKey = isPlayerOwner ? &quot;Wave&quot; : &quot;EnemyWave&quot;;
        Debug.Log($&quot;SpawnWave 시작: {owner}, {centerPosition}, {damage}, {intervalTime}, {count}, {speed}&quot;);
        float pluspos = 0f;
        for (int i = 0; i &amp;lt; count; i++)
        {

            
            IPoolable waveLeft = PoolManager.Instance.Pop(waveKey);
            var leftTransform = waveLeft.GameObject.transform;
            leftTransform.position = new Vector3(centerPosition.x - 1.5f - pluspos, centerPosition.y, 0f);
            leftTransform.localScale = new Vector3(1f, 1f, 1f);

            waveLeft.GameObject.GetComponent&amp;lt;AttackTriggerScript&amp;gt;().ChangeSetting(
                damage,
                3,
                new Vector2(1f, 8f),
                magiceffect,
                1,
                AttackType.Magic,
                AttackElement.Null,
                owner,
                0.5f
            );
            waveLeft.GameObject.SetActive(true);

            IPoolable waveRight = PoolManager.Instance.Pop(waveKey);
            var rightTransform = waveRight.GameObject.transform;
            rightTransform.position = new Vector3(centerPosition.x + 1.5f + pluspos, centerPosition.y, 0f);
            rightTransform.localScale = new Vector3(1f, 1f, 1f);

            waveRight.GameObject.GetComponent&amp;lt;AttackTriggerScript&amp;gt;().ChangeSetting(
                damage,
                3,
                new Vector2(1f, 8f),
                magiceffect,
                1,
                AttackType.Magic,
                AttackElement.Null,
                owner,
                0.5f
            );
            waveRight.GameObject.SetActive(true);
            pluspos += 1.5f;
            yield return new WaitForSeconds(intervalTime);
        }
    }


    private int GetFinalStack(List&amp;lt;float&amp;gt; parameters, float multiplier)
    {
        float baseStack = parameters.Count &amp;gt; 0 ? parameters[0] : 1f;
        float finalValue = baseStack * multiplier;

        if (finalValue &amp;gt;= 1f)
        {
            return Mathf.RoundToInt(finalValue);
        }
        else
        {
            return UnityEngine.Random.value &amp;lt; finalValue ? 1 : 0;
        }
    }
    public void ApplyEffectWithMultiplier(Entity owner, Vector2 target, EventEffect effect, float multiplier)
    {
        Debug.Log($&quot;Vector2 - 이팩트 부여,{owner},{target},{effect},{multiplier}&quot;);
        switch (effect.effectType)
        {
            case EventEffect.EffectType.AttackUp:
                {
                    owner.entityStatusManager.ApplyEffect(StatusEffectType.AttackUp,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.DefenseUp:
                {
                    owner.entityStatusManager.ApplyEffect(StatusEffectType.DefenseUp,
                        GetFinalStack(effect.parameters, multiplier));
                    break;
                }
            case EventEffect.EffectType.Shield:
                owner.AddShield((effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] : 50f) * multiplier);
                break;

            case EventEffect.EffectType.Explosion:
                float hDmga = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] * owner.statObject.FinalAttackPower : 5f) * multiplier;
                float hRada = (effect.parameters.Count &amp;gt; 2 ? effect.parameters[1] : 1f);
                IPoolable hb = PoolManager.Instance.Pop(&quot;AttackHitBox&quot;);

                hb.GameObject.GetComponent&amp;lt;AttackTriggerScript&amp;gt;().ChangeSetting(hDmga, 3, new Vector2(10, 10), magiceffect, 5f, AttackType.Magic, AttackElement.Null, owner, 0.5f);
                Vector3 basePosEx = target;
                hb.GameObject.transform.position = new Vector3(basePosEx.x, basePosEx.y, basePosEx.z);
                bool isPlayerOwnera = (owner.Faction == &quot;Player&quot;);
                string waveKeya = isPlayerOwnera ? &quot;Explosion&quot; : &quot;EnemyExplosion&quot;;
                IPoolable meff = PoolManager.Instance.Pop(waveKeya);
                meff.GameObject.transform.position = new Vector3(basePosEx.x, basePosEx.y, basePosEx.z);
                meff.GameObject.transform.localScale = new Vector3(hRada, hRada, hRada);
                hb.GameObject.transform.localScale = new Vector3(hRada, hRada, hRada);
                hb.GameObject.SetActive(true);
                meff.GameObject.SetActive(true);
                impulseSource.GenerateImpulse(hRada / 7);
                break;
            case EventEffect.EffectType.HeatWave:
                {
                    Vector2 spawnPos;
                    if (TryGetGroundPosition(target + Vector2.up * 1f, out spawnPos))
                    {
                        float hDmg = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] * owner.statObject.FinalAttackPower : 0.2f) * multiplier;
                        float hDur = (effect.parameters.Count &amp;gt; 1 ? effect.parameters[1] : 0.2f);
                        float hRange = (effect.parameters.Count &amp;gt; 2 ? effect.parameters[2] : 1f);
                        IPoolable heatWave;
                        bool isPlayerOwner = (owner.Faction == &quot;Player&quot;);
                        string waveKey = isPlayerOwner ? &quot;HeatWave&quot; : &quot;EnemyHeatWave&quot;;
                        heatWave = PoolManager.Instance.Pop(waveKey);
                        heatWave.GameObject.GetComponent&amp;lt;HeatWave&amp;gt;().OnSetting(hDmg, hDur, owner, magiceffect);
                        heatWave.GameObject.transform.position = spawnPos + new Vector2(UnityEngine.Random.Range(-hRange, hRange), 0);

                        heatWave.GameObject.SetActive(true);
                    }
                    break;
                }

            case EventEffect.EffectType.Wave:
                {
                    Vector2 spawnPos;
                    if (TryGetGroundPosition((Vector2)target + Vector2.up * 1f, out spawnPos))
                    {
                        float wDmg = (effect.parameters.Count &amp;gt; 0 ? effect.parameters[0] * owner.statObject.FinalAttackPower : 1f) * multiplier;
                        float wIntervalTime = (effect.parameters.Count &amp;gt; 1 ? effect.parameters[1] : 0.3f);
                        int wCount = (effect.parameters.Count &amp;gt; 2 ? (int)effect.parameters[2] : 5);
                        float wSpeed = (effect.parameters.Count &amp;gt; 3 ? effect.parameters[3] : 1f);

                        StartCoroutine(SpawnWave(owner, spawnPos, wDmg, wIntervalTime, wCount, wSpeed));
                    }
                    break;
                }

        }
    }

    public bool HasBuff(Entity owner, Entity target, Statuseffect buffSample)
    {
        return target.statusEffects.Exists(buff =&amp;gt; buff.name == buffSample.name);
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인챈트로 인한 효과 발생 등을 관리하는 스크립트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인챈트와 관련된 정보(발생하는 행동, 발생하는 행동으로 인챈트 발동 시 인챈트의 위력 증가 비율, 효과)들도 이곳에 작성되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 인챈트 발동 시 주 조건 &amp;gt; 보조 조건 &amp;gt; 효과 순으로 진행하며 각자 조건에 맞지 않을 경우 발동을 취소하고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 둘 다 만족되있는 경우 target으로 받은 값이 Vector인지, Target인지에 따라 각자 다른 효과 발동 방식(디버프, 흡혈, 충격은 발동되지 않으며. 파동, 열파,폭발은 target일 경우 target의 위치, Vector일 경우 Vector 값에서 발생하는 형식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로는 그나마 괜찮다고 느껴지지만 코드 가독성이 매우 떨어졌기 때문에 앞으로는 반드시 명심해야 할 요소인 것 같습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;넣지 못한 방들&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 아브락사스는 여러가지 방을 중심으로 개발한 작품이다 보니까 여러 개의 방이 존재할 예정이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나. 생각보다 효과 개발이 극도로 늦어지면서 결국 상점, 일반 방 외엔 특색없는 방들밖에 남지 않게 되었고. 전체적으로 꽤나 아쉬운 게임성이 되어버렸죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 분배와 능력 부족으 인해 꽤나 괜찮은 방들도 있었지만 빛을 보지 못했던 것이 너무 아쉽습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;775&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nrbdh/btsPN45CZUP/xF2Bqkg5khSXO5C1jpL7j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nrbdh/btsPN45CZUP/xF2Bqkg5khSXO5C1jpL7j1/img.png&quot; data-alt=&quot;원래는 일반 방도 층수에 따라 다양한 형태가 존재하도록 할 예정이였습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nrbdh/btsPN45CZUP/xF2Bqkg5khSXO5C1jpL7j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNrbdh%2FbtsPN45CZUP%2FxF2Bqkg5khSXO5C1jpL7j1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;775&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;775&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원래는 일반 방도 층수에 따라 다양한 형태가 존재하도록 할 예정이였습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLdh8r/btsPO9rRXzF/zIQ1fAkxRmhngtcEHVqTk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLdh8r/btsPO9rRXzF/zIQ1fAkxRmhngtcEHVqTk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLdh8r/btsPO9rRXzF/zIQ1fAkxRmhngtcEHVqTk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLdh8r%2FbtsPO9rRXzF%2FzIQ1fAkxRmhngtcEHVqTk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;451&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UMbMZ/btsPOEez5qZ/Tjk1ILEAeEeBekkvEsx4t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UMbMZ/btsPOEez5qZ/Tjk1ILEAeEeBekkvEsx4t0/img.png&quot; data-alt=&quot;넣으려고 했었던 방들. 이것들은 아예 요소 하나 완성할 시간도 부족하였기 떄문에 폐기하였다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UMbMZ/btsPOEez5qZ/Tjk1ILEAeEeBekkvEsx4t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUMbMZ%2FbtsPOEez5qZ%2FTjk1ILEAeEeBekkvEsx4t0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;472&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;넣으려고 했었던 방들. 이것들은 아예 요소 하나 완성할 시간도 부족하였기 떄문에 폐기하였다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 스텟 상승 아이템 시스템이 굉장히 난잡하게 이루어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 인챈트 발생 스크립트는 몇개를 스크립터블 오브젝트로 대체하였으면 더 좋았을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 평가 : 4점/10점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>siriussquare</author>
      <guid isPermaLink="true">https://siriussquare.tistory.com/2</guid>
      <comments>https://siriussquare.tistory.com/2#entry2comment</comments>
      <pubDate>Sun, 10 Aug 2025 21:19:25 +0900</pubDate>
    </item>
  </channel>
</rss>