Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

1vdlrwnsv1 님의 블로그

무기 교체, 아이템 장착 시스템 구현 본문

Unity 최종

무기 교체, 아이템 장착 시스템 구현

1vdlrwnsv1 2025. 4. 17. 20:41
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class WeaponChangeHandler : MonoBehaviour
{
    public PlayerEquipment playerEquipment;
    [SerializeField] private GameObject currentWeapon;
    void Update()
    {
        Ray ray = playerEquipment.playerCam.ScreenPointToRay(Input.mousePosition);
        
        if (Physics.Raycast(ray, out RaycastHit hit, 100f) && hit.collider.CompareTag("Gun"))
        {
            WeaponSelector selector = hit.collider.GetComponent<WeaponSelector>();

            if (Input.GetKeyDown(KeyCode.E))
            {
                if (selector != null)
                {
                    // 기존 무기 다시 바닥에 보여줌
                    if (currentWeapon != null)
                        currentWeapon.SetActive(true);

                    // 새 무기 들고, 인덱스로 무기 변경
                    currentWeapon = selector.gameObject;
                    playerEquipment.SwitchWeapon(selector.weaponIndex);

                    // 현재 바닥 무기 숨김 (플레이어 손으로 간다고 가정)
                    selector.gameObject.SetActive(false);
                }
            }
        }
    }
}​
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.PlayerLoop;
using UnityEngine.UI;

public class PlayerEquipment : MonoBehaviour
{
    public GameObject[] weaponPrefabs; // 무기 프리팹 배열 (인덱스로 선택)
    private GameObject currentWeaponObject; // 현재 들고 있는 무기 오브젝트
    private int currentWeaponIndex = -1;

    public Transform handransform; // 무기를 들 위치
    public Transform camRoot;      // 카메라 루트 (조준용 위치 이동에 사용)
    public Camera playerCam;       // 플레이어 카메라
    public FpsCamera fpsCamera;    // 커스텀 FPS 카메라 (흔들림 등 제어)
    public GameObject playerObject;// 플레이어 오브젝트
    public Text bulletStatText;    // 탄약 표시용 UI

    public void SwitchWeapon(int index)
    {
        // 인덱스가 범위 밖이거나 이미 들고 있는 무기라면 리턴
        if (index < 0 || index >= weaponPrefabs.Length || index == currentWeaponIndex)
            return;

        // 기존 무기 제거
        if (currentWeaponObject != null)
            Destroy(currentWeaponObject);

        // 새 무기 생성 및 장착 위치에 붙임
        currentWeaponObject = Instantiate(weaponPrefabs[index], handransform, false);
        currentWeaponIndex = index;

        // 무기 스탯 핸들러 세팅
        var handler = currentWeaponObject.GetComponent<WeaponStatHandler>();
        if (handler != null)
        {
            handler.SetSharedReferences(handransform, camRoot, playerCam, fpsCamera, playerObject, bulletStatText);

            var fireController = currentWeaponObject.GetComponent<WeaponFireController>();
            if (fireController != null)
                fireController.InitReferences(); // 발사/재장전/조준 등 기능 초기화
        }
    }
}
  • SwitchWeapon(int index)를 통해 무기를 교체하고,
  • 무기 생성 후 WeaponStatHandler와 WeaponFireController에 공통 참조를 넘겨서 정상 작동하게 만듦.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class WeaponStatHandler : MonoBehaviour
{
    [Header("Weapon Data (SO)")]
    public WeaponSO weaponData;

    [Header("Weapon State")]
    public bool isReloading = false;
    public bool isADS = false;

    [Header("Weapon Stat")]
    public string ID;
    public string Name;
    public string Description;
    public float ShootRecoil;
    public float DMG;
    public float ReloadTime;
    public int MaxAmmo;
    public int Cost;

    [Header("Transforms")]
    public Transform barrelLocation;
    public Transform casingExitLocation;
    public Transform handransform;
    public Transform camRoot;
    public Camera playerCam;
    public FpsCamera fpsCamera;
    public GameObject playerObject;
    public Text bulletStatText;

    [Header("Prefabs")]
    public List<ItemSO> equippedParts = new List<ItemSO>();
    public GameObject casingPrefab;
    public GameObject muzzleFlashPrefab;
    public GameObject bulletImpactPrefab;

    [Header("PartsPrefabs")]
    public GameObject redDot;
    public GameObject holographic;
    public GameObject laserPointer;
    public GameObject compensator;

    [Header("Settings")]
    [Range(0f, 20f)]
    public float spreadAngle = 10.5f;
    public float destroyTimer = 2f;
    public float ejectPower = 150f;
    public float fireCooldown = 0.7f;

    [Header("ADS Settings")]
    public Vector3 adsPosition = new Vector3(0.062f, -0.007f, 0f);
    public float camMoveSpeed = 10f;

    [Header("Animator")]
    public Animator gunAnimator;

    [Header("Sound Container")]
    public AudioClip fireSound;
    public AudioClip reloadSound;
    public AudioClip emptySound;

    [Header("Item Stat")]
    public float itemRecoil = 0;

    [HideInInspector] public Vector3 camRootOriginPos;
    [HideInInspector] public Quaternion initialLocalRotation;
    [HideInInspector] public float lastFireTime = 0f;
    public Action<int, int> onAmmoChanged;

    public void WeaponDataFromSO() // ScriptableObject로부터 스탯 불러오기
    {
        if (weaponData == null)
        {
            Debug.Log("WeaponSO없음");
        }

        ID = weaponData.ID;
        Description = weaponData.Description;
        ShootRecoil = weaponData.ShootRecoil;
        DMG = weaponData.DMG;
        ReloadTime = weaponData.ReloadTime;
        MaxAmmo = weaponData.MaxAmmo;
        Cost = weaponData.Cost;
    }

    // PlayerEquipment에서 참조 넘겨받음
    public void SetSharedReferences(Transform hand, Transform camRoot, Camera cam, FpsCamera fps, GameObject player, Text bulletText)
    {
        this.handransform = hand;
        this.camRoot = camRoot;
        this.playerCam = cam;
        this.fpsCamera = fps;
        this.playerObject = player;
        this.bulletStatText = bulletText;
    }

    public Transform GetHandTransform() => handransform;
    public Transform GetCamRoot() => camRoot;
    public Camera GetPlayerCamera() => playerCam;
    public FpsCamera GetFpsCamera() => fpsCamera;
    public GameObject GetPlayerObject() => playerObject;

    // 조준경/부착물 교체용 파츠 장착
    public void EquipItem(ItemSO item)
    {
        if (!equippedParts.Contains(item))
        {
            equippedParts.Add(item);
            ApplyItemStats(item);
        }
    }

    public void UnEquipItem(ItemSO item)
    {
        equippedParts.Remove(item);
        RemoveItemStats(item);
    }

    private void ApplyItemStats(ItemSO item) // 아이템 효과 반영
    {
        DMG += item.DMG;
        MaxAmmo += item.MaxAmmo;
        ShootRecoil *= 1f - (item.ShootRecoil * 0.01f);
    }

    private void RemoveItemStats(ItemSO item)
    {
        DMG -= item.DMG;
        MaxAmmo -= item.MaxAmmo;
        ShootRecoil /= 1f - (item.ShootRecoil * 0.01f);
    }
    /// <summary>
    /// 조준경 아닌 파츠 사용
    /// </summary>
    /// <param name="파츠이름F"></param>
    public void ToggleAttachment(GameObject attachment) // 파츠 토글
    {
        if (attachment == null) return;

        bool isActive = attachment.activeSelf;
        ItemReference itemRef = attachment.GetComponent<ItemReference>();

        if (!isActive)
        {
            EquipItem(itemRef.itemData);
        }
        else
        {
            UnEquipItem(itemRef.itemData);
        }

        attachment.SetActive(!isActive);

        if (itemRef == null || itemRef.itemData == null) return;
    }

    /// <summary>
    /// 조준경 파츠 사용
    /// </summary>
    /// <param name="조준경 이름"></param>
    public void ToggleOpticAttachment(GameObject selectedOptic) //조준경 토글
    {
        if (selectedOptic == null) return;

        // optics 그룹
        List<GameObject> optics = new List<GameObject> { redDot, holographic };

        foreach (var optic in optics)
        {
            if (optic == null) continue;
            if (optic == selectedOptic)
            {
                bool willBeActive = !optic.activeSelf;
                optic.SetActive(willBeActive);

                ItemReference itemRef = optic.GetComponent<ItemReference>();
                if (itemRef != null && itemRef.itemData != null)
                {
                    if (willBeActive)
                        EquipItem(itemRef.itemData);
                    else
                        UnEquipItem(itemRef.itemData);
                }
            }
            else
            {
                if (optic.activeSelf)
                {
                    optic.SetActive(false);
                    ItemReference itemRef = optic.GetComponent<ItemReference>();
                    if (itemRef != null && itemRef.itemData != null)
                        UnEquipItem(itemRef.itemData);
                }
            }
        }
    }
    public void BindToWeapon(WeaponFireController fireController)
    {
        onAmmoChanged += UpdateBulletText;
    }

    private void UpdateBulletText(int currentAmmo, int MaxAmmo)
    {
        bulletStatText.text = $"{currentAmmo} / {MaxAmmo}";
    }
}
  • 무기 스탯은 ScriptableObject에서 가져오고 (WeaponDataFromSO)
  • 부착 아이템은 EquipItem()에서 스탯에 반영함.
  • SetSharedReferences()로 카메라, 텍스트 UI 등 외부 참조를 받아 연결함.
  • EquipItem() 호출 시, 같은 그룹의 파츠가 있으면 자동 교체됨.
  • ApplyItemStats()로 스탯 반영, RemoveItemStats()로 제거 반영.
  • equipParts 딕셔너리로 그룹별 단일 장착 제어 가능.
public class WeaponSelector : MonoBehaviour
{
    public int weaponIndex;
}

 

 

무기마다 프리팹에 붙어서 자신이 몇 번째 무기인지 보관하는 역할