Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
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
관리 메뉴

동구의_C# & Unity_개발일지

2024.02.06 내일배움캠프 31일차 TIL_Unity (숙련, 알고리즘) 본문

Unity

2024.02.06 내일배움캠프 31일차 TIL_Unity (숙련, 알고리즘)

mongle_0l 2024. 2. 6. 17:45

내일은 개인 과제 제출일이다.

10시까지 완성은 다 못할 것같다..

 

필수요구사항

1. ATM 화면 구성 (완료)

2. 입금 기능 (미완료)

3. 출금 기능 (미완료)

 

선택요구사항

1. 통화 단위 적용 (난이도 - ★☆☆☆☆)

2. 금액 입력시 숫자만 입력 (난이도 - ★☆☆☆☆)

3. 로그인 기능 (난이도 - ★★☆☆☆)

4. 회원가입 (난이도 - ★★★★☆)

5. 송금 기능 (난이도 - ★★★★☆)


알고리즘 코드카타 17일차

하샤드 수

문제 설명
양의 정수 x가 하샤드 수이려면 x의 자릿수의 합으로 x가 나누어져야 합니다. 예를 들어 18의 자릿수 합은 1+8=9이고, 18은 9로 나누어 떨어지므로 18은 하샤드 수입니다. 자연수 x를 입력받아 x가 하샤드 수인지 아닌지 검사하는 함수, solution을 완성
public class Solution {
         public  bool solution(int x)
        {
            int tmp = x;
            int value = 0;
            bool answer = false;
            while (tmp > 0)
            {
                value += tmp % 10;
                tmp = tmp / 10;
            }

            if (x % value == 0)
                answer = true;

            return answer;
        }
}


3D 게임 기초 개발

낮과 밤 구현
아이템과 상호작용
인벤토리 만들기 & 아이템 사용하기
아이템 장착과 모션
자원 채취
스테미나 사용하기


01. 핵심 내용

1-1) AnimationCurve

AnimationCurve AnimationCurve는 Unity에서 애니메이션의 키프레임(Keyframe)을 사용하여 값을 보간(interpolate)하는데 사용되는 클래스입니다. 이 클래스를 사용하여 시간에 따라 값을 부드럽게 변화시키는 커브를 정의하고, 이를 기반으로 애니메이션을 만들 수 있습니다.
AnimationCurve클래스의 기본적인 구성 요소는 다음과 같습니다.

 

  • 키프레임(Keyframe): 시간에 따른 값을 정의하는 점을 의미합니다. 키프레임은 시간(t)과 해당 시간에 대응하는 값(value)으로 이루어집니다.
  • 보간 방식(Interpolation Mode): 인접한 키프레임 사이의 값을 보간하는 방법을 지정합니다. 기본적으로는 Cubic Bezier 보간이 사용되며, 선형, 스텝, 등 다양한 보간 방식을 선택할 수 있습니다.

주요한 AnimationCurve메서드 및 사용법은 다음과 같습니다.

1. AddKey:새로운 키프레임을 추가합니다.

using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    private AnimationCurve curve;

    private void Start()
    {
        // 새로운 AnimationCurve 생성
        curve = new AnimationCurve();

        // 키프레임 추가 (시간, 값)
        curve.AddKey(0f, 0f);
        curve.AddKey(1f, 1f);
    }

    private void Update()
    {
        // 시간에 따라 값을 보간하여 출력
        float time = Time.time;
        float value = curve.Evaluate(time);
        Debug.Log("Time: " + time + ", Value: " + value);
    }
}
  1. Evaluate: 특정 시간에 해당하는 값을 보간하여 반환합니다.
  2. keys: 키프레임의 배열을 가져옵니다. 이를 통해 키프레임들을 추가, 수정, 삭제할 수 있습니다.
AnimationCurve는 주로 애니메이션 시간에 따른 값을 정의하는데 사용되며, 특히 더 복잡한 애니메이션을 제어하기 위해 사용되는 경우가 많습니다. 예를 들어, 오브젝트의 움직임, 크기 조정, 회전 등에 대한 애니메이션을 정의하거나, 재미있는 게임 요소들에 활용할 수 있습니다.

중요한 기능들을 정리해서 알아보자!
인벤토리 기능 코드
더보기
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;


public class ItemSlot
{
    public ItemData item;
    public int quantity;
}

public class Inventory : MonoBehaviour
{
    public ItemSlotUI[] uiSlots;
    public ItemSlot[] slots;

    public GameObject inventoryWindow;
    public Transform dropPosition;

    [Header("Selected Item")]
    private ItemSlot selectedItem;
    private int selectedItemIndex;
    public TextMeshProUGUI selectedItemName;
    public TextMeshProUGUI selectedItemDescription;
    public TextMeshProUGUI selectedItemStatName;
    public TextMeshProUGUI selectedItemStatValue;
    public GameObject useButton;
    public GameObject equipButton;
    public GameObject unEquipButton;
    public GameObject dropButton;

    private int curEqiuipIndex;

    private PlayerController controller;
    private PlayerConditions condition;

    [Header("Events")]
    public UnityEvent onOpenInventory;
    public UnityEvent onCloseInventory;

    public static Inventory instance;

    private void Awake()
    {
        instance = this;
        condition = GetComponent<PlayerConditions>();
        controller = GetComponent<PlayerController>();
    }
   
    void Start()
    {
        inventoryWindow.SetActive(false);
        slots = new ItemSlot[uiSlots.Length];

        for (int i = 0; i < slots.Length; i++)
        {
            slots[i] = new ItemSlot();
            uiSlots[i].index = i;
            uiSlots[i].Clear();
        }

        ClearSeletecItemWindow();
    }

    public void OnInventorybutton(InputAction.CallbackContext callbackContext)
    {
        if(callbackContext.phase == InputActionPhase.Started)
        {
            Toggle();
        }
    }

    public void Toggle()
    {
        if (inventoryWindow.activeInHierarchy)
        {
            inventoryWindow.SetActive(false);
            onCloseInventory?.Invoke();
            controller.ToggleCursor(false);
        }
        else
        {
            inventoryWindow.SetActive(true);
            onCloseInventory?.Invoke();
            controller.ToggleCursor(true);
        }
    }

    public bool IsOpen()
    {
        return inventoryWindow.activeInHierarchy;
    }

    public void AddItem(ItemData item)
    {
        if(item.canStack)
        {
            ItemSlot slotToStackTo = GetItemStack(item);
            if(slotToStackTo != null)
            {
                slotToStackTo.quantity++;
                UpdateUI();
                return;
            }
        }

        ItemSlot emptySlot = GetEmptySlot();

        if(emptySlot != null)
        {
            emptySlot.item = item;
            emptySlot.quantity = 1;
            UpdateUI();
            return;
        }

        ThrowItem(item);
    }

    void ThrowItem(ItemData item)
    {
        Instantiate(item.dropPrefab, dropPosition.position, Quaternion.Euler(Vector3.one * Random.value * 360f));
    }

    void UpdateUI()
    {
        for(int i = 0; i < slots.Length; i++)
        {
            if (slots[i].item != null)
                uiSlots[i].Set(slots[i]);
            else
                uiSlots[i].Clear();
        }
    }

    ItemSlot GetItemStack(ItemData item)
    {
        for(int i = 0; i < slots.Length; i++)
        {
            if (slots[i].item == item && slots[i].quantity < item.maxStackAmount)
                return slots[i];
        }
        return null;
    }

    ItemSlot GetEmptySlot()
    {
        for(int i = 0; i < slots.Length; i++)
        {
            if (slots[i].item == null)
                return slots[i];
        }
        return null;
    }

    public void SelectItem(int index)
    {
        if (slots[index].item == null)
            return;

        selectedItem = slots[index];
        selectedItemIndex = index;

        selectedItemName.text = selectedItem.item.displayName;
        selectedItemDescription.text = selectedItem.item.description;

        selectedItemStatName.text = string.Empty;
        selectedItemStatValue .text = string.Empty;

        for(int i = 0; i< selectedItem.item.consumables.Length; i++)
        {
            selectedItemStatName.text += selectedItem.item.consumables[i].type.ToString() + "\n";
            selectedItemStatValue.text += selectedItem.item.consumables[i].value.ToString() + "\n";
        }

        useButton.SetActive(selectedItem.item.type == ItemType.Consumable);
        equipButton.SetActive(selectedItem.item.type == ItemType.Equipable && !uiSlots[index].equipped);
        unEquipButton.SetActive(selectedItem.item.type == ItemType.Equipable && uiSlots[index].equipped);
        dropButton.SetActive(true);
    }

    private void ClearSeletecItemWindow()
    {
        selectedItem = null;
        selectedItemName.text = string.Empty;
        selectedItemDescription.text = string.Empty;

        selectedItemStatName.text = string.Empty;
        selectedItemStatValue.text = string.Empty;

        useButton.SetActive(false);
        equipButton.SetActive(false);
        unEquipButton.SetActive(false);
        dropButton.SetActive(false);
    }

    public void OnUseButton()
    {
        if(selectedItem.item.type == ItemType.Consumable)
        {
            for (int i = 0; i < selectedItem.item.consumables.Length; i++)
            {
                switch(selectedItem.item.consumables[i].type)
                {
                    case ConsumableType.Health:
                        condition.Heal(selectedItem.item.consumables[i].value); break;
                    case ConsumableType.Hunger:
                        condition.Eat(selectedItem.item.consumables[i].value); break;
                }
            }
        }
        RemoveSelectedItem();
    }

    public void OnEquipButton()
    {
        if (uiSlots[curEqiuipIndex].equipped)
        {
            UnEquip(curEqiuipIndex);
        }

        uiSlots[selectedItemIndex].equipped = true;
        curEqiuipIndex = selectedItemIndex;
        EquipManager.instance.EquipNew(selectedItem.item);
        UpdateUI();

        SelectItem(selectedItemIndex);
    }

    void UnEquip(int index)
    {
        uiSlots[index].equipped = false;
        EquipManager.instance.UnEquip();
        UpdateUI();

        if (selectedItemIndex == index)
            SelectItem(index);
    }

    public void OnUnEquipButton()
    {
        UnEquip(selectedItemIndex);
    }

    public void OnDropButton()
    {
        ThrowItem(selectedItem.item);
        RemoveSelectedItem();
    }

    private void RemoveSelectedItem()
    {
        selectedItem.quantity--;

        if(selectedItem.quantity <= 0)
        {
            if (uiSlots[selectedItemIndex].equipped)
            {
                UnEquip(selectedItemIndex);
            }
            
            selectedItem.item = null;
            ClearSeletecItemWindow();
        }

        UpdateUI();
    }

    public void RemoveItem(ItemData item)
    {

    }

    public bool HasItems(ItemData item, int quantity)
    {
        return false;
    }
}
public void Toggle()
{
    if (inventoryWindow.activeInHierarchy)
    {
        inventoryWindow.SetActive(false);
        onCloseInventory?.Invoke();
        controller.ToggleCursor(false);
    }
    else
    {
        inventoryWindow.SetActive(true);
        onCloseInventory?.Invoke();
        controller.ToggleCursor(true);
    }
}
인벤토리 창을 열고 닫을 수 있는 코드이다.
public void OnEquipButton()
{
    if (uiSlots[curEqiuipIndex].equipped)
    {
        UnEquip(curEqiuipIndex);
    }

    uiSlots[selectedItemIndex].equipped = true;
    curEqiuipIndex = selectedItemIndex;
    EquipManager.instance.EquipNew(selectedItem.item);
    UpdateUI();

    SelectItem(selectedItemIndex);
}
게임에서 아이템을 장착할 때 호출되는 OnEquipButton 메서드이다.

(feat.ChatGPT)
1. 먼저, 코드는 현재 선택된 아이템이 장착되었는지 확인합니다. 이를 위해 uiSlots[curEqiuipIndex].equipped를 사용하여 현재 장착된 아이템을 확인합니다.
2. 만약 현재 선택된 아이템이 이미 장착되어 있다면, UnEquip(curEqiuipIndex)를 호출하여 현재 장착된 아이템을 해제합니다. 이 메서드는 이전에 장착된 아이템을 해제하고 해당 슬롯을 업데이트하는 역할을 합니다.
3. 그런 다음, 코드는 선택된 아이템을 장착하고 관련된 변수를 업데이트합니다. uiSlots[selectedItemIndex].equipped = true를 사용하여 선택된 아이템을 장착 표시로 변경합니다. curEqiuipIndex = selectedItemIndex를 통해 현재 장착된 아이템의 인덱스를 업데이트합니다.
4. EquipManager.instance.EquipNew(selectedItem.item)를 호출하여 선택된 아이템을 실제로 장착합니다. 이 메서드는 아이템을 장착하는 데 필요한 로직을 처리합니다.
5. UpdateUI()를 호출하여 UI를 업데이트합니다. 이는 장착 상태가 변경되었으므로 UI에 이를 반영해야 함을 의미합니다.
6. 마지막으로, SelectItem(selectedItemIndex)를 호출하여 선택된 아이템을 강조 표시합니다. 이는 사용자가 선택한 아이템을 시각적으로 구별할 수 있도록 도와줍니다.
이렇게 함으로써 OnEquipButton 메서드는 선택된 아이템을 장착하고, UI를 업데이트하여 해당 변경 사항을 반영합니다.
void UnEquip(int index)
{
    uiSlots[index].equipped = false;
    EquipManager.instance.UnEquip();
    UpdateUI();

    if (selectedItemIndex == index)
        SelectItem(index);
}
게임에서 아이템을 해제할 때 호출되는 UnEquip 메서드이다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DayNightCycle : MonoBehaviour
{
    [Range(0.0f, 1.0f)]
    public float time;
    public float fullDayLength;
    public float startTime = 0.4f;
    private float timeRate;
    public Vector3 noon;

    [Header("Sun")]
    public Light sun;
    public Gradient sunColor;
    public AnimationCurve sunIntensity;

    [Header("Moon")]
    public Light moon;
    public Gradient moonColor;
    public AnimationCurve moonIntensity;

    [Header("Other Lighting")]
    public AnimationCurve lightingIntensityMultiplier;
    public AnimationCurve reflectionIntensityMultiplier;

    private void Start()
    {
        timeRate = 1.0f / fullDayLength;
        time = startTime;
    }

    private void Update()
    {
        time = (time + timeRate * Time.deltaTime) % 1.0f;

        UpdateLighting(sun, sunColor,sunIntensity);
        UpdateLighting(moon, moonColor,moonIntensity);

        RenderSettings.ambientIntensity = lightingIntensityMultiplier.Evaluate(time);
        RenderSettings.reflectionIntensity = reflectionIntensityMultiplier.Evaluate(time);

    }

    void UpdateLighting(Light lightSource, Gradient colorGradiant, AnimationCurve intensityCurve)
    {
        float intensity = intensityCurve.Evaluate(time);

        lightSource.transform.eulerAngles = (time - (lightSource == sun ? 0.25f : 0.75f)) * noon * 4.0f;
        lightSource.color = colorGradiant.Evaluate(time);
        lightSource.intensity = intensity;

        GameObject go = lightSource.gameObject;
        if(lightSource.intensity ==0&&go.activeInHierarchy)
            go.SetActive(false);
        else if( lightSource.intensity>0 &&!go.activeInHierarchy)
            go.SetActive(true);
    }
}
낮과 밤을 구현 할 수있는 코드이다.


개인 과제 - UI 구성