
1. ScriptableObject 를 활용한 데이터 관리
using UnityEngine;
[CreateAssetMenu(fileName = "EquipCard_", menuName = "EquipCard/Card")]
// 장착 카드 데이터
public class EquipCard_Data : ScriptableObject
{
// 아이콘
[field: SerializeField, Header("아이콘")] public Sprite icon { get; private set; }
// 종류
[field: SerializeField] public EquipCard_Sort_Selector equipCard_Part { get; private set; }
// 등급
[field: SerializeField, Header("등급")] public EquipCard_Rank rank { get; private set; }
// 장착 코스트
[field: SerializeField, Header("코스트")] public int cost { get; private set; }
// 능력치 변화들
[field: SerializeField, Header("능력치 변화")] public StatusChange[] statusChange { get; private set; }
}
// 장착 부위
public enum EquipCard_Part
{
InkWell,
Body,
Hook,
Shooter,
Shotgun,
Charger,
RPG,
Drill,
Chaser,
Gatling,
}
// 인스펙터 정리 + 작업 효율을 위한 커스텀 에디터를 쓰기 위해 사용
// 장착 부위를 드롭다운으로 선택 >> 해당 부위의 장착 카드, 특수 능력만 출현
// 텍스트
[System.Serializable]
public class EquipCard_Sort_Selector
{
public EquipCard_Part category;
public string sort; // 실제 선택된 키 이름 (문자열로 저장)
// sort에 따라서 특능 부여하면 안되나?
// 같은 종류의 카드라도 이후 등급에 따라 특능이 생기거나 빠질 수 있기에 따로 두자
public string ability;
}
// 장착 부위 별 카드 종류
// >> 장착 부위 + 종류 값을 통해 어떤 카드인지 판별
// >> 정보 가져와서 보여주기
// 잉크통
public enum EquipCards_InkWell
{
Light,
Big,
Super_InkGenerator,
Unauthorized_Nozzle,
}
// 바디
public enum EquipCards_Body
{
Light,
GPU_Upgrade,
Memory_Upgrade,
Overload_Booster,
Ink_Burster,
Magnet,
Transparency,
}
// 그래플링 훅
public enum EquipCards_Hook
{
WithPaint,
Big,
Absorb_HP,
Absorb_Ink,
Traction, // 견인
}
// 무기
// 슈터
public enum EquipCards_Shooter
{
Shooter_Large_diameter,
Shooter_Assult,
Shooter_Bounce,
}
// 샷건
public enum EquipCards_Shotgun
{
Shotgun_High_Pressure,
Shotgun_Sprayer, // 분무기 칙칙
Shotgun_InkEjector, // 잉크 방사기(화염방사기랑 비슷함)
}
public enum EquipCards_Charger
{
Charger_Quick,
Charger_Beam,
Charger_MapHack,
}
public enum EquipCards_RPG
{
RPG_Grenade_Launcher,
RPG_Nuclear,
RPG_EMP,
RPG_Pressure,
}
public enum EquipCards_Drill
{
Drill_Large,
Drill_Sharp,
Drill_Durable,
Drill_Skyroad,
}
public enum EquipCards_Chaser
{
Chaser_Wing,
Chaser_Smart,
}
public enum EquipCards_Gatling
{
현재_아이디어_없음,
}
// 부품 등급
public enum EquipCard_Rank
{
Normal,
Rare,
Epic,
Legend,
}
// 어떤 종류 + 얼마나 변화시키는지 통합 관리
[System.Serializable]
public class StatusChange
{
public StatusToChange statusToChange;
[Tooltip("얼마나 변화시킬지 % 그대로 쓰기 \n" +
"예) 5% 증가 = 5 \n" +
"3% 하락 = -3")]
public float percentage;
}
// 어떤 종류의 스텟을 변화시키는지
public enum StatusToChange
{
maxHP,
maxInk,
moveSpeed,
InkRecover_Natural,
InkRecover_Boost,
RespawnTIme,
EquipCard_maxCost,
// 갈고리
Hook_Speed,
Hook_Range,
// 무기
Weapon_AtkDelay,
Weapon_inkConsume,
// 히트스캔 타입은 사거리가 존재하기에 해당 스텟 추가
Weapon_HitScan_Range,
Weapon_Charger_maxChargeTime,
// 탄환
// 샷건, 차저와 같은 히트스캔 타입 무기는 아래 Bullet에서 바꿔주면 무기에 바로 적용되게끔
Bullet_Speed,
Bullet_LifeTime,
Bullet_Damage,
Bullet_Paint,
}
// 장비 특수 능력
// 게임 시작 시, 착용 특수능력들에 따라 착용 특수 능력들이 작용하는 곳에 값 부여
// UI 특능 작동 버튼 출현 같은 간단한 것은 해당 착용 특수 능력들이 작용하는 곳에서 분기 처리
// 무기/탄환 매커니즘 같은 복잡한 것은 기존 스크립트 상속 및 특능 착용에 따라 교체하도록 하면
// 기존 매커니즘을 건드릴 필요가 없어 문제 시 기존 부분은 정상 플레이 가능하게끔 할 수 있음
// 장착 부위별로 특수 능력을 만들어둠
// >> 이후에 다른 부위를 추가할 일이 있더라도 확장성이 좋음 + 세분화하여 관리 및 인스펙터 작업이 편함
public enum SpecialAbility_InkWell
{
None,
}
// 몸통
public enum SpecialAbility_Body
{
None, // 기본 = 없음
// 1. 과부하 부스터
// n1초간 이동 속도 증가 + 그 후 n2초간 딜레이 및 해당 시간 동안 이동 속도 감소
// >> 짧은 시간 고출력을 내는 대신 과부하
Body_OverloadBooster,
// 2. 잉크 버스터
// 잉크 30 사용(부족 시 UI 버튼 disable로 사용자에게 사용 불가 상태 알림)
// 범위 내 플레이어 넉백(1회성 물감이 펑 터지는 느낌의 충격파로 상대 팀은 대미지를 주면서 밀어냄 + 아군은 그냥 밀어냄) + 색칠 가능한 구조물들에 색칠
// 사용 이후 쿨타임 n초 + 그 동안 물감 자연 회복력 감소
Body_InkBurster,
// 3. 마그넷 잉크
// 자성을 띤 잉크를 도포(시전 캐릭터 주위로 블랙홀 느낌의 물감 이펙트)하여 상대 플레이어를 자신의 주위로 끌어들임
Body_Magnet,
// 4. 투명 잉크 도포 장치
// >> 잉크 30 사용(부족 시 UI 버튼 disable로 사용자에게 사용 불가 상태 알림). 사용 시 2~3초간 투명화(본인 클라에서는 자신이 반투명, 투명화 사용 시 오토 에임의 타겟이 되지 않도록) + 투명 상태에서 이동속도 감소. 재사용 쿨타임 n초
// >> 교전, 생존에 유리한 투명화 효과를 가져가는 만큼 잉크를 사용하게 하여 투명화 이후 폭발적인 화력을 내는 데 제약을 둠
Body_Transparency,
}
// 그래플링 훅
public enum SpecialAbility_Hook
{
None, // 기본 = 없음
// (1) 잉크 갈고리
// >> 그래플링 훅 사용 시 해당 위치에 물감 색칠. 단, 색칠은 크지 않게. 쿨타임 n초 + 물감 자연 회복량 감소
// >> 모바일에서 조준 + 그래플링 훅을 사용하는 것을 줄여줄 수 있음. 대신에 훅을 보다 자주 사용해야 하게끔 자연 회복량을 줄이기
Hook_WithPaint,
// (2) 거대한 갈고리
// 그래플링 훅 머리의 크기를 키움
// >> 머리의 크기가 커진 만큼 방사형으로 레이캐스트를 여럿 쏘아서 주변의 물감 색칠도 탐색하게끔 (물감 충전이 쉬워짐 + 다른 물체를 맞추기 쉬워짐) + 훅의 물감 고속 충전 속도 상승, 이동 속도 감소
Hook_Big,
// (3) 모기 갈고리
// 상대 팀에 갈고리 부착 시, HP 뺏어오는 기능, 갈고리 발사 속도 증가, 최대 HP 감소
Hook_Absorb_HP,
// (4) 잉크 갈취 갈고리
// 상대 팀에 갈고리 부착 시, 잉크를 뺏어오는 기능, 갈고리 발사 속도 증가, 최대 잉크 감소
Hook_Absorb_Ink,
// (5) 견인 갈고리
// 맞은 상대를 자신에게 끌고 오는 갈고리(견인 쿨타임 N초), 이동속도 소폭 감소
Hook_traction,
}
// 무기
public enum SpecialAbility_Shooter
{
None, // 기본 = 없음
Shooter_Bounce, // 튕기는 탄환 >> 이건.. Bullet을 상속하는 BouncingBullet을 하나 만드는 게 더 깔끔해보임
}
public enum SpecialAbility_Shotgun
{
None, // 기본 = 없음
Shotgun_flamethrower, // 화염방사기처럼 발사
}
public enum SpecialAbility_Charger
{
None, // 기본 = 없음
Charger_mapHack, // 조준 중에 구조물 뒤의 플레이어가 보임
}
public enum SpecialAbility_RPG
{
None, // 기본 = 없음
RPG_multiple, // 한번에 모든 물감을 발사하지 않음 >> 연사 가능
RPG_EMP, // 맞은 적들에게 짧은 시간 조작 불가 부여(지직거리는 이펙트라던지.. 플레이어들에게 상태를 나타낼 것도 필요)
RPG_Press, // 폭발 진원지로부터 반대 방향으로 날아가게끔 힘 주기 (팀원도 날려버리면?)
}
public enum SpecialAbility_Drill
{
None, // 기본 = 없음
Drill_skyroad, // 드릴이 지나간 자리에 길 이펙트가 남아 닿는 상대에 대미지
}
public enum SpecialAbility_Chaser
{
None, // 기본 = 없음
Chaser_Wing, // 한번에 6발 + 옆 방향으로 발사하는 체이서
}
public enum SpecialAbility_Gatling
{
None, // 기본 = 없음
//Gatling_, //게틀링.. 은 아직 아이디어가 없음
}
2. 커스텀 에디터
인스펙터에서 데이터 작업 실수를 줄이기 위한 장비 카드 장착 부위에 따른 enum 세분화
장비 장착 부위 선택 -> 해당 부위 카드 종류, 특수 능력 enum으로 변경
// 드롭다운 선택 enum에 따라 이하 항목 세분화(작업 효율성 증대 + 작업자 실수 방지)를 위한 커스텀 에디터
// >> 장비 종류별 카드
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(EquipCard_Sort_Selector))]
public class EquipCard_Sort_Drawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var categoryProp = property.FindPropertyRelative("category");
var sortProp = property.FindPropertyRelative("sort");
var abilityProp = property.FindPropertyRelative("ability");
EditorGUI.BeginProperty(position, label, property);
// 카테고리 선택
Rect categoryRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(categoryRect, categoryProp);
// 카테고리에 따라 enum 타입 결정
// sort
Type enumType_sort = null;
// ability
Type enumType_ability = null;
switch ((EquipCard_Part)categoryProp.enumValueIndex)
{
case EquipCard_Part.InkWell:
enumType_sort = typeof(EquipCards_InkWell);
enumType_ability = typeof(SpecialAbility_InkWell);
break;
case EquipCard_Part.Body:
enumType_sort = typeof(EquipCards_Body);
enumType_ability = typeof(SpecialAbility_Body);
break;
case EquipCard_Part.Hook:
enumType_sort = typeof(EquipCards_Hook);
enumType_ability = typeof(SpecialAbility_Hook);
break;
case EquipCard_Part.Shooter:
enumType_sort = typeof(EquipCards_Shooter);
enumType_ability = typeof(SpecialAbility_Shooter);
break;
case EquipCard_Part.Shotgun:
enumType_sort = typeof(EquipCards_Shotgun);
enumType_ability = typeof(SpecialAbility_Shotgun);
break;
case EquipCard_Part.Charger:
enumType_sort = typeof(EquipCards_Charger);
enumType_ability = typeof (SpecialAbility_Charger);
break;
case EquipCard_Part.RPG:
enumType_sort = typeof(EquipCards_RPG);
enumType_ability = typeof(SpecialAbility_RPG);
break;
case EquipCard_Part.Drill:
enumType_sort = typeof(EquipCards_Drill);
enumType_ability = typeof(SpecialAbility_Drill);
break;
case EquipCard_Part.Chaser:
enumType_sort = typeof(EquipCards_Chaser);
enumType_ability = typeof(SpecialAbility_Chaser);
break;
case EquipCard_Part.Gatling:
enumType_sort = typeof(EquipCards_Gatling);
enumType_ability = typeof(SpecialAbility_Gatling);
break;
// 무기 추가 시 enum을 각각 만들고, EquipCard_Part에 항목을 추가한 뒤, 여기에 추가
}
// Sort enum : enumType에 따라 그에 해당하는 enum 항목들을 드롭다운으로 생성
if (enumType_sort != null)
{
Array values = Enum.GetValues(enumType_sort);
string[] names = Enum.GetNames(enumType_sort);
int index = Array.IndexOf(names, sortProp.stringValue);
if (index < 0) index = 0;
Rect sortRect = new Rect(position.x, position.y + (EditorGUIUtility.singleLineHeight + 3), position.width, EditorGUIUtility.singleLineHeight);
int newIndex = EditorGUI.Popup(sortRect, "Sort", index, names);
sortProp.stringValue = names[newIndex];
}
// Ability Enum
if (enumType_ability != null)
{
Array values = Enum.GetValues(enumType_ability);
string[] names = Enum.GetNames(enumType_ability);
int index = Array.IndexOf(names, abilityProp.stringValue);
if (index < 0) index = 0;
Rect abilityRect = new Rect(position.x, position.y + (EditorGUIUtility.singleLineHeight+3) * 2, position.width, EditorGUIUtility.singleLineHeight);
int newIndex = EditorGUI.Popup(abilityRect, "Ability", index, names);
abilityProp.stringValue = names[newIndex];
}
EditorGUI.EndProperty();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return (EditorGUIUtility.singleLineHeight + 3) * 3;
}
}
#endif'Unity 3D > Inkoid' 카테고리의 다른 글
| TPS 게임 내 덱 빌딩 카드 개발 - (1) 개요 (0) | 2026.05.04 |
|---|---|
| Inkoid 튜토리얼 제작 (0) | 2026.01.11 |
| Inkoid 최적화 내용 정리 (0) | 2025.11.30 |
| 게임 최적화 공부 정리 (0) | 2025.11.30 |
| 지스타 2025 출품 이후 Inkoid 무기 개편 (0) | 2025.11.25 |