Project_WL/AgentScripts/WL788_Look.cs

160 lines
11 KiB
C#
Raw Normal View History

WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// WL788_Look.cs — #788 "tail 방식이 아니라 이전에 만든 고퀄리티 이펙트" : 힘민지 원본 슬래시(EricWang FX_Slash) 룩을 무기 추종 리본에 적용
// Edit : unity command run_script --file AgentScripts/WL788_Look.cs --entry WL788_Look.MakeAssets (스트릭 텍스처 + 리본 머티리얼 생성)
// Play : unity command run_script --file AgentScripts/WL788_Look.cs --entry WL788_Look.Apply --args '[1.6, 1, 1]' (emission, blend(1=One,6=OneMinusSrcAlpha), burst(1=ReuseShowEffect,0=None) · 메모리만)
// Edit : unity command run_script --file AgentScripts/WL788_Look.cs --entry WL788_Look.Bake --args '[1.6, 1, 1]' (설정 SO 굽기)
// Any : unity command run_script --file AgentScripts/WL788_Look.cs --entry WL788_Look.Dump
// 구성: ① 리본 머티리얼 = EricWang `Additive.shader`(원본 슬래시와 같은 셰이더) + 원본 `slash_L01.png` 스트릭을 리본 UV 에 맞게 전치(u=잔상축)·흑백화한 `T_WL_SlashStreak.png`
// ② 클래스별 색 = SlashTrailSettings 클래스 오버라이드 colorAdditive(HDR) — 정점색 경로(useGradients 0)
// ③ 원본 슬래시 프리팹의 플레어·글로우·링은 burstMode ReuseShowEffect + crescentRules(크레센트 스프라이트만 마스크) 로 그대로 재생
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public static class WL788_Look
{
const string SrcTexPath = "Assets/ResWork/EricWang/GameVFX Collection(URP)/FX_Slash01/Textures/slash_L01.png";
const string ShaderPath = "Assets/ResWork/EricWang/GameVFX Collection(URP)/Shaders/Additive.shader";
const string TexPath = "Assets/WL/Combat/Textures/T_WL_SlashStreak.png";
const string MatPath = "Assets/WL/Combat/Materials/M_WL_SlashRibbon.mat";
const string SettingsPath = "Assets/WL/Settings/Resources/WL/SlashTrailSettings.asset";
// 클래스별 리본 색(HDR) — 원본 슬래시 프리팹 계열 색: 10101 Blue_Slash · 10102 Orange_Slash · 10501/10502 Lana(노랑·백금)
static readonly (int cid, Color col)[] ClassColors =
{
(10101, new Color(0.55f, 0.85f, 1.60f, 1f)),
(10102, new Color(1.60f, 0.85f, 0.35f, 1f)),
(10501, new Color(1.60f, 1.35f, 0.60f, 1f)),
(10502, new Color(1.30f, 1.20f, 0.80f, 1f)),
(10201, new Color(1.60f, 0.55f, 0.70f, 1f)),
(10202, new Color(1.60f, 0.55f, 0.70f, 1f)),
};
/// <summary>원본 스트릭을 전치(x↔y) + 흑백화해 리본용 텍스처로 굽는다. 리본 UV: u=잔상(1 머리) · v=칼날축.</summary>
public static object MakeAssets()
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var src = AssetDatabase.LoadAssetAtPath<Texture2D>(SrcTexPath);
if (src == null) return "source tex null " + SrcTexPath;
// 읽기 불가 텍스처도 RenderTexture 경유로 픽셀을 얻는다
var rt = RenderTexture.GetTemporary(src.width, src.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
Graphics.Blit(src, rt);
var prev = RenderTexture.active; RenderTexture.active = rt;
var readable = new Texture2D(src.width, src.height, TextureFormat.RGBA32, false);
readable.ReadPixels(new Rect(0, 0, src.width, src.height), 0, 0); readable.Apply();
RenderTexture.active = prev; RenderTexture.ReleaseTemporary(rt);
int W = src.height, H = src.width; // 전치
var dst = new Texture2D(W, H, TextureFormat.RGBA32, false, false);
var sp = readable.GetPixels();
var dp = new Color[W * H];
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++)
{
// dst(x,y) = src(y,x) : src 의 세로(스트릭 방향)가 dst 의 가로(u=잔상축)가 된다
var c = sp[x * src.width + y];
float lum = Mathf.Clamp01(0.2126f * c.r + 0.7152f * c.g + 0.0722f * c.b);
float a = Mathf.Max(c.a, lum); // 원본이 가산용(알파 무의미)일 수 있어 밝기를 알파로도 쓴다
dp[y * W + x] = new Color(lum, lum, lum, a);
}
dst.SetPixels(dp); dst.Apply(false, false);
Directory.CreateDirectory(Path.GetDirectoryName(TexPath));
File.WriteAllBytes(TexPath, dst.EncodeToPNG());
Object.DestroyImmediate(readable); Object.DestroyImmediate(dst);
AssetDatabase.ImportAsset(TexPath, ImportAssetOptions.ForceUpdate);
var imp = AssetImporter.GetAtPath(TexPath) as TextureImporter;
if (imp != null)
{
imp.textureType = TextureImporterType.Default; imp.sRGBTexture = true; imp.alphaSource = TextureImporterAlphaSource.FromInput;
imp.mipmapEnabled = false; imp.wrapMode = TextureWrapMode.Clamp; imp.filterMode = FilterMode.Bilinear;
imp.maxTextureSize = 256; imp.textureCompression = TextureImporterCompression.Uncompressed; imp.isReadable = false;
imp.SaveAndReimport();
}
var tex = AssetDatabase.LoadAssetAtPath<Texture2D>(TexPath);
var shader = AssetDatabase.LoadAssetAtPath<Shader>(ShaderPath);
if (shader == null) return "shader null " + ShaderPath;
var mat = AssetDatabase.LoadAssetAtPath<Material>(MatPath);
if (mat == null) { mat = new Material(shader); AssetDatabase.CreateAsset(mat, MatPath); }
else mat.shader = shader;
mat.SetTexture("_MainTex", tex);
mat.SetColor("_Color", new Color(0.5f, 0.5f, 0.5f, 1f)); // 원본 슬래시 머티리얼 기본값과 동일
mat.SetFloat("_Emission", 1.6f);
mat.SetFloat("_Blend2", 1f); // One One = 원본과 같은 가산
mat.SetFloat("_CullMode", 0f);
mat.renderQueue = 3000;
EditorUtility.SetDirty(mat); AssetDatabase.SaveAssets(); AssetDatabase.Refresh();
return "made " + TexPath + " (" + W + "x" + H + ") + " + MatPath + " shader=" + shader.name;
}
static string ApplyTo(WL.Combat.SlashTrailSettings s, Material mat, float emission, int blend, int burst)
{
s.materialAdditive = mat;
s.useGradients = false; // 정점색 = 클래스 오버라이드 colorAdditive
s.colorAdditive = new Color(1.2f, 1.2f, 1.2f, 1f); // 오버라이드 없는 클래스 기본(백색)
foreach (var cc in ClassColors)
{
var o = s.FindOverride(cc.cid);
if (o == null) continue;
// 이전 팀이 원본 슬래시 색을 실측해 넣어 둔 colorAdditive 가 있으면 그것을 쓴다(최대 채널 > 0.5 = 설정됨)
bool hasColor = Mathf.Max(o.colorAdditive.r, Mathf.Max(o.colorAdditive.g, o.colorAdditive.b)) > 0.5f
&& !(Mathf.Approximately(o.colorAdditive.r, 1f) && Mathf.Approximately(o.colorAdditive.g, 1f) && Mathf.Approximately(o.colorAdditive.b, 1f));
o.overrideLook = true; if (!hasColor) o.colorAdditive = cc.col; o.widthScale = 1f;
}
s.burstMode = (WL.Combat.BurstMode)burst; // 1 = ReuseShowEffect (원본 플레어·글로우 재생)
s.suppressMode = WL.Combat.SuppressMode.CrescentOnly; // crescentRules 에 적힌 크레센트 스프라이트만 마스크
if (mat != null) { mat.SetFloat("_Emission", emission); mat.SetFloat("_Blend2", blend); }
return "applied mat=" + (mat != null ? mat.name : "null") + " emission=" + emission + " blend=" + blend + " burst=" + s.burstMode + " suppress=" + s.suppressMode + " useGradients=0 classOverrides=" + ClassColors.Length;
}
public static object Apply(float emission, int blend, int burst)
{
if (!Application.isPlaying) return "not playing (Edit 모드에서는 Bake)";
var s = WL.Combat.SlashTrailSettings.Instance; if (s == null) return "settings null";
var mat = AssetDatabase.LoadAssetAtPath<Material>(MatPath); if (mat == null) return "mat null — MakeAssets 먼저";
string r = ApplyTo(s, mat, emission, blend, burst);
// BladeTrail 은 생성 시점에 머티리얼을 굽는다 → 이미 떠 있는 트레일의 렌더러를 직접 바꿔 준다(Play 검증용)
int swapped = 0;
foreach (var drv in Object.FindObjectsByType<WL.Combat.WeaponTrailDriver>(FindObjectsInactive.Include, FindObjectsSortMode.None))
for (int i = 0; i < 4; i++)
{
var t = drv.GetTrail(i); if (t == null) continue;
var mr = t.GetComponent<MeshRenderer>(); if (mr == null) continue;
mr.sharedMaterials = new[] { mat }; swapped++;
}
return r + " · 런타임 트레일 렌더러 교체=" + swapped;
}
public static object Bake(float emission, int blend, int burst)
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var s = AssetDatabase.LoadAssetAtPath<WL.Combat.SlashTrailSettings>(SettingsPath); if (s == null) return "settings null";
var mat = AssetDatabase.LoadAssetAtPath<Material>(MatPath); if (mat == null) return "mat null — MakeAssets 먼저";
string r = ApplyTo(s, mat, emission, blend, burst);
EditorUtility.SetDirty(s); EditorUtility.SetDirty(mat); AssetDatabase.SaveAssets();
return "baked: " + r;
}
/// <summary>Play: 버스트 모드만 바꾼다(0 None · 1 ReuseShowEffect = 원본 슬래시 프리팹의 플레어/글로우 재생 · 2 PrefabArc).</summary>
public static object Burst(int mode, int timing)
{
if (!Application.isPlaying) return "not playing";
var s = WL.Combat.SlashTrailSettings.Instance; if (s == null) return "settings null";
s.burstMode = (WL.Combat.BurstMode)mode;
s.burstTiming = (WL.Combat.BurstTiming)timing;
s.suppressMode = WL.Combat.SuppressMode.CrescentOnly;
return "burst=" + s.burstMode + " timing=" + s.burstTiming + " suppress=" + s.suppressMode;
}
public static object Dump()
{
var s = Application.isPlaying ? WL.Combat.SlashTrailSettings.Instance : AssetDatabase.LoadAssetAtPath<WL.Combat.SlashTrailSettings>(SettingsPath);
var sb = new StringBuilder();
if (s == null) return "settings null";
sb.AppendLine("mat=" + (s.materialAdditive != null ? s.materialAdditive.name + " / " + s.materialAdditive.shader.name : "null") + " useGradients=" + s.useGradients + " burst=" + s.burstMode + " suppress=" + s.suppressMode + " drawRibbon=" + s.drawRibbon + " fade=" + s.fadeSeconds);
foreach (var cc in ClassColors) { var o = s.FindOverride(cc.cid); sb.AppendLine(" " + cc.cid + " override=" + (o != null ? o.overrideLook + " col=" + o.colorAdditive : "none")); }
if (s.materialAdditive != null && s.materialAdditive.HasProperty("_Emission")) sb.AppendLine(" emission=" + s.materialAdditive.GetFloat("_Emission") + " blend2=" + s.materialAdditive.GetFloat("_Blend2") + " tex=" + (s.materialAdditive.GetTexture("_MainTex") != null ? s.materialAdditive.GetTexture("_MainTex").name : "none"));
return sb.ToString();
}
}