Scriptable Object
ScriptableObject는 보통 MonoBehaviour를 상속받아 게임 오브젝트와 함께 동작하는 스크립트와 달리 씬에 존재하지 않아도 되는 데이터만 저장하는 객체이다.
게임에서 반복적으로 사용되는 데이터(아이템, 능력치 등)의 설정을 줄이고, 저장 및 효율적으로 관리하는 데 사용된다.
왜 사용할까?
- 데이터 재사용의 편리성 : 한 번 생성해두면 여러 오브젝트에서 공유 가능
- 메모리 절약 : MonoBehaviour 기반 스크립트는 씬이 로드될 때마다 새롭게 생성되지만, ScriptableObject는 같은 데이터라면 하나만 로드해서 공유 가능
- 유니티 에디터에서 편집 가능 : 에셋 파일로 저장되어 Unity 에디터에서 직접 값을 변경할 수 있음, CreateAssetMenu 속성을 사용하면 에디터에서 ScriptableObject를 쉽게 생성 가능
MonoBehaviour vs ScriptableObject 차이점
MonoBehaviour | ScriptableObject | |
목적 | 게임 오브젝트의 동작 관리 | 데이터 저장 및 공유 |
씬 내 존재 | 씬에 있어야 함 | 씬에 없어도 됨 |
수명 주기 | 씬이 바뀌면 사라짐 | 씬이 바뀌어도 유지 |
메모리 관리 | 씬이 변경되면 다시 생성 | 같은 데이터를 계속 사용 가능 |
사용 방법
1) ScriptableObject 클래스 생성
using UnityEngine;
// [CreateAssetMenu(...)] : Unity 에디터에서 새 ScriptableObject를 만들 수 있도록 메뉴 추가
// fileName.. -> 새 파일을 만들면 기본 이름이 "Item"
// menuName.. -> Unity에서 "ScriptableObject" 폴더 안에 "ItemData" 항목이 생김
[CreateAssetMenu(fileName = "Item", menuName = "ScriptableObject/ItemData")]
public class ItemData : ScriptableObject
{
public string weaponName; // 무기 이름
public int damage; // 공격력
public float attackSpeed; // 공격 속도
public Sprite icon; // 무기 아이콘
}
2) 필요한 스크립트에 ScriptableObject 데이터 적용
3) Assets 폴더에서 우클릭 -> "Create/ScriptableObject/IteamData" 선택하여 새로운 무기 데이터를 생성 후 값 입력


4) Item 스크립트가 추가된 무기 오브젝트를 Hierarchy에 생성 후 ItemData 필드에 만들어놓은 ScriptableObject 연결


ItemData.cs
ScriptableObject 클래스를 생성하는 역할을 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Item", menuName = "ScriptableObject/ItemData")]
public class ItemData : ScriptableObject
{
// enum은 열거형(Enumeration)으로 여러 가지 타입을 명확하게 구분할 때 사용
public enum ItemType { Melee, Range, Glove, Shoe, Heal } // 아이템 타입(종류) 정의
// 아이템의 기본 정보
[Header("# Main Info")] // 인스펙터에서 변수들을 구분하기 위해 섹션 헤더 추가
public ItemType itemType; // 아이템 타입
public int itemId; // 아이템 고유 ID
public string itemName; // 아이템 이름
[TextArea] // 인스펙터에서 여러줄의 텍스트 작성 가능하게 변경
public string itemDesc; // 아이템 설명(Description)
public Sprite itemIcon; // 아이템 아이콘
// 아이템 레벨업 시 강해지는 능력치
[Header("# Level Data")]
public float baseDamage; // 기본 데미지(레벨 0)
public int baseCount; // 기본 공격 횟수(레벨 0) - 근거리 : 무기 개수, 원거리 : 관통력
public float[] damages; // 레벨별 데미지 증가량
public int[] counts; // 레벨별 공격 횟수 증가량
// 실제 발사할 Prefab을 연결
[Header("# Weapon")]
public GameObject projectile; // 발사체 프리팹(총알, 화살 등)
public Sprite hand; // 무기를 들고 있는 손 이미지
}
Item.cs
아이템 UI를 관리하고, 버튼을 눌렀을 때 아이템을 장착하거나 레벨업하는 역할을 한다.
주요 역할
- 아이템 데이터를 가져와 UI에 표시 (이름, 아이콘, 설명, 레벨)
- 버튼을 눌렀을 때 적절한 동작 수행 (무기 장착, 레벨업, 체력 회복)
- 레벨이 최대치에 도달하면 버튼 비활성화
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Item : MonoBehaviour
{
// 아이템 정보 저장을 위한 변수들
public ItemData data; // 아이템 데이터(Scriptable Object)
public int level; // 아이템 레벨
public Weapon weapon; // 무기 정보
public Gear gear; // 장비 정보
Image icon; // 아이템의 아이콘 이미지
Text textLevel; // 아이템 레벨을 표시하는 텍스트
Text textName; // 아이템 이름을 표시하는 텍스트
Text textDesc; // 아이템 설명을 표시하는 텍스트
// UI 초기화
void Awake()
{
// GetComponentsInChildren<Image>()는 자식 오브젝트의 Image 컴포넌트들을 가져와 배열로 반환
icon = GetComponentsInChildren<Image>()[1]; // 두 번째 Iamge 컴포넌트를 아이콘으로 사용
icon.sprite = data.itemIcon; // 아이콘 이미지 설정
Text[] texts = GetComponentsInChildren<Text>();
textLevel = texts[0]; // 레벨 표시
textName = texts[1]; // 아이템 이름
textDesc = texts[2]; // 아이템 설명
textName.text = data.itemName; // 아이템 이름은 초기 설정 후 변경되지 않기 때문에 바로 초기화
}
// 오브젝트가 활성화될 때마다 자동 실행
void OnEnable()
{
textLevel.text = "Lv." + (level + 1); // 레벨 표시(Lv. 1부터 시작)
switch (data.itemType) {
case ItemData.ItemType.Melee:
case ItemData.ItemType.Range:
// string.Format(...) -> 문자열을 만들 때 변수를 삽입할 수 있도록 도와주는 함수
/* data.damages[level] 값은 0.1, 0.2처럼 소수(배율) 형태로 저장됨
* UI에서 백분율료 표기하기 위해 100을 곱해 정수로 변환 */
textDesc.text = string.Format(data.itemDesc, data.damages[level] * 100, data.counts[level]); // 데이터에 맞는 아이템 설명 설정
break;
case ItemData.ItemType.Glove:
case ItemData.ItemType.Shoe:
textDesc.text = string.Format(data.itemDesc, data.damages[level] * 100);
break;
default:
textDesc.text = string.Format(data.itemDesc);
break;
}
}
// 버튼 클릭 이벤트와 연결
public void OnClick()
{
switch (data.itemType)
{
// 동일한 로직으로 작동하기 때문에 2개의 case를 붙임
// 무기 아이템 클릭 시
case ItemData.ItemType.Melee:
case ItemData.ItemType.Range:
if (level == 0) { // 처음 선택하면(level == 0) 무기를 새로 생성
GameObject newWeapon = new GameObject();
// AddComponent<T> = 오브젝트에 T 컴포넌트를 추가하는 함수
// 반환 값을 미리 선언한 변수에 저장
weapon = newWeapon.AddComponent<Weapon>();
weapon.Init(data);
}
else { // 처음 선택이 아니라면
float nextDamage = data.baseDamage;
int nextCount = 0;
nextDamage += data.baseDamage * data.damages[level]; // 기존 데미지에 레벨별 데미지(백분율)를 곱해 레벨업 시 데미지를 설정
nextCount += data.counts[level];
weapon.LevelUp(nextDamage, nextCount); // 레벨업 함수 호출
}
level++;
break;
// 장비 아이템 클릭 시
case ItemData.ItemType.Glove:
case ItemData.ItemType.Shoe:
if (level == 0) {
GameObject newGear = new GameObject();
gear = newGear.AddComponent<Gear>();
gear.Init(data);
}
else {
float nextRate = data.damages[level];
gear.LevelUp(nextRate);
}
level++;
break;
case ItemData.ItemType.Heal:
GameManager.instance.health = GameManager.instance.maxHealth; // 최대 체력으로 회복
break;
}
if (level == data.damages.Length) // Scriptable Object에 작성한 최대 레벨로 제한
{
GetComponent<Button>().interactable = false; // interactable(상호작용) 비활성화
}
}
}
Gear.cs
아이템 중에서도 장비(Glove, Shoe 등)를 관리하는 역할을 합니다.
주요 역할
- 장비(Glove, Shoe)의 정보를 저장
- 장비가 장착될 때 효과를 적용 (ApplyGear())
- 장비 레벨이 올라갈 때 능력 증가 (LevelUp())
- Glove는 무기 속도를 증가, Shoe는 플레이어 이동 속도를 증가
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gear : MonoBehaviour
{
public ItemData.ItemType type; // 장비의 타입
public float rate; // 능력의 수치 증가율(%)
// 장비 초기화(처음 장착될 때)
public void Init(ItemData data)
{
// 기본 설정
name = "Gear " + data.itemId; // 장비의 이름 설정
transform.parent = GameManager.instance.player.transform; // 플레이어에게 장착(부모를 플레이어로 설정)
transform.localPosition = Vector3.zero; // 오브젝트 위치를 플레이어에의 중심(0, 0, 0)에 위치하도록 설정
// 속성 설정
type = data.itemType;
rate = data.damages[0];
ApplyGear();
}
// 장비 레벨업
public void LevelUp(float rate)
{
this.rate = rate; // 능력의 새로운 수치를 저장
ApplyGear(); // 레벨업 효과 반영
}
// 장비 효과 적용
void ApplyGear()
{
// 장비의 타입에 따라 로직을 적용
switch (type) {
case ItemData.ItemType.Glove: // Glove면 공격 속도를 증가
RateUP();
break;
case ItemData.ItemType.Shoe: // Shoe면 이동 속도를 증가
SpeedUp();
break;
default:
break;
}
}
// Glove의 기능(공격 속도 증가)
void RateUP()
{
Weapon[] weapons = transform.parent.GetComponentsInChildren<Weapon>();
float speed;
// 장비의 타입에 따라 변경되는 속도 설정
foreach (Weapon weapon in weapons) {
switch (weapon.id) {
case 0: // 근거리 무기
// 여기서 150은 게임 밸런스를 맞추기 위해 개발자가 설정한 기본 무기 속도
speed = 150 * Character.WeaponSpeed; // 기본 공격 속도 설정
/* 고정값(speed + rate)을 더하면 속도 증가량이 일정하여 모든 레벨에서 동일하게 추가됨
* 비율(speed + speed * rate)을 적용하면 속도가 속도에 비례해 증가해 현재 속도가 높을수록 더 크게 증가함
* 즉, 장비 장착 시 속도가 기존 속도의 일정 비율(%)만큼 증가하도록 설계 */
weapon.speed = speed + speed * rate;
break;
default: // 원거리 무기
speed = 0.5f * Character.WeaponRate; // 기본 발사 간격 설정
// 빼기로 점점 작은값을 만들어 발사 간격을 줄임(연사력 증가)
weapon.speed = speed * (1f - rate);
break;
}
}
}
// Shoe의 기능(이동 속도 증가)
void SpeedUp()
{
float speed = 3 * Character.Speed; // 기본 이동 속도를 가져와
GameManager.instance.player.speed = speed + speed * rate; // rate% 만큼 증가시킴
}
}
'Unity > Undead Survivor' 카테고리의 다른 글
로컬 저장소(PlayerPrefs) / 업적 시스템(Achievement System) (1) | 2025.03.19 |
---|---|
레벨업 시스템(LevelUp System) / 손과 무기 장착 표현(Hands) / 캐릭터 선택(Character Select) (0) | 2025.03.19 |
열거형(enum) / 월드 좌표(World Position)와 스크린 좌표(Screen Position) / HUD(Head-Up Display) (0) | 2025.03.18 |
범위 감지(Scanner) / 무기(Weapon)와 총알(Bullet) (0) | 2025.03.18 |
배열과 리스트의 차이 / 오브젝트 풀링(Object Pooling) (0) | 2025.03.17 |