[WL-814q] 시범 전투장 씬(Critter 만듦새 · 반경 14m) (#814)
- 새 씬 Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity (기존 씬·빌드 씬 목록 수정 0) - 바닥 = Unity Terrain 80x12x80 + Demo_Grass/Demo_Dirt 레이어 + Demo Terrain.mat(ToonTerrain) — 데모 실측 방식 그대로 - 경계 = 반경 14 m 에 Stone_1/2 + Tree + Arch_Large/Small · 연못 Water_Plane · 배경 Windmill/House - 캐릭터 LH_M05 중앙(스케일 1.05266 = f_Scale 0.7 x 보정 1.5038) · 프리팹 수정 0 - 조명/ambient/fog/스카이박스 = 데모 실측값 그대로 - 머티리얼 14종(+Terrain.mat) · 삼각형 29,875 (목표 15종/5만 이내) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2d83d05a58
commit
6e9f711529
|
|
@ -0,0 +1,475 @@
|
|||
// WL-814q — 시범 전투장 씬 신규 제작 (Critter Demo 만듦새 재현 · 반경 14 m)
|
||||
// 데모 실측값(AgentScripts/WL814q_Probe.cs · Demo.unity YAML)을 그대로 재현한다. 원본 에셋 수정 0(참조만).
|
||||
public static class WL814q_BuildArena
|
||||
{
|
||||
const string SceneDir = "Assets/WL/Look/Arena/Scenes";
|
||||
const string TerrDir = "Assets/WL/Look/Arena/Terrain";
|
||||
const string ScenePath = SceneDir + "/WL_ArenaProto.unity";
|
||||
const string TerrPath = TerrDir + "/WL_ArenaTerrain.asset";
|
||||
const string PfxDir = "Assets/3DPixelArtEnvironment/Prefabs/";
|
||||
const string DemoDir = "Assets/3DPixelArtEnvironment/Demo/";
|
||||
const string MatDir = "Assets/3DPixelArtEnvironment/Materials/";
|
||||
|
||||
const float TerrSize = 80f; // m (데모 200 m · 반경 14 m 아레나에 맞춰 축소)
|
||||
const float TerrHeight = 12f; // m (데모 100 m)
|
||||
const int HmRes = 65; // 64×64 쿼드 = 8,192 tri (데모 513 은 삼각형 예산 초과)
|
||||
const int AlphaRes = 256;
|
||||
const float TerrY = -1.0f; // 아레나 바닥이 월드 y=0 이 되도록
|
||||
const float BaseH = 1.0f / TerrHeight;
|
||||
|
||||
const float Barrier = 14f; // WLStageTable barrierRadius
|
||||
const float StartR = 2f; // WLStageTable startRadius
|
||||
|
||||
static readonly UnityEngine.Vector2 PondC = new UnityEngine.Vector2(17.5f, -10.5f);
|
||||
const float PondR = 6.0f;
|
||||
const float WaterY = -0.10f;
|
||||
|
||||
static System.Random rnd;
|
||||
static UnityEngine.Terrain terrain;
|
||||
static System.Text.StringBuilder log;
|
||||
|
||||
/// <summary>HLSL smoothstep. 🔴 Unity 의 Mathf.SmoothStep(from,to,t) 은 이것과 다르다(from~to 보간).</summary>
|
||||
static float SStep(float e0, float e1, float x)
|
||||
{
|
||||
float t = UnityEngine.Mathf.Clamp01((x - e0) / (e1 - e0));
|
||||
return t * t * (3f - 2f * t);
|
||||
}
|
||||
|
||||
static UnityEngine.GameObject Pfx(string n)
|
||||
{
|
||||
return UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(PfxDir + n + ".prefab");
|
||||
}
|
||||
|
||||
static float Rf(float a, float b) { return a + (float)rnd.NextDouble() * (b - a); }
|
||||
static float Mirror() { return (rnd.NextDouble() < 0.5) ? -1f : 1f; }
|
||||
|
||||
static UnityEngine.GameObject Place(string prefab, UnityEngine.Transform parent, string name,
|
||||
float wx, float wz, float yaw, UnityEngine.Vector3 scale, float sink, bool alignByBounds)
|
||||
{
|
||||
var src = Pfx(prefab);
|
||||
if (src == null) { log.AppendLine("MISSING PREFAB " + prefab); return null; }
|
||||
var go = (UnityEngine.GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(src, parent);
|
||||
go.name = name;
|
||||
go.transform.localScale = scale;
|
||||
go.transform.rotation = UnityEngine.Quaternion.Euler(0f, yaw, 0f);
|
||||
go.transform.position = new UnityEngine.Vector3(wx, 0f, wz);
|
||||
|
||||
float groundY = terrain != null
|
||||
? terrain.SampleHeight(new UnityEngine.Vector3(wx, 0f, wz)) + terrain.transform.position.y : 0f;
|
||||
|
||||
var b = Bounds(go);
|
||||
if (alignByBounds)
|
||||
{
|
||||
go.transform.position += new UnityEngine.Vector3(wx - b.center.x, 0f, wz - b.center.z);
|
||||
b = Bounds(go);
|
||||
}
|
||||
go.transform.position += new UnityEngine.Vector3(0f, groundY - b.min.y - sink, 0f);
|
||||
return go;
|
||||
}
|
||||
|
||||
static UnityEngine.Bounds Bounds(UnityEngine.GameObject go)
|
||||
{
|
||||
var rs = go.GetComponentsInChildren<UnityEngine.Renderer>(true);
|
||||
var b = new UnityEngine.Bounds(go.transform.position, UnityEngine.Vector3.zero);
|
||||
bool f = true;
|
||||
foreach (var r in rs) { if (f) { b = r.bounds; f = false; } else b.Encapsulate(r.bounds); }
|
||||
return b;
|
||||
}
|
||||
|
||||
static float PondAngle { get { return UnityEngine.Mathf.Atan2(PondC.y, PondC.x) * UnityEngine.Mathf.Rad2Deg; } }
|
||||
static bool NearPond(float a) { return UnityEngine.Mathf.Abs(UnityEngine.Mathf.DeltaAngle(a, PondAngle)) < 15f; }
|
||||
|
||||
public static string Run()
|
||||
{
|
||||
log = new System.Text.StringBuilder();
|
||||
rnd = new System.Random(814);
|
||||
|
||||
if (!UnityEditor.AssetDatabase.IsValidFolder("Assets/WL/Look/Arena"))
|
||||
UnityEditor.AssetDatabase.CreateFolder("Assets/WL/Look", "Arena");
|
||||
if (!UnityEditor.AssetDatabase.IsValidFolder(SceneDir))
|
||||
UnityEditor.AssetDatabase.CreateFolder("Assets/WL/Look/Arena", "Scenes");
|
||||
if (!UnityEditor.AssetDatabase.IsValidFolder(TerrDir))
|
||||
UnityEditor.AssetDatabase.CreateFolder("Assets/WL/Look/Arena", "Terrain");
|
||||
|
||||
var scene = UnityEditor.SceneManagement.EditorSceneManager.NewScene(
|
||||
UnityEditor.SceneManagement.NewSceneSetup.EmptyScene,
|
||||
UnityEditor.SceneManagement.NewSceneMode.Single);
|
||||
|
||||
// ── 1. 지형 (데모와 같은 방식: Unity Terrain + Demo 터레인 레이어 2종 + Terrain.mat[ToonTerrain])
|
||||
var td = new UnityEngine.TerrainData();
|
||||
td.heightmapResolution = HmRes;
|
||||
td.size = new UnityEngine.Vector3(TerrSize, TerrHeight, TerrSize);
|
||||
td.alphamapResolution = AlphaRes;
|
||||
td.SetDetailResolution(0, 8);
|
||||
|
||||
float half = TerrSize * 0.5f;
|
||||
var h = new float[HmRes, HmRes];
|
||||
for (int y = 0; y < HmRes; y++)
|
||||
for (int x = 0; x < HmRes; x++)
|
||||
{
|
||||
float wx = (float)x / (HmRes - 1) * TerrSize - half;
|
||||
float wz = (float)y / (HmRes - 1) * TerrSize - half;
|
||||
float r = UnityEngine.Mathf.Sqrt(wx * wx + wz * wz);
|
||||
// 전투장 안은 평지 + 아주 얕은 기복(툰 셰이딩 단차가 보이게 · 이동엔 영향 없음)
|
||||
float ripple = (UnityEngine.Mathf.PerlinNoise(wx * 0.14f + 31.7f, wz * 0.14f + 19.3f) - 0.5f) * 0.14f;
|
||||
float v = BaseH + ripple / TerrHeight;
|
||||
if (r > 15.5f)
|
||||
{
|
||||
float t = SStep(15.5f, 38f, r);
|
||||
float n = UnityEngine.Mathf.PerlinNoise(wx * 0.035f + 11.3f, wz * 0.035f + 7.1f);
|
||||
v += (0.4f + n * 2.2f) * t / TerrHeight;
|
||||
}
|
||||
float pr = UnityEngine.Mathf.Sqrt((wx - PondC.x) * (wx - PondC.x) + (wz - PondC.y) * (wz - PondC.y));
|
||||
if (pr < PondR)
|
||||
v = UnityEngine.Mathf.Lerp(v, 0.15f / TerrHeight, SStep(PondR, PondR * 0.25f, pr));
|
||||
h[y, x] = v;
|
||||
}
|
||||
var grass = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TerrainLayer>(DemoDir + "Demo_Grass.terrainlayer");
|
||||
var dirt = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TerrainLayer>(DemoDir + "Demo_Dirt.terrainlayer");
|
||||
td.terrainLayers = new UnityEngine.TerrainLayer[] { grass, dirt }; // 0=R채널(풀 초록) · 1=G채널(흙 갈색) — Terrain.mat 실측
|
||||
|
||||
var alpha = new float[AlphaRes, AlphaRes, 2];
|
||||
for (int y = 0; y < AlphaRes; y++)
|
||||
for (int x = 0; x < AlphaRes; x++)
|
||||
{
|
||||
float wx = (float)x / (AlphaRes - 1) * TerrSize - half;
|
||||
float wz = (float)y / (AlphaRes - 1) * TerrSize - half;
|
||||
float n = UnityEngine.Mathf.PerlinNoise(wx * 0.16f + 3.7f, wz * 0.16f + 5.2f);
|
||||
float r = UnityEngine.Mathf.Sqrt(wx * wx + wz * wz) + (n - 0.5f) * 3.2f;
|
||||
float d = 1f - SStep(11.0f, 15.0f, r); // 흙 = 전투장 바닥(반경 ~13 m)
|
||||
float pr = UnityEngine.Mathf.Sqrt((wx - PondC.x) * (wx - PondC.x) + (wz - PondC.y) * (wz - PondC.y));
|
||||
d = UnityEngine.Mathf.Max(d, 1f - SStep(PondR - 1.0f, PondR + 1.8f, pr)); // 물가 = 흙
|
||||
alpha[y, x, 1] = UnityEngine.Mathf.Clamp01(d);
|
||||
alpha[y, x, 0] = 1f - UnityEngine.Mathf.Clamp01(d);
|
||||
}
|
||||
// 🔴 높이·스플랫은 에셋을 만든 뒤에 써야 남는다(CreateAsset 전에 쓰면 ch1 이 0 으로 날아간다 — 실측)
|
||||
UnityEditor.AssetDatabase.CreateAsset(td, TerrPath);
|
||||
td.SetHeights(0, 0, h);
|
||||
td.SetAlphamaps(0, 0, alpha);
|
||||
UnityEditor.EditorUtility.SetDirty(td);
|
||||
UnityEditor.AssetDatabase.SaveAssets();
|
||||
|
||||
var terrGo = UnityEngine.Terrain.CreateTerrainGameObject(td);
|
||||
terrGo.name = "Terrain";
|
||||
terrGo.transform.position = new UnityEngine.Vector3(-half, TerrY, -half);
|
||||
terrain = terrGo.GetComponent<UnityEngine.Terrain>();
|
||||
terrain.materialTemplate = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(MatDir + "Terrain.mat");
|
||||
terrain.heightmapPixelError = 5f;
|
||||
terrain.drawTreesAndFoliage = false;
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(terrGo, scene);
|
||||
AddTerrainInstancing(terrGo);
|
||||
|
||||
// ── 2. 조명 (데모 실측 그대로)
|
||||
var lightRoot = new UnityEngine.GameObject("Lighting");
|
||||
var dl = new UnityEngine.GameObject("Directional Light");
|
||||
dl.transform.SetParent(lightRoot.transform);
|
||||
var dlc = dl.AddComponent<UnityEngine.Light>();
|
||||
dlc.type = UnityEngine.LightType.Directional;
|
||||
dlc.color = UnityEngine.Color.white;
|
||||
dlc.intensity = 1f;
|
||||
dlc.shadows = UnityEngine.LightShadows.Soft;
|
||||
dl.transform.position = new UnityEngine.Vector3(0f, 3f, 0f);
|
||||
dl.transform.rotation = new UnityEngine.Quaternion(0.40821788f, -0.23456968f, 0.10938163f, 0.8754261f);
|
||||
var warm = new UnityEngine.Color(1f, 0.9639759f, 0.63836473f, 1f);
|
||||
|
||||
// ── 3. 렌더 설정 (데모 실측)
|
||||
UnityEngine.RenderSettings.fog = false;
|
||||
UnityEngine.RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Skybox;
|
||||
UnityEngine.RenderSettings.ambientSkyColor = new UnityEngine.Color(0.212f, 0.227f, 0.259f, 1f);
|
||||
UnityEngine.RenderSettings.ambientEquatorColor = new UnityEngine.Color(0.114f, 0.125f, 0.133f, 1f);
|
||||
UnityEngine.RenderSettings.ambientGroundColor = new UnityEngine.Color(0.047f, 0.043f, 0.035f, 1f);
|
||||
UnityEngine.RenderSettings.ambientIntensity = 1f;
|
||||
UnityEngine.RenderSettings.skybox = UnityEngine.Resources.GetBuiltinResource<UnityEngine.Material>("Default-Skybox.mat");
|
||||
UnityEngine.RenderSettings.subtractiveShadowColor = new UnityEngine.Color(0.42f, 0.478f, 0.627f, 1f);
|
||||
|
||||
// ── 4. 경계(반경 14 m) — 바위 띠를 촘촘히 + 나무 숲 + 아치 2개
|
||||
var ring = new UnityEngine.GameObject("Arena_Ring");
|
||||
int nStone = 56;
|
||||
for (int i = 0; i < nStone; i++)
|
||||
{
|
||||
float a = (float)i / nStone * 360f + Rf(-2.5f, 2.5f);
|
||||
if (UnityEngine.Mathf.Abs(UnityEngine.Mathf.DeltaAngle(a, 0f)) < 13f) continue; // 정문
|
||||
if (UnityEngine.Mathf.Abs(UnityEngine.Mathf.DeltaAngle(a, 180f)) < 11f) continue; // 후문
|
||||
if (NearPond(a)) continue; // 연못 쪽은 트인다
|
||||
float r = Barrier + Rf(0.1f, 1.4f);
|
||||
float rad = a * UnityEngine.Mathf.Deg2Rad;
|
||||
bool big = rnd.NextDouble() < 0.6;
|
||||
float s = big ? Rf(1.0f, 1.8f) : Rf(1.1f, 2.0f);
|
||||
// 변형: y 회전 + 비균일 스케일 + x 음수 스케일(거울) — 데모 Stone_2 실측 기법 그대로
|
||||
var sc = new UnityEngine.Vector3(s * Mirror() * Rf(0.8f, 1.3f), s * Rf(0.8f, 1.6f), s * Rf(0.85f, 1.2f));
|
||||
Place(big ? "Stone_2" : "Stone_1", ring.transform, (big ? "Stone_2_" : "Stone_1_") + i,
|
||||
UnityEngine.Mathf.Cos(rad) * r, UnityEngine.Mathf.Sin(rad) * r, Rf(0f, 360f), sc, Rf(0.1f, 0.5f), false);
|
||||
}
|
||||
int nTree = 20;
|
||||
for (int i = 0; i < nTree; i++)
|
||||
{
|
||||
float a = (float)i / nTree * 360f + Rf(-6f, 6f);
|
||||
if (UnityEngine.Mathf.Abs(UnityEngine.Mathf.DeltaAngle(a, 0f)) < 18f) continue;
|
||||
if (NearPond(a)) continue;
|
||||
float r = Barrier + Rf(2.2f, 5.0f);
|
||||
float rad = a * UnityEngine.Mathf.Deg2Rad;
|
||||
float s = Rf(0.38f, 0.62f);
|
||||
Place("Tree", ring.transform, "Tree_Ring_" + i,
|
||||
UnityEngine.Mathf.Cos(rad) * r, UnityEngine.Mathf.Sin(rad) * r, Rf(0f, 360f),
|
||||
new UnityEngine.Vector3(s * Mirror(), s * Rf(0.85f, 1.25f), s), 0.05f, false);
|
||||
}
|
||||
Place("Arch_Large", ring.transform, "Gate_Arch_Large", Barrier + 0.6f, 0f, -90f,
|
||||
UnityEngine.Vector3.one * 0.34f, 0.05f, true);
|
||||
Place("Arch_Small", ring.transform, "Gate_Arch_Small", -(Barrier + 0.4f), 0f, 90f,
|
||||
UnityEngine.Vector3.one * 0.34f, 0.05f, true);
|
||||
|
||||
// ── 5. 전투장 바닥 (Slab 포석 + 잔돌 · 중앙 반경 2 m 는 비운다)
|
||||
var floor = new UnityEngine.GameObject("Arena_Floor");
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
float a = Rf(0f, 360f);
|
||||
float r = Rf(StartR + 1.2f, 12.2f);
|
||||
float rad = a * UnityEngine.Mathf.Deg2Rad;
|
||||
float s = Rf(0.26f, 0.50f); // Slab 원 크기 3.37×0.91×4.41 m → 0.9~2.2 m 포석
|
||||
var g = Place("Slab", floor.transform, "Slab_" + i,
|
||||
UnityEngine.Mathf.Cos(rad) * r, UnityEngine.Mathf.Sin(rad) * r, Rf(0f, 360f),
|
||||
new UnityEngine.Vector3(s * Rf(0.85f, 1.25f), s * Rf(0.7f, 1.1f), s * Rf(0.85f, 1.25f)), Rf(0.12f, 0.26f), false);
|
||||
// 변형: z 180° 뒤집기 — 데모 Slab (2)(3) 실측 기법
|
||||
if (g != null && rnd.NextDouble() < 0.35)
|
||||
g.transform.rotation = UnityEngine.Quaternion.Euler(0f, g.transform.eulerAngles.y, 180f);
|
||||
}
|
||||
for (int i = 0; i < 26; i++) // 잔돌 (Stone.mat · 새 머티리얼 0)
|
||||
{
|
||||
float a = Rf(0f, 360f);
|
||||
float r = Rf(StartR + 0.6f, 13.2f);
|
||||
float rad = a * UnityEngine.Mathf.Deg2Rad;
|
||||
float s = Rf(0.10f, 0.28f);
|
||||
Place((rnd.NextDouble() < 0.5) ? "Stone_1" : "Stone_2", floor.transform, "Pebble_" + i,
|
||||
UnityEngine.Mathf.Cos(rad) * r, UnityEngine.Mathf.Sin(rad) * r, Rf(0f, 360f),
|
||||
new UnityEngine.Vector3(s * Mirror() * Rf(0.8f, 1.4f), s * Rf(0.5f, 1.0f), s * Rf(0.8f, 1.4f)),
|
||||
Rf(0.01f, 0.06f), false);
|
||||
}
|
||||
// 가로등 2개 + 데모의 따뜻한 포인트 라이트 2개
|
||||
float[] lpA = { 58f, 236f };
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
float rad = lpA[i] * UnityEngine.Mathf.Deg2Rad;
|
||||
float px = UnityEngine.Mathf.Cos(rad) * 12.4f, pz = UnityEngine.Mathf.Sin(rad) * 12.4f;
|
||||
var lp = Place("Light_Post", floor.transform, "Light_Post_" + i, px, pz, lpA[i] + 180f,
|
||||
UnityEngine.Vector3.one * 0.22f, 0f, false);
|
||||
var pl = new UnityEngine.GameObject("Point Light " + i);
|
||||
pl.transform.SetParent(lightRoot.transform);
|
||||
pl.transform.position = new UnityEngine.Vector3(px, (lp != null ? Bounds(lp).max.y - 0.35f : 3.5f), pz);
|
||||
var plc = pl.AddComponent<UnityEngine.Light>();
|
||||
plc.type = UnityEngine.LightType.Point;
|
||||
plc.color = warm; plc.intensity = 0.54f; plc.range = 6.14f;
|
||||
plc.shadows = UnityEngine.LightShadows.None;
|
||||
}
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(lightRoot, scene);
|
||||
|
||||
// ── 6. 연못 (Water_Plane · 데모의 흰 거품 가장자리)
|
||||
var pondRoot = new UnityEngine.GameObject("Pond");
|
||||
var water = (UnityEngine.GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(Pfx("Water_Plane"), pondRoot.transform);
|
||||
water.name = "Water_Plane";
|
||||
water.transform.position = new UnityEngine.Vector3(PondC.x, WaterY, PondC.y);
|
||||
water.transform.localScale = new UnityEngine.Vector3(PondR * 2.15f / 10f, 1f, PondR * 1.9f / 10f);
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(pondRoot, scene);
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
float a = Rf(0f, 360f) * UnityEngine.Mathf.Deg2Rad;
|
||||
float s = Rf(0.6f, 1.3f);
|
||||
Place("Stone_1", pondRoot.transform, "Pond_Stone_" + i,
|
||||
PondC.x + UnityEngine.Mathf.Cos(a) * Rf(PondR * 1.0f, PondR * 1.35f),
|
||||
PondC.y + UnityEngine.Mathf.Sin(a) * Rf(PondR * 1.0f, PondR * 1.35f),
|
||||
Rf(0f, 360f), new UnityEngine.Vector3(s * Mirror(), s * Rf(0.7f, 1.1f), s), Rf(0.1f, 0.4f), false);
|
||||
}
|
||||
|
||||
// ── 7. 배경 랜드마크 (원근감)
|
||||
var bg = new UnityEngine.GameObject("Background");
|
||||
Place("Windmill", bg.transform, "Windmill", -20f, 28f, 143f, UnityEngine.Vector3.one * 3.2f, 0.1f, false);
|
||||
Place("House", bg.transform, "House_1", 25f, 23f, -142f, UnityEngine.Vector3.one * 4.6f, 0.1f, false);
|
||||
Place("House", bg.transform, "House_2", 31f, 16f, -118f, UnityEngine.Vector3.one * 4.0f, 0.1f, false);
|
||||
for (int i = 0; i < 14; i++)
|
||||
{
|
||||
float a = Rf(0f, 360f) * UnityEngine.Mathf.Deg2Rad;
|
||||
float r = Rf(22f, 36f);
|
||||
float s = Rf(0.5f, 0.9f);
|
||||
Place("Tree", bg.transform, "Tree_BG_" + i,
|
||||
UnityEngine.Mathf.Cos(a) * r, UnityEngine.Mathf.Sin(a) * r, Rf(0f, 360f),
|
||||
new UnityEngine.Vector3(s * Mirror(), s * Rf(0.85f, 1.25f), s), 0.05f, false);
|
||||
}
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(bg, scene);
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(ring, scene);
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(floor, scene);
|
||||
|
||||
// ── 8. 캐릭터 (중앙 · 인게임과 같은 월드 높이 1.1912 m)
|
||||
float pcScale = 0.7f * 1.5038f; // ClassConfig f_Scale × WLCharacterSwapSettings.heightCompensation
|
||||
var pcSrc = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>("Assets/Res_Addr/PC/LH_M05.prefab");
|
||||
var pc = (UnityEngine.GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(pcSrc);
|
||||
pc.name = "PC_LH_M05";
|
||||
pc.transform.localScale = UnityEngine.Vector3.one * pcScale;
|
||||
pc.transform.position = UnityEngine.Vector3.zero;
|
||||
pc.transform.rotation = UnityEngine.Quaternion.Euler(0f, 200f, 0f);
|
||||
var comp = pc.GetComponent("WL.Character.WLPcScaleCompensator") as UnityEngine.MonoBehaviour;
|
||||
if (comp != null)
|
||||
{ // 씬 인스턴스 한정 오버라이드 — Play 때 스케일이 다시 곱해지지 않게(프리팹 수정 0)
|
||||
var so = new UnityEditor.SerializedObject(comp);
|
||||
var p = so.FindProperty("overrideCompensation");
|
||||
if (p != null) { p.floatValue = 1f; so.ApplyModifiedProperties(); }
|
||||
}
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(pc, scene);
|
||||
log.AppendLine(PoseIdle(pc));
|
||||
var pb = Bounds(pc);
|
||||
log.AppendLine(string.Format("PC scale={0:F5} worldHeight={1:F4} m (min.y={2:F4} max.y={3:F4})",
|
||||
pcScale, pb.size.y, pb.min.y, pb.max.y));
|
||||
|
||||
// ── 9. 카메라 (인게임 도트A 와 같은 각도·거리 · CameraFramingOverride 수식 실측)
|
||||
float H = 80f, D = 3f, Up = 3f, lookAtY = 1.06f;
|
||||
float rr = H * UnityEngine.Mathf.Deg2Rad;
|
||||
float camUp = Up + D * (UnityEngine.Mathf.Sin(rr) - UnityEngine.Mathf.Cos(rr));
|
||||
float ss = UnityEngine.Mathf.Sin(rr) + UnityEngine.Mathf.Cos(rr);
|
||||
float camHoriz = D * UnityEngine.Mathf.Sqrt(1f + ss * ss);
|
||||
float yaw = 200f;
|
||||
var dir = new UnityEngine.Vector3(UnityEngine.Mathf.Sin(yaw * UnityEngine.Mathf.Deg2Rad), 0f,
|
||||
UnityEngine.Mathf.Cos(yaw * UnityEngine.Mathf.Deg2Rad));
|
||||
var camGo = new UnityEngine.GameObject("Main Camera");
|
||||
camGo.tag = "MainCamera";
|
||||
var cam = camGo.AddComponent<UnityEngine.Camera>();
|
||||
camGo.AddComponent<UnityEngine.AudioListener>();
|
||||
camGo.transform.position = UnityEngine.Vector3.up * camUp - dir * camHoriz;
|
||||
camGo.transform.LookAt(UnityEngine.Vector3.up * lookAtY);
|
||||
cam.orthographic = true;
|
||||
cam.orthographicSize = 3.4f;
|
||||
cam.nearClipPlane = 0.3f;
|
||||
cam.farClipPlane = 500f;
|
||||
cam.clearFlags = UnityEngine.CameraClearFlags.Skybox;
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene(camGo, scene);
|
||||
log.AppendLine(string.Format("Camera pos={0} euler={1} camUp={2:F4} camHoriz={3:F4} down={4:F2}deg orthoSize=3.4 · 지면 깊이={5:F2} m",
|
||||
camGo.transform.position, camGo.transform.eulerAngles, camUp, camHoriz,
|
||||
UnityEngine.Mathf.Atan2(camUp - lookAtY, camHoriz) * UnityEngine.Mathf.Rad2Deg,
|
||||
6.8f / UnityEngine.Mathf.Sin(UnityEngine.Mathf.Atan2(camUp - lookAtY, camHoriz))));
|
||||
|
||||
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene, ScenePath);
|
||||
UnityEditor.AssetDatabase.SaveAssets();
|
||||
UnityEditor.AssetDatabase.Refresh();
|
||||
log.AppendLine("SAVED " + ScenePath);
|
||||
log.Append(WL814q_Stats.Measure());
|
||||
return log.ToString();
|
||||
}
|
||||
|
||||
/// <summary>데모의 지면 인스턴싱(풀·꽃·자갈)을 실측 설정 그대로 얹는다. 🔴 ExecuteAlways 가 아니라 Play 에서만 그려진다.</summary>
|
||||
static void AddTerrainInstancing(UnityEngine.GameObject terrGo)
|
||||
{
|
||||
var t = System.Type.GetType("Environment.Instancing.TerrainInstancesBehaviour, Assembly-CSharp");
|
||||
if (t == null) { log.AppendLine("TerrainInstancesBehaviour type NOT FOUND — 생략"); return; }
|
||||
var c = terrGo.AddComponent(t) as UnityEngine.MonoBehaviour;
|
||||
var so = new UnityEditor.SerializedObject(c);
|
||||
// 데모 Demo.unity 실측값
|
||||
SetLayer(so, "FirstLayer", 2.5f, new string[] { "1f298817fdd2a184480e8af5b21278bc", "dab2c84bf0b5ced4aaf0cffc4c491d55" },
|
||||
new string[] { "a194cb4ce6027c246af528b83b728a15", "d50a40cb47ec86444b898d9121bbe0f7" },
|
||||
new float[] { 100f, 1f }, new float[] { 0.35f, 0.35f }, new float[] { 0.04f, 0.04f });
|
||||
SetLayer(so, "SecondLayer", 0.05f, new string[] { "7f1f11ac2c783374b876f8dc2bb7c7db" },
|
||||
new string[] { "77d84437c100f274aad603219110357d" },
|
||||
new float[] { 1f }, new float[] { 0.7f }, new float[] { 0.1f });
|
||||
var pv = so.FindProperty("PositionVariance"); if (pv != null) pv.floatValue = 0.5f;
|
||||
var sv = so.FindProperty("ScaleVariance"); if (sv != null) sv.floatValue = 0.2f;
|
||||
so.ApplyModifiedProperties();
|
||||
log.AppendLine("TerrainInstancesBehaviour 적용(데모 실측값) — 에디트 모드 캡처엔 안 나옴(ExecuteAlways 아님)");
|
||||
}
|
||||
|
||||
static void SetLayer(UnityEditor.SerializedObject so, string layer, float density,
|
||||
string[] meshGuid, string[] matGuid, float[] prob, float[] scale, float[] normOff)
|
||||
{
|
||||
var lp = so.FindProperty(layer);
|
||||
if (lp == null) { log.AppendLine("no prop " + layer); return; }
|
||||
lp.FindPropertyRelative("Density").floatValue = density;
|
||||
var arr = lp.FindPropertyRelative("Settings");
|
||||
arr.arraySize = meshGuid.Length;
|
||||
for (int i = 0; i < meshGuid.Length; i++)
|
||||
{
|
||||
var e = arr.GetArrayElementAtIndex(i);
|
||||
e.FindPropertyRelative("Mesh").objectReferenceValue = FirstMesh(meshGuid[i]);
|
||||
e.FindPropertyRelative("Material").objectReferenceValue =
|
||||
UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(
|
||||
UnityEditor.AssetDatabase.GUIDToAssetPath(matGuid[i]));
|
||||
e.FindPropertyRelative("Probability").floatValue = prob[i];
|
||||
e.FindPropertyRelative("Scale").floatValue = scale[i];
|
||||
e.FindPropertyRelative("NormalOffset").floatValue = normOff[i];
|
||||
}
|
||||
}
|
||||
|
||||
static UnityEngine.Mesh FirstMesh(string guid)
|
||||
{
|
||||
string p = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
||||
foreach (var o in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(p))
|
||||
if (o is UnityEngine.Mesh) return (UnityEngine.Mesh)o;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>T 포즈를 피한다 — 컨트롤러의 idle 클립 1프레임을 씬 인스턴스에 굽는다(프리팹 수정 0).</summary>
|
||||
static string PoseIdle(UnityEngine.GameObject pc)
|
||||
{
|
||||
var an = pc.GetComponentInChildren<UnityEngine.Animator>(true);
|
||||
if (an == null) return "PoseIdle: Animator 없음";
|
||||
UnityEngine.AnimationClip pick = null;
|
||||
if (an.runtimeAnimatorController != null)
|
||||
foreach (var cl in an.runtimeAnimatorController.animationClips)
|
||||
{
|
||||
if (cl == null) continue;
|
||||
string n = cl.name.ToLower();
|
||||
if (n.Contains("idle") || n.Contains("stand")) { pick = cl; break; }
|
||||
if (pick == null) pick = cl;
|
||||
}
|
||||
if (pick == null) return "PoseIdle: 클립 없음(T 포즈 유지)";
|
||||
bool prev = UnityEditor.AnimationMode.InAnimationMode();
|
||||
if (!prev) UnityEditor.AnimationMode.StartAnimationMode();
|
||||
UnityEditor.AnimationMode.BeginSampling();
|
||||
UnityEditor.AnimationMode.SampleAnimationClip(an.gameObject, pick, 0.4f);
|
||||
UnityEditor.AnimationMode.EndSampling();
|
||||
// 🔴 StopAnimationMode 가 트랜스폼을 되돌린다 — 샘플된 포즈를 먼저 떠 두고 나중에 다시 쓴다
|
||||
var ts = pc.GetComponentsInChildren<UnityEngine.Transform>(true);
|
||||
var p = new UnityEngine.Vector3[ts.Length];
|
||||
var q = new UnityEngine.Quaternion[ts.Length];
|
||||
var s = new UnityEngine.Vector3[ts.Length];
|
||||
for (int i = 0; i < ts.Length; i++)
|
||||
{ p[i] = ts[i].localPosition; q[i] = ts[i].localRotation; s[i] = ts[i].localScale; }
|
||||
if (!prev) UnityEditor.AnimationMode.StopAnimationMode();
|
||||
for (int i = 0; i < ts.Length; i++)
|
||||
{ ts[i].localPosition = p[i]; ts[i].localRotation = q[i]; ts[i].localScale = s[i]; }
|
||||
return "PoseIdle: '" + pick.name + "' @0.4s 샘플(스냅샷 재적용)";
|
||||
}
|
||||
}
|
||||
|
||||
public static class WL814q_Stats
|
||||
{
|
||||
public static string Measure()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||
var mats = new System.Collections.Generic.HashSet<UnityEngine.Material>();
|
||||
int tris = 0, rend = 0;
|
||||
foreach (var root in scene.GetRootGameObjects())
|
||||
foreach (var r in root.GetComponentsInChildren<UnityEngine.Renderer>(true))
|
||||
{
|
||||
if (r is UnityEngine.ParticleSystemRenderer) continue;
|
||||
rend++;
|
||||
foreach (var m in r.sharedMaterials) if (m != null) mats.Add(m);
|
||||
var mf = r.GetComponent<UnityEngine.MeshFilter>();
|
||||
if (mf != null && mf.sharedMesh != null) tris += mf.sharedMesh.triangles.Length / 3;
|
||||
var smr = r as UnityEngine.SkinnedMeshRenderer;
|
||||
if (smr != null && smr.sharedMesh != null) tris += smr.sharedMesh.triangles.Length / 3;
|
||||
}
|
||||
int terrTris = 0;
|
||||
var t = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
|
||||
if (t != null)
|
||||
{
|
||||
int q = t.terrainData.heightmapResolution - 1;
|
||||
terrTris = q * q * 2;
|
||||
sb.AppendLine(string.Format("Terrain hmRes={0} size={1} tris(full LOD)={2} mat={3}",
|
||||
t.terrainData.heightmapResolution, t.terrainData.size, terrTris,
|
||||
t.materialTemplate != null ? t.materialTemplate.name : "null"));
|
||||
}
|
||||
sb.AppendLine(string.Format("SCENE STATS: renderers={0} materialKinds={1}(+Terrain.mat) meshTris={2} +terrain={3} TOTAL={4}",
|
||||
rend, mats.Count, tris, terrTris, tris + terrTris));
|
||||
var names = new System.Collections.Generic.List<string>();
|
||||
foreach (var m in mats) names.Add(m.name);
|
||||
names.Sort();
|
||||
sb.AppendLine("MATERIALS: " + string.Join(" · ", names.ToArray()));
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// WL-814q — 캡처 4장(+개요) · 1080×1920 · Screenshots_WL/WL814q/
|
||||
public static class WL814q_Capture
|
||||
{
|
||||
const string Out = "Screenshots_WL/WL814q/";
|
||||
const string ScenePath = "Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity";
|
||||
const string DemoPath = "Assets/3DPixelArtEnvironment/Demo/Demo.unity";
|
||||
const int W = 1080, H = 1920;
|
||||
|
||||
static UnityEngine.Texture2D Shot(UnityEngine.Camera cam, int w, int h)
|
||||
{
|
||||
var rt = new UnityEngine.RenderTexture(w, h, 24, UnityEngine.RenderTextureFormat.ARGB32,
|
||||
UnityEngine.RenderTextureReadWrite.sRGB);
|
||||
rt.filterMode = UnityEngine.FilterMode.Point;
|
||||
rt.antiAliasing = 1;
|
||||
rt.Create();
|
||||
var prev = cam.targetTexture;
|
||||
cam.targetTexture = rt;
|
||||
cam.Render();
|
||||
var oldActive = UnityEngine.RenderTexture.active;
|
||||
UnityEngine.RenderTexture.active = rt;
|
||||
var tex = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
|
||||
tex.ReadPixels(new UnityEngine.Rect(0, 0, w, h), 0, 0);
|
||||
tex.Apply();
|
||||
UnityEngine.RenderTexture.active = oldActive;
|
||||
cam.targetTexture = prev;
|
||||
rt.Release();
|
||||
UnityEngine.Object.DestroyImmediate(rt);
|
||||
return tex;
|
||||
}
|
||||
|
||||
/// <summary>점(Point) 확대 — 도트 격자를 그대로 키운다.</summary>
|
||||
static UnityEngine.Texture2D Upscale(UnityEngine.Texture2D src, int w, int h)
|
||||
{
|
||||
var d = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
|
||||
var sp = src.GetPixels32();
|
||||
var dp = new UnityEngine.Color32[w * h];
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
int sy = y * src.height / h; if (sy >= src.height) sy = src.height - 1;
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int sx = x * src.width / w; if (sx >= src.width) sx = src.width - 1;
|
||||
dp[y * w + x] = sp[sy * src.width + sx];
|
||||
}
|
||||
}
|
||||
d.SetPixels32(dp); d.Apply();
|
||||
return d;
|
||||
}
|
||||
|
||||
static void Save(UnityEngine.Texture2D t, string name)
|
||||
{
|
||||
var dir = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), Out);
|
||||
System.IO.Directory.CreateDirectory(dir);
|
||||
System.IO.File.WriteAllBytes(System.IO.Path.Combine(dir, name), UnityEngine.ImageConversion.EncodeToPNG(t));
|
||||
UnityEngine.Object.DestroyImmediate(t);
|
||||
}
|
||||
|
||||
/// <summary>도트A: pixelHeight 240 저해상도 렌더 → 점 확대.</summary>
|
||||
static UnityEngine.Texture2D DotA(UnityEngine.Camera cam, float orthoSize)
|
||||
{
|
||||
bool o = cam.orthographic; float os = cam.orthographicSize;
|
||||
cam.orthographic = true; cam.orthographicSize = orthoSize;
|
||||
int ph = 240, pw = UnityEngine.Mathf.RoundToInt(240f * W / H); // 135×240
|
||||
var small = Shot(cam, pw, ph);
|
||||
var big = Upscale(small, W, H);
|
||||
UnityEngine.Object.DestroyImmediate(small);
|
||||
cam.orthographic = o; cam.orthographicSize = os;
|
||||
return big;
|
||||
}
|
||||
|
||||
static UnityEngine.Texture2D SideBySide(UnityEngine.Texture2D a, UnityEngine.Texture2D b, int gap)
|
||||
{
|
||||
int w = a.width + gap + b.width, h = UnityEngine.Mathf.Max(a.height, b.height);
|
||||
var d = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
|
||||
var px = new UnityEngine.Color32[w * h];
|
||||
for (int i = 0; i < px.Length; i++) px[i] = new UnityEngine.Color32(18, 18, 22, 255);
|
||||
d.SetPixels32(px); d.Apply();
|
||||
UnityEngine.Graphics.CopyTexture(a, 0, 0, 0, 0, a.width, a.height, d, 0, 0, 0, 0);
|
||||
UnityEngine.Graphics.CopyTexture(b, 0, 0, 0, 0, b.width, b.height, d, 0, 0, a.width + gap, 0);
|
||||
return d;
|
||||
}
|
||||
|
||||
public static string Run()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var scene = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(ScenePath,
|
||||
UnityEditor.SceneManagement.OpenSceneMode.Single);
|
||||
// 🔴 Water_Plane 프리팹의 PlanarReflectionProbe 도 Camera 다 — 이름으로 고른다
|
||||
UnityEngine.Camera cam = null;
|
||||
foreach (var c in UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(
|
||||
UnityEngine.FindObjectsSortMode.None))
|
||||
if (c.gameObject.name == "Main Camera") cam = c;
|
||||
if (cam == null) return "NO CAMERA";
|
||||
var camPos = cam.transform.position;
|
||||
var camRot = cam.transform.rotation;
|
||||
|
||||
// ⓐ 일반 모드 — 같은 위치·각도 · 퍼스펙티브 · 풀해상도(도트 없음)
|
||||
cam.orthographic = false;
|
||||
// 주시점까지 거리에서 세로 화각이 ortho 3.4 와 같아지도록 FOV 를 맞춘다(같은 구도 유지)
|
||||
float dist = UnityEngine.Vector3.Distance(camPos, new UnityEngine.Vector3(0f, 1.06f, 0f));
|
||||
cam.fieldOfView = 2f * UnityEngine.Mathf.Atan2(3.4f, dist) * UnityEngine.Mathf.Rad2Deg;
|
||||
var a = Shot(cam, W, H);
|
||||
Save(a, "a_normal_1080x1920.png");
|
||||
sb.AppendLine("a_normal: perspective fov=" + cam.fieldOfView.ToString("F2") + " dist=" + dist.ToString("F3"));
|
||||
cam.orthographic = true; cam.orthographicSize = 3.4f;
|
||||
|
||||
// ⓑ 도트A — 같은 구도 · orthoSize 3.4 · pixelHeight 240
|
||||
Save(DotA(cam, 3.4f), "b_dotA_1080x1920.png");
|
||||
sb.AppendLine("b_dotA: ortho 3.4 · 135x240 -> point x8");
|
||||
|
||||
// ⓒ 캐릭터가 보이는 눈높이 구도(도트A) — 더 가깝고 낮게
|
||||
cam.transform.position = new UnityEngine.Vector3(0f, 1.15f, 0f)
|
||||
+ UnityEngine.Quaternion.Euler(0f, 205f, 0f) * new UnityEngine.Vector3(0f, 0f, -4.2f)
|
||||
+ UnityEngine.Vector3.up * 0.55f;
|
||||
cam.transform.LookAt(new UnityEngine.Vector3(0f, 0.75f, 0f));
|
||||
Save(DotA(cam, 2.0f), "c_dotA_eye_1080x1920.png");
|
||||
sb.AppendLine("c_dotA_eye: ortho 2.0 · 135x240 -> point x8 · pos=" + cam.transform.position);
|
||||
|
||||
// 개요(참고) — 아레나 전체 + 연못 + 풍차 (퍼스펙티브 3/4 뷰)
|
||||
cam.orthographic = false;
|
||||
cam.fieldOfView = 40f;
|
||||
cam.transform.position = new UnityEngine.Vector3(30f, 20f, -33f);
|
||||
cam.transform.LookAt(new UnityEngine.Vector3(1f, 1.5f, -1f));
|
||||
Save(Shot(cam, W, H), "e_overview_1080x1920.png");
|
||||
sb.AppendLine("e_overview: perspective fov40 @" + cam.transform.position);
|
||||
cam.orthographic = true; cam.orthographicSize = 3.4f;
|
||||
|
||||
// ⓓ 비교용 — 새 아레나(도트A) 렌더를 먼저 잡아 둔다
|
||||
cam.transform.position = camPos; cam.transform.rotation = camRot;
|
||||
cam.orthographic = true; cam.orthographicSize = 3.4f;
|
||||
var mine = DotA(cam, 3.4f);
|
||||
var mineHalf = Upscale(mine, W / 2, H / 2);
|
||||
UnityEngine.Object.DestroyImmediate(mine);
|
||||
// 텍스처를 씬 전환에서 지키기
|
||||
mineHalf.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
|
||||
// 데모 씬 — 같은 규격(도트A)으로. 🔴 저장하지 않는다.
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(DemoPath,
|
||||
UnityEditor.SceneManagement.OpenSceneMode.Single);
|
||||
UnityEngine.Camera demoCam = null;
|
||||
foreach (var c in UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(
|
||||
UnityEngine.FindObjectsSortMode.None))
|
||||
if (c.gameObject.name == "Main Camera") demoCam = c;
|
||||
UnityEngine.Texture2D demoHalf = null;
|
||||
if (demoCam != null)
|
||||
{
|
||||
// 데모의 마을 중심(House 2채 + Light_Post 일대)을 우리와 같은 내림각 43.61°·도트A 규격으로
|
||||
var dt = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
|
||||
var fxz = new UnityEngine.Vector3(94f, 0f, 78f);
|
||||
float fy = dt != null ? dt.SampleHeight(fxz) + dt.transform.position.y : 4f;
|
||||
var focus = new UnityEngine.Vector3(fxz.x, fy, fxz.z);
|
||||
float camUp = 5.4335f, camHoriz = 4.5911f;
|
||||
float yaw = 215f;
|
||||
var dir = new UnityEngine.Vector3(UnityEngine.Mathf.Sin(yaw * UnityEngine.Mathf.Deg2Rad), 0f,
|
||||
UnityEngine.Mathf.Cos(yaw * UnityEngine.Mathf.Deg2Rad));
|
||||
demoCam.transform.position = focus + UnityEngine.Vector3.up * camUp - dir * camHoriz;
|
||||
demoCam.transform.LookAt(focus + UnityEngine.Vector3.up * 1.06f);
|
||||
demoCam.orthographic = true; demoCam.orthographicSize = 3.4f;
|
||||
var d = DotA(demoCam, 3.4f);
|
||||
demoHalf = Upscale(d, W / 2, H / 2);
|
||||
demoHalf.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
|
||||
UnityEngine.Object.DestroyImmediate(d);
|
||||
sb.AppendLine("demo shot ok @" + demoCam.transform.position);
|
||||
}
|
||||
else sb.AppendLine("DEMO CAMERA NOT FOUND");
|
||||
|
||||
if (demoHalf != null)
|
||||
{
|
||||
var cmp = SideBySide(demoHalf, mineHalf, 16);
|
||||
Save(cmp, "d_compare_demo_vs_arena.png");
|
||||
sb.AppendLine("d_compare: left=Critter Demo · right=WL_ArenaProto (both dotA 240p)");
|
||||
UnityEngine.Object.DestroyImmediate(demoHalf);
|
||||
}
|
||||
UnityEngine.Object.DestroyImmediate(mineHalf);
|
||||
|
||||
// 🔴 데모 씬은 저장하지 않고 원래 씬으로 되돌린다
|
||||
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(ScenePath,
|
||||
UnityEditor.SceneManagement.OpenSceneMode.Single);
|
||||
sb.AppendLine("OUT " + System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), Out));
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
public static class WL814q_Probe
|
||||
{
|
||||
static string V(UnityEngine.Vector3 v)
|
||||
{
|
||||
return string.Format("({0:F3},{1:F3},{2:F3})", v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
public static string Run()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
string[] names = { "Arch_Large", "Arch_Small", "House", "Light_Post", "Slab", "Stone_1", "Stone_2", "Tree", "Water_Plane", "Windmill" };
|
||||
sb.AppendLine("prefab | worldSize(x,y,z) m @scale1 | center | tris | mats | renderers | colliders");
|
||||
foreach (var n in names)
|
||||
{
|
||||
string path = "Assets/3DPixelArtEnvironment/Prefabs/" + n + ".prefab";
|
||||
var go = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(path);
|
||||
if (go == null) { sb.AppendLine(n + " | NOT FOUND"); continue; }
|
||||
var inst = (UnityEngine.GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(go);
|
||||
inst.transform.position = UnityEngine.Vector3.zero;
|
||||
inst.transform.rotation = UnityEngine.Quaternion.identity;
|
||||
var rs = inst.GetComponentsInChildren<UnityEngine.Renderer>(true);
|
||||
var b = new UnityEngine.Bounds(inst.transform.position, UnityEngine.Vector3.zero);
|
||||
bool first = true;
|
||||
int tris = 0;
|
||||
var mats = new System.Collections.Generic.HashSet<string>();
|
||||
foreach (var r in rs)
|
||||
{
|
||||
if (first) { b = r.bounds; first = false; } else b.Encapsulate(r.bounds);
|
||||
foreach (var m in r.sharedMaterials) if (m != null) mats.Add(m.name);
|
||||
var mf = r.GetComponent<UnityEngine.MeshFilter>();
|
||||
if (mf != null && mf.sharedMesh != null) tris += mf.sharedMesh.triangles.Length / 3;
|
||||
}
|
||||
var cols = inst.GetComponentsInChildren<UnityEngine.Collider>(true);
|
||||
string ctxt = "";
|
||||
foreach (var c in cols) ctxt += c.GetType().Name + ",";
|
||||
sb.AppendLine(string.Format("{0} | {1} | c={2} | tris={3} | mats={4} | rend={5} | col=[{6}]",
|
||||
n, V(b.size), V(b.center), tris, string.Join("/", System.Linq.Enumerable.ToArray(mats)), rs.Length, ctxt));
|
||||
UnityEngine.Object.DestroyImmediate(inst);
|
||||
}
|
||||
|
||||
// Terrain data
|
||||
var td = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TerrainData>("Assets/3DPixelArtEnvironment/Demo/Demo_Terrain.asset");
|
||||
if (td != null)
|
||||
sb.AppendLine(string.Format("TerrainData size={0} heightmapRes={1} layers={2} detailW={3}",
|
||||
V(td.size), td.heightmapResolution, td.terrainLayers.Length, td.detailWidth));
|
||||
|
||||
// Character prefab
|
||||
var pc = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>("Assets/Res_Addr/PC/LH_M05.prefab");
|
||||
if (pc != null)
|
||||
{
|
||||
var pi = (UnityEngine.GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(pc);
|
||||
pi.transform.position = UnityEngine.Vector3.zero;
|
||||
var rs = pi.GetComponentsInChildren<UnityEngine.Renderer>(true);
|
||||
var b = new UnityEngine.Bounds(UnityEngine.Vector3.zero, UnityEngine.Vector3.zero);
|
||||
bool f = true; int tris = 0;
|
||||
foreach (var r in rs)
|
||||
{
|
||||
if (f) { b = r.bounds; f = false; } else b.Encapsulate(r.bounds);
|
||||
var smr = r as UnityEngine.SkinnedMeshRenderer;
|
||||
if (smr != null && smr.sharedMesh != null) tris += smr.sharedMesh.triangles.Length / 3;
|
||||
var mf = r.GetComponent<UnityEngine.MeshFilter>();
|
||||
if (mf != null && mf.sharedMesh != null) tris += mf.sharedMesh.triangles.Length / 3;
|
||||
}
|
||||
sb.AppendLine(string.Format("LH_M05 prefabScale={0} worldSize={1} min.y={2:F4} max.y={3:F4} tris={4} rend={5}",
|
||||
V(pi.transform.localScale), V(b.size), b.min.y, b.max.y, tris, rs.Length));
|
||||
UnityEngine.Object.DestroyImmediate(pi);
|
||||
}
|
||||
else sb.AppendLine("LH_M05 NOT FOUND");
|
||||
|
||||
// Stage table
|
||||
var st = UnityEditor.AssetDatabase.FindAssets("WLStageTable");
|
||||
foreach (var g in st) sb.AppendLine("StageTable: " + UnityEditor.AssetDatabase.GUIDToAssetPath(g));
|
||||
var ls = UnityEditor.AssetDatabase.FindAssets("WLLookModeSettings");
|
||||
foreach (var g in ls) sb.AppendLine("LookModeSettings: " + UnityEditor.AssetDatabase.GUIDToAssetPath(g));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
public static class WL814q_Probe2
|
||||
{
|
||||
public static string Run()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
string[] paths = {
|
||||
"Assets/WL/Look/Arena/Terrain/WL_ArenaTerrain.asset",
|
||||
"Assets/3DPixelArtEnvironment/Demo/Demo_Terrain.asset"
|
||||
};
|
||||
foreach (var p in paths)
|
||||
{
|
||||
var td = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TerrainData>(p);
|
||||
if (td == null) { sb.AppendLine(p + " NOT FOUND"); continue; }
|
||||
sb.AppendLine(p + " layers=" + td.terrainLayers.Length + " alphaRes=" + td.alphamapResolution
|
||||
+ " alphaTex=" + td.alphamapTextureCount);
|
||||
for (int i = 0; i < td.terrainLayers.Length; i++)
|
||||
sb.AppendLine(" layer[" + i + "] = " + (td.terrainLayers[i] != null ? td.terrainLayers[i].name : "null"));
|
||||
var a = td.GetAlphamaps(0, 0, td.alphamapWidth, td.alphamapHeight);
|
||||
int n = a.GetLength(2);
|
||||
var mx = new float[n]; var sum = new float[n];
|
||||
for (int y = 0; y < a.GetLength(0); y++)
|
||||
for (int x = 0; x < a.GetLength(1); x++)
|
||||
for (int c = 0; c < n; c++) { if (a[y, x, c] > mx[c]) mx[c] = a[y, x, c]; sum[c] += a[y, x, c]; }
|
||||
for (int c = 0; c < n; c++)
|
||||
sb.AppendLine(string.Format(" ch{0} max={1:F3} mean={2:F3}", c, mx[c], sum[c] / (a.GetLength(0) * a.GetLength(1))));
|
||||
// 중앙(아레나 안) 샘플
|
||||
if (p.Contains("Arena"))
|
||||
{
|
||||
int cx = td.alphamapWidth / 2, cy = td.alphamapHeight / 2;
|
||||
sb.AppendLine(string.Format(" center px: ch0={0:F3} ch1={1:F3}", a[cy, cx, 0], a[cy, cx, 1]));
|
||||
}
|
||||
}
|
||||
var m = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(
|
||||
"Assets/3DPixelArtEnvironment/Materials/Terrain.mat");
|
||||
if (m != null && m.shader != null)
|
||||
{
|
||||
sb.AppendLine("Terrain.mat shader=" + m.shader.name + " props=" + m.shader.GetPropertyCount());
|
||||
for (int i = 0; i < m.shader.GetPropertyCount(); i++)
|
||||
{
|
||||
string pn = m.shader.GetPropertyName(i);
|
||||
if (pn.Contains("Control") || pn.Contains("Splat") || pn.Contains("Terrain") || pn.Contains("Layer")
|
||||
|| pn.Contains("Highlight") || pn.Contains("Shadow"))
|
||||
sb.AppendLine(" " + pn + " : " + m.shader.GetPropertyType(i));
|
||||
}
|
||||
}
|
||||
// 우리 터레인 GO 상태
|
||||
var t = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
|
||||
if (t != null)
|
||||
{
|
||||
sb.AppendLine("scene Terrain mat=" + (t.materialTemplate != null ? t.materialTemplate.name : "null")
|
||||
+ " shader=" + (t.materialTemplate != null && t.materialTemplate.shader != null ? t.materialTemplate.shader.name : "?")
|
||||
+ " drawInstanced=" + t.drawInstanced + " basemapDist=" + t.basemapDistance);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 618fa1ecbb3878749a4f77406b382564
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 201987a476308434cb6f30de76035b0d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: be57fe49225e2ce4a880f566860efa9a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b3bbee3e0de6134cbc83b04f9292692
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0429ecb77ea323f43aac1e8485a924f2
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 15600000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue