diff --git a/Assets/Resources/Units/art_size.csv b/Assets/Resources/Units/art_size.csv
new file mode 100644
index 00000000..6e245f51
--- /dev/null
+++ b/Assets/Resources/Units/art_size.csv
@@ -0,0 +1,21 @@
+art,mounted,eye_px,body_px,size_px,note
+cc_archer,0,125.1,744,741.6,
+cc_boss_caocao,0,135.0,808,800.3,
+cc_cavalry,1,91.9,849,544.8,
+cc_infantry,0,125.3,765,742.8,
+cc_spear,0,124.1,785,735.7,
+dz_archer,0,123.3,745,730.9,
+dz_boss_lvbu,1,75.8,819,449.3,
+dz_cavalry,1,96.2,873,570.3,
+dz_infantry,0,139.1,844,824.6,
+dz_spear,0,117.2,818,694.8,
+guanyu,0,122.7,725,727.4,
+hgj_archer,0,154.7,833,917.0,
+hgj_boss_zhangjiao,0,147.8,854,876.1,
+hgj_cavalry,1,107.4,879,636.7,
+hgj_infantry,0,161.5,850,957.4,
+hgj_spear,0,168.5,862,998.9,
+huangzhong,0,148.8,797,882.1,
+liubei,0,142.2,752,843.0,
+zhangfei,1,108.2,917,641.4,
+zhaoyun,1,100.4,926,595.2,
diff --git a/Assets/Resources/Units/wei_boss_simayi/meta.json.meta b/Assets/Resources/Units/art_size.csv.meta
similarity index 75%
rename from Assets/Resources/Units/wei_boss_simayi/meta.json.meta
rename to Assets/Resources/Units/art_size.csv.meta
index d68eb508..6371c547 100644
--- a/Assets/Resources/Units/wei_boss_simayi/meta.json.meta
+++ b/Assets/Resources/Units/art_size.csv.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 6c52ae5244a4c1d47999b26723feb8d6
+guid: 7f92b435a2912a24cb826a5b40683511
TextScriptImporter:
externalObjects: {}
userData:
diff --git a/Assets/Resources/Units/wei_archer.meta b/Assets/Resources/Units/wei_archer.meta
deleted file mode 100644
index e7829653..00000000
--- a/Assets/Resources/Units/wei_archer.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: c8fe4e3059806cd4198d7544e2061771
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_archer/front_idle.meta b/Assets/Resources/Units/wei_archer/front_idle.meta
deleted file mode 100644
index 518c1fa0..00000000
--- a/Assets/Resources/Units/wei_archer/front_idle.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 44bb65b9a5cf86848ab36c47523fe2a2
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_archer/front_idle/f000.png b/Assets/Resources/Units/wei_archer/front_idle/f000.png
deleted file mode 100644
index 0c2de2c6..00000000
Binary files a/Assets/Resources/Units/wei_archer/front_idle/f000.png and /dev/null differ
diff --git a/Assets/Resources/Units/wei_archer/front_idle/f000.png.meta b/Assets/Resources/Units/wei_archer/front_idle/f000.png.meta
deleted file mode 100644
index b69bd755..00000000
--- a/Assets/Resources/Units/wei_archer/front_idle/f000.png.meta
+++ /dev/null
@@ -1,168 +0,0 @@
-fileFormatVersion: 2
-guid: 154714b72b6b71a439a066069edd217a
-TextureImporter:
- internalIDToNameTable:
- - first:
- 213: -166343660977832731
- second: f000_0
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 1
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 0
- alignment: 9
- spritePivot: {x: 0.5, y: 0.02101}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Android
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: WebGL
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites:
- - serializedVersion: 2
- name: f000_0
- rect:
- serializedVersion: 2
- x: 19
- y: 19
- width: 578
- height: 914
- alignment: 0
- pivot: {x: 0, y: 0}
- border: {x: 0, y: 0, z: 0, w: 0}
- customData:
- outline: []
- physicsShape: []
- tessellationDetail: -1
- bones: []
- spriteID: 5ec37c8525701bdf0800000000000000
- internalID: -166343660977832731
- vertices: []
- indices:
- edges: []
- weights: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_archer/meta.json b/Assets/Resources/Units/wei_archer/meta.json
deleted file mode 100644
index b15a7788..00000000
--- a/Assets/Resources/Units/wei_archer/meta.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "fps": 1,
- "ppu": 100,
- "frame": [
- 616,
- 952
- ],
- "pivot": [
- 0.5,
- 0.02101
- ],
- "charHeightPx": 912,
- "bodyHeightPx": 842,
- "clips": [
- {
- "name": "front_idle",
- "frames": 1,
- "loop": true
- }
- ]
-}
\ No newline at end of file
diff --git a/Assets/Resources/Units/wei_archer/meta.json.meta b/Assets/Resources/Units/wei_archer/meta.json.meta
deleted file mode 100644
index bef538ca..00000000
--- a/Assets/Resources/Units/wei_archer/meta.json.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 357942899e8b8504792baf9a75f4795f
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_boss_simayi.meta b/Assets/Resources/Units/wei_boss_simayi.meta
deleted file mode 100644
index 53b7efbe..00000000
--- a/Assets/Resources/Units/wei_boss_simayi.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 321a9b9e1c1291640bb97fc92b2924c9
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_boss_simayi/front_idle.meta b/Assets/Resources/Units/wei_boss_simayi/front_idle.meta
deleted file mode 100644
index 9aa3f57a..00000000
--- a/Assets/Resources/Units/wei_boss_simayi/front_idle.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: f2de55fd9322ded4e8e550631eb53734
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_boss_simayi/front_idle/f000.png b/Assets/Resources/Units/wei_boss_simayi/front_idle/f000.png
deleted file mode 100644
index a9b62535..00000000
Binary files a/Assets/Resources/Units/wei_boss_simayi/front_idle/f000.png and /dev/null differ
diff --git a/Assets/Resources/Units/wei_boss_simayi/front_idle/f000.png.meta b/Assets/Resources/Units/wei_boss_simayi/front_idle/f000.png.meta
deleted file mode 100644
index de3ccca1..00000000
--- a/Assets/Resources/Units/wei_boss_simayi/front_idle/f000.png.meta
+++ /dev/null
@@ -1,168 +0,0 @@
-fileFormatVersion: 2
-guid: a5abb9cd3db4e694891b52a867a1707a
-TextureImporter:
- internalIDToNameTable:
- - first:
- 213: -166343660977832731
- second: f000_0
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 1
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 0
- alignment: 9
- spritePivot: {x: 0.5, y: 0.02037}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Android
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: WebGL
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites:
- - serializedVersion: 2
- name: f000_0
- rect:
- serializedVersion: 2
- x: 19
- y: 19
- width: 532
- height: 944
- alignment: 0
- pivot: {x: 0, y: 0}
- border: {x: 0, y: 0, z: 0, w: 0}
- customData:
- outline: []
- physicsShape: []
- tessellationDetail: -1
- bones: []
- spriteID: 5ec37c8525701bdf0800000000000000
- internalID: -166343660977832731
- vertices: []
- indices:
- edges: []
- weights: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_boss_simayi/meta.json b/Assets/Resources/Units/wei_boss_simayi/meta.json
deleted file mode 100644
index e5abc5ae..00000000
--- a/Assets/Resources/Units/wei_boss_simayi/meta.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "fps": 1,
- "ppu": 100,
- "frame": [
- 570,
- 982
- ],
- "pivot": [
- 0.5,
- 0.02037
- ],
- "charHeightPx": 942,
- "bodyHeightPx": 799,
- "clips": [
- {
- "name": "front_idle",
- "frames": 1,
- "loop": true
- }
- ]
-}
\ No newline at end of file
diff --git a/Assets/Resources/Units/wei_cavalry.meta b/Assets/Resources/Units/wei_cavalry.meta
deleted file mode 100644
index eea46d12..00000000
--- a/Assets/Resources/Units/wei_cavalry.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 48fa1fa1606e6d44e857c4e315d8ba96
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_cavalry/front_idle.meta b/Assets/Resources/Units/wei_cavalry/front_idle.meta
deleted file mode 100644
index 0a0519d2..00000000
--- a/Assets/Resources/Units/wei_cavalry/front_idle.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 0d68a9e892581594e939bb8bb78a3ac1
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_cavalry/front_idle/f000.png b/Assets/Resources/Units/wei_cavalry/front_idle/f000.png
deleted file mode 100644
index 26e7978e..00000000
Binary files a/Assets/Resources/Units/wei_cavalry/front_idle/f000.png and /dev/null differ
diff --git a/Assets/Resources/Units/wei_cavalry/front_idle/f000.png.meta b/Assets/Resources/Units/wei_cavalry/front_idle/f000.png.meta
deleted file mode 100644
index ec90679d..00000000
--- a/Assets/Resources/Units/wei_cavalry/front_idle/f000.png.meta
+++ /dev/null
@@ -1,168 +0,0 @@
-fileFormatVersion: 2
-guid: 1900b449bbfb58b4393d5665f5129b0c
-TextureImporter:
- internalIDToNameTable:
- - first:
- 213: -166343660977832731
- second: f000_0
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 1
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 0
- alignment: 9
- spritePivot: {x: 0.5, y: 0.02045}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Android
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: WebGL
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites:
- - serializedVersion: 2
- name: f000_0
- rect:
- serializedVersion: 2
- x: 19
- y: 19
- width: 694
- height: 940
- alignment: 0
- pivot: {x: 0, y: 0}
- border: {x: 0, y: 0, z: 0, w: 0}
- customData:
- outline: []
- physicsShape: []
- tessellationDetail: -1
- bones: []
- spriteID: 5ec37c8525701bdf0800000000000000
- internalID: -166343660977832731
- vertices: []
- indices:
- edges: []
- weights: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_cavalry/meta.json b/Assets/Resources/Units/wei_cavalry/meta.json
deleted file mode 100644
index 29aad89e..00000000
--- a/Assets/Resources/Units/wei_cavalry/meta.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "fps": 1,
- "ppu": 100,
- "frame": [
- 732,
- 978
- ],
- "pivot": [
- 0.5,
- 0.02045
- ],
- "charHeightPx": 938,
- "bodyHeightPx": 877,
- "clips": [
- {
- "name": "front_idle",
- "frames": 1,
- "loop": true
- }
- ]
-}
\ No newline at end of file
diff --git a/Assets/Resources/Units/wei_cavalry/meta.json.meta b/Assets/Resources/Units/wei_cavalry/meta.json.meta
deleted file mode 100644
index 1c94f711..00000000
--- a/Assets/Resources/Units/wei_cavalry/meta.json.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: e2e0e727ef2627e4e95edf3bee7fc76b
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_infantry.meta b/Assets/Resources/Units/wei_infantry.meta
deleted file mode 100644
index a5cb73f4..00000000
--- a/Assets/Resources/Units/wei_infantry.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: ab5e1fb35f54c734f9849c8cea6396b2
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_infantry/front_idle.meta b/Assets/Resources/Units/wei_infantry/front_idle.meta
deleted file mode 100644
index daaa6ae4..00000000
--- a/Assets/Resources/Units/wei_infantry/front_idle.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 2182604a7db43d840b09344b749496d4
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_infantry/front_idle/f000.png b/Assets/Resources/Units/wei_infantry/front_idle/f000.png
deleted file mode 100644
index b6baae88..00000000
Binary files a/Assets/Resources/Units/wei_infantry/front_idle/f000.png and /dev/null differ
diff --git a/Assets/Resources/Units/wei_infantry/front_idle/f000.png.meta b/Assets/Resources/Units/wei_infantry/front_idle/f000.png.meta
deleted file mode 100644
index 84cafacd..00000000
--- a/Assets/Resources/Units/wei_infantry/front_idle/f000.png.meta
+++ /dev/null
@@ -1,168 +0,0 @@
-fileFormatVersion: 2
-guid: cc7eaa04bc7977848bc4717a47ff8f4b
-TextureImporter:
- internalIDToNameTable:
- - first:
- 213: -166343660977832731
- second: f000_0
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 1
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 0
- alignment: 9
- spritePivot: {x: 0.5, y: 0.02075}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Android
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: WebGL
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites:
- - serializedVersion: 2
- name: f000_0
- rect:
- serializedVersion: 2
- x: 19
- y: 19
- width: 525
- height: 926
- alignment: 0
- pivot: {x: 0, y: 0}
- border: {x: 0, y: 0, z: 0, w: 0}
- customData:
- outline: []
- physicsShape: []
- tessellationDetail: -1
- bones: []
- spriteID: 5ec37c8525701bdf0800000000000000
- internalID: -166343660977832731
- vertices: []
- indices:
- edges: []
- weights: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_infantry/meta.json b/Assets/Resources/Units/wei_infantry/meta.json
deleted file mode 100644
index a75aa1d9..00000000
--- a/Assets/Resources/Units/wei_infantry/meta.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "fps": 1,
- "ppu": 100,
- "frame": [
- 563,
- 964
- ],
- "pivot": [
- 0.5,
- 0.02075
- ],
- "charHeightPx": 924,
- "bodyHeightPx": 850,
- "clips": [
- {
- "name": "front_idle",
- "frames": 1,
- "loop": true
- }
- ]
-}
\ No newline at end of file
diff --git a/Assets/Resources/Units/wei_infantry/meta.json.meta b/Assets/Resources/Units/wei_infantry/meta.json.meta
deleted file mode 100644
index 534830d5..00000000
--- a/Assets/Resources/Units/wei_infantry/meta.json.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: a91577d55a26cf145bdb48cae6068b51
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_spear.meta b/Assets/Resources/Units/wei_spear.meta
deleted file mode 100644
index 7eda3655..00000000
--- a/Assets/Resources/Units/wei_spear.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: daf601eef2501824cba4fcf78852bed7
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_spear/front_idle.meta b/Assets/Resources/Units/wei_spear/front_idle.meta
deleted file mode 100644
index 60740d66..00000000
--- a/Assets/Resources/Units/wei_spear/front_idle.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 70d931937e5258447851f43e7fc4429b
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_spear/front_idle/f000.png b/Assets/Resources/Units/wei_spear/front_idle/f000.png
deleted file mode 100644
index 2db95e07..00000000
Binary files a/Assets/Resources/Units/wei_spear/front_idle/f000.png and /dev/null differ
diff --git a/Assets/Resources/Units/wei_spear/front_idle/f000.png.meta b/Assets/Resources/Units/wei_spear/front_idle/f000.png.meta
deleted file mode 100644
index 386bc465..00000000
--- a/Assets/Resources/Units/wei_spear/front_idle/f000.png.meta
+++ /dev/null
@@ -1,168 +0,0 @@
-fileFormatVersion: 2
-guid: 8671e3ec0807b3340b1921dc2ed4a333
-TextureImporter:
- internalIDToNameTable:
- - first:
- 213: -166343660977832731
- second: f000_0
- externalObjects: {}
- serializedVersion: 13
- mipmaps:
- mipMapMode: 0
- enableMipMap: 0
- sRGBTexture: 1
- linearTexture: 0
- fadeOut: 0
- borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
- mipMapFadeDistanceStart: 1
- mipMapFadeDistanceEnd: 3
- bumpmap:
- convertToNormalMap: 0
- externalNormalMap: 0
- heightScale: 0.25
- normalMapFilter: 0
- flipGreenChannel: 0
- isReadable: 0
- streamingMipmaps: 0
- streamingMipmapsPriority: 0
- vTOnly: 0
- ignoreMipmapLimit: 0
- grayScaleToAlpha: 0
- generateCubemap: 6
- cubemapConvolution: 0
- seamlessCubemap: 0
- textureFormat: 1
- maxTextureSize: 2048
- textureSettings:
- serializedVersion: 2
- filterMode: 1
- aniso: 1
- mipBias: 0
- wrapU: 1
- wrapV: 1
- wrapW: 1
- nPOTScale: 0
- lightmap: 0
- compressionQuality: 50
- spriteMode: 1
- spriteExtrude: 1
- spriteMeshType: 0
- alignment: 9
- spritePivot: {x: 0.5, y: 0.02121}
- spritePixelsToUnits: 100
- spriteBorder: {x: 0, y: 0, z: 0, w: 0}
- spriteGenerateFallbackPhysicsShape: 1
- alphaUsage: 1
- alphaIsTransparency: 1
- spriteTessellationDetail: -1
- textureType: 8
- textureShape: 1
- singleChannelComponent: 0
- flipbookRows: 1
- flipbookColumns: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- ignorePngGamma: 0
- applyGammaDecoding: 0
- swizzle: 50462976
- cookieLightType: 0
- platformSettings:
- - serializedVersion: 4
- buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: Android
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- - serializedVersion: 4
- buildTarget: WebGL
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 1
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- ignorePlatformSupport: 0
- androidETC2FallbackOverride: 0
- forceMaximumCompressionQuality_BC6H_BC7: 0
- spriteSheet:
- serializedVersion: 2
- sprites:
- - serializedVersion: 2
- name: f000_0
- rect:
- serializedVersion: 2
- x: 19
- y: 19
- width: 474
- height: 905
- alignment: 0
- pivot: {x: 0, y: 0}
- border: {x: 0, y: 0, z: 0, w: 0}
- customData:
- outline: []
- physicsShape: []
- tessellationDetail: -1
- bones: []
- spriteID: 5ec37c8525701bdf0800000000000000
- internalID: -166343660977832731
- vertices: []
- indices:
- edges: []
- weights: []
- outline: []
- customData:
- physicsShape: []
- bones: []
- spriteID: 5e97eb03825dee720800000000000000
- internalID: 0
- vertices: []
- indices:
- edges: []
- weights: []
- secondaryTextures: []
- spriteCustomMetadata:
- entries: []
- nameFileIdTable: {}
- mipmapLimitGroupName:
- pSDRemoveMatte: 0
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Resources/Units/wei_spear/meta.json b/Assets/Resources/Units/wei_spear/meta.json
deleted file mode 100644
index b2fa5e71..00000000
--- a/Assets/Resources/Units/wei_spear/meta.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "fps": 1,
- "ppu": 100,
- "frame": [
- 512,
- 943
- ],
- "pivot": [
- 0.5,
- 0.02121
- ],
- "charHeightPx": 903,
- "bodyHeightPx": 860,
- "clips": [
- {
- "name": "front_idle",
- "frames": 1,
- "loop": true
- }
- ]
-}
\ No newline at end of file
diff --git a/Assets/Resources/Units/wei_spear/meta.json.meta b/Assets/Resources/Units/wei_spear/meta.json.meta
deleted file mode 100644
index 90678804..00000000
--- a/Assets/Resources/Units/wei_spear/meta.json.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: c3f282f7fd7dedf4a9d91436db8ca357
-TextScriptImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/OutGame/UI/HeroScreens.cs b/Assets/Scripts/OutGame/UI/HeroScreens.cs
index 4cb49f0d..35e85a10 100644
--- a/Assets/Scripts/OutGame/UI/HeroScreens.cs
+++ b/Assets/Scripts/OutGame/UI/HeroScreens.cs
@@ -84,8 +84,8 @@ namespace TKS.OutGame
{
public static readonly Vector2 CellSize = new Vector2(320f, 431f);
public static readonly Vector2 Spacing = new Vector2(22f, 20f);
- /// 일러 배율(원본 px → 화면 px · 전 유닛 공통) · 머리 끝 위치(카드 위에서 px).
- const float ArtScale = 0.56f;
+ /// 크기 표 size_px(머리 크기 기준 표준 키)가 카드에서 차지할 px — 배율 = UnitPx ÷ size_px(모든 유닛 머리 크기 같음) · 머리 끝 위치(카드 위에서 px).
+ const float UnitPx = 450f;
const float HeadTop = 50f;
public static Image Card(Transform content, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap)
@@ -107,17 +107,18 @@ namespace TKS.OutGame
var sp = UiKit.ArtSprite(h.art);
if (sp != null)
{
- // 모든 유닛 같은 배율(PD 2026-11-09 「유닛 크기를 동일한 비율로」) = 원본 px × ArtScale · 머리 끝(발 + bodyHeightPx · 무기 제외)을 카드 위 HeadTop 에 맞춤
- // → 머리 크기가 같고 다리·말은 아래 띠 뒤로 잘림(레퍼런스 흉상 느낌) · 메타가 없으면 칸 맞춤
+ // 머리 크기 통일(PD 2026-11-09 「캐릭터 크기가 동일하게 보이도록」) = 배율 UnitPx ÷ 크기 표 size_px(눈 간격 기준 · 말 탄 유닛은 탄 사람 기준) ·
+ // 머리 끝(발 + bodyHeightPx · 무기 제외)을 카드 위 HeadTop 에 맞춤 → 다리·말은 아래 띠 뒤로 잘림(레퍼런스 흉상 느낌) · 메타가 없으면 칸 맞춤
var a = UnitArt.Load(h.art);
var m = a != null ? a.Meta : null;
- float body = m != null ? (m.bodyHeightPx > 0.5f ? m.bodyHeightPx : m.charHeightPx) : 0f;
+ float body = a != null ? a.BodyPx : 0f;
+ float size = a != null && a.SizePx > 0.5f ? a.SizePx : body;
Image art;
if (body > 0.5f && m.pivot != null && m.pivot.Length > 1)
{
float hgt = sp.rect.height;
float headY = Mathf.Clamp01((m.pivot[1] * hgt + body) / hgt); // 머리 끝 = 프레임 아래에서의 비율
- art = UiKit.Rect("Character", b, new Vector2(0.5f, 1f), new Vector2(0.5f, headY), new Vector2(0f, -HeadTop), sp.rect.size * ArtScale).gameObject.AddComponent();
+ art = UiKit.Rect("Character", b, new Vector2(0.5f, 1f), new Vector2(0.5f, headY), new Vector2(0f, -HeadTop), sp.rect.size * (UnitPx / size)).gameObject.AddComponent();
}
else
{
diff --git a/Assets/Scripts/Runtime/Unit.cs b/Assets/Scripts/Runtime/Unit.cs
index 205be0e2..4890a5b3 100644
--- a/Assets/Scripts/Runtime/Unit.cs
+++ b/Assets/Scripts/Runtime/Unit.cs
@@ -23,7 +23,7 @@ namespace TKS
public float ArcherMoveAllow;
/// Resources/Units/<ArtId>/ 아트 폴더 · 비면 도형.
public string ArtId = "";
- /// 아트 세로 크기(월드) = unitArtHeight × 반지름 배율.
+ /// 아트 머리 끝 높이(월드) — Init = 표준 키(unitArtHeight × 반지름 배율) · 아트가 있으면 UnitView 가 크기 표 배율 뒤 실제 머리 끝으로 바꾼다.
public float ArtHeight;
/// 이번 아군 턴에 행동을 마쳤는가.
diff --git a/Assets/Scripts/Runtime/UnitArt.cs b/Assets/Scripts/Runtime/UnitArt.cs
index 18e2363f..44071f7d 100644
--- a/Assets/Scripts/Runtime/UnitArt.cs
+++ b/Assets/Scripts/Runtime/UnitArt.cs
@@ -37,15 +37,25 @@ namespace TKS
public UnitArtMeta Meta;
public readonly Dictionary Clips = new Dictionary();
public readonly Dictionary Loop = new Dictionary();
+ ///
+ /// 크기 기준 px = Resources/Units/art_size.csv 의 size_px(눈 간격 ÷ 기준 비율 = 머리 크기가 표준인 유닛의 키로 환산 · Tools/unit_art_size.py) ·
+ /// 0 = 표에 없음 → BodyPx. PD 2026-11-09 「캐릭터 크기 동일 · 말 탄 유닛도 보병·창병과 비슷한 비율」.
+ ///
+ public float SizePx;
- /// 캐릭터 몸 세로 크기(월드) — 임포터 PPU = meta.ppu 전제 · bodyHeightPx(무기 제외) 우선, 없으면 charHeightPx.
+ /// 그림 속 몸(머리 끝~발) 세로 px — bodyHeightPx(무기 제외) 우선, 없으면 charHeightPx.
+ public float BodyPx
+ {
+ get { return Meta.bodyHeightPx > 0.5f ? Meta.bodyHeightPx : Meta.charHeightPx; }
+ }
+
+ /// 배율 기준 세로 크기(월드) — 임포터 PPU = meta.ppu 전제 · 크기 표 size_px 우선(모든 유닛의 머리 크기가 같아짐), 없으면 BodyPx.
public float CharHeightWorld
{
get
{
if (Meta.ppu <= 0.01f) return 1f;
- float px = Meta.bodyHeightPx > 0.5f ? Meta.bodyHeightPx : Meta.charHeightPx;
- return px / Meta.ppu;
+ return (SizePx > 0.5f ? SizePx : BodyPx) / Meta.ppu;
}
}
}
@@ -54,7 +64,41 @@ namespace TKS
public static class UnitArt
{
public const string Root = "Units/";
+ /// 크기 표(TextAsset · utf-8-sig CSV · 열 art · size_px) = Resources/Units/art_size.csv.
+ public const string SizeTable = "art_size";
static readonly Dictionary cache = new Dictionary();
+ static Dictionary sizes;
+
+ /// 크기 표의 size_px(없으면 0).
+ public static float SizeOf(string id)
+ {
+ if (sizes == null) sizes = ParseSizes(Resources.Load(Root + SizeTable));
+ float v;
+ return id != null && sizes.TryGetValue(id, out v) ? v : 0f;
+ }
+
+ static Dictionary ParseSizes(TextAsset ta)
+ {
+ var d = new Dictionary();
+ if (ta == null) return d;
+ var lines = ta.text.TrimStart('').Split('\n');
+ if (lines.Length == 0) return d;
+ var head = new List(lines[0].Trim().Split(','));
+ int ia = head.IndexOf("art"), isz = head.IndexOf("size_px");
+ if (ia < 0 || isz < 0)
+ {
+ Debug.LogWarning("[TKS] 유닛 크기 표 열(art · size_px) 없음");
+ return d;
+ }
+ for (int i = 1; i < lines.Length; i++)
+ {
+ var c = lines[i].Trim().Split(',');
+ float v;
+ if (c.Length > Mathf.Max(ia, isz) && float.TryParse(c[isz], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out v) && v > 0f)
+ d[c[ia]] = v;
+ }
+ return d;
+ }
public static UnitArtData Load(string id)
{
@@ -69,6 +113,7 @@ namespace TKS
public static void ClearCache()
{
cache.Clear();
+ sizes = null;
}
static UnitArtData LoadUncached(string id)
@@ -87,7 +132,7 @@ namespace TKS
}
if (meta == null || meta.clips == null) return null;
- var d = new UnitArtData { Id = id, Meta = meta };
+ var d = new UnitArtData { Id = id, Meta = meta, SizePx = SizeOf(id) };
foreach (var c in meta.clips)
{
if (c == null || string.IsNullOrEmpty(c.name)) continue;
@@ -134,6 +179,10 @@ namespace TKS
get { return data; }
}
+ /// 그려진 머리 끝 높이(월드 · 발 기준) = BodyPx × 배율 — 크기 표로 커진 기마 유닛은 표준 키보다 높다.
+ public float TopHeight { get; private set; }
+
+ /// 표준 키(월드) — 크기 표 size_px 가 이 높이가 되도록 배율(= 머리 크기 통일).
public void Setup(UnitArtData d, float worldHeight, int sortingOrder)
{
data = d;
@@ -143,6 +192,7 @@ namespace TKS
float h = d.CharHeightWorld;
float s = h > 0.001f ? worldHeight / h : 1f;
transform.localScale = new Vector3(s, s, 1f);
+ TopHeight = d.Meta.ppu > 0.01f ? d.BodyPx / d.Meta.ppu * s : worldHeight;
}
public bool Has(string clip)
diff --git a/Assets/Scripts/Runtime/UnitView.cs b/Assets/Scripts/Runtime/UnitView.cs
index b4377aec..6e02c904 100644
--- a/Assets/Scripts/Runtime/UnitView.cs
+++ b/Assets/Scripts/Runtime/UnitView.cs
@@ -140,6 +140,7 @@ namespace TKS
ago.transform.localPosition = new Vector3(0f, -u.Radius * 0.55f, 0f);
anim = ago.AddComponent();
anim.Setup(art, u.ArtHeight, 0);
+ u.ArtHeight = anim.TopHeight; // 크기 표 배율 뒤 실제 머리 끝(HP 바 · 머리 위 표시 기준)
anim.Renderer.flipX = facingLeft;
anim.SetIdle(ClipName("idle"));
anim.PlayIdle();
diff --git a/Assets/Tests/PlayMode/BattleFlowTests.cs b/Assets/Tests/PlayMode/BattleFlowTests.cs
index 4539cfcb..6466caa0 100644
--- a/Assets/Tests/PlayMode/BattleFlowTests.cs
+++ b/Assets/Tests/PlayMode/BattleFlowTests.cs
@@ -975,6 +975,49 @@ namespace TKS.Tests
Assert.IsTrue(u.View.Anim.Renderer.flipX, "왼쪽 = 좌우 반전");
}
+ [UnityTest]
+ public IEnumerator UnitArt_SizeTable_SameHeadSize_MountedRiderLikeFoot()
+ {
+ // PD 2026-11-09 「캐릭터 크기 동일 · 말 탄 유닛도 보병·창병과 비슷한 비율」 — 크기 표(art_size.csv · 눈 간격 기준)로 배율을 정하면
+ // 게임 안 눈 간격(월드)이 모든 유닛에서 같다 · 기마 유닛은 표준 키보다 커진다(탄 사람 = 보병 크기)
+ var ta = Resources.Load(UnitArt.Root + UnitArt.SizeTable);
+ Assert.IsNotNull(ta, "크기 표 Resources/Units/art_size.csv");
+ var lines = ta.text.TrimStart('').Split('\n');
+ var head = new List(lines[0].Trim().Split(','));
+ int ia = head.IndexOf("art"), im = head.IndexOf("mounted"), ie = head.IndexOf("eye_px");
+ var eyes = new List();
+ int mounted = 0;
+ var go = new GameObject("SizeProbe");
+ var fa = go.AddComponent();
+ for (int i = 1; i < lines.Length; i++)
+ {
+ var c = lines[i].Trim().Split(',');
+ if (c.Length <= ie) continue;
+ var a = UnitArt.Load(c[ia]);
+ Assert.IsNotNull(a, "표의 아트 " + c[ia]);
+ Assert.Greater(a.SizePx, 0f, c[ia] + " size_px");
+ fa.Setup(a, cfg.unitArtHeight, 0);
+ eyes.Add(float.Parse(c[ie], System.Globalization.CultureInfo.InvariantCulture) / a.Meta.ppu * go.transform.localScale.x);
+ if (c[im] == "1")
+ {
+ mounted++;
+ Assert.Greater(fa.TopHeight, cfg.unitArtHeight * 1.2f, c[ia] + " 기마 = 탄 사람이 보병 크기가 되도록 커짐");
+ }
+ }
+ Object.Destroy(go);
+ Assert.GreaterOrEqual(eyes.Count, 15, "표 행 수");
+ Assert.GreaterOrEqual(mounted, 5, "기마 행 수");
+ eyes.Sort();
+ Assert.Less(eyes[eyes.Count - 1] / eyes[0], 1.01f, "모든 유닛 눈 간격(월드) 같음");
+
+ // 배치된 기마 영웅 = HP 바 기준(ArtHeight)이 실제 머리 끝
+ yield return Fresh();
+ var u = bm.SpawnUnit(UnitType.Cavalry, Faction.Player, new Vector2(0f, -4f), "조운", 1f, 1f, false, "zhaoyun");
+ yield return null;
+ Assert.AreEqual(u.View.Anim.TopHeight, u.ArtHeight, 1e-4f, "ArtHeight = 그려진 머리 끝");
+ Assert.Greater(u.ArtHeight, cfg.unitArtHeight, "기마 = 표준 키보다 높음");
+ }
+
// ---------- 스테이지 표(Assets/Data/Stages/*.csv · 145 스테이지 · 2026-11-05 #1086) ----------
static StageTable.Tables ReadStageTable()
diff --git a/Tools/unit_art_size.py b/Tools/unit_art_size.py
new file mode 100644
index 00000000..2c1bf297
--- /dev/null
+++ b/Tools/unit_art_size.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+"""유닛 아트 크기 표 생성(PD 2026-11-09 「영웅 목록 캐릭터 크기 동일 · 말 탄 유닛도 보병·창병과 비슷한 비율」).
+
+기준 = 눈 간격(머리 크기) — 투구·수염·말과 상관없이 같은 화풍이면 머리 크기에 비례한다.
+ eye_px = 대기 첫 프레임(front_idle/f000.png)의 두 눈 중심 사이 가로 px(얼굴 피부 덩어리 안 어두운 구멍 2개)
+ ref = 말을 타지 않은 유닛(mounted=0)의 eye_px / body_px 중앙값
+ size_px = eye_px / ref = 「머리 크기가 표준인 유닛의 키」로 환산한 세로 px
+게임(FrameAnimator)·영웅 목록은 bodyHeightPx 대신 size_px 로 배율을 정한다 → 모든 유닛의 머리 크기가 같아지고,
+말 탄 유닛은 탄 사람이 보병과 같은 크기가 되도록 커진다.
+
+표 = Assets/Resources/Units/art_size.csv(utf-8-sig · SOT) · mounted 열만 사람이 고친다(새 아트 = 이름에 cavalry 면 1).
+눈을 못 찾으면 size_px 를 비워 둔다(게임은 bodyHeightPx 로 대체) · 실행 결과를 --sheet 시트로 눈 위치를 확인할 것.
+
+사용: python -I -B -X utf8 Tools/unit_art_size.py <프로젝트 루트> [--sheet out.png]
+"""
+import csv
+import glob
+import json
+import os
+import sys
+
+import numpy as np
+from PIL import Image, ImageDraw, ImageFont
+from scipy import ndimage
+
+TABLE = 'Assets/Resources/Units/art_size.csv'
+FIELDS = ['art', 'mounted', 'eye_px', 'body_px', 'size_px', 'note']
+
+
+def skin_mask(a, red):
+ r, g, b, al = a[..., 0], a[..., 1], a[..., 2], a[..., 3]
+ mx = np.maximum(np.maximum(r, g), b)
+ mn = np.minimum(np.minimum(r, g), b)
+ sat = np.where(mx > 0, (mx - mn) / np.maximum(mx, 1e-6), 0)
+ if red: # 붉은 얼굴(관우)
+ sk = (al > 0.9) & (r > 0.8) & (g > 0.3) & (g < 0.6) & (b > 0.25) & (b < 0.55)
+ else:
+ sk = (al > 0.9) & (r >= g) & (g >= b - 0.02) & (mx > 0.78) & (sat > 0.06) & (sat < 0.45) & ((r - b) > 0.06)
+ return sk, mx
+
+
+def find_eyes(img):
+ """두 눈 (왼쪽, 오른쪽) = (면적, x, y) · 못 찾으면 None."""
+ a = np.asarray(img.convert('RGBA')).astype(np.float32) / 255.0
+ for red in (False, True):
+ sk, val = skin_mask(a, red)
+ lab, n = ndimage.label(sk)
+ if not n:
+ continue
+ sizes = ndimage.sum(sk, lab, range(1, n + 1))
+ for k in list(np.argsort(sizes)[::-1][:6] + 1):
+ face = lab == k
+ ys, xs = np.where(face)
+ if len(ys) < 3000:
+ continue
+ fh = ys.max() - ys.min() + 1
+ holes = ndimage.binary_fill_holes(face) & ~face
+ hl, hn = ndimage.label(holes)
+ cand = []
+ for j in range(1, hn + 1):
+ yy, xx = np.where(hl == j)
+ if len(yy) < 150 or val[yy, xx].mean() > 0.4:
+ continue
+ if (xx.max() - xx.min() + 1) > 2.2 * (yy.max() - yy.min() + 1):
+ continue # 가로로 긴 구멍 = 수염·입
+ cand.append((len(yy), xx.mean(), yy.mean()))
+ best = None
+ for l in cand:
+ for r in cand:
+ if l[1] >= r[1] or abs(l[2] - r[2]) > 0.12 * fh:
+ continue
+ if min(l[0], r[0]) < 0.45 * max(l[0], r[0]):
+ continue
+ score = min(l[0], r[0]) * (r[1] - l[1])
+ if best is None or score > best[0]:
+ best = (score, l, r)
+ if best:
+ return best[1], best[2]
+ return None
+
+
+def main():
+ root = sys.argv[1]
+ sheet_path = sys.argv[sys.argv.index('--sheet') + 1] if '--sheet' in sys.argv else None
+ table = os.path.join(root, TABLE)
+ old = {}
+ if os.path.exists(table):
+ with open(table, encoding='utf-8-sig', newline='') as f:
+ old = {r['art']: r for r in csv.DictReader(f)}
+
+ rows, shots = [], []
+ for meta_path in sorted(glob.glob(os.path.join(root, 'Assets/Resources/Units/*/meta.json'))):
+ folder = os.path.dirname(meta_path)
+ art = os.path.basename(folder)
+ meta = json.load(open(meta_path, encoding='utf-8'))
+ body = meta.get('bodyHeightPx') or meta.get('charHeightPx') or 0
+ frame = os.path.join(folder, 'front_idle', 'f000.png')
+ if not os.path.exists(frame):
+ frame = sorted(glob.glob(os.path.join(folder, '*', 'f000.png')))[0]
+ img = Image.open(frame).convert('RGBA')
+ eyes = find_eyes(img)
+ prev = old.get(art, {})
+ mounted = prev.get('mounted', '1' if 'cavalry' in art else '0')
+ eye = round(eyes[1][1] - eyes[0][1], 1) if eyes else 0
+ rows.append({'art': art, 'mounted': mounted, 'eye_px': eye, 'body_px': body, 'size_px': '', 'note': prev.get('note', '')})
+ shots.append((art, meta, img, eyes))
+
+ foot = sorted(r['eye_px'] / r['body_px'] for r in rows if r['mounted'] == '0' and r['eye_px'] and r['body_px'])
+ if not foot:
+ sys.exit('말을 타지 않은 유닛의 눈 간격을 하나도 못 찾음')
+ mid = len(foot) // 2
+ ref = foot[mid] if len(foot) % 2 else (foot[mid - 1] + foot[mid]) / 2
+ for r in rows:
+ r['size_px'] = round(r['eye_px'] / ref, 1) if r['eye_px'] else ''
+
+ with open(table, 'w', encoding='utf-8-sig', newline='') as f:
+ w = csv.DictWriter(f, fieldnames=FIELDS, lineterminator='\n')
+ w.writeheader()
+ w.writerows(rows)
+ print('ref(눈 간격 / 키 · 말 안 탄 %d종 중앙값) = %.4f' % (len(foot), ref))
+ for r in rows:
+ s = (r['size_px'] / r['body_px']) if r['size_px'] else 0
+ print('%-22s mounted %s eye %6s body %5s size %7s 배율 x%.2f%s' % (
+ r['art'], r['mounted'], r['eye_px'], r['body_px'], r['size_px'], 1 / s if s else 0, '' if r['eye_px'] else ' ← 눈 못 찾음(bodyHeightPx 대체)'))
+
+ if sheet_path: # 확인 시트: 표 배율로 그린 대기 그림 + 눈 표시(머리 크기가 같아 보여야 함)
+ unit = 160.0 # 표준 키 = 시트 160 px
+ cols, cw, ch = 9, 220, 330
+ sheet = Image.new('RGB', (cw * cols, ch * ((len(shots) + cols - 1) // cols)), (40, 44, 60))
+ dr = ImageDraw.Draw(sheet)
+ font = ImageFont.truetype('C:/Windows/Fonts/malgun.ttf', 13)
+ for i, ((art, meta, img, eyes), r) in enumerate(zip(shots, rows)):
+ size = r['size_px'] or r['body_px']
+ sc = unit / size
+ x0, y0 = (i % cols) * cw, (i // cols) * ch
+ t = img.resize((max(1, int(img.width * sc)), max(1, int(img.height * sc))), Image.LANCZOS)
+ base = y0 + ch - 22
+ ox, oy = x0 + cw // 2 - t.width // 2, int(base - (1 - meta['pivot'][1]) * img.height * sc)
+ sheet.paste(t, (ox, oy), t)
+ for e in eyes or ():
+ ex, ey = ox + e[1] * sc, oy + e[2] * sc
+ dr.ellipse([ex - 3, ey - 3, ex + 3, ey + 3], outline=(0, 255, 0), width=2)
+ dr.text((x0 + 3, base + 3), '%s%s' % (art, ' (말)' if r['mounted'] == '1' else ''), fill=(255, 255, 255), font=font)
+ sheet.save(sheet_path)
+ print('sheet', sheet_path)
+
+
+if __name__ == '__main__':
+ main()