전투 로직 #1080 — 알까기 충돌(보병) · 관통·아군 직전 정지(기병) · 밀며 함께 전진(창병) · 밀린 아군 공격 기회 · 궁병 제자리 사격(사거리 = 슬링샷)+접근 20 % · 궁수 근접 피해 50 %/피격 +50 % · 원화 1차·2차 반영(전장 배경 + 황건적 5 + 동탁군 4) · 테스트 22/22
- ActionResolver 전면 재작성: InfantryRoutine(접촉점 정지 · Push 접촉 방향 × collisionPushRatio · 적이면 공격+반격 뒤 밀림) · SpearRoutine(앞 유닛과 같은 속도로 함께 전진 · 접촉 간격 유지) · CavalryRoutine(벽만 막음 · StopBeforeUnclearedAllies) · ArcherRoutine(AttackRange = 슬링 · ArcherMoveAllow = 슬링 × archerRangeRatio) · PushedAllyAttack(규칙 ⑤ · ActionOutcome.Pushed/Notes) - Combat: Damage(melee) 궁병 근접 배율(근접 유닛 상대만) · FirstUnitOnPath · TouchingOpponent · FreeTravel ignore - BalanceConfig/Defaults: archerMeleeDealtRatio 0.5 · archerMeleeTakenRatio 1.5 · collisionPushRatio 1 · boardArt battlefield_01 (자산 재시드) - Board: Resources/Board/<boardArt> 원화 배경(격자·진영 띠 생략) · UnitArtImporter Board 규칙 · GameBootstrap - Resources/Units: hgj_infantry·spear·cavalry·archer·boss_zhangjiao · dz_infantry·spear·cavalry·archer (원화 세션 납품 정지 그림 · rig_frames_export static) · Resources/Board/battlefield_01.png - BattleManager Describe 「밀어냄 · 밀린 B → 적 공격」 · BattleHUD 궁병 줄 · 테스트 22종(신규·개정 7 + 원화 확인) · CLAUDE.md · README Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
|
@ -67,7 +67,11 @@ MonoBehaviour:
|
|||
bossRadiusMult: 1.25
|
||||
cavalryKnockback: 0.35
|
||||
archerRangeRatio: 0.2
|
||||
archerMeleeDealtRatio: 0.5
|
||||
archerMeleeTakenRatio: 1.5
|
||||
collisionPushRatio: 1
|
||||
unitArtHeight: 1.5
|
||||
boardArt: battlefield_01
|
||||
minDragDistance: 0.35
|
||||
finalHitTolerance: 0.15
|
||||
moveSpeed: 9
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a74463feafb491c4dafe8d1133c7f3cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 6.3 MiB |
|
|
@ -0,0 +1,169 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e3a6ea6e68311c4683b0c9f0944480f
|
||||
TextureImporter:
|
||||
internalIDToNameTable:
|
||||
- first:
|
||||
213: 6962303912857251952
|
||||
second: battlefield_01_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: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
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: 4096
|
||||
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: 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: battlefield_01_0
|
||||
rect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 2000
|
||||
height: 3200
|
||||
alignment: 0
|
||||
pivot: {x: 0, y: 0}
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
customData:
|
||||
outline: []
|
||||
physicsShape: []
|
||||
tessellationDetail: -1
|
||||
bones: []
|
||||
spriteID: 0703b2855821f9060800000000000000
|
||||
internalID: 6962303912857251952
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable:
|
||||
battlefield_01_0: 6962303912857251952
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c762694df723e0449b223cfd4674c673
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b61c180460f762d45a53cbc320967d18
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 470 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9948907966e189148b1268d13d76c475
|
||||
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.02064}
|
||||
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: 18
|
||||
width: 530
|
||||
height: 932
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
568,
|
||||
969
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02064
|
||||
],
|
||||
"charHeightPx": 929,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 68b84df74057f3f47a3c33b6b3342573
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c58e18725c9a5514da41526a47391814
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5769dc3460a78ca47a6b782fc06ebd7c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 535 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e1b356e27b2ca84409a21237e725c1ad
|
||||
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.02092}
|
||||
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: 676
|
||||
height: 918
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
714,
|
||||
956
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02092
|
||||
],
|
||||
"charHeightPx": 916,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d3a20275187eda3458bc205f153411ae
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8264d82aab0a4ec4da8daef6b607a1d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 64bd26411189d0149a15c532ddb851d1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 441 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 82e9825e5f8895c4d88848505ca6a5b9
|
||||
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.0221}
|
||||
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: 528
|
||||
height: 867
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
566,
|
||||
905
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.0221
|
||||
],
|
||||
"charHeightPx": 865,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ce640825d5c4e74fbfb0c09a8d7fced
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a518d38fe2e823429d906d3681919ce
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 21750aef097feac448849e0781b4d12f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 509 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b581a6de17271047ba0feb63ea70500
|
||||
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.02033}
|
||||
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: 540
|
||||
height: 960
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
578,
|
||||
984
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02033
|
||||
],
|
||||
"charHeightPx": 957,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c406e2a385c03a74f87aa25e6a6f3c09
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6c3636902d4db564792989f078754f48
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b3ec43f64b3072d4abd386d5e1895eb6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 560 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e0d9e04563411c949a226789ed94534f
|
||||
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.02157}
|
||||
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: 557
|
||||
height: 890
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
595,
|
||||
927
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02157
|
||||
],
|
||||
"charHeightPx": 887,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d046c89bed998a340bd6f9ecefbfc4f0
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 09f436c99abc85c40903215547c36eaf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d343dbcb4505382479e05bdfc7ab2ffe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 549 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 17f44627146649542b114aa9b63c9dd1
|
||||
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.02119}
|
||||
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: 608
|
||||
height: 906
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
646,
|
||||
944
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02119
|
||||
],
|
||||
"charHeightPx": 904,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c4fddb826eaa7874cba807c3f1cc1903
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e8fa108f337384f4fa47e6a21d775d21
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07ca2b82651d3184f845bf4709a9692d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 567 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9fed149a8ac54b94eb620fe7bf0ad83c
|
||||
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.02083}
|
||||
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: 687
|
||||
height: 922
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
725,
|
||||
960
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02083
|
||||
],
|
||||
"charHeightPx": 920,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7092a9a34597e54d90b46dbbde76bd4
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: da646cd4bb2c34f418b81a081dac92ed
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6cad2bd92c97c484f8fc94ded468c7bb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 457 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fe11dbe17cc3fe840820473f273cf926
|
||||
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.02291}
|
||||
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: 835
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
570,
|
||||
873
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02291
|
||||
],
|
||||
"charHeightPx": 833,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3294e2cca1df12e4a82604ec8c6ad12d
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c1131a44c41c90e4483cfcd59e05716f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 84f1180717c2745438b1b87279a68ce1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
After Width: | Height: | Size: 489 KiB |
|
|
@ -0,0 +1,168 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dc29d8b6aee0ae141800576aff7e4518
|
||||
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.02232}
|
||||
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: 500
|
||||
height: 858
|
||||
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:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"fps": 1,
|
||||
"ppu": 100,
|
||||
"frame": [
|
||||
538,
|
||||
896
|
||||
],
|
||||
"pivot": [
|
||||
0.5,
|
||||
0.02232
|
||||
],
|
||||
"charHeightPx": 856,
|
||||
"clips": [
|
||||
{
|
||||
"name": "front_idle",
|
||||
"frames": 1,
|
||||
"loop": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 35d4a4ca7503dc84e896e62dbc609093
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -16,7 +16,7 @@ namespace TKS.EditorTools
|
|||
{
|
||||
Stats(UnitType.Infantry, 100, 30, 10, 4.5f, 0f, 0.45f),
|
||||
Stats(UnitType.Cavalry, 110, 28, 8, 6.5f, 0f, 0.45f),
|
||||
Stats(UnitType.Archer, 70, 26, 4, 4.0f, 0f, 0.42f), // 궁병 사거리 = slingMaxDistance × archerRangeRatio (attackRange 미사용)
|
||||
Stats(UnitType.Archer, 70, 26, 4, 4.0f, 0f, 0.42f), // 궁병 사거리 = slingMaxDistance (attackRange 미사용 · #1080)
|
||||
Stats(UnitType.Spear, 100, 30, 10, 4.5f, 2.2f, 0.45f),
|
||||
};
|
||||
|
||||
|
|
@ -41,8 +41,12 @@ namespace TKS.EditorTools
|
|||
c.bossHpMult = 1.6f;
|
||||
c.bossRadiusMult = 1.25f;
|
||||
c.cavalryKnockback = 0.35f;
|
||||
c.archerRangeRatio = 0.2f; // PD 2026-11-04: 슬링샷 범위 + 20 %
|
||||
c.archerRangeRatio = 0.2f; // PD 2026-11-04: 사거리(슬링샷 범위) 밖 +20 % 안의 적에게 다가가 사격
|
||||
c.archerMeleeDealtRatio = 0.5f; // PD 2026-11-04 #1080: 궁수 근접 전투 피해 50 %
|
||||
c.archerMeleeTakenRatio = 1.5f; // PD 2026-11-04 #1080: 궁수 근접 피격 +50 %
|
||||
c.collisionPushRatio = 1f; // PD 2026-11-04 #1080: 보병 알까기 밀어내기 = 남은 거리 그대로
|
||||
c.unitArtHeight = 1.5f;
|
||||
c.boardArt = "battlefield_01"; // 원화 세션 1차 납품(2026-11-04) · Resources/Board/
|
||||
c.minDragDistance = 0.35f;
|
||||
c.finalHitTolerance = 0.15f;
|
||||
c.moveSpeed = 9f;
|
||||
|
|
|
|||
|
|
@ -11,10 +11,30 @@ namespace TKS.EditorTools
|
|||
public class UnitArtImporter : AssetPostprocessor
|
||||
{
|
||||
public const string Root = "Assets/Resources/Units/";
|
||||
/// <summary>전장 배경(원화 세션 납품 · 5:8 불투명 · PPU 100 = 전장 20×32 월드).</summary>
|
||||
public const string BoardRoot = "Assets/Resources/Board/";
|
||||
|
||||
void OnPreprocessTexture()
|
||||
{
|
||||
string path = assetPath.Replace('\\', '/');
|
||||
if (path.StartsWith(BoardRoot))
|
||||
{
|
||||
var bi = (TextureImporter)assetImporter;
|
||||
bi.textureType = TextureImporterType.Sprite;
|
||||
bi.spriteImportMode = SpriteImportMode.Single;
|
||||
bi.mipmapEnabled = false;
|
||||
bi.filterMode = FilterMode.Bilinear;
|
||||
bi.alphaIsTransparency = false;
|
||||
bi.textureCompression = TextureImporterCompression.Compressed;
|
||||
bi.maxTextureSize = 4096;
|
||||
bi.spritePixelsPerUnit = 100f;
|
||||
var bs = new TextureImporterSettings();
|
||||
bi.ReadTextureSettings(bs);
|
||||
bs.spriteMeshType = SpriteMeshType.FullRect;
|
||||
bs.spriteAlignment = (int)SpriteAlignment.Center;
|
||||
bi.SetTextureSettings(bs);
|
||||
return;
|
||||
}
|
||||
if (!path.StartsWith(Root)) return;
|
||||
var ti = (TextureImporter)assetImporter;
|
||||
ti.textureType = TextureImporterType.Sprite;
|
||||
|
|
@ -59,15 +79,18 @@ namespace TKS.EditorTools
|
|||
[MenuItem("TKS/Reimport Unit Art (유닛 아트 재임포트)")]
|
||||
public static void ReimportAll()
|
||||
{
|
||||
string dir = Root.TrimEnd('/');
|
||||
if (!AssetDatabase.IsValidFolder(dir))
|
||||
foreach (var root in new[] { Root, BoardRoot })
|
||||
{
|
||||
Debug.Log("[TKS] " + dir + " 없음");
|
||||
return;
|
||||
string dir = root.TrimEnd('/');
|
||||
if (!AssetDatabase.IsValidFolder(dir))
|
||||
{
|
||||
Debug.Log("[TKS] " + dir + " 없음");
|
||||
continue;
|
||||
}
|
||||
AssetDatabase.ImportAsset(dir, ImportAssetOptions.ImportRecursive | ImportAssetOptions.ForceUpdate);
|
||||
Debug.Log("[TKS] 아트 재임포트 완료 · " + dir);
|
||||
}
|
||||
AssetDatabase.ImportAsset(dir, ImportAssetOptions.ImportRecursive | ImportAssetOptions.ForceUpdate);
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("[TKS] 유닛 아트 재임포트 완료 · " + dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,22 +11,26 @@ namespace TKS
|
|||
public bool Returned; // 기병: 최종 목표점 명중 → 제자리 복귀
|
||||
public bool Overwatch; // 창병: 최종 목표점 명중 → 대기(견제)
|
||||
public bool Crit; // 궁병: 최종 목표점 명중 → 크리티컬
|
||||
public bool MovedOnly; // 공격 없이 이동만
|
||||
public bool MovedOnly; // 행동 유닛 자신은 공격하지 않음(이동 · 밀어내기만)
|
||||
public int DamageDealt;
|
||||
public int CounterTaken;
|
||||
public List<Unit> Attacked = new List<Unit>();
|
||||
/// <summary>충돌로 밀려난 유닛(아군·적군 · PD 2026-11-04 #1080).</summary>
|
||||
public List<Unit> Pushed = new List<Unit>();
|
||||
/// <summary>밀린 아군이 적과 닿아 얻은 공격 등 부가 설명(HUD 문구).</summary>
|
||||
public List<string> Notes = new List<string>();
|
||||
/// <summary>실제로 지나간 경로(시작 포함). 창병 견제 판정용.</summary>
|
||||
public List<Vector2> Path = new List<Vector2>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 슬링샷 벡터(시작점 → 목표점)를 병종 규칙대로 이동·공격으로 해석한다.
|
||||
/// ① 보병: 경로 위 첫 적까지 이동 후 공격 · 목표점 명중 시 추가 이동 1회
|
||||
/// ② 기병: 경로 위 적을 모두 관통 피해 · 목표점까지 이동 · 목표점 명중 시 제자리 복귀
|
||||
/// ③ 창병: 보병과 동일 · 목표점 명중 시 대기 → 반경 안에서 움직이는 적에게 피해
|
||||
/// ④ 궁병: 조준 방향 슬링샷 거리 + 사거리(슬링샷 최대 × archerRangeRatio) 안에 적이 있으면
|
||||
/// 적 가장자리가 사거리에 들어올 때까지만 이동 후 사격(반격 없음) · 목표점 명중 시 크리티컬 ·
|
||||
/// 적이 없으면 목표점까지 이동만(PD 2026-11-04 2차 지시 · 1차의 「행동 취소」 폐기)
|
||||
/// 슬링샷 벡터(시작점 → 목표점)를 병종 규칙대로 이동·공격으로 해석한다 (PD 2026-11-04 #1080 충돌 규칙).
|
||||
/// ① 보병: 경로 위 첫 유닛(아군·적군)에 부딪히면 알까기처럼 그 유닛을 남은 힘만큼 밀어내고 자신은 멈춤 · 적이면 공격(반격 받음) · 목표점 명중 시 추가 이동 1회
|
||||
/// ② 기병: 유닛을 무시하고 관통 · 경로 위 적은 모두 피해(옆으로 밀림) · 아군은 지나칠 거리가 안 나오면 그 아군 직전에 멈춤 · 목표점 명중 시 제자리 복귀
|
||||
/// ③ 창병: 첫 유닛에 부딪히면 튕기지 않고 남은 거리만큼 밀며 함께 전진 · 적이면 피해(반격 받음) · 아군이면 밀기만 · 목표점 명중 시 대기(견제)
|
||||
/// ④ 궁병: 사거리 = 슬링샷 최대 거리 · 조준 방향 사거리 안의 적은 제자리에서 사격 · 사거리 밖 ~ ×(1+비율) 안이면 들어올 만큼만 다가가 사격 ·
|
||||
/// 더 멀면 목표점까지 이동 · 반격 없음 · 목표점 명중 시 크리티컬 · 근접 전투는 피해 50 % · 피격 +50 %(Combat.Damage)
|
||||
/// ⑤ 밀린 아군(행동 유닛과 같은 진영)이 상대 유닛과 닿으면 그 자리에서 공격 1회(조작마다 · 자기 행동은 소모하지 않음)
|
||||
/// </summary>
|
||||
public class ActionResolver
|
||||
{
|
||||
|
|
@ -53,8 +57,10 @@ namespace TKS
|
|||
switch (u.Type)
|
||||
{
|
||||
case UnitType.Infantry:
|
||||
yield return InfantryRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||
break;
|
||||
case UnitType.Spear:
|
||||
yield return MeleeRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||
yield return SpearRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||
break;
|
||||
case UnitType.Cavalry:
|
||||
yield return CavalryRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||
|
|
@ -67,7 +73,7 @@ namespace TKS
|
|||
|
||||
// ---------- 미리보기(드라이런) ----------
|
||||
|
||||
/// <summary>조작 중 표시용: 이 슬링샷이 공격하게 될 적 목록.</summary>
|
||||
/// <summary>조작 중 표시용: 이 슬링샷으로 행동 유닛이 직접 공격하게 될 적 목록(밀린 아군의 공격은 예측하지 않음).</summary>
|
||||
public List<Unit> PredictTargets(Unit u, Vector2 target, bool moveOnly)
|
||||
{
|
||||
var result = new List<Unit>();
|
||||
|
|
@ -83,20 +89,21 @@ namespace TKS
|
|||
case UnitType.Infantry:
|
||||
case UnitType.Spear:
|
||||
{
|
||||
var first = Combat.FirstEnemyOnPath(u, origin, dir, dist, out hitDist);
|
||||
if (first != null && Combat.FreeTravel(u, origin, dir, hitDist, false) >= hitDist - 0.05f) result.Add(first);
|
||||
var first = FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
||||
if (first != null && first.Faction != u.Faction) result.Add(first);
|
||||
break;
|
||||
}
|
||||
case UnitType.Cavalry:
|
||||
{
|
||||
float travel = Combat.FreeTravel(u, origin, dir, dist, false);
|
||||
float travel = Combat.FreeTravel(u, origin, dir, dist, false, false);
|
||||
travel = StopBeforeUnclearedAllies(u, origin, dir, travel);
|
||||
var dists = new List<float>();
|
||||
result.AddRange(Combat.EnemiesOnPath(u, origin, dir, travel, dists));
|
||||
break;
|
||||
}
|
||||
case UnitType.Archer:
|
||||
{
|
||||
var first = Combat.FirstEnemyOnPath(u, origin, dir, dist + u.AttackRange, out hitDist);
|
||||
var first = Combat.FirstEnemyOnPath(u, origin, dir, u.AttackRange + u.ArcherMoveAllow, out hitDist);
|
||||
if (first != null) result.Add(first);
|
||||
break;
|
||||
}
|
||||
|
|
@ -104,48 +111,134 @@ namespace TKS
|
|||
return result;
|
||||
}
|
||||
|
||||
// ---------- 보병 · 창병 ----------
|
||||
// ---------- 공통 질의 ----------
|
||||
|
||||
IEnumerator MeleeRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||
/// <summary>경로 위 첫 유닛. 벽이 그보다 먼저 있으면 null.</summary>
|
||||
Unit FirstUnitBeforeWall(Unit u, Vector2 origin, Vector2 dir, float dist, out float hitDist)
|
||||
{
|
||||
float hitDist = 0f;
|
||||
Unit first = moveOnly ? null : Combat.FirstEnemyOnPath(u, origin, dir, dist, out hitDist);
|
||||
Unit first = Combat.FirstUnitOnPath(u, origin, dir, dist, out hitDist);
|
||||
if (first == null) return null;
|
||||
float wall = Combat.FreeTravel(u, origin, dir, dist, false, false);
|
||||
if (hitDist > wall + Combat.Epsilon)
|
||||
{
|
||||
hitDist = 0f;
|
||||
return null;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
// ---------- 보병: 알까기 충돌 ----------
|
||||
|
||||
IEnumerator InfantryRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||
{
|
||||
if (moveOnly)
|
||||
{
|
||||
yield return MoveFree(u, origin, dir, dist, o);
|
||||
yield break;
|
||||
}
|
||||
|
||||
float hitDist;
|
||||
Unit first = FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
||||
if (first == null)
|
||||
{
|
||||
float free = Combat.FreeTravel(u, origin, dir, dist, true);
|
||||
yield return Move(u, origin + dir * free, o);
|
||||
o.MovedOnly = true;
|
||||
yield return MoveFree(u, origin, dir, dist, o);
|
||||
yield break;
|
||||
}
|
||||
|
||||
float freeToEnemy = Combat.FreeTravel(u, origin, dir, hitDist, false);
|
||||
if (freeToEnemy < hitDist - 0.05f)
|
||||
Vector2 contact = origin + dir * hitDist;
|
||||
yield return Move(u, contact, o); // 부딪힌 지점에서 멈춤
|
||||
float remaining = Mathf.Max(0f, dist - hitDist);
|
||||
bool enemy = first.Faction != u.Faction;
|
||||
bool isFinal = enemy && Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance) == first;
|
||||
|
||||
if (enemy)
|
||||
{
|
||||
yield return Attack(u, first, false, false, o, true);
|
||||
if (!u.IsAlive) yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 아군·벽이 먼저 막는다 → 거기까지만 이동
|
||||
yield return Move(u, origin + dir * freeToEnemy, o);
|
||||
o.MovedOnly = true;
|
||||
}
|
||||
|
||||
if (first.IsAlive)
|
||||
{
|
||||
// 알까기: 접촉 방향(행동 유닛 중심 → 부딪힌 유닛 중심)으로 남은 힘만큼 밀어낸다
|
||||
Vector2 n = first.Position - contact;
|
||||
n = n.sqrMagnitude > 1e-6f ? n.normalized : dir;
|
||||
yield return Push(first, n, remaining * cfg.collisionPushRatio, u, o);
|
||||
if (!enemy) yield return PushedAllyAttack(first, o);
|
||||
}
|
||||
|
||||
if (isFinal && u.IsAlive) o.BonusMove = true;
|
||||
}
|
||||
|
||||
// ---------- 창병: 밀며 함께 전진 ----------
|
||||
|
||||
IEnumerator SpearRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||
{
|
||||
if (moveOnly)
|
||||
{
|
||||
yield return MoveFree(u, origin, dir, dist, o);
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return Move(u, origin + dir * hitDist, o);
|
||||
Unit finalHit = Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance);
|
||||
bool isFinal = finalHit == first;
|
||||
yield return Attack(u, first, false, false, o);
|
||||
if (!u.IsAlive) yield break;
|
||||
float hitDist;
|
||||
Unit first = FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
||||
if (first == null)
|
||||
{
|
||||
yield return MoveFree(u, origin, dir, dist, o);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (u.Type == UnitType.Infantry && isFinal) o.BonusMove = true;
|
||||
if (u.Type == UnitType.Spear && isFinal)
|
||||
Vector2 contact = origin + dir * hitDist;
|
||||
yield return Move(u, contact, o);
|
||||
float remaining = Mathf.Max(0f, dist - hitDist);
|
||||
bool enemy = first.Faction != u.Faction;
|
||||
bool isFinal = enemy && Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance) == first;
|
||||
|
||||
if (enemy)
|
||||
{
|
||||
yield return Attack(u, first, false, false, o, true);
|
||||
if (!u.IsAlive) yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
o.MovedOnly = true;
|
||||
}
|
||||
|
||||
if (first.IsAlive && remaining > 0.01f)
|
||||
{
|
||||
// 튕기지 않고 앞 유닛을 밀며 함께 전진 · 앞 유닛이 다른 유닛·벽에 막히면 둘 다 멈춤 · 접촉 간격 유지
|
||||
float travel = Combat.FreeTravel(first, first.Position, dir, remaining, true, true, u);
|
||||
if (travel > 0.01f)
|
||||
{
|
||||
Vector2 firstEnd = first.Position + dir * travel;
|
||||
Vector2 spearEnd = contact + dir * travel;
|
||||
Fx.Run(Fx.MoveAlong(first.transform, first.Position, firstEnd, cfg.moveSpeed, null));
|
||||
yield return Fx.MoveAlong(u.transform, contact, spearEnd, cfg.moveSpeed, null);
|
||||
first.Position = firstEnd;
|
||||
u.Position = spearEnd;
|
||||
o.Path.Add(spearEnd);
|
||||
battle.NotifyChanged();
|
||||
}
|
||||
o.Pushed.Add(first);
|
||||
if (!enemy) yield return PushedAllyAttack(first, o);
|
||||
}
|
||||
|
||||
if (isFinal && u.IsAlive)
|
||||
{
|
||||
u.SetOverwatch(true);
|
||||
o.Overwatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 기병 ----------
|
||||
// ---------- 기병: 관통 ----------
|
||||
|
||||
IEnumerator CavalryRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||
{
|
||||
float travel = Combat.FreeTravel(u, origin, dir, dist, false);
|
||||
float travel = Combat.FreeTravel(u, origin, dir, dist, false, false); // 유닛은 무시 · 벽만
|
||||
travel = StopBeforeUnclearedAllies(u, origin, dir, travel); // 지나칠 수 없는 아군 직전에서 멈춤
|
||||
var dists = new List<float>();
|
||||
var enemies = moveOnly ? new List<Unit>() : Combat.EnemiesOnPath(u, origin, dir, travel, dists);
|
||||
Unit finalHit = moveOnly ? null : Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance);
|
||||
|
|
@ -209,7 +302,7 @@ namespace TKS
|
|||
void PierceHit(Unit cav, Unit e, Vector2 dir, ActionOutcome o)
|
||||
{
|
||||
if (!e.IsAlive) return;
|
||||
int dmg = Combat.Damage(cfg, cav, e, false, rng);
|
||||
int dmg = Combat.Damage(cfg, cav, e, false, rng, true);
|
||||
e.TakeDamage(dmg);
|
||||
o.Attacked.Add(e);
|
||||
o.DamageDealt += dmg;
|
||||
|
|
@ -225,6 +318,33 @@ namespace TKS
|
|||
battle.NotifyChanged();
|
||||
}
|
||||
|
||||
/// <summary>아군은 관통(통과)하되 travel 안에 완전히 지나칠 수 없는 아군이 있으면 그 아군 직전까지로 줄인다.</summary>
|
||||
float StopBeforeUnclearedAllies(Unit u, Vector2 origin, Vector2 dir, float travel)
|
||||
{
|
||||
float t = travel;
|
||||
var allies = battle.AliveUnits(u.Faction);
|
||||
for (int pass = 0; pass < 4; pass++)
|
||||
{
|
||||
float before = t;
|
||||
foreach (var a in allies)
|
||||
{
|
||||
if (a == u) continue;
|
||||
Vector2 rel = a.Position - origin;
|
||||
float along = Vector2.Dot(rel, dir);
|
||||
float lat = Mathf.Abs(rel.x * dir.y - rel.y * dir.x);
|
||||
float sum = u.Radius + a.Radius;
|
||||
if (lat >= sum || along + sum <= 0f) continue; // 경로와 겹치지 않음 · 뒤쪽
|
||||
float half = Mathf.Sqrt(sum * sum - lat * lat);
|
||||
float touch = along - half;
|
||||
float clear = along + half;
|
||||
if (t >= clear || t <= touch) continue; // 완전히 지나침 · 닿기 전에 멈춤
|
||||
t = Mathf.Min(t, Mathf.Max(0f, touch - Combat.Epsilon));
|
||||
}
|
||||
if (Mathf.Approximately(before, t)) break;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>종착점이 적과 겹치면 경로를 되짚어 겹치지 않는 지점까지 물러선다.</summary>
|
||||
Vector2 BackOffFromEnemies(Unit u, Vector2 origin, Vector2 dir, float travel)
|
||||
{
|
||||
|
|
@ -251,27 +371,25 @@ namespace TKS
|
|||
|
||||
IEnumerator ArcherRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||
{
|
||||
// 조준 방향으로 슬링샷 거리 + 사거리 안의 첫 적. hitDist = 그 적 가장자리에 닿기까지의 이동 거리.
|
||||
// 조준 방향으로 사거리 + 접근 허용 거리 안의 첫 적. hitDist = 그 적 가장자리에 닿기까지의 이동 거리(= 가장자리 간격).
|
||||
float hitDist = 0f;
|
||||
Unit first = moveOnly ? null : Combat.FirstEnemyOnPath(u, origin, dir, dist + u.AttackRange, out hitDist);
|
||||
Unit first = moveOnly ? null : Combat.FirstEnemyOnPath(u, origin, dir, u.AttackRange + u.ArcherMoveAllow, out hitDist);
|
||||
if (first == null)
|
||||
{
|
||||
// 적이 없으면 목표점까지 이동(PD 2026-11-04 2차: 「슬링샷 범위 내 적이 없을 경우 해당 위치까지 이동」)
|
||||
float free = Combat.FreeTravel(u, origin, dir, dist, true);
|
||||
yield return Move(u, origin + dir * free, o);
|
||||
o.MovedOnly = true;
|
||||
// 공격 가능 범위에 적이 없으면 목표점까지 이동 (PD 2026-11-04 2차 지시)
|
||||
yield return MoveFree(u, origin, dir, dist, o);
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 적 가장자리까지 남은 간격이 사거리 안에 들어올 때까지만 이동(슬링샷 거리 상한)
|
||||
float need = Mathf.Clamp(hitDist - u.AttackRange, 0f, dist);
|
||||
float freeMove = Combat.FreeTravel(u, origin, dir, need, true);
|
||||
// 사거리 안이면 제자리 · 밖이면 가장자리가 사거리에 들어올 만큼만 다가간다(접근 허용 거리 상한)
|
||||
float need = Mathf.Clamp(hitDist - u.AttackRange, 0f, u.ArcherMoveAllow);
|
||||
float freeMove = need > 0.01f ? Combat.FreeTravel(u, origin, dir, need, true) : 0f;
|
||||
if (freeMove > 0.01f) yield return Move(u, origin + dir * freeMove, o);
|
||||
|
||||
if (hitDist - freeMove <= u.AttackRange + 0.05f)
|
||||
{
|
||||
bool crit = Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance) == first;
|
||||
yield return Attack(u, first, true, crit, o);
|
||||
yield return Attack(u, first, true, crit, o, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -281,6 +399,13 @@ namespace TKS
|
|||
|
||||
// ---------- 공통 ----------
|
||||
|
||||
IEnumerator MoveFree(Unit u, Vector2 origin, Vector2 dir, float dist, ActionOutcome o)
|
||||
{
|
||||
float free = Combat.FreeTravel(u, origin, dir, dist, true, true);
|
||||
yield return Move(u, origin + dir * free, o);
|
||||
o.MovedOnly = true;
|
||||
}
|
||||
|
||||
IEnumerator Move(Unit u, Vector2 to, ActionOutcome o)
|
||||
{
|
||||
yield return Fx.MoveAlong(u.transform, u.Position, to, cfg.moveSpeed, null);
|
||||
|
|
@ -288,32 +413,67 @@ namespace TKS
|
|||
o.Path.Add(to);
|
||||
}
|
||||
|
||||
IEnumerator Attack(Unit a, Unit d, bool ranged, bool crit, ActionOutcome o)
|
||||
/// <summary>부딪힌 유닛을 n 방향으로 force 만큼 밀어낸다(다른 유닛·벽에 닿으면 멈춤). 밀어 주는 유닛(pusher)은 막는 것으로 보지 않는다.</summary>
|
||||
IEnumerator Push(Unit target, Vector2 n, float force, Unit pusher, ActionOutcome o)
|
||||
{
|
||||
if (target == null || !target.IsAlive || force < 0.01f) yield break;
|
||||
float slide = Combat.FreeTravel(target, target.Position, n, force, true, true, pusher);
|
||||
if (slide > 0.01f)
|
||||
{
|
||||
Vector2 end = battle.Board.ClampInside(target.Position + n * slide, target.Radius);
|
||||
yield return Fx.MoveAlong(target.transform, target.Position, end, cfg.moveSpeed, null);
|
||||
target.Position = end;
|
||||
}
|
||||
o.Pushed.Add(target);
|
||||
battle.NotifyChanged();
|
||||
}
|
||||
|
||||
/// <summary>규칙 ⑤: 밀린 아군이 상대 유닛과 닿아 있으면 그 자리에서 공격 1회(근접 · 자기 행동 소모 없음).</summary>
|
||||
IEnumerator PushedAllyAttack(Unit ally, ActionOutcome o)
|
||||
{
|
||||
if (ally == null || !ally.IsAlive) yield break;
|
||||
Unit foe = Combat.TouchingOpponent(ally);
|
||||
if (foe == null) yield break;
|
||||
yield return Fx.Wait(0.1f);
|
||||
yield return Attack(ally, foe, false, false, o, false);
|
||||
}
|
||||
|
||||
/// <summary>공격 1회. byActor = 행동 유닛의 공격(결과 요약에 집계) · false = 밀린 아군의 공격(Notes 에 기록).</summary>
|
||||
IEnumerator Attack(Unit a, Unit d, bool ranged, bool crit, ActionOutcome o, bool byActor)
|
||||
{
|
||||
if (a.View != null) a.View.PlayAttack();
|
||||
if (ranged) yield return Fx.Arrow(a.Position, d.Position);
|
||||
else yield return Fx.Lunge(a, d.Position);
|
||||
|
||||
int dmg = Combat.Damage(cfg, a, d, crit, rng);
|
||||
int dmg = Combat.Damage(cfg, a, d, crit, rng, !ranged);
|
||||
d.TakeDamage(dmg);
|
||||
o.Attacked.Add(d);
|
||||
o.DamageDealt += dmg;
|
||||
o.Crit |= crit;
|
||||
if (byActor)
|
||||
{
|
||||
o.Attacked.Add(d);
|
||||
o.DamageDealt += dmg;
|
||||
o.Crit |= crit;
|
||||
}
|
||||
Fx.Popup(d.Position, crit ? dmg + "!" : dmg.ToString(), crit ? new Color(1f, 0.7f, 0.2f) : Color.white);
|
||||
battle.NotifyChanged();
|
||||
yield return Fx.Wait(0.2f);
|
||||
|
||||
int counter = 0;
|
||||
if (!ranged && d.IsAlive && a.IsAlive)
|
||||
{
|
||||
int c = Combat.CounterDamage(cfg, d, a, rng);
|
||||
counter = Combat.CounterDamage(cfg, d, a, rng);
|
||||
if (d.View != null) d.View.PlayAttack();
|
||||
yield return Fx.Lunge(d, a.Position);
|
||||
a.TakeDamage(c);
|
||||
o.CounterTaken += c;
|
||||
Fx.Popup(a.Position, c.ToString(), new Color(1f, 0.45f, 0.45f));
|
||||
a.TakeDamage(counter);
|
||||
if (byActor) o.CounterTaken += counter;
|
||||
Fx.Popup(a.Position, counter.ToString(), new Color(1f, 0.45f, 0.45f));
|
||||
battle.NotifyChanged();
|
||||
yield return Fx.Wait(0.2f);
|
||||
}
|
||||
|
||||
if (!byActor)
|
||||
{
|
||||
o.Notes.Add("밀린 " + a.DisplayName + " → " + d.DisplayName + " 공격 " + dmg + (counter > 0 ? " · 반격 " + counter : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace TKS
|
|||
public int def;
|
||||
[Tooltip("슬링샷으로 지정할 수 있는 최대 거리(월드 단위)")]
|
||||
public float slingMaxDistance;
|
||||
[Tooltip("창병 대기(견제) 반경 · 궁병은 쓰지 않음(사거리 = slingMaxDistance × archerRangeRatio)")]
|
||||
[Tooltip("창병 대기(견제) 반경 · 궁병은 쓰지 않음(사거리 = slingMaxDistance)")]
|
||||
public float attackRange;
|
||||
public float radius;
|
||||
}
|
||||
|
|
@ -81,13 +81,23 @@ namespace TKS
|
|||
[Tooltip("기병 관통 시 적을 옆으로 밀어내는 거리")]
|
||||
public float cavalryKnockback = 0.35f;
|
||||
|
||||
[Header("궁병 규칙 (PD 2026-11-04)")]
|
||||
[Tooltip("궁병 사거리 = 슬링샷 최대 거리 × 비율. 조준 방향의 슬링샷 거리 + 사거리 안에 적이 있을 때만 행동 가능(없으면 놓아도 취소) · 적 가장자리가 사거리에 들어올 때까지만 이동 후 사격")]
|
||||
[Header("궁병 규칙 (PD 2026-11-04 · #1080)")]
|
||||
[Tooltip("궁병 사거리 = 슬링샷 최대 거리(얇은 원 · 안의 적은 제자리 사격). 그 밖 ~ 사거리 × (1 + 비율) 안(붉은 도넛)의 적은 사거리에 들어올 만큼만 다가가 사격 · 더 멀면 목표점까지 이동")]
|
||||
public float archerRangeRatio = 0.2f;
|
||||
[Tooltip("궁병이 근접 유닛(보병·기병·창병)과 근접 전투(돌진·반격·관통·견제·밀려 닿은 공격)할 때 주는 피해 배율 · 원거리 사격은 1")]
|
||||
public float archerMeleeDealtRatio = 0.5f;
|
||||
[Tooltip("궁병이 근접 유닛에게 근접으로 맞을 때 받는 피해 배율(상성 배율과 곱)")]
|
||||
public float archerMeleeTakenRatio = 1.5f;
|
||||
|
||||
[Header("충돌 (PD 2026-11-04 · #1080)")]
|
||||
[Tooltip("보병 알까기: 부딪힌 유닛(아군·적군)이 밀려나는 거리 = 충돌 시 남은 슬링샷 거리 × 비율 · 창병은 비율 없이 남은 거리만큼 밀며 함께 전진")]
|
||||
public float collisionPushRatio = 1f;
|
||||
|
||||
[Header("표시")]
|
||||
[Tooltip("유닛 아트(스프라이트) 세로 크기 · 월드 단위 · 장수는 반지름 배율만큼 커진다")]
|
||||
public float unitArtHeight = 1.5f;
|
||||
[Tooltip("Resources/Board/<이름> 전장 배경 스프라이트(원화 세션 납품 · 5:8) · 비면 도형·격자 바닥")]
|
||||
public string boardArt = "battlefield_01";
|
||||
|
||||
[Header("조작")]
|
||||
[Tooltip("이보다 짧게 당기면 취소")]
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ namespace TKS
|
|||
string line1 = side + " · " + u.DisplayName + " · " + UnitTypeInfo.Korean(u.Type) + (u.IsBoss ? " (장수)" : "");
|
||||
string line2 = "HP " + u.Hp + "/" + u.MaxHp + " 공 " + u.Atk + " 방 " + u.Def;
|
||||
string line3 = "슬링 " + u.SlingMaxDistance.ToString("0.#");
|
||||
if (u.Type == UnitType.Archer) line3 += " 사거리 " + u.AttackRange.ToString("0.#") + " (슬링 +" + Mathf.RoundToInt(battle.Config.archerRangeRatio * 100f) + " %)";
|
||||
if (u.Type == UnitType.Archer) line3 += " 사거리 " + u.AttackRange.ToString("0.#") + " (접근 +" + u.ArcherMoveAllow.ToString("0.#") + ")";
|
||||
if (u.Type == UnitType.Spear) line3 += " 견제 반경 " + u.AttackRange.ToString("0.#");
|
||||
string state = "";
|
||||
if (u.Faction == Faction.Player)
|
||||
|
|
|
|||
|
|
@ -184,11 +184,11 @@ namespace TKS
|
|||
return Phase == BattlePhase.PlayerTurn && u != null && u.Faction == Faction.Player && u.IsAlive && !u.ActedThisTurn;
|
||||
}
|
||||
|
||||
/// <summary>궁병 공격 가능 범위(슬링샷 최대 + 사거리 · 가장자리 간격 기준) 안에 상대 유닛이 하나라도 있는가 — 도넛 색 표시용.</summary>
|
||||
/// <summary>궁병 공격 가능 범위(사거리 + 접근 허용 거리 · 가장자리 간격 기준) 안에 상대 유닛이 하나라도 있는가 — 도넛 색 표시용.</summary>
|
||||
public bool AnyEnemyWithinArcherReach(Unit u)
|
||||
{
|
||||
if (u == null) return false;
|
||||
float reach = u.SlingMaxDistance + u.AttackRange;
|
||||
float reach = u.AttackRange + u.ArcherMoveAllow;
|
||||
Faction other = u.Faction == Faction.Player ? Faction.Enemy : Faction.Player;
|
||||
foreach (var e in AliveUnits(other))
|
||||
{
|
||||
|
|
@ -383,18 +383,32 @@ namespace TKS
|
|||
static string Describe(Unit u, ActionOutcome o, bool moveOnly)
|
||||
{
|
||||
string who = u.DisplayName + "(" + UnitTypeInfo.Korean(u.Type) + ")";
|
||||
string s;
|
||||
if (o.Attacked.Count == 0)
|
||||
{
|
||||
return who + (moveOnly ? " 추가 이동" : " 이동");
|
||||
s = who + (moveOnly ? " 추가 이동" : " 이동");
|
||||
}
|
||||
var names = new List<string>();
|
||||
foreach (var a in o.Attacked)
|
||||
else
|
||||
{
|
||||
if (!names.Contains(a.DisplayName)) names.Add(a.DisplayName);
|
||||
var names = new List<string>();
|
||||
foreach (var a in o.Attacked)
|
||||
{
|
||||
if (!names.Contains(a.DisplayName)) names.Add(a.DisplayName);
|
||||
}
|
||||
s = who + " → " + string.Join(", ", names.ToArray()) + " 공격 " + o.DamageDealt;
|
||||
if (o.Crit) s += " 크리티컬!";
|
||||
if (o.CounterTaken > 0) s += " · 반격 " + o.CounterTaken;
|
||||
}
|
||||
string s = who + " → " + string.Join(", ", names.ToArray()) + " 공격 " + o.DamageDealt;
|
||||
if (o.Crit) s += " 크리티컬!";
|
||||
if (o.CounterTaken > 0) s += " · 반격 " + o.CounterTaken;
|
||||
if (o.Pushed.Count > 0)
|
||||
{
|
||||
var pushed = new List<string>();
|
||||
foreach (var p in o.Pushed)
|
||||
{
|
||||
if (!pushed.Contains(p.DisplayName)) pushed.Add(p.DisplayName);
|
||||
}
|
||||
s += " · " + string.Join(", ", pushed.ToArray()) + " 밀어냄";
|
||||
}
|
||||
foreach (var n in o.Notes) s += " · " + n;
|
||||
if (o.BonusMove) s += " · 추가 이동!";
|
||||
if (o.Returned) s += " · 제자리 복귀";
|
||||
if (o.Overwatch) s += " · 대기(견제) 발동";
|
||||
|
|
|
|||
|
|
@ -7,48 +7,61 @@ namespace TKS
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>전장(바닥·격자·테두리·4면 벽 콜라이더). 화면보다 넓어 카메라로 스크롤한다.</summary>
|
||||
/// <summary>전장(바닥 = 원화 배경 또는 도형·격자 · 테두리 · 4면 벽 콜라이더). 화면보다 넓어 카메라로 스크롤한다.</summary>
|
||||
public class Board : MonoBehaviour
|
||||
{
|
||||
public Vector2 Size { get; private set; }
|
||||
/// <summary>Resources/Board/<boardArt> 원화 배경을 썼는가(없으면 도형·격자).</summary>
|
||||
public bool HasArt { get; private set; }
|
||||
|
||||
public Rect Rect
|
||||
{
|
||||
get { return new Rect(-Size.x * 0.5f, -Size.y * 0.5f, Size.x, Size.y); }
|
||||
}
|
||||
|
||||
public void Build(Vector2 size)
|
||||
public void Build(Vector2 size, string boardArt = null)
|
||||
{
|
||||
Size = size;
|
||||
float w = size.x;
|
||||
float h = size.y;
|
||||
|
||||
var bg = ProceduralSprites.NewSprite(transform, "Background", ProceduralSprites.Square, new Color(0.17f, 0.24f, 0.17f), 1f, -20);
|
||||
bg.transform.localScale = new Vector3(w, h, 1f);
|
||||
|
||||
// 아군·적군 진영 구분용 옅은 띠
|
||||
var enemyHalf = ProceduralSprites.NewSprite(transform, "EnemyHalf", ProceduralSprites.Square, new Color(0.45f, 0.2f, 0.2f, 0.18f), 1f, -19);
|
||||
enemyHalf.transform.localScale = new Vector3(w, h * 0.5f, 1f);
|
||||
enemyHalf.transform.localPosition = new Vector3(0f, h * 0.25f, 0f);
|
||||
|
||||
// 격자(4 단위) — 스크롤 체감용
|
||||
var gridColor = new Color(1f, 1f, 1f, 0.07f);
|
||||
const float gridStep = 4f;
|
||||
for (float x = -w * 0.5f + gridStep; x < w * 0.5f - 0.01f; x += gridStep)
|
||||
Sprite art = string.IsNullOrEmpty(boardArt) ? null : Resources.Load<Sprite>("Board/" + boardArt);
|
||||
HasArt = art != null;
|
||||
if (HasArt)
|
||||
{
|
||||
var g = ProceduralSprites.NewSprite(transform, "GridV", ProceduralSprites.Square, gridColor, 1f, -18);
|
||||
g.transform.localPosition = new Vector3(x, 0f, 0f);
|
||||
g.transform.localScale = new Vector3(0.04f, h, 1f);
|
||||
// 원화 세션 납품 배경(5:8 · 위 = 적 진영) — 전장 크기에 맞춰 늘린다 · 격자·진영 띠는 그리지 않음
|
||||
var bg = ProceduralSprites.NewSprite(transform, "Background", art, Color.white, 1f, -20);
|
||||
bg.transform.localScale = new Vector3(w / art.bounds.size.x, h / art.bounds.size.y, 1f);
|
||||
}
|
||||
for (float y = -h * 0.5f + gridStep; y < h * 0.5f - 0.01f; y += gridStep)
|
||||
else
|
||||
{
|
||||
var g = ProceduralSprites.NewSprite(transform, "GridH", ProceduralSprites.Square, gridColor, 1f, -18);
|
||||
g.transform.localPosition = new Vector3(0f, y, 0f);
|
||||
g.transform.localScale = new Vector3(w, 0.04f, 1f);
|
||||
}
|
||||
var bg = ProceduralSprites.NewSprite(transform, "Background", ProceduralSprites.Square, new Color(0.17f, 0.24f, 0.17f), 1f, -20);
|
||||
bg.transform.localScale = new Vector3(w, h, 1f);
|
||||
|
||||
var mid = ProceduralSprites.NewSprite(transform, "MidLine", ProceduralSprites.Square, new Color(1f, 1f, 1f, 0.14f), 1f, -17);
|
||||
mid.transform.localScale = new Vector3(w, 0.06f, 1f);
|
||||
// 아군·적군 진영 구분용 옅은 띠
|
||||
var enemyHalf = ProceduralSprites.NewSprite(transform, "EnemyHalf", ProceduralSprites.Square, new Color(0.45f, 0.2f, 0.2f, 0.18f), 1f, -19);
|
||||
enemyHalf.transform.localScale = new Vector3(w, h * 0.5f, 1f);
|
||||
enemyHalf.transform.localPosition = new Vector3(0f, h * 0.25f, 0f);
|
||||
|
||||
// 격자(4 단위) — 스크롤 체감용
|
||||
var gridColor = new Color(1f, 1f, 1f, 0.07f);
|
||||
const float gridStep = 4f;
|
||||
for (float x = -w * 0.5f + gridStep; x < w * 0.5f - 0.01f; x += gridStep)
|
||||
{
|
||||
var g = ProceduralSprites.NewSprite(transform, "GridV", ProceduralSprites.Square, gridColor, 1f, -18);
|
||||
g.transform.localPosition = new Vector3(x, 0f, 0f);
|
||||
g.transform.localScale = new Vector3(0.04f, h, 1f);
|
||||
}
|
||||
for (float y = -h * 0.5f + gridStep; y < h * 0.5f - 0.01f; y += gridStep)
|
||||
{
|
||||
var g = ProceduralSprites.NewSprite(transform, "GridH", ProceduralSprites.Square, gridColor, 1f, -18);
|
||||
g.transform.localPosition = new Vector3(0f, y, 0f);
|
||||
g.transform.localScale = new Vector3(w, 0.04f, 1f);
|
||||
}
|
||||
|
||||
var mid = ProceduralSprites.NewSprite(transform, "MidLine", ProceduralSprites.Square, new Color(1f, 1f, 1f, 0.14f), 1f, -17);
|
||||
mid.transform.localScale = new Vector3(w, 0.06f, 1f);
|
||||
}
|
||||
|
||||
// 테두리
|
||||
var borderColor = new Color(0.85f, 0.75f, 0.45f);
|
||||
|
|
|
|||
|
|
@ -8,27 +8,39 @@ namespace TKS
|
|||
{
|
||||
/// <summary>경로 질의에 쓰는 원 반지름 배율(살짝 작게 캐스트해 스침을 관대하게).</summary>
|
||||
public const float CastRadiusScale = 0.9f;
|
||||
const float Epsilon = 0.02f;
|
||||
public const float Epsilon = 0.02f;
|
||||
/// <summary>두 유닛 가장자리 간격이 이 이하면 「닿아 있다」(밀린 아군의 공격 기회 판정).</summary>
|
||||
public const float ContactTolerance = 0.12f;
|
||||
|
||||
// ---------- 피해 공식 ----------
|
||||
|
||||
public static int Damage(BalanceConfig cfg, Unit attacker, Unit defender, bool crit, System.Random rng)
|
||||
/// <summary>
|
||||
/// 피해 = (공격 × 상성 × 크리 − 방어 × defenseFactor) × 궁병 근접 배율 × 난수.
|
||||
/// melee = 근접 전투(돌진·반격·관통·견제·밀려 닿은 공격). 궁병이 근접 유닛(보병·기병·창병)과 근접 전투하면
|
||||
/// 주는 피해 × archerMeleeDealtRatio · 받는 피해 × archerMeleeTakenRatio (PD 2026-11-04 #1080) · 원거리 사격은 패널티 없음.
|
||||
/// </summary>
|
||||
public static int Damage(BalanceConfig cfg, Unit attacker, Unit defender, bool crit, System.Random rng, bool melee)
|
||||
{
|
||||
float mult = cfg.Matchup(attacker.Type, defender.Type);
|
||||
float raw = attacker.Atk * mult * (crit ? cfg.critMultiplier : 1f) - defender.Def * cfg.defenseFactor;
|
||||
if (melee)
|
||||
{
|
||||
if (attacker.Type == UnitType.Archer && UnitTypeInfo.IsMelee(defender.Type)) raw *= cfg.archerMeleeDealtRatio;
|
||||
if (defender.Type == UnitType.Archer && UnitTypeInfo.IsMelee(attacker.Type)) raw *= cfg.archerMeleeTakenRatio;
|
||||
}
|
||||
float variance = 1f + ((float)rng.NextDouble() * 2f - 1f) * cfg.damageVariance;
|
||||
return Mathf.Max(1, Mathf.RoundToInt(raw * variance));
|
||||
}
|
||||
|
||||
public static int CounterDamage(BalanceConfig cfg, Unit defender, Unit attacker, System.Random rng)
|
||||
{
|
||||
int baseDmg = Damage(cfg, defender, attacker, false, rng);
|
||||
int baseDmg = Damage(cfg, defender, attacker, false, rng, true);
|
||||
return Mathf.Max(1, Mathf.RoundToInt(baseDmg * cfg.counterRatio));
|
||||
}
|
||||
|
||||
public static int OverwatchDamage(BalanceConfig cfg, Unit spear, Unit mover, System.Random rng)
|
||||
{
|
||||
int baseDmg = Damage(cfg, spear, mover, false, rng);
|
||||
int baseDmg = Damage(cfg, spear, mover, false, rng, true);
|
||||
return Mathf.Max(1, Mathf.RoundToInt(baseDmg * cfg.overwatchDamageRatio));
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +73,40 @@ namespace TKS
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>경로(원 캐스트) 위 가장 가까운 유닛(아군·적군 불문). hitDist = 닿기까지 이동 거리. 벽은 무시한다(호출 쪽에서 FreeTravel 과 비교).</summary>
|
||||
public static Unit FirstUnitOnPath(Unit actor, Vector2 origin, Vector2 dir, float dist, out float hitDist)
|
||||
{
|
||||
hitDist = 0f;
|
||||
foreach (var h in Cast(actor, origin, dir, dist))
|
||||
{
|
||||
var u = UnitOf(h.collider);
|
||||
if (u == null || u == actor || !u.IsAlive) continue;
|
||||
hitDist = h.distance;
|
||||
return u;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>u 와 가장자리가 닿아 있는(ContactTolerance 이내) 가장 가까운 상대 진영 유닛.</summary>
|
||||
public static Unit TouchingOpponent(Unit u)
|
||||
{
|
||||
Physics2D.SyncTransforms();
|
||||
Unit best = null;
|
||||
float bestGap = float.MaxValue;
|
||||
foreach (var c in Physics2D.OverlapCircleAll(u.Position, u.Radius + ContactTolerance + 0.5f))
|
||||
{
|
||||
var o = UnitOf(c);
|
||||
if (o == null || o == u || !o.IsAlive || o.Faction == u.Faction) continue;
|
||||
float gap = Vector2.Distance(u.Position, o.Position) - u.Radius - o.Radius;
|
||||
if (gap <= ContactTolerance && gap < bestGap)
|
||||
{
|
||||
bestGap = gap;
|
||||
best = o;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>경로 위의 모든 적(가까운 순)과 각 접촉 거리.</summary>
|
||||
public static List<Unit> EnemiesOnPath(Unit actor, Vector2 origin, Vector2 dir, float dist, List<float> distances)
|
||||
{
|
||||
|
|
@ -77,8 +123,8 @@ namespace TKS
|
|||
return list;
|
||||
}
|
||||
|
||||
/// <summary>벽·(옵션) 유닛에 막히기 전까지 실제로 나아갈 수 있는 거리.</summary>
|
||||
public static float FreeTravel(Unit actor, Vector2 origin, Vector2 dir, float dist, bool enemiesBlock, bool friendsBlock = true)
|
||||
/// <summary>벽·(옵션) 유닛에 막히기 전까지 실제로 나아갈 수 있는 거리. ignore = 밀어 주는 유닛처럼 출발 지점에서 닿아 있어도 무시할 유닛.</summary>
|
||||
public static float FreeTravel(Unit actor, Vector2 origin, Vector2 dir, float dist, bool enemiesBlock, bool friendsBlock = true, Unit ignore = null)
|
||||
{
|
||||
float min = dist;
|
||||
foreach (var h in Cast(actor, origin, dir, dist))
|
||||
|
|
@ -89,7 +135,7 @@ namespace TKS
|
|||
continue;
|
||||
}
|
||||
var u = UnitOf(h.collider);
|
||||
if (u == null || u == actor || !u.IsAlive) continue;
|
||||
if (u == null || u == actor || u == ignore || !u.IsAlive) continue;
|
||||
bool enemy = u.Faction != actor.Faction;
|
||||
if (enemy && !enemiesBlock) continue;
|
||||
if (!enemy && !friendsBlock) continue;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace TKS
|
|||
cam.transparencySortAxis = Vector3.up;
|
||||
|
||||
var board = new GameObject("Board").AddComponent<Board>();
|
||||
board.Build(Config.boardSize);
|
||||
board.Build(Config.boardSize, Config.boardArt);
|
||||
|
||||
CameraCtrl = cam.GetComponent<CameraController>();
|
||||
if (CameraCtrl == null) CameraCtrl = cam.gameObject.AddComponent<CameraController>();
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ namespace TKS
|
|||
public int Def;
|
||||
public float Radius;
|
||||
public float SlingMaxDistance;
|
||||
/// <summary>궁병 = 슬링샷 최대 거리 × archerRangeRatio (적 가장자리까지의 간격 기준) · 창병 = 대기(견제) 반경.</summary>
|
||||
/// <summary>궁병 = 사거리 = 슬링샷 최대 거리(적 가장자리까지의 간격 기준 · 제자리 사격) · 창병 = 대기(견제) 반경.</summary>
|
||||
public float AttackRange;
|
||||
/// <summary>궁병이 사거리 밖(~ 사거리 × (1 + archerRangeRatio))의 적에게 다가갈 수 있는 최대 거리. 다른 병종 = 0.</summary>
|
||||
public float ArcherMoveAllow;
|
||||
/// <summary>Resources/Units/<ArtId>/ 아트 폴더 · 비면 도형.</summary>
|
||||
public string ArtId = "";
|
||||
/// <summary>아트 세로 크기(월드) = unitArtHeight × 반지름 배율.</summary>
|
||||
|
|
@ -61,8 +63,9 @@ namespace TKS
|
|||
Radius = s.radius * radiusMult;
|
||||
ArtHeight = cfg.unitArtHeight * radiusMult;
|
||||
SlingMaxDistance = s.slingMaxDistance;
|
||||
// PD 2026-11-04: 궁병 사거리 = 슬링샷 범위의 비율(+20 %) · 창병은 자산의 대기 반경
|
||||
AttackRange = type == UnitType.Archer ? s.slingMaxDistance * cfg.archerRangeRatio : s.attackRange;
|
||||
// PD 2026-11-04 #1080: 궁병 사거리 = 슬링샷 범위(제자리 사격) · 그 밖 +비율 안의 적은 들어올 만큼만 다가가 사격 · 창병은 자산의 대기 반경
|
||||
AttackRange = type == UnitType.Archer ? s.slingMaxDistance : s.attackRange;
|
||||
ArcherMoveAllow = type == UnitType.Archer ? s.slingMaxDistance * cfg.archerRangeRatio : 0f;
|
||||
gameObject.name = (faction == Faction.Player ? "P_" : "E_") + displayName;
|
||||
|
||||
Collider = GetComponent<CircleCollider2D>();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using UnityEngine.TestTools;
|
|||
|
||||
namespace TKS.Tests
|
||||
{
|
||||
/// <summary>병종 4종 규칙·턴 흐름·승리 판정을 실제 씬에서 검증한다(헤드리스 실행 가능).</summary>
|
||||
/// <summary>병종 4종 규칙(PD 2026-11-04 #1080 충돌 규칙 포함)·턴 흐름·승리 판정을 실제 씬에서 검증한다(헤드리스 실행 가능).</summary>
|
||||
public class BattleFlowTests
|
||||
{
|
||||
BattleManager bm;
|
||||
|
|
@ -40,6 +40,17 @@ namespace TKS.Tests
|
|||
yield return null; // 파괴된 콜라이더가 물리 월드에서 빠질 시간
|
||||
}
|
||||
|
||||
/// <summary>경로 질의가 쓰는 접촉 거리(행동 유닛 캐스트 반지름 + 상대 반지름).</summary>
|
||||
static float CastContact(Unit mover, Unit other)
|
||||
{
|
||||
return mover.Radius * Combat.CastRadiusScale + other.Radius;
|
||||
}
|
||||
|
||||
static float Gap(Unit a, Unit b)
|
||||
{
|
||||
return Vector2.Distance(a.Position, b.Position) - a.Radius - b.Radius;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Bootstrap_SpawnsHeroesAndStageEnemies()
|
||||
{
|
||||
|
|
@ -64,19 +75,24 @@ namespace TKS.Tests
|
|||
Assert.Greater(c.Matchup(UnitType.Spear, UnitType.Archer), 1f);
|
||||
}
|
||||
|
||||
// ---------- 보병 ----------
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Infantry_AttacksFirstEnemyOnPath_BonusMoveOnFinalHit()
|
||||
public IEnumerator Infantry_HitsEnemy_StopsAtContact_PushesIt_BonusMoveOnFinalHit()
|
||||
{
|
||||
yield return Fresh();
|
||||
var inf = bm.SpawnUnit(UnitType.Infantry, Faction.Player, new Vector2(0f, -3f), "보병");
|
||||
var foe = bm.SpawnUnit(UnitType.Spear, Faction.Enemy, new Vector2(0f, 0f), "적창병");
|
||||
float contactY = -3f + (3f - CastContact(inf, foe));
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(inf, foe.Position, false, o);
|
||||
|
||||
Assert.Contains(foe, o.Attacked);
|
||||
Assert.Less(foe.Hp, foe.MaxHp, "경로 위 첫 적을 공격");
|
||||
Assert.Less(foe.Hp, foe.MaxHp, "부딪힌 적을 공격");
|
||||
Assert.IsTrue(o.BonusMove, "최종 목표점 명중 → 추가 이동");
|
||||
Assert.Less(Vector2.Distance(inf.Position, foe.Position), inf.Radius + foe.Radius + 0.2f, "적에 닿는 지점까지 이동");
|
||||
Assert.AreEqual(contactY, inf.Position.y, 0.1f, "보병은 부딪힌 지점에서 멈춘다(알까기)");
|
||||
Assert.Contains(foe, o.Pushed, "부딪힌 유닛은 밀려난다");
|
||||
Assert.Greater(foe.Position.y, 0.3f, "적이 남은 힘만큼 밀려남");
|
||||
Assert.Less(inf.Hp, inf.MaxHp, "근접 공격은 반격 피해를 받는다");
|
||||
|
||||
int foeHp = foe.Hp;
|
||||
|
|
@ -99,6 +115,39 @@ namespace TKS.Tests
|
|||
Assert.IsFalse(o.BonusMove);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Infantry_PushesAlly_AllyAttacksTouchingEnemy_OncePerPush()
|
||||
{
|
||||
yield return Fresh();
|
||||
// A(보병) 가 B(아군 창병)를 +x 로 밀어 적에게 붙이면 B 가 공격 · C 가 B 를 다시 밀면 B 가 또 공격 (규칙 ⑤)
|
||||
var a = bm.SpawnUnit(UnitType.Infantry, Faction.Player, new Vector2(-3f, -2f), "보병A");
|
||||
var b = bm.SpawnUnit(UnitType.Spear, Faction.Player, new Vector2(0f, -2f), "창병B");
|
||||
float pushX = CastContact(a, b); // A 는 3 − 접촉거리 만큼 가서 닿으므로 남는 힘(= 밀리는 거리) = 접촉거리
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(pushX, -2f + b.Radius + 0.45f + 0.05f), "적보병");
|
||||
Assert.AreEqual(b.MaxHp, b.Hp);
|
||||
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(a, new Vector2(0f, -2f), false, o);
|
||||
Assert.AreEqual(0, o.Attacked.Count, "아군에 부딪힌 보병 자신은 공격하지 않음");
|
||||
Assert.Contains(b, o.Pushed);
|
||||
Assert.AreEqual(pushX, b.Position.x, 0.1f, "B 가 남은 힘만큼 밀려남");
|
||||
Assert.LessOrEqual(Gap(b, foe), Combat.ContactTolerance + 0.05f, "밀려서 적과 닿음");
|
||||
Assert.Less(foe.Hp, foe.MaxHp, "밀린 아군 B 가 적을 공격");
|
||||
Assert.AreEqual(a.MaxHp, a.Hp, "A 는 피해 없음");
|
||||
Assert.IsFalse(b.ActedThisTurn, "B 의 공격은 자기 행동을 소모하지 않음");
|
||||
Assert.AreEqual(1, o.Notes.Count);
|
||||
|
||||
int hp = foe.Hp;
|
||||
var c = bm.SpawnUnit(UnitType.Infantry, Faction.Player, new Vector2(pushX, -5f), "보병C");
|
||||
var o2 = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(c, new Vector2(pushX, -2.2f), false, o2);
|
||||
Assert.Contains(b, o2.Pushed, "C 가 B 를 다시 밀었다");
|
||||
Assert.Less(foe.Hp, hp, "B 가 C 의 기회로 또 공격");
|
||||
Assert.AreEqual(0, o2.Attacked.Count);
|
||||
}
|
||||
|
||||
// ---------- 기병 ----------
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Cavalry_PiercesAllOnPath_AndReturnsOnFinalHit()
|
||||
{
|
||||
|
|
@ -129,41 +178,82 @@ namespace TKS.Tests
|
|||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Archer_RangeIsSlingTimesRatio_MovesIntoRange_ShootsWithoutCounter()
|
||||
public IEnumerator Cavalry_PassesThroughAlly_OrStopsJustBeforeIt()
|
||||
{
|
||||
yield return Fresh();
|
||||
var cav = bm.SpawnUnit(UnitType.Cavalry, Faction.Player, new Vector2(0f, -4f), "기병");
|
||||
var ally = bm.SpawnUnit(UnitType.Infantry, Faction.Player, new Vector2(0f, -1.5f), "아군보병");
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(cav, new Vector2(0f, 2f), false, o);
|
||||
Assert.AreEqual(2f, cav.Position.y, 0.1f, "아군을 관통해 목표점까지");
|
||||
Assert.AreEqual(-1.5f, ally.Position.y, 0.01f, "아군은 밀리지 않음");
|
||||
Assert.AreEqual(0, o.Pushed.Count);
|
||||
|
||||
var cav2 = bm.SpawnUnit(UnitType.Cavalry, Faction.Player, new Vector2(4f, -4f), "기병2");
|
||||
var ally2 = bm.SpawnUnit(UnitType.Infantry, Faction.Player, new Vector2(4f, -1.5f), "아군2");
|
||||
float sum = cav2.Radius + ally2.Radius;
|
||||
var o2 = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(cav2, new Vector2(4f, -1.5f + sum * 0.5f), false, o2); // 지나칠 거리가 안 나옴
|
||||
Assert.AreEqual(-1.5f - sum, cav2.Position.y, 0.1f, "통과할 거리가 안 나오면 아군 직전에 멈춤");
|
||||
Assert.GreaterOrEqual(Vector2.Distance(cav2.Position, ally2.Position), sum - 0.05f, "아군과 겹치지 않음");
|
||||
Assert.AreEqual(-1.5f, ally2.Position.y, 0.01f);
|
||||
}
|
||||
|
||||
// ---------- 궁병 ----------
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Archer_ShootsInPlace_WhenEnemyWithinSlingRange()
|
||||
{
|
||||
yield return Fresh();
|
||||
var arc = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(0f, -5f), "궁병");
|
||||
Assert.AreEqual(arc.SlingMaxDistance * cfg.archerRangeRatio, arc.AttackRange, 0.001f, "궁병 사거리 = 슬링샷 최대 × 비율 (PD 2026-11-04 · +20 %)");
|
||||
// 적 가장자리까지 4.8 − 반지름 합 ≈ 4.0 ≤ 슬링샷 4.0 + 사거리 0.8 → 행동 가능 · 목표점 (0,−1) 은 적 원 밖(크리 아님)
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -0.2f), "적보병");
|
||||
Assert.AreEqual(arc.SlingMaxDistance, arc.AttackRange, 0.001f, "궁병 사거리 = 슬링샷 범위 (PD #1080)");
|
||||
Assert.AreEqual(arc.SlingMaxDistance * cfg.archerRangeRatio, arc.ArcherMoveAllow, 0.001f, "접근 허용 = 슬링샷 × 비율");
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -1.5f), "적보병");
|
||||
Assert.Less(3.5f - CastContact(arc, foe), arc.AttackRange, "전제: 적 가장자리가 사거리 안");
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(arc, new Vector2(0f, -1f), false, o);
|
||||
Assert.Contains(foe, o.Attacked, "조준 방향의 적을 사거리까지 이동 후 사격");
|
||||
yield return bm.ResolveForTest(arc, new Vector2(0f, -2.5f), false, o);
|
||||
Assert.Contains(foe, o.Attacked, "사거리 안의 적을 사격");
|
||||
Assert.Less(foe.Hp, foe.MaxHp);
|
||||
Assert.AreEqual(arc.MaxHp, arc.Hp, "궁병은 반격을 받지 않는다");
|
||||
float gap = Vector2.Distance(arc.Position, foe.Position) - arc.Radius * Combat.CastRadiusScale - foe.Radius;
|
||||
Assert.LessOrEqual(gap, arc.AttackRange + 0.1f, "적 가장자리가 사거리에 들어올 때까지만 이동");
|
||||
Assert.Greater(arc.Position.y, -5f + 2.5f, "사거리에 들어오기 위해 실제로 이동");
|
||||
Assert.Less(Vector2.Distance(arc.Position, new Vector2(0f, -5f)), 0.01f, "움직이지 않고 제자리에서");
|
||||
Assert.AreEqual(arc.MaxHp, arc.Hp, "원거리 사격은 반격을 받지 않는다");
|
||||
Assert.IsFalse(o.Crit);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Archer_MovesToTarget_WhenNoEnemyWithinSlingPlusRatio()
|
||||
public IEnumerator Archer_StepsInOnlyAsNeeded_WhenEnemyInDonut()
|
||||
{
|
||||
yield return Fresh();
|
||||
var arc = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(0f, -5f), "궁병");
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, 0.3f), "적보병");
|
||||
float hitDist = 5.3f - CastContact(arc, foe);
|
||||
Assert.Greater(hitDist, arc.AttackRange, "전제: 사거리 밖");
|
||||
Assert.LessOrEqual(hitDist, arc.AttackRange + arc.ArcherMoveAllow, "전제: 공격 가능 범위(도넛) 안");
|
||||
float need = hitDist - arc.AttackRange;
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(arc, new Vector2(0f, -1f), false, o);
|
||||
Assert.Contains(foe, o.Attacked, "사거리에 들어올 만큼만 다가가 사격");
|
||||
Assert.AreEqual(-5f + need, arc.Position.y, 0.1f, "필요한 만큼만 이동");
|
||||
Assert.AreEqual(arc.MaxHp, arc.Hp);
|
||||
Assert.IsFalse(o.Crit);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Archer_MovesToTarget_WhenNoEnemyWithinReach()
|
||||
{
|
||||
yield return Fresh();
|
||||
var arc = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(0f, -5f), "궁병");
|
||||
// 적 가장자리까지 6.0 − 반지름 합 ≈ 5.2 > 슬링샷 + 사거리 → 공격 없이 목표점까지 이동(PD 2026-11-04 2차 지시)
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, 1f), "적보병");
|
||||
Assert.Greater(6f - CastContact(arc, foe), arc.AttackRange + arc.ArcherMoveAllow, "전제: 공격 가능 범위 밖");
|
||||
Assert.IsFalse(bm.AnyEnemyWithinArcherReach(arc), "공격 가능 범위 안에 적 없음(도넛 회색)");
|
||||
var o = new ActionOutcome();
|
||||
var target = new Vector2(0f, -1f);
|
||||
yield return bm.ResolveForTest(arc, target, false, o);
|
||||
Assert.IsTrue(o.MovedOnly, "슬링샷 범위 + 비율 안에 적이 없으면 궁병은 목표점까지 이동만 한다");
|
||||
Assert.IsTrue(o.MovedOnly, "공격 가능 범위에 적이 없으면 목표점까지 이동만 (PD 2026-11-04 2차 지시)");
|
||||
Assert.AreEqual(0, o.Attacked.Count);
|
||||
Assert.AreEqual(foe.MaxHp, foe.Hp);
|
||||
Assert.Less(Vector2.Distance(arc.Position, target), 0.05f, "목표 지점까지 이동");
|
||||
|
||||
var near = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, -0.4f), "가까운적");
|
||||
var near = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(2f, -1f), "가까운적");
|
||||
Assert.IsTrue(bm.AnyEnemyWithinArcherReach(arc), "범위 안에 적이 생기면 도넛 붉게");
|
||||
Assert.IsTrue(near.IsAlive);
|
||||
}
|
||||
|
|
@ -182,13 +272,42 @@ namespace TKS.Tests
|
|||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Spear_OverwatchOnFinalHit_DamagesMovingEnemy()
|
||||
public IEnumerator Archer_MeleePenalty_DealsRatio_TakesRatio_RangedUnaffected()
|
||||
{
|
||||
yield return Fresh();
|
||||
var arc = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(0f, -5f), "궁병");
|
||||
var arc2 = bm.SpawnUnit(UnitType.Archer, Faction.Player, new Vector2(2f, -5f), "궁병2");
|
||||
var inf = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(4f, -5f), "적보병");
|
||||
|
||||
int ranged = Combat.Damage(cfg, arc, inf, false, new System.Random(7), false);
|
||||
int melee = Combat.Damage(cfg, arc, inf, false, new System.Random(7), true);
|
||||
Assert.AreEqual(ranged * cfg.archerMeleeDealtRatio, melee, 1f, "궁병이 근접 유닛과 근접 전투하면 주는 피해 × 비율(기본 50 %)");
|
||||
|
||||
int takenRanged = Combat.Damage(cfg, inf, arc, false, new System.Random(7), false);
|
||||
int takenMelee = Combat.Damage(cfg, inf, arc, false, new System.Random(7), true);
|
||||
Assert.AreEqual(takenRanged * cfg.archerMeleeTakenRatio, takenMelee, 1f, "근접 유닛에게 근접으로 맞으면 받는 피해 × 비율(기본 +50 %)");
|
||||
|
||||
int aa = Combat.Damage(cfg, arc, arc2, false, new System.Random(7), true);
|
||||
int ab = Combat.Damage(cfg, arc, arc2, false, new System.Random(7), false);
|
||||
Assert.AreEqual(ab, aa, "궁병끼리는 근접 패널티 없음(근접 유닛 상대만)");
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// ---------- 창병 ----------
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Spear_PlowsEnemyForward_OverwatchOnFinalHit_DamagesMovingEnemy()
|
||||
{
|
||||
yield return Fresh();
|
||||
var spear = bm.SpawnUnit(UnitType.Spear, Faction.Player, new Vector2(0f, -3f), "창병");
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, 0f), "적보병");
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(spear, foe.Position, false, o);
|
||||
Assert.Contains(foe, o.Attacked);
|
||||
Assert.Less(foe.Hp, foe.MaxHp, "부딪힌 적에게 피해");
|
||||
Assert.Contains(foe, o.Pushed, "튕기지 않고 밀며 함께 전진");
|
||||
Assert.Greater(foe.Position.y, 0.5f, "적이 남은 거리만큼 앞으로 밀림");
|
||||
Assert.AreEqual(CastContact(spear, foe), foe.Position.y - spear.Position.y, 0.1f, "창병은 접촉 간격을 유지하며 따라감");
|
||||
Assert.IsTrue(o.Overwatch && spear.Overwatch, "최종 목표점 명중 → 대기(견제)");
|
||||
|
||||
int hp = foe.Hp;
|
||||
|
|
@ -203,6 +322,29 @@ namespace TKS.Tests
|
|||
Assert.AreEqual(farHp, far.Hp);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator Spear_PlowsAlly_NoDamage_AllyAttacksWhenPushedOntoEnemy()
|
||||
{
|
||||
yield return Fresh();
|
||||
var spear = bm.SpawnUnit(UnitType.Spear, Faction.Player, new Vector2(0f, -4f), "창병");
|
||||
var ally = bm.SpawnUnit(UnitType.Infantry, Faction.Player, new Vector2(0f, -2f), "아군보병");
|
||||
var foe = bm.SpawnUnit(UnitType.Infantry, Faction.Enemy, new Vector2(0f, 1.5f), "적보병");
|
||||
int allyHp = ally.Hp;
|
||||
var o = new ActionOutcome();
|
||||
yield return bm.ResolveForTest(spear, new Vector2(0f, 0f), false, o);
|
||||
Assert.AreEqual(0, o.Attacked.Count, "아군에 부딪힌 창병 자신은 공격하지 않음");
|
||||
Assert.Contains(ally, o.Pushed);
|
||||
Assert.Greater(ally.Position.y, 0.4f, "아군을 밀며 함께 전진(적에 닿을 때까지)");
|
||||
Assert.LessOrEqual(Gap(ally, foe), Combat.ContactTolerance + 0.05f, "밀린 아군이 적과 닿음");
|
||||
Assert.AreEqual(CastContact(spear, ally), ally.Position.y - spear.Position.y, 0.1f, "창병은 아군 바로 뒤까지 따라옴");
|
||||
Assert.Less(foe.Hp, foe.MaxHp, "밀린 아군이 적을 공격(규칙 ⑤)");
|
||||
Assert.AreEqual(spear.MaxHp, spear.Hp, "창병은 피해 없음");
|
||||
Assert.AreEqual(1, o.Notes.Count);
|
||||
Assert.IsTrue(allyHp >= ally.Hp, "아군은 밀려서 피해를 받지 않는다(근접 공격 반격만 가능)");
|
||||
}
|
||||
|
||||
// ---------- 턴 · 승패 · AI · 카메라 · 표시 ----------
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator EnemyTurn_RunsAndReturnsToPlayerTurn()
|
||||
{
|
||||
|
|
@ -313,7 +455,7 @@ namespace TKS.Tests
|
|||
[UnityTest]
|
||||
public IEnumerator UnitArt_HeroWithArt_PlaysBackIdle_UnitWithoutArt_IsShape()
|
||||
{
|
||||
// 부트스트랩 스테이지 1: 유비·관우·장비 = 스켈레탈 세션 아트(Resources/Units/liubei·guanyu·zhangfei) · 조운·황충·적 = 도형
|
||||
// 부트스트랩 스테이지 1: 유비·관우·장비 = 스켈레탈 세션 아트(Resources/Units/liubei·guanyu·zhangfei) · 조운·황충 = 도형
|
||||
Unit liubei = null;
|
||||
Unit shape = null;
|
||||
foreach (var u in bm.AliveUnits(Faction.Player))
|
||||
|
|
@ -332,6 +474,29 @@ namespace TKS.Tests
|
|||
|
||||
liubei.View.PlayAttack();
|
||||
Assert.AreEqual("back_action", liubei.View.Anim.Current, "공격 자세 클립");
|
||||
|
||||
// 원화 세션 1차 납품(2026-11-04): 황건적 5종 정지 그림(front_idle 1장) + 전장 배경
|
||||
int enemyArt = 0;
|
||||
foreach (var e in bm.AliveUnits(Faction.Enemy))
|
||||
{
|
||||
if (e.View.HasArt)
|
||||
{
|
||||
enemyArt++;
|
||||
Assert.AreEqual("front_idle", e.View.Anim.Current, "적은 앞모습 정지 그림");
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(bm.AliveCount(Faction.Enemy), enemyArt, "스테이지 1 황건적은 전원 원화(hgj_*)로 표시");
|
||||
Assert.IsTrue(bm.Board.HasArt, "전장 배경 = Resources/Board/" + cfg.boardArt);
|
||||
|
||||
// 원화 2차 납품(2026-11-04): 동탁군 보·창·기·궁 4종 반영 · 여포(dz_boss_lvbu)는 보류 → 도형
|
||||
bm.StartStage(1);
|
||||
yield return null;
|
||||
foreach (var e in bm.AliveUnits(Faction.Enemy))
|
||||
{
|
||||
if (e.IsBoss) continue;
|
||||
Assert.IsTrue(e.View.HasArt, "스테이지 2 동탁군 병종 4종은 원화(" + e.ArtId + ")");
|
||||
}
|
||||
bm.StartStage(0);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,9 +44,10 @@ Unity.exe -projectPath <proj> -executeMethod TKS.EditorTools.ProjectSetup.AutoPl
|
|||
## 코드 규칙
|
||||
|
||||
- 수치·영웅·스테이지 SOT = `Assets/Resources/BalanceConfig.asset`(Inspector 조정 · 최초 시드만 `Assets/Scripts/Editor/BalanceDefaults.cs`). 코드·테스트에 수치를 직접 적지 않는다(C45).
|
||||
- 규칙 해석 변경 = `Assets/Scripts/Runtime/ActionResolver.cs`(병종 4종) · `BattleManager.cs`(턴·승패·창병 대기) · `Combat.cs`(피해 공식·경로 질의). 변경 시 `Assets/Tests/PlayMode/BattleFlowTests.cs` 에 테스트 추가 후 14종 이상 전부 통과 확인.
|
||||
- 규칙 해석 변경 = `Assets/Scripts/Runtime/ActionResolver.cs`(병종 4종) · `BattleManager.cs`(턴·승패·창병 대기) · `Combat.cs`(피해 공식·경로 질의). 변경 시 `Assets/Tests/PlayMode/BattleFlowTests.cs` 에 테스트 추가 후 22종 이상 전부 통과 확인.
|
||||
- 콜라이더 질의 전 `Physics2D.SyncTransforms()` · 유닛 제거는 `SetActive(false)` 후 `Destroy` · 자식 렌더러가 있는 표시물은 GameObject 통째로 끈다.
|
||||
- **궁병 규칙(PD 2026-11-04)**: 사거리 = `slingMaxDistance × archerRangeRatio`(자산 · 기본 0.2 = 슬링샷 범위 +20 %). 조준 방향 「슬링샷 거리 + 사거리」 안에 적이 있으면 적 **가장자리**가 사거리에 들어올 때까지만 이동 후 사격 · 적이 없으면 **목표점까지 이동만**(PD 2026-11-04 2차 지시 · 1차의 「행동 취소」 폐기 · AI 적 궁병도 같음). `UnitStats.attackRange` 는 창병 견제 반경만.
|
||||
- **궁병 규칙(PD 2026-11-04 · #1080)**: 사거리 = `slingMaxDistance`(얇은 원). 조준 방향 사거리 안의 적(가장자리 간격 기준) = **제자리 사격** · 사거리 밖 ~ ×(1 + `archerRangeRatio`)(붉은 도넛) 안의 적 = 들어올 만큼만(최대 `ArcherMoveAllow` = 슬링 × 비율) 다가가 사격 · 더 멀면 **목표점까지 이동만**(#1079). 반격 없음 · 목표점 명중 크리. **근접 전투**(돌진·반격·관통·견제·밀려 닿은 공격)에서 궁병은 근접 유닛 상대 주는 피해 × `archerMeleeDealtRatio`(0.5) · 받는 피해 × `archerMeleeTakenRatio`(1.5) — `Combat.Damage(…, melee)` · 원거리 사격은 패널티 없음.
|
||||
- **충돌 규칙(PD 2026-11-04 · #1080)** — `ActionResolver`: 보병 = 경로 첫 유닛(아군·적군)에 부딪히면 **알까기**(자신은 접촉점에 멈춤 · 상대는 접촉 방향으로 남은 거리 × `collisionPushRatio` 만큼 밀림 · 적이면 공격+반격 뒤 밀림) · 기병 = 유닛 무시 관통(적 피해 · 아군은 지나칠 거리가 안 나오면 **그 아군 직전 정지** `StopBeforeUnclearedAllies`) · 창병 = 첫 유닛을 **밀며 함께 전진**(접촉 간격 유지 · 앞 유닛이 막히면 둘 다 정지 · 적이면 피해 · 아군은 밀기만) · **규칙 ⑤** = 밀린 같은 진영 유닛이 상대와 닿으면(`Combat.TouchingOpponent` · 간격 ≤ 0.12) 그 자리에서 근접 공격 1회(조작마다 · 자기 행동 소모 없음 · `ActionOutcome.Notes`). 적 AI 도 같은 코드(대칭). `UnitStats.attackRange` 는 창병 견제 반경만.
|
||||
- **사거리 표시(PD 2026-11-04)**: 유닛 탭(아군·적군) → `RangeIndicator`(슬링샷 최대 거리 = 얇은 반투명 원 · 궁병 = 슬링샷 범위 ~ ×(1+비율) 채워진 붉은 도넛 · 범위 안 적이 없으면 회색).
|
||||
- 보고는 실측·추정·미확인을 분리한다(C23 · C5). 커밋은 의도 변경 파일만.
|
||||
|
||||
|
|
@ -54,7 +55,8 @@ Unity.exe -projectPath <proj> -executeMethod TKS.EditorTools.ProjectSetup.AutoPl
|
|||
|
||||
- 아트 위치 = `Assets/Resources/Units/<art>/<clip>/f000.png …` + `meta.json`(fps · ppu · pivot(발) · charHeightPx · clips). `<art>` 는 `BalanceConfig.asset` 의 `heroes[].art` / `stages[].enemies[].art`(비면 도형). 아군 = `liubei`·`guanyu`·`zhangfei`(스켈레탈 세션 리그 프레임) · 적 = `<세력>_<병종>`(`hgj_infantry` …) · 보스 = `<세력>_boss_<로마자>`(`hgj_boss_zhangjiao` …).
|
||||
- 스켈레탈 리그 → 프레임: 조직 레포 스킬 `skeletal-anim-pipeline/files/scripts/rig_frames_export.py rig <리그 패키지> --parts <작업>/parts --out Assets/Resources/Units/<art> --clips back_idle,back_ready,back_action,back_damage,back_stop,front_idle,front_ready,front_action,front_damage,front_stop,front_victory,front_failed --px 180 --step 2`(ComfyUI venv python · 실측 482×470 · 15 fps · 3인 11 MB).
|
||||
- 정지 그림(적 SD 전신 `_rgba.png`) → `rig_frames_export.py static <png> --out Assets/Resources/Units/<art>`(front_idle 1장 · 발 = 불투명 상자 아래).
|
||||
- 정지 그림(적 SD 전신 `_rgba.png`) → `rig_frames_export.py static <png> --out Assets/Resources/Units/<art>`(front_idle 1장 · 발 = 불투명 상자 아래). 원화 세션 납품 = `C:\ArtRnD\samguk_game\manifest.json` · 파일명 `<art>_rgba.png` → `<art>` 폴더(2026-11-04 1차 `hgj_*` 5종 + 2차 `dz_infantry·spear·cavalry·archer` 반영 완료 · 여포 `dz_boss_lvbu`·`cc_*`·`wei_*` 는 납품 시 같은 명령).
|
||||
- 전장 배경 = `Assets/Resources/Board/<boardArt>.png`(자산 `boardArt` · 5:8 불투명 · PPU 100 = 전장 20×32) → `Board.Build` 가 있으면 바닥으로 쓰고 격자·진영 띠는 생략. 임포트 규칙(스프라이트 · 4096 · 압축)은 같은 `UnitArtImporter` 의 `Board/` 분기 · 재임포트 메뉴가 두 폴더 처리.
|
||||
- 임포트 설정은 `Editor/UnitArtImporter.cs`(AssetPostprocessor · meta 의 pivot·ppu) 가 자동 적용 · 프레임을 넣거나 meta 를 바꾼 뒤 = 메뉴 `TKS/Reimport Unit Art`(Build/BuildFresh 가 함께 호출).
|
||||
- 재생 = `UnitArt.cs`(Resources 로더 · 캐시) + `FrameAnimator`(대기 loop · 준비 = 조준 중 · 공격·피격 = 1프레임 자세 유지 후 대기 · 승리 = 끝 프레임 유지) · `UnitView` 가 아군 = `back_*` · 적군 = `front_*` 로 고르고 없으면 다른 보기 → 대기. 세로 크기 = `unitArtHeight`(자산) × 반지름 배율 · 발 = 원 중심 아래 0.55 R.
|
||||
|
||||
|
|
|
|||
10
README.md
|
|
@ -22,12 +22,12 @@ Unity 6000.3.23f1 · 2D · 도형 플레이스홀더(아트 리소스는 별도
|
|||
|
||||
| 병종 | 경로 위 적 | 최종 목표점 명중 시 |
|
||||
|---|---|---|
|
||||
| 보병 | 첫 적까지 이동 후 공격(반격 받음) | 추가 이동 1회(이동만) |
|
||||
| 기병 | 경로 위 모든 적 관통 피해 · 목표점까지 이동 | 제자리로 복귀 |
|
||||
| 창병 | 보병과 동일 | 대기 — 적군 턴에 반경 안에서 움직이는 적에게 피해 |
|
||||
| 궁병 | 목표 방향 「슬링샷 거리 + 사거리」 안의 적을 향해 사거리까지 이동 후 사격(반격 없음) · 적이 없으면 목표점까지 이동 | 무조건 크리티컬 |
|
||||
| 보병 | 첫 유닛(아군·적군)에 부딪히면 알까기처럼 남은 힘만큼 밀어내고 자신은 멈춤 · 적이면 공격(반격 받음) | 추가 이동 1회(이동만) |
|
||||
| 기병 | 유닛을 무시하고 관통 · 경로 위 적은 모두 피해 · 아군은 지나칠 거리가 안 나오면 직전에 멈춤 | 제자리로 복귀 |
|
||||
| 창병 | 첫 유닛을 튕기지 않고 밀며 함께 전진 · 적이면 피해(반격 받음) · 아군은 밀기만 | 대기 — 적군 턴에 반경 안에서 움직이는 적에게 피해 |
|
||||
| 궁병 | 사거리(= 슬링샷 범위) 안의 적은 제자리 사격 · 사거리 밖 +20 % 안의 적은 들어올 만큼만 다가가 사격(반격 없음) · 더 멀면 목표점까지 이동 | 무조건 크리티컬 |
|
||||
|
||||
상성: 보병 > 창병 > 기병 > 보병. 궁병은 모두에게 약함(받는 피해 1.5배).
|
||||
밀린 아군이 적과 닿으면 그 자리에서 공격 1회(조작마다 · 자기 행동 소모 없음). 상성: 보병 > 창병 > 기병 > 보병. 궁병은 모두에게 약함(받는 피해 1.5배) · 근접 유닛과 근접 전투하면 주는 피해 50 % · 받는 피해 +50 %(원거리 사격은 패널티 없음).
|
||||
|
||||
## 데이터(SOT)
|
||||
|
||||
|
|
|
|||