0512 - <팀 프로젝트 3일차> Unity부트캠프 [23일차]

Hyeon O·2025년 5월 12일

Unity_BootCamp 6주차

목록 보기
1/5

📚 오늘은….

기존에 진행하였던 프로젝트에서, 추가 요청사항과 팀원들의 코드에 맞춰서 리펙토링 및 추가 메소드를 구현하였다.

💡오늘 진행한 프로젝트 과정

이전에 구현한 무기는 충돌 또는 타격한 오브젝트에 대해서 데미지값에 대한 로직이 없는 상태였다.

팀원과 회의 결과 데미지에 대한 처리를 타격을 받는 대상에서 처리하기로 하였고

무기에서는 공격력에 대한 값을 보유하고 이를 타격 대상이 가져갈 수 있게 하기로 했다.

근접무기는 다음과 같이 수정하였다.

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

public class MeleeWeapon : BaseWeapon
{

    [Header("근접 공격 범위 오프셋")]
    [SerializeField] private Vector2 hitboxSize = Vector2.one;
    [SerializeField] private Vector2 hitboxOffset = Vector2.zero;

   public StatController Owner; 
    
    private float lastAttackTime;
    protected override void Start()
    {
        base.Start();
        lastAttackTime = -Mathf.Infinity;
        animator = GetComponentInChildren<Animator>();

        Owner = GetComponentInParent<StatController>();

    }
		//총 공격력을 반환하는 메소드
		//플레이어나 몬스터 측에서 충돌 시 위 메소드 호출로 데미지값을 받아온다.
    public float GetAttackPower()
    {
        float Total = data.attackPower + Owner.Atk;
        return Total;
    }

    public override void Attack(Vector3 v)
    {
        float cooldown = 1f / AtkSpeed;
        if (Time.time < lastAttackTime + cooldown)
            return;

        lastAttackTime = Time.time;
        AttackAnimation();
        animator.SetTrigger("IsAttack");

        // 공격 방향에 따라 히트박스 회전
        Vector2 dir = ((Vector2)v - (Vector2)transform.position).normalized;
        float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
        Vector2 center = (Vector2)transform.position + hitboxOffset;
        Quaternion rotation = Quaternion.Euler(0, 0, angle);

        //// BoxCast로 충돌 검사, 또는 OverlapBox 사용
        //RaycastHit2D[] hits = Physics2D.BoxCastAll(
        //    center,
        //    hitboxSize * WeaponSize,
        //    angle,
        //    Vector2.zero,
        //    0f,
        //    targetLayerMask
        //);

        //foreach (var hit in hits)
        //{
        //    //if (hit.collider != null && hit.collider.TryGetComponent<IDamageable>(out var dmg))
        //    //{
        //    //    dmg.TakeDamage(AtkPower);
        //    //}
        //}

        // (선택) 데미지 이펙트나 사운드 재생 가능
    }
   
}

원거리는 조금 로직을 생각해야 했다.

현재 원거리 공격은

활에서 ProjectileManager 화살을 생성하도록 지시하고

ProjectileManager에서 Projectile의 투사체 생성 메소드를 통해 투사체를 생성, 발사한다.

여기저 공격력은

쏜 물체의 기본 공격력 + 활의 공격력 + 화살의 공격력의 합이어야 한다.

따라서 발사한 주체의 공격력과 활의 공격력을

ProjectileManager의 SpawnProjectile() 메소드에 파라미터를 추가하여

ProjectileController 의 Initialize메소드 파라미터로 넘겨주어서,

ProjectileController (화살 오브젝트가 가지는 컴포넌트)에 총 공격력 값에 화살 공격력을 추가하여

메소드를 통해 반환하도록 구현하였다.

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

public class RangeWeapon : BaseWeapon
{
	 //...생략
    public StatController Owner;
    public float totalatk_OwnerAndWeapon;

    //private ProjectileManager projectileManager;
    
    protected override void Start()
    {
        base.Start();
        //projectileManager = ProjectileManager.Instance;

        lastAttackTime = -Mathf.Infinity; //첫 공격이 즉시 가능하도록
        Owner = GetComponentInParent<StatController>();
        
        //화살 + 쏜 대상의 공격력
        totalatk_OwnerAndWeapon = r_data.attackPower + Owner.Atk;
    }

    public override void Attack(Vector3 targetPosition) //위치를 파라미터로 받아와서 공격
    {
        float cooldown = 1f / r_data.attackSpeed;
        if (Time.time < lastAttackTime + cooldown)
            return;
        lastAttackTime = Time.time;

        // 활 회전: 타겟을 정확히 바라보도록 transform 회전
        Vector2 toTarget = (Vector2)targetPosition - (Vector2)transform.position;
        float bowAngle = Mathf.Atan2(toTarget.y, toTarget.x) * Mathf.Rad2Deg;
        transform.rotation = Quaternion.Euler(0f, 0f, bowAngle);
        base.Attack(targetPosition);

        int count = r_data.multiShotCount;
        float angleStep = r_data.multiShotAngle;
        float startAngle = -(count - 1) / 2f * angleStep;

        
        Vector2 spawnPos = projectileSpawnPoint.position;
        
        float baseZ = Mathf.Atan2(toTarget.y, toTarget.x) * Mathf.Rad2Deg;

        for (int i = 0; i < count; i++)
        {

            float localAngle = startAngle + angleStep * i;
            float zAngle = baseZ + localAngle;

            // 최종 발사 방향 계산
            Vector2 dir = Quaternion.Euler(0f, 0f, zAngle) * Vector2.right;

            ProjectileManager.Instance.SpawnProjectile(
                r_data.projectileData,
                spawnPos,
                dir,
                totalatk_OwnerAndWeapon //파라미터 추가
            );
        }
    }
}
//---------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProjectileManager : MonoBehaviour
{
    //발사체 발사 매니저(발사체 생성과 생성할 발사체 프리팹 설정

   
    public static ProjectileManager Instance { get; private set; }
    [SerializeField] private ParticleSystem impactParticleSystem; //파티클 임시

    private RangeWeapon b;//임시
    public Transform dummyTarget;//테스트용 타겟 오브젝트

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else Destroy(gameObject);
    }

    //투사체 생성
    public ProjectileController SpawnProjectile(ProjectileData data, Vector2 position, Vector2 direction, float atk)
    {
        var go = Instantiate(data.prefab, position, Quaternion.identity);
        var ctrl = go.GetComponent<ProjectileController>();
        ctrl.Initialize(data, direction, atk); //파라미터 추가
        return ctrl;
    }

}
//--------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//발사체 충돌과 파괴 처리
public class ProjectileController : MonoBehaviour
{
    [SerializeField] private ProjectileData data;
    private Vector2 direction;
    private float elapsedTime;

    private Rigidbody2D rb;
    private SpriteRenderer sr;

    public float TotalAtk;
    
    /// <summary>
    /// 발사체를 초기화합니다.
    /// </summary>
    /// <param name="data">ScriptableObject로 정의된 발사체 데이터</param>
    /// <param name="direction">발사 방향 (정규화된 벡터)</param>
    public void Initialize(ProjectileData data, Vector2 direction, float totalatk)
    {
        this.data = data;
        this.direction = direction.normalized;
        this.elapsedTime = 0f;
        
        //final_Attack += 무기공격력 + 부모의 공격력
        rb = GetComponent<Rigidbody2D>();
        sr = GetComponentInChildren<SpriteRenderer>();
        
        //총 공격력 계산
        TotalAtk = totalatk;
        // 발사체 색상 설정
        if (sr != null)
            sr.color = data.Color;

        // 회전을 통해 방향 맞추기
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        transform.rotation = Quaternion.Euler(0f, 0f, angle);
    }
		//플레이어나 몬스터에서 호출하여 사용
    public float GetAttackPower()
    {
        return TotalAtk + data.attackPower;
    }

    private void Update()
    {
        if (data == null) return;

        elapsedTime += Time.deltaTime;
        // 수명 경과 시 파괴, 수명은 화살 데이터에서
        if (elapsedTime >= data.lifetime)
        {
            DestroyProjectile(transform.position);
            return;
        }

        // 이동
        rb.velocity = direction * data.moveSpeed;
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
    }

    //임시 코드, 맞았을 경우 이펙트 효과
    private void DestroyProjectile(Vector3 hitPosition)
    {
        // 충돌 이펙트 생성
        if (data.impactEffect != null)
            Instantiate(data.impactEffect, hitPosition, Quaternion.identity);

        Destroy(gameObject);
    }
}

💭오늘의 회고

유니티에서 팀으로 작업을 진행하니

좋은 경험이 되는 팀 프로젝트인 것 같다.

아직은 생각했던 것보다 분업이 애매하고 어려운 부분이 많다

profile
천천히, 꾸준하게, 끝까지

0개의 댓글