Compare commits
6 Commits
kill-json-
...
animation-
| Author | SHA1 | Date | |
|---|---|---|---|
| 16962a5ae3 | |||
|
|
612ca7eae8 | ||
| 2602cbbeb3 | |||
| acf46c701e | |||
| fe2eb0a280 | |||
| 0aa2270e1a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -104,3 +104,6 @@ InitTestScene*.unity*
|
|||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
||||||
|
|
||||||
|
# WIP docs
|
||||||
|
/docs/wip/
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ AnimatorController:
|
|||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
m_PrefabInstance: {fileID: 0}
|
m_PrefabInstance: {fileID: 0}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_Name: trafalgar_0002
|
m_Name: AnimSM_Trafalgar
|
||||||
serializedVersion: 5
|
serializedVersion: 5
|
||||||
m_AnimatorParameters:
|
m_AnimatorParameters:
|
||||||
- m_Name: Speed
|
- m_Name: Speed
|
||||||
8
Assets/Art/Animations/Trafalgar.meta
Normal file
8
Assets/Art/Animations/Trafalgar.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bdcb5eef530d4d341b9957fc5d954164
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.png
Normal file
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 524 KiB |
570
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.png.meta
Normal file
570
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.png.meta
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e071f6ab7f5db5642bcf5502788a8b6e
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
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: 2
|
||||||
|
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: 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: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: iOS
|
||||||
|
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: 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: WindowsStoreApps
|
||||||
|
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: Traf_0012
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 1294
|
||||||
|
width: 497
|
||||||
|
height: 753
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.43661973, y: -0.006799469}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 2104c4fa569ede04e9e6db84327b11cc
|
||||||
|
internalID: -1603423718
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0013
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 1294
|
||||||
|
width: 497
|
||||||
|
height: 753
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.43661973, y: -0.006799469}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: dfc34b8e58260474097ac74c0e16fcc7
|
||||||
|
internalID: -1907988884
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0022
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 887
|
||||||
|
y: 1295
|
||||||
|
width: 489
|
||||||
|
height: 752
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4212679, y: -0.0014893617}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 5c931e231e8e6474f8882cd577bf312e
|
||||||
|
internalID: 959286612
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0023
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 887
|
||||||
|
y: 1295
|
||||||
|
width: 489
|
||||||
|
height: 752
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4212679, y: -0.0014893617}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 3d3411da19b993140a2d54d72ebdbfe3
|
||||||
|
internalID: -1984904044
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0031
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 523
|
||||||
|
width: 388
|
||||||
|
height: 769
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.01576073}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: d76407aa4dcdc8648b365da2d2eb5d10
|
||||||
|
internalID: 1903400688
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0032
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 523
|
||||||
|
width: 388
|
||||||
|
height: 769
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.01576073}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 6584cd841ef691d47b5c391cf55bcb5a
|
||||||
|
internalID: -346991893
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0033
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1168
|
||||||
|
y: 530
|
||||||
|
width: 388
|
||||||
|
height: 749
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.046889186}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: c812b6a01ffaa1c4cbfdfd343883fedd
|
||||||
|
internalID: -1312077390
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0034
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1168
|
||||||
|
y: 530
|
||||||
|
width: 388
|
||||||
|
height: 749
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.046889186}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: dadb8259df47d584d948a41e2679ed4f
|
||||||
|
internalID: -251651705
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0035
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1378
|
||||||
|
y: 1281
|
||||||
|
width: 388
|
||||||
|
height: 766
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.027571801}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 0e6daaa9f75a679449677f606258ad79
|
||||||
|
internalID: -1497385347
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0036
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1378
|
||||||
|
y: 1281
|
||||||
|
width: 388
|
||||||
|
height: 766
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.027571801}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: c83aaa087c551d645af3e82a7bfd409c
|
||||||
|
internalID: 2048706373
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0039
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 778
|
||||||
|
y: 523
|
||||||
|
width: 388
|
||||||
|
height: 769
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.01576073}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 450ecc69dd2e0b14a8428b41d32a4618
|
||||||
|
internalID: 1138447422
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0040
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 778
|
||||||
|
y: 523
|
||||||
|
width: 388
|
||||||
|
height: 769
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.01576073}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 8a8ac3b3cf365d94882a586d83233997
|
||||||
|
internalID: -1620690620
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0041
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1558
|
||||||
|
y: 530
|
||||||
|
width: 388
|
||||||
|
height: 749
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.046889186}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: c7949d6564430af498a79afd6fc5f4be
|
||||||
|
internalID: 110470137
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0042
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1558
|
||||||
|
y: 530
|
||||||
|
width: 388
|
||||||
|
height: 749
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.046889186}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 742eaa0dbdc99824898ed699287521fe
|
||||||
|
internalID: 393019130
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0045
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 1267
|
||||||
|
width: 385
|
||||||
|
height: 780
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.04502564}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 4e7250220be8bc2408ffd1ae6fe05476
|
||||||
|
internalID: 1669039794
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0046
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 1267
|
||||||
|
width: 385
|
||||||
|
height: 780
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.04502564}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 8d3317b4ee45cff4f836db94bac449ca
|
||||||
|
internalID: 1592877174
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0053
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 487
|
||||||
|
width: 385
|
||||||
|
height: 778
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.045141388}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 87e25079c251b2a4d9ac3aa05da45bc0
|
||||||
|
internalID: -1032471120
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0054
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 487
|
||||||
|
width: 385
|
||||||
|
height: 778
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.045141388}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 229c666cb10a51d4aaf6a9ac700fded1
|
||||||
|
internalID: 206645212
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: f90f4f1df4613dd4c9174310f08eec3e
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable:
|
||||||
|
Traf_0012: -1603423718
|
||||||
|
Traf_0013: -1907988884
|
||||||
|
Traf_0022: 959286612
|
||||||
|
Traf_0023: -1984904044
|
||||||
|
Traf_0031: 1903400688
|
||||||
|
Traf_0032: -346991893
|
||||||
|
Traf_0033: -1312077390
|
||||||
|
Traf_0034: -251651705
|
||||||
|
Traf_0035: -1497385347
|
||||||
|
Traf_0036: 2048706373
|
||||||
|
Traf_0039: 1138447422
|
||||||
|
Traf_0040: -1620690620
|
||||||
|
Traf_0041: 110470137
|
||||||
|
Traf_0042: 393019130
|
||||||
|
Traf_0045: 1669039794
|
||||||
|
Traf_0046: 1592877174
|
||||||
|
Traf_0053: -1032471120
|
||||||
|
Traf_0054: 206645212
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
33
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.tpsheet
Normal file
33
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.tpsheet
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# Sprite sheet data for Unity.
|
||||||
|
#
|
||||||
|
# To import these sprites into your Unity project, download "TexturePackerImporter":
|
||||||
|
# https://www.codeandweb.com/texturepacker/unity
|
||||||
|
#
|
||||||
|
# $TexturePacker:SmartUpdate:12cdac846eeb0ac92e8b589818b5b9fd:1617b416bec25e82693e9f78935249ba:a11e88a1024a8564f888a3954b97f2ab$
|
||||||
|
#
|
||||||
|
:format=40300
|
||||||
|
:texture=Trafalgar-_Walking1.png
|
||||||
|
:size=2048x2048
|
||||||
|
:pivotpoints=enabled
|
||||||
|
:borders=disabled
|
||||||
|
:alphahandling=ClearTransparentPixels
|
||||||
|
|
||||||
|
Traf_0012;388;1294;497;753; 0.43661971830985913;-0.006799468791500729; 0;0;0;0
|
||||||
|
Traf_0013;388;1294;497;753; 0.43661971830985913;-0.006799468791500729; 0;0;0;0
|
||||||
|
Traf_0022;887;1295;489;752; 0.4212678936605317;-0.0014893617021276562; 0;0;0;0
|
||||||
|
Traf_0023;887;1295;489;752; 0.4212678936605317;-0.0014893617021276562; 0;0;0;0
|
||||||
|
Traf_0031;388;523;388;769; 0.49742268041237114;-0.015760728218465525; 0;0;0;0
|
||||||
|
Traf_0032;388;523;388;769; 0.49742268041237114;-0.015760728218465525; 0;0;0;0
|
||||||
|
Traf_0033;1168;530;388;749; 0.49742268041237114;-0.04688918558077426; 0;0;0;0
|
||||||
|
Traf_0034;1168;530;388;749; 0.49742268041237114;-0.04688918558077426; 0;0;0;0
|
||||||
|
Traf_0035;1378;1281;388;766; 0.49742268041237114;-0.027571801566579568; 0;0;0;0
|
||||||
|
Traf_0036;1378;1281;388;766; 0.49742268041237114;-0.027571801566579568; 0;0;0;0
|
||||||
|
Traf_0039;778;523;388;769; 0.49742268041237114;-0.015760728218465525; 0;0;0;0
|
||||||
|
Traf_0040;778;523;388;769; 0.49742268041237114;-0.015760728218465525; 0;0;0;0
|
||||||
|
Traf_0041;1558;530;388;749; 0.49742268041237114;-0.04688918558077426; 0;0;0;0
|
||||||
|
Traf_0042;1558;530;388;749; 0.49742268041237114;-0.04688918558077426; 0;0;0;0
|
||||||
|
Traf_0045;1;1267;385;780; 0.4987012987012987;-0.045025641025641105; 0;0;0;0
|
||||||
|
Traf_0046;1;1267;385;780; 0.4987012987012987;-0.045025641025641105; 0;0;0;0
|
||||||
|
Traf_0053;1;487;385;778; 0.4987012987012987;-0.045141388174807284; 0;0;0;0
|
||||||
|
Traf_0054;1;487;385;778; 0.4987012987012987;-0.045141388174807284; 0;0;0;0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4f8877ac6aea8414b836f7aebe5971c5
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.png
Normal file
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 602 KiB |
455
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.png.meta
Normal file
455
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.png.meta
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 733a93f45fa2217478df69dfce47bfe4
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
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: 2
|
||||||
|
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: 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: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: iOS
|
||||||
|
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: 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: WindowsStoreApps
|
||||||
|
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: Traf_0001
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 435
|
||||||
|
width: 388
|
||||||
|
height: 789
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: 0.0011153359}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 99e3a7760a8f854449fd6d920b476d9a
|
||||||
|
internalID: 2116105473
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0002
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 428
|
||||||
|
y: 1276
|
||||||
|
width: 484
|
||||||
|
height: 771
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4318182, y: 0.008923476}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 42b45001238b5a344bb3c33c518f09c5
|
||||||
|
internalID: 1747672391
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0004
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 1226
|
||||||
|
width: 425
|
||||||
|
height: 821
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.45411766, y: 0.0010718636}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 38b273fc1f1eb2e4d86d7d188ae29d3b
|
||||||
|
internalID: 676913695
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0010
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 914
|
||||||
|
y: 1287
|
||||||
|
width: 487
|
||||||
|
height: 760
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.42299795, y: -0.00015789474}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 4f6ffaf61113d374abe4e06fd73cbc36
|
||||||
|
internalID: 1666239922
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0011
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 914
|
||||||
|
y: 1287
|
||||||
|
width: 487
|
||||||
|
height: 760
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.42299795, y: -0.00015789474}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 86ba464b7d2c8634fb72a9a07dbbef5b
|
||||||
|
internalID: -1222548245
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0014
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1403
|
||||||
|
y: 1299
|
||||||
|
width: 489
|
||||||
|
height: 748
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4212679, y: -0.0068449196}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 16f254ec4e949174e9994e2ab73659b0
|
||||||
|
internalID: -996981228
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0015
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1403
|
||||||
|
y: 1299
|
||||||
|
width: 489
|
||||||
|
height: 748
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4212679, y: -0.0068449196}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 1009f1f27d01b2f4388b7bbe4a3f831e
|
||||||
|
internalID: -575895047
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0016
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1403
|
||||||
|
y: 539
|
||||||
|
width: 488
|
||||||
|
height: 758
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.42008197, y: 0.002480211}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 8169527510c5b444396b0858c71de75b
|
||||||
|
internalID: -157325566
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0017
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1403
|
||||||
|
y: 539
|
||||||
|
width: 488
|
||||||
|
height: 758
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.42008197, y: 0.002480211}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 2f72d53a23eef9f438ea36b8cc42f35b
|
||||||
|
internalID: 879950995
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0027
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 781
|
||||||
|
y: 508
|
||||||
|
width: 388
|
||||||
|
height: 766
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.027571801}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 93a43eb0ec82d584397edb3d0382e9f1
|
||||||
|
internalID: -687394884
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0028
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 781
|
||||||
|
y: 508
|
||||||
|
width: 388
|
||||||
|
height: 766
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: -0.027571801}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 536dcf11c6fb0364ebc1fac7120e2a2e
|
||||||
|
internalID: -200100742
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0029
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 391
|
||||||
|
y: 436
|
||||||
|
width: 388
|
||||||
|
height: 788
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: 0.0023857867}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 177be93cf20be3e438bd15e6a40ccf16
|
||||||
|
internalID: 97038495
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0030
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 391
|
||||||
|
y: 436
|
||||||
|
width: 388
|
||||||
|
height: 788
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: 0.0023857867}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 713b76ad51f3f6840bb01e6eb75b75a8
|
||||||
|
internalID: 1945709413
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 1d9a5d7bb6391e34dbaa067b99ff0718
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable:
|
||||||
|
Traf_0001: 2116105473
|
||||||
|
Traf_0002: 1747672391
|
||||||
|
Traf_0004: 676913695
|
||||||
|
Traf_0010: 1666239922
|
||||||
|
Traf_0011: -1222548245
|
||||||
|
Traf_0014: -996981228
|
||||||
|
Traf_0015: -575895047
|
||||||
|
Traf_0016: -157325566
|
||||||
|
Traf_0017: 879950995
|
||||||
|
Traf_0027: -687394884
|
||||||
|
Traf_0028: -200100742
|
||||||
|
Traf_0029: 97038495
|
||||||
|
Traf_0030: 1945709413
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
28
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.tpsheet
Normal file
28
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.tpsheet
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#
|
||||||
|
# Sprite sheet data for Unity.
|
||||||
|
#
|
||||||
|
# To import these sprites into your Unity project, download "TexturePackerImporter":
|
||||||
|
# https://www.codeandweb.com/texturepacker/unity
|
||||||
|
#
|
||||||
|
# $TexturePacker:SmartUpdate:12cdac846eeb0ac92e8b589818b5b9fd:1617b416bec25e82693e9f78935249ba:a11e88a1024a8564f888a3954b97f2ab$
|
||||||
|
#
|
||||||
|
:format=40300
|
||||||
|
:texture=Trafalgar-_Walking2.png
|
||||||
|
:size=2048x2048
|
||||||
|
:pivotpoints=enabled
|
||||||
|
:borders=disabled
|
||||||
|
:alphahandling=ClearTransparentPixels
|
||||||
|
|
||||||
|
Traf_0001;1;435;388;789; 0.49742268041237114;0.0011153358681875725; 0;0;0;0
|
||||||
|
Traf_0002;428;1276;484;771; 0.4318181818181818;0.008923476005188058; 0;0;0;0
|
||||||
|
Traf_0004;1;1226;425;821; 0.4541176470588235;0.0010718635809987553; 0;0;0;0
|
||||||
|
Traf_0010;914;1287;487;760; 0.42299794661190965;-0.00015789473684213462; 0;0;0;0
|
||||||
|
Traf_0011;914;1287;487;760; 0.42299794661190965;-0.00015789473684213462; 0;0;0;0
|
||||||
|
Traf_0014;1403;1299;489;748; 0.4212678936605317;-0.0068449197860962485; 0;0;0;0
|
||||||
|
Traf_0015;1403;1299;489;748; 0.4212678936605317;-0.0068449197860962485; 0;0;0;0
|
||||||
|
Traf_0016;1403;539;488;758; 0.42008196721311475;0.0024802110817941925; 0;0;0;0
|
||||||
|
Traf_0017;1403;539;488;758; 0.42008196721311475;0.0024802110817941925; 0;0;0;0
|
||||||
|
Traf_0027;781;508;388;766; 0.49742268041237114;-0.027571801566579568; 0;0;0;0
|
||||||
|
Traf_0028;781;508;388;766; 0.49742268041237114;-0.027571801566579568; 0;0;0;0
|
||||||
|
Traf_0029;391;436;388;788; 0.49742268041237114;0.0023857868020304984; 0;0;0;0
|
||||||
|
Traf_0030;391;436;388;788; 0.49742268041237114;0.0023857868020304984; 0;0;0;0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9cbfb3dc06a1e1e469c8648617968feb
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.png
Normal file
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 498 KiB |
570
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.png.meta
Normal file
570
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.png.meta
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 433bc1aad6c5ab748aac4443291f7252
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
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: 2
|
||||||
|
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: 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: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: iOS
|
||||||
|
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: 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: WindowsStoreApps
|
||||||
|
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: Traf_0018
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 874
|
||||||
|
y: 1296
|
||||||
|
width: 484
|
||||||
|
height: 751
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4194215, y: -0.012143808}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: a858c3dfbb70e8446aa55ae6b35af296
|
||||||
|
internalID: 733294300
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0019
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 874
|
||||||
|
y: 1296
|
||||||
|
width: 484
|
||||||
|
height: 751
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4194215, y: -0.012143808}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 43a8dc58a6d230d4491863fb2958623d
|
||||||
|
internalID: 705458408
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0020
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 1295
|
||||||
|
width: 484
|
||||||
|
height: 752
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4214876, y: -0.008138298}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: fde948e9fdc670045a63f5375c9a76e4
|
||||||
|
internalID: -1431518560
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0021
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 1295
|
||||||
|
width: 484
|
||||||
|
height: 752
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4214876, y: -0.008138298}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 092b482429e28a643b19d6599355fcc0
|
||||||
|
internalID: 1414616556
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0037
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1360
|
||||||
|
y: 1259
|
||||||
|
width: 388
|
||||||
|
height: 788
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: 0.0023857867}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 58adfd457989f754cb25c1bc446597d8
|
||||||
|
internalID: -1013923138
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0038
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1360
|
||||||
|
y: 1259
|
||||||
|
width: 388
|
||||||
|
height: 788
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4974227, y: 0.0023857867}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 7803c4e1dadb44747b0aefc79fcc49e8
|
||||||
|
internalID: -1414315956
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0047
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 495
|
||||||
|
width: 385
|
||||||
|
height: 798
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.01518797}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: f90b9fc757002b645bc596cb8264a9aa
|
||||||
|
internalID: -1890952713
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0048
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 388
|
||||||
|
y: 495
|
||||||
|
width: 385
|
||||||
|
height: 798
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.01518797}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 9659dae68f79ac041bb18891d2f76a29
|
||||||
|
internalID: 475769653
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0049
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 1231
|
||||||
|
width: 385
|
||||||
|
height: 816
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: 0.0023039216}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 6d91a74e4045ee84b8e74ab315c17fbd
|
||||||
|
internalID: -1497933548
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0050
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 1231
|
||||||
|
width: 385
|
||||||
|
height: 816
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: 0.0023039216}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 0e6beda4798000a4d83a5157043b0e34
|
||||||
|
internalID: 436138382
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0051
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1162
|
||||||
|
y: 462
|
||||||
|
width: 385
|
||||||
|
height: 795
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.026566038}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 3cf4c64953b169a4784b520927e9f63a
|
||||||
|
internalID: -1550351557
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0052
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1162
|
||||||
|
y: 462
|
||||||
|
width: 385
|
||||||
|
height: 795
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.026566038}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 50bd5e33bf88ec6478003c5d562236e3
|
||||||
|
internalID: 69286420
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0055
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 775
|
||||||
|
y: 495
|
||||||
|
width: 385
|
||||||
|
height: 798
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.01518797}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: bf7c57e5ba290b745a5da716a0923e18
|
||||||
|
internalID: -1976038361
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0056
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 775
|
||||||
|
y: 495
|
||||||
|
width: 385
|
||||||
|
height: 798
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.01518797}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 1762325b04ba42c4d86923c76cf3274c
|
||||||
|
internalID: -1779701630
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0057
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 413
|
||||||
|
width: 385
|
||||||
|
height: 816
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: 0.0023039216}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: e0dff8cf484ad2b43a9de9a0fd05b3f5
|
||||||
|
internalID: 747752778
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0058
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 413
|
||||||
|
width: 385
|
||||||
|
height: 816
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: 0.0023039216}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 47cd4dcfd00536e42910a354fecde20c
|
||||||
|
internalID: 1507533172
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0059
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1549
|
||||||
|
y: 462
|
||||||
|
width: 385
|
||||||
|
height: 795
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.026566038}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: e90e03a6ba1435f4e97d2f5f4205f366
|
||||||
|
internalID: -1052919729
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0060
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1549
|
||||||
|
y: 462
|
||||||
|
width: 385
|
||||||
|
height: 795
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.4987013, y: -0.026566038}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: db25bd93e4d789a48a38745450b1dad1
|
||||||
|
internalID: -413710878
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 140593767a7e00749b0fe65319e90927
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable:
|
||||||
|
Traf_0018: 733294300
|
||||||
|
Traf_0019: 705458408
|
||||||
|
Traf_0020: -1431518560
|
||||||
|
Traf_0021: 1414616556
|
||||||
|
Traf_0037: -1013923138
|
||||||
|
Traf_0038: -1414315956
|
||||||
|
Traf_0047: -1890952713
|
||||||
|
Traf_0048: 475769653
|
||||||
|
Traf_0049: -1497933548
|
||||||
|
Traf_0050: 436138382
|
||||||
|
Traf_0051: -1550351557
|
||||||
|
Traf_0052: 69286420
|
||||||
|
Traf_0055: -1976038361
|
||||||
|
Traf_0056: -1779701630
|
||||||
|
Traf_0057: 747752778
|
||||||
|
Traf_0058: 1507533172
|
||||||
|
Traf_0059: -1052919729
|
||||||
|
Traf_0060: -413710878
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
33
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.tpsheet
Normal file
33
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.tpsheet
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# Sprite sheet data for Unity.
|
||||||
|
#
|
||||||
|
# To import these sprites into your Unity project, download "TexturePackerImporter":
|
||||||
|
# https://www.codeandweb.com/texturepacker/unity
|
||||||
|
#
|
||||||
|
# $TexturePacker:SmartUpdate:12cdac846eeb0ac92e8b589818b5b9fd:1617b416bec25e82693e9f78935249ba:a11e88a1024a8564f888a3954b97f2ab$
|
||||||
|
#
|
||||||
|
:format=40300
|
||||||
|
:texture=Trafalgar-_Walking3.png
|
||||||
|
:size=2048x2048
|
||||||
|
:pivotpoints=enabled
|
||||||
|
:borders=disabled
|
||||||
|
:alphahandling=ClearTransparentPixels
|
||||||
|
|
||||||
|
Traf_0018;874;1296;484;751; 0.4194214876033058;-0.012143808255659083; 0;0;0;0
|
||||||
|
Traf_0019;874;1296;484;751; 0.4194214876033058;-0.012143808255659083; 0;0;0;0
|
||||||
|
Traf_0020;388;1295;484;752; 0.4214876033057851;-0.008138297872340328; 0;0;0;0
|
||||||
|
Traf_0021;388;1295;484;752; 0.4214876033057851;-0.008138297872340328; 0;0;0;0
|
||||||
|
Traf_0037;1360;1259;388;788; 0.49742268041237114;0.0023857868020304984; 0;0;0;0
|
||||||
|
Traf_0038;1360;1259;388;788; 0.49742268041237114;0.0023857868020304984; 0;0;0;0
|
||||||
|
Traf_0047;388;495;385;798; 0.4987012987012987;-0.015187969924812084; 0;0;0;0
|
||||||
|
Traf_0048;388;495;385;798; 0.4987012987012987;-0.015187969924812084; 0;0;0;0
|
||||||
|
Traf_0049;1;1231;385;816; 0.4987012987012987;0.002303921568627465; 0;0;0;0
|
||||||
|
Traf_0050;1;1231;385;816; 0.4987012987012987;0.002303921568627465; 0;0;0;0
|
||||||
|
Traf_0051;1162;462;385;795; 0.4987012987012987;-0.02656603773584898; 0;0;0;0
|
||||||
|
Traf_0052;1162;462;385;795; 0.4987012987012987;-0.02656603773584898; 0;0;0;0
|
||||||
|
Traf_0055;775;495;385;798; 0.4987012987012987;-0.015187969924812084; 0;0;0;0
|
||||||
|
Traf_0056;775;495;385;798; 0.4987012987012987;-0.015187969924812084; 0;0;0;0
|
||||||
|
Traf_0057;1;413;385;816; 0.4987012987012987;0.002303921568627465; 0;0;0;0
|
||||||
|
Traf_0058;1;413;385;816; 0.4987012987012987;0.002303921568627465; 0;0;0;0
|
||||||
|
Traf_0059;1549;462;385;795; 0.4987012987012987;-0.02656603773584898; 0;0;0;0
|
||||||
|
Traf_0060;1549;462;385;795; 0.4987012987012987;-0.02656603773584898; 0;0;0;0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ae948f43b1dcedb408002364a6c894df
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.png
Normal file
BIN
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
202
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.png.meta
Normal file
202
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.png.meta
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0f575f07e5277824294ade86131e83c2
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
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: 2
|
||||||
|
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: 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: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: iOS
|
||||||
|
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: 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: WindowsStoreApps
|
||||||
|
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: Traf_0024
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 273
|
||||||
|
width: 488
|
||||||
|
height: 750
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.42008197, y: -0.00816}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: 19eae4ca1bf1e984eb588f7c980dc810
|
||||||
|
internalID: -1703591597
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
- serializedVersion: 2
|
||||||
|
name: Traf_0025
|
||||||
|
rect:
|
||||||
|
serializedVersion: 2
|
||||||
|
x: 1
|
||||||
|
y: 273
|
||||||
|
width: 488
|
||||||
|
height: 750
|
||||||
|
alignment: 9
|
||||||
|
pivot: {x: 0.42008197, y: -0.00816}
|
||||||
|
border: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
customData:
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
tessellationDetail: 0
|
||||||
|
bones: []
|
||||||
|
spriteID: f3855632e843eb34e88fcf7251c7cd83
|
||||||
|
internalID: -746435906
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 79d7676310adae84494d551c08b3c47c
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable:
|
||||||
|
Traf_0024: -1703591597
|
||||||
|
Traf_0025: -746435906
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
17
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.tpsheet
Normal file
17
Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.tpsheet
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#
|
||||||
|
# Sprite sheet data for Unity.
|
||||||
|
#
|
||||||
|
# To import these sprites into your Unity project, download "TexturePackerImporter":
|
||||||
|
# https://www.codeandweb.com/texturepacker/unity
|
||||||
|
#
|
||||||
|
# $TexturePacker:SmartUpdate:12cdac846eeb0ac92e8b589818b5b9fd:1617b416bec25e82693e9f78935249ba:a11e88a1024a8564f888a3954b97f2ab$
|
||||||
|
#
|
||||||
|
:format=40300
|
||||||
|
:texture=Trafalgar-_Walking4.png
|
||||||
|
:size=512x1024
|
||||||
|
:pivotpoints=enabled
|
||||||
|
:borders=disabled
|
||||||
|
:alphahandling=ClearTransparentPixels
|
||||||
|
|
||||||
|
Traf_0024;1;273;488;750; 0.42008196721311475;-0.008159999999999945; 0;0;0;0
|
||||||
|
Traf_0025;1;273;488;750; 0.42008196721311475;-0.008159999999999945; 0;0;0;0
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5f5d73421b606944bb2a2c34b4fc7117
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -71,7 +71,6 @@ namespace AppleHills.Editor
|
|||||||
{
|
{
|
||||||
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
|
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
|
||||||
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
|
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
|
||||||
CardSystemManager.Instance.OnCardRarityUpgraded += OnCardRarityUpgraded;
|
|
||||||
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
||||||
|
|
||||||
isSubscribed = true;
|
isSubscribed = true;
|
||||||
@@ -87,7 +86,6 @@ namespace AppleHills.Editor
|
|||||||
{
|
{
|
||||||
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
|
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
|
||||||
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
|
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
|
||||||
CardSystemManager.Instance.OnCardRarityUpgraded -= OnCardRarityUpgraded;
|
|
||||||
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +107,6 @@ namespace AppleHills.Editor
|
|||||||
Repaint();
|
Repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCardRarityUpgraded(CardData card)
|
|
||||||
{
|
|
||||||
lastEventMessage = $"Card upgraded: {card.Name} → {card.Rarity}!";
|
|
||||||
RefreshData();
|
|
||||||
Repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBoosterCountChanged(int count)
|
private void OnBoosterCountChanged(int count)
|
||||||
{
|
{
|
||||||
boosterCount = count;
|
boosterCount = count;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
|
||||||
namespace Interactions
|
namespace Interactions
|
||||||
|
|||||||
829
Assets/Editor/CustomLogWindow.cs
Normal file
829
Assets/Editor/CustomLogWindow.cs
Normal file
@@ -0,0 +1,829 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Core;
|
||||||
|
|
||||||
|
namespace Editor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Custom dockable log window with advanced filtering capabilities.
|
||||||
|
/// Supports filtering by class, method, log level, time range, and text search.
|
||||||
|
/// Multiple instances can be opened with independent filter states.
|
||||||
|
///
|
||||||
|
/// PERFORMANCE NOTE: For large log counts (10,000+), consider implementing virtualized scrolling.
|
||||||
|
/// Only render visible entries within the scroll view to avoid GUI overhead.
|
||||||
|
/// See DrawLogEntries() method for potential optimization location.
|
||||||
|
/// </summary>
|
||||||
|
public class CustomLogWindow : EditorWindow
|
||||||
|
{
|
||||||
|
#region Window State
|
||||||
|
|
||||||
|
private Vector2 scrollPosition;
|
||||||
|
private readonly List<LogEntry> allLogs = new List<LogEntry>();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Filter State
|
||||||
|
|
||||||
|
private HashSet<string> activeClassTags = new HashSet<string>();
|
||||||
|
private HashSet<string> activeMethodTags = new HashSet<string>();
|
||||||
|
private HashSet<string> selectedClassFilters = new HashSet<string>();
|
||||||
|
private HashSet<string> selectedMethodFilters = new HashSet<string>();
|
||||||
|
private HashSet<LogLevel> selectedLevelFilters = new HashSet<LogLevel>
|
||||||
|
{
|
||||||
|
LogLevel.Debug, LogLevel.Info, LogLevel.Warning, LogLevel.Error
|
||||||
|
};
|
||||||
|
private string searchText = "";
|
||||||
|
private bool autoScroll = true;
|
||||||
|
|
||||||
|
// Time range filtering
|
||||||
|
private bool enableTimeFilter = false;
|
||||||
|
private float minTimestamp = 0;
|
||||||
|
private float maxTimestamp = 0;
|
||||||
|
private float currentMaxTimestamp = 0;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UI State
|
||||||
|
|
||||||
|
// Colors for log levels
|
||||||
|
private readonly Color debugColor = Color.white;
|
||||||
|
private readonly Color infoColor = Color.white;
|
||||||
|
private readonly Color warningColor = Color.yellow;
|
||||||
|
private readonly Color errorColor = new Color(1f, 0.3f, 0.3f);
|
||||||
|
|
||||||
|
// Alternating row background colors
|
||||||
|
private readonly Color evenRowColor = new Color(0.22f, 0.22f, 0.22f);
|
||||||
|
private readonly Color oddRowColor = new Color(0.26f, 0.26f, 0.26f);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Menu Items
|
||||||
|
|
||||||
|
[MenuItem("AppleHills/Custom Log Console")]
|
||||||
|
public static void ShowWindow()
|
||||||
|
{
|
||||||
|
var window = GetWindow<CustomLogWindow>("Custom Log");
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Unity Lifecycle
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
// Subscribe to new log events
|
||||||
|
Logging.OnLogEntryAdded += OnLogAdded;
|
||||||
|
|
||||||
|
// Load existing logs
|
||||||
|
allLogs.AddRange(Logging.GetRecentLogs());
|
||||||
|
|
||||||
|
// Build initial tag lists
|
||||||
|
RebuildTagLists();
|
||||||
|
|
||||||
|
// Initialize time range
|
||||||
|
UpdateTimeRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
Logging.OnLogEntryAdded -= OnLogAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLogAdded(LogEntry entry)
|
||||||
|
{
|
||||||
|
allLogs.Add(entry);
|
||||||
|
|
||||||
|
// Update active tags
|
||||||
|
activeClassTags.Add(entry.ClassName);
|
||||||
|
activeMethodTags.Add(entry.MethodName);
|
||||||
|
|
||||||
|
// Update time range
|
||||||
|
UpdateTimeRange();
|
||||||
|
|
||||||
|
if (autoScroll)
|
||||||
|
scrollPosition.y = float.MaxValue;
|
||||||
|
|
||||||
|
Repaint(); // Redraw window
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GUI Drawing
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
DrawToolbar();
|
||||||
|
DrawLogEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawToolbar()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
|
|
||||||
|
// Clear button
|
||||||
|
if (GUILayout.Button("Clear", EditorStyles.toolbarButton, GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
allLogs.Clear();
|
||||||
|
activeClassTags.Clear();
|
||||||
|
activeMethodTags.Clear();
|
||||||
|
Logging.ClearLogs();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-scroll toggle
|
||||||
|
autoScroll = GUILayout.Toggle(autoScroll, "Auto-scroll", EditorStyles.toolbarButton, GUILayout.Width(80));
|
||||||
|
|
||||||
|
GUILayout.Space(10);
|
||||||
|
|
||||||
|
// Class Filter Button
|
||||||
|
string classLabel = selectedClassFilters.Count == 0 ? "Classes: All" :
|
||||||
|
selectedClassFilters.Count == activeClassTags.Count ? "Classes: All" :
|
||||||
|
$"Classes: {selectedClassFilters.Count}";
|
||||||
|
if (GUILayout.Button(new GUIContent(classLabel, "Filter by class"), EditorStyles.toolbarDropDown, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
ShowClassFilterMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method Filter Button
|
||||||
|
string methodLabel = selectedMethodFilters.Count == 0 ? "Methods: All" :
|
||||||
|
selectedMethodFilters.Count == activeMethodTags.Count ? "Methods: All" :
|
||||||
|
$"Methods: {selectedMethodFilters.Count}";
|
||||||
|
if (GUILayout.Button(new GUIContent(methodLabel, "Filter by method"), EditorStyles.toolbarDropDown, GUILayout.Width(110)))
|
||||||
|
{
|
||||||
|
ShowMethodFilterMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log Level Filter Button
|
||||||
|
string levelLabel = selectedLevelFilters.Count == 4 ? "Levels: All" :
|
||||||
|
selectedLevelFilters.Count == 0 ? "Levels: None" :
|
||||||
|
$"Levels: {selectedLevelFilters.Count}";
|
||||||
|
if (GUILayout.Button(new GUIContent(levelLabel, "Filter by log level"), EditorStyles.toolbarDropDown, GUILayout.Width(90)))
|
||||||
|
{
|
||||||
|
ShowLevelFilterMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time Range Filter Button
|
||||||
|
if (GUILayout.Button(new GUIContent("⏱", "Time range filter"), EditorStyles.toolbarButton, GUILayout.Width(25)))
|
||||||
|
{
|
||||||
|
ShowTimeRangeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
// Search box
|
||||||
|
GUILayout.Label("Search:", GUILayout.Width(50));
|
||||||
|
searchText = GUILayout.TextField(searchText, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||||
|
|
||||||
|
GUILayout.Space(10);
|
||||||
|
|
||||||
|
// Export button
|
||||||
|
if (GUILayout.Button("Export", EditorStyles.toolbarButton, GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
ExportLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Space(5);
|
||||||
|
|
||||||
|
// Log count
|
||||||
|
var filteredCount = GetFilteredLogs().Count();
|
||||||
|
GUILayout.Label($"{filteredCount}/{allLogs.Count}", GUILayout.Width(80));
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowClassFilterMenu()
|
||||||
|
{
|
||||||
|
ClassFilterWindow.ShowWindow(this, activeClassTags, selectedClassFilters,
|
||||||
|
(newSelection) =>
|
||||||
|
{
|
||||||
|
selectedClassFilters = newSelection;
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowMethodFilterMenu()
|
||||||
|
{
|
||||||
|
MethodFilterWindow.ShowWindow(this, activeMethodTags, selectedMethodFilters,
|
||||||
|
(newSelection) =>
|
||||||
|
{
|
||||||
|
selectedMethodFilters = newSelection;
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowLevelFilterMenu()
|
||||||
|
{
|
||||||
|
LevelFilterWindow.ShowWindow(this, selectedLevelFilters,
|
||||||
|
(newSelection) =>
|
||||||
|
{
|
||||||
|
selectedLevelFilters = newSelection;
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowTimeRangeWindow()
|
||||||
|
{
|
||||||
|
TimeRangeFilterWindow.ShowWindow(this, enableTimeFilter, minTimestamp, maxTimestamp, currentMaxTimestamp,
|
||||||
|
(enabled, min, max) =>
|
||||||
|
{
|
||||||
|
enableTimeFilter = enabled;
|
||||||
|
minTimestamp = min;
|
||||||
|
maxTimestamp = max;
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawLogEntries()
|
||||||
|
{
|
||||||
|
// PERFORMANCE NOTE: For 10,000+ logs, consider virtualized scrolling here.
|
||||||
|
// Only render entries visible within the scroll view rect.
|
||||||
|
// Current implementation renders all filtered logs which may cause GUI slowdown.
|
||||||
|
|
||||||
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||||
|
|
||||||
|
var filteredLogs = GetFilteredLogs().ToList();
|
||||||
|
|
||||||
|
for (int i = 0; i < filteredLogs.Count; i++)
|
||||||
|
{
|
||||||
|
DrawLogEntry(filteredLogs[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawLogEntry(LogEntry entry, int index)
|
||||||
|
{
|
||||||
|
// Draw alternating row background
|
||||||
|
Color bgColor = (index % 2 == 0) ? evenRowColor : oddRowColor;
|
||||||
|
Rect rowRect = EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUI.DrawRect(rowRect, bgColor);
|
||||||
|
|
||||||
|
// Color-code by log level
|
||||||
|
Color color = GetColorForLevel(entry.Level);
|
||||||
|
var originalColor = GUI.contentColor;
|
||||||
|
GUI.contentColor = color;
|
||||||
|
|
||||||
|
// Timestamp
|
||||||
|
GUILayout.Label($"[{entry.Timestamp:F2}s]", GUILayout.Width(80));
|
||||||
|
|
||||||
|
// Level
|
||||||
|
GUILayout.Label($"[{entry.Level}]", GUILayout.Width(70));
|
||||||
|
|
||||||
|
// Class
|
||||||
|
GUILayout.Label($"[{entry.ClassName}]", GUILayout.Width(150));
|
||||||
|
|
||||||
|
// Method
|
||||||
|
GUILayout.Label($"[{entry.MethodName}]", GUILayout.Width(150));
|
||||||
|
|
||||||
|
// Message
|
||||||
|
GUILayout.Label(entry.Message, GUILayout.ExpandWidth(true));
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
GUI.contentColor = originalColor;
|
||||||
|
|
||||||
|
// Right-click context menu
|
||||||
|
if (Event.current.type == EventType.ContextClick && rowRect.Contains(Event.current.mousePosition))
|
||||||
|
{
|
||||||
|
GenericMenu menu = new GenericMenu();
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Copy Full Message"), false, () =>
|
||||||
|
{
|
||||||
|
EditorGUIUtility.systemCopyBuffer = entry.FullFormattedMessage;
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Copy Message Only"), false, () =>
|
||||||
|
{
|
||||||
|
EditorGUIUtility.systemCopyBuffer = entry.Message;
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.AddSeparator("");
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Filter to this Class"), false, () =>
|
||||||
|
{
|
||||||
|
selectedClassFilters.Clear();
|
||||||
|
selectedClassFilters.Add(entry.ClassName);
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Filter to this Method"), false, () =>
|
||||||
|
{
|
||||||
|
selectedMethodFilters.Clear();
|
||||||
|
selectedMethodFilters.Add(entry.MethodName);
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Filter to this Class + Method"), false, () =>
|
||||||
|
{
|
||||||
|
selectedClassFilters.Clear();
|
||||||
|
selectedClassFilters.Add(entry.ClassName);
|
||||||
|
selectedMethodFilters.Clear();
|
||||||
|
selectedMethodFilters.Add(entry.MethodName);
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.AddSeparator("");
|
||||||
|
|
||||||
|
menu.AddItem(new GUIContent("Filter to this Log Level"), false, () =>
|
||||||
|
{
|
||||||
|
selectedLevelFilters.Clear();
|
||||||
|
selectedLevelFilters.Add(entry.Level);
|
||||||
|
Repaint();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.ShowAsContext();
|
||||||
|
Event.current.Use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Filtering Logic
|
||||||
|
|
||||||
|
private IEnumerable<LogEntry> GetFilteredLogs()
|
||||||
|
{
|
||||||
|
return allLogs.Where(entry =>
|
||||||
|
{
|
||||||
|
// Filter by class (if any selected)
|
||||||
|
if (selectedClassFilters.Count > 0 && !selectedClassFilters.Contains(entry.ClassName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Filter by method (if any selected)
|
||||||
|
if (selectedMethodFilters.Count > 0 && !selectedMethodFilters.Contains(entry.MethodName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Filter by log level
|
||||||
|
if (!selectedLevelFilters.Contains(entry.Level))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Filter by time range
|
||||||
|
if (enableTimeFilter)
|
||||||
|
{
|
||||||
|
if (entry.Timestamp < minTimestamp || entry.Timestamp > maxTimestamp)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by search text
|
||||||
|
if (!string.IsNullOrEmpty(searchText))
|
||||||
|
{
|
||||||
|
bool matchesSearch =
|
||||||
|
entry.ClassName.Contains(searchText, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
entry.MethodName.Contains(searchText, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
entry.Message.Contains(searchText, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (!matchesSearch)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
private Color GetColorForLevel(LogLevel level)
|
||||||
|
{
|
||||||
|
return level switch
|
||||||
|
{
|
||||||
|
LogLevel.Debug => debugColor,
|
||||||
|
LogLevel.Info => infoColor,
|
||||||
|
LogLevel.Warning => warningColor,
|
||||||
|
LogLevel.Error => errorColor,
|
||||||
|
_ => infoColor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RebuildTagLists()
|
||||||
|
{
|
||||||
|
activeClassTags.Clear();
|
||||||
|
activeMethodTags.Clear();
|
||||||
|
|
||||||
|
foreach (var log in allLogs)
|
||||||
|
{
|
||||||
|
activeClassTags.Add(log.ClassName);
|
||||||
|
activeMethodTags.Add(log.MethodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTimeRange()
|
||||||
|
{
|
||||||
|
if (allLogs.Count > 0)
|
||||||
|
{
|
||||||
|
currentMaxTimestamp = allLogs.Max(l => l.Timestamp);
|
||||||
|
|
||||||
|
// Initialize time range on first log
|
||||||
|
if (maxTimestamp == 0)
|
||||||
|
{
|
||||||
|
maxTimestamp = currentMaxTimestamp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Auto-expand max range as new logs come in
|
||||||
|
maxTimestamp = currentMaxTimestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExportLogs()
|
||||||
|
{
|
||||||
|
string defaultFileName = $"logs_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt";
|
||||||
|
string path = EditorUtility.SaveFilePanel("Export Logs", "", defaultFileName, "txt");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filteredLogs = GetFilteredLogs().ToList();
|
||||||
|
var lines = filteredLogs.Select(e => e.FullFormattedMessage);
|
||||||
|
File.WriteAllLines(path, lines);
|
||||||
|
|
||||||
|
EditorUtility.DisplayDialog("Export Successful",
|
||||||
|
$"Exported {filteredLogs.Count} log entries to:\n{path}", "OK");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog("Export Failed",
|
||||||
|
$"Failed to export logs:\n{ex.Message}", "OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persistent popup window for class filtering with multi-selection support
|
||||||
|
/// </summary>
|
||||||
|
public class ClassFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private HashSet<string> availableTags;
|
||||||
|
private HashSet<string> selectedTags;
|
||||||
|
private System.Action<HashSet<string>> onApply;
|
||||||
|
private Vector2 scrollPosition;
|
||||||
|
private string searchFilter = "";
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||||
|
System.Action<HashSet<string>> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<ClassFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Class Filter");
|
||||||
|
window.availableTags = available;
|
||||||
|
window.selectedTags = new HashSet<string>(selected);
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
if (availableTags != null)
|
||||||
|
selectedTags = new HashSet<string>(availableTags);
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedTags.Clear();
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Scrollable list of toggles
|
||||||
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||||
|
|
||||||
|
if (availableTags != null && availableTags.Count > 0)
|
||||||
|
{
|
||||||
|
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||||
|
? availableTags.OrderBy(t => t)
|
||||||
|
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||||
|
|
||||||
|
foreach (var tag in filteredTags)
|
||||||
|
{
|
||||||
|
bool isSelected = selectedTags.Contains(tag);
|
||||||
|
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||||
|
|
||||||
|
if (newSelection != isSelected)
|
||||||
|
{
|
||||||
|
if (newSelection)
|
||||||
|
selectedTags.Add(tag);
|
||||||
|
else
|
||||||
|
selectedTags.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No classes available", MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Apply/Close buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(selectedTags);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persistent popup window for method filtering with multi-selection support
|
||||||
|
/// </summary>
|
||||||
|
public class MethodFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private HashSet<string> availableTags;
|
||||||
|
private HashSet<string> selectedTags;
|
||||||
|
private System.Action<HashSet<string>> onApply;
|
||||||
|
private Vector2 scrollPosition;
|
||||||
|
private string searchFilter = "";
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||||
|
System.Action<HashSet<string>> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<MethodFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Method Filter");
|
||||||
|
window.availableTags = available;
|
||||||
|
window.selectedTags = new HashSet<string>(selected);
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
if (availableTags != null)
|
||||||
|
selectedTags = new HashSet<string>(availableTags);
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedTags.Clear();
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Scrollable list of toggles
|
||||||
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||||
|
|
||||||
|
if (availableTags != null && availableTags.Count > 0)
|
||||||
|
{
|
||||||
|
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||||
|
? availableTags.OrderBy(t => t)
|
||||||
|
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||||
|
|
||||||
|
foreach (var tag in filteredTags)
|
||||||
|
{
|
||||||
|
bool isSelected = selectedTags.Contains(tag);
|
||||||
|
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||||
|
|
||||||
|
if (newSelection != isSelected)
|
||||||
|
{
|
||||||
|
if (newSelection)
|
||||||
|
selectedTags.Add(tag);
|
||||||
|
else
|
||||||
|
selectedTags.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No methods available", MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Apply/Close buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(selectedTags);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persistent popup window for log level filtering with multi-selection support
|
||||||
|
/// </summary>
|
||||||
|
public class LevelFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private HashSet<LogLevel> selectedLevels;
|
||||||
|
private System.Action<HashSet<LogLevel>> onApply;
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, HashSet<LogLevel> selected,
|
||||||
|
System.Action<HashSet<LogLevel>> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<LevelFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Log Level Filter");
|
||||||
|
window.selectedLevels = new HashSet<LogLevel>(selected);
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 250, 180);
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedLevels = new HashSet<LogLevel>
|
||||||
|
{
|
||||||
|
LogLevel.Debug, LogLevel.Info, LogLevel.Warning, LogLevel.Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedLevels.Clear();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
// Log level toggles
|
||||||
|
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
||||||
|
{
|
||||||
|
bool isSelected = selectedLevels.Contains(level);
|
||||||
|
bool newSelection = EditorGUILayout.ToggleLeft(level.ToString(), isSelected);
|
||||||
|
|
||||||
|
if (newSelection != isSelected)
|
||||||
|
{
|
||||||
|
if (newSelection)
|
||||||
|
selectedLevels.Add(level);
|
||||||
|
else
|
||||||
|
selectedLevels.Remove(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
// Apply/Close buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(selectedLevels);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Popup window for configuring time range filters
|
||||||
|
/// </summary>
|
||||||
|
public class TimeRangeFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private bool enableTimeFilter;
|
||||||
|
private float minTimestamp;
|
||||||
|
private float maxTimestamp;
|
||||||
|
private float currentMaxTimestamp;
|
||||||
|
private System.Action<bool, float, float> onApply;
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, bool enabled, float min, float max, float currentMax,
|
||||||
|
System.Action<bool, float, float> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<TimeRangeFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Time Range Filter");
|
||||||
|
window.enableTimeFilter = enabled;
|
||||||
|
window.minTimestamp = min;
|
||||||
|
window.maxTimestamp = max;
|
||||||
|
window.currentMaxTimestamp = currentMax;
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
// Position near the parent window
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 100, parentRect.y + 100, 350, 120);
|
||||||
|
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
enableTimeFilter = EditorGUILayout.Toggle("Enable Time Filter", enableTimeFilter);
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
if (enableTimeFilter && currentMaxTimestamp > 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField($"Min: {minTimestamp:F2}s", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.LabelField($"Max: {maxTimestamp:F2}s", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.MinMaxSlider(
|
||||||
|
ref minTimestamp,
|
||||||
|
ref maxTimestamp,
|
||||||
|
0,
|
||||||
|
currentMaxTimestamp);
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Reset Range"))
|
||||||
|
{
|
||||||
|
minTimestamp = 0;
|
||||||
|
maxTimestamp = currentMaxTimestamp;
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
else if (enableTimeFilter)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No logs available for time filtering", MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(enableTimeFilter, minTimestamp, maxTimestamp);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Cancel", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Assets/Editor/CustomLogWindow.cs.meta
Normal file
3
Assets/Editor/CustomLogWindow.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 60a300585fef4ac990c963c8b37c3887
|
||||||
|
timeCreated: 1762814971
|
||||||
@@ -181,7 +181,7 @@ namespace Editor
|
|||||||
string scenePath = AssetDatabase.GUIDToAssetPath(guid);
|
string scenePath = AssetDatabase.GUIDToAssetPath(guid);
|
||||||
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
|
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
|
||||||
|
|
||||||
var allComponents = GameObject.FindObjectsOfType<Component>(true);
|
var allComponents = GameObject.FindObjectsByType<Component>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||||
foreach (var component in allComponents)
|
foreach (var component in allComponents)
|
||||||
{
|
{
|
||||||
if (component == null || component.gameObject.scene != scene) continue;
|
if (component == null || component.gameObject.scene != scene) continue;
|
||||||
|
|||||||
3
Assets/Editor/InteractionSystem.meta
Normal file
3
Assets/Editor/InteractionSystem.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cb41c852d70c4066bf510792ee19b3f5
|
||||||
|
timeCreated: 1762866335
|
||||||
748
Assets/Editor/InteractionSystem/InteractableEditorWindow.cs
Normal file
748
Assets/Editor/InteractionSystem/InteractableEditorWindow.cs
Normal file
@@ -0,0 +1,748 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Interactions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AppleHills.Editor.InteractionSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Editor utility for managing and debugging interactable objects in the scene.
|
||||||
|
/// Provides scene object locator, inspector editing, and runtime debugging capabilities.
|
||||||
|
/// </summary>
|
||||||
|
public class InteractableEditorWindow : EditorWindow
|
||||||
|
{
|
||||||
|
// Tab management
|
||||||
|
private int _selectedTab = 0;
|
||||||
|
private readonly string[] _tabNames = { "Scene", "Debug" };
|
||||||
|
|
||||||
|
// Scene interactables tracking
|
||||||
|
private List<InteractableBase> _sceneInteractables = new List<InteractableBase>();
|
||||||
|
private InteractableBase _selectedInteractable;
|
||||||
|
private GameObject _selectedGameObject;
|
||||||
|
|
||||||
|
// UI state
|
||||||
|
private Vector2 _listScrollPosition;
|
||||||
|
private Vector2 _inspectorScrollPosition;
|
||||||
|
private Vector2 _debugScrollPosition;
|
||||||
|
private string _searchQuery = "";
|
||||||
|
|
||||||
|
// Runtime state
|
||||||
|
private bool _isPlaying = false;
|
||||||
|
|
||||||
|
// Editor for selected interactable
|
||||||
|
private UnityEditor.Editor _cachedEditor;
|
||||||
|
|
||||||
|
// Available interactable types for adding
|
||||||
|
private static readonly Type[] AvailableInteractableTypes = new Type[]
|
||||||
|
{
|
||||||
|
typeof(OneClickInteraction),
|
||||||
|
typeof(Pickup),
|
||||||
|
typeof(ItemSlot),
|
||||||
|
typeof(SaveableInteractable),
|
||||||
|
typeof(InteractableBase)
|
||||||
|
};
|
||||||
|
|
||||||
|
[MenuItem("AppleHills/Interactable Editor")]
|
||||||
|
public static void ShowWindow()
|
||||||
|
{
|
||||||
|
var window = GetWindow<InteractableEditorWindow>("Interactable Editor");
|
||||||
|
window.minSize = new Vector2(900, 600);
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
|
||||||
|
// Register for scene and selection changes
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += OnSceneOpened;
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.sceneClosed += OnSceneClosed;
|
||||||
|
Selection.selectionChanged += OnSelectionChanged;
|
||||||
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||||
|
EditorApplication.hierarchyChanged += OnHierarchyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened -= OnSceneOpened;
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.sceneClosed -= OnSceneClosed;
|
||||||
|
Selection.selectionChanged -= OnSelectionChanged;
|
||||||
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||||
|
EditorApplication.hierarchyChanged -= OnHierarchyChanged;
|
||||||
|
|
||||||
|
// Clean up cached editor
|
||||||
|
if (_cachedEditor != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(_cachedEditor);
|
||||||
|
_cachedEditor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneOpened(UnityEngine.SceneManagement.Scene scene, UnityEditor.SceneManagement.OpenSceneMode mode)
|
||||||
|
{
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSceneClosed(UnityEngine.SceneManagement.Scene scene)
|
||||||
|
{
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHierarchyChanged()
|
||||||
|
{
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectionChanged()
|
||||||
|
{
|
||||||
|
// Check if selected object has changed
|
||||||
|
if (Selection.activeGameObject != null && Selection.activeGameObject != _selectedGameObject)
|
||||||
|
{
|
||||||
|
var interactable = Selection.activeGameObject.GetComponent<InteractableBase>();
|
||||||
|
if (interactable != null)
|
||||||
|
{
|
||||||
|
// GameObject has an interactable - select it
|
||||||
|
SelectInteractable(interactable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// GameObject doesn't have an interactable - track it for add menu
|
||||||
|
_selectedGameObject = Selection.activeGameObject;
|
||||||
|
_selectedInteractable = null;
|
||||||
|
|
||||||
|
// Clear cached editor
|
||||||
|
if (_cachedEditor != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(_cachedEditor);
|
||||||
|
_cachedEditor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
else if (Selection.activeGameObject == null)
|
||||||
|
{
|
||||||
|
// Nothing selected - clear selection
|
||||||
|
_selectedGameObject = null;
|
||||||
|
_selectedInteractable = null;
|
||||||
|
|
||||||
|
if (_cachedEditor != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(_cachedEditor);
|
||||||
|
_cachedEditor = null;
|
||||||
|
}
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||||
|
{
|
||||||
|
_isPlaying = EditorApplication.isPlaying;
|
||||||
|
if (_isPlaying)
|
||||||
|
{
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
}
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
DrawHeader();
|
||||||
|
|
||||||
|
_selectedTab = GUILayout.Toolbar(_selectedTab, _tabNames);
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
switch (_selectedTab)
|
||||||
|
{
|
||||||
|
case 0: // Scene tab
|
||||||
|
DrawSceneTab();
|
||||||
|
break;
|
||||||
|
case 1: // Debug tab
|
||||||
|
DrawDebugTab();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Header UI
|
||||||
|
|
||||||
|
private void DrawHeader()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
|
|
||||||
|
if (GUILayout.Button("Refresh", EditorStyles.toolbarButton, GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
// Tab-specific toolbar options
|
||||||
|
if (_selectedTab == 0) // Scene tab
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField($"Found: {_sceneInteractables.Count} interactables", EditorStyles.toolbarButton, GUILayout.Width(150));
|
||||||
|
}
|
||||||
|
else if (_selectedTab == 1) // Debug tab
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField(_isPlaying ? "Runtime Active" : "Editor Mode", EditorStyles.toolbarButton, GUILayout.Width(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Scene Tab
|
||||||
|
|
||||||
|
private void DrawSceneTab()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
// Left panel - interactable list
|
||||||
|
EditorGUILayout.BeginVertical(GUILayout.Width(300));
|
||||||
|
DrawInteractableListPanel();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Right panel - inspector/editor
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
DrawInspectorPanel();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawInteractableListPanel()
|
||||||
|
{
|
||||||
|
// Search field
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
|
_searchQuery = EditorGUILayout.TextField(_searchQuery, EditorStyles.toolbarSearchField);
|
||||||
|
if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(20)) && !string.IsNullOrEmpty(_searchQuery))
|
||||||
|
{
|
||||||
|
_searchQuery = "";
|
||||||
|
GUI.FocusControl(null);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
_listScrollPosition = EditorGUILayout.BeginScrollView(_listScrollPosition);
|
||||||
|
|
||||||
|
// Filter interactables by search query
|
||||||
|
var filteredInteractables = string.IsNullOrEmpty(_searchQuery)
|
||||||
|
? _sceneInteractables
|
||||||
|
: _sceneInteractables.Where(i => i != null && i.gameObject.name.ToLower().Contains(_searchQuery.ToLower())).ToList();
|
||||||
|
|
||||||
|
if (filteredInteractables.Count == 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No interactables found in scene", MessageType.Info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var interactable in filteredInteractables)
|
||||||
|
{
|
||||||
|
if (interactable == null) continue;
|
||||||
|
|
||||||
|
if (DrawInteractableListItem(interactable))
|
||||||
|
{
|
||||||
|
SelectInteractable(interactable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DrawInteractableListItem(InteractableBase interactable)
|
||||||
|
{
|
||||||
|
bool isSelected = interactable == _selectedInteractable;
|
||||||
|
|
||||||
|
Color originalColor = GUI.backgroundColor;
|
||||||
|
if (isSelected)
|
||||||
|
GUI.backgroundColor = new Color(0.3f, 0.5f, 0.8f);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
GUI.backgroundColor = originalColor;
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
// Interactable info
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
EditorGUILayout.LabelField(interactable.gameObject.name, EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.LabelField(interactable.GetType().Name, EditorStyles.miniLabel);
|
||||||
|
|
||||||
|
// Show additional info for specific types
|
||||||
|
if (interactable is Pickup pickup && pickup.itemData != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField($"Item: {pickup.itemData.itemName}", EditorStyles.miniLabel);
|
||||||
|
}
|
||||||
|
else if (interactable is ItemSlot slot && slot.itemData != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField($"Slot: {slot.itemData.itemName}", EditorStyles.miniLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
// Ping button
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(interactable.gameObject);
|
||||||
|
Selection.activeGameObject = interactable.gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
Rect itemRect = GUILayoutUtility.GetLastRect();
|
||||||
|
bool wasClicked = Event.current.type == EventType.MouseDown && Event.current.button == 0
|
||||||
|
&& itemRect.Contains(Event.current.mousePosition);
|
||||||
|
|
||||||
|
if (wasClicked)
|
||||||
|
{
|
||||||
|
Event.current.Use();
|
||||||
|
}
|
||||||
|
|
||||||
|
return wasClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawInspectorPanel()
|
||||||
|
{
|
||||||
|
_inspectorScrollPosition = EditorGUILayout.BeginScrollView(_inspectorScrollPosition);
|
||||||
|
|
||||||
|
if (_selectedInteractable == null && _selectedGameObject == null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Select an interactable from the list or in the scene hierarchy", MessageType.Info);
|
||||||
|
}
|
||||||
|
else if (_selectedGameObject != null && _selectedInteractable == null)
|
||||||
|
{
|
||||||
|
// Selected object doesn't have an interactable - show add menu
|
||||||
|
DrawAddInteractableMenu();
|
||||||
|
}
|
||||||
|
else if (_selectedInteractable != null)
|
||||||
|
{
|
||||||
|
// Draw custom inspector for the selected interactable
|
||||||
|
DrawInteractableInspector();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAddInteractableMenu()
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox($"GameObject '{_selectedGameObject.name}' doesn't have an Interactable component", MessageType.Info);
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.LabelField("Add Interactable Component:", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
foreach (var interactableType in AvailableInteractableTypes)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button($"Add {interactableType.Name}", GUILayout.Height(30)))
|
||||||
|
{
|
||||||
|
Undo.RecordObject(_selectedGameObject, $"Add {interactableType.Name}");
|
||||||
|
var component = _selectedGameObject.AddComponent(interactableType) as InteractableBase;
|
||||||
|
EditorUtility.SetDirty(_selectedGameObject);
|
||||||
|
SelectInteractable(component);
|
||||||
|
RefreshSceneInteractables();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawInteractableInspector()
|
||||||
|
{
|
||||||
|
// Header
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Editing:", EditorStyles.boldLabel, GUILayout.Width(60));
|
||||||
|
EditorGUILayout.LabelField(_selectedInteractable.gameObject.name, EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(_selectedInteractable.gameObject);
|
||||||
|
Selection.activeGameObject = _selectedInteractable.gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Draw default inspector using Editor
|
||||||
|
if (_cachedEditor == null || _cachedEditor.target != _selectedInteractable)
|
||||||
|
{
|
||||||
|
if (_cachedEditor != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(_cachedEditor);
|
||||||
|
}
|
||||||
|
_cachedEditor = UnityEditor.Editor.CreateEditor(_selectedInteractable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedEditor != null)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
_cachedEditor.OnInspectorGUI();
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
EditorUtility.SetDirty(_selectedInteractable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Additional info section
|
||||||
|
DrawAdditionalInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawAdditionalInfo()
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Additional Information", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
|
||||||
|
// Show component information
|
||||||
|
EditorGUILayout.LabelField("Type:", _selectedInteractable.GetType().Name);
|
||||||
|
|
||||||
|
// Show attached actions
|
||||||
|
var actions = _selectedInteractable.GetComponents<InteractionActionBase>();
|
||||||
|
if (actions.Length > 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField($"Actions: {actions.Length}");
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(action.GetType().Name, EditorStyles.miniLabel);
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(action);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show specific type info
|
||||||
|
if (_selectedInteractable is Pickup pickup)
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.LabelField("Pickup Info:", EditorStyles.boldLabel);
|
||||||
|
if (pickup.itemData != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Item Data:", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.ObjectField(pickup.itemData, typeof(PickupItemData), false);
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(pickup.itemData);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_selectedInteractable is ItemSlot slot)
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.LabelField("Slot Info:", EditorStyles.boldLabel);
|
||||||
|
if (slot.itemData != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Slot Data:", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.ObjectField(slot.itemData, typeof(PickupItemData), false);
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(slot.itemData);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isPlaying)
|
||||||
|
{
|
||||||
|
var slottedObject = slot.GetSlottedObject();
|
||||||
|
if (slottedObject != null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Slotted Object:", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.ObjectField(slottedObject, typeof(GameObject), true);
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(slottedObject);
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Debug Tab
|
||||||
|
|
||||||
|
private void DrawDebugTab()
|
||||||
|
{
|
||||||
|
if (!_isPlaying)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("Enter Play Mode to debug interactables at runtime", MessageType.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_debugScrollPosition = EditorGUILayout.BeginScrollView(_debugScrollPosition);
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Scene Interactables", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.HelpBox("Test interactions and trigger individual events", MessageType.Info);
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
foreach (var interactable in _sceneInteractables)
|
||||||
|
{
|
||||||
|
if (interactable == null) continue;
|
||||||
|
|
||||||
|
DrawDebugInteractableItem(interactable);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawDebugInteractableItem(InteractableBase interactable)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(interactable.gameObject.name, EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.LabelField($"({interactable.GetType().Name})", EditorStyles.miniLabel);
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Ping", GUILayout.Width(50)))
|
||||||
|
{
|
||||||
|
EditorGUIUtility.PingObject(interactable.gameObject);
|
||||||
|
Selection.activeGameObject = interactable.gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Interaction buttons
|
||||||
|
EditorGUILayout.LabelField("Trigger Interaction:", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Full Interaction", GUILayout.Height(25)))
|
||||||
|
{
|
||||||
|
TriggerFullInteraction(interactable);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
// Event buttons
|
||||||
|
EditorGUILayout.LabelField("Trigger Individual Events:", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Started"))
|
||||||
|
{
|
||||||
|
TriggerEvent(interactable, "OnInteractionStarted");
|
||||||
|
TriggerUnityEvent(interactable, "interactionStarted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Arrived"))
|
||||||
|
{
|
||||||
|
TriggerEvent(interactable, "OnInteractingCharacterArrived");
|
||||||
|
TriggerUnityEvent(interactable, "characterArrived");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Do Interaction"))
|
||||||
|
{
|
||||||
|
TriggerEvent(interactable, "DoInteraction");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Complete (Success)"))
|
||||||
|
{
|
||||||
|
TriggerEventWithParam(interactable, "OnInteractionFinished", true);
|
||||||
|
TriggerUnityEventWithParam(interactable, "interactionComplete", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Complete (Fail)"))
|
||||||
|
{
|
||||||
|
TriggerEventWithParam(interactable, "OnInteractionFinished", false);
|
||||||
|
TriggerUnityEventWithParam(interactable, "interactionComplete", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Interrupted"))
|
||||||
|
{
|
||||||
|
TriggerUnityEvent(interactable, "interactionInterrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
// Show registered actions
|
||||||
|
var actions = interactable.GetComponents<InteractionActionBase>();
|
||||||
|
if (actions.Length > 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.LabelField($"Registered Actions ({actions.Length}):", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
|
||||||
|
EditorGUILayout.LabelField(action.GetType().Name);
|
||||||
|
|
||||||
|
if (action.respondToEvents != null && action.respondToEvents.Count > 0)
|
||||||
|
{
|
||||||
|
string events = string.Join(", ", action.respondToEvents);
|
||||||
|
EditorGUILayout.LabelField($"Events: {events}", EditorStyles.miniLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data Management
|
||||||
|
|
||||||
|
private void RefreshSceneInteractables()
|
||||||
|
{
|
||||||
|
_sceneInteractables.Clear();
|
||||||
|
|
||||||
|
// Find all interactables in the scene
|
||||||
|
var allInteractables = FindObjectsByType<InteractableBase>(FindObjectsSortMode.None);
|
||||||
|
_sceneInteractables.AddRange(allInteractables);
|
||||||
|
|
||||||
|
// Sort by name for easier browsing
|
||||||
|
_sceneInteractables.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
if (a == null || b == null) return 0;
|
||||||
|
return string.Compare(a.gameObject.name, b.gameObject.name, StringComparison.Ordinal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectInteractable(InteractableBase interactable)
|
||||||
|
{
|
||||||
|
_selectedInteractable = interactable;
|
||||||
|
_selectedGameObject = interactable?.gameObject;
|
||||||
|
|
||||||
|
// Clear cached editor to force recreation
|
||||||
|
if (_cachedEditor != null)
|
||||||
|
{
|
||||||
|
DestroyImmediate(_cachedEditor);
|
||||||
|
_cachedEditor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Debug Helpers
|
||||||
|
|
||||||
|
private void TriggerFullInteraction(InteractableBase interactable)
|
||||||
|
{
|
||||||
|
if (!_isPlaying || interactable == null) return;
|
||||||
|
|
||||||
|
// Simulate a tap on the interactable
|
||||||
|
Vector3 worldPos = interactable.transform.position;
|
||||||
|
interactable.OnTap(new Vector2(worldPos.x, worldPos.y));
|
||||||
|
|
||||||
|
Debug.Log($"[Interactable Editor] Triggered full interaction on {interactable.gameObject.name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerEvent(InteractableBase interactable, string methodName)
|
||||||
|
{
|
||||||
|
if (!_isPlaying || interactable == null) return;
|
||||||
|
|
||||||
|
Type type = interactable.GetType();
|
||||||
|
MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
method.Invoke(interactable, null);
|
||||||
|
Debug.Log($"[Interactable Editor] Invoked {methodName} on {interactable.gameObject.name}");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[Interactable Editor] Error invoking {methodName}: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[Interactable Editor] Method {methodName} not found on {type.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerEventWithParam(InteractableBase interactable, string methodName, object param)
|
||||||
|
{
|
||||||
|
if (!_isPlaying || interactable == null) return;
|
||||||
|
|
||||||
|
Type type = interactable.GetType();
|
||||||
|
MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
method.Invoke(interactable, new object[] { param });
|
||||||
|
Debug.Log($"[Interactable Editor] Invoked {methodName}({param}) on {interactable.gameObject.name}");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[Interactable Editor] Error invoking {methodName}: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[Interactable Editor] Method {methodName} not found on {type.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUnityEvent(InteractableBase interactable, string fieldName)
|
||||||
|
{
|
||||||
|
if (!_isPlaying || interactable == null) return;
|
||||||
|
|
||||||
|
Type type = interactable.GetType();
|
||||||
|
FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (field != null && field.GetValue(interactable) is UnityEngine.Events.UnityEventBase unityEvent)
|
||||||
|
{
|
||||||
|
// Use reflection to invoke the protected Invoke method
|
||||||
|
MethodInfo invokeMethod = unityEvent.GetType().GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
if (invokeMethod != null)
|
||||||
|
{
|
||||||
|
invokeMethod.Invoke(unityEvent, null);
|
||||||
|
Debug.Log($"[Interactable Editor] Invoked UnityEvent {fieldName} on {interactable.gameObject.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerUnityEventWithParam(InteractableBase interactable, string fieldName, bool param)
|
||||||
|
{
|
||||||
|
if (!_isPlaying || interactable == null) return;
|
||||||
|
|
||||||
|
Type type = interactable.GetType();
|
||||||
|
FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (field != null && field.GetValue(interactable) is UnityEngine.Events.UnityEvent<bool> unityEvent)
|
||||||
|
{
|
||||||
|
unityEvent.Invoke(param);
|
||||||
|
Debug.Log($"[Interactable Editor] Invoked UnityEvent<bool> {fieldName}({param}) on {interactable.gameObject.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3045d5bcf3e04203bfe060f80d8913ca
|
||||||
|
timeCreated: 1762866335
|
||||||
@@ -12,7 +12,7 @@ namespace Editor.Lifecycle
|
|||||||
/// Editor-only bootstrap that ensures OnSceneReady is triggered when playing directly from a scene in Unity Editor.
|
/// Editor-only bootstrap that ensures OnSceneReady is triggered when playing directly from a scene in Unity Editor.
|
||||||
///
|
///
|
||||||
/// PROBLEM: When you press Play in the editor without going through the scene manager:
|
/// PROBLEM: When you press Play in the editor without going through the scene manager:
|
||||||
/// - CustomBoot runs and triggers OnBootCompletionTriggered (which broadcasts OnManagedAwake)
|
/// - CustomBoot runs and triggers OnBootCompletionTriggered (which broadcasts OnManagedStart)
|
||||||
/// - But BroadcastSceneReady is NEVER called for the initial scene
|
/// - But BroadcastSceneReady is NEVER called for the initial scene
|
||||||
/// - Components in the scene never receive their OnSceneReady() callback
|
/// - Components in the scene never receive their OnSceneReady() callback
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -560,6 +560,15 @@ namespace AppleHills.Editor.PuzzleSystem
|
|||||||
|
|
||||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
EditorGUILayout.LabelField($"Current Level: {_runtimeLevelData.levelId}", EditorStyles.boldLabel);
|
EditorGUILayout.LabelField($"Current Level: {_runtimeLevelData.levelId}", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
// Unlock All button
|
||||||
|
if (GUILayout.Button("Unlock All", EditorStyles.toolbarButton, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
UnlockAllPuzzles();
|
||||||
|
}
|
||||||
|
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
_debugScrollPosition = EditorGUILayout.BeginScrollView(_debugScrollPosition);
|
_debugScrollPosition = EditorGUILayout.BeginScrollView(_debugScrollPosition);
|
||||||
@@ -870,6 +879,121 @@ namespace AppleHills.Editor.PuzzleSystem
|
|||||||
UpdateRuntimeData();
|
UpdateRuntimeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UnlockAllPuzzles()
|
||||||
|
{
|
||||||
|
if (!_isPlaying || _runtimeLevelData == null) return;
|
||||||
|
|
||||||
|
PuzzleManager puzzleManager = Object.FindFirstObjectByType<PuzzleManager>();
|
||||||
|
if (puzzleManager == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[Puzzle Editor] Cannot find PuzzleManager in scene");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("[Puzzle Editor] Starting to unlock all puzzles...");
|
||||||
|
|
||||||
|
// Get all steps from the level data
|
||||||
|
List<PuzzleStepSO> allSteps = new List<PuzzleStepSO>(_runtimeLevelData.allSteps);
|
||||||
|
|
||||||
|
// Track which steps we've processed
|
||||||
|
HashSet<string> processedSteps = new HashSet<string>();
|
||||||
|
bool madeProgress = true;
|
||||||
|
int maxIterations = 100; // Safety limit to prevent infinite loops
|
||||||
|
int iteration = 0;
|
||||||
|
|
||||||
|
// Keep iterating until no more steps can be unlocked/completed
|
||||||
|
while (madeProgress && iteration < maxIterations)
|
||||||
|
{
|
||||||
|
madeProgress = false;
|
||||||
|
iteration++;
|
||||||
|
|
||||||
|
foreach (var step in allSteps)
|
||||||
|
{
|
||||||
|
if (step == null || processedSteps.Contains(step.stepId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if already completed
|
||||||
|
if (puzzleManager.IsPuzzleStepCompleted(step.stepId))
|
||||||
|
{
|
||||||
|
processedSteps.Add(step.stepId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if step is unlocked or can be unlocked
|
||||||
|
bool isUnlocked = puzzleManager.IsStepUnlocked(step);
|
||||||
|
|
||||||
|
if (!isUnlocked)
|
||||||
|
{
|
||||||
|
// Try to unlock it if dependencies are met
|
||||||
|
// We need to check if all dependencies are completed
|
||||||
|
bool canUnlock = CanUnlockStep(step, puzzleManager);
|
||||||
|
|
||||||
|
if (canUnlock)
|
||||||
|
{
|
||||||
|
// Unlock the step using reflection
|
||||||
|
System.Type managerType = puzzleManager.GetType();
|
||||||
|
System.Reflection.MethodInfo unlockMethod = managerType.GetMethod("UnlockStep",
|
||||||
|
System.Reflection.BindingFlags.Instance |
|
||||||
|
System.Reflection.BindingFlags.Public |
|
||||||
|
System.Reflection.BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (unlockMethod != null)
|
||||||
|
{
|
||||||
|
unlockMethod.Invoke(puzzleManager, new object[] { step });
|
||||||
|
Debug.Log($"[Puzzle Editor] Unlocked step: {step.stepId}");
|
||||||
|
isUnlocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If unlocked, complete it
|
||||||
|
if (isUnlocked && !puzzleManager.IsPuzzleStepCompleted(step.stepId))
|
||||||
|
{
|
||||||
|
puzzleManager.MarkPuzzleStepCompleted(step);
|
||||||
|
Debug.Log($"[Puzzle Editor] Completed step: {step.stepId}");
|
||||||
|
processedSteps.Add(step.stepId);
|
||||||
|
madeProgress = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iteration >= maxIterations)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[Puzzle Editor] Reached maximum iterations ({maxIterations}). Some steps may not have been completed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"[Puzzle Editor] Unlock all complete. Processed {processedSteps.Count} steps in {iteration} iterations.");
|
||||||
|
|
||||||
|
// Update runtime data to reflect all changes
|
||||||
|
UpdateRuntimeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a step can be unlocked by verifying all its dependencies are completed
|
||||||
|
/// </summary>
|
||||||
|
private bool CanUnlockStep(PuzzleStepSO step, PuzzleManager puzzleManager)
|
||||||
|
{
|
||||||
|
if (step == null || _runtimeLevelData == null) return false;
|
||||||
|
|
||||||
|
// Initial steps can always be unlocked
|
||||||
|
if (_runtimeLevelData.IsInitialStep(step))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if all dependencies are completed
|
||||||
|
if (_runtimeLevelData.stepDependencies.TryGetValue(step.stepId, out string[] dependencies))
|
||||||
|
{
|
||||||
|
foreach (var depId in dependencies)
|
||||||
|
{
|
||||||
|
if (!puzzleManager.IsPuzzleStepCompleted(depId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using AppleHills.Core.Settings;
|
using AppleHills.Core.Settings;
|
||||||
using Core;
|
using Core;
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering.VirtualTexturing;
|
|
||||||
|
|
||||||
namespace AppleHills.Editor
|
namespace AppleHills.Editor
|
||||||
{
|
{
|
||||||
@@ -65,7 +63,7 @@ namespace AppleHills.Editor
|
|||||||
GetPuzzlePromptRange
|
GetPuzzlePromptRange
|
||||||
);
|
);
|
||||||
|
|
||||||
LogDebugMessage("Editor settings loaded for Scene View use");
|
Logging.Debug("Editor settings loaded for Scene View use");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RefreshSceneViews()
|
public static void RefreshSceneViews()
|
||||||
@@ -102,14 +100,5 @@ namespace AppleHills.Editor
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (Application.isPlaying &&
|
|
||||||
DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[EditorSettingsProvider] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
using Interactions;
|
|
||||||
using PuzzleS;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Editor
|
|
||||||
{
|
|
||||||
public class ItemPrefabEditorWindow : EditorWindow
|
|
||||||
{
|
|
||||||
private GameObject _selectedGameObject;
|
|
||||||
private InteractableBase _interactable;
|
|
||||||
private PickupItemData _pickupData;
|
|
||||||
private PuzzleStepSO _objectiveData;
|
|
||||||
private UnityEditor.Editor _soEditor;
|
|
||||||
private string _pickupSoFolderPath = "Assets/Data/Items";
|
|
||||||
private string _puzzleSoFolderPath = "Assets/Data/Puzzles";
|
|
||||||
|
|
||||||
private enum ItemType { None, Pickup, ItemSlot }
|
|
||||||
private ItemType _itemType = ItemType.None;
|
|
||||||
|
|
||||||
[MenuItem("AppleHills/Item Prefab Editor")]
|
|
||||||
public static void ShowWindow()
|
|
||||||
{
|
|
||||||
var window = GetWindow<ItemPrefabEditorWindow>("Item Prefab Editor");
|
|
||||||
window.minSize = new Vector2(400, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
Selection.selectionChanged += Repaint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
Selection.selectionChanged -= Repaint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGUI()
|
|
||||||
{
|
|
||||||
_selectedGameObject = null;
|
|
||||||
_interactable = null;
|
|
||||||
if (Selection.activeGameObject != null)
|
|
||||||
{
|
|
||||||
_selectedGameObject = Selection.activeGameObject;
|
|
||||||
_interactable = _selectedGameObject.GetComponent<InteractableBase>();
|
|
||||||
}
|
|
||||||
else if (Selection.activeObject is GameObject go)
|
|
||||||
{
|
|
||||||
_selectedGameObject = go;
|
|
||||||
_interactable = go.GetComponent<InteractableBase>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_selectedGameObject == null || _interactable == null)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox("Select a GameObject or prefab with an InteractableBase component to edit.", MessageType.Info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUILayout.LabelField("Editing: ", _selectedGameObject.name, EditorStyles.boldLabel);
|
|
||||||
EditorGUILayout.Space();
|
|
||||||
|
|
||||||
// Determine current type
|
|
||||||
bool hasPickup = _selectedGameObject.GetComponent<Pickup>() != null;
|
|
||||||
bool hasSlot = _selectedGameObject.GetComponent<ItemSlot>() != null;
|
|
||||||
if (hasSlot) _itemType = ItemType.ItemSlot;
|
|
||||||
else if (hasPickup) _itemType = ItemType.Pickup;
|
|
||||||
else _itemType = ItemType.None;
|
|
||||||
|
|
||||||
// Item type selection
|
|
||||||
var newType = (ItemType)EditorGUILayout.EnumPopup("Item Type", _itemType);
|
|
||||||
if (newType != _itemType)
|
|
||||||
{
|
|
||||||
// Remove both, then add selected
|
|
||||||
PrefabEditorUtility.RemoveComponent<Pickup>(_selectedGameObject);
|
|
||||||
PrefabEditorUtility.RemoveComponent<ItemSlot>(_selectedGameObject);
|
|
||||||
if (newType == ItemType.Pickup)
|
|
||||||
PrefabEditorUtility.AddOrGetComponent<Pickup>(_selectedGameObject);
|
|
||||||
else if (newType == ItemType.ItemSlot)
|
|
||||||
PrefabEditorUtility.AddOrGetComponent<ItemSlot>(_selectedGameObject);
|
|
||||||
_itemType = newType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectiveStepBehaviour
|
|
||||||
bool hasObjective = _selectedGameObject.GetComponent<ObjectiveStepBehaviour>() != null;
|
|
||||||
bool addObjective = EditorGUILayout.Toggle("ObjectiveStepBehaviour", hasObjective);
|
|
||||||
if (addObjective && !hasObjective)
|
|
||||||
{
|
|
||||||
PrefabEditorUtility.AddOrGetComponent<ObjectiveStepBehaviour>(_selectedGameObject);
|
|
||||||
}
|
|
||||||
else if (!addObjective && hasObjective)
|
|
||||||
{
|
|
||||||
PrefabEditorUtility.RemoveComponent<ObjectiveStepBehaviour>(_selectedGameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pickup Data (for Pickup or ItemSlot)
|
|
||||||
if (_itemType == ItemType.Pickup || _itemType == ItemType.ItemSlot)
|
|
||||||
{
|
|
||||||
var pickup = _selectedGameObject.GetComponent<Pickup>();
|
|
||||||
_pickupData = pickup.itemData;
|
|
||||||
EditorGUILayout.LabelField("Pickup Data:", EditorStyles.boldLabel);
|
|
||||||
_pickupData = (PickupItemData)EditorGUILayout.ObjectField("PickupItemData", _pickupData, typeof(PickupItemData), false);
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
EditorGUILayout.PrefixLabel("Save To");
|
|
||||||
EditorGUILayout.SelectableLabel(_pickupSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
||||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
|
||||||
{
|
|
||||||
_pickupSoFolderPath = PrefabEditorUtility.SelectFolder(_pickupSoFolderPath, "Data/Items");
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
if (_pickupData == null && GUILayout.Button("Create New PickupItemData"))
|
|
||||||
{
|
|
||||||
_pickupData = PrefabEditorUtility.CreateScriptableAsset<PickupItemData>(_selectedGameObject.name + "_pickup", _pickupSoFolderPath);
|
|
||||||
}
|
|
||||||
if (_pickupData != null)
|
|
||||||
{
|
|
||||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _pickupData);
|
|
||||||
pickup.itemData = _pickupData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Objective Data
|
|
||||||
if (addObjective)
|
|
||||||
{
|
|
||||||
var obj = _selectedGameObject.GetComponent<ObjectiveStepBehaviour>();
|
|
||||||
_objectiveData = obj.stepData;
|
|
||||||
EditorGUILayout.LabelField("Objective Data:", EditorStyles.boldLabel);
|
|
||||||
_objectiveData = (PuzzleStepSO)EditorGUILayout.ObjectField("PuzzleStepSO", _objectiveData, typeof(PuzzleStepSO), false);
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
EditorGUILayout.PrefixLabel("Save To");
|
|
||||||
EditorGUILayout.SelectableLabel(_puzzleSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
||||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
|
||||||
{
|
|
||||||
_puzzleSoFolderPath = PrefabEditorUtility.SelectFolder(_puzzleSoFolderPath, "Data/Puzzles");
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
if (_objectiveData == null && GUILayout.Button("Create New PuzzleStepSO"))
|
|
||||||
{
|
|
||||||
_objectiveData = PrefabEditorUtility.CreateScriptableAsset<PuzzleStepSO>(_selectedGameObject.name + "_puzzle", _puzzleSoFolderPath);
|
|
||||||
}
|
|
||||||
if (_objectiveData != null)
|
|
||||||
{
|
|
||||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _objectiveData);
|
|
||||||
obj.stepData = _objectiveData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (GUI.changed)
|
|
||||||
{
|
|
||||||
EditorUtility.SetDirty(_selectedGameObject);
|
|
||||||
PrefabUtility.RecordPrefabInstancePropertyModifications(_selectedGameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 943b203cde5343c68a6278c111fce2ed
|
|
||||||
timeCreated: 1757508162
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
using System.IO;
|
|
||||||
using Interactions;
|
|
||||||
using PuzzleS;
|
|
||||||
|
|
||||||
namespace Editor
|
|
||||||
{
|
|
||||||
public class PrefabCreatorWindow : EditorWindow
|
|
||||||
{
|
|
||||||
private string _prefabName = "NewPrefab";
|
|
||||||
private string _saveFolderPath = "Assets/Prefabs/Items";
|
|
||||||
private string _pickupSoFolderPath = "Assets/Data/Items";
|
|
||||||
private string _puzzleSoFolderPath = "Assets/Data/Puzzles";
|
|
||||||
private bool _addObjective;
|
|
||||||
|
|
||||||
private PickupItemData _pickupData;
|
|
||||||
private PuzzleStepSO _objectiveData;
|
|
||||||
private UnityEditor.Editor _soEditor;
|
|
||||||
|
|
||||||
private enum ItemType { None, Pickup, ItemSlot }
|
|
||||||
private ItemType _itemType = ItemType.None;
|
|
||||||
|
|
||||||
private bool _createNext = false;
|
|
||||||
|
|
||||||
[MenuItem("AppleHills/Item Prefab Creator")]
|
|
||||||
public static void ShowWindow()
|
|
||||||
{
|
|
||||||
var window = GetWindow<PrefabCreatorWindow>("Prefab Creator");
|
|
||||||
window.minSize = new Vector2(400, 400);
|
|
||||||
// Set default paths if not already set
|
|
||||||
if (string.IsNullOrEmpty(window._saveFolderPath))
|
|
||||||
window._saveFolderPath = "Assets/Prefabs/Items";
|
|
||||||
if (string.IsNullOrEmpty(window._pickupSoFolderPath))
|
|
||||||
window._pickupSoFolderPath = "Assets/Data/Items";
|
|
||||||
if (string.IsNullOrEmpty(window._puzzleSoFolderPath))
|
|
||||||
window._puzzleSoFolderPath = "Assets/Data/Puzzles";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGUI()
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Prefab Creator", EditorStyles.boldLabel);
|
|
||||||
_prefabName = EditorGUILayout.TextField("Prefab Name", _prefabName);
|
|
||||||
// Prefab save folder
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
EditorGUILayout.PrefixLabel("Save Folder");
|
|
||||||
EditorGUILayout.SelectableLabel(_saveFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
||||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
|
||||||
{
|
|
||||||
_saveFolderPath = PrefabEditorUtility.SelectFolder(_saveFolderPath, "Prefabs/Items");
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
EditorGUILayout.Space();
|
|
||||||
EditorGUILayout.LabelField("Add Components:", EditorStyles.boldLabel);
|
|
||||||
|
|
||||||
// Item type selection
|
|
||||||
var newType = (ItemType)EditorGUILayout.EnumPopup("Item Type", _itemType);
|
|
||||||
if (newType != _itemType)
|
|
||||||
{
|
|
||||||
_itemType = newType;
|
|
||||||
}
|
|
||||||
bool addObjective = EditorGUILayout.Toggle("ObjectiveStepBehaviour", _addObjective);
|
|
||||||
_addObjective = addObjective;
|
|
||||||
EditorGUILayout.Space();
|
|
||||||
|
|
||||||
// Pickup Data (for Pickup or ItemSlot)
|
|
||||||
if (_itemType == ItemType.Pickup || _itemType == ItemType.ItemSlot)
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Pickup Data:", EditorStyles.boldLabel);
|
|
||||||
_pickupData = (PickupItemData)EditorGUILayout.ObjectField("PickupItemData", _pickupData, typeof(PickupItemData), false);
|
|
||||||
// Pickup SO save folder
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
EditorGUILayout.PrefixLabel("Save To");
|
|
||||||
EditorGUILayout.SelectableLabel(_pickupSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
||||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
|
||||||
{
|
|
||||||
_pickupSoFolderPath = PrefabEditorUtility.SelectFolder(_pickupSoFolderPath, "Data/Items");
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
if (_pickupData == null && GUILayout.Button("Create New PickupItemData"))
|
|
||||||
{
|
|
||||||
_pickupData = PrefabEditorUtility.CreateScriptableAsset<PickupItemData>(_prefabName + "_pickup", _pickupSoFolderPath);
|
|
||||||
}
|
|
||||||
if (_pickupData != null)
|
|
||||||
{
|
|
||||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _pickupData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Objective Data
|
|
||||||
if (_addObjective)
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Objective Data:", EditorStyles.boldLabel);
|
|
||||||
_objectiveData = (PuzzleStepSO)EditorGUILayout.ObjectField("PuzzleStepSO", _objectiveData, typeof(PuzzleStepSO), false);
|
|
||||||
// Puzzle SO save folder
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
EditorGUILayout.PrefixLabel("Save To");
|
|
||||||
EditorGUILayout.SelectableLabel(_puzzleSoFolderPath, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
|
||||||
if (GUILayout.Button("Select...", GUILayout.Width(80)))
|
|
||||||
{
|
|
||||||
_puzzleSoFolderPath = PrefabEditorUtility.SelectFolder(_puzzleSoFolderPath, "Data/Puzzles");
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
if (_objectiveData == null && GUILayout.Button("Create New PuzzleStepSO"))
|
|
||||||
{
|
|
||||||
_objectiveData = PrefabEditorUtility.CreateScriptableAsset<PuzzleStepSO>(_prefabName + "_puzzle", _puzzleSoFolderPath);
|
|
||||||
}
|
|
||||||
if (_objectiveData != null)
|
|
||||||
{
|
|
||||||
PrefabEditorUtility.DrawScriptableObjectEditor(ref _soEditor, _objectiveData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GUILayout.FlexibleSpace();
|
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
GUI.enabled = !string.IsNullOrEmpty(_prefabName) && !string.IsNullOrEmpty(_saveFolderPath);
|
|
||||||
if (GUILayout.Button("Create Prefab", GUILayout.Height(28)))
|
|
||||||
{
|
|
||||||
CreatePrefab();
|
|
||||||
}
|
|
||||||
_createNext = GUILayout.Toggle(_createNext, "Create Next", GUILayout.Width(100), GUILayout.Height(28));
|
|
||||||
GUI.enabled = true;
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreatePrefab()
|
|
||||||
{
|
|
||||||
var go = new GameObject(_prefabName);
|
|
||||||
// Note: No need to add InteractableBase separately - Pickup and ItemSlot inherit from it
|
|
||||||
go.AddComponent<BoxCollider>();
|
|
||||||
int interactableLayer = LayerMask.NameToLayer("Interactable");
|
|
||||||
if (interactableLayer != -1)
|
|
||||||
go.layer = interactableLayer;
|
|
||||||
go.AddComponent<SpriteRenderer>();
|
|
||||||
if (_itemType == ItemType.Pickup)
|
|
||||||
{
|
|
||||||
var pickup = go.AddComponent<Pickup>();
|
|
||||||
pickup.itemData = _pickupData;
|
|
||||||
}
|
|
||||||
else if (_itemType == ItemType.ItemSlot)
|
|
||||||
{
|
|
||||||
var slot = go.AddComponent<ItemSlot>();
|
|
||||||
slot.itemData = _pickupData;
|
|
||||||
}
|
|
||||||
if (_addObjective)
|
|
||||||
{
|
|
||||||
var obj = go.AddComponent<ObjectiveStepBehaviour>();
|
|
||||||
obj.stepData = _objectiveData;
|
|
||||||
}
|
|
||||||
string prefabPath = Path.Combine(_saveFolderPath, _prefabName + ".prefab").Replace("\\", "/");
|
|
||||||
var prefab = PrefabUtility.SaveAsPrefabAsset(go, prefabPath);
|
|
||||||
DestroyImmediate(go);
|
|
||||||
AssetDatabase.Refresh();
|
|
||||||
Selection.activeObject = prefab;
|
|
||||||
EditorGUIUtility.PingObject(prefab);
|
|
||||||
EditorUtility.DisplayDialog("Prefab Created", $"Prefab saved to {prefabPath}", "OK");
|
|
||||||
if (_createNext)
|
|
||||||
{
|
|
||||||
_prefabName = "NewPrefab";
|
|
||||||
_pickupData = null;
|
|
||||||
_objectiveData = null;
|
|
||||||
_itemType = ItemType.None;
|
|
||||||
_addObjective = false;
|
|
||||||
_soEditor = null;
|
|
||||||
GUI.FocusControl(null);
|
|
||||||
Repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f67e06e997f642509ba61ea12b0f793e
|
|
||||||
timeCreated: 1757503955
|
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
version: 2
|
version: 2
|
||||||
tpsheetFileNames:
|
tpsheetFileNames:
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.tpsheet
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.tpsheet
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.tpsheet
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.tpsheet
|
||||||
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_1.tpsheet
|
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_1.tpsheet
|
||||||
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_2.tpsheet
|
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_2.tpsheet
|
||||||
- Assets/Art/Sprites/Spritesheets/Characters/Gardener/Gardener-IdleRun.tpsheet
|
- Assets/Art/Sprites/Spritesheets/Characters/Gardener/Gardener-IdleRun.tpsheet
|
||||||
@@ -30,6 +34,10 @@ tpsheetFileNames:
|
|||||||
- Assets/Art/Sprites/Spritesheets/Quarry/Trees&Bushes/Trees.tpsheet
|
- Assets/Art/Sprites/Spritesheets/Quarry/Trees&Bushes/Trees.tpsheet
|
||||||
- Assets/External/Plugins/codeandweb.com/Example/SpriteSheet/sprites.tpsheet
|
- Assets/External/Plugins/codeandweb.com/Example/SpriteSheet/sprites.tpsheet
|
||||||
textureFileNames:
|
textureFileNames:
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking1.png
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking2.png
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking3.png
|
||||||
|
- Assets/Art/Animations/Trafalgar/Trafalgar-_Walking4.png
|
||||||
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_1.png
|
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_1.png
|
||||||
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_2.png
|
- Assets/Art/Sprites/Spritesheets/Characters/Annelise_Camera/Annelise_camera-AnneLise_2.png
|
||||||
- Assets/Art/Sprites/Spritesheets/Characters/Gardener/Gardener-IdleRun.png
|
- Assets/Art/Sprites/Spritesheets/Characters/Gardener/Gardener-IdleRun.png
|
||||||
@@ -88,5 +96,9 @@ normalmapFileNames:
|
|||||||
-
|
-
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-
|
||||||
- Assets/External/Plugins/codeandweb.com/Example/SpriteSheet/sprites_n.png
|
- Assets/External/Plugins/codeandweb.com/Example/SpriteSheet/sprites_n.png
|
||||||
enableDebugOutput: 0
|
enableDebugOutput: 0
|
||||||
|
|||||||
@@ -313,14 +313,22 @@ PrefabInstance:
|
|||||||
propertyPath: m_Layer
|
propertyPath: m_Layer
|
||||||
value: 7
|
value: 7
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3714001194702331617, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
|
propertyPath: m_Enabled
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
propertyPath: m_Size.x
|
propertyPath: m_Size.x
|
||||||
value: 3.8700001
|
value: 4.97
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
propertyPath: m_Size.y
|
propertyPath: m_Size.y
|
||||||
value: 7.55122
|
value: 7.53
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
|
propertyPath: m_Sprite
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: -1603423718, guid: e071f6ab7f5db5642bcf5502788a8b6e, type: 3}
|
||||||
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
propertyPath: m_SortingOrder
|
propertyPath: m_SortingOrder
|
||||||
value: 1
|
value: 1
|
||||||
@@ -329,6 +337,10 @@ PrefabInstance:
|
|||||||
propertyPath: m_SpriteSortPoint
|
propertyPath: m_SpriteSortPoint
|
||||||
value: 1
|
value: 1
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
|
propertyPath: m_WasSpriteAssigned
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 7877460049793670011, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
- target: {fileID: 7877460049793670011, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
|
||||||
propertyPath: m_LocalScale.x
|
propertyPath: m_LocalScale.x
|
||||||
value: 1
|
value: 1
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ MonoBehaviour:
|
|||||||
getFlirtyMin: 4
|
getFlirtyMin: 4
|
||||||
getFlirtyMax: 5
|
getFlirtyMax: 5
|
||||||
fakeChocolate: {fileID: 2391935521422290070}
|
fakeChocolate: {fileID: 2391935521422290070}
|
||||||
|
realChocolate: {fileID: 0}
|
||||||
distractedAudioClips: {fileID: 6418180475301049370, guid: 956d8d84e8dd1de4e94ba48c041dc6ec, type: 2}
|
distractedAudioClips: {fileID: 6418180475301049370, guid: 956d8d84e8dd1de4e94ba48c041dc6ec, type: 2}
|
||||||
angryAudioClips: {fileID: 6418180475301049370, guid: 22e6e844862e5b94989b572cb70c1eff, type: 2}
|
angryAudioClips: {fileID: 6418180475301049370, guid: 22e6e844862e5b94989b572cb70c1eff, type: 2}
|
||||||
feederClips: {fileID: 6418180475301049370, guid: 2e607d3f32c25a14ea074850dd2f8ac5, type: 2}
|
feederClips: {fileID: 6418180475301049370, guid: 2e607d3f32c25a14ea074850dd2f8ac5, type: 2}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -56,7 +56,7 @@ public class BirdEyesBehavior : ManagedBehaviour
|
|||||||
_statemachine.ChangeState("BirdSpawned");
|
_statemachine.ChangeState("BirdSpawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
base.OnSceneRestoreRequested(serializedData);
|
base.OnSceneRestoreRequested(serializedData);
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ public class BirdEyesBehavior : ManagedBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
return _wolterisoutTriggered.ToString();
|
return _wolterisoutTriggered.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using AppleHills.Core.Settings;
|
using AppleHills.Core.Settings;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Core;
|
using Core;
|
||||||
@@ -30,14 +30,10 @@ namespace Bootstrap
|
|||||||
private float _sceneLoadingProgress = 0f;
|
private float _sceneLoadingProgress = 0f;
|
||||||
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
|
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
|
||||||
|
|
||||||
// Run very early - need to set up loading screen before other systems initialize
|
|
||||||
public override int ManagedAwakePriority => 5;
|
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // Register with LifecycleManager
|
Logging.Debug("BootSceneController.Awake() - Initializing loading screen DURING bootstrap");
|
||||||
|
|
||||||
LogDebugMessage("BootSceneController.Awake() - Initializing loading screen DURING bootstrap");
|
|
||||||
|
|
||||||
// Validate loading screen exists
|
// Validate loading screen exists
|
||||||
if (initialLoadingScreen == null)
|
if (initialLoadingScreen == null)
|
||||||
@@ -71,11 +67,11 @@ namespace Bootstrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
LogDebugMessage("BootSceneController.OnManagedAwake() - Boot is GUARANTEED complete, starting scene loading");
|
Logging.Debug("BootSceneController.OnManagedStart() - Boot is GUARANTEED complete, starting scene loading");
|
||||||
|
|
||||||
// Boot is GUARANTEED complete at this point - that's the whole point of OnManagedAwake!
|
// Boot is GUARANTEED complete at this point - that's the whole point of OnManagedStart!
|
||||||
// No need to subscribe to OnBootCompleted or check CustomBoot.Initialised
|
// No need to subscribe to OnBootCompleted or check CustomBoot.Initialised
|
||||||
_bootComplete = true;
|
_bootComplete = true;
|
||||||
_currentPhase = LoadingPhase.SceneLoading;
|
_currentPhase = LoadingPhase.SceneLoading;
|
||||||
@@ -85,10 +81,8 @@ namespace Bootstrap
|
|||||||
Invoke(nameof(StartLoadingMainMenu), minDelayAfterBoot);
|
Invoke(nameof(StartLoadingMainMenu), minDelayAfterBoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Manual cleanup for events
|
// Manual cleanup for events
|
||||||
if (initialLoadingScreen != null)
|
if (initialLoadingScreen != null)
|
||||||
{
|
{
|
||||||
@@ -102,12 +96,12 @@ namespace Bootstrap
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnInitialLoadingComplete()
|
private void OnInitialLoadingComplete()
|
||||||
{
|
{
|
||||||
LogDebugMessage("Initial loading screen fully hidden, boot sequence completed");
|
Logging.Debug("Initial loading screen fully hidden, boot sequence completed");
|
||||||
|
|
||||||
// Play the intro cinematic if available
|
// Play the intro cinematic if available
|
||||||
if (CinematicsManager.Instance != null)
|
if (CinematicsManager.Instance != null)
|
||||||
{
|
{
|
||||||
LogDebugMessage("Attempting to play intro cinematic");
|
Logging.Debug("Attempting to play intro cinematic");
|
||||||
|
|
||||||
// Use LoadAndPlayCinematic to play the intro sequence
|
// Use LoadAndPlayCinematic to play the intro sequence
|
||||||
CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence", false);
|
CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence", false);
|
||||||
@@ -149,13 +143,13 @@ namespace Bootstrap
|
|||||||
{
|
{
|
||||||
if (debugMode)
|
if (debugMode)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
|
Logging.Debug($"Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugInfo()
|
private void LogDebugInfo()
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Debug - Phase: {_currentPhase}, Bootstrap: {CustomBoot.CurrentProgress:P0}, " +
|
Logging.Debug($"Debug - Phase: {_currentPhase}, Bootstrap: {CustomBoot.CurrentProgress:P0}, " +
|
||||||
$"Scene: {_sceneLoadingProgress:P0}, Combined: {GetCombinedProgress():P0}, Boot Complete: {_bootComplete}");
|
$"Scene: {_sceneLoadingProgress:P0}, Combined: {GetCombinedProgress():P0}, Boot Complete: {_bootComplete}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +166,7 @@ namespace Bootstrap
|
|||||||
|
|
||||||
private async void LoadMainScene()
|
private async void LoadMainScene()
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Loading main menu scene: {mainSceneName}");
|
Logging.Debug($"Loading main menu scene: {mainSceneName}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -186,7 +180,7 @@ namespace Bootstrap
|
|||||||
|
|
||||||
if (debugMode)
|
if (debugMode)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Scene loading raw: {value:P0}, Combined: {GetCombinedProgress():P0}");
|
Logging.Debug($"Scene loading raw: {value:P0}, Combined: {GetCombinedProgress():P0}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -210,13 +204,13 @@ namespace Bootstrap
|
|||||||
_sceneLoadingProgress = 1f;
|
_sceneLoadingProgress = 1f;
|
||||||
|
|
||||||
// CRITICAL: Broadcast lifecycle events so components get their OnSceneReady callbacks
|
// CRITICAL: Broadcast lifecycle events so components get their OnSceneReady callbacks
|
||||||
LogDebugMessage($"Broadcasting OnSceneReady for: {mainSceneName}");
|
Logging.Debug($"Broadcasting OnSceneReady for: {mainSceneName}");
|
||||||
LifecycleManager.Instance?.BroadcastSceneReady(mainSceneName);
|
LifecycleManager.Instance?.BroadcastSceneReady(mainSceneName);
|
||||||
|
|
||||||
// Restore scene data for the main menu
|
// Restore scene data for the main menu
|
||||||
if (SaveLoadManager.Instance != null)
|
if (SaveLoadManager.Instance != null)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Restoring scene data for: {mainSceneName}");
|
Logging.Debug($"Restoring scene data for: {mainSceneName}");
|
||||||
SaveLoadManager.Instance.RestoreSceneData();
|
SaveLoadManager.Instance.RestoreSceneData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +240,7 @@ namespace Bootstrap
|
|||||||
Scene currentScene = SceneManager.GetActiveScene();
|
Scene currentScene = SceneManager.GetActiveScene();
|
||||||
string startingSceneName = currentScene.name;
|
string startingSceneName = currentScene.name;
|
||||||
|
|
||||||
LogDebugMessage($"Unloading StartingScene: {startingSceneName}");
|
Logging.Debug($"Unloading StartingScene: {startingSceneName}");
|
||||||
|
|
||||||
// Unload the StartingScene
|
// Unload the StartingScene
|
||||||
await SceneManager.UnloadSceneAsync(startingSceneName);
|
await SceneManager.UnloadSceneAsync(startingSceneName);
|
||||||
@@ -255,14 +249,14 @@ namespace Bootstrap
|
|||||||
Scene mainMenuScene = SceneManager.GetSceneByName(mainSceneName);
|
Scene mainMenuScene = SceneManager.GetSceneByName(mainSceneName);
|
||||||
SceneManager.SetActiveScene(mainMenuScene);
|
SceneManager.SetActiveScene(mainMenuScene);
|
||||||
|
|
||||||
LogDebugMessage($"Transition complete: {startingSceneName} unloaded, {mainSceneName} is now active");
|
Logging.Debug($"Transition complete: {startingSceneName} unloaded, {mainSceneName} is now active");
|
||||||
|
|
||||||
// Destroy the boot scene controller since its job is done
|
// Destroy the boot scene controller since its job is done
|
||||||
Destroy(gameObject);
|
Destroy(gameObject);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.Warning($"[BootSceneController] Error unloading StartingScene: {e.Message}");
|
Logging.Warning($"Error unloading StartingScene: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,13 +277,5 @@ namespace Bootstrap
|
|||||||
_progressAction?.Invoke(value);
|
_progressAction?.Invoke(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if ( _logVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[BootSceneController] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AppleHills.Core.Settings;
|
|
||||||
using Core;
|
using Core;
|
||||||
using Core.Lifecycle;
|
using Core.Lifecycle;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -105,7 +104,7 @@ namespace Bootstrap
|
|||||||
// Notify the LifecycleManager that boot is complete
|
// Notify the LifecycleManager that boot is complete
|
||||||
if (Application.isPlaying)
|
if (Application.isPlaying)
|
||||||
{
|
{
|
||||||
LogDebugMessage("Calling LifecycleManager.OnBootCompletionTriggered()");
|
Logging.Debug("Calling LifecycleManager.OnBootCompletionTriggered()");
|
||||||
if (LifecycleManager.Instance != null)
|
if (LifecycleManager.Instance != null)
|
||||||
{
|
{
|
||||||
LifecycleManager.Instance.OnBootCompletionTriggered();
|
LifecycleManager.Instance.OnBootCompletionTriggered();
|
||||||
@@ -127,7 +126,7 @@ namespace Bootstrap
|
|||||||
// Notify the LifecycleManager that boot is complete
|
// Notify the LifecycleManager that boot is complete
|
||||||
if (Application.isPlaying)
|
if (Application.isPlaying)
|
||||||
{
|
{
|
||||||
LogDebugMessage("Calling LifecycleManager.OnBootCompletionTriggered()");
|
Logging.Debug("Calling LifecycleManager.OnBootCompletionTriggered()");
|
||||||
if (LifecycleManager.Instance != null)
|
if (LifecycleManager.Instance != null)
|
||||||
{
|
{
|
||||||
LifecycleManager.Instance.OnBootCompletionTriggered();
|
LifecycleManager.Instance.OnBootCompletionTriggered();
|
||||||
@@ -238,16 +237,7 @@ namespace Bootstrap
|
|||||||
{
|
{
|
||||||
CurrentProgress = Mathf.Clamp01(progress);
|
CurrentProgress = Mathf.Clamp01(progress);
|
||||||
OnBootProgressChanged?.Invoke(CurrentProgress);
|
OnBootProgressChanged?.Invoke(CurrentProgress);
|
||||||
LogDebugMessage($"Progress: {CurrentProgress:P0}");
|
Logging.Debug($"Progress: {CurrentProgress:P0}");
|
||||||
}
|
|
||||||
|
|
||||||
private static void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().bootstrapLogVerbosity <=
|
|
||||||
LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[CustomBoot] {message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ namespace Bootstrap
|
|||||||
float displayProgress = Mathf.Min(steadyProgress, actualProgress);
|
float displayProgress = Mathf.Min(steadyProgress, actualProgress);
|
||||||
|
|
||||||
// Log the progress values for debugging
|
// Log the progress values for debugging
|
||||||
LogDebugMessage($"Progress - Default: {steadyProgress:F2}, Actual: {actualProgress:F2}, Display: {displayProgress:F2}");
|
Logging.Debug($"Progress - Default: {steadyProgress:F2}, Actual: {actualProgress:F2}, Display: {displayProgress:F2}");
|
||||||
|
|
||||||
// Directly set the progress bar fill amount without smoothing
|
// Directly set the progress bar fill amount without smoothing
|
||||||
if (progressBarImage != null)
|
if (progressBarImage != null)
|
||||||
@@ -151,7 +151,7 @@ namespace Bootstrap
|
|||||||
if (steadyProgress >= 1.0f && displayProgress >= 1.0f)
|
if (steadyProgress >= 1.0f && displayProgress >= 1.0f)
|
||||||
{
|
{
|
||||||
_animationComplete = true;
|
_animationComplete = true;
|
||||||
LogDebugMessage("Animation complete");
|
Logging.Debug("Animation complete");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ namespace Bootstrap
|
|||||||
if (progressBarImage != null)
|
if (progressBarImage != null)
|
||||||
{
|
{
|
||||||
progressBarImage.fillAmount = 1.0f;
|
progressBarImage.fillAmount = 1.0f;
|
||||||
LogDebugMessage("Final progress set to 1.0");
|
Logging.Debug("Final progress set to 1.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide the screen if loading is also complete
|
// Hide the screen if loading is also complete
|
||||||
@@ -172,7 +172,7 @@ namespace Bootstrap
|
|||||||
if (loadingScreenContainer != null)
|
if (loadingScreenContainer != null)
|
||||||
{
|
{
|
||||||
loadingScreenContainer.SetActive(false);
|
loadingScreenContainer.SetActive(false);
|
||||||
LogDebugMessage("Animation AND loading complete, hiding screen");
|
Logging.Debug("Animation AND loading complete, hiding screen");
|
||||||
|
|
||||||
// Invoke the callback when fully hidden
|
// Invoke the callback when fully hidden
|
||||||
_onLoadingScreenFullyHidden?.Invoke();
|
_onLoadingScreenFullyHidden?.Invoke();
|
||||||
@@ -189,7 +189,7 @@ namespace Bootstrap
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void HideLoadingScreen()
|
public void HideLoadingScreen()
|
||||||
{
|
{
|
||||||
LogDebugMessage("Loading complete, marking loading as finished");
|
Logging.Debug("Loading complete, marking loading as finished");
|
||||||
|
|
||||||
// Mark that loading is complete
|
// Mark that loading is complete
|
||||||
_loadingComplete = true;
|
_loadingComplete = true;
|
||||||
@@ -200,7 +200,7 @@ namespace Bootstrap
|
|||||||
if (loadingScreenContainer != null)
|
if (loadingScreenContainer != null)
|
||||||
{
|
{
|
||||||
loadingScreenContainer.SetActive(false);
|
loadingScreenContainer.SetActive(false);
|
||||||
LogDebugMessage("Animation already complete, hiding screen immediately");
|
Logging.Debug("Animation already complete, hiding screen immediately");
|
||||||
|
|
||||||
// Invoke the callback when fully hidden
|
// Invoke the callback when fully hidden
|
||||||
_onLoadingScreenFullyHidden?.Invoke();
|
_onLoadingScreenFullyHidden?.Invoke();
|
||||||
@@ -210,7 +210,7 @@ namespace Bootstrap
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogDebugMessage("Animation still in progress, waiting for it to complete");
|
Logging.Debug("Animation still in progress, waiting for it to complete");
|
||||||
// The coroutine will handle hiding when animation completes
|
// The coroutine will handle hiding when animation completes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,13 +244,5 @@ namespace Bootstrap
|
|||||||
|
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if ( _logVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[InitialLoadingScreen] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,17 +37,13 @@ namespace Cinematics
|
|||||||
|
|
||||||
public PlayableDirector playableDirector;
|
public PlayableDirector playableDirector;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 170; // Cinematic systems
|
internal override void OnManagedAwake()
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug("[CinematicsManager] Initialized");
|
Logging.Debug("[CinematicsManager] Initialized");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,8 @@ namespace Cinematics
|
|||||||
private float _holdStartTime;
|
private float _holdStartTime;
|
||||||
private bool _isHolding;
|
private bool _isHolding;
|
||||||
private bool _skipPerformed;
|
private bool _skipPerformed;
|
||||||
private bool _initialized = false;
|
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 180; // Cinematic UI
|
internal override void OnManagedStart()
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
|
||||||
{
|
{
|
||||||
// Reset the progress bar
|
// Reset the progress bar
|
||||||
if (radialProgressBar != null)
|
if (radialProgressBar != null)
|
||||||
@@ -33,10 +30,8 @@ namespace Cinematics
|
|||||||
Logging.Debug("[SkipCinematic] Initialized");
|
Logging.Debug("[SkipCinematic] Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Clean up subscriptions
|
// Clean up subscriptions
|
||||||
UnsubscribeFromCinematicsEvents();
|
UnsubscribeFromCinematicsEvents();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,17 +34,13 @@ namespace Core
|
|||||||
public event Action OnGamePaused;
|
public event Action OnGamePaused;
|
||||||
public event Action OnGameResumed;
|
public event Action OnGameResumed;
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
|
||||||
public override int ManagedAwakePriority => 10; // Core infrastructure - runs early
|
|
||||||
|
|
||||||
private new void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Create settings providers - must happen in Awake so other managers can access settings in their ManagedAwake
|
// Create settings providers - must happen in OnManagedAwake so other managers can access settings in their ManagedStart
|
||||||
SettingsProvider.Instance.gameObject.name = "Settings Provider";
|
SettingsProvider.Instance.gameObject.name = "Settings Provider";
|
||||||
DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";
|
DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";
|
||||||
|
|
||||||
@@ -57,9 +53,9 @@ namespace Core
|
|||||||
_managerLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().gameManagerLogVerbosity;
|
_managerLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().gameManagerLogVerbosity;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Settings are already initialized in Awake()
|
// Settings are already initialized in OnManagedAwake()
|
||||||
// This is available for future initialization that depends on other managers
|
// This is available for future initialization that depends on other managers
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +75,7 @@ namespace Core
|
|||||||
component.Pause();
|
component.Pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebugMessage($"Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
Logging.Debug($"Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +88,7 @@ namespace Core
|
|||||||
if (component != null && _pausableComponents.Contains(component))
|
if (component != null && _pausableComponents.Contains(component))
|
||||||
{
|
{
|
||||||
_pausableComponents.Remove(component);
|
_pausableComponents.Remove(component);
|
||||||
LogDebugMessage($"Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
Logging.Debug($"Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +104,7 @@ namespace Core
|
|||||||
ApplyPause(true);
|
ApplyPause(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebugMessage($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
Logging.Debug($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -123,7 +119,7 @@ namespace Core
|
|||||||
ApplyPause(false);
|
ApplyPause(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebugMessage($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
Logging.Debug($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -162,12 +158,12 @@ namespace Core
|
|||||||
OnGameResumed?.Invoke();
|
OnGameResumed?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDebugMessage($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
|
Logging.Debug($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeSettings()
|
private void InitializeSettings()
|
||||||
{
|
{
|
||||||
LogDebugMessage("Starting settings initialization...", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("Starting settings initialization...");
|
||||||
|
|
||||||
// Load settings synchronously
|
// Load settings synchronously
|
||||||
var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous<PlayerFollowerSettings>();
|
var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous<PlayerFollowerSettings>();
|
||||||
@@ -178,7 +174,7 @@ namespace Core
|
|||||||
if (playerSettings != null)
|
if (playerSettings != null)
|
||||||
{
|
{
|
||||||
ServiceLocator.Register<IPlayerFollowerSettings>(playerSettings);
|
ServiceLocator.Register<IPlayerFollowerSettings>(playerSettings);
|
||||||
LogDebugMessage("PlayerFollowerSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("PlayerFollowerSettings registered successfully");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -188,7 +184,7 @@ namespace Core
|
|||||||
if (interactionSettings != null)
|
if (interactionSettings != null)
|
||||||
{
|
{
|
||||||
ServiceLocator.Register<IInteractionSettings>(interactionSettings);
|
ServiceLocator.Register<IInteractionSettings>(interactionSettings);
|
||||||
LogDebugMessage("InteractionSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("InteractionSettings registered successfully");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -198,7 +194,7 @@ namespace Core
|
|||||||
if (minigameSettings != null)
|
if (minigameSettings != null)
|
||||||
{
|
{
|
||||||
ServiceLocator.Register<IDivingMinigameSettings>(minigameSettings);
|
ServiceLocator.Register<IDivingMinigameSettings>(minigameSettings);
|
||||||
LogDebugMessage("MinigameSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("MinigameSettings registered successfully");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -209,7 +205,7 @@ namespace Core
|
|||||||
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null;
|
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null;
|
||||||
if (_settingsLoaded)
|
if (_settingsLoaded)
|
||||||
{
|
{
|
||||||
LogDebugMessage("All settings loaded and registered with ServiceLocator", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("All settings loaded and registered with ServiceLocator");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -222,7 +218,7 @@ namespace Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeDeveloperSettings()
|
private void InitializeDeveloperSettings()
|
||||||
{
|
{
|
||||||
LogDebugMessage("Starting developer settings initialization...", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("Starting developer settings initialization...");
|
||||||
|
|
||||||
// Load developer settings
|
// Load developer settings
|
||||||
var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings<DivingDeveloperSettings>();
|
var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings<DivingDeveloperSettings>();
|
||||||
@@ -232,7 +228,7 @@ namespace Core
|
|||||||
|
|
||||||
if (_developerSettingsLoaded)
|
if (_developerSettingsLoaded)
|
||||||
{
|
{
|
||||||
LogDebugMessage("All developer settings loaded successfully", "SettingsInitialization", _settingsLogVerbosity);
|
Logging.Debug("All developer settings loaded successfully");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -267,19 +263,6 @@ namespace Core
|
|||||||
return DeveloperSettingsProvider.Instance?.GetSettings<T>();
|
return DeveloperSettingsProvider.Instance?.GetSettings<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message, string prefix = "GameManager", LogVerbosity verbosity = LogVerbosity.None)
|
|
||||||
{
|
|
||||||
if (verbosity == LogVerbosity.None)
|
|
||||||
{
|
|
||||||
verbosity = _managerLogVerbosity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( verbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[{prefix}] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LEFTOVER LEGACY SETTINGS
|
// LEFTOVER LEGACY SETTINGS
|
||||||
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;
|
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;
|
||||||
public float PlayerStopDistanceDirectInteraction => GetSettings<IInteractionSettings>()?.PlayerStopDistanceDirectInteraction ?? 2.0f;
|
public float PlayerStopDistanceDirectInteraction => GetSettings<IInteractionSettings>()?.PlayerStopDistanceDirectInteraction ?? 2.0f;
|
||||||
|
|||||||
@@ -48,31 +48,25 @@ namespace Core
|
|||||||
// Args: first item data, second item data, result item data
|
// Args: first item data, second item data, result item data
|
||||||
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 75; // Item registry
|
internal override void OnManagedAwake()
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug("[ItemManager] Initialized");
|
Logging.Debug("[ItemManager] Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
// Replaces SceneLoadStarted subscription for clearing registrations
|
// Replaces SceneLoadStarted subscription for clearing registrations
|
||||||
ClearAllRegistrations();
|
ClearAllRegistrations();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Ensure we clean up any subscriptions from registered items when the manager is destroyed
|
// Ensure we clean up any subscriptions from registered items when the manager is destroyed
|
||||||
ClearAllRegistrations();
|
ClearAllRegistrations();
|
||||||
}
|
}
|
||||||
@@ -144,7 +138,6 @@ namespace Core
|
|||||||
{
|
{
|
||||||
s.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
|
s.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
|
||||||
s.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
|
s.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
|
||||||
s.OnForbiddenItemSlotted -= ItemSlot_OnForbiddenItemSlotted;
|
|
||||||
s.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
|
s.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +184,6 @@ namespace Core
|
|||||||
// Subscribe to all slot events
|
// Subscribe to all slot events
|
||||||
slot.OnCorrectItemSlotted += ItemSlot_OnCorrectItemSlotted;
|
slot.OnCorrectItemSlotted += ItemSlot_OnCorrectItemSlotted;
|
||||||
slot.OnIncorrectItemSlotted += ItemSlot_OnIncorrectItemSlotted;
|
slot.OnIncorrectItemSlotted += ItemSlot_OnIncorrectItemSlotted;
|
||||||
slot.OnForbiddenItemSlotted += ItemSlot_OnForbiddenItemSlotted;
|
|
||||||
slot.OnItemSlotRemoved += ItemSlot_OnItemSlotRemoved;
|
slot.OnItemSlotRemoved += ItemSlot_OnItemSlotRemoved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,7 +196,6 @@ namespace Core
|
|||||||
// Unsubscribe from all slot events
|
// Unsubscribe from all slot events
|
||||||
slot.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
|
slot.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
|
||||||
slot.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
|
slot.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
|
||||||
slot.OnForbiddenItemSlotted -= ItemSlot_OnForbiddenItemSlotted;
|
|
||||||
slot.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
|
slot.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,19 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public enum LifecyclePhase
|
public enum LifecyclePhase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Called immediately during registration (during Awake).
|
||||||
|
/// Use for early initialization such as setting singleton instances.
|
||||||
|
/// NOT ordered - fires whenever Unity calls this component's Awake().
|
||||||
|
/// </summary>
|
||||||
|
ManagedAwake,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called once per component after bootstrap completes.
|
/// Called once per component after bootstrap completes.
|
||||||
/// Guaranteed to be called after all bootstrap resources are loaded.
|
/// Guaranteed to be called after all bootstrap resources are loaded.
|
||||||
/// For late-registered components, called immediately upon registration.
|
/// For late-registered components, called immediately upon registration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ManagedAwake,
|
ManagedStart,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called before a scene is unloaded.
|
/// Called before a scene is unloaded.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -59,11 +59,11 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
#region State Flags
|
#region State Flags
|
||||||
|
|
||||||
private bool isBootComplete = false;
|
private bool isBootComplete;
|
||||||
private string currentSceneReady = "";
|
private string currentSceneReady = "";
|
||||||
|
|
||||||
// Scene loading state tracking
|
// Scene loading state tracking
|
||||||
private bool isLoadingScene = false;
|
private bool isLoadingScene;
|
||||||
private string sceneBeingLoaded = "";
|
private string sceneBeingLoaded = "";
|
||||||
private List<ManagedBehaviour> pendingSceneComponents = new List<ManagedBehaviour>();
|
private List<ManagedBehaviour> pendingSceneComponents = new List<ManagedBehaviour>();
|
||||||
|
|
||||||
@@ -120,43 +120,49 @@ namespace Core.Lifecycle
|
|||||||
// Track which scene this component belongs to
|
// Track which scene this component belongs to
|
||||||
componentScenes[component] = sceneName;
|
componentScenes[component] = sceneName;
|
||||||
|
|
||||||
// ALWAYS add to managedAwakeList - this is the master list used for save/load
|
// Add to all lifecycle lists (order of registration determines execution order)
|
||||||
InsertSorted(managedAwakeList, component, component.ManagedAwakePriority);
|
managedAwakeList.Add(component);
|
||||||
|
sceneUnloadingList.Add(component);
|
||||||
|
sceneReadyList.Add(component);
|
||||||
|
saveRequestedList.Add(component);
|
||||||
|
restoreRequestedList.Add(component);
|
||||||
|
destroyList.Add(component);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
component.OnManagedAwake();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for {component.gameObject.name}: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
// Handle ManagedAwake timing based on boot state
|
// Handle OnManagedStart timing based on boot state
|
||||||
if (isBootComplete)
|
if (isBootComplete)
|
||||||
{
|
{
|
||||||
// Check if we're currently loading a scene
|
// Check if we're currently loading a scene
|
||||||
if (isLoadingScene && sceneName == sceneBeingLoaded)
|
if (isLoadingScene && sceneName == sceneBeingLoaded)
|
||||||
{
|
{
|
||||||
// Batch this component - will be processed in priority order when scene load completes
|
// Batch this component - will be processed when scene load completes
|
||||||
pendingSceneComponents.Add(component);
|
pendingSceneComponents.Add(component);
|
||||||
LogDebug($"Batched component for scene load: {component.gameObject.name} (Scene: {sceneName})");
|
LogDebug($"Batched component for scene load: {component.gameObject.name} (Scene: {sceneName})");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Truly late registration (component enabled after scene is ready)
|
// Truly late registration (component enabled after scene is ready)
|
||||||
// Call OnManagedAwake immediately since boot already completed
|
// Call OnManagedStart immediately since boot already completed
|
||||||
LogDebug($"Late registration: Calling OnManagedAwake immediately for {component.gameObject.name}");
|
LogDebug($"Late registration: Calling OnManagedStart immediately for {component.gameObject.name}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeManagedAwake();
|
component.OnManagedStart();
|
||||||
HandleAutoRegistrations(component);
|
HandleAutoRegistrations(component);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for {component.gameObject.name}: {ex}");
|
Debug.LogError($"[LifecycleManager] Error in OnManagedStart for {component.gameObject.name}: {ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If boot not complete, component stays in list and will be processed by BroadcastManagedAwake()
|
// If boot not complete, component stays in list and will be processed by BroadcastManagedStart()
|
||||||
|
|
||||||
// Register for all scene lifecycle hooks
|
|
||||||
InsertSorted(sceneUnloadingList, component, component.SceneUnloadingPriority);
|
|
||||||
InsertSorted(sceneReadyList, component, component.SceneReadyPriority);
|
|
||||||
InsertSorted(saveRequestedList, component, component.SavePriority);
|
|
||||||
InsertSorted(restoreRequestedList, component, component.RestorePriority);
|
|
||||||
InsertSorted(destroyList, component, component.DestroyPriority);
|
|
||||||
|
|
||||||
// If this scene is already ready (and we're not in loading mode), call OnSceneReady immediately
|
// If this scene is already ready (and we're not in loading mode), call OnSceneReady immediately
|
||||||
if (!isLoadingScene && currentSceneReady == sceneName)
|
if (!isLoadingScene && currentSceneReady == sceneName)
|
||||||
@@ -164,7 +170,7 @@ namespace Core.Lifecycle
|
|||||||
LogDebug($"Late registration: Calling OnSceneReady immediately for {component.gameObject.name}");
|
LogDebug($"Late registration: Calling OnSceneReady immediately for {component.gameObject.name}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeSceneReady();
|
component.OnSceneReady();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -202,7 +208,7 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called by CustomBoot when boot completes.
|
/// Called by CustomBoot when boot completes.
|
||||||
/// Broadcasts ManagedAwake to all registered components.
|
/// Broadcasts ManagedStart to all registered components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void OnBootCompletionTriggered()
|
public void OnBootCompletionTriggered()
|
||||||
{
|
{
|
||||||
@@ -210,16 +216,16 @@ namespace Core.Lifecycle
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
LogDebug("=== Boot Completion Triggered ===");
|
LogDebug("=== Boot Completion Triggered ===");
|
||||||
BroadcastManagedAwake();
|
BroadcastManagedStart();
|
||||||
isBootComplete = true;
|
isBootComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Broadcast OnManagedAwake to all registered components (priority ordered).
|
/// Broadcast OnManagedStart to all registered components (priority ordered).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void BroadcastManagedAwake()
|
private void BroadcastManagedStart()
|
||||||
{
|
{
|
||||||
LogDebug($"Broadcasting ManagedAwake to {managedAwakeList.Count} components");
|
LogDebug($"Broadcasting ManagedStart to {managedAwakeList.Count} components");
|
||||||
|
|
||||||
// Create a copy to avoid collection modification during iteration
|
// Create a copy to avoid collection modification during iteration
|
||||||
var componentsCopy = new List<ManagedBehaviour>(managedAwakeList);
|
var componentsCopy = new List<ManagedBehaviour>(managedAwakeList);
|
||||||
@@ -230,12 +236,12 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeManagedAwake();
|
component.OnManagedStart();
|
||||||
HandleAutoRegistrations(component);
|
HandleAutoRegistrations(component);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for {component.gameObject.name}: {ex}");
|
Debug.LogError($"[LifecycleManager] Error in OnManagedStart for {component.gameObject.name}: {ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,23 +278,20 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
LogDebug($"Processing {pendingSceneComponents.Count} batched components for scene: {sceneBeingLoaded}");
|
LogDebug($"Processing {pendingSceneComponents.Count} batched components for scene: {sceneBeingLoaded}");
|
||||||
|
|
||||||
// Sort by ManagedAwake priority (lower values first)
|
// Call OnManagedStart in registration order
|
||||||
pendingSceneComponents.Sort((a, b) => a.ManagedAwakePriority.CompareTo(b.ManagedAwakePriority));
|
|
||||||
|
|
||||||
// Call OnManagedAwake in priority order
|
|
||||||
foreach (var component in pendingSceneComponents)
|
foreach (var component in pendingSceneComponents)
|
||||||
{
|
{
|
||||||
if (component == null) continue;
|
if (component == null) continue;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeManagedAwake();
|
component.OnManagedStart();
|
||||||
HandleAutoRegistrations(component);
|
HandleAutoRegistrations(component);
|
||||||
LogDebug($"Processed batched component: {component.gameObject.name} (Priority: {component.ManagedAwakePriority})");
|
LogDebug($"Processed batched component: {component.gameObject.name}");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[LifecycleManager] Error in OnManagedAwake for batched component {component.gameObject.name}: {ex}");
|
Debug.LogError($"[LifecycleManager] Error in OnManagedStart for batched component {component.gameObject.name}: {ex}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +302,7 @@ namespace Core.Lifecycle
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Broadcast OnSceneUnloading to components in the specified scene (reverse priority order).
|
/// Broadcast OnSceneUnloading to components in the specified scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BroadcastSceneUnloading(string sceneName)
|
public void BroadcastSceneUnloading(string sceneName)
|
||||||
{
|
{
|
||||||
@@ -315,7 +318,7 @@ namespace Core.Lifecycle
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeSceneUnloading();
|
component.OnSceneUnloading();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -326,8 +329,8 @@ namespace Core.Lifecycle
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Broadcast OnSceneReady to components in the specified scene (priority order).
|
/// Broadcast OnSceneReady to components in the specified scene.
|
||||||
/// If scene loading mode is active, processes batched components first.
|
/// Processes batched components first, then calls OnSceneReady on all components in that scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BroadcastSceneReady(string sceneName)
|
public void BroadcastSceneReady(string sceneName)
|
||||||
{
|
{
|
||||||
@@ -351,7 +354,7 @@ namespace Core.Lifecycle
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeSceneReady();
|
component.OnSceneReady();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -379,7 +382,7 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string serializedData = component.InvokeSceneSaveRequested();
|
string serializedData = component.OnSceneSaveRequested();
|
||||||
if (!string.IsNullOrEmpty(serializedData))
|
if (!string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
string saveId = component.SaveId;
|
string saveId = component.SaveId;
|
||||||
@@ -415,7 +418,7 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string serializedData = component.InvokeGlobalSaveRequested();
|
string serializedData = component.OnGlobalSaveRequested();
|
||||||
if (!string.IsNullOrEmpty(serializedData))
|
if (!string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
saveData[component.SaveId] = serializedData;
|
saveData[component.SaveId] = serializedData;
|
||||||
@@ -455,7 +458,7 @@ namespace Core.Lifecycle
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeSceneRestoreRequested(serializedData);
|
component.OnSceneRestoreRequested(serializedData);
|
||||||
restoredCount++;
|
restoredCount++;
|
||||||
LogDebug($"Restored scene data to: {component.SaveId}");
|
LogDebug($"Restored scene data to: {component.SaveId}");
|
||||||
}
|
}
|
||||||
@@ -486,7 +489,7 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeSceneRestoreCompleted();
|
component.OnSceneRestoreCompleted();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -519,7 +522,7 @@ namespace Core.Lifecycle
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeGlobalRestoreRequested(serializedData);
|
component.OnGlobalRestoreRequested(serializedData);
|
||||||
restoredCount++;
|
restoredCount++;
|
||||||
LogDebug($"Restored global data to: {component.SaveId}");
|
LogDebug($"Restored global data to: {component.SaveId}");
|
||||||
}
|
}
|
||||||
@@ -551,7 +554,7 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeGlobalLoadCompleted();
|
component.OnGlobalLoadCompleted();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -578,7 +581,7 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
component.InvokeGlobalSaveStarted();
|
component.OnGlobalSaveStarted();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -612,42 +615,6 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
#region Helper Methods
|
#region Helper Methods
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Insert component into list maintaining sorted order by priority.
|
|
||||||
/// Uses binary search for efficient insertion.
|
|
||||||
/// </summary>
|
|
||||||
private void InsertSorted(List<ManagedBehaviour> list, ManagedBehaviour component, int priority)
|
|
||||||
{
|
|
||||||
// Simple linear insertion for now (can optimize with binary search later if needed)
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < list.Count; i++)
|
|
||||||
{
|
|
||||||
int existingPriority = GetPriorityForList(list[i], list);
|
|
||||||
if (priority < existingPriority)
|
|
||||||
{
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
list.Insert(index, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the priority value for a component based on which list it's in.
|
|
||||||
/// </summary>
|
|
||||||
private int GetPriorityForList(ManagedBehaviour component, List<ManagedBehaviour> list)
|
|
||||||
{
|
|
||||||
if (list == managedAwakeList) return component.ManagedAwakePriority;
|
|
||||||
if (list == sceneUnloadingList) return component.SceneUnloadingPriority;
|
|
||||||
if (list == sceneReadyList) return component.SceneReadyPriority;
|
|
||||||
if (list == saveRequestedList) return component.SavePriority;
|
|
||||||
if (list == restoreRequestedList) return component.RestorePriority;
|
|
||||||
if (list == destroyList) return component.DestroyPriority;
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Log debug message if debug logging is enabled.
|
/// Log debug message if debug logging is enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using UnityEngine;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Core.Lifecycle
|
namespace Core.Lifecycle
|
||||||
{
|
{
|
||||||
@@ -9,46 +8,6 @@ namespace Core.Lifecycle
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ManagedBehaviour : MonoBehaviour
|
public abstract class ManagedBehaviour : MonoBehaviour
|
||||||
{
|
{
|
||||||
#region Priority Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority for OnManagedAwake (lower values execute first).
|
|
||||||
/// Default: 100
|
|
||||||
/// </summary>
|
|
||||||
public virtual int ManagedAwakePriority => 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority for OnSceneUnloading (executed in reverse: higher values execute first).
|
|
||||||
/// Default: 100
|
|
||||||
/// </summary>
|
|
||||||
public virtual int SceneUnloadingPriority => 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority for OnSceneReady (lower values execute first).
|
|
||||||
/// Default: 100
|
|
||||||
/// </summary>
|
|
||||||
public virtual int SceneReadyPriority => 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority for OnSaveRequested (executed in reverse: higher values execute first).
|
|
||||||
/// Default: 100
|
|
||||||
/// </summary>
|
|
||||||
public virtual int SavePriority => 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority for OnRestoreRequested (lower values execute first).
|
|
||||||
/// Default: 100
|
|
||||||
/// </summary>
|
|
||||||
public virtual int RestorePriority => 100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority for OnManagedDestroy (executed in reverse: higher values execute first).
|
|
||||||
/// Default: 100
|
|
||||||
/// </summary>
|
|
||||||
public virtual int DestroyPriority => 100;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Configuration Properties
|
#region Configuration Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -68,39 +27,28 @@ namespace Core.Lifecycle
|
|||||||
/// Unique identifier for this component in the save system.
|
/// Unique identifier for this component in the save system.
|
||||||
/// Default: "SceneName/GameObjectName/ComponentType"
|
/// Default: "SceneName/GameObjectName/ComponentType"
|
||||||
/// Override ONLY for special cases (e.g., singletons like "PlayerController", or custom IDs).
|
/// Override ONLY for special cases (e.g., singletons like "PlayerController", or custom IDs).
|
||||||
|
/// Cached on first access to avoid runtime allocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string SaveId
|
public virtual string SaveId
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
{
|
||||||
|
if (_cachedSaveId == null)
|
||||||
{
|
{
|
||||||
string sceneName = gameObject.scene.IsValid() ? gameObject.scene.name : "UnknownScene";
|
string sceneName = gameObject.scene.IsValid() ? gameObject.scene.name : "UnknownScene";
|
||||||
string componentType = GetType().Name;
|
string componentType = GetType().Name;
|
||||||
return $"{sceneName}/{gameObject.name}/{componentType}";
|
_cachedSaveId = $"{sceneName}/{gameObject.name}/{componentType}";
|
||||||
|
}
|
||||||
|
return _cachedSaveId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Public Accessors (for LifecycleManager)
|
|
||||||
|
|
||||||
// Public wrappers to invoke protected lifecycle methods
|
|
||||||
public void InvokeManagedAwake() => OnManagedAwake();
|
|
||||||
public void InvokeSceneUnloading() => OnSceneUnloading();
|
|
||||||
public void InvokeSceneReady() => OnSceneReady();
|
|
||||||
public string InvokeSceneSaveRequested() => OnSceneSaveRequested();
|
|
||||||
public void InvokeSceneRestoreRequested(string data) => OnSceneRestoreRequested(data);
|
|
||||||
public void InvokeSceneRestoreCompleted() => OnSceneRestoreCompleted();
|
|
||||||
public string InvokeGlobalSaveRequested() => OnGlobalSaveRequested();
|
|
||||||
public void InvokeGlobalRestoreRequested(string data) => OnGlobalRestoreRequested(data);
|
|
||||||
public void InvokeManagedDestroy() => OnManagedDestroy();
|
|
||||||
public void InvokeGlobalLoadCompleted() => OnGlobalLoadCompleted();
|
|
||||||
public void InvokeGlobalSaveStarted() => OnGlobalSaveStarted();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
|
|
||||||
private bool _isRegistered;
|
private bool _isRegistered;
|
||||||
|
private string _cachedSaveId;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -108,9 +56,9 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unity Awake - automatically registers with LifecycleManager.
|
/// Unity Awake - automatically registers with LifecycleManager.
|
||||||
/// IMPORTANT: Derived classes that override Awake MUST call base.Awake()
|
/// SEALED: Cannot be overridden. Use OnManagedAwake() for early initialization or OnManagedStart() for late initialization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
if (LifecycleManager.Instance != null)
|
if (LifecycleManager.Instance != null)
|
||||||
{
|
{
|
||||||
@@ -125,13 +73,16 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unity OnDestroy - automatically unregisters and cleans up.
|
/// Unity OnDestroy - automatically unregisters and cleans up.
|
||||||
/// IMPORTANT: Derived classes that override OnDestroy MUST call base.OnDestroy()
|
/// SEALED: Cannot be overridden. Use OnManagedDestroy() for custom cleanup logic.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (!_isRegistered)
|
if (!_isRegistered)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Call managed destroy hook
|
||||||
|
OnManagedDestroy();
|
||||||
|
|
||||||
// Unregister from LifecycleManager
|
// Unregister from LifecycleManager
|
||||||
if (LifecycleManager.Instance != null)
|
if (LifecycleManager.Instance != null)
|
||||||
{
|
{
|
||||||
@@ -152,34 +103,47 @@ namespace Core.Lifecycle
|
|||||||
|
|
||||||
#region Managed Lifecycle Hooks
|
#region Managed Lifecycle Hooks
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called immediately during registration (during Awake).
|
||||||
|
/// Use for early initialization such as setting singleton instances.
|
||||||
|
/// TIMING: Fires during component's Awake(), no execution order guarantees between components.
|
||||||
|
/// NOT priority-ordered - fires whenever Unity calls this component's Awake().
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
|
/// </summary>
|
||||||
|
internal virtual void OnManagedAwake()
|
||||||
|
{
|
||||||
|
// Override in derived classes
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called once per component after bootstrap completes.
|
/// Called once per component after bootstrap completes.
|
||||||
/// GUARANTEE: Bootstrap resources are available, all managers are initialized.
|
/// GUARANTEE: Bootstrap resources are available, all managers are initialized.
|
||||||
/// For boot-time components: Called during LifecycleManager.BroadcastManagedAwake (priority ordered).
|
/// For boot-time components: Called during LifecycleManager.BroadcastManagedStart (registration order).
|
||||||
/// For late-registered components: Called immediately upon registration (bootstrap already complete).
|
/// For late-registered components: Called immediately upon registration (bootstrap already complete).
|
||||||
/// Replaces the old Awake + InitializePostBoot pattern.
|
/// Use for initialization that depends on other systems.
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnManagedAwake()
|
internal virtual void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Override in derived classes
|
// Override in derived classes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called before the scene this component belongs to is unloaded.
|
/// Called before the scene this component belongs to is unloaded.
|
||||||
/// Called in REVERSE priority order (higher values execute first).
|
|
||||||
/// Use for scene-specific cleanup.
|
/// Use for scene-specific cleanup.
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnSceneUnloading()
|
internal virtual void OnSceneUnloading()
|
||||||
{
|
{
|
||||||
// Override in derived classes
|
// Override in derived classes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called after the scene this component belongs to has finished loading.
|
/// Called after the scene this component belongs to has finished loading.
|
||||||
/// Called in priority order (lower values execute first).
|
|
||||||
/// Use for scene-specific initialization.
|
/// Use for scene-specific initialization.
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnSceneReady()
|
internal virtual void OnSceneReady()
|
||||||
{
|
{
|
||||||
// Override in derived classes
|
// Override in derived classes
|
||||||
}
|
}
|
||||||
@@ -193,8 +157,10 @@ namespace Core.Lifecycle
|
|||||||
/// - Called BEFORE scene unload during scene transitions
|
/// - Called BEFORE scene unload during scene transitions
|
||||||
/// - Frequency: Every scene transition
|
/// - Frequency: Every scene transition
|
||||||
/// - Use for: Level progress, object positions, puzzle states
|
/// - Use for: Level progress, object positions, puzzle states
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual string OnSceneSaveRequested()
|
internal virtual string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
return null; // Default: no data to save
|
return null; // Default: no data to save
|
||||||
}
|
}
|
||||||
@@ -211,8 +177,10 @@ namespace Core.Lifecycle
|
|||||||
/// - Called AFTER scene load, during OnSceneReady phase
|
/// - Called AFTER scene load, during OnSceneReady phase
|
||||||
/// - Frequency: Every scene transition
|
/// - Frequency: Every scene transition
|
||||||
/// - Use for: Restoring level progress, object positions, puzzle states
|
/// - Use for: Restoring level progress, object positions, puzzle states
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnSceneRestoreRequested(string serializedData)
|
internal virtual void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
// Default: no-op
|
// Default: no-op
|
||||||
}
|
}
|
||||||
@@ -234,8 +202,10 @@ namespace Core.Lifecycle
|
|||||||
/// COMMON PATTERN:
|
/// COMMON PATTERN:
|
||||||
/// Use this to perform actions that depend on whether data was restored or not.
|
/// Use this to perform actions that depend on whether data was restored or not.
|
||||||
/// Example: Play one-time audio only if it hasn't been played before (_hasPlayed == false).
|
/// Example: Play one-time audio only if it hasn't been played before (_hasPlayed == false).
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnSceneRestoreCompleted()
|
internal virtual void OnSceneRestoreCompleted()
|
||||||
{
|
{
|
||||||
// Default: no-op
|
// Default: no-op
|
||||||
}
|
}
|
||||||
@@ -248,8 +218,10 @@ namespace Core.Lifecycle
|
|||||||
/// - Called ONCE on game boot after save file is read
|
/// - Called ONCE on game boot after save file is read
|
||||||
/// - NOT called during scene transitions
|
/// - NOT called during scene transitions
|
||||||
/// - Use for: Player inventory, unlocked features, card collections
|
/// - Use for: Player inventory, unlocked features, card collections
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnGlobalRestoreRequested(string serializedData)
|
internal virtual void OnGlobalRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
// Default: no-op
|
// Default: no-op
|
||||||
}
|
}
|
||||||
@@ -263,8 +235,10 @@ namespace Core.Lifecycle
|
|||||||
/// - Called ONCE before save file is written (on quit, manual save, etc.)
|
/// - Called ONCE before save file is written (on quit, manual save, etc.)
|
||||||
/// - NOT called during scene transitions
|
/// - NOT called during scene transitions
|
||||||
/// - Use for: Player inventory, unlocked features, card collections
|
/// - Use for: Player inventory, unlocked features, card collections
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual string OnGlobalSaveRequested()
|
internal virtual string OnGlobalSaveRequested()
|
||||||
{
|
{
|
||||||
return null; // Default: no data to save
|
return null; // Default: no data to save
|
||||||
}
|
}
|
||||||
@@ -278,8 +252,10 @@ namespace Core.Lifecycle
|
|||||||
/// - Called ONCE on game boot after all restore operations complete
|
/// - Called ONCE on game boot after all restore operations complete
|
||||||
/// - NOT called during scene transitions
|
/// - NOT called during scene transitions
|
||||||
/// - Use for: Triggering UI updates, broadcasting load events
|
/// - Use for: Triggering UI updates, broadcasting load events
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnGlobalLoadCompleted()
|
internal virtual void OnGlobalLoadCompleted()
|
||||||
{
|
{
|
||||||
// Default: no-op
|
// Default: no-op
|
||||||
}
|
}
|
||||||
@@ -293,19 +269,21 @@ namespace Core.Lifecycle
|
|||||||
/// - Called ONCE before save file is written
|
/// - Called ONCE before save file is written
|
||||||
/// - NOT called during scene transitions
|
/// - NOT called during scene transitions
|
||||||
/// - Use for: Final validation, cleanup operations
|
/// - Use for: Final validation, cleanup operations
|
||||||
|
///
|
||||||
|
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnGlobalSaveStarted()
|
internal virtual void OnGlobalSaveStarted()
|
||||||
{
|
{
|
||||||
// Default: no-op
|
// Default: no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called during OnDestroy before component is destroyed.
|
/// Called during OnDestroy before component is destroyed.
|
||||||
/// Called in REVERSE priority order (higher values execute first).
|
|
||||||
/// NOTE: Most cleanup is automatic (managed events, auto-registrations).
|
/// NOTE: Most cleanup is automatic (managed events, auto-registrations).
|
||||||
/// Only override if you need custom cleanup logic.
|
/// Only override if you need custom cleanup logic.
|
||||||
|
/// Internal visibility allows LifecycleManager to call directly. Override in derived classes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void OnManagedDestroy()
|
internal virtual void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Override in derived classes
|
// Override in derived classes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,144 @@
|
|||||||
namespace Core
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Centralized logging system with automatic class/method tagging and editor integration.
|
||||||
|
/// Broadcasts log entries to custom editor windows for filtering and analysis.
|
||||||
|
/// </summary>
|
||||||
public static class Logging
|
public static class Logging
|
||||||
{
|
{
|
||||||
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
/// <summary>
|
||||||
public static void Debug(object message)
|
/// Event fired when a new log entry is added. Subscribe to this in editor windows.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action<LogEntry> OnLogEntryAdded;
|
||||||
|
|
||||||
|
// Store recent logs for late-subscriber editor windows (e.g., windows opened after play mode started)
|
||||||
|
private static readonly List<LogEntry> RecentLogs = new List<LogEntry>();
|
||||||
|
private const int MaxStoredLogs = 5000; // Prevent memory bloat
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all recent logs. Used by editor windows when they first open.
|
||||||
|
/// </summary>
|
||||||
|
public static IReadOnlyList<LogEntry> GetRecentLogs() => RecentLogs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear all stored logs. Useful for editor windows "Clear" button.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearLogs()
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.Log(message);
|
RecentLogs.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||||
public static void Warning(object message)
|
public static void Debug(string message,
|
||||||
|
[CallerFilePath] string filePath = "",
|
||||||
|
[CallerMemberName] string memberName = "")
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.LogWarning(message);
|
LogInternal(LogLevel.Debug, message, filePath, memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||||
|
public static void Info(string message,
|
||||||
|
[CallerFilePath] string filePath = "",
|
||||||
|
[CallerMemberName] string memberName = "")
|
||||||
|
{
|
||||||
|
LogInternal(LogLevel.Info, message, filePath, memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Diagnostics.Conditional("ENABLE_LOG")]
|
||||||
|
public static void Warning(string message,
|
||||||
|
[CallerFilePath] string filePath = "",
|
||||||
|
[CallerMemberName] string memberName = "")
|
||||||
|
{
|
||||||
|
LogInternal(LogLevel.Warning, message, filePath, memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Error(string message,
|
||||||
|
[CallerFilePath] string filePath = "",
|
||||||
|
[CallerMemberName] string memberName = "")
|
||||||
|
{
|
||||||
|
LogInternal(LogLevel.Error, message, filePath, memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogInternal(LogLevel level, string message, string filePath, string memberName)
|
||||||
|
{
|
||||||
|
string className = Path.GetFileNameWithoutExtension(filePath);
|
||||||
|
string formattedMessage = $"[{className}][{memberName}] {message}";
|
||||||
|
|
||||||
|
// Create log entry
|
||||||
|
var entry = new LogEntry(className, memberName, message, level, Time.realtimeSinceStartup);
|
||||||
|
|
||||||
|
// Store for late subscribers
|
||||||
|
RecentLogs.Add(entry);
|
||||||
|
if (RecentLogs.Count > MaxStoredLogs)
|
||||||
|
RecentLogs.RemoveAt(0);
|
||||||
|
|
||||||
|
// Broadcast to editor windows (editor-only, won't fire in builds)
|
||||||
|
OnLogEntryAdded?.Invoke(entry);
|
||||||
|
|
||||||
|
// Also log to Unity console
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case LogLevel.Debug:
|
||||||
|
case LogLevel.Info:
|
||||||
|
UnityEngine.Debug.Log(formattedMessage);
|
||||||
|
break;
|
||||||
|
case LogLevel.Warning:
|
||||||
|
UnityEngine.Debug.LogWarning(formattedMessage);
|
||||||
|
break;
|
||||||
|
case LogLevel.Error:
|
||||||
|
UnityEngine.Debug.LogError(formattedMessage);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single log entry with class, method, message, level, and timestamp.
|
||||||
|
/// </summary>
|
||||||
|
public class LogEntry
|
||||||
|
{
|
||||||
|
public string ClassName { get; }
|
||||||
|
public string MethodName { get; }
|
||||||
|
public string Message { get; }
|
||||||
|
public LogLevel Level { get; }
|
||||||
|
public float Timestamp { get; }
|
||||||
|
|
||||||
|
public LogEntry(string className, string methodName, string message, LogLevel level, float timestamp)
|
||||||
|
{
|
||||||
|
ClassName = className;
|
||||||
|
MethodName = methodName;
|
||||||
|
Message = message;
|
||||||
|
Level = level;
|
||||||
|
Timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formatted message with class and method tags.
|
||||||
|
/// Format: [ClassName][MethodName] Message
|
||||||
|
/// </summary>
|
||||||
|
public string FormattedMessage => $"[{ClassName}][{MethodName}] {Message}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Full formatted message with timestamp and level.
|
||||||
|
/// Format: [12.34s][Debug][ClassName][MethodName] Message
|
||||||
|
/// </summary>
|
||||||
|
public string FullFormattedMessage => $"[{Timestamp:F2}s][{Level}]{FormattedMessage}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log severity levels.
|
||||||
|
/// </summary>
|
||||||
|
public enum LogLevel
|
||||||
|
{
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,9 +24,6 @@ namespace AppleHills.Core
|
|||||||
|
|
||||||
#endregion Singleton Setup
|
#endregion Singleton Setup
|
||||||
|
|
||||||
// Very early initialization - QuickAccess should be available immediately
|
|
||||||
public override int ManagedAwakePriority => 5;
|
|
||||||
|
|
||||||
#region Manager Instances
|
#region Manager Instances
|
||||||
|
|
||||||
// Core Managers
|
// Core Managers
|
||||||
@@ -129,20 +126,18 @@ namespace AppleHills.Core
|
|||||||
|
|
||||||
#region Lifecycle Methods
|
#region Lifecycle Methods
|
||||||
|
|
||||||
private new void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// QuickAccess has minimal initialization
|
// QuickAccess has minimal initialization
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneUnloading()
|
internal override void OnSceneUnloading()
|
||||||
{
|
{
|
||||||
// Clear references BEFORE scene unloads for better cleanup timing
|
// Clear references BEFORE scene unloads for better cleanup timing
|
||||||
ClearReferences();
|
ClearReferences();
|
||||||
|
|||||||
@@ -43,14 +43,10 @@ namespace Core.SaveLoad
|
|||||||
public event Action<string> OnLoadCompleted;
|
public event Action<string> OnLoadCompleted;
|
||||||
public event Action OnParticipantStatesRestored;
|
public event Action OnParticipantStatesRestored;
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
|
||||||
public override int ManagedAwakePriority => 20; // After GameManager and SceneManagerService
|
|
||||||
|
|
||||||
private new void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Initialize critical state immediately
|
// Initialize critical state immediately
|
||||||
@@ -58,7 +54,7 @@ namespace Core.SaveLoad
|
|||||||
IsRestoringState = false;
|
IsRestoringState = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug("[SaveLoadManager] Initialized");
|
Logging.Debug("[SaveLoadManager] Initialized");
|
||||||
|
|
||||||
@@ -69,19 +65,19 @@ namespace Core.SaveLoad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
// SaveableInteractables now auto-register via ManagedBehaviour lifecycle
|
// SaveableInteractables now auto-register via ManagedBehaviour lifecycle
|
||||||
// No need to discover and register them manually
|
// No need to discover and register them manually
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
// SaveLoadManager orchestrates saves, doesn't participate in them
|
// SaveLoadManager orchestrates saves, doesn't participate in them
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string OnGlobalSaveRequested()
|
internal override string OnGlobalSaveRequested()
|
||||||
{
|
{
|
||||||
// SaveLoadManager orchestrates saves, doesn't participate in them
|
// SaveLoadManager orchestrates saves, doesn't participate in them
|
||||||
return null;
|
return null;
|
||||||
@@ -97,10 +93,8 @@ namespace Core.SaveLoad
|
|||||||
|
|
||||||
// ...existing code...
|
// ...existing code...
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy(); // Important: call base to unregister from LifecycleManager
|
|
||||||
|
|
||||||
if (_instance == this)
|
if (_instance == this)
|
||||||
{
|
{
|
||||||
_instance = null;
|
_instance = null;
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ namespace Core
|
|||||||
// Enable save/load participation
|
// Enable save/load participation
|
||||||
public override bool AutoRegisterForSave => true;
|
public override bool AutoRegisterForSave => true;
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
base.OnManagedAwake();
|
||||||
|
|
||||||
_director = GetComponent<PlayableDirector>();
|
_director = GetComponent<PlayableDirector>();
|
||||||
if (_director != null)
|
if (_director != null)
|
||||||
@@ -42,10 +42,8 @@ namespace Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
if (_director != null)
|
if (_director != null)
|
||||||
{
|
{
|
||||||
_director.stopped -= OnDirectorStopped;
|
_director.stopped -= OnDirectorStopped;
|
||||||
@@ -65,7 +63,7 @@ namespace Core
|
|||||||
|
|
||||||
#region Save/Load Implementation
|
#region Save/Load Implementation
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
var saveData = new PlayableDirectorSaveData
|
var saveData = new PlayableDirectorSaveData
|
||||||
{
|
{
|
||||||
@@ -77,7 +75,7 @@ namespace Core
|
|||||||
return JsonUtility.ToJson(saveData);
|
return JsonUtility.ToJson(saveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,14 +44,10 @@ namespace Core
|
|||||||
private LogVerbosity _logVerbosity = LogVerbosity.Debug;
|
private LogVerbosity _logVerbosity = LogVerbosity.Debug;
|
||||||
private const string BootstrapSceneName = "BootstrapScene";
|
private const string BootstrapSceneName = "BootstrapScene";
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
|
||||||
public override int ManagedAwakePriority => 15; // Core infrastructure, after GameManager
|
|
||||||
|
|
||||||
private new void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Initialize current scene tracking - critical for scene management
|
// Initialize current scene tracking - critical for scene management
|
||||||
@@ -65,17 +61,17 @@ namespace Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Set up loading screen reference and events
|
// Set up loading screen reference and events
|
||||||
// This must happen in ManagedAwake because LoadingScreenController instance needs to be set first
|
// This must happen in ManagedStart because LoadingScreenController instance needs to be set first
|
||||||
_loadingScreen = LoadingScreenController.Instance;
|
_loadingScreen = LoadingScreenController.Instance;
|
||||||
SetupLoadingScreenEvents();
|
SetupLoadingScreenEvents();
|
||||||
|
|
||||||
// Load verbosity settings
|
// Load verbosity settings
|
||||||
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
|
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
|
||||||
|
|
||||||
LogDebugMessage($"SceneManagerService initialized, current scene is: {CurrentGameplayScene}");
|
Logging.Debug($"SceneManagerService initialized, current scene is: {CurrentGameplayScene}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -93,19 +89,19 @@ namespace Core
|
|||||||
if (activeScene.name != BootstrapSceneName)
|
if (activeScene.name != BootstrapSceneName)
|
||||||
{
|
{
|
||||||
CurrentGameplayScene = activeScene.name;
|
CurrentGameplayScene = activeScene.name;
|
||||||
LogDebugMessage($"Initialized with current scene: {CurrentGameplayScene}");
|
Logging.Debug($"Initialized with current scene: {CurrentGameplayScene}");
|
||||||
}
|
}
|
||||||
// Otherwise default to MainMenu
|
// Otherwise default to MainMenu
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CurrentGameplayScene = "AppleHillsOverworld";
|
CurrentGameplayScene = "AppleHillsOverworld";
|
||||||
LogDebugMessage($"Initialized with default scene: {CurrentGameplayScene}");
|
Logging.Debug($"Initialized with default scene: {CurrentGameplayScene}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CurrentGameplayScene = "AppleHillsOverworld";
|
CurrentGameplayScene = "AppleHillsOverworld";
|
||||||
LogDebugMessage($"No valid active scene, defaulting to: {CurrentGameplayScene}");
|
Logging.Debug($"No valid active scene, defaulting to: {CurrentGameplayScene}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +303,7 @@ namespace Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 2: Broadcast scene unloading - notify components to cleanup
|
// PHASE 2: Broadcast scene unloading - notify components to cleanup
|
||||||
LogDebugMessage($"Broadcasting OnSceneUnloading for: {oldSceneName}");
|
Logging.Debug($"Broadcasting OnSceneUnloading for: {oldSceneName}");
|
||||||
LifecycleManager.Instance?.BroadcastSceneUnloading(oldSceneName);
|
LifecycleManager.Instance?.BroadcastSceneUnloading(oldSceneName);
|
||||||
|
|
||||||
// PHASE 3: Save scene-specific data via SaveLoadManager (unless skipSave is true)
|
// PHASE 3: Save scene-specific data via SaveLoadManager (unless skipSave is true)
|
||||||
@@ -316,19 +312,19 @@ namespace Core
|
|||||||
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
||||||
if (debugSettings.useSaveLoadSystem)
|
if (debugSettings.useSaveLoadSystem)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Saving scene data for: {oldSceneName}");
|
Logging.Debug($"Saving scene data for: {oldSceneName}");
|
||||||
SaveLoadManager.Instance.SaveSceneData();
|
SaveLoadManager.Instance.SaveSceneData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (skipSave)
|
else if (skipSave)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Skipping save for: {oldSceneName} (skipSave=true)");
|
Logging.Debug($"Skipping save for: {oldSceneName} (skipSave=true)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 4: Clear PuzzleManager state before scene transition
|
// PHASE 4: Clear PuzzleManager state before scene transition
|
||||||
if (PuzzleS.PuzzleManager.Instance != null)
|
if (PuzzleS.PuzzleManager.Instance != null)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Clearing puzzle state before scene transition");
|
Logging.Debug($"Clearing puzzle state before scene transition");
|
||||||
PuzzleS.PuzzleManager.Instance.ClearPuzzleState();
|
PuzzleS.PuzzleManager.Instance.ClearPuzzleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +348,7 @@ namespace Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Warning($"[SceneManagerService] Previous scene '{oldSceneName}' is not loaded, skipping unload.");
|
Logging.Warning($"Previous scene '{oldSceneName}' is not loaded, skipping unload.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,15 +360,15 @@ namespace Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 8: Begin scene loading mode - enables priority-ordered component initialization
|
// PHASE 8: Begin scene loading mode - enables priority-ordered component initialization
|
||||||
LogDebugMessage($"Beginning scene load for: {newSceneName}");
|
Logging.Debug($"Beginning scene load for: {newSceneName}");
|
||||||
LifecycleManager.Instance?.BeginSceneLoad(newSceneName);
|
LifecycleManager.Instance?.BeginSceneLoad(newSceneName);
|
||||||
|
|
||||||
// PHASE 9: Load new gameplay scene
|
// PHASE 9: Load new gameplay scene
|
||||||
await LoadSceneAsync(newSceneName, progress);
|
await LoadSceneAsync(newSceneName, progress);
|
||||||
CurrentGameplayScene = newSceneName;
|
CurrentGameplayScene = newSceneName;
|
||||||
|
|
||||||
// PHASE 10: Broadcast scene ready - processes batched components in priority order, then calls OnSceneReady
|
// PHASE 10: Broadcast scene ready - processes batched components, then calls OnSceneReady
|
||||||
LogDebugMessage($"Broadcasting OnSceneReady for: {newSceneName}");
|
Logging.Debug($"Broadcasting OnSceneReady for: {newSceneName}");
|
||||||
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);
|
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);
|
||||||
|
|
||||||
// PHASE 11: Restore scene-specific data via SaveLoadManager
|
// PHASE 11: Restore scene-specific data via SaveLoadManager
|
||||||
@@ -381,14 +377,14 @@ namespace Core
|
|||||||
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
||||||
if (debugSettings.useSaveLoadSystem)
|
if (debugSettings.useSaveLoadSystem)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Restoring scene data for: {newSceneName}");
|
Logging.Debug($"Restoring scene data for: {newSceneName}");
|
||||||
SaveLoadManager.Instance.RestoreSceneData();
|
SaveLoadManager.Instance.RestoreSceneData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (skipSave)
|
else if (skipSave)
|
||||||
{
|
{
|
||||||
SaveLoadManager.Instance.RestoreSceneData();
|
SaveLoadManager.Instance.RestoreSceneData();
|
||||||
LogDebugMessage($"Skipping restore for: {newSceneName} (skipSave=true)");
|
Logging.Debug($"Skipping restore for: {newSceneName} (skipSave=true)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHASE 12: Only hide the loading screen if autoHideLoadingScreen is true
|
// PHASE 12: Only hide the loading screen if autoHideLoadingScreen is true
|
||||||
@@ -397,13 +393,5 @@ namespace Core
|
|||||||
_loadingScreen.HideLoadingScreen();
|
_loadingScreen.HideLoadingScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (_logVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[SceneManagerService] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,18 @@ namespace Core
|
|||||||
public GameObject orientationPromptPrefab;
|
public GameObject orientationPromptPrefab;
|
||||||
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
|
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
internal override void OnManagedAwake()
|
||||||
public override int ManagedAwakePriority => 70; // Platform-specific utility
|
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Load verbosity settings early (GameManager sets up settings in its Awake)
|
// Load verbosity settings early (GameManager sets up settings in its OnManagedAwake)
|
||||||
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
|
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
|
||||||
|
|
||||||
LogDebugMessage("Initialized");
|
Logging.Debug("Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Subscribe to SceneManagerService to enforce orientation on every scene load
|
// Subscribe to SceneManagerService to enforce orientation on every scene load
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
@@ -51,7 +46,7 @@ namespace Core
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
// Handle orientation when scene is ready (initial scene)
|
// Handle orientation when scene is ready (initial scene)
|
||||||
// Note: This fires for the scene that just loaded, LifecycleManager tracks which scene
|
// Note: This fires for the scene that just loaded, LifecycleManager tracks which scene
|
||||||
@@ -73,7 +68,7 @@ namespace Core
|
|||||||
if (sceneName.ToLower().Contains("bootstrap"))
|
if (sceneName.ToLower().Contains("bootstrap"))
|
||||||
{
|
{
|
||||||
// Bootstrap being loaded additively, don't do anything
|
// Bootstrap being loaded additively, don't do anything
|
||||||
LogDebugMessage($"Detected bootstrapped scene: '{sceneName}'. Skipping orientation enforcement.");
|
Logging.Debug($"Detected bootstrapped scene: '{sceneName}'. Skipping orientation enforcement.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,37 +78,35 @@ namespace Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogDebugMessage($"No orientationConfig assigned. Defaulting to Landscape for scene '{sceneName}'");
|
Logging.Debug($"No orientationConfig assigned. Defaulting to Landscape for scene '{sceneName}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (requirement)
|
switch (requirement)
|
||||||
{
|
{
|
||||||
case ScreenOrientationRequirement.Portrait:
|
case ScreenOrientationRequirement.Portrait:
|
||||||
LogDebugMessage($"Forcing Portrait for scene '{sceneName}'");
|
Logging.Debug($"Forcing Portrait for scene '{sceneName}'");
|
||||||
StartCoroutine(ForcePortrait());
|
StartCoroutine(ForcePortrait());
|
||||||
break;
|
break;
|
||||||
case ScreenOrientationRequirement.Landscape:
|
case ScreenOrientationRequirement.Landscape:
|
||||||
LogDebugMessage($"Forcing Landscape for scene '{sceneName}'");
|
Logging.Debug($"Forcing Landscape for scene '{sceneName}'");
|
||||||
StartCoroutine(ForceLandscape());
|
StartCoroutine(ForceLandscape());
|
||||||
break;
|
break;
|
||||||
case ScreenOrientationRequirement.NotApplicable:
|
case ScreenOrientationRequirement.NotApplicable:
|
||||||
default:
|
default:
|
||||||
// Default to landscape when no specific requirement is found
|
// Default to landscape when no specific requirement is found
|
||||||
LogDebugMessage($"No specific orientation for scene '{sceneName}'. Defaulting to Landscape");
|
Logging.Debug($"No specific orientation for scene '{sceneName}'. Defaulting to Landscape");
|
||||||
StartCoroutine(ForceLandscape());
|
StartCoroutine(ForceLandscape());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Unsubscribe from events to prevent memory leaks
|
// Unsubscribe from events to prevent memory leaks
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
{
|
{
|
||||||
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
|
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnDestroy(); // Important: call base
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -127,12 +120,12 @@ namespace Core
|
|||||||
if (!currentlyLandscape)
|
if (!currentlyLandscape)
|
||||||
{
|
{
|
||||||
// Lock it to portrait and allow the device to orient itself
|
// Lock it to portrait and allow the device to orient itself
|
||||||
LogDebugMessage($"Actually forcing Portrait from previous: {Screen.orientation}");
|
Logging.Debug($"Actually forcing Portrait from previous: {Screen.orientation}");
|
||||||
Screen.orientation = ScreenOrientation.LandscapeRight;
|
Screen.orientation = ScreenOrientation.LandscapeRight;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Skipping Landscape enforcement, device already in: {Screen.orientation}");
|
Logging.Debug($"Skipping Landscape enforcement, device already in: {Screen.orientation}");
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return null;
|
yield return null;
|
||||||
@@ -160,12 +153,12 @@ namespace Core
|
|||||||
if (!currentlyPortrait)
|
if (!currentlyPortrait)
|
||||||
{
|
{
|
||||||
// Lock it to portrait and allow the device to orient itself
|
// Lock it to portrait and allow the device to orient itself
|
||||||
LogDebugMessage($"Actually forcing Portrait from previous: {Screen.orientation}");
|
Logging.Debug($"Actually forcing Portrait from previous: {Screen.orientation}");
|
||||||
Screen.orientation = ScreenOrientation.PortraitUpsideDown;
|
Screen.orientation = ScreenOrientation.PortraitUpsideDown;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Skipping Portrait enforcement, device already in: {Screen.orientation}");
|
Logging.Debug($"Skipping Portrait enforcement, device already in: {Screen.orientation}");
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return null;
|
yield return null;
|
||||||
@@ -181,13 +174,5 @@ namespace Core
|
|||||||
// Allow device to auto-rotate to correct portrait orientation
|
// Allow device to auto-rotate to correct portrait orientation
|
||||||
Screen.orientation = ScreenOrientation.AutoRotation;
|
Screen.orientation = ScreenOrientation.AutoRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (_logVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[SceneOrientationEnforcer] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using AppleHills.Core.Settings;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Core.Settings
|
namespace Core.Settings
|
||||||
{
|
{
|
||||||
@@ -21,7 +19,7 @@ namespace Core.Settings
|
|||||||
public static void Register<T>(T service) where T : class
|
public static void Register<T>(T service) where T : class
|
||||||
{
|
{
|
||||||
Services[typeof(T)] = service;
|
Services[typeof(T)] = service;
|
||||||
LogDebugMessage($"Service registered: {typeof(T).Name}");
|
Logging.Debug($"Service registered: {typeof(T).Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,7 +34,7 @@ namespace Core.Settings
|
|||||||
return service as T;
|
return service as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.Warning($"[ServiceLocator] Service of type {typeof(T).Name} not found!");
|
Logging.Warning($"Service of type {typeof(T).Name} not found!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,16 +44,7 @@ namespace Core.Settings
|
|||||||
public static void Clear()
|
public static void Clear()
|
||||||
{
|
{
|
||||||
Services.Clear();
|
Services.Clear();
|
||||||
LogDebugMessage("All services cleared");
|
Logging.Debug("All services cleared");
|
||||||
}
|
|
||||||
|
|
||||||
private static void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity <=
|
|
||||||
LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[ServiceLocator] {message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Pixelplacement;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using Core;
|
using Core;
|
||||||
|
using Core.Lifecycle;
|
||||||
using Core.SaveLoad;
|
using Core.SaveLoad;
|
||||||
using UnityEngine.Audio;
|
using UnityEngine.Audio;
|
||||||
|
|
||||||
public class PicnicBehaviour : MonoBehaviour
|
public class PicnicBehaviour : ManagedBehaviour
|
||||||
{
|
{
|
||||||
[Header("Random Call Settings")]
|
[Header("Random Call Settings")]
|
||||||
public float getDistractedMin = 2f;
|
public float getDistractedMin = 2f;
|
||||||
@@ -17,7 +18,8 @@ public class PicnicBehaviour : MonoBehaviour
|
|||||||
private Animator animator;
|
private Animator animator;
|
||||||
|
|
||||||
[Header("The FakeChocolate to destroy!")]
|
[Header("The FakeChocolate to destroy!")]
|
||||||
[SerializeField] private GameObject fakeChocolate; // Assign in Inspector
|
[SerializeField] private GameObject fakeChocolate;
|
||||||
|
[SerializeField] private GameObject realChocolate;
|
||||||
|
|
||||||
private AppleAudioSource _audioSource;
|
private AppleAudioSource _audioSource;
|
||||||
public AudioResource distractedAudioClips;
|
public AudioResource distractedAudioClips;
|
||||||
@@ -25,32 +27,44 @@ public class PicnicBehaviour : MonoBehaviour
|
|||||||
public AudioResource feederClips;
|
public AudioResource feederClips;
|
||||||
public AudioResource moanerClips;
|
public AudioResource moanerClips;
|
||||||
|
|
||||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
// Save system configuration
|
||||||
void Start()
|
public override bool AutoRegisterForSave => true;
|
||||||
{
|
|
||||||
StartCoroutine(StateCycleRoutine());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Awake()
|
// Runtime state tracking
|
||||||
|
private bool _fakeChocolateDestroyed;
|
||||||
|
|
||||||
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
stateMachine = GetComponent<AppleMachine>();
|
stateMachine = GetComponent<AppleMachine>();
|
||||||
animator = GetComponent<Animator>();
|
animator = GetComponent<Animator>();
|
||||||
_audioSource = GetComponent<AppleAudioSource>();
|
_audioSource = GetComponent<AppleAudioSource>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreCompleted()
|
||||||
|
{
|
||||||
|
if (_fakeChocolateDestroyed)
|
||||||
|
{
|
||||||
|
DestroyChocolateObjects();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StartCoroutine(StateCycleRoutine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerator StateCycleRoutine()
|
private IEnumerator StateCycleRoutine()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// Distracted state
|
// Distracted state
|
||||||
float distractedWait = Random.Range(getDistractedMin, getDistractedMax);
|
float distractedWait = UnityEngine.Random.Range(getDistractedMin, getDistractedMax);
|
||||||
stateMachine.ChangeState("Picnic PPL Distracted");
|
stateMachine.ChangeState("Picnic PPL Distracted");
|
||||||
animator.SetBool("theyDistracted", true);
|
animator.SetBool("theyDistracted", true);
|
||||||
_audioSource.Stop();
|
_audioSource.Stop();
|
||||||
yield return new WaitForSeconds(distractedWait);
|
yield return new WaitForSeconds(distractedWait);
|
||||||
|
|
||||||
// Chilling state
|
// Chilling state
|
||||||
float chillingWait = Random.Range(getFlirtyMin, getFlirtyMax);
|
float chillingWait = UnityEngine.Random.Range(getFlirtyMin, getFlirtyMax);
|
||||||
stateMachine.ChangeState("Picnic PPL Chilling");
|
stateMachine.ChangeState("Picnic PPL Chilling");
|
||||||
animator.SetBool("theyDistracted", false);
|
animator.SetBool("theyDistracted", false);
|
||||||
_audioSource.Stop();
|
_audioSource.Stop();
|
||||||
@@ -58,27 +72,33 @@ public class PicnicBehaviour : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopAudio()
|
|
||||||
{
|
|
||||||
_audioSource.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void triedToStealChocolate()
|
public void triedToStealChocolate()
|
||||||
{
|
{
|
||||||
_audioSource.Stop();
|
_audioSource.Stop();
|
||||||
animator.SetTrigger("theyAngry");
|
animator.SetTrigger("theyAngry");
|
||||||
//stateMachine.ChangeState("Picnic PPL Angry");
|
|
||||||
Logging.Debug("Hey! Don't steal my chocolate!");
|
Logging.Debug("Hey! Don't steal my chocolate!");
|
||||||
_audioSource.audioSource.resource = angryAudioClips;
|
_audioSource.audioSource.resource = angryAudioClips;
|
||||||
_audioSource.Play(0);
|
_audioSource.Play(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroyFakeChocolate()
|
public void destroyFakeChocolate()
|
||||||
|
{
|
||||||
|
_fakeChocolateDestroyed = true;
|
||||||
|
Destroy(fakeChocolate);
|
||||||
|
Destroy(realChocolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DestroyChocolateObjects()
|
||||||
{
|
{
|
||||||
if (fakeChocolate != null)
|
if (fakeChocolate != null)
|
||||||
{
|
{
|
||||||
Destroy(fakeChocolate);
|
Destroy(fakeChocolate);
|
||||||
fakeChocolate = null; // Optional: clear reference
|
fakeChocolate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realChocolate != null)
|
||||||
|
{
|
||||||
|
realChocolate.SetActive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,5 +120,33 @@ public class PicnicBehaviour : MonoBehaviour
|
|||||||
_audioSource.Play(0);
|
_audioSource.Play(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override string OnSceneSaveRequested()
|
||||||
|
{
|
||||||
|
var state = new PicnicBehaviourState { fakeChocolateDestroyed = _fakeChocolateDestroyed };
|
||||||
|
return JsonUtility.ToJson(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(serializedData)) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var state = JsonUtility.FromJson<PicnicBehaviourState>(serializedData);
|
||||||
|
if (state != null)
|
||||||
|
{
|
||||||
|
_fakeChocolateDestroyed = state.fakeChocolateDestroyed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[PicnicBehaviour] Failed to restore state: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class PicnicBehaviourState
|
||||||
|
{
|
||||||
|
public bool fakeChocolateDestroyed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class soundBird_CanFly : ManagedBehaviour
|
|||||||
|
|
||||||
#region Save/Load Implementation
|
#region Save/Load Implementation
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
var saveData = new SoundBirdSaveData
|
var saveData = new SoundBirdSaveData
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ public class soundBird_CanFly : ManagedBehaviour
|
|||||||
return JsonUtility.ToJson(saveData);
|
return JsonUtility.ToJson(saveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,25 +40,20 @@ namespace Data.CardSystem
|
|||||||
// Event callbacks using System.Action
|
// Event callbacks using System.Action
|
||||||
public event Action<List<CardData>> OnBoosterOpened;
|
public event Action<List<CardData>> OnBoosterOpened;
|
||||||
public event Action<CardData> OnCardCollected;
|
public event Action<CardData> OnCardCollected;
|
||||||
public event Action<CardData> OnCardRarityUpgraded;
|
|
||||||
public event Action<int> OnBoosterCountChanged;
|
public event Action<int> OnBoosterCountChanged;
|
||||||
public event Action<CardData> OnPendingCardAdded;
|
public event Action<CardData> OnPendingCardAdded;
|
||||||
public event Action<CardData> OnCardPlacedInAlbum;
|
public event Action<CardData> OnCardPlacedInAlbum;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 60; // Data systems
|
internal override void OnManagedAwake()
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Load card definitions from Addressables, then register with save system
|
// Load card definitions from Addressables, then register with save system
|
||||||
LoadCardDefinitionsFromAddressables();
|
LoadCardDefinitionsFromAddressables();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug("[CardSystemManager] Initialized");
|
Logging.Debug("[CardSystemManager] Initialized");
|
||||||
}
|
}
|
||||||
@@ -720,13 +715,13 @@ namespace Data.CardSystem
|
|||||||
|
|
||||||
#region Save/Load Lifecycle Hooks
|
#region Save/Load Lifecycle Hooks
|
||||||
|
|
||||||
protected override string OnGlobalSaveRequested()
|
internal override string OnGlobalSaveRequested()
|
||||||
{
|
{
|
||||||
var state = ExportCardCollectionState();
|
var state = ExportCardCollectionState();
|
||||||
return JsonUtility.ToJson(state);
|
return JsonUtility.ToJson(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnGlobalRestoreRequested(string serializedData)
|
internal override void OnGlobalRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Core;
|
using Core;
|
||||||
using Core.Lifecycle;
|
using Core.Lifecycle;
|
||||||
using Interactions;
|
using Interactions;
|
||||||
@@ -34,10 +32,7 @@ namespace Dialogue
|
|||||||
public bool IsCompleted { get; private set; }
|
public bool IsCompleted { get; private set; }
|
||||||
public string CurrentSpeakerName => dialogueGraph?.speakerName;
|
public string CurrentSpeakerName => dialogueGraph?.speakerName;
|
||||||
|
|
||||||
|
internal override void OnManagedStart()
|
||||||
public override int ManagedAwakePriority => 150; // Dialogue systems
|
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
|
||||||
{
|
{
|
||||||
// Get required components
|
// Get required components
|
||||||
appleAudioSource = GetComponent<AppleAudioSource>();
|
appleAudioSource = GetComponent<AppleAudioSource>();
|
||||||
@@ -186,7 +181,7 @@ namespace Dialogue
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Unregister from events
|
// Unregister from events
|
||||||
if (PuzzleManager.Instance != null)
|
if (PuzzleManager.Instance != null)
|
||||||
|
|||||||
@@ -50,13 +50,9 @@ namespace Input
|
|||||||
private bool isHoldActive;
|
private bool isHoldActive;
|
||||||
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
|
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 25; // Input infrastructure
|
internal override void OnManagedAwake()
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Load verbosity settings early
|
// Load verbosity settings early
|
||||||
@@ -89,10 +85,10 @@ namespace Input
|
|||||||
SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name);
|
SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Subscribe to scene load events from SceneManagerService
|
// Subscribe to scene load events from SceneManagerService
|
||||||
// This must happen in ManagedAwake because SceneManagerService instance needs to be set first
|
// This must happen in ManagedStart because SceneManagerService instance needs to be set first
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
{
|
{
|
||||||
SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
|
SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
|
||||||
@@ -104,11 +100,11 @@ namespace Input
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnSceneLoadCompleted(string sceneName)
|
private void OnSceneLoadCompleted(string sceneName)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Scene loaded: {sceneName}, restoring input mode");
|
Logging.Debug($"Scene loaded: {sceneName}, restoring input mode");
|
||||||
SwitchInputOnSceneLoaded(sceneName);
|
SwitchInputOnSceneLoaded(sceneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Unsubscribe from SceneManagerService events
|
// Unsubscribe from SceneManagerService events
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
@@ -116,7 +112,6 @@ namespace Input
|
|||||||
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
|
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnDestroy();
|
|
||||||
// Input action cleanup happens automatically
|
// Input action cleanup happens automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,24 +177,24 @@ namespace Input
|
|||||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||||
LogDebugMessage($"TapMove performed at {worldPos2D}");
|
Logging.Debug($"TapMove performed at {worldPos2D}");
|
||||||
|
|
||||||
// First try to delegate to an override consumer if available
|
// First try to delegate to an override consumer if available
|
||||||
if (TryDelegateToOverrideConsumer(screenPos, worldPos2D))
|
if (TryDelegateToOverrideConsumer(screenPos, worldPos2D))
|
||||||
{
|
{
|
||||||
LogDebugMessage("Tap delegated to override consumer");
|
Logging.Debug("Tap delegated to override consumer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then try to delegate to any ITouchInputConsumer (UI or world interactable)
|
// Then try to delegate to any ITouchInputConsumer (UI or world interactable)
|
||||||
if (!TryDelegateToAnyInputConsumer(screenPos, worldPos2D))
|
if (!TryDelegateToAnyInputConsumer(screenPos, worldPos2D))
|
||||||
{
|
{
|
||||||
LogDebugMessage("No input consumer found, forwarding tap to default consumer");
|
Logging.Debug("No input consumer found, forwarding tap to default consumer");
|
||||||
defaultConsumer?.OnTap(worldPos2D);
|
defaultConsumer?.OnTap(worldPos2D);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogDebugMessage("Tap delegated to input consumer");
|
Logging.Debug("Tap delegated to input consumer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,13 +207,13 @@ namespace Input
|
|||||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||||
LogDebugMessage($"HoldMove started at {worldPos2D}");
|
Logging.Debug($"HoldMove started at {worldPos2D}");
|
||||||
|
|
||||||
// First check for override consumers
|
// First check for override consumers
|
||||||
if (_overrideConsumers.Count > 0)
|
if (_overrideConsumers.Count > 0)
|
||||||
{
|
{
|
||||||
_activeHoldConsumer = _overrideConsumers[_overrideConsumers.Count - 1];
|
_activeHoldConsumer = _overrideConsumers[_overrideConsumers.Count - 1];
|
||||||
LogDebugMessage($"Hold delegated to override consumer: {_activeHoldConsumer}");
|
Logging.Debug($"Hold delegated to override consumer: {_activeHoldConsumer}");
|
||||||
_activeHoldConsumer.OnHoldStart(worldPos2D);
|
_activeHoldConsumer.OnHoldStart(worldPos2D);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -238,7 +233,7 @@ namespace Input
|
|||||||
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
Vector2 screenPos = positionAction.ReadValue<Vector2>();
|
||||||
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
|
||||||
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
|
||||||
LogDebugMessage($"HoldMove canceled at {worldPos2D}");
|
Logging.Debug($"HoldMove canceled at {worldPos2D}");
|
||||||
|
|
||||||
// Notify the active hold consumer that the hold has ended
|
// Notify the active hold consumer that the hold has ended
|
||||||
_activeHoldConsumer?.OnHoldEnd(worldPos2D);
|
_activeHoldConsumer?.OnHoldEnd(worldPos2D);
|
||||||
@@ -302,7 +297,7 @@ namespace Input
|
|||||||
}
|
}
|
||||||
if (consumer != null)
|
if (consumer != null)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Delegating tap to UI consumer at {screenPos} (GameObject: {result.gameObject.name})");
|
Logging.Debug($"Delegating tap to UI consumer at {screenPos} (GameObject: {result.gameObject.name})");
|
||||||
consumer.OnTap(screenPos);
|
consumer.OnTap(screenPos);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -331,7 +326,7 @@ namespace Input
|
|||||||
}
|
}
|
||||||
if (consumer != null)
|
if (consumer != null)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Delegating tap to consumer at {worldPos} (GameObject: {hitWithMask.gameObject.name})");
|
Logging.Debug($"Delegating tap to consumer at {worldPos} (GameObject: {hitWithMask.gameObject.name})");
|
||||||
consumer.OnTap(worldPos);
|
consumer.OnTap(worldPos);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -345,15 +340,15 @@ namespace Input
|
|||||||
var consumer = hit.GetComponent<ITouchInputConsumer>();
|
var consumer = hit.GetComponent<ITouchInputConsumer>();
|
||||||
if (consumer != null)
|
if (consumer != null)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"Delegating tap to consumer at {worldPos} (GameObject: {hit.gameObject.name})");
|
Logging.Debug($"Delegating tap to consumer at {worldPos} (GameObject: {hit.gameObject.name})");
|
||||||
consumer.OnTap(worldPos);
|
consumer.OnTap(worldPos);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
LogDebugMessage($"Collider2D hit at {worldPos} (GameObject: {hit.gameObject.name}), but no ITouchInputConsumer found.");
|
Logging.Debug($"Collider2D hit at {worldPos} (GameObject: {hit.gameObject.name}), but no ITouchInputConsumer found.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogDebugMessage($"No Collider2D found at {worldPos} for interactable delegation.");
|
Logging.Debug($"No Collider2D found at {worldPos} for interactable delegation.");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -368,7 +363,7 @@ namespace Input
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_overrideConsumers.Add(consumer);
|
_overrideConsumers.Add(consumer);
|
||||||
LogDebugMessage($"Override consumer registered: {consumer}");
|
Logging.Debug($"Override consumer registered: {consumer}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -386,7 +381,7 @@ namespace Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
_overrideConsumers.Remove(consumer);
|
_overrideConsumers.Remove(consumer);
|
||||||
LogDebugMessage($"Override consumer unregistered: {consumer}");
|
Logging.Debug($"Override consumer unregistered: {consumer}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -396,7 +391,7 @@ namespace Input
|
|||||||
{
|
{
|
||||||
_activeHoldConsumer = null;
|
_activeHoldConsumer = null;
|
||||||
_overrideConsumers.Clear();
|
_overrideConsumers.Clear();
|
||||||
LogDebugMessage("All override consumers cleared.");
|
Logging.Debug("All override consumers cleared.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -409,17 +404,9 @@ namespace Input
|
|||||||
|
|
||||||
// Get the topmost override consumer (last registered)
|
// Get the topmost override consumer (last registered)
|
||||||
var consumer = _overrideConsumers[_overrideConsumers.Count - 1];
|
var consumer = _overrideConsumers[_overrideConsumers.Count - 1];
|
||||||
LogDebugMessage($"Delegating tap to override consumer at {worldPos} (GameObject: {consumer})");
|
Logging.Debug($"Delegating tap to override consumer at {worldPos} (GameObject: {consumer})");
|
||||||
consumer.OnTap(worldPos);
|
consumer.OnTap(worldPos);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (_logVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[InputManager] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Pathfinding;
|
|||||||
using AppleHills.Core.Settings;
|
using AppleHills.Core.Settings;
|
||||||
using Core;
|
using Core;
|
||||||
using Core.Lifecycle;
|
using Core.Lifecycle;
|
||||||
using Core.SaveLoad;
|
|
||||||
|
|
||||||
namespace Input
|
namespace Input
|
||||||
{
|
{
|
||||||
@@ -71,9 +70,8 @@ namespace Input
|
|||||||
public override bool AutoRegisterForSave => true;
|
public override bool AutoRegisterForSave => true;
|
||||||
// Scene-specific SaveId - each level has its own player state
|
// Scene-specific SaveId - each level has its own player state
|
||||||
public override string SaveId => $"{gameObject.scene.name}/PlayerController";
|
public override string SaveId => $"{gameObject.scene.name}/PlayerController";
|
||||||
public override int ManagedAwakePriority => 100; // Player controller
|
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
aiPath = GetComponent<AIPath>();
|
aiPath = GetComponent<AIPath>();
|
||||||
artTransform = transform.Find("CharacterArt");
|
artTransform = transform.Find("CharacterArt");
|
||||||
@@ -103,7 +101,7 @@ namespace Input
|
|||||||
public void OnTap(Vector2 worldPosition)
|
public void OnTap(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
InterruptMoveTo();
|
InterruptMoveTo();
|
||||||
LogDebugMessage($"OnTap at {worldPosition}");
|
Logging.Debug($"OnTap at {worldPosition}");
|
||||||
if (aiPath != null)
|
if (aiPath != null)
|
||||||
{
|
{
|
||||||
aiPath.enabled = true;
|
aiPath.enabled = true;
|
||||||
@@ -122,7 +120,7 @@ namespace Input
|
|||||||
public void OnHoldStart(Vector2 worldPosition)
|
public void OnHoldStart(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
InterruptMoveTo();
|
InterruptMoveTo();
|
||||||
LogDebugMessage($"OnHoldStart at {worldPosition}");
|
Logging.Debug($"OnHoldStart at {worldPosition}");
|
||||||
lastHoldPosition = worldPosition;
|
lastHoldPosition = worldPosition;
|
||||||
isHolding = true;
|
isHolding = true;
|
||||||
if (_settings.DefaultHoldMovementMode == HoldMovementMode.Pathfinding &&
|
if (_settings.DefaultHoldMovementMode == HoldMovementMode.Pathfinding &&
|
||||||
@@ -159,7 +157,7 @@ namespace Input
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void OnHoldEnd(Vector2 worldPosition)
|
public void OnHoldEnd(Vector2 worldPosition)
|
||||||
{
|
{
|
||||||
LogDebugMessage($"OnHoldEnd at {worldPosition}");
|
Logging.Debug($"OnHoldEnd at {worldPosition}");
|
||||||
isHolding = false;
|
isHolding = false;
|
||||||
directMoveVelocity = Vector3.zero;
|
directMoveVelocity = Vector3.zero;
|
||||||
if (aiPath != null && _settings.DefaultHoldMovementMode ==
|
if (aiPath != null && _settings.DefaultHoldMovementMode ==
|
||||||
@@ -335,13 +333,13 @@ namespace Input
|
|||||||
{
|
{
|
||||||
_isMoving = true;
|
_isMoving = true;
|
||||||
OnMovementStarted?.Invoke();
|
OnMovementStarted?.Invoke();
|
||||||
LogDebugMessage("Movement started");
|
Logging.Debug("Movement started");
|
||||||
}
|
}
|
||||||
else if (!isCurrentlyMoving && _isMoving)
|
else if (!isCurrentlyMoving && _isMoving)
|
||||||
{
|
{
|
||||||
_isMoving = false;
|
_isMoving = false;
|
||||||
OnMovementStopped?.Invoke();
|
OnMovementStopped?.Invoke();
|
||||||
LogDebugMessage("Movement stopped");
|
Logging.Debug("Movement stopped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,17 +423,9 @@ namespace Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogDebugMessage(string message)
|
|
||||||
{
|
|
||||||
if (_logVerbosity <= LogVerbosity.Debug)
|
|
||||||
{
|
|
||||||
Logging.Debug($"[PlayerTouchController] {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Save/Load Lifecycle Hooks
|
#region Save/Load Lifecycle Hooks
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
var saveData = new PlayerSaveData
|
var saveData = new PlayerSaveData
|
||||||
{
|
{
|
||||||
@@ -445,7 +435,7 @@ namespace Input
|
|||||||
return JsonUtility.ToJson(saveData);
|
return JsonUtility.ToJson(saveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,9 +41,6 @@ namespace Interactions
|
|||||||
// Action component system
|
// Action component system
|
||||||
private List<InteractionActionBase> _registeredActions = new List<InteractionActionBase>();
|
private List<InteractionActionBase> _registeredActions = new List<InteractionActionBase>();
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
|
||||||
public override int ManagedAwakePriority => 100; // Gameplay base classes
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register an action component with this interactable
|
/// Register an action component with this interactable
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ namespace Interactions
|
|||||||
public event Action<PickupItemData, PickupItemData> OnIncorrectItemSlotted;
|
public event Action<PickupItemData, PickupItemData> OnIncorrectItemSlotted;
|
||||||
|
|
||||||
public UnityEvent onForbiddenItemSlotted;
|
public UnityEvent onForbiddenItemSlotted;
|
||||||
// Native C# event alternative for code-only subscribers
|
|
||||||
public event Action<PickupItemData, PickupItemData> OnForbiddenItemSlotted;
|
|
||||||
|
|
||||||
public GameObject GetSlottedObject()
|
public GameObject GetSlottedObject()
|
||||||
{
|
{
|
||||||
@@ -85,9 +83,9 @@ namespace Interactions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // SaveableInteractable registration
|
base.OnManagedAwake(); // SaveableInteractable registration
|
||||||
|
|
||||||
// Setup visuals
|
// Setup visuals
|
||||||
if (iconRenderer == null)
|
if (iconRenderer == null)
|
||||||
@@ -289,10 +287,8 @@ namespace Interactions
|
|||||||
ItemManager.Instance?.RegisterItemSlot(this);
|
ItemManager.Instance?.RegisterItemSlot(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Unregister from slot manager
|
// Unregister from slot manager
|
||||||
ItemManager.Instance?.UnregisterItemSlot(this);
|
ItemManager.Instance?.UnregisterItemSlot(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ namespace Interactions
|
|||||||
public event Action<PickupItemData> OnItemPickedUp;
|
public event Action<PickupItemData> OnItemPickedUp;
|
||||||
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // Register with save system
|
base.OnManagedAwake(); // Register with save system
|
||||||
|
|
||||||
if (iconRenderer == null)
|
if (iconRenderer == null)
|
||||||
iconRenderer = GetComponent<SpriteRenderer>();
|
iconRenderer = GetComponent<SpriteRenderer>();
|
||||||
@@ -44,15 +44,14 @@ namespace Interactions
|
|||||||
|
|
||||||
// Always register with ItemManager, even if picked up
|
// Always register with ItemManager, even if picked up
|
||||||
// This allows the save/load system to find held items when restoring state
|
// This allows the save/load system to find held items when restoring state
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
|
base.OnManagedStart();
|
||||||
ItemManager.Instance?.RegisterPickup(this);
|
ItemManager.Instance?.RegisterPickup(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Unregister from ItemManager
|
// Unregister from ItemManager
|
||||||
ItemManager.Instance?.UnregisterPickup(this);
|
ItemManager.Instance?.UnregisterPickup(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Interactions
|
|||||||
|
|
||||||
#region Save/Load Lifecycle Hooks
|
#region Save/Load Lifecycle Hooks
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
object stateData = GetSerializableState();
|
object stateData = GetSerializableState();
|
||||||
if (stateData == null)
|
if (stateData == null)
|
||||||
@@ -35,7 +35,7 @@ namespace Interactions
|
|||||||
return JsonUtility.ToJson(stateData);
|
return JsonUtility.ToJson(stateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ namespace Levels
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
base.OnManagedAwake();
|
||||||
|
|
||||||
Logging.Debug($"[LevelSwitch] Awake called for {gameObject.name} in scene {gameObject.scene.name}");
|
Logging.Debug($"[LevelSwitch] Awake called for {gameObject.name} in scene {gameObject.scene.name}");
|
||||||
|
|
||||||
@@ -36,12 +36,12 @@ namespace Levels
|
|||||||
ApplySwitchData();
|
ApplySwitchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug($"[LevelSwitch] OnManagedAwake called for {gameObject.name}");
|
Logging.Debug($"[LevelSwitch] OnManagedStart called for {gameObject.name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
Logging.Debug($"[LevelSwitch] OnSceneReady called for {gameObject.name}");
|
Logging.Debug($"[LevelSwitch] OnSceneReady called for {gameObject.name}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,21 +35,16 @@ namespace Levels
|
|||||||
[SerializeField] private bool startUnlocked = false;
|
[SerializeField] private bool startUnlocked = false;
|
||||||
|
|
||||||
private SpriteRenderer iconRenderer;
|
private SpriteRenderer iconRenderer;
|
||||||
|
|
||||||
// Settings reference
|
|
||||||
private IInteractionSettings interactionSettings;
|
private IInteractionSettings interactionSettings;
|
||||||
|
|
||||||
private bool switchActive = true;
|
|
||||||
private bool isUnlocked;
|
private bool isUnlocked;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
base.OnManagedAwake();
|
||||||
|
|
||||||
switchActive = true;
|
|
||||||
if (iconRenderer == null)
|
if (iconRenderer == null)
|
||||||
iconRenderer = GetComponent<SpriteRenderer>();
|
iconRenderer = GetComponent<SpriteRenderer>();
|
||||||
|
|
||||||
@@ -64,10 +59,9 @@ namespace Levels
|
|||||||
ApplySwitchData();
|
ApplySwitchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
base.OnManagedAwake();
|
base.OnManagedStart();
|
||||||
|
|
||||||
// If startUnlocked is true, always start active
|
// If startUnlocked is true, always start active
|
||||||
if (startUnlocked)
|
if (startUnlocked)
|
||||||
{
|
{
|
||||||
@@ -86,10 +80,8 @@ namespace Levels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
if (PuzzleManager.Instance != null)
|
if (PuzzleManager.Instance != null)
|
||||||
{
|
{
|
||||||
PuzzleManager.Instance.OnAllPuzzlesComplete -= HandleAllPuzzlesComplete;
|
PuzzleManager.Instance.OnAllPuzzlesComplete -= HandleAllPuzzlesComplete;
|
||||||
@@ -145,14 +137,6 @@ namespace Levels
|
|||||||
return base.CanBeClicked() && isUnlocked;
|
return base.CanBeClicked() && isUnlocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setup: Prevent re-entry while interaction is in progress.
|
|
||||||
/// </summary>
|
|
||||||
protected override void OnInteractionStarted()
|
|
||||||
{
|
|
||||||
switchActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main interaction logic: Spawn menu and switch input mode.
|
/// Main interaction logic: Spawn menu and switch input mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -203,7 +187,7 @@ namespace Levels
|
|||||||
|
|
||||||
private void OnMenuCancel()
|
private void OnMenuCancel()
|
||||||
{
|
{
|
||||||
switchActive = true; // Allow interaction again if cancelled
|
|
||||||
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
|
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,13 +103,10 @@ namespace Minigames.DivingForPictures
|
|||||||
|
|
||||||
public static DivingGameManager Instance => _instance;
|
public static DivingGameManager Instance => _instance;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 190;
|
|
||||||
public override bool AutoRegisterPausable => true; // Automatic GameManager registration
|
public override bool AutoRegisterPausable => true; // Automatic GameManager registration
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
|
||||||
|
|
||||||
if (_instance == null)
|
if (_instance == null)
|
||||||
{
|
{
|
||||||
_instance = this;
|
_instance = this;
|
||||||
@@ -120,7 +117,7 @@ namespace Minigames.DivingForPictures
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
|
||||||
_currentSpawnProbability = _settings?.BaseSpawnProbability ?? 0.2f;
|
_currentSpawnProbability = _settings?.BaseSpawnProbability ?? 0.2f;
|
||||||
@@ -131,7 +128,7 @@ namespace Minigames.DivingForPictures
|
|||||||
Logging.Debug("[DivingGameManager] Initialized");
|
Logging.Debug("[DivingGameManager] Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
InitializeGame();
|
InitializeGame();
|
||||||
|
|
||||||
@@ -163,10 +160,8 @@ namespace Minigames.DivingForPictures
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy(); // Handles auto-unregister from GameManager
|
|
||||||
|
|
||||||
// Unsubscribe from events when the manager is destroyed
|
// Unsubscribe from events when the manager is destroyed
|
||||||
PlayerCollisionBehavior.OnDamageTaken -= OnPlayerDamageTaken;
|
PlayerCollisionBehavior.OnDamageTaken -= OnPlayerDamageTaken;
|
||||||
OnMonsterSpawned -= DoMonsterSpawned;
|
OnMonsterSpawned -= DoMonsterSpawned;
|
||||||
|
|||||||
@@ -106,9 +106,7 @@ public class FollowerController : ManagedBehaviour
|
|||||||
private bool _hasRestoredHeldItem; // Track if held item restoration completed
|
private bool _hasRestoredHeldItem; // Track if held item restoration completed
|
||||||
private string _expectedHeldItemSaveId; // Expected saveId during restoration
|
private string _expectedHeldItemSaveId; // Expected saveId during restoration
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 110; // Follower after player
|
internal override void OnManagedStart()
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
|
||||||
{
|
{
|
||||||
_aiPath = GetComponent<AIPath>();
|
_aiPath = GetComponent<AIPath>();
|
||||||
// Find art prefab and animator
|
// Find art prefab and animator
|
||||||
@@ -129,7 +127,7 @@ public class FollowerController : ManagedBehaviour
|
|||||||
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
// Find player reference when scene is ready (called for every scene load)
|
// Find player reference when scene is ready (called for every scene load)
|
||||||
FindPlayerReference();
|
FindPlayerReference();
|
||||||
@@ -727,7 +725,7 @@ public class FollowerController : ManagedBehaviour
|
|||||||
|
|
||||||
#region Save/Load Lifecycle Hooks
|
#region Save/Load Lifecycle Hooks
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
var saveData = new FollowerSaveData
|
var saveData = new FollowerSaveData
|
||||||
{
|
{
|
||||||
@@ -754,7 +752,7 @@ public class FollowerController : ManagedBehaviour
|
|||||||
return JsonUtility.ToJson(saveData);
|
return JsonUtility.ToJson(saveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
@@ -914,7 +912,7 @@ public class FollowerController : ManagedBehaviour
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static FollowerController FindInstance()
|
public static FollowerController FindInstance()
|
||||||
{
|
{
|
||||||
return FindObjectOfType<FollowerController>();
|
return FindFirstObjectByType<FollowerController>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Save/Load Lifecycle Hooks
|
#endregion Save/Load Lifecycle Hooks
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace PuzzleS
|
|||||||
// Save system configuration
|
// Save system configuration
|
||||||
public override bool AutoRegisterForSave => true;
|
public override bool AutoRegisterForSave => true;
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Initialize after all managers are ready
|
// Initialize after all managers are ready
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ namespace PuzzleS
|
|||||||
|
|
||||||
#region Save/Load Lifecycle Hooks
|
#region Save/Load Lifecycle Hooks
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
// Save scene-specific progress
|
// Save scene-specific progress
|
||||||
var state = new BirdGameState
|
var state = new BirdGameState
|
||||||
@@ -38,7 +38,7 @@ namespace PuzzleS
|
|||||||
return JsonUtility.ToJson(state);
|
return JsonUtility.ToJson(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(serializedData))
|
if (string.IsNullOrEmpty(serializedData))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace PuzzleS
|
|||||||
// Enum for tracking proximity state (simplified to just Close and Far)
|
// Enum for tracking proximity state (simplified to just Close and Far)
|
||||||
public enum ProximityState { Close, Far }
|
public enum ProximityState { Close, Far }
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
_interactable = GetComponent<InteractableBase>();
|
_interactable = GetComponent<InteractableBase>();
|
||||||
|
|
||||||
@@ -56,14 +56,10 @@ namespace PuzzleS
|
|||||||
Logging.Warning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt");
|
Logging.Warning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Awake();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
base.OnManagedAwake();
|
|
||||||
|
|
||||||
// Register with PuzzleManager - safe to access .Instance here
|
// Register with PuzzleManager - safe to access .Instance here
|
||||||
if (stepData != null && PuzzleManager.Instance != null)
|
if (stepData != null && PuzzleManager.Instance != null)
|
||||||
{
|
{
|
||||||
@@ -87,10 +83,8 @@ namespace PuzzleS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
if (PuzzleManager.Instance != null && stepData != null)
|
if (PuzzleManager.Instance != null && stepData != null)
|
||||||
{
|
{
|
||||||
PuzzleManager.Instance.UnregisterStepBehaviour(this);
|
PuzzleManager.Instance.UnregisterStepBehaviour(this);
|
||||||
|
|||||||
@@ -94,17 +94,13 @@ namespace PuzzleS
|
|||||||
// Track pending unlocks for steps that were unlocked before their behavior registered
|
// Track pending unlocks for steps that were unlocked before their behavior registered
|
||||||
private HashSet<string> _pendingUnlocks = new HashSet<string>();
|
private HashSet<string> _pendingUnlocks = new HashSet<string>();
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 80; // Puzzle systems
|
internal override void OnManagedAwake()
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Initialize settings reference
|
// Initialize settings reference
|
||||||
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
||||||
@@ -140,10 +136,8 @@ namespace PuzzleS
|
|||||||
LoadPuzzlesForScene(sceneName);
|
LoadPuzzlesForScene(sceneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Unsubscribe from SceneManagerService events
|
// Unsubscribe from SceneManagerService events
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
{
|
{
|
||||||
@@ -591,7 +585,7 @@ namespace PuzzleS
|
|||||||
|
|
||||||
#region Save/Load Lifecycle Hooks
|
#region Save/Load Lifecycle Hooks
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
if (_currentLevelData == null)
|
if (_currentLevelData == null)
|
||||||
{
|
{
|
||||||
@@ -611,7 +605,7 @@ namespace PuzzleS
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string data)
|
internal override void OnSceneRestoreRequested(string data)
|
||||||
{
|
{
|
||||||
Logging.Debug("[XAXA] PuzzleManager loading with data: " + data);
|
Logging.Debug("[XAXA] PuzzleManager loading with data: " + data);
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,13 @@ public class AppleAudioSource : ManagedBehaviour
|
|||||||
public int sourcePriority;
|
public int sourcePriority;
|
||||||
|
|
||||||
|
|
||||||
protected override void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
|
||||||
audioSource = GetComponent<AudioSource>();
|
audioSource = GetComponent<AudioSource>();
|
||||||
}
|
}
|
||||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
audioSource = GetComponent<AudioSource>();
|
|
||||||
AudioManager.Instance.RegisterNewAudioSource(this);
|
AudioManager.Instance.RegisterNewAudioSource(this);
|
||||||
_audioMixer = AudioManager.Instance.audioMixer;
|
_audioMixer = AudioManager.Instance.audioMixer;
|
||||||
InitializeAudioSource();
|
InitializeAudioSource();
|
||||||
|
|||||||
@@ -40,19 +40,15 @@ public class AudioManager : ManagedBehaviour, IPausable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static AudioManager Instance => _instance;
|
public static AudioManager Instance => _instance;
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
|
||||||
public override int ManagedAwakePriority => 30; // Audio infrastructure
|
|
||||||
public override bool AutoRegisterPausable => true; // Auto-register as IPausable
|
public override bool AutoRegisterPausable => true; // Auto-register as IPausable
|
||||||
|
|
||||||
private new void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Initialize lists if they were not set in inspector
|
// Initialize lists if they were not set in inspector
|
||||||
criticalVOSources = criticalVOSources ?? new List<AppleAudioSource>();
|
criticalVOSources = criticalVOSources ?? new List<AppleAudioSource>();
|
||||||
@@ -72,7 +68,7 @@ public class AudioManager : ManagedBehaviour, IPausable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Warning("[AudioManager] QuickAccess.Instance is null during OnManagedAwake. Some audio references may remain unset.");
|
Logging.Warning("[AudioManager] QuickAccess.Instance is null during OnManagedStart. Some audio references may remain unset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic
|
// Diagnostic
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class BushAudioController : ManagedBehaviour
|
|||||||
|
|
||||||
|
|
||||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
_eventSource = VOPlayer.audioSource.RequestEventHandlers();
|
_eventSource = VOPlayer.audioSource.RequestEventHandlers();
|
||||||
_eventSource.AudioStopped += PlayBirdCounter;
|
_eventSource.AudioStopped += PlayBirdCounter;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class LevelAudioObject : ManagedBehaviour
|
|||||||
|
|
||||||
public override bool AutoRegisterForSave => isOneTime; // Only save if one-time audio
|
public override bool AutoRegisterForSave => isOneTime; // Only save if one-time audio
|
||||||
|
|
||||||
protected override string OnSceneSaveRequested()
|
internal override string OnSceneSaveRequested()
|
||||||
{
|
{
|
||||||
if (!isOneTime)
|
if (!isOneTime)
|
||||||
return null; // No need to save if not one-time
|
return null; // No need to save if not one-time
|
||||||
@@ -39,7 +39,7 @@ public class LevelAudioObject : ManagedBehaviour
|
|||||||
return JsonUtility.ToJson(saveData);
|
return JsonUtility.ToJson(saveData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreRequested(string serializedData)
|
internal override void OnSceneRestoreRequested(string serializedData)
|
||||||
{
|
{
|
||||||
if (!isOneTime || string.IsNullOrEmpty(serializedData))
|
if (!isOneTime || string.IsNullOrEmpty(serializedData))
|
||||||
return;
|
return;
|
||||||
@@ -55,7 +55,7 @@ public class LevelAudioObject : ManagedBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneRestoreCompleted()
|
internal override void OnSceneRestoreCompleted()
|
||||||
{
|
{
|
||||||
if (isOneTime && !_hasPlayed)
|
if (isOneTime && !_hasPlayed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class PulverAudioController : ManagedBehaviour
|
|||||||
private FollowerController followerController;
|
private FollowerController followerController;
|
||||||
public ItemManager itemManager;
|
public ItemManager itemManager;
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
followerController = GetComponent<FollowerController>();
|
followerController = GetComponent<FollowerController>();
|
||||||
followerController.PulverIsCombining.AddListener(PulverIsCombining);
|
followerController.PulverIsCombining.AddListener(PulverIsCombining);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class AppSwitcher : UIPage
|
|||||||
private TweenBase slideInTween;
|
private TweenBase slideInTween;
|
||||||
private TweenBase slideOutTween;
|
private TweenBase slideOutTween;
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.OnManagedAwake();
|
base.OnManagedAwake();
|
||||||
|
|
||||||
@@ -93,10 +93,8 @@ public class AppSwitcher : UIPage
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Clean up tweens
|
// Clean up tweens
|
||||||
slideInTween?.Stop();
|
slideInTween?.Stop();
|
||||||
slideOutTween?.Stop();
|
slideOutTween?.Stop();
|
||||||
|
|||||||
@@ -44,10 +44,8 @@ namespace UI.CardSystem
|
|||||||
private List<AlbumCardPlacementDraggable> _activeCards = new List<AlbumCardPlacementDraggable>();
|
private List<AlbumCardPlacementDraggable> _activeCards = new List<AlbumCardPlacementDraggable>();
|
||||||
private const int MAX_VISIBLE_CARDS = 3;
|
private const int MAX_VISIBLE_CARDS = 3;
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
base.OnManagedAwake();
|
|
||||||
|
|
||||||
// Discover zone tabs from container
|
// Discover zone tabs from container
|
||||||
DiscoverZoneTabs();
|
DiscoverZoneTabs();
|
||||||
|
|
||||||
@@ -151,7 +149,7 @@ namespace UI.CardSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Unsubscribe from CardSystemManager
|
// Unsubscribe from CardSystemManager
|
||||||
if (CardSystemManager.Instance != null)
|
if (CardSystemManager.Instance != null)
|
||||||
@@ -183,9 +181,6 @@ namespace UI.CardSystem
|
|||||||
|
|
||||||
// Clean up active cards
|
// Clean up active cards
|
||||||
CleanupActiveCards();
|
CleanupActiveCards();
|
||||||
|
|
||||||
// Call base implementation
|
|
||||||
base.OnDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExitButtonClicked()
|
private void OnExitButtonClicked()
|
||||||
|
|||||||
@@ -40,10 +40,8 @@ namespace UI.CardSystem
|
|||||||
|
|
||||||
private TweenBase _activeTween;
|
private TweenBase _activeTween;
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
base.OnManagedAwake();
|
|
||||||
|
|
||||||
// Store original scale for pulse animation
|
// Store original scale for pulse animation
|
||||||
if (dotBackground != null)
|
if (dotBackground != null)
|
||||||
{
|
{
|
||||||
@@ -72,16 +70,13 @@ namespace UI.CardSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Unsubscribe from CardSystemManager events to prevent memory leaks
|
// Unsubscribe from CardSystemManager events to prevent memory leaks
|
||||||
if (CardSystemManager.Instance != null)
|
if (CardSystemManager.Instance != null)
|
||||||
{
|
{
|
||||||
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call base implementation
|
|
||||||
base.OnDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ namespace UI.CardSystem
|
|||||||
[SerializeField] private float cardSpacing = 150f;
|
[SerializeField] private float cardSpacing = 150f;
|
||||||
|
|
||||||
[Header("Settings")]
|
[Header("Settings")]
|
||||||
[SerializeField] private float cardRevealDelay = 0.5f;
|
|
||||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||||
[SerializeField] private CinemachineImpulseSource impulseSource;
|
[SerializeField] private CinemachineImpulseSource impulseSource;
|
||||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||||
@@ -77,7 +76,7 @@ namespace UI.CardSystem
|
|||||||
gameObject.SetActive(false);
|
gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
// Unsubscribe from dismiss button
|
// Unsubscribe from dismiss button
|
||||||
if (_dismissButton != null)
|
if (_dismissButton != null)
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ namespace UI.CardSystem
|
|||||||
albumCard.SetParentSlot(this);
|
albumCard.SetParentSlot(this);
|
||||||
|
|
||||||
// Register with AlbumViewPage for enlarge/shrink handling
|
// Register with AlbumViewPage for enlarge/shrink handling
|
||||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||||
if (albumPage != null)
|
if (albumPage != null)
|
||||||
{
|
{
|
||||||
albumPage.RegisterAlbumCard(albumCard);
|
albumPage.RegisterAlbumCard(albumCard);
|
||||||
@@ -208,7 +208,7 @@ namespace UI.CardSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register with AlbumViewPage for enlarge/shrink handling
|
// Register with AlbumViewPage for enlarge/shrink handling
|
||||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||||
if (albumPage != null)
|
if (albumPage != null)
|
||||||
{
|
{
|
||||||
albumPage.RegisterAlbumCard(albumCard);
|
albumPage.RegisterAlbumCard(albumCard);
|
||||||
@@ -284,7 +284,7 @@ namespace UI.CardSystem
|
|||||||
previewCardDisplay.transform.localScale = _previewOriginalScale;
|
previewCardDisplay.transform.localScale = _previewOriginalScale;
|
||||||
|
|
||||||
// Get AlbumViewPage to show backdrop and reparent
|
// Get AlbumViewPage to show backdrop and reparent
|
||||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||||
if (albumPage != null)
|
if (albumPage != null)
|
||||||
{
|
{
|
||||||
albumPage.ShowSlotPreview(this, previewCardDisplay.transform);
|
albumPage.ShowSlotPreview(this, previewCardDisplay.transform);
|
||||||
@@ -311,7 +311,7 @@ namespace UI.CardSystem
|
|||||||
previewCardDisplay.SetPreviewMode(false, null);
|
previewCardDisplay.SetPreviewMode(false, null);
|
||||||
|
|
||||||
// Get AlbumViewPage to hide backdrop
|
// Get AlbumViewPage to hide backdrop
|
||||||
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
|
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
|
||||||
if (albumPage != null)
|
if (albumPage != null)
|
||||||
{
|
{
|
||||||
albumPage.HideSlotPreview(this, previewCardDisplay.transform, () =>
|
albumPage.HideSlotPreview(this, previewCardDisplay.transform, () =>
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ namespace UI.CardSystem
|
|||||||
// State
|
// State
|
||||||
private bool _isFlipped = false;
|
private bool _isFlipped = false;
|
||||||
private bool _isFlipping = false;
|
private bool _isFlipping = false;
|
||||||
private bool _isHovering = false;
|
|
||||||
private TweenBase _idleHoverTween;
|
private TweenBase _idleHoverTween;
|
||||||
private CardData _cardData;
|
private CardData _cardData;
|
||||||
private Vector2 _originalPosition; // Track original spawn position
|
private Vector2 _originalPosition; // Track original spawn position
|
||||||
@@ -242,8 +241,6 @@ namespace UI.CardSystem
|
|||||||
if (_isFlipped || _isFlipping)
|
if (_isFlipped || _isFlipping)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_isHovering = true;
|
|
||||||
|
|
||||||
// Scale up slightly on hover
|
// Scale up slightly on hover
|
||||||
Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack);
|
Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack);
|
||||||
}
|
}
|
||||||
@@ -253,8 +250,6 @@ namespace UI.CardSystem
|
|||||||
if (_isFlipped || _isFlipping)
|
if (_isFlipped || _isFlipping)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_isHovering = false;
|
|
||||||
|
|
||||||
// Scale back to normal
|
// Scale back to normal
|
||||||
Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack);
|
Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ namespace UI.CardSystem
|
|||||||
[Header("Animation Settings")]
|
[Header("Animation Settings")]
|
||||||
[SerializeField] private float hoverAmount = 20f;
|
[SerializeField] private float hoverAmount = 20f;
|
||||||
[SerializeField] private float hoverDuration = 1.5f;
|
[SerializeField] private float hoverDuration = 1.5f;
|
||||||
[SerializeField] private float glowPulseMin = 0.9f;
|
|
||||||
[SerializeField] private float glowPulseMax = 1.1f;
|
[SerializeField] private float glowPulseMax = 1.1f;
|
||||||
[SerializeField] private float glowPulseDuration = 1.2f;
|
[SerializeField] private float glowPulseDuration = 1.2f;
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ namespace UI.Core
|
|||||||
[Header("Page Settings")]
|
[Header("Page Settings")]
|
||||||
public string PageName;
|
public string PageName;
|
||||||
|
|
||||||
// UI pages load after UI infrastructure (UIPageController is priority 50)
|
|
||||||
public override int ManagedAwakePriority => 200;
|
|
||||||
|
|
||||||
// Events using System.Action instead of UnityEvents
|
// Events using System.Action instead of UnityEvents
|
||||||
public event Action OnTransitionInStarted;
|
public event Action OnTransitionInStarted;
|
||||||
public event Action OnTransitionInCompleted;
|
public event Action OnTransitionInCompleted;
|
||||||
|
|||||||
@@ -37,25 +37,19 @@ namespace UI.Core
|
|||||||
private PlayerInput _playerInput;
|
private PlayerInput _playerInput;
|
||||||
private InputAction _cancelAction;
|
private InputAction _cancelAction;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 50; // UI infrastructure
|
internal override void OnManagedAwake()
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug("[UIPageController] Initialized");
|
Logging.Debug("[UIPageController] Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Clean up cached instances
|
// Clean up cached instances
|
||||||
foreach (var cachedPage in _prefabInstanceCache.Values)
|
foreach (var cachedPage in _prefabInstanceCache.Values)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ namespace UI.DragAndDrop.Core
|
|||||||
|
|
||||||
protected virtual void FindAndSnapToSlot()
|
protected virtual void FindAndSnapToSlot()
|
||||||
{
|
{
|
||||||
SlotContainer[] containers = FindObjectsOfType<SlotContainer>();
|
SlotContainer[] containers = FindObjectsByType<SlotContainer>(FindObjectsSortMode.None);
|
||||||
DraggableSlot closestSlot = null;
|
DraggableSlot closestSlot = null;
|
||||||
float closestDistance = float.MaxValue;
|
float closestDistance = float.MaxValue;
|
||||||
|
|
||||||
|
|||||||
@@ -53,14 +53,9 @@ namespace UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LoadingScreenController Instance => _instance;
|
public static LoadingScreenController Instance => _instance;
|
||||||
|
|
||||||
// ManagedBehaviour configuration
|
internal override void OnManagedAwake()
|
||||||
public override int ManagedAwakePriority => 45; // UI infrastructure, before UIPageController
|
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Set up container reference early
|
// Set up container reference early
|
||||||
@@ -74,7 +69,7 @@ namespace UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
Logging.Debug("[LoadingScreenController] Initialized");
|
Logging.Debug("[LoadingScreenController] Initialized");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,14 +28,9 @@ namespace UI
|
|||||||
[SerializeField] private GameObject mainOptionsContainer;
|
[SerializeField] private GameObject mainOptionsContainer;
|
||||||
[SerializeField] private GameObject devOptionsContainer;
|
[SerializeField] private GameObject devOptionsContainer;
|
||||||
|
|
||||||
// After UIPageController (50)
|
internal override void OnManagedAwake()
|
||||||
public override int ManagedAwakePriority => 55;
|
|
||||||
|
|
||||||
private new void Awake()
|
|
||||||
{
|
{
|
||||||
base.Awake(); // CRITICAL: Register with LifecycleManager!
|
// Set instance immediately (early initialization)
|
||||||
|
|
||||||
// Set instance immediately so it's available before OnManagedAwake() is called
|
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Ensure we have a CanvasGroup for transitions
|
// Ensure we have a CanvasGroup for transitions
|
||||||
@@ -51,9 +46,9 @@ namespace UI
|
|||||||
gameObject.SetActive(false);
|
gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Subscribe to scene-dependent events - must be in OnManagedAwake, not OnSceneReady
|
// Subscribe to scene-dependent events - must be in OnManagedStart, not OnSceneReady
|
||||||
// because PauseMenu is in DontDestroyOnLoad and OnSceneReady only fires once
|
// because PauseMenu is in DontDestroyOnLoad and OnSceneReady only fires once
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
{
|
{
|
||||||
@@ -73,15 +68,13 @@ namespace UI
|
|||||||
Logging.Debug("[PauseMenu] Subscribed to SceneManagerService events");
|
Logging.Debug("[PauseMenu] Subscribed to SceneManagerService events");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSceneReady()
|
internal override void OnSceneReady()
|
||||||
{
|
{
|
||||||
// This only fires once for DontDestroyOnLoad objects, so we handle scene loads in OnManagedAwake
|
// This only fires once for DontDestroyOnLoad objects, so we handle scene loads in OnManagedAwake
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Unsubscribe when destroyed
|
// Unsubscribe when destroyed
|
||||||
if (SceneManagerService.Instance != null)
|
if (SceneManagerService.Instance != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -115,14 +115,14 @@ namespace UI
|
|||||||
private UIPageController _uiPageController;
|
private UIPageController _uiPageController;
|
||||||
private AppSwitcher _appSwitcherComponent;
|
private AppSwitcher _appSwitcherComponent;
|
||||||
|
|
||||||
private new void Awake()
|
internal override void OnManagedAwake()
|
||||||
{
|
{
|
||||||
base.Awake();
|
|
||||||
if (Instance != null)
|
if (Instance != null)
|
||||||
{
|
{
|
||||||
Destroy(this);
|
Destroy(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Set instance immediately (early initialization)
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
// Get UIPageController on same GameObject
|
// Get UIPageController on same GameObject
|
||||||
@@ -135,7 +135,7 @@ namespace UI
|
|||||||
InitializeReferences();
|
InitializeReferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Subscribe to UIPageController page changes for auto HUD management
|
// Subscribe to UIPageController page changes for auto HUD management
|
||||||
if (_uiPageController != null)
|
if (_uiPageController != null)
|
||||||
@@ -172,10 +172,8 @@ namespace UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
internal override void OnManagedDestroy()
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
|
||||||
|
|
||||||
// Unsubscribe from events
|
// Unsubscribe from events
|
||||||
if (_uiPageController != null)
|
if (_uiPageController != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ namespace UI.Tutorial
|
|||||||
private bool _canAcceptInput;
|
private bool _canAcceptInput;
|
||||||
private Coroutine _waitLoopCoroutine;
|
private Coroutine _waitLoopCoroutine;
|
||||||
|
|
||||||
public override int ManagedAwakePriority => 200; // Tutorial runs late, after other systems
|
|
||||||
|
|
||||||
protected override void OnManagedAwake()
|
internal override void OnManagedStart()
|
||||||
{
|
{
|
||||||
// Ensure prompt is hidden initially (even before tutorial initialization)
|
// Ensure prompt is hidden initially (even before tutorial initialization)
|
||||||
if (tapPrompt != null)
|
if (tapPrompt != null)
|
||||||
|
|||||||
8
Assets/Settings/Build Profiles.meta
Normal file
8
Assets/Settings/Build Profiles.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 53ec386bba82c1748886a5beb8468ecf
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
48
Assets/Settings/Build Profiles/iOS.asset
Normal file
48
Assets/Settings/Build Profiles/iOS.asset
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
m_Name: iOS
|
||||||
|
m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.Build.Profile.BuildProfile
|
||||||
|
m_AssetVersion: 1
|
||||||
|
m_BuildTarget: 9
|
||||||
|
m_Subtarget: 0
|
||||||
|
m_PlatformId: ad48d16a66894befa4d8181998c3cb09
|
||||||
|
m_PlatformBuildProfile:
|
||||||
|
rid: 3475452038477774988
|
||||||
|
m_OverrideGlobalSceneList: 0
|
||||||
|
m_Scenes: []
|
||||||
|
m_ScriptingDefines: []
|
||||||
|
m_PlayerSettingsYaml:
|
||||||
|
m_Settings: []
|
||||||
|
references:
|
||||||
|
version: 2
|
||||||
|
RefIds:
|
||||||
|
- rid: 3475452038477774988
|
||||||
|
type: {class: iOSPlatformSettings, ns: UnityEditor.iOS, asm: UnityEditor.iOS.Extensions}
|
||||||
|
data:
|
||||||
|
m_Development: 0
|
||||||
|
m_ConnectProfiler: 0
|
||||||
|
m_BuildWithDeepProfilingSupport: 0
|
||||||
|
m_AllowDebugging: 0
|
||||||
|
m_WaitForManagedDebugger: 0
|
||||||
|
m_ManagedDebuggerFixedPort: 0
|
||||||
|
m_ExplicitNullChecks: 0
|
||||||
|
m_ExplicitDivideByZeroChecks: 0
|
||||||
|
m_ExplicitArrayBoundsChecks: 0
|
||||||
|
m_CompressionType: -1
|
||||||
|
m_InstallInBuildFolder: 0
|
||||||
|
m_InsightsSettingsContainer:
|
||||||
|
m_BuildProfileEngineDiagnosticsState: 2
|
||||||
|
m_iOSXcodeBuildConfig: 1
|
||||||
|
m_SymlinkSources: 0
|
||||||
|
m_PreferredXcode:
|
||||||
|
m_SymlinkTrampoline: 0
|
||||||
8
Assets/Settings/Build Profiles/iOS.asset.meta
Normal file
8
Assets/Settings/Build Profiles/iOS.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 110a4eabb37dbaa428e55c751696cd1e
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
120
docs/custom_log_console.md
Normal file
120
docs/custom_log_console.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# Custom Log Console
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A centralized logging system with an advanced filtering console that automatically tags log entries with class and method names. Provides powerful filtering capabilities beyond Unity's default console, including multi-select filters, text search, time-range filtering, and log export.
|
||||||
|
|
||||||
|
## Using the Logging System in Code
|
||||||
|
|
||||||
|
All logging automatically captures the calling class and method name using `CallerMemberName` and `CallerFilePath` attributes. Simply call the static logging methods:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Core;
|
||||||
|
|
||||||
|
public class MyClass : ManagedBehaviour
|
||||||
|
{
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
Logging.Debug("Initialization complete");
|
||||||
|
Logging.Info("Player spawned at position");
|
||||||
|
Logging.Warning("Missing configuration, using defaults");
|
||||||
|
Logging.Error("Failed to load required asset");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output format:**
|
||||||
|
```
|
||||||
|
[ClassName][MethodName] Your message
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available methods:**
|
||||||
|
- `Logging.Debug(string message)` - Detailed diagnostic information
|
||||||
|
- `Logging.Info(string message)` - General informational messages
|
||||||
|
- `Logging.Warning(string message)` - Non-critical issues
|
||||||
|
- `Logging.Error(string message)` - Critical errors
|
||||||
|
|
||||||
|
**Note:** All logs are broadcast via the `Logging.OnLogEntryAdded` event and stored in a central buffer accessible via `Logging.GetRecentLogs()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Opening the Console Window
|
||||||
|
|
||||||
|
**Menu:** `AppleHills > Custom Log Console`
|
||||||
|
|
||||||
|
You can open multiple independent console instances with different filter configurations to monitor separate systems simultaneously
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Console Interface
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Toolbar Controls
|
||||||
|
|
||||||
|
#### 🔵 Basic Controls (Blue outline)
|
||||||
|
- **Clear** - Clears all log entries and resets tag lists
|
||||||
|
- **Auto-scroll** - Automatically scrolls to newest entries when enabled
|
||||||
|
|
||||||
|
#### Filter Buttons (Persistent Popups)
|
||||||
|
|
||||||
|
All filter buttons open persistent popup windows that remain open during multi-selection. Changes apply when you click "Apply" or dismiss with "Close".
|
||||||
|
|
||||||
|
- **🔴 Classes Filter (Red outline)**
|
||||||
|
- Multi-select which classes to display
|
||||||
|
- Includes search box for quick filtering
|
||||||
|
- All/None quick actions
|
||||||
|
|
||||||
|
- **🟢 Methods Filter (Green outline)**
|
||||||
|
- Multi-select which methods to display
|
||||||
|
- Includes search box for quick filtering
|
||||||
|
- All/None quick actions
|
||||||
|
|
||||||
|
- **🟡 Levels Filter (Yellow outline)**
|
||||||
|
- Toggle Debug, Info, Warning, Error levels
|
||||||
|
- All/None quick actions
|
||||||
|
|
||||||
|
- **⏱ Time Filter**
|
||||||
|
- Opens utility window with MinMaxSlider
|
||||||
|
- Filter logs by timestamp range
|
||||||
|
- Enable/disable toggle with reset option
|
||||||
|
|
||||||
|
#### Search & Export
|
||||||
|
- **Search** - Full-text search across class names, method names, and message content
|
||||||
|
- **Export** - Save filtered logs to .txt file with timestamp
|
||||||
|
- **Count** - Shows `filtered/total` log count
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Visual Indicators
|
||||||
|
|
||||||
|
**Color Coding:**
|
||||||
|
- White: Debug/Info (normal operation)
|
||||||
|
- Yellow: Warning (non-critical issues)
|
||||||
|
- Red: Error (critical failures)
|
||||||
|
|
||||||
|
**Alternating Rows:** Light/dark grey backgrounds improve readability for dense log output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
**Event Broadcasting:**
|
||||||
|
```csharp
|
||||||
|
Logging.OnLogEntryAdded += (LogEntry entry) => { /* handle */ };
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manual Log Retrieval:**
|
||||||
|
```csharp
|
||||||
|
List<LogEntry> recentLogs = Logging.GetRecentLogs();
|
||||||
|
```
|
||||||
|
|
||||||
|
**LogEntry Structure:**
|
||||||
|
- `ClassName` - Captured from calling file path
|
||||||
|
- `MethodName` - Captured from `CallerMemberName`
|
||||||
|
- `Message` - User-provided message text
|
||||||
|
- `Level` - Debug/Info/Warning/Error enum
|
||||||
|
- `Timestamp` - Time.realtimeSinceStartup
|
||||||
|
- `FullFormattedMessage` - Complete formatted string
|
||||||
|
|
||||||
|
---
|
||||||
573
docs/interactables/code_reference.md
Normal file
573
docs/interactables/code_reference.md
Normal file
@@ -0,0 +1,573 @@
|
|||||||
|
# Interactables System - Code Reference
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Class Hierarchy](#class-hierarchy)
|
||||||
|
3. [InteractableBase - The Template Method](#interactablebase---the-template-method)
|
||||||
|
- [Interaction Flow](#interaction-flow)
|
||||||
|
- [Virtual Methods to Override](#virtual-methods-to-override)
|
||||||
|
4. [Creating Custom Interactables](#creating-custom-interactables)
|
||||||
|
- [Example 1: Simple Button (OneClickInteraction)](#example-1-simple-button-oneclickinteraction)
|
||||||
|
- [Example 2: Item Pickup](#example-2-item-pickup)
|
||||||
|
- [Example 3: Item Slot with Validation](#example-3-item-slot-with-validation)
|
||||||
|
5. [Character Movement](#character-movement)
|
||||||
|
6. [Action Component System](#action-component-system)
|
||||||
|
7. [Events System](#events-system)
|
||||||
|
8. [Save/Load System Integration](#saveload-system-integration)
|
||||||
|
9. [Integration with Puzzle System](#integration-with-puzzle-system)
|
||||||
|
10. [Advanced Patterns](#advanced-patterns)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Simple, centrally orchestrated interaction system for player and follower characters.
|
||||||
|
|
||||||
|
### Core Concepts
|
||||||
|
|
||||||
|
- **Template Method Pattern**: `InteractableBase` defines the interaction flow; subclasses override specific steps
|
||||||
|
- **Action Component System**: Modular actions respond to interaction events independently
|
||||||
|
- **Async/Await Flow**: Character movement and timeline playback use async patterns
|
||||||
|
- **Save/Load Integration**: `SaveableInteractable` provides persistence for interaction state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
ManagedBehaviour
|
||||||
|
└── InteractableBase
|
||||||
|
├── OneClickInteraction
|
||||||
|
└── SaveableInteractable
|
||||||
|
├── Pickup
|
||||||
|
└── ItemSlot
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class Descriptions
|
||||||
|
|
||||||
|
- **InteractableBase** - Abstract base class that orchestrates the complete interaction flow using the Template Method pattern. Handles tap input, character movement, validation, and event dispatching for all interactables.
|
||||||
|
|
||||||
|
- **SaveableInteractable** - Extends InteractableBase with save/load capabilities, integrating with the ManagedBehaviour save system. Provides abstract methods for JSON serialization and deserialization of state.
|
||||||
|
|
||||||
|
- **OneClickInteraction** - Simplest concrete interactable that completes immediately when character arrives with no additional logic. All functionality comes from UnityEvents configured in the Inspector.
|
||||||
|
|
||||||
|
- **Pickup** - Represents items that can be picked up by the follower, handling item combination and state tracking. Integrates with ItemManager and supports bilateral restoration with ItemSlots.
|
||||||
|
|
||||||
|
- **ItemSlot** - Container that accepts specific items with validation for correct/incorrect/forbidden items. Manages item placement, swapping, and supports combination with special puzzle integration that allows swapping when locked.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## InteractableBase - The Template Method
|
||||||
|
|
||||||
|
### Interaction Flow
|
||||||
|
|
||||||
|
When a player taps an interactable, the following flow executes:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
OnTap() → CanBeClicked() → StartInteractionFlowAsync()
|
||||||
|
↓
|
||||||
|
1. Find Characters (player, follower)
|
||||||
|
2. OnInteractionStarted() [Virtual Hook]
|
||||||
|
3. Fire interactionStarted events
|
||||||
|
4. MoveCharactersAsync()
|
||||||
|
5. OnInteractingCharacterArrived() [Virtual Hook]
|
||||||
|
6. Fire characterArrived events
|
||||||
|
7. ValidateInteraction()
|
||||||
|
8. DoInteraction() [Virtual Hook - OVERRIDE THIS]
|
||||||
|
9. OnInteractionFinished() [Virtual Hook]
|
||||||
|
10. Fire interactionComplete events
|
||||||
|
```
|
||||||
|
|
||||||
|
### Virtual Methods to Override
|
||||||
|
|
||||||
|
#### 1. `CanBeClicked()` - Pre-Interaction Validation
|
||||||
|
```csharp
|
||||||
|
protected virtual bool CanBeClicked()
|
||||||
|
{
|
||||||
|
if (!isActive) return false;
|
||||||
|
// Add custom checks here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**When to override:** Add high-level validation before interaction starts (cooldowns, prerequisites, etc.)
|
||||||
|
|
||||||
|
#### 2. `OnInteractionStarted()` - Setup Logic
|
||||||
|
```csharp
|
||||||
|
protected virtual void OnInteractionStarted()
|
||||||
|
{
|
||||||
|
// Called after characters found, before movement
|
||||||
|
// Setup animations, sound effects, etc.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**When to override:** Perform setup that needs to happen before character movement
|
||||||
|
|
||||||
|
#### 3. `DoInteraction()` - Main Logic ⭐ **OVERRIDE THIS**
|
||||||
|
```csharp
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
// Your interaction logic here
|
||||||
|
return true; // Return true for success, false for failure
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**When to override:** **Always override this** - this is your main interaction logic
|
||||||
|
|
||||||
|
#### 4. `OnInteractingCharacterArrived()` - Arrival Reaction
|
||||||
|
```csharp
|
||||||
|
protected virtual void OnInteractingCharacterArrived()
|
||||||
|
{
|
||||||
|
// Called when character reaches interaction point
|
||||||
|
// Trigger arrival animations, sounds, etc.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**When to override:** React to character arrival with visuals/audio
|
||||||
|
|
||||||
|
#### 5. `OnInteractionFinished()` - Cleanup Logic
|
||||||
|
```csharp
|
||||||
|
protected virtual void OnInteractionFinished(bool success)
|
||||||
|
{
|
||||||
|
// Called after interaction completes
|
||||||
|
// Cleanup, reset state, etc.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**When to override:** Perform cleanup after interaction completes
|
||||||
|
|
||||||
|
#### 6. `CanProceedWithInteraction()` - Validation
|
||||||
|
```csharp
|
||||||
|
protected virtual (bool canProceed, string errorMessage) CanProceedWithInteraction()
|
||||||
|
{
|
||||||
|
// Validate if interaction can proceed
|
||||||
|
// Return error message to show to player
|
||||||
|
return (true, null);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
**When to override:** Add validation that shows error messages to player
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Creating Custom Interactables
|
||||||
|
|
||||||
|
### Example 1: Simple Button (OneClickInteraction)
|
||||||
|
|
||||||
|
The simplest interactable just completes when the character arrives:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Interactions;
|
||||||
|
|
||||||
|
public class OneClickInteraction : InteractableBase
|
||||||
|
{
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
// Simply return success - no additional logic needed
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Use Case:** Triggers, pressure plates, simple activators
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- Set `characterToInteract` to define which character activates it
|
||||||
|
- Use UnityEvents in inspector to trigger game logic
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 2: Item Pickup
|
||||||
|
|
||||||
|
From `Pickup.cs` - demonstrates validation and follower interaction:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class Pickup : SaveableInteractable
|
||||||
|
{
|
||||||
|
public PickupItemData itemData;
|
||||||
|
public bool IsPickedUp { get; internal set; }
|
||||||
|
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
// Try combination first if follower is holding something
|
||||||
|
var heldItemObject = FollowerController?.GetHeldPickupObject();
|
||||||
|
var heldItemData = heldItemObject?.GetComponent<Pickup>()?.itemData;
|
||||||
|
|
||||||
|
var combinationResult = FollowerController.TryCombineItems(
|
||||||
|
this, out var resultItem
|
||||||
|
);
|
||||||
|
|
||||||
|
if (combinationResult == FollowerController.CombinationResult.Successful)
|
||||||
|
{
|
||||||
|
IsPickedUp = true;
|
||||||
|
FireCombinationEvent(resultItem, heldItemData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No combination - do regular pickup
|
||||||
|
FollowerController?.TryPickupItem(gameObject, itemData);
|
||||||
|
IsPickedUp = true;
|
||||||
|
OnItemPickedUp?.Invoke(itemData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns:**
|
||||||
|
- Access `FollowerController` directly (set by base class)
|
||||||
|
- Return `true` for successful pickup
|
||||||
|
- Use custom events (`OnItemPickedUp`) for specific notifications
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example 3: Item Slot with Validation
|
||||||
|
|
||||||
|
From `ItemSlot.cs` - demonstrates complex validation and state management:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ItemSlot : SaveableInteractable
|
||||||
|
{
|
||||||
|
public PickupItemData itemData; // What item should go here
|
||||||
|
private ItemSlotState currentState = ItemSlotState.None;
|
||||||
|
|
||||||
|
protected override (bool canProceed, string errorMessage) CanProceedWithInteraction()
|
||||||
|
{
|
||||||
|
var heldItem = FollowerController?.CurrentlyHeldItemData;
|
||||||
|
|
||||||
|
// Can't interact with empty slot and no item
|
||||||
|
if (heldItem == null && currentlySlottedItemObject == null)
|
||||||
|
return (false, "This requires an item.");
|
||||||
|
|
||||||
|
// Check forbidden items
|
||||||
|
if (heldItem != null && currentlySlottedItemObject == null)
|
||||||
|
{
|
||||||
|
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
||||||
|
var forbidden = config?.forbiddenItems ?? new List<PickupItemData>();
|
||||||
|
|
||||||
|
if (PickupItemData.ListContainsEquivalent(forbidden, heldItem))
|
||||||
|
return (false, "Can't place that here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
var heldItemData = FollowerController.CurrentlyHeldItemData;
|
||||||
|
var heldItemObj = FollowerController.GetHeldPickupObject();
|
||||||
|
|
||||||
|
// Scenario 1: Slot empty + holding item = Slot it
|
||||||
|
if (heldItemData != null && currentlySlottedItemObject == null)
|
||||||
|
{
|
||||||
|
SlotItem(heldItemObj, heldItemData);
|
||||||
|
FollowerController.ClearHeldItem();
|
||||||
|
return IsSlottedItemCorrect(); // Returns true only if correct item
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scenario 2: Slot full + holding item = Try combine or swap
|
||||||
|
if (currentlySlottedItemObject != null)
|
||||||
|
{
|
||||||
|
// Try combination...
|
||||||
|
// Or swap items...
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Patterns:**
|
||||||
|
- `CanProceedWithInteraction()` shows error messages to player
|
||||||
|
- `DoInteraction()` returns true only for correct item (affects puzzle completion)
|
||||||
|
- Access settings via `GameManager.GetSettingsObject<T>()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Character Movement
|
||||||
|
|
||||||
|
### Character Types
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum CharacterToInteract
|
||||||
|
{
|
||||||
|
None, // No character movement
|
||||||
|
Trafalgar, // Player only
|
||||||
|
Pulver, // Follower only (player moves to range first)
|
||||||
|
Both // Both characters move
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Set in Inspector on `InteractableBase`.
|
||||||
|
|
||||||
|
### Custom Movement Targets
|
||||||
|
|
||||||
|
Add `CharacterMoveToTarget` component as child of your interactable:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Automatically used if present
|
||||||
|
var moveTarget = GetComponentInChildren<CharacterMoveToTarget>();
|
||||||
|
Vector3 targetPos = moveTarget.GetTargetPosition();
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Editor Reference](editor_reference.md#character-movement-targets) for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Action Component System
|
||||||
|
|
||||||
|
Add modular behaviors to interactables via `InteractionActionBase` components.
|
||||||
|
|
||||||
|
### Creating an Action Component
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Interactions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public class MyCustomAction : InteractionActionBase
|
||||||
|
{
|
||||||
|
protected override async Task<bool> ExecuteAsync(
|
||||||
|
InteractionEventType eventType,
|
||||||
|
PlayerTouchController player,
|
||||||
|
FollowerController follower)
|
||||||
|
{
|
||||||
|
// Your action logic here
|
||||||
|
|
||||||
|
if (eventType == InteractionEventType.InteractionStarted)
|
||||||
|
{
|
||||||
|
// Play sound, spawn VFX, etc.
|
||||||
|
await Task.Delay(1000); // Simulate async work
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Return success
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ShouldExecute(
|
||||||
|
InteractionEventType eventType,
|
||||||
|
PlayerTouchController player,
|
||||||
|
FollowerController follower)
|
||||||
|
{
|
||||||
|
// Add conditions for when this action should run
|
||||||
|
return base.ShouldExecute(eventType, player, follower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuring in Inspector
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **Respond To Events**: Select which events trigger this action
|
||||||
|
- **Pause Interaction Flow**: If true, interaction waits for this action to complete
|
||||||
|
|
||||||
|
### Built-in Action: Timeline Playback
|
||||||
|
|
||||||
|
`InteractionTimelineAction` plays Unity Timeline sequences in response to events:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Automatically configured via Inspector
|
||||||
|
// See Editor Reference for details
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Character binding to timeline tracks
|
||||||
|
- Sequential timeline playback
|
||||||
|
- Loop options (loop all, loop last)
|
||||||
|
- Timeout protection
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Events System
|
||||||
|
|
||||||
|
### UnityEvents (Inspector-Configurable)
|
||||||
|
|
||||||
|
Available on all `InteractableBase`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Header("Interaction Events")]
|
||||||
|
public UnityEvent<PlayerTouchController, FollowerController> interactionStarted;
|
||||||
|
public UnityEvent interactionInterrupted;
|
||||||
|
public UnityEvent characterArrived;
|
||||||
|
public UnityEvent<bool> interactionComplete; // bool = success
|
||||||
|
```
|
||||||
|
|
||||||
|
### C# Events (Code Subscribers)
|
||||||
|
|
||||||
|
Pickup example:
|
||||||
|
```csharp
|
||||||
|
public event Action<PickupItemData> OnItemPickedUp;
|
||||||
|
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
||||||
|
```
|
||||||
|
|
||||||
|
ItemSlot example:
|
||||||
|
```csharp
|
||||||
|
public event Action<PickupItemData> OnItemSlotRemoved;
|
||||||
|
public event Action<PickupItemData, PickupItemData> OnCorrectItemSlotted;
|
||||||
|
public event Action<PickupItemData, PickupItemData> OnIncorrectItemSlotted;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subscribing to Events
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
var pickup = GetComponent<Pickup>();
|
||||||
|
pickup.OnItemPickedUp += HandleItemPickedUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleItemPickedUp(PickupItemData itemData)
|
||||||
|
{
|
||||||
|
Debug.Log($"Picked up: {itemData.itemName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
var pickup = GetComponent<Pickup>();
|
||||||
|
if (pickup != null)
|
||||||
|
pickup.OnItemPickedUp -= HandleItemPickedUp;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Save/Load System Integration
|
||||||
|
|
||||||
|
### Making an Interactable Saveable
|
||||||
|
|
||||||
|
1. Inherit from `SaveableInteractable` instead of `InteractableBase`
|
||||||
|
2. Define a serializable data structure
|
||||||
|
3. Override `GetSerializableState()` and `ApplySerializableState()`
|
||||||
|
|
||||||
|
### Example Implementation
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Interactions;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// 1. Define save data structure
|
||||||
|
[System.Serializable]
|
||||||
|
public class MyInteractableSaveData
|
||||||
|
{
|
||||||
|
public bool hasBeenActivated;
|
||||||
|
public int activationCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Inherit from SaveableInteractable
|
||||||
|
public class MyInteractable : SaveableInteractable
|
||||||
|
{
|
||||||
|
private bool hasBeenActivated = false;
|
||||||
|
private int activationCount = 0;
|
||||||
|
|
||||||
|
// 3. Serialize state
|
||||||
|
protected override object GetSerializableState()
|
||||||
|
{
|
||||||
|
return new MyInteractableSaveData
|
||||||
|
{
|
||||||
|
hasBeenActivated = this.hasBeenActivated,
|
||||||
|
activationCount = this.activationCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Deserialize state
|
||||||
|
protected override void ApplySerializableState(string serializedData)
|
||||||
|
{
|
||||||
|
var data = JsonUtility.FromJson<MyInteractableSaveData>(serializedData);
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
this.hasBeenActivated = data.hasBeenActivated;
|
||||||
|
this.activationCount = data.activationCount;
|
||||||
|
|
||||||
|
// IMPORTANT: Don't fire events during restoration
|
||||||
|
// Don't re-run initialization logic
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
hasBeenActivated = true;
|
||||||
|
activationCount++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration with Puzzle System
|
||||||
|
|
||||||
|
Interactables can be puzzle steps by adding `ObjectiveStepBehaviour`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// On GameObject with Interactable component
|
||||||
|
var stepBehaviour = gameObject.AddComponent<ObjectiveStepBehaviour>();
|
||||||
|
stepBehaviour.stepData = myPuzzleStepSO;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automatic Puzzle Integration
|
||||||
|
|
||||||
|
`InteractableBase` automatically checks for puzzle locks:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private (bool, string) ValidateInteractionBase()
|
||||||
|
{
|
||||||
|
var step = GetComponent<PuzzleS.ObjectiveStepBehaviour>();
|
||||||
|
if (step != null && !step.IsStepUnlocked())
|
||||||
|
{
|
||||||
|
// Special case: ItemSlots can swap even when locked
|
||||||
|
if (!(this is ItemSlot))
|
||||||
|
{
|
||||||
|
return (false, "This step is locked!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (true, null);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result:** Locked puzzle steps can't be interacted with (except ItemSlots for item swapping).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Patterns
|
||||||
|
|
||||||
|
### Async Validation
|
||||||
|
|
||||||
|
For complex validation that requires async operations:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
protected override (bool canProceed, string errorMessage) CanProceedWithInteraction()
|
||||||
|
{
|
||||||
|
// Synchronous validation only
|
||||||
|
// Async validation should be done in OnInteractionStarted
|
||||||
|
return (true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInteractionStarted()
|
||||||
|
{
|
||||||
|
// Can perform async checks here if needed
|
||||||
|
// But interaction flow continues automatically
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interrupting Interactions
|
||||||
|
|
||||||
|
Interactions auto-interrupt if player cancels movement:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Automatically handled in MoveCharactersAsync()
|
||||||
|
playerRef.OnMoveToCancelled += () => {
|
||||||
|
interactionInterrupted?.Invoke();
|
||||||
|
// Flow stops here
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### One-Time Interactions
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Header("Interaction Settings")]
|
||||||
|
public bool isOneTime = true;
|
||||||
|
|
||||||
|
// Automatically disabled after first successful interaction
|
||||||
|
// No override needed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cooldown Systems
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Header("Interaction Settings")]
|
||||||
|
public float cooldown = 5f; // Seconds
|
||||||
|
|
||||||
|
// Automatically handled by base class
|
||||||
|
// Interaction disabled for 5 seconds after completion
|
||||||
|
```
|
||||||
305
docs/interactables/editor_reference.md
Normal file
305
docs/interactables/editor_reference.md
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
# Interactables System - Editor Reference
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Adding Interactables to Scene](#adding-interactables-to-scene)
|
||||||
|
3. [InteractableBase Inspector](#interactablebase-inspector)
|
||||||
|
- [Interaction Settings](#interaction-settings)
|
||||||
|
- [Interaction Events](#interaction-events-unityevents)
|
||||||
|
4. [Character Movement Targets](#character-movement-targets)
|
||||||
|
5. [Pickup Inspector](#pickup-inspector)
|
||||||
|
6. [ItemSlot Inspector](#itemslot-inspector)
|
||||||
|
7. [OneClickInteraction Inspector](#oneclickinteraction-inspector)
|
||||||
|
8. [Interaction Action Components](#interaction-action-components)
|
||||||
|
9. [Custom Action Components](#custom-action-components)
|
||||||
|
10. [Puzzle Integration](#puzzle-integration)
|
||||||
|
11. [Save System Configuration](#save-system-configuration)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers configuring interactables using the Unity Inspector and scene tools. It might be helpful, although
|
||||||
|
not necessary to be familiar with the code architecture covered in the [Code Reference](code_reference.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding Interactables to Scene
|
||||||
|
|
||||||
|
### Method 1: Add Component Manually
|
||||||
|
Select GameObject → Add Component → Search "Interactable" → Choose type
|
||||||
|
|
||||||
|
### Method 2: Use Interactable Editor
|
||||||
|
`AppleHills > Interactable Editor` → Scene tab → Select GameObject → Click button for desired type
|
||||||
|
|
||||||
|
See [Editor Tools Reference](editor_tools_reference.md#interactable-editor) for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## InteractableBase Inspector
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Interaction Settings
|
||||||
|
|
||||||
|
**Is One Time** - Disable after first successful interaction (switches, consumables)
|
||||||
|
|
||||||
|
**Cooldown** - Temporarily disable after use, in seconds. `-1` = no cooldown (levers, buttons)
|
||||||
|
|
||||||
|
**Character To Interact** - Which character(s) move to activate:
|
||||||
|
- **None** - No movement, instant interaction
|
||||||
|
- **Trafalgar** - Player moves to point
|
||||||
|
- **Pulver** - Follower moves (player moves to range first)
|
||||||
|
- **Both** - Both characters move
|
||||||
|
|
||||||
|
### Interaction Events (UnityEvents)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Interaction Started** `<PlayerTouchController, FollowerController>` - Fires after tap, before movement
|
||||||
|
|
||||||
|
**Interaction Interrupted** - Player cancels or validation fails
|
||||||
|
|
||||||
|
**Character Arrived** - Character reaches destination
|
||||||
|
|
||||||
|
**Interaction Complete** `<bool>` - After DoInteraction(), bool = success
|
||||||
|
|
||||||
|
### Example Event Configuration
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Door that opens when player arrives:**
|
||||||
|
- Character To Interact: `Trafalgar`
|
||||||
|
- Character Arrived: `DoorAnimator.SetTrigger("Open")`, `AudioSource.Play()`
|
||||||
|
- Interaction Complete: `PuzzleStep.CompleteStep()` (if success)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Character Movement Targets
|
||||||
|
|
||||||
|
### Default Movement
|
||||||
|
|
||||||
|
Without `CharacterMoveToTarget`, characters move to default distances configured in `GameManager`:
|
||||||
|
- `PlayerStopDistance` - Follower interactions (~1.5 units)
|
||||||
|
- `PlayerStopDistanceDirectInteraction` - Player interactions (~0.5 units)
|
||||||
|
|
||||||
|
### Custom Movement Targets
|
||||||
|
|
||||||
|
Add `CharacterMoveToTarget` component to child GameObject:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
- **Character Type** - Which character (Trafalgar/Pulver/Both/None)
|
||||||
|
- **Position Offset** - Offset from transform position
|
||||||
|
|
||||||
|
### Scene Gizmos
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Colors:** 🔵 Blue (Trafalgar), 🟠 Orange (Pulver), 🟣 Purple (Both), ⚪ Gray (None)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pickup Inspector
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Required Fields:**
|
||||||
|
|
||||||
|
**Item Data** - `PickupItemData` ScriptableObject defining the item. Create via `Assets > Create > AppleHills > Items + Puzzles > Pickup Item Data`
|
||||||
|
|
||||||
|
**Icon Renderer** - `SpriteRenderer` displaying item icon (auto-assigned if not set)
|
||||||
|
|
||||||
|
### PickupItemData ScriptableObject
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Fields:** Item Name, Description, Map Sprite, Pick Up Sound, Drop Sound
|
||||||
|
|
||||||
|
**Item ID** (Read-Only) - Auto-generated unique identifier for save/load
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ItemSlot Inspector
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Required Fields:**
|
||||||
|
|
||||||
|
**Item Data** - `PickupItemData` defining the **correct** item for this slot
|
||||||
|
|
||||||
|
**Icon Renderer** - `SpriteRenderer` showing slot icon (background/outline)
|
||||||
|
|
||||||
|
**Slotted Item Renderer** - `SpriteRenderer` showing currently slotted item (usually child GameObject)
|
||||||
|
|
||||||
|
### Slot Events
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**On Item Slotted** - Any item placed
|
||||||
|
**On Item Slot Removed** - Item removed
|
||||||
|
**On Correct Item Slotted** - Correct item placed (also fires `interactionComplete(true)`)
|
||||||
|
**On Incorrect Item Slotted** - Wrong item placed
|
||||||
|
**On Forbidden Item Slotted** - Forbidden item attempted
|
||||||
|
|
||||||
|
|
||||||
|
### Slot Item Configuration (Settings)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Configured in `InteractionSettings` at `Assets/Settings/InteractionSettings`:
|
||||||
|
- **Correct Items** - List of accepted items
|
||||||
|
- **Forbidden Items** - Items that can't be placed
|
||||||
|
- **Incorrect Items** - Items that slot but aren't correct
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OneClickInteraction Inspector
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**No additional fields** - only inherits `InteractableBase` settings.
|
||||||
|
|
||||||
|
### Typical Configuration
|
||||||
|
|
||||||
|
- **Character To Interact:** `Trafalgar` or `Pulver`
|
||||||
|
- **Is One Time:** Depends on use case
|
||||||
|
- **Interaction Complete Event:** Configure to trigger game logic
|
||||||
|
|
||||||
|
### Example Use Cases
|
||||||
|
|
||||||
|
**Pressure Plate:**
|
||||||
|
- Character To Interact: `Pulver`
|
||||||
|
- Is One Time: `false`
|
||||||
|
- Interaction Complete: Call `Door.Open()`
|
||||||
|
|
||||||
|
**Tutorial Trigger:**
|
||||||
|
- Character To Interact: `None`
|
||||||
|
- Is One Time: `true`
|
||||||
|
- Interaction Started: Call `TutorialManager.ShowTip()`
|
||||||
|
|
||||||
|
**Dialogue Starter:**
|
||||||
|
- Character To Interact: `Both`
|
||||||
|
- Is One Time: `false`
|
||||||
|
- Character Arrived: Call `DialogueManager.StartDialogue()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interaction Action Components
|
||||||
|
|
||||||
|
### InteractionTimelineAction
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Plays Unity Timeline sequences in response to interaction events.
|
||||||
|
|
||||||
|
#### Required Fields
|
||||||
|
|
||||||
|
**Playable Director**
|
||||||
|
- **Type:** `PlayableDirector` component
|
||||||
|
- **Purpose:** Timeline player
|
||||||
|
- **Setup:** Auto-assigned from same GameObject if present
|
||||||
|
|
||||||
|
**Timeline Mappings** (Array)
|
||||||
|
Each element maps an interaction event to timeline(s):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
##### Event Type
|
||||||
|
- **Type:** `InteractionEventType` enum
|
||||||
|
- **Options:**
|
||||||
|
- `InteractionStarted`
|
||||||
|
- `PlayerArrived`
|
||||||
|
- `InteractingCharacterArrived`
|
||||||
|
- `InteractionComplete`
|
||||||
|
- `InteractionInterrupted`
|
||||||
|
- **Purpose:** When to play this timeline
|
||||||
|
|
||||||
|
##### Timelines (Array)
|
||||||
|
- **Type:** `PlayableAsset[]`
|
||||||
|
- **Purpose:** Timeline(s) to play for this event
|
||||||
|
- **Note:** Plays sequentially if multiple
|
||||||
|
|
||||||
|
##### Bind Player Character
|
||||||
|
- **Type:** `bool`
|
||||||
|
- **Purpose:** Automatically bind player to timeline track
|
||||||
|
- **Track Name:** `Player` (customizable via Player Track Name field)
|
||||||
|
|
||||||
|
##### Bind Pulver Character
|
||||||
|
- **Type:** `bool`
|
||||||
|
- **Purpose:** Automatically bind follower to timeline track
|
||||||
|
- **Track Name:** `Pulver` (customizable via Pulver Track Name field)
|
||||||
|
|
||||||
|
##### Player Track Name / Pulver Track Name
|
||||||
|
- **Type:** `string`
|
||||||
|
- **Default:** `"Player"` / `"Pulver"`
|
||||||
|
- **Purpose:** Name of timeline track to bind character to
|
||||||
|
- **Note:** Must match track name in Timeline asset exactly
|
||||||
|
|
||||||
|
##### Timeout Seconds
|
||||||
|
- **Type:** `float`
|
||||||
|
- **Default:** `30`
|
||||||
|
- **Purpose:** Safety timeout - auto-complete if timeline doesn't finish
|
||||||
|
- **Use Case:** Prevent stuck interactions if timeline errors
|
||||||
|
|
||||||
|
##### Loop Last / Loop All
|
||||||
|
- **Type:** `bool`
|
||||||
|
- **Purpose:** Loop behavior for timeline sequence
|
||||||
|
- **Loop Last:** Replays final timeline on next interaction
|
||||||
|
- **Loop All:** Cycles through all timelines repeatedly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Action Components
|
||||||
|
|
||||||
|
See [Code Reference - Action Component System](code_reference.md#action-component-system).
|
||||||
|
|
||||||
|
**Base Fields:**
|
||||||
|
- **Respond To Events** - Which events trigger this action
|
||||||
|
- **Pause Interaction Flow** - Wait for completion (`true`) or run in background (`false`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Puzzle Integration
|
||||||
|
|
||||||
|
### Adding Puzzle Step to Interactable
|
||||||
|
|
||||||
|
1. Select interactable GameObject
|
||||||
|
2. Add Component → `ObjectiveStepBehaviour`
|
||||||
|
3. Assign `Step Data` (PuzzleStepSO asset)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**GameObject has two components:**
|
||||||
|
- **ItemSlot** (or other Interactable type)
|
||||||
|
- **ObjectiveStepBehaviour**
|
||||||
|
|
||||||
|
**Behavior:**
|
||||||
|
- Interactable locked until puzzle step unlocked
|
||||||
|
- Successful interaction (return `true` from `DoInteraction()`) completes puzzle step
|
||||||
|
- ItemSlots can still swap items when locked (special case)
|
||||||
|
|
||||||
|
### Automatic Step Completion
|
||||||
|
|
||||||
|
**For Pickup:**
|
||||||
|
```csharp
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
// ...pickup logic...
|
||||||
|
return true; // Automatically completes puzzle step if present
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**For ItemSlot:**
|
||||||
|
```csharp
|
||||||
|
protected override bool DoInteraction()
|
||||||
|
{
|
||||||
|
// ...slot logic...
|
||||||
|
return IsSlottedItemCorrect(); // Only completes if correct item
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
No additional code needed - `InteractableBase` handles step completion automatically.
|
||||||
251
docs/interactables/editor_tools_reference.md
Normal file
251
docs/interactables/editor_tools_reference.md
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
# Editor Tools Reference
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
AppleHills provides two specialized editor tools for managing puzzles and interactables:
|
||||||
|
- **Interactable Editor** - Manage scene interactables and debug interactions
|
||||||
|
- **Puzzle Editor** - Manage puzzle steps and debug puzzle flow
|
||||||
|
|
||||||
|
Both tools are accessible via the `AppleHills` menu and follow a consistent two-tab design pattern for editing and debugging.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interactable Editor
|
||||||
|
|
||||||
|
**Menu:** `AppleHills > Interactable Editor`
|
||||||
|
|
||||||
|
The Interactable Editor provides scene-based management and runtime debugging for interactable objects in your scenes.
|
||||||
|
|
||||||
|
### Edit Tab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The Edit tab lets you browse, select, and modify all interactables in the current scene with a real-time inspector.
|
||||||
|
|
||||||
|
#### Left Panel - Scene Interactable List
|
||||||
|
|
||||||
|
**Refresh Button** - Manual refresh (auto-refreshes on scene changes, hierarchy changes, and play mode toggle)
|
||||||
|
|
||||||
|
**Found Count** - Displays total number of interactables found in the active scene
|
||||||
|
|
||||||
|
**Search Field** - Filter interactables by GameObject name for quick access
|
||||||
|
|
||||||
|
**Interactable Cards** - Each card shows:
|
||||||
|
- GameObject name
|
||||||
|
- Interactable component type
|
||||||
|
- Item/Slot data reference (for Pickup and ItemSlot types)
|
||||||
|
- Ping button to highlight in scene hierarchy
|
||||||
|
|
||||||
|
**Auto-Discovery:** Automatically scans for all `InteractableBase` components when:
|
||||||
|
- Scene loads or unloads
|
||||||
|
- GameObjects or components are added/removed
|
||||||
|
- Play mode is toggled
|
||||||
|
|
||||||
|
#### Right Panel - Dynamic Inspector
|
||||||
|
|
||||||
|
The right panel adapts based on what's selected:
|
||||||
|
|
||||||
|
**Nothing Selected State:**
|
||||||
|
- Shows a help message prompting you to select an interactable from the list
|
||||||
|
|
||||||
|
**GameObject Without Interactable State:**
|
||||||
|
- Displays "Add Interactable Component" buttons for:
|
||||||
|
- OneClickInteraction
|
||||||
|
- Pickup
|
||||||
|
- ItemSlot
|
||||||
|
- SaveableInteractable
|
||||||
|
- InteractableBase
|
||||||
|
- Clicking any button adds the component with full undo support and auto-refreshes the list
|
||||||
|
|
||||||
|
**Interactable Selected State:**
|
||||||
|
- Shows full Unity inspector for the selected component
|
||||||
|
- All changes auto-save with undo/redo support (Ctrl+Z / Ctrl+Y)
|
||||||
|
- Additional Information section displays:
|
||||||
|
- Component type
|
||||||
|
- Attached action components with Ping buttons
|
||||||
|
- Type-specific data (Item Data for Pickup, Slot Data for ItemSlot)
|
||||||
|
- In Play Mode: shows slotted object for ItemSlot types
|
||||||
|
|
||||||
|
**Selection Synchronization:**
|
||||||
|
- Bidirectional sync between editor list and scene hierarchy
|
||||||
|
- Selecting in the list highlights in hierarchy and vice versa
|
||||||
|
- Selecting a GameObject without an interactable shows the "Add Component" interface
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Debug Tab
|
||||||
|
|
||||||
|
**Availability:** Play Mode only
|
||||||
|
|
||||||
|
The Debug tab provides runtime testing tools for triggering interactions and events on interactables.
|
||||||
|
|
||||||
|
#### Interactable Debug Cards
|
||||||
|
|
||||||
|
Each debug card represents one interactable in the scene and includes:
|
||||||
|
|
||||||
|
**Header Section:**
|
||||||
|
- GameObject name (bold text)
|
||||||
|
- Interactable component type (gray text)
|
||||||
|
- Ping button (locates object in hierarchy)
|
||||||
|
|
||||||
|
**Full Interaction Button:**
|
||||||
|
- Simulates complete `OnTap()` flow
|
||||||
|
- Triggers character movement and full event chain
|
||||||
|
- Tests end-to-end interaction behavior
|
||||||
|
|
||||||
|
**Individual Event Triggers:**
|
||||||
|
- **Started** - Calls `OnInteractionStarted()` and fires `interactionStarted` event
|
||||||
|
- **Arrived** - Calls `OnInteractingCharacterArrived()` and fires `characterArrived` event
|
||||||
|
- **Do Interaction** - Calls `DoInteraction()` directly to test core interaction logic
|
||||||
|
- **Complete (Success)** - Calls `OnInteractionFinished(true)` and triggers puzzle completion
|
||||||
|
- **Complete (Fail)** - Calls `OnInteractionFinished(false)` to test failure handling
|
||||||
|
- **Interrupted** - Invokes `interactionInterrupted` event
|
||||||
|
|
||||||
|
**Registered Actions Display:**
|
||||||
|
- Lists all action components registered to this interactable
|
||||||
|
- Shows which events each action responds to
|
||||||
|
|
||||||
|
#### Common Testing Workflows
|
||||||
|
|
||||||
|
**Test Full Interaction:**
|
||||||
|
1. Enter Play Mode
|
||||||
|
2. Find target interactable in debug list
|
||||||
|
3. Click **Full Interaction** button
|
||||||
|
4. Verify complete behavior chain
|
||||||
|
|
||||||
|
**Test Specific Event:**
|
||||||
|
1. Enter Play Mode
|
||||||
|
2. Locate interactable
|
||||||
|
3. Click individual event button (e.g., **Started** or **Arrived**)
|
||||||
|
4. Verify specific event behavior
|
||||||
|
|
||||||
|
**Test Event Sequence:**
|
||||||
|
1. Click **Started**
|
||||||
|
2. Click **Arrived**
|
||||||
|
3. Click **Do Interaction**
|
||||||
|
4. Click **Complete (Success)**
|
||||||
|
5. Verify full event chain executes correctly
|
||||||
|
|
||||||
|
**Test Action Integration:**
|
||||||
|
1. Find interactable with timeline or dialogue action
|
||||||
|
2. Check Registered Actions to confirm action is attached
|
||||||
|
3. Click **Started** or appropriate event trigger
|
||||||
|
4. Verify action executes (timeline plays, dialogue shows, etc.)
|
||||||
|
|
||||||
|
**Test Puzzle Integration:**
|
||||||
|
1. Open both Interactable Editor and Puzzle Editor
|
||||||
|
2. Verify required puzzle step is unlocked in Puzzle Editor
|
||||||
|
3. Click **Full Interaction** in Interactable Editor
|
||||||
|
4. Switch to Puzzle Editor and verify step marked as completed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Puzzle Editor
|
||||||
|
|
||||||
|
**Menu:** `AppleHills > Puzzle Editor`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The Puzzle Editor manages puzzle step assets and provides runtime debugging for the puzzle progression system.
|
||||||
|
|
||||||
|
### Edit Tab
|
||||||
|
|
||||||
|
The Edit tab displays all `PuzzleStepSO` assets in your project with full editing capabilities.
|
||||||
|
|
||||||
|
#### Left Panel - Puzzle Step List
|
||||||
|
|
||||||
|
**Search Field** - Filter puzzle steps by name
|
||||||
|
|
||||||
|
**Folder Organization:**
|
||||||
|
- Steps are grouped by their asset folder location
|
||||||
|
- Click folder headers to expand/collapse groups
|
||||||
|
- Helps organize large numbers of puzzle steps
|
||||||
|
|
||||||
|
**Step Cards** - Each card displays:
|
||||||
|
- Display name (user-friendly identifier)
|
||||||
|
- Step ID (unique technical identifier)
|
||||||
|
- Dependency information (unlocked by / unlocks)
|
||||||
|
|
||||||
|
**Toolbar Actions:**
|
||||||
|
- **Refresh** - Reloads all puzzle step assets from project
|
||||||
|
- **Create New** - Opens creation dialog
|
||||||
|
|
||||||
|
**Creating New Steps:**
|
||||||
|
1. Click **Create New** button
|
||||||
|
2. Enter step name (stepId auto-generates from name)
|
||||||
|
3. Select destination folder
|
||||||
|
4. Click Create
|
||||||
|
5. New step appears in list and is auto-selected
|
||||||
|
|
||||||
|
#### Right Panel - Step Inspector
|
||||||
|
|
||||||
|
When a puzzle step is selected, the inspector shows:
|
||||||
|
|
||||||
|
**Basic Properties:**
|
||||||
|
- **Display Name** - Editable user-friendly name for the step
|
||||||
|
- **Step ID** - Read-only unique identifier (lowercase, underscored format)
|
||||||
|
|
||||||
|
**Dependencies Configuration:**
|
||||||
|
- **Unlocked By** - List of steps that must complete before this step unlocks
|
||||||
|
- Drag and drop `PuzzleStepSO` assets to add dependencies
|
||||||
|
- Empty list means this is an initial step (unlocked by default)
|
||||||
|
- **Unlocks** - List of steps that this step will unlock when completed
|
||||||
|
- Bidirectional relationship (automatically syncs with "Unlocked By" on other steps)
|
||||||
|
- Edit from either side of the relationship
|
||||||
|
|
||||||
|
**Asset Management:**
|
||||||
|
- **Asset Path** - Shows full file path to the .asset file
|
||||||
|
- **Delete Button** - Permanently deletes the step asset
|
||||||
|
- Shows confirmation dialog before deletion
|
||||||
|
- Cannot be undone after confirmation
|
||||||
|
|
||||||
|
**Auto-Save:** All changes save automatically to the asset with full undo/redo support (Ctrl+Z / Ctrl+Y)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Debug Tab
|
||||||
|
|
||||||
|
**Availability:** Play Mode only
|
||||||
|
|
||||||
|
The Debug tab provides runtime testing and debugging tools for the puzzle progression system.
|
||||||
|
|
||||||
|
#### Toolbar
|
||||||
|
|
||||||
|
**Current Level Display:**
|
||||||
|
- Shows the name of the currently loaded puzzle level
|
||||||
|
- Updates automatically when scenes change
|
||||||
|
- Displays "No level loaded" if puzzle system is inactive
|
||||||
|
|
||||||
|
**Unlock All Button:**
|
||||||
|
- Unlocks and completes all puzzle steps in the current level
|
||||||
|
- Processes steps in dependency order using iterative algorithm
|
||||||
|
- Logs progression to console for debugging
|
||||||
|
- Useful for testing late-game content or verifying completion flow
|
||||||
|
|
||||||
|
#### Step List
|
||||||
|
|
||||||
|
Each step in the current level displays:
|
||||||
|
|
||||||
|
**Step Header:**
|
||||||
|
- Display name in bold text
|
||||||
|
- Step ID in gray text below name
|
||||||
|
|
||||||
|
**State Indicators:**
|
||||||
|
- 🔒 **Locked** (gray background) - Dependencies not met, step unavailable
|
||||||
|
- 🔓 **Unlocked** (yellow background) - Available for interaction but not completed
|
||||||
|
- ✅ **Completed** (green background) - Successfully completed
|
||||||
|
|
||||||
|
**Action Buttons:**
|
||||||
|
- **Toggle Lock** - Manually lock/unlock the step
|
||||||
|
- Bypasses normal dependency requirements
|
||||||
|
- Useful for testing specific scenarios
|
||||||
|
- Does not affect dependent steps automatically
|
||||||
|
- **Complete** - Marks step as completed
|
||||||
|
- Only enabled when step is unlocked
|
||||||
|
- Fires completion events
|
||||||
|
- Automatically unlocks dependent steps
|
||||||
|
- Updates state indicators in real-time
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user