[Unity / C#] 아이템 기능 만들기 #2 - 제트팩

주예성·2025년 7월 2일

📋 목차

  1. 제트팩 장착
  2. 날아오르기
  3. 중간 결과
  4. UI 표시
  5. 최종 결과
  6. 오늘의 배운 점
  7. 다음 계획

🚀 제트팩 장착

제트팩은 등 뒤에 장착하겠습니다.
Player에게 BackSocket이라는 빈 오브젝트를 등에 달아줍니다. 그리고 PlayerItemHandler에서 관리합시다.

// PlayerItemHandler.cs

[HideInInspector] public ItemInstance backSocketItem;

[Header("Socket")]
public Transform backSocket;

public void SetBackSocket(ItemInstance item)
{
    if (!item || item == backSocketItem) return;
    backSocketItem = item;
    SetActiveCurrentItem(backSocketItem, backSocket);
}

// 본래 있던 함수를 변형함
// 다른 Bone에도 붙일 수 있게끔 item과 해당 위치의 transform을 인수에 둠
private void SetHoldingItem(ItemInstance item, Transform transform)
{
    if (!item) return;

    item.transform.position = transform.position;
    item.transform.rotation = transform.rotation;

    item.transform.SetParent(transform);
    item.transform.localPosition = Vector3.zero;
    item.transform.localRotation = Quaternion.identity;

    Rigidbody rb = item.GetComponent<Rigidbody>();
    if (rb) rb.isKinematic = true;

    Collider[] colliders = item.GetComponents<Collider>();
    foreach (Collider collider in colliders)
    {
        collider.enabled = false;
    }

    item.gameObject.SetActive(true);
}
// UseItem.cs

public void UseItems(ItemInstance item)
{
    switch(item.itemData.itemName)
    {
        case "스캐너":
            if (Input.GetMouseButtonDown(1)) UseScanner();
            break;
        case "나이프":
            if (Input.GetMouseButtonDown(0)) UseKnife(item.itemData.attackPower);
            break;
        case "제트팩":
            if (Input.GetMouseButtonDown(1)) UseJetPack(item);
            break;
        default:
            return;
    }
}

private void UseJetPack(ItemInstance item)
{
    if (chargeUIOpen) return;
    playerItemHandler.SetBackSocket(item);
}

우클릭을 누르면 플레이어의 등에 장착하게 됩니다.


🚀 날아오르기

이 게임에서 제트팩의 효과는 점프가 더 높이되며, 내려올때 천천히 내려와 낙하데미지를 입지 않는 것입니다. JetpackController 스크립트를 생성합시다.

// JetpackController.cs

using UnityEngine;

public class JetpackController : MonoBehaviour
{
    [Header("Jetpack Settings")]
    public float jetpackForce = 15f;        // 제트팩 추진력
    public float maxAltitude = 50f;         // 최대 고도
    public float gravityScale = 1f;         // 중력 영향

    private ItemInstance jetPack;
    private Rigidbody rb;
    private float groundLevel;              // 지면 높이
    private bool isUsingJetpack;
    private GameObject player;

    private float foolPowerTime = 5f;		// 허공에 머물러 있을 수 있는 시간
    private float currentPowerTime;

    void Start()
    {
        player = GameObject.Find("Player");
        jetPack = GetComponent<ItemInstance>(); 
        rb = player.GetComponent<Rigidbody>();
        groundLevel = player.transform.position.y;  // 시작 지점을 지면으로 설정
        currentPowerTime = foolPowerTime;
    }

    void Update()
    {
        Debug.Log($"현재 제트팩 가능 게이지 : {currentPowerTime}");
        bool isCan = false;
        if (Input.GetKey(KeyCode.Space))
        {
        	// 허공에 있을 수 있는 게이지가 모두 닳아버릴 경우 천천히 떨어짐
            if (currentPowerTime > 0)
            {
                currentPowerTime -= Time.deltaTime; // 누르고 있을 때는 게이지가 줄어듬
                isCan = CanUseFuelProduct(jetPack);
            }
            isUsingJetpack = isCan;
        }
        else // 누르고 있지 않을 때는 게이지가 느리게 채워짐
        {
            if (currentPowerTime <= foolPowerTime)
            {
                currentPowerTime += Time.deltaTime / 3;
            }
            isUsingJetpack = false;
        }
    }

    private void FixedUpdate()
    {
        if (isUsingJetpack)
        {
            UseJetpack();
        }
    }

    private bool CanUseFuelProduct(ItemInstance jetPack)
    {
        ItemInstance fuel = jetPack.Get<ItemInstance>("Fuel");
        if (!fuel)
        {
            Debug.Log("현재 연료통이 존재하지 않습니다!");
            return false;
        }
        float rate = fuel.Get<float>("fuelUsageRate");
        if (rate <= 0)
        {
            Debug.Log("연료가 모두 소진되었습니다!");
            return false;
        }
        rate -= Time.deltaTime;
        fuel.Set<float>("fuelUsageRate", rate);
        return true;
    }

    void UseJetpack()
    {
    	// 현재 고도
        float currentAltitude = player.transform.position.y - groundLevel;

        if (currentAltitude < maxAltitude)
        {
        	// 만약 있을 수 있는 고도면 위로 날 수 있게
            rb.AddForce(Vector3.up * jetpackForce, ForceMode.Force);
        }
        else
        {
        	// 아니라면 더이상 올라가지 못함
            rb.AddForce(Vector3.up * (jetpackForce * 0.3f), ForceMode.Acceleration);
        }
    }
}

🎮 중간 결과

플레이어가 공중에서 잠시 뜰 수 있게 됩니다. 고도 제한과 시간 등의 설정은 나중에 밸런스를 맞추도록 합시다.


🎨 UI표시

제트팩은 현재 연료량과 날 수 있는 제한 시간에 대한 UI 표시가 필요합니다. HUD_Panel에서 JetpackBar의 빈 오브젝트를 생성하고, 다른 HUD와 비슷하게 설정할겁니다.
Background_Bar(Image), Fill_Bar(Image, FuelRate(Text)란 자식 오브젝트를 생성합시다.

UIManager에서 편집하도록 합시다.

// UIManager.cs

[Header("Bar")]
public GameObject jetpackBar;
public Image jetpackGaugeBar;

[Header("Text")]
public Text jetPackRateText;

private UseItem useItem;

void Start()
{
	// 기존 코드...
    if (player)
    {
    	useItem = player.GetComponent<UseItem>();
    }
    // 기존 코드...
}

public void UpdateJetpackUI(ItemInstance jetpack, float time, float foolTime)
{
    if (!jetpackBar || !jetpackGaugeBar || !jetpackRateText || !jetpack) return;

    JetpackController jetpackController = jetpack.GetComponent<JetpackController>();
    jetpackBar.SetActive(true);

    jetpackGaugeBar.fillAmount = time / foolTime;
    
    ItemInstance fuel = jetpack.Get<ItemInstance>("Fuel");
    int displayRate = 0;

    if (fuel)
    {
        displayRate = Mathf.Max(0, (int)fuel.Get<float>("fuelUsageRate"));
        Debug.Log(jetpack.Get<float>("fuelUsageRate"));
        jetpackRateText.text = displayRate.ToString() + "%";
    }
    else
    {
        jetpackRateText.text = "None";
    }
}
// JetpackController.cs

void Update()
{
	// 기존 코드...
    if (uiManager)
    {
    	uiManager.UpdateJetpackUI(jetPack, currentPowerTime, foolPowerTime);
    }
    // 기존 코드...
}

🎮 최종 결과


📚 오늘의 배운 점

  • Rigidbody를 이용한 비행
  • 플레이어의 몸에 아이템 장착

🎯 다음 계획

다음 글에서는:

  1. 현재 장착 아이템을 인벤토리에 표시
  2. 더블클릭으로 탈착 구현
profile
Unreal Engine & Unity 게임 개발자

0개의 댓글