1vdlrwnsv1 님의 블로그
무기 교체, 아이템 장착 시스템 구현 본문
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;
}
무기마다 프리팹에 붙어서 자신이 몇 번째 무기인지 보관하는 역할
'Unity 최종' 카테고리의 다른 글
유니티 PlayerPref를 이용한 사운드 값, 마우스 감도 값 저장 불러오기 (0) | 2025.04.23 |
---|---|
옵션을 만들자 (0) | 2025.04.22 |
유니티 레드토드사이트 셰이더 만들기 (0) | 2025.04.14 |
Unity 총기 흔들림, 반동 구현, 사운드 처리 (0) | 2025.04.09 |
Unity SoundManager 구조와 활용 (0) | 2025.04.08 |