TPS 게임 내 덱 빌딩 카드 개발 - (2) 장착 카드 데이터 형식

2026. 5. 4. 19:38·Unity 3D/Inkoid

 

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
'Unity 3D/Inkoid' 카테고리의 다른 글
  • TPS 게임 내 덱 빌딩 카드 개발 - (1) 개요
  • Inkoid 튜토리얼 제작
  • Inkoid 최적화 내용 정리
  • 게임 최적화 공부 정리
ybbro
ybbro
대부분의 포스팅은 pc에서 작성되었습니다. 모바일에서 볼 때 설명이 잘리면 데스크탑 모드를 사용해보길 바랍니다.
  • ybbro
    어떻게든 굴리는 게임 공방
    ybbro
  • 전체
    오늘
    어제
    • 전체 N
      • 스파르타코딩클럽_Unity개발과정
      • Unity 2D
        • 카드게임
        • 플랫포머 게임
        • 뱀서라이크
      • Unity 3D
        • 닷지
        • 유니티 짱
        • 디펜스 게임
        • Inkoid
      • Unity 에러 노트
      • 기능 구현 방법 정리
      • 셰이더 그래프
        • 2D
        • 3D
      • 프로그래머스
      • 자료구조
      • 기타
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    64비트
    룰렛
    대시
    마스크
    삭제
    UI
    유니티
    스파인
    unity
    세이브
    무료스킨
    직렬화
    텍스트매시프로
    sprite mask
    갤럭시 S24
    잔상
    유니티 애니메이터 파라미터 초기화
    다크모드
    hello
    앱이 휴대전화와 호환되지 않아 설치되지 않았습니다
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
ybbro
TPS 게임 내 덱 빌딩 카드 개발 - (2) 장착 카드 데이터 형식
상단으로

티스토리툴바