2025유니티수업

0627 유니티(Scriptable Object, Raycast 총 발사)

버스창문 2025. 6. 27. 17:54

Scriptable Object : 대량의 공유데이터를 저장할 수 있는 클래스

확장자 .asset으로 생성

 

특징

컴포넌트가 아님

일반적인 대부분의 이벤트 함수가 없음

다른 게임 오브젝트나 프리팹에 추가할 수 없음

 

 

 

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

[CreateAssetMenu(fileName = "RifleGunData" , menuName ="ScriptableObjects/RifleGunData" , order =1)]
// 파일명,  메뉴 이름,   순서
public class PlayerRifleData : ScriptableObject
{
    public AudioClip shotClip;
    public AudioClip reloadClip;
    public float fireRate = 0.1f; // 발사 속도
    public float reloadTime = 2.0f; // 재장전 시간
}

 

 

 

 

 

* 스크립터블 오브젝트로 세이브 / 로드

 

[CreateAssetMenu(fileName = "GameDataSO" , menuName = "ScriptableObjects/GameDataSO" , order = 2)]
//Scriptable Object로 세이브 / 로드
public class GameDataObject :ScriptableObject
{
    public int killCnt = 0;
    public float hp = 120;
    public float damage = 25;
    public float speed = 6f;
    public List<Item> equipItem = new List<Item>();
}

 

만든다

 

 

void LoadGameData()
{
    //KillCnt = PlayerPrefs.GetInt("KillCnt", 0);//PlayerPrefs : 플레이어 Preferences
    
    
    //gameData = this.dataManager.Load();

    if(gameData.equipItem.Count > 0)
    {
        this.InventorySetUp();
    }
    this.killCntTxtUi.text = $"Kill : <color=#ff0000>{this.gameData.killCnt.ToString("000")}</color>";

    //.asset 파일에 데이터 저장
    UnityEditor.EditorUtility.SetDirty(gameData);
}
private void SaveGameData()
{
    //this.dataManager.Save(this.gameData);
    UnityEditor.EditorUtility.SetDirty(gameData);

}

아이템 추가랑 제거 부분에도 저거 추가

수치 활용은 저 gameData로 하고,

나머지 data관리하던 부분을 UnityEditor.EditorUtility.SetDirty(gameData) 해놓으면 알아서 전역으로 처리 가능

 

단, 저렇게 두고 빌드하면 에러가 나서 안된다

#if UNITY_EDITOR
        UnityEditor.EditorUtility.SetDirty(gameData);
#endif

이렇게 넣어서 에디터에서만 동작하게 해놓아야 빌드된다

하지만 이러면 빌드에선 저장이 안된다.

 

 

빌드에서 저장되는법은 따로 있다

 

 

 

__________________________________________________________

 

rayCast로 총 발사

 

RaycastHit hit;
if (Physics.Raycast(this.firePos.position, firePos.forward, out hit, 20f, this.layerMask))
{
    isFire = (hit.collider.CompareTag(enemyTag));
}
else
    isFire = false;

if (!isReload && isFire)
{
    if (Time.time > this.prevFire)
        this.Shot();
    this.prevFire = Time.time + this.autiFireRate;
}

 

 

_______________________________________________________________

Drop(장비창) -> 자신이 가지고있는 장비, 이 효과를 적용할 플레이어

Drag(장비) -> 지가 올리는 스텟

장비창에 장비를 올려두면 Drop이 장비 정보를 갖게되고, 
그 장비의 효과를 플레이어한태 적용
빼면 Drop이 가지고있던 장비가 사라지고, 효과 또한 제거

player(유저) -> 장비창 data 를 가지고있어야함, 한두개가 아닐테니, 그 리스트를 data로써
가지고 있으면 됨
기초 스탯 Data도 가지고 있어야함
그 후 기초스탯, 장비창 변동 내용만 들고있으면 게임 부팅시 알아서 적용되는 형태

DataManager -> Player가 본인의 data를 Save하거나
게임을 킬때 Load하는 기능만 있어도 됨

이를 Scriptable object를 이용해서 DataManager없이 저장

 

 

 

Scriptable IO



//------------------------------------------------------------------------------
// <auto-generated>
//     이 코드는 도구를 사용하여 생성되었습니다.
//     런타임 버전:4.0.30319.42000
//
//     파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
//     이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Collections.Generic;
using UnityEditor.Search;
using UnityEngine;

[CreateAssetMenu(fileName ="GameDataSO",menuName ="Create GameData" ,order = 1)]
public class GameDataSO:ScriptableObject
{

    public int hp = 10;

    public int speed = 1;

    public int granade = 1;

    public int damage = 1;

    public ItemData[] items = new ItemData[4];


}


code 생성으로 만든 코드를 따로 수정해서 사용

 

Player



this.InitStats();



for(int i = 0; i < slots.Length; i++)
{
    slots[i].itemEquipAction += (idx) =>
    {
        this.gameData.items[idx] = slots[idx].item;
        this.StatUpdate(this.slots[idx].item ,(CharValue, ItemValue) =>
        {
            return CharValue + ItemValue;
        });
        UnityEditor.EditorUtility.SetDirty(this.gameData);
    };
    slots[i].itemRemoveAction += (idx) =>
    {
        this.StatUpdate(this.slots[idx].item, (CharValue, ItemValue) =>
        {
            return CharValue - ItemValue;
        });
        this.gameData.items[idx] = null;
        UnityEditor.EditorUtility.SetDirty(this.gameData);
    };
    if (gameData.items[i].Idx != ItemData.eItemIdx.None)
    {
        Debug.Log(this.gameData.items[i].Idx);
        this.slots[i].item = this.gameData.items[i];
    }
}

this.hp = this.maxhp;





    private void StatUpdate(ItemData item, Func<int,int,int> op) // 아이템이 갱신됐을때 스탯 적용용 함수
    {
        switch (item.Idx)
        {
            case ItemData.eItemIdx.None:
                break;
            case ItemData.eItemIdx.Hp:
                this.maxhp =op(this.maxhp, item.Value); // 식을 전달해서 함수 하나로 이용 가능 하도록
                break;
            case ItemData.eItemIdx.Speed:
                this.speed =op(this.speed, item.Value);
                break;
            case ItemData.eItemIdx.Shock:
                this.B_damage = op(this.B_damage, item.Value);
                break;
            case ItemData.eItemIdx.Granade:
                this.granade = op(this.granade, item.Value);
                break;
        }

    }

    private void InitStats() //저장된 게임 정보 로드할때 스탯과 장비창 적용 부분
    {
        this.maxhp = gameData.hp;
        this.speed = gameData.speed;
        this.B_damage = gameData.damage;
        this.granade = gameData.granade;
        for (int i = 0; i < gameData.items.Length; i++) 
        {
            if (gameData.items[i].Idx != ItemData.eItemIdx.None)
                this.items[(int)gameData.items[i].Idx - 1].transform.SetParent(this.slots[i].transform);
            switch (gameData.items[i].Idx)
            {
                case ItemData.eItemIdx.None:
                    break;
                case ItemData.eItemIdx.Hp:
                    this.maxhp += gameData.items[i].Value;
                    
                    break;
                case ItemData.eItemIdx.Speed:
                    this.speed += gameData.items[i].Value;
                    break;
                case ItemData.eItemIdx.Granade:
                    this.granade += gameData.items[i].Value;
                    break;
                case ItemData.eItemIdx.Shock:
                    this.B_damage += gameData.items[i].Value;
                    break;
            }
        }
    }
Slot(장비창)


public class Drop : MonoBehaviour,IDropHandler
{
    //슬롯: 장비 데이터를 얘가 가지고있음
    //그걸 플레이어쪽에 적용
    bool isEquiped = false;
    public ItemData item;
    public int idx;

    public Action<int> itemEquipAction; //아이템 낄때 플레이어쪽에서 실행할 액션
    public Action<int> itemRemoveAction; // 아이템 뺄때 플레이어쪽에서 실행할 액션
    //public Player player;
    public void OnDrop(PointerEventData eventData)
    {
        if (isEquiped)
        {
            return;
        }
        Drag.draggingItem.transform.SetParent(this.transform, false);
        this.item = Drag.draggingItem.GetComponent<ItemInfo>().data;

        this.itemEquipAction(this.idx);
        //
        //DataManager.instance.AddItem(item);
    }
    private void OnTransformChildrenChanged()
    {
        if (!isEquiped)
        {
            isEquiped = true;
            
        }
        else
        {
            isEquiped = false;
            this.itemRemoveAction(this.idx);
            //this.item.Idx = ItemData.eItemIdx.None;
        }
    }
}



OnDrop의 실행 순서가 OnTransformChildrenChanged 보다 느리기때문에, 
아이템을 빼는건 ChildrenChanged쪽에서 해도 되지만, 끼는건 Drop쪽에서 해야
아이템 정보를 가진 상태에서 실행 시킬 수 있다.
아이템(장비)


public class Drag : MonoBehaviour,IDragHandler,IBeginDragHandler,IEndDragHandler
{

    public static GameObject draggingItem = null;
    public GameObject inventory;
    public GameObject itemSlot;
    CanvasGroup cvGroup;

    void Start()
    {
        this.cvGroup = GetComponent<CanvasGroup>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        draggingItem = this.gameObject;
        this.transform.SetParent(this.inventory.transform);
        this.cvGroup.blocksRaycasts = false; // 드래그가 시작되면 다른 UI 이벤트를 받지 않도록 설정
    }

    public void OnDrag(PointerEventData eventData)
    {
        this.transform.position = Input.mousePosition;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        draggingItem = null;
        this.cvGroup.blocksRaycasts = true;
        if (this.transform.parent == this.inventory.transform)
        {
            this.transform.SetParent(this.itemSlot.transform);
        }
    }


}



___________________________________________


//------------------------------------------------------------------------------
// <auto-generated>
//     이 코드는 도구를 사용하여 생성되었습니다.
//     런타임 버전:4.0.30319.42000
//
//     파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
//     이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------


[System.Serializable]
public class ItemData
{
    public enum eItemIdx
    {
        None,Hp,Speed,Granade,Shock
    }
    
    public eItemIdx Idx;
    
    public int Value;
}





_________________________________________________


public class ItemInfo : MonoBehaviour
{
    public ItemData data;
}




Drag는 그저 ui적인 내용만 수행함, 드래그해서 슬롯(장비창)에 붙이면 붙고, 
이상한곳에 드래그되면 원래 위치로 돌아오는 내용

아이템 수치적인 내용은 ItemData를 가지고있는 ItemInfo에서 수행