[25.05.08] TIL( 팀 프로젝트 - 공격 모듈 )

설민우·2025년 5월 9일

내일배움캠프 - Unity

목록 보기
38/85

이번에 자동 공격 파트를 맡게 되면서 아이템을 습득 할때 공격 특징이 바뀌고, 이것들이 조합되는 특징을 가질 수 있도록 공격 변경에 확장성을 띌 수 있도록 코드를 작성해야 했습니다.
이를 위해서 Module 형식으로 공격 특성을 붙일 수 있도록 코드를 작성해보았습니다.

0. Item

아이템은 각각 모듈 리스트를 가지고 있으면서 필요 할때 해당 리스트를 반환해 현재 적용되고 있는 모듈을 알려줍니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Item/Item Data")]
public class ItemData : ScriptableObject
{
    public string ItemName;
    public Sprite Icon;

    public List<ProjectileModuleData> ProjectileModules;
    public List<FireModuleData> FireModules;
    public List<StatModuleData> StatModules;
    
}

1. ProjectileModule

투사체에 직접 붙은 모듈로써, 관통, 유도등의 투사체에 반영되는 모듈입니다.

public interface IProjectileModule 
{
    void OnFire(Projectile projectile);
    void OnUpdate(Projectile projectile);
}
using UnityEngine;

[CreateAssetMenu(menuName ="Projectile/Module data")]
public class ProjectileModuleData : ScriptableObject
{
    public string ModuleName;
    public GameObject ModulePrefab;// 반드시 IProjectileModule이 붙어야함
}
using System.Collections;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;

public class HomingModule : MonoBehaviour, IProjectileModule
{
    private Transform _target;
    public void OnFire(Projectile projectile)
    {
        _target = projectile.Target;
    }

    public void OnUpdate(Projectile projectile)
    {
        if (_target == null) return;

        Vector3 dir = (_target.position - projectile.transform.position).normalized;
        Quaternion rotate = Quaternion.LookRotation(Vector3.forward, dir);
        projectile.transform.rotation = Quaternion.Lerp(
            projectile.transform.rotation, rotate, Time.deltaTime * projectile.Speed);
    }

}
  • 위와 같은 방식으로 ScriptableObject를 이용하여 데이터를 저장하고, 여기에서의 정보를 가져와서 HomingModule에서 사용하도록 하였습니다.

2. FireModule

FireModule은 발사시에 반영되는 모듈로써, 투사체를 3갈래로 발사한다던가, 커다란 투사체를 발사하는 등의 투사체에 변경을 주게 됩니다.

using UnityEngine;

public abstract class FireModuleData : ScriptableObject
{
    public abstract IFireModule CreateInstance();
}
using UnityEngine;

public interface IFireModule 
{
    void OnFire(AttackController attack, Transform enemy);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "FireModules/Spread Shot")]
public class SpreadShotData : FireModuleData
{
    public float Angle = 15f;
    public int ProjCount;

    public override IFireModule CreateInstance()
    {
        return new SpreadShotModule(this);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpreadShotModule : MonoBehaviour, IFireModule
{
    private SpreadShotData _spreadShotData;

    public SpreadShotModule(SpreadShotData data)
    {
        _spreadShotData = data;
    }

    public void OnFire(AttackController attack, Transform enemy)
    {
        int middle = _spreadShotData.ProjCount / 2;

        // 중심 발사 방향 계산 (타겟을 향한 방향)
        Vector2 dir = (enemy.position - attack.AttackPivot.transform.position).normalized;
        float baseAngle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f; // Z축 기준

        for (int i = 0; i < _spreadShotData.ProjCount; i++)
        {
            float offset = (i - middle) * _spreadShotData.Angle;
            Quaternion rotation = Quaternion.Euler(0, 0, baseAngle + offset);

            GameObject go = GameObject.Instantiate(
                attack.ProjectilePrefab,
                attack.AttackPivot.transform.position,
                rotation
            );

            var proj = go.GetComponent<Projectile>();
            proj.Init(attack.StatHandler, enemy, attack.ItemEffectManager.GetProjModules());
        }
    }

}
  • 위와 같이 똑같이 Item에서 반영할 수 있도록 Data와 Module로 구성되어 있고, Onfire를 PlayerController에서의 발사체 생성시에 발동해주면 발사 관련된 모듈이 적용됩니다.

처음 생각했을 때는 이렇게 특징이 붙어져 나오는 탄환들을 모두 Bool 로 관리해야 하나 했지만 이렇게 Module 형식으로 코드를 작성하여 붙였더니 통합해서 하나의 화살 모양을 보여주는 것이 매우 쉬워졌습니다.

profile
클라이언트 개발자를 지망하고 있습니다.

0개의 댓글