ThreeKingdomsSlingshot/Assets/Scripts/Runtime/SkillTest/SkillTestBootstrap.cs

686 lines
32 KiB
C#
Raw Permalink Normal View History

스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
using System;
using System.Collections.Generic;
using System.Globalization;
using TKS.OutGame;
using UnityEngine;
using UnityEngine.UI;
namespace TKS
{
/// <summary>
/// 스킬 테스트 씬(<c>Assets/Scenes/SkillTest.unity</c> · 메뉴 TKS/Skill Test/Build Scene · 2026-11-09 PD
/// 「유닛마다 액티브 스킬을 구현하기 쉽도록 목록에서 유닛을 선택 · 선택된 유닛으로 기본 스킬 사용 방식 및 슬링샷 조작이 가능한 공간」).
/// WL #947 스킬 테스트 씬과 같은 역할 — 전투 코드(BattleManager · SlingshotInput · ActionResolver · SkillSystem · BattleHUD)는 그대로 쓰고
/// 월드맵·아웃게임·세이브 없이 「고른 영웅 1 + 더미」 만 세운다.
/// 더미 = 죽지 않음 · 기본 행동 안 함(토글) · 배치 = 표 <c>Resources/Skills/skill_test.csv</c>(영웅 출발 자리 기준 상대 좌표).
/// 왼쪽 위 「유닛」 = 영웅 목록(heroes.csv · 병사 제외) · 「이펙트」 = 팩 이펙트 미리보기(에디터 전용 · <c>Assets/VFX</c> 프리팹 검색 → 고른 영웅에 띄움 · 경로 복사).
/// 스킬 이펙트 값 = <c>Resources/Skills/skill_vfx.csv</c>(SkillVfx) · 「다시 배치」 = 표 다시 읽기 + 처음 자리.
/// </summary>
public class SkillTestBootstrap : MonoBehaviour
{
public const string DummyTablePath = "Skills/skill_test";
const string PrefKey = "TKS_SKILLTEST_HERO";
public const string VfxRoot = "Assets/VFX";
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
/// <summary>「기운 시험」 스위치가 켜는 이펙트 표 키(skill_vfx.csv test_aura 행).</summary>
public const string AuraTestKey = "test_aura";
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
public BalanceConfig Config;
public static SkillTestBootstrap Instance { get; private set; }
public BattleManager Battle { get; private set; }
public BattleHUD Hud { get; private set; }
public SlingshotInput Sling { get; private set; }
public CameraController CameraCtrl { get; private set; }
public HeroDb.Hero Current { get; private set; }
public IReadOnlyList<HeroDb.Hero> Heroes
{
get { return heroes; }
}
public bool NoCooldown = true;
public bool EnemyActs;
public bool HeroInvincible = true;
readonly List<HeroDb.Hero> heroes = new List<HeroDb.Hero>();
readonly List<RectTransform> blockers = new List<RectTransform>();
Camera cam;
Text infoText;
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
Text cdLabel, enemyLabel, invLabel, auraLabel;
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
RectTransform heroPanel, fxPanel;
void Awake()
{
Instance = this;
if (Config == null) Config = Resources.Load<BalanceConfig>("BalanceConfig");
if (Config == null)
{
Debug.LogError("[TKS] BalanceConfig 자산이 없습니다. 메뉴 TKS/Setup Project 를 실행하세요.");
return;
}
cam = Camera.main;
if (cam == null)
{
var cgo = new GameObject("Main Camera");
cgo.tag = "MainCamera";
cam = cgo.AddComponent<Camera>();
}
cam.orthographic = true;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = new Color(0.07f, 0.09f, 0.11f);
cam.transform.position = new Vector3(0f, 0f, -10f);
cam.nearClipPlane = 0.1f;
cam.farClipPlane = 100f;
cam.transparencySortMode = TransparencySortMode.CustomAxis;
cam.transparencySortAxis = Vector3.up;
cam.depthTextureMode |= DepthTextureMode.Depth; // 이펙트 셰이더 소프트 파티클(GameBootstrap 과 같음)
var board = new GameObject("Board").AddComponent<Board>();
board.Build(Config.boardSize, Config.boardArt);
CameraCtrl = cam.GetComponent<CameraController>();
if (CameraCtrl == null) CameraCtrl = cam.gameObject.AddComponent<CameraController>();
CameraCtrl.Init(board, Config.cameraViewHeight, Config.cameraMinViewHeight, Config.cameraMaxViewHeight);
Battle = new GameObject("BattleManager").AddComponent<BattleManager>();
Battle.Configure(Config, board);
Battle.LineupProvider = Lineup;
Battle.EnemyHold = !EnemyActs;
Hud = new GameObject("HUD").AddComponent<BattleHUD>();
Hud.Build(Battle, cam, CameraCtrl);
Battle.Hud = Hud;
Sling = new GameObject("SlingshotInput").AddComponent<SlingshotInput>();
Sling.Init(Battle, cam, Hud, CameraCtrl);
Sling.BlockPress = OverPanel;
Battle.Sling = Sling;
var cc = CameraCtrl;
Battle.UnitActing += u => cc.Follow(u.transform);
Battle.PhaseChanged += OnPhase;
Battle.StageStarted += () => cc.SnapTo(Focus());
var db = OutGameData.Default;
if (db != null && db.Heroes != null)
foreach (var h in db.Heroes.heroes)
if (h != null && !h.isSoldier && !string.IsNullOrEmpty(h.id)) heroes.Add(h);
if (heroes.Count == 0)
{
Debug.LogError("[TKS] 스킬 테스트: 영웅 표가 비었습니다(메뉴 TKS/Import OutGame Data).");
return;
}
// 진영(촉 · 위 · 오 · 군) 순으로 묶되 진영 안은 표 순서 그대로(안정 정렬)
var order = new List<string>();
foreach (var h in heroes)
if (!order.Contains(h.faction)) order.Add(h.faction);
var sorted = new List<HeroDb.Hero>();
foreach (var f in order)
foreach (var h in heroes)
if (h.faction == f) sorted.Add(h);
heroes.Clear();
heroes.AddRange(sorted);
BuildUi();
string last = PlayerPrefs.GetString(PrefKey, "");
var first = heroes.Find(h => h.id == last) ?? heroes[0];
SelectHero(first);
}
void OnDestroy()
{
if (Instance == this) Instance = null;
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
SkillVfx.TestTravelKey = ""; // 시험 키가 다른 씬(실제 전투)으로 새지 않게
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
}
// ---------- 전투 구성 ----------
List<LineupEntry> Lineup()
{
var list = new List<LineupEntry>();
var h = Current;
if (h == null) return list;
list.Add(new LineupEntry
{
slot = heroSlot, id = h.id, name = h.nameKo, type = h.type, art = h.art,
hpMult = h.hpMult, atkMult = h.atkMult, defMult = h.defMult,
skillActive = h.skillActive ?? "", skillPassive = h.skillPassive ?? "", activeLv = 1, passiveLv = 1, passiveMult = 1f, general = true,
});
return list;
}
public void SelectHero(HeroDb.Hero h)
{
if (h == null) return;
Current = h;
PlayerPrefs.SetString(PrefKey, h.id);
if (heroPanel != null) heroPanel.gameObject.SetActive(false);
Restart();
}
/// <summary>처음 자리로 다시 배치(표 skill_vfx.csv · skill_test.csv 다시 읽음).</summary>
public void Restart()
{
if (Battle == null || Current == null) return;
Battle.EnemyHold = !EnemyActs;
var stage = MakeStage(); // 영웅 자리(heroSlot)도 이 표에서 → Lineup 보다 먼저
var mode = new BattleMode { kind = "skilltest", stage = stage, maxTurns = 0, onExit = Restart };
Battle.StartMode(mode);
ApplyFlags();
RefreshInfo();
}
/// <summary>영웅 출발 자리 = BalanceConfig.playerSpawnPoints[heroSlot](표 skill_test.csv 의 e_type = hero 행 n_slot).</summary>
int heroSlot;
Vector2 HeroSpawn
{
get
{
var sp = Config.playerSpawnPoints;
return sp != null && sp.Length > 0 ? sp[Mathf.Clamp(heroSlot, 0, sp.Length - 1)] : Vector2.zero;
}
}
BalanceConfig.StageDef MakeStage()
{
var st = new BalanceConfig.StageDef { title = "스킬 테스트", subtitle = "", aiAccuracy = 1f, aiAggroRange = 999f };
var ta = Resources.Load<TextAsset>(DummyTablePath);
heroSlot = 0;
if (ta == null) return st;
var rows = new List<string[]>();
string[] head = null;
foreach (var raw in ta.text.Replace("\r", "").Split('\n'))
{
var line = raw.TrimStart('');
if (line.Trim().Length == 0 || line.TrimStart().StartsWith("#")) continue;
var c = SkillVfx.SplitCsv(line);
if (head == null) head = c;
else rows.Add(c);
}
if (head == null) return st;
int iType = Array.IndexOf(head, "e_type"), iSlot = Array.IndexOf(head, "n_slot");
foreach (var c in rows) // 영웅 자리 먼저(더미 좌표 = 영웅 자리 기준)
{
if (iType < 0 || iType >= c.Length || c[iType].Trim().ToLowerInvariant() != "hero") continue;
if (iSlot >= 0 && iSlot < c.Length) int.TryParse(c[iSlot].Trim(), out heroSlot);
}
head = null;
foreach (var raw in ta.text.Replace("\r", "").Split('\n'))
{
var line = raw.TrimStart('');
if (line.Trim().Length == 0 || line.TrimStart().StartsWith("#")) continue;
var c = SkillVfx.SplitCsv(line);
if (head == null)
{
head = c;
continue;
}
UnitType type = UnitType.Infantry;
float dx = 0f, dy = 0f, hp = 1f;
string name = "";
bool skip = false;
for (int i = 0; i < head.Length && i < c.Length; i++)
{
string k = head[i].Trim(), v = c[i].Trim();
if (v.Length == 0) continue;
if (k == "e_type")
{
if (v.ToLowerInvariant() == "hero" || !Enum.TryParse(v, true, out type)) skip = true;
}
else if (k == "f_dx") dx = F(v);
else if (k == "f_dy") dy = F(v);
else if (k == "f_hp") hp = F(v);
else if (k == "s_name") name = v;
}
if (skip) continue;
st.enemies.Add(new BalanceConfig.EnemySpawn
{
name = name.Length > 0 ? name : "더미 " + UnitTypeInfo.Korean(type),
type = type,
position = HeroSpawn + new Vector2(dx, dy),
hpMult = hp > 0f ? hp : 1f,
art = DummyArt(type),
});
}
return st;
}
static float F(string v)
{
float f;
return float.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out f) ? f : 0f;
}
/// <summary>더미 그림 = 스테이지 표의 첫 번째 같은 병종 병사 그림(장수 제외 · 없으면 도형).</summary>
string DummyArt(UnitType t)
{
if (Config.stages == null) return "";
foreach (var s in Config.stages)
{
if (s == null || s.enemies == null) continue;
foreach (var e in s.enemies)
if (e != null && e.type == t && !e.isBoss && !e.isGeneral && !string.IsNullOrEmpty(e.art)) return e.art;
}
return "";
}
void ApplyFlags()
{
if (Battle == null) return;
foreach (var u in Battle.Units)
{
if (u == null) continue;
u.Invincible = u.Faction == Faction.Enemy || HeroInvincible;
}
}
void OnPhase(BattlePhase p)
{
if (p != BattlePhase.PlayerTurn) return;
ApplyFlags();
foreach (var u in Battle.Units)
{
if (u == null || !u.IsAlive) continue;
if (u.Faction == Faction.Enemy && u.Hp < u.MaxHp)
{
u.Hp = u.MaxHp; // 더미 체력 회복(다음 시험을 같은 조건으로)
if (u.View != null) u.View.Refresh();
}
if (NoCooldown && u.Faction == Faction.Player && Battle.Skills != null)
{
var s = Battle.Skills.Of(u);
if (s != null) s.ReadyTurn = Battle.TurnNumber;
if (u.View != null) u.View.Refresh();
}
}
if (CameraCtrl != null) CameraCtrl.FocusOn(Focus());
Battle.NotifyChanged();
}
Vector2 Focus()
{
var ps = Battle.AliveUnits(Faction.Player);
Vector2 p = ps.Count > 0 ? ps[0].Position : HeroSpawn;
return p + new Vector2(0f, Config.cameraViewHeight * 0.2f);
}
// ---------- UI ----------
static readonly Color PanelColor = new Color(0.08f, 0.09f, 0.12f, 0.92f);
static readonly Color BtnColor = new Color(0.22f, 0.26f, 0.34f, 0.95f);
static readonly Color BtnOnColor = new Color(0.25f, 0.5f, 0.32f, 0.95f);
static readonly Color TextColor = new Color(0.95f, 0.93f, 0.86f);
bool OverPanel(Vector2 screen)
{
foreach (var rt in blockers)
if (rt != null && rt.gameObject.activeInHierarchy && RectTransformUtility.RectangleContainsScreenPoint(rt, screen, null)) return true;
return false;
}
void BuildUi()
{
var cgo = new GameObject("SkillTestUI");
var canvas = cgo.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 200;
var scaler = cgo.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1080f, 1920f);
scaler.matchWidthOrHeight = 0.5f;
cgo.AddComponent<GraphicRaycaster>();
var root = cgo.transform;
// 왼쪽 위: 유닛 · 이펙트 열기 + 고른 영웅 정보
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
var top = Panel("Top", root, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(16f, -290f), new Vector2(640f, 300f), PanelColor);
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
blockers.Add(top);
Btn(top, "▤ 유닛 선택", new Vector2(10f, -10f), new Vector2(300f, 76f), () => Toggle(heroPanel));
Btn(top, "✦ 이펙트 보기", new Vector2(320f, -10f), new Vector2(310f, 76f), () => Toggle(fxPanel));
infoText = Label(top, "", 27, TextAnchor.UpperLeft);
SetRect(infoText.rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
infoText.rectTransform.offsetMin = new Vector2(14f, 6f);
infoText.rectTransform.offsetMax = new Vector2(-14f, -94f);
// 왼쪽 아래: 시험 토글
var tools = Panel("Tools", root, Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(16f, 40f), new Vector2(560f, 260f), PanelColor);
blockers.Add(tools);
Btn(tools, "다시 배치", new Vector2(10f, -10f), new Vector2(265f, 74f), Restart);
cdLabel = Btn(tools, "", new Vector2(285f, -10f), new Vector2(265f, 74f), () =>
{
NoCooldown = !NoCooldown;
OnPhase(Battle.Phase);
RefreshInfo();
});
enemyLabel = Btn(tools, "", new Vector2(10f, -94f), new Vector2(265f, 74f), () =>
{
EnemyActs = !EnemyActs;
Battle.EnemyHold = !EnemyActs;
RefreshInfo();
});
invLabel = Btn(tools, "", new Vector2(285f, -94f), new Vector2(265f, 74f), () =>
{
HeroInvincible = !HeroInvincible;
ApplyFlags();
RefreshInfo();
});
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
Btn(tools, "이펙트 표 다시 읽기", new Vector2(10f, -178f), new Vector2(265f, 70f), () =>
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
{
SkillVfx.Reload();
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
HitFeel.Reload();
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
Battle.LastMessage = "skill_vfx.csv 다시 읽음 · 행 " + SkillVfx.Rows.Count;
Battle.NotifyChanged();
RefreshInfo();
});
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
// 몸 뒤덮는 기운 시험(PD 2026-11-10 「첨부 같은 느낌으로 몸을 뒤덮는 연출 · 스킬용 시험」): 켜면 슬링샷 공격 돌진에 표 test_aura 행을 함께 띄움
auraLabel = Btn(tools, "", new Vector2(285f, -178f), new Vector2(265f, 70f), () =>
{
SkillVfx.TestTravelKey = SkillVfx.TestTravelKey.Length > 0 ? "" : AuraTestKey;
RefreshInfo();
});
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
heroPanel = BuildHeroList(root);
blockers.Add(heroPanel);
fxPanel = BuildFxBrowser(root);
blockers.Add(fxPanel);
heroPanel.gameObject.SetActive(false);
fxPanel.gameObject.SetActive(false);
}
void Toggle(RectTransform p)
{
if (p == null) return;
bool on = !p.gameObject.activeSelf;
heroPanel.gameObject.SetActive(false);
fxPanel.gameObject.SetActive(false);
p.gameObject.SetActive(on);
}
RectTransform BuildHeroList(Transform root)
{
var p = Panel("HeroList", root, new Vector2(0f, 0f), new Vector2(0.78f, 1f), new Vector2(0f, 1f), new Vector2(16f, -550f), Vector2.zero, PanelColor);
p.offsetMin = new Vector2(16f, 320f);
p.offsetMax = new Vector2(0f, -550f);
var content = Scroll(p);
string lastFaction = null;
foreach (var h in heroes)
{
if (h.faction != lastFaction)
{
lastFaction = h.faction;
var hd = Label(content, "— " + UiKit.FactionKo(h.faction) + " —", 30, TextAnchor.MiddleLeft);
hd.color = new Color(1f, 0.85f, 0.4f);
hd.gameObject.AddComponent<LayoutElement>().preferredHeight = 52f;
}
var hero = h;
var a = SkillCatalog.Get(h.skillActive);
string text = h.nameKo + " · " + UnitTypeInfo.Korean(h.type) + (a != null ? " · " + a.NameKo + "(" + TriggerKo(a.Trigger) + ")" : " · 액티브 없음");
var b = ListItem(content, text, () => SelectHero(hero));
b.gameObject.name = "Hero_" + h.id;
}
return p;
}
RectTransform BuildFxBrowser(Transform root)
{
var p = Panel("FxBrowser", root, new Vector2(0f, 0f), new Vector2(0.86f, 1f), new Vector2(0f, 1f), Vector2.zero, Vector2.zero, PanelColor);
p.offsetMin = new Vector2(16f, 320f);
p.offsetMax = new Vector2(0f, -550f);
#if UNITY_EDITOR
var bar = Rect("Bar", p, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -8f), new Vector2(-16f, 170f));
var search = SearchField(bar, "검색(예: HCFX_Hit · Slash · Earth)", new Vector2(8f, -4f), new Vector2(520f, 74f));
var scaleBtn = Btn(bar, "", new Vector2(536f, -4f), new Vector2(160f, 74f), null);
var tiltBtn = Btn(bar, "", new Vector2(704f, -4f), new Vector2(170f, 74f), null);
var faceBtn = Btn(bar, "", new Vector2(8f, -86f), new Vector2(250f, 74f), null);
var copyBtn = Btn(bar, "경로 복사", new Vector2(266f, -86f), new Vector2(200f, 74f), null);
var countText = Label(bar, "", 26, TextAnchor.MiddleLeft);
SetRect(countText.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(476f, -86f), new Vector2(400f, 74f));
var listHost = Rect("ListHost", p, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
listHost.offsetMin = new Vector2(0f, 0f);
listHost.offsetMax = new Vector2(0f, -180f);
var content = Scroll(listHost);
var all = new List<string>();
foreach (var g in UnityEditor.AssetDatabase.FindAssets("t:Prefab", new[] { VfxRoot }))
all.Add(UnityEditor.AssetDatabase.GUIDToAssetPath(g));
all.Sort(StringComparer.OrdinalIgnoreCase);
float scale = 1f;
float tilt = 0f;
bool face = false;
string lastPath = null;
Action refreshBar = () =>
{
scaleBtn.text = "크기 ×" + scale.ToString("0.##");
tiltBtn.text = "기울기 " + tilt.ToString("0") + "°";
faceBtn.text = face ? "방향: 더미 쪽" : "방향: 없음";
};
Action<string> play = path =>
{
lastPath = path;
var prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
var hero = Battle.AliveUnits(Faction.Player);
if (prefab == null || hero.Count == 0) return;
var u = hero[0];
Vector2 dir = Vector2.up;
var ds = Battle.AliveUnits(Faction.Enemy);
if (ds.Count > 0) dir = (ds[0].Position - u.Position).normalized;
var go = SkillVfx.SpawnPrefab(prefab, u.Position + new Vector2(0f, u.ArtHeight * 0.45f), scale * u.ArtHeight, tilt,
face ? (Vector2?)dir : null, SkillVfx.SortingBase, 1f, null);
Destroy(go, SkillVfx.AutoLife(go));
Battle.LastMessage = "이펙트 " + System.IO.Path.GetFileNameWithoutExtension(path) + " · 크기 ×" + scale.ToString("0.##") + " · 기울기 " + tilt.ToString("0") + "°";
Battle.NotifyChanged();
};
scaleBtn.GetComponentInParent<Button>().onClick.AddListener(() =>
{
scale = scale >= 4f ? 0.25f : scale * 2f;
refreshBar();
if (lastPath != null) play(lastPath);
});
tiltBtn.GetComponentInParent<Button>().onClick.AddListener(() =>
{
tilt = tilt > -1f ? -45f : tilt > -46f ? -90f : 0f;
refreshBar();
if (lastPath != null) play(lastPath);
});
faceBtn.GetComponentInParent<Button>().onClick.AddListener(() =>
{
face = !face;
refreshBar();
if (lastPath != null) play(lastPath);
});
copyBtn.GetComponentInParent<Button>().onClick.AddListener(() =>
{
if (lastPath == null) return;
GUIUtility.systemCopyBuffer = lastPath;
Battle.LastMessage = "복사: " + lastPath;
Battle.NotifyChanged();
});
const int MaxRows = 120;
Action<string> fill = q =>
{
for (int i = content.childCount - 1; i >= 0; i--) Destroy(content.GetChild(i).gameObject);
int n = 0, hit = 0;
string ql = (q ?? "").Trim().ToLowerInvariant();
foreach (var path in all)
{
if (ql.Length > 0 && path.ToLowerInvariant().IndexOf(ql, StringComparison.Ordinal) < 0) continue;
hit++;
if (n >= MaxRows) continue;
n++;
var pth = path;
string shortName = pth.Substring(VfxRoot.Length + 1);
ListItem(content, shortName, () => play(pth)).GetComponentInChildren<Text>().fontSize = 24;
}
countText.text = hit + "개" + (hit > MaxRows ? " (앞 " + MaxRows + ")" : "") + " / 전체 " + all.Count;
};
search.onValueChanged.AddListener(v => fill(v));
refreshBar();
fill("");
#else
var t = Label(p, "이펙트 보기는 에디터에서만(Assets/VFX 검색)", 30, TextAnchor.MiddleCenter);
SetRect(t.rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
#endif
return p;
}
static string TriggerKo(SkillTrigger t)
{
return t == SkillTrigger.NextAction ? "다음 행동" : t == SkillTrigger.Instant ? "즉시" : "상시";
}
void RefreshInfo()
{
if (cdLabel != null) cdLabel.text = "쿨 무시: " + (NoCooldown ? "켬" : "끔");
if (enemyLabel != null) enemyLabel.text = "더미 행동: " + (EnemyActs ? "켬" : "끔");
if (invLabel != null) invLabel.text = "영웅 무적: " + (HeroInvincible ? "켬" : "끔");
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
if (auraLabel != null) auraLabel.text = "기운 시험: " + (SkillVfx.TestTravelKey.Length > 0 ? "켬" : "끔");
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
if (infoText == null || Current == null) return;
var h = Current;
var a = SkillCatalog.Get(h.skillActive);
var ps = SkillCatalog.Get(h.skillPassive);
string s = h.nameKo + " · " + UnitTypeInfo.Korean(h.type) + " · " + UiKit.FactionKo(h.faction);
s += "\n액티브 " + (a != null ? a.NameKo + "(" + TriggerKo(a.Trigger) + " · 쿨 " + a.Cooldown + ")" : "없음");
s += "\n패시브 " + (ps != null ? ps.NameKo : "없음");
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
// 이 영웅에게 걸리는 이펙트 표 행 수(스킬 id 행 + 일반 공격 basic_<병종> 행 · 진영 s_camp 일치)
string camp = string.IsNullOrEmpty(h.faction) ? "other" : h.faction.ToLowerInvariant();
string basic = SkillVfx.BasicKey(h.type);
int c = 0, t = 0, hit = 0, bt = 0, bh = 0;
foreach (var r in SkillVfx.Rows)
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
{
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
if (r.camp.Length > 0 && r.camp != camp) continue;
if (a != null && r.HasKey(a.Id))
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
{
if (r.evt == SkillVfx.EventCast) c++;
else if (r.evt == SkillVfx.EventTravel) t++;
else if (r.evt == SkillVfx.EventHit) hit++;
}
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
if (r.HasKey(basic))
{
if (r.evt == SkillVfx.EventTravel) bt++;
else if (r.evt == SkillVfx.EventHit) bh++;
}
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
}
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
s += "\n이펙트 표: 스킬 발동 " + c + " · 돌진 " + t + " · 명중 " + hit + " / 일반 돌진 " + bt + " · 명중 " + bh;
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
infoText.text = s;
}
// ---------- UI 부품 ----------
static RectTransform Rect(string name, Transform parent, Vector2 aMin, Vector2 aMax, Vector2 pivot, Vector2 pos, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
SetRect(rt, aMin, aMax, pivot, pos, size);
return rt;
}
static void SetRect(RectTransform rt, Vector2 aMin, Vector2 aMax, Vector2 pivot, Vector2 pos, Vector2 size)
{
rt.anchorMin = aMin;
rt.anchorMax = aMax;
rt.pivot = pivot;
rt.anchoredPosition = pos;
rt.sizeDelta = size;
}
static RectTransform Panel(string name, Transform parent, Vector2 aMin, Vector2 aMax, Vector2 pivot, Vector2 pos, Vector2 size, Color c)
{
var rt = Rect(name, parent, aMin, aMax, pivot, pos, size);
var img = rt.gameObject.AddComponent<Image>();
img.color = c;
return rt;
}
static Text Label(Transform parent, string text, int size, TextAnchor anchor)
{
var go = new GameObject("Text", typeof(RectTransform));
go.transform.SetParent(parent, false);
var t = go.AddComponent<Text>();
t.font = TextUtil.Font;
t.fontSize = size;
t.alignment = anchor;
t.color = TextColor;
t.text = text;
t.raycastTarget = false;
t.horizontalOverflow = HorizontalWrapMode.Wrap;
t.verticalOverflow = VerticalWrapMode.Overflow;
return t;
}
/// <summary>왼쪽 위 기준 버튼 · 반환 = 글자(토글 문구 갱신용).</summary>
static Text Btn(Transform parent, string text, Vector2 pos, Vector2 size, Action onClick)
{
var rt = Panel("Btn", parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), pos, size, BtnColor);
var b = rt.gameObject.AddComponent<Button>();
if (onClick != null) b.onClick.AddListener(() => onClick());
var t = Label(rt, text, 30, TextAnchor.MiddleCenter);
SetRect(t.rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
return t;
}
static Button ListItem(Transform content, string text, Action onClick)
{
var go = new GameObject("Item", typeof(RectTransform));
go.transform.SetParent(content, false);
var img = go.AddComponent<Image>();
img.color = BtnColor;
var b = go.AddComponent<Button>();
b.onClick.AddListener(() => onClick());
go.AddComponent<LayoutElement>().preferredHeight = 76f;
var t = Label(go.transform, text, 30, TextAnchor.MiddleLeft);
SetRect(t.rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
t.rectTransform.offsetMin = new Vector2(16f, 0f);
t.rectTransform.offsetMax = new Vector2(-10f, 0f);
t.horizontalOverflow = HorizontalWrapMode.Overflow;
return b;
}
static Transform Scroll(RectTransform host)
{
var view = Rect("Viewport", host, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
view.offsetMin = new Vector2(8f, 8f);
view.offsetMax = new Vector2(-8f, -8f);
view.gameObject.AddComponent<Image>().color = new Color(0f, 0f, 0f, 0.01f);
view.gameObject.AddComponent<RectMask2D>();
var content = Rect("Content", view, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), Vector2.zero, Vector2.zero);
var lay = content.gameObject.AddComponent<VerticalLayoutGroup>();
lay.spacing = 6f;
lay.childControlHeight = true;
lay.childControlWidth = true;
lay.childForceExpandHeight = false;
lay.childForceExpandWidth = true;
content.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var sr = host.gameObject.AddComponent<ScrollRect>();
sr.viewport = view;
sr.content = content;
sr.horizontal = false;
sr.movementType = ScrollRect.MovementType.Clamped;
sr.scrollSensitivity = 60f;
return content;
}
#if UNITY_EDITOR
static InputField SearchField(Transform parent, string placeholder, Vector2 pos, Vector2 size)
{
var rt = Panel("Search", parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), pos, size, new Color(1f, 1f, 1f, 0.92f));
var f = rt.gameObject.AddComponent<InputField>();
var t = Label(rt, "", 30, TextAnchor.MiddleLeft);
t.color = Color.black;
t.supportRichText = false;
SetRect(t.rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
t.rectTransform.offsetMin = new Vector2(14f, 0f);
t.rectTransform.offsetMax = new Vector2(-10f, 0f);
var ph = Label(rt, placeholder, 26, TextAnchor.MiddleLeft);
ph.color = new Color(0f, 0f, 0f, 0.4f);
SetRect(ph.rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
ph.rectTransform.offsetMin = new Vector2(14f, 0f);
f.textComponent = t;
f.placeholder = ph;
return f;
}
#endif
}
}