Compare commits

..

13 Commits

Author SHA1 Message Date
16962a5ae3 Merge branch 'main' into animation-pipeline-overhaul 2025-11-13 15:21:32 +01:00
Michal Pikulski
612ca7eae8 Interactables documentaiton 2025-11-11 15:55:38 +01:00
2602cbbeb3 Trafalgar-Reimport 2025-11-11 14:24:56 +01:00
acf46c701e Last life cycle refactor updates + add comprehensive documentation (#57)
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: #57
2025-11-11 12:32:36 +00:00
fe2eb0a280 Merge remote-tracking branch 'origin/kill-json-player' 2025-11-11 10:06:23 +01:00
0aa2270e1a Lifecycle System Refactor & Logging Centralization (#56)
## ManagedBehaviour System Refactor

- **Sealed `Awake()`** to prevent override mistakes that break singleton registration
- **Added `OnManagedAwake()`** for early initialization (fires during registration)
- **Renamed lifecycle hook:** `OnManagedAwake()` → `OnManagedStart()` (fires after boot, mirrors Unity's Awake→Start)
- **40 files migrated** to new pattern (2 core, 38 components)
- Eliminated all fragile `private new void Awake()` patterns
- Zero breaking changes - backward compatible

## Centralized Logging System

- **Automatic tagging** via `CallerMemberName` and `CallerFilePath` - logs auto-tagged as `[ClassName][MethodName] message`
- **Unified API:** Single `Logging.Debug/Info/Warning/Error()` replaces custom `LogDebugMessage()` implementations
- **~90 logging call sites** migrated across 10 files
- **10 redundant helper methods** removed
- All logs broadcast via `Logging.OnLogEntryAdded` event for real-time monitoring

## Custom Log Console (Editor Window)

- **Persistent filter popups** for multi-selection (classes, methods, log levels) - windows stay open during selection
- **Search** across class names, methods, and message content
- **Time range filter** with MinMaxSlider
- **Export** filtered logs to timestamped `.txt` files
- **Right-click context menu** for quick filtering and copy actions
- **Visual improvements:** White text, alternating row backgrounds, color-coded log levels
- **Multiple instances** supported for simultaneous system monitoring
- Open via `AppleHills > Custom Log Console`

Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com>
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: #56
2025-11-11 08:48:29 +00:00
MacBuilder
d647bb5707 Target eliminated, good job 47 2025-11-10 15:55:28 +01:00
MacBuilder
dec7ced2d1 Began killing json 2025-11-10 14:57:42 +01:00
Michal Pikulski
c4d356886f Dumb fix for data clearing 2025-11-10 14:27:14 +01:00
Michal Pikulski
64d7f19b83 ADd clearing saves 2025-11-10 14:11:02 +01:00
ce21e8b02e Merge branch 'main' of https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction 2025-11-10 13:13:36 +01:00
f44f8c5171 Made intro VO play in diving minigame 2025-11-10 13:13:32 +01:00
9772206362 Merge pull request 'Clean up logging' (#55) from clean_up_logging into main
Reviewed-on: #55
2025-11-10 12:06:05 +00:00
138 changed files with 6853 additions and 2280 deletions

3
.gitignore vendored
View File

@@ -104,3 +104,6 @@ InitTestScene*.unity*
.vscode/launch.json
.vscode/settings.json
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
# WIP docs
/docs/wip/

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d60f8ff6bf46b494b80e210228c43e61
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -185,7 +185,7 @@ AnimatorController:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: trafalgar_0002
m_Name: AnimSM_Trafalgar
serializedVersion: 5
m_AnimatorParameters:
- m_Name: Speed

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bdcb5eef530d4d341b9957fc5d954164
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

View 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:

View 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

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4f8877ac6aea8414b836f7aebe5971c5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

View 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:

View 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

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9cbfb3dc06a1e1e469c8648617968feb
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

View 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:

View 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

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ae948f43b1dcedb408002364a6c894df
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View 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:

View 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

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5f5d73421b606944bb2a2c34b4fc7117
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 57224356af3d045dbbf8b420e13b4b10
TextureImporter:
internalIDToNameTable:
- first:
213: 2364528222304962155
second: ramasjang_icon_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
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: 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: 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
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: ramasjang_icon_0
rect:
serializedVersion: 2
x: 4
y: 5
width: 334
height: 334
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: b668fdfe3ed70d020800000000000000
internalID: 2364528222304962155
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
ramasjang_icon_0: 2364528222304962155
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -71,7 +71,6 @@ namespace AppleHills.Editor
{
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
CardSystemManager.Instance.OnCardRarityUpgraded += OnCardRarityUpgraded;
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
isSubscribed = true;
@@ -87,7 +86,6 @@ namespace AppleHills.Editor
{
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
CardSystemManager.Instance.OnCardRarityUpgraded -= OnCardRarityUpgraded;
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
}
@@ -109,13 +107,6 @@ namespace AppleHills.Editor
Repaint();
}
private void OnCardRarityUpgraded(CardData card)
{
lastEventMessage = $"Card upgraded: {card.Name} → {card.Rarity}!";
RefreshData();
Repaint();
}
private void OnBoosterCountChanged(int count)
{
boosterCount = count;

View File

@@ -1,4 +1,4 @@
using UnityEngine;
using UnityEngine;
using UnityEditor;
namespace Interactions

View 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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60a300585fef4ac990c963c8b37c3887
timeCreated: 1762814971

View File

@@ -181,7 +181,7 @@ namespace Editor
string scenePath = AssetDatabase.GUIDToAssetPath(guid);
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)
{
if (component == null || component.gameObject.scene != scene) continue;

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cb41c852d70c4066bf510792ee19b3f5
timeCreated: 1762866335

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3045d5bcf3e04203bfe060f80d8913ca
timeCreated: 1762866335

View File

@@ -12,7 +12,7 @@ namespace Editor.Lifecycle
/// 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:
/// - 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
/// - Components in the scene never receive their OnSceneReady() callback
///

View File

@@ -560,6 +560,15 @@ namespace AppleHills.Editor.PuzzleSystem
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
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();
_debugScrollPosition = EditorGUILayout.BeginScrollView(_debugScrollPosition);
@@ -870,6 +879,121 @@ namespace AppleHills.Editor.PuzzleSystem
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
}
}

View File

@@ -1,8 +1,6 @@
using UnityEditor;
using AppleHills.Core.Settings;
using Core;
using UnityEngine;
using UnityEngine.Rendering.VirtualTexturing;
namespace AppleHills.Editor
{
@@ -65,7 +63,7 @@ namespace AppleHills.Editor
GetPuzzlePromptRange
);
LogDebugMessage("Editor settings loaded for Scene View use");
Logging.Debug("Editor settings loaded for Scene View use");
}
public static void RefreshSceneViews()
@@ -102,14 +100,5 @@ namespace AppleHills.Editor
return null;
}
private static void LogDebugMessage(string message)
{
if (Application.isPlaying &&
DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[EditorSettingsProvider] {message}");
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 943b203cde5343c68a6278c111fce2ed
timeCreated: 1757508162

View File

@@ -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();
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f67e06e997f642509ba61ea12b0f793e
timeCreated: 1757503955

View File

@@ -1,5 +1,9 @@
version: 2
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_2.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/External/Plugins/codeandweb.com/Example/SpriteSheet/sprites.tpsheet
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_2.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
enableDebugOutput: 0

View File

@@ -313,14 +313,22 @@ PrefabInstance:
propertyPath: m_Layer
value: 7
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}
propertyPath: m_Size.x
value: 3.8700001
value: 4.97
objectReference: {fileID: 0}
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
propertyPath: m_Size.y
value: 7.55122
value: 7.53
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}
propertyPath: m_SortingOrder
value: 1
@@ -329,6 +337,10 @@ PrefabInstance:
propertyPath: m_SpriteSortPoint
value: 1
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}
propertyPath: m_LocalScale.x
value: 1

View File

@@ -725,10 +725,9 @@ GameObject:
m_Component:
- component: {fileID: 3864057818161790164}
- component: {fileID: 5271824036850954050}
- component: {fileID: 8081783206361873868}
- component: {fileID: 5548642987123338363}
- component: {fileID: 3058107077406709872}
- component: {fileID: 587711432829270645}
- component: {fileID: 4189849640380816173}
m_Layer: 5
m_Name: Icon
m_TagString: Untagged
@@ -763,53 +762,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4599222264323240281}
m_CullTransparentMesh: 1
--- !u!114 &8081783206361873868
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4599222264323240281}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: -1
--- !u!114 &5548642987123338363
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4599222264323240281}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fd09147b9e9d42a48d6ddc915ddc3d2, type: 3}
m_Name:
m_EditorClassIdentifier: SkiaSharp.Unity::SkiaSharp.Unity.SkottiePlayerV2
lottieFile: {fileID: 4900000, guid: 50e22b5bb8a496840952f2563758c13c, type: 3}
customResolution: 0
resWidth: 250
resHeight: 250
stateName: Idle
resetAfterFinished: 0
autoPlay: 1
loop: 1
--- !u!114 &3058107077406709872
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -850,7 +802,7 @@ MonoBehaviour:
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 8081783206361873868}
m_TargetGraphic: {fileID: 0}
m_OnClick:
m_PersistentCalls:
m_Calls:
@@ -880,6 +832,36 @@ MonoBehaviour:
m_EditorClassIdentifier: AppleHillsScripts::UI.HudMenuButton
pagePrefab: {fileID: 1498581815400593087, guid: ccd858c7962d48147b0233c1bf1382f5, type: 3}
buttonName: RamaSjang Button
--- !u!114 &4189849640380816173
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4599222264323240281}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 2364528222304962155, guid: 57224356af3d045dbbf8b420e13b4b10, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &5113586844274188410
GameObject:
m_ObjectHideFlags: 0

View File

@@ -260,6 +260,7 @@ MonoBehaviour:
getFlirtyMin: 4
getFlirtyMax: 5
fakeChocolate: {fileID: 2391935521422290070}
realChocolate: {fileID: 0}
distractedAudioClips: {fileID: 6418180475301049370, guid: 956d8d84e8dd1de4e94ba48c041dc6ec, type: 2}
angryAudioClips: {fileID: 6418180475301049370, guid: 22e6e844862e5b94989b572cb70c1eff, type: 2}
feederClips: {fileID: 6418180475301049370, guid: 2e607d3f32c25a14ea074850dd2f8ac5, type: 2}

View File

@@ -148,8 +148,6 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6108475066390421500}
- {fileID: 6717870941799174515}
- {fileID: 4136733570236406132}
m_Father: {fileID: 1315170081792486277}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -402,8 +400,8 @@ MonoBehaviour:
m_EditorClassIdentifier: '::'
PageName:
transitionDuration: 0.3
rainbowIn: {fileID: 5382650426034128680}
rainbowOut: {fileID: 3983328028282460839}
rainbowIn: {fileID: 0}
rainbowOut: {fileID: 0}
gameLayoutContainer: {fileID: 904161782565348054}
exitButton: {fileID: 8427602740714176801}
rectMask: {fileID: 7425566603516801919}
@@ -528,99 +526,6 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!1 &3983328028282460839
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6717870941799174515}
- component: {fileID: 6334910097310371923}
- component: {fileID: 8821021733826365168}
- component: {fileID: 9163925342151684341}
m_Layer: 5
m_Name: RainbowOut
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6717870941799174515
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3983328028282460839}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8293076336493130222}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6334910097310371923
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3983328028282460839}
m_CullTransparentMesh: 1
--- !u!114 &8821021733826365168
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3983328028282460839}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: -1
--- !u!114 &9163925342151684341
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3983328028282460839}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fd09147b9e9d42a48d6ddc915ddc3d2, type: 3}
m_Name:
m_EditorClassIdentifier: SkiaSharp.Unity::SkiaSharp.Unity.SkottiePlayerV2
lottieFile: {fileID: 4900000, guid: 589505308c5daf449800f30dd4b92ce7, type: 3}
customResolution: 0
resWidth: 250
resHeight: 250
stateName:
resetAfterFinished: 0
autoPlay: 0
loop: 0
--- !u!1 &4270065472017787841
GameObject:
m_ObjectHideFlags: 0
@@ -936,99 +841,6 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!1 &5382650426034128680
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6108475066390421500}
- component: {fileID: 2398693306920598044}
- component: {fileID: 7920249735731934357}
- component: {fileID: 3566391948883171773}
m_Layer: 5
m_Name: RainbowIn
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6108475066390421500
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5382650426034128680}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8293076336493130222}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2398693306920598044
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5382650426034128680}
m_CullTransparentMesh: 1
--- !u!114 &7920249735731934357
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5382650426034128680}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.RawImage
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 0}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: -1
--- !u!114 &3566391948883171773
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5382650426034128680}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fd09147b9e9d42a48d6ddc915ddc3d2, type: 3}
m_Name:
m_EditorClassIdentifier: SkiaSharp.Unity::SkiaSharp.Unity.SkottiePlayerV2
lottieFile: {fileID: 4900000, guid: 622be2ef9d5e27d45a9deaf7ed805f5f, type: 3}
customResolution: 0
resWidth: 250
resHeight: 250
stateName:
resetAfterFinished: 0
autoPlay: 0
loop: 0
--- !u!1 &5867455130109727138
GameObject:
m_ObjectHideFlags: 0

File diff suppressed because one or more lines are too long

View File

@@ -1562,7 +1562,7 @@ AudioSource:
OutputAudioMixerGroup: {fileID: 3533147658878909314, guid: 727a7e4b6df4b0d47897f7d8ee7fa323, type: 2}
m_audioClip: {fileID: 0}
m_Resource: {fileID: 0}
m_PlayOnAwake: 1
m_PlayOnAwake: 0
m_Volume: 1
m_Pitch: 1
Loop: 0
@@ -3633,6 +3633,18 @@ PrefabInstance:
propertyPath: m_Name
value: Tutorial
objectReference: {fileID: 0}
- target: {fileID: 4267886887244421663, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3}
propertyPath: introVO
value:
objectReference: {fileID: 8300000, guid: fca641cdc8dcd074483fad3db1cbe24c, type: 3}
- target: {fileID: 4267886887244421663, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3}
propertyPath: playTutorial
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4267886887244421663, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3}
propertyPath: bottleAudioPlayer
value:
objectReference: {fileID: 747976408}
- target: {fileID: 8452897808363562605, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3}
propertyPath: m_Sprite
value:

View File

@@ -56,7 +56,7 @@ public class BirdEyesBehavior : ManagedBehaviour
_statemachine.ChangeState("BirdSpawned");
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
base.OnSceneRestoreRequested(serializedData);
@@ -75,7 +75,7 @@ public class BirdEyesBehavior : ManagedBehaviour
}
}
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
return _wolterisoutTriggered.ToString();
}

View File

@@ -11,9 +11,6 @@
"OptimizedRope",
"AudioSourceEvents",
"NewAssembly",
"SkiaSharp.Unity",
"SkiaSharp.Editor",
"SkiaSharp",
"Unity.Cinemachine"
],
"includePlatforms": [],

View File

@@ -1,4 +1,4 @@
using System;
using System;
using AppleHills.Core.Settings;
using UnityEngine;
using Core;
@@ -30,14 +30,10 @@ namespace Bootstrap
private float _sceneLoadingProgress = 0f;
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
LogDebugMessage("BootSceneController.Awake() - Initializing loading screen DURING bootstrap");
Logging.Debug("BootSceneController.Awake() - Initializing loading screen DURING bootstrap");
// Validate loading screen exists
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
_bootComplete = true;
_currentPhase = LoadingPhase.SceneLoading;
@@ -85,10 +81,8 @@ namespace Bootstrap
Invoke(nameof(StartLoadingMainMenu), minDelayAfterBoot);
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Manual cleanup for events
if (initialLoadingScreen != null)
{
@@ -102,12 +96,12 @@ namespace Bootstrap
/// </summary>
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
if (CinematicsManager.Instance != null)
{
LogDebugMessage("Attempting to play intro cinematic");
Logging.Debug("Attempting to play intro cinematic");
// Use LoadAndPlayCinematic to play the intro sequence
CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence", false);
@@ -149,13 +143,13 @@ namespace Bootstrap
{
if (debugMode)
{
LogDebugMessage($"Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
Logging.Debug($"Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
}
}
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}");
}
@@ -172,7 +166,7 @@ namespace Bootstrap
private async void LoadMainScene()
{
LogDebugMessage($"Loading main menu scene: {mainSceneName}");
Logging.Debug($"Loading main menu scene: {mainSceneName}");
try
{
@@ -186,7 +180,7 @@ namespace Bootstrap
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;
// 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);
// Restore scene data for the main menu
if (SaveLoadManager.Instance != null)
{
LogDebugMessage($"Restoring scene data for: {mainSceneName}");
Logging.Debug($"Restoring scene data for: {mainSceneName}");
SaveLoadManager.Instance.RestoreSceneData();
}
@@ -246,7 +240,7 @@ namespace Bootstrap
Scene currentScene = SceneManager.GetActiveScene();
string startingSceneName = currentScene.name;
LogDebugMessage($"Unloading StartingScene: {startingSceneName}");
Logging.Debug($"Unloading StartingScene: {startingSceneName}");
// Unload the StartingScene
await SceneManager.UnloadSceneAsync(startingSceneName);
@@ -255,14 +249,14 @@ namespace Bootstrap
Scene mainMenuScene = SceneManager.GetSceneByName(mainSceneName);
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(gameObject);
}
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);
}
}
private void LogDebugMessage(string message)
{
if ( _logVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[BootSceneController] {message}");
}
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Threading.Tasks;
using AppleHills.Core.Settings;
using Core;
using Core.Lifecycle;
using UnityEngine;
@@ -105,7 +104,7 @@ namespace Bootstrap
// Notify the LifecycleManager that boot is complete
if (Application.isPlaying)
{
LogDebugMessage("Calling LifecycleManager.OnBootCompletionTriggered()");
Logging.Debug("Calling LifecycleManager.OnBootCompletionTriggered()");
if (LifecycleManager.Instance != null)
{
LifecycleManager.Instance.OnBootCompletionTriggered();
@@ -127,7 +126,7 @@ namespace Bootstrap
// Notify the LifecycleManager that boot is complete
if (Application.isPlaying)
{
LogDebugMessage("Calling LifecycleManager.OnBootCompletionTriggered()");
Logging.Debug("Calling LifecycleManager.OnBootCompletionTriggered()");
if (LifecycleManager.Instance != null)
{
LifecycleManager.Instance.OnBootCompletionTriggered();
@@ -238,16 +237,7 @@ namespace Bootstrap
{
CurrentProgress = Mathf.Clamp01(progress);
OnBootProgressChanged?.Invoke(CurrentProgress);
LogDebugMessage($"Progress: {CurrentProgress:P0}");
}
private static void LogDebugMessage(string message)
{
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().bootstrapLogVerbosity <=
LogVerbosity.Debug)
{
Logging.Debug($"[CustomBoot] {message}");
}
Logging.Debug($"Progress: {CurrentProgress:P0}");
}
}
}

View File

@@ -138,7 +138,7 @@ namespace Bootstrap
float displayProgress = Mathf.Min(steadyProgress, actualProgress);
// 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
if (progressBarImage != null)
@@ -151,7 +151,7 @@ namespace Bootstrap
if (steadyProgress >= 1.0f && displayProgress >= 1.0f)
{
_animationComplete = true;
LogDebugMessage("Animation complete");
Logging.Debug("Animation complete");
break;
}
@@ -163,7 +163,7 @@ namespace Bootstrap
if (progressBarImage != null)
{
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
@@ -172,7 +172,7 @@ namespace Bootstrap
if (loadingScreenContainer != null)
{
loadingScreenContainer.SetActive(false);
LogDebugMessage("Animation AND loading complete, hiding screen");
Logging.Debug("Animation AND loading complete, hiding screen");
// Invoke the callback when fully hidden
_onLoadingScreenFullyHidden?.Invoke();
@@ -189,7 +189,7 @@ namespace Bootstrap
/// </summary>
public void HideLoadingScreen()
{
LogDebugMessage("Loading complete, marking loading as finished");
Logging.Debug("Loading complete, marking loading as finished");
// Mark that loading is complete
_loadingComplete = true;
@@ -200,7 +200,7 @@ namespace Bootstrap
if (loadingScreenContainer != null)
{
loadingScreenContainer.SetActive(false);
LogDebugMessage("Animation already complete, hiding screen immediately");
Logging.Debug("Animation already complete, hiding screen immediately");
// Invoke the callback when fully hidden
_onLoadingScreenFullyHidden?.Invoke();
@@ -210,7 +210,7 @@ namespace Bootstrap
}
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
}
}
@@ -244,13 +244,5 @@ namespace Bootstrap
return tcs.Task;
}
private void LogDebugMessage(string message)
{
if ( _logVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[InitialLoadingScreen] {message}");
}
}
}
}

View File

@@ -37,17 +37,13 @@ namespace Cinematics
public PlayableDirector playableDirector;
public override int ManagedAwakePriority => 170; // Cinematic systems
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
Logging.Debug("[CinematicsManager] Initialized");
}

View File

@@ -15,11 +15,8 @@ namespace Cinematics
private float _holdStartTime;
private bool _isHolding;
private bool _skipPerformed;
private bool _initialized = false;
public override int ManagedAwakePriority => 180; // Cinematic UI
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// Reset the progress bar
if (radialProgressBar != null)
@@ -33,10 +30,8 @@ namespace Cinematics
Logging.Debug("[SkipCinematic] Initialized");
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Clean up subscriptions
UnsubscribeFromCinematicsEvents();
}

View File

@@ -34,17 +34,13 @@ namespace Core
public event Action OnGamePaused;
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 so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_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";
DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";
@@ -57,9 +53,9 @@ namespace Core
_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
}
@@ -79,7 +75,7 @@ namespace Core
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))
{
_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);
}
LogDebugMessage($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
Logging.Debug($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
}
/// <summary>
@@ -123,7 +119,7 @@ namespace Core
ApplyPause(false);
}
LogDebugMessage($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
Logging.Debug($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
}
/// <summary>
@@ -162,12 +158,12 @@ namespace Core
OnGameResumed?.Invoke();
}
LogDebugMessage($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
Logging.Debug($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
}
private void InitializeSettings()
{
LogDebugMessage("Starting settings initialization...", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("Starting settings initialization...");
// Load settings synchronously
var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous<PlayerFollowerSettings>();
@@ -178,7 +174,7 @@ namespace Core
if (playerSettings != null)
{
ServiceLocator.Register<IPlayerFollowerSettings>(playerSettings);
LogDebugMessage("PlayerFollowerSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("PlayerFollowerSettings registered successfully");
}
else
{
@@ -188,7 +184,7 @@ namespace Core
if (interactionSettings != null)
{
ServiceLocator.Register<IInteractionSettings>(interactionSettings);
LogDebugMessage("InteractionSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("InteractionSettings registered successfully");
}
else
{
@@ -198,7 +194,7 @@ namespace Core
if (minigameSettings != null)
{
ServiceLocator.Register<IDivingMinigameSettings>(minigameSettings);
LogDebugMessage("MinigameSettings registered successfully", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("MinigameSettings registered successfully");
}
else
{
@@ -209,7 +205,7 @@ namespace Core
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null;
if (_settingsLoaded)
{
LogDebugMessage("All settings loaded and registered with ServiceLocator", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("All settings loaded and registered with ServiceLocator");
}
else
{
@@ -222,7 +218,7 @@ namespace Core
/// </summary>
private void InitializeDeveloperSettings()
{
LogDebugMessage("Starting developer settings initialization...", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("Starting developer settings initialization...");
// Load developer settings
var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings<DivingDeveloperSettings>();
@@ -232,7 +228,7 @@ namespace Core
if (_developerSettingsLoaded)
{
LogDebugMessage("All developer settings loaded successfully", "SettingsInitialization", _settingsLogVerbosity);
Logging.Debug("All developer settings loaded successfully");
}
else
{
@@ -266,19 +262,6 @@ namespace Core
{
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
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;

View File

@@ -47,32 +47,26 @@ namespace Core
// Broadcasts when any two items are successfully combined
// Args: first item data, second item data, result item data
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
public override int ManagedAwakePriority => 75; // Item registry
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
Logging.Debug("[ItemManager] Initialized");
}
protected override void OnSceneReady()
internal override void OnSceneReady()
{
// Replaces SceneLoadStarted subscription for clearing registrations
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
ClearAllRegistrations();
}
@@ -144,7 +138,6 @@ namespace Core
{
s.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
s.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
s.OnForbiddenItemSlotted -= ItemSlot_OnForbiddenItemSlotted;
s.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
}
}
@@ -191,7 +184,6 @@ namespace Core
// Subscribe to all slot events
slot.OnCorrectItemSlotted += ItemSlot_OnCorrectItemSlotted;
slot.OnIncorrectItemSlotted += ItemSlot_OnIncorrectItemSlotted;
slot.OnForbiddenItemSlotted += ItemSlot_OnForbiddenItemSlotted;
slot.OnItemSlotRemoved += ItemSlot_OnItemSlotRemoved;
}
}
@@ -204,7 +196,6 @@ namespace Core
// Unsubscribe from all slot events
slot.OnCorrectItemSlotted -= ItemSlot_OnCorrectItemSlotted;
slot.OnIncorrectItemSlotted -= ItemSlot_OnIncorrectItemSlotted;
slot.OnForbiddenItemSlotted -= ItemSlot_OnForbiddenItemSlotted;
slot.OnItemSlotRemoved -= ItemSlot_OnItemSlotRemoved;
}
}

View File

@@ -6,12 +6,19 @@
/// </summary>
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>
/// Called once per component after bootstrap completes.
/// Guaranteed to be called after all bootstrap resources are loaded.
/// For late-registered components, called immediately upon registration.
/// </summary>
ManagedAwake,
ManagedStart,
/// <summary>
/// Called before a scene is unloaded.

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using UnityEngine;
@@ -59,11 +59,11 @@ namespace Core.Lifecycle
#region State Flags
private bool isBootComplete = false;
private bool isBootComplete;
private string currentSceneReady = "";
// Scene loading state tracking
private bool isLoadingScene = false;
private bool isLoadingScene;
private string sceneBeingLoaded = "";
private List<ManagedBehaviour> pendingSceneComponents = new List<ManagedBehaviour>();
@@ -120,43 +120,49 @@ namespace Core.Lifecycle
// Track which scene this component belongs to
componentScenes[component] = sceneName;
// ALWAYS add to managedAwakeList - this is the master list used for save/load
InsertSorted(managedAwakeList, component, component.ManagedAwakePriority);
// Add to all lifecycle lists (order of registration determines execution order)
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)
{
// Check if we're currently loading a scene
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);
LogDebug($"Batched component for scene load: {component.gameObject.name} (Scene: {sceneName})");
}
else
{
// Truly late registration (component enabled after scene is ready)
// Call OnManagedAwake immediately since boot already completed
LogDebug($"Late registration: Calling OnManagedAwake immediately for {component.gameObject.name}");
// Call OnManagedStart immediately since boot already completed
LogDebug($"Late registration: Calling OnManagedStart immediately for {component.gameObject.name}");
try
{
component.InvokeManagedAwake();
component.OnManagedStart();
HandleAutoRegistrations(component);
}
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()
// 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 boot not complete, component stays in list and will be processed by BroadcastManagedStart()
// If this scene is already ready (and we're not in loading mode), call OnSceneReady immediately
if (!isLoadingScene && currentSceneReady == sceneName)
@@ -164,7 +170,7 @@ namespace Core.Lifecycle
LogDebug($"Late registration: Calling OnSceneReady immediately for {component.gameObject.name}");
try
{
component.InvokeSceneReady();
component.OnSceneReady();
}
catch (Exception ex)
{
@@ -202,7 +208,7 @@ namespace Core.Lifecycle
/// <summary>
/// Called by CustomBoot when boot completes.
/// Broadcasts ManagedAwake to all registered components.
/// Broadcasts ManagedStart to all registered components.
/// </summary>
public void OnBootCompletionTriggered()
{
@@ -210,16 +216,16 @@ namespace Core.Lifecycle
return;
LogDebug("=== Boot Completion Triggered ===");
BroadcastManagedAwake();
BroadcastManagedStart();
isBootComplete = true;
}
/// <summary>
/// Broadcast OnManagedAwake to all registered components (priority ordered).
/// Broadcast OnManagedStart to all registered components (priority ordered).
/// </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
var componentsCopy = new List<ManagedBehaviour>(managedAwakeList);
@@ -230,12 +236,12 @@ namespace Core.Lifecycle
try
{
component.InvokeManagedAwake();
component.OnManagedStart();
HandleAutoRegistrations(component);
}
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}");
// Sort by ManagedAwake priority (lower values first)
pendingSceneComponents.Sort((a, b) => a.ManagedAwakePriority.CompareTo(b.ManagedAwakePriority));
// Call OnManagedAwake in priority order
// Call OnManagedStart in registration order
foreach (var component in pendingSceneComponents)
{
if (component == null) continue;
try
{
component.InvokeManagedAwake();
component.OnManagedStart();
HandleAutoRegistrations(component);
LogDebug($"Processed batched component: {component.gameObject.name} (Priority: {component.ManagedAwakePriority})");
LogDebug($"Processed batched component: {component.gameObject.name}");
}
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>
/// Broadcast OnSceneUnloading to components in the specified scene (reverse priority order).
/// Broadcast OnSceneUnloading to components in the specified scene.
/// </summary>
public void BroadcastSceneUnloading(string sceneName)
{
@@ -315,7 +318,7 @@ namespace Core.Lifecycle
{
try
{
component.InvokeSceneUnloading();
component.OnSceneUnloading();
}
catch (Exception ex)
{
@@ -326,8 +329,8 @@ namespace Core.Lifecycle
}
/// <summary>
/// Broadcast OnSceneReady to components in the specified scene (priority order).
/// If scene loading mode is active, processes batched components first.
/// Broadcast OnSceneReady to components in the specified scene.
/// Processes batched components first, then calls OnSceneReady on all components in that scene.
/// </summary>
public void BroadcastSceneReady(string sceneName)
{
@@ -351,7 +354,7 @@ namespace Core.Lifecycle
{
try
{
component.InvokeSceneReady();
component.OnSceneReady();
}
catch (Exception ex)
{
@@ -379,7 +382,7 @@ namespace Core.Lifecycle
try
{
string serializedData = component.InvokeSceneSaveRequested();
string serializedData = component.OnSceneSaveRequested();
if (!string.IsNullOrEmpty(serializedData))
{
string saveId = component.SaveId;
@@ -415,7 +418,7 @@ namespace Core.Lifecycle
try
{
string serializedData = component.InvokeGlobalSaveRequested();
string serializedData = component.OnGlobalSaveRequested();
if (!string.IsNullOrEmpty(serializedData))
{
saveData[component.SaveId] = serializedData;
@@ -455,7 +458,7 @@ namespace Core.Lifecycle
{
try
{
component.InvokeSceneRestoreRequested(serializedData);
component.OnSceneRestoreRequested(serializedData);
restoredCount++;
LogDebug($"Restored scene data to: {component.SaveId}");
}
@@ -486,7 +489,7 @@ namespace Core.Lifecycle
try
{
component.InvokeSceneRestoreCompleted();
component.OnSceneRestoreCompleted();
}
catch (Exception ex)
{
@@ -519,7 +522,7 @@ namespace Core.Lifecycle
{
try
{
component.InvokeGlobalRestoreRequested(serializedData);
component.OnGlobalRestoreRequested(serializedData);
restoredCount++;
LogDebug($"Restored global data to: {component.SaveId}");
}
@@ -551,7 +554,7 @@ namespace Core.Lifecycle
try
{
component.InvokeGlobalLoadCompleted();
component.OnGlobalLoadCompleted();
}
catch (Exception ex)
{
@@ -578,7 +581,7 @@ namespace Core.Lifecycle
try
{
component.InvokeGlobalSaveStarted();
component.OnGlobalSaveStarted();
}
catch (Exception ex)
{
@@ -611,42 +614,6 @@ namespace Core.Lifecycle
#endregion
#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>
/// Log debug message if debug logging is enabled.

View File

@@ -1,5 +1,4 @@
using System;
using UnityEngine;
using UnityEngine;
namespace Core.Lifecycle
{
@@ -9,46 +8,6 @@ namespace Core.Lifecycle
/// </summary>
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
/// <summary>
@@ -68,39 +27,28 @@ namespace Core.Lifecycle
/// Unique identifier for this component in the save system.
/// Default: "SceneName/GameObjectName/ComponentType"
/// Override ONLY for special cases (e.g., singletons like "PlayerController", or custom IDs).
/// Cached on first access to avoid runtime allocation.
/// </summary>
public virtual string SaveId
{
get
{
string sceneName = gameObject.scene.IsValid() ? gameObject.scene.name : "UnknownScene";
string componentType = GetType().Name;
return $"{sceneName}/{gameObject.name}/{componentType}";
if (_cachedSaveId == null)
{
string sceneName = gameObject.scene.IsValid() ? gameObject.scene.name : "UnknownScene";
string componentType = GetType().Name;
_cachedSaveId = $"{sceneName}/{gameObject.name}/{componentType}";
}
return _cachedSaveId;
}
}
#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
private bool _isRegistered;
private string _cachedSaveId;
#endregion
@@ -108,9 +56,9 @@ namespace Core.Lifecycle
/// <summary>
/// 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>
protected virtual void Awake()
private void Awake()
{
if (LifecycleManager.Instance != null)
{
@@ -125,13 +73,16 @@ namespace Core.Lifecycle
/// <summary>
/// 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>
protected virtual void OnDestroy()
private void OnDestroy()
{
if (!_isRegistered)
return;
// Call managed destroy hook
OnManagedDestroy();
// Unregister from LifecycleManager
if (LifecycleManager.Instance != null)
{
@@ -152,34 +103,47 @@ namespace Core.Lifecycle
#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>
/// Called once per component after bootstrap completes.
/// 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).
/// 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>
protected virtual void OnManagedAwake()
internal virtual void OnManagedStart()
{
// Override in derived classes
}
/// <summary>
/// Called before the scene this component belongs to is unloaded.
/// Called in REVERSE priority order (higher values execute first).
/// Use for scene-specific cleanup.
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnSceneUnloading()
internal virtual void OnSceneUnloading()
{
// Override in derived classes
}
/// <summary>
/// Called after the scene this component belongs to has finished loading.
/// Called in priority order (lower values execute first).
/// Use for scene-specific initialization.
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnSceneReady()
internal virtual void OnSceneReady()
{
// Override in derived classes
}
@@ -193,8 +157,10 @@ namespace Core.Lifecycle
/// - Called BEFORE scene unload during scene transitions
/// - Frequency: Every scene transition
/// - Use for: Level progress, object positions, puzzle states
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual string OnSceneSaveRequested()
internal virtual string OnSceneSaveRequested()
{
return null; // Default: no data to save
}
@@ -211,8 +177,10 @@ namespace Core.Lifecycle
/// - Called AFTER scene load, during OnSceneReady phase
/// - Frequency: Every scene transition
/// - Use for: Restoring level progress, object positions, puzzle states
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnSceneRestoreRequested(string serializedData)
internal virtual void OnSceneRestoreRequested(string serializedData)
{
// Default: no-op
}
@@ -234,8 +202,10 @@ namespace Core.Lifecycle
/// COMMON PATTERN:
/// 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).
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnSceneRestoreCompleted()
internal virtual void OnSceneRestoreCompleted()
{
// Default: no-op
}
@@ -248,8 +218,10 @@ namespace Core.Lifecycle
/// - Called ONCE on game boot after save file is read
/// - NOT called during scene transitions
/// - Use for: Player inventory, unlocked features, card collections
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnGlobalRestoreRequested(string serializedData)
internal virtual void OnGlobalRestoreRequested(string serializedData)
{
// Default: no-op
}
@@ -263,8 +235,10 @@ namespace Core.Lifecycle
/// - Called ONCE before save file is written (on quit, manual save, etc.)
/// - NOT called during scene transitions
/// - Use for: Player inventory, unlocked features, card collections
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual string OnGlobalSaveRequested()
internal virtual string OnGlobalSaveRequested()
{
return null; // Default: no data to save
}
@@ -278,8 +252,10 @@ namespace Core.Lifecycle
/// - Called ONCE on game boot after all restore operations complete
/// - NOT called during scene transitions
/// - Use for: Triggering UI updates, broadcasting load events
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnGlobalLoadCompleted()
internal virtual void OnGlobalLoadCompleted()
{
// Default: no-op
}
@@ -293,19 +269,21 @@ namespace Core.Lifecycle
/// - Called ONCE before save file is written
/// - NOT called during scene transitions
/// - Use for: Final validation, cleanup operations
///
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnGlobalSaveStarted()
internal virtual void OnGlobalSaveStarted()
{
// Default: no-op
}
/// <summary>
/// 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).
/// Only override if you need custom cleanup logic.
/// Internal visibility allows LifecycleManager to call directly. Override in derived classes.
/// </summary>
protected virtual void OnManagedDestroy()
internal virtual void OnManagedDestroy()
{
// Override in derived classes
}

View File

@@ -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
{
[System.Diagnostics.Conditional("ENABLE_LOG")]
public static void Debug(object message)
/// <summary>
/// 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")]
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
}
}

View File

@@ -24,9 +24,6 @@ namespace AppleHills.Core
#endregion Singleton Setup
// Very early initialization - QuickAccess should be available immediately
public override int ManagedAwakePriority => 5;
#region Manager Instances
// Core Managers
@@ -129,20 +126,18 @@ namespace AppleHills.Core
#region Lifecycle Methods
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// QuickAccess has minimal initialization
}
protected override void OnSceneUnloading()
internal override void OnSceneUnloading()
{
// Clear references BEFORE scene unloads for better cleanup timing
ClearReferences();

View File

@@ -43,14 +43,10 @@ namespace Core.SaveLoad
public event Action<string> OnLoadCompleted;
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 so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
// Initialize critical state immediately
@@ -58,7 +54,7 @@ namespace Core.SaveLoad
IsRestoringState = false;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
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
// No need to discover and register them manually
}
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
// SaveLoadManager orchestrates saves, doesn't participate in them
return null;
}
protected override string OnGlobalSaveRequested()
internal override string OnGlobalSaveRequested()
{
// SaveLoadManager orchestrates saves, doesn't participate in them
return null;
@@ -97,10 +93,8 @@ namespace Core.SaveLoad
// ...existing code...
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy(); // Important: call base to unregister from LifecycleManager
if (_instance == this)
{
_instance = null;

View File

@@ -30,9 +30,9 @@ namespace Core
// Enable save/load participation
public override bool AutoRegisterForSave => true;
protected override void Awake()
internal override void OnManagedAwake()
{
base.Awake();
base.OnManagedAwake();
_director = GetComponent<PlayableDirector>();
if (_director != null)
@@ -42,10 +42,8 @@ namespace Core
}
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
if (_director != null)
{
_director.stopped -= OnDirectorStopped;
@@ -65,7 +63,7 @@ namespace Core
#region Save/Load Implementation
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
var saveData = new PlayableDirectorSaveData
{
@@ -77,7 +75,7 @@ namespace Core
return JsonUtility.ToJson(saveData);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{

View File

@@ -44,14 +44,10 @@ namespace Core
private LogVerbosity _logVerbosity = LogVerbosity.Debug;
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 so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
// 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
// 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;
SetupLoadingScreenEvents();
// Load verbosity settings
_logVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().sceneLogVerbosity;
LogDebugMessage($"SceneManagerService initialized, current scene is: {CurrentGameplayScene}");
Logging.Debug($"SceneManagerService initialized, current scene is: {CurrentGameplayScene}");
}
/// <summary>
@@ -93,19 +89,19 @@ namespace Core
if (activeScene.name != BootstrapSceneName)
{
CurrentGameplayScene = activeScene.name;
LogDebugMessage($"Initialized with current scene: {CurrentGameplayScene}");
Logging.Debug($"Initialized with current scene: {CurrentGameplayScene}");
}
// Otherwise default to MainMenu
else
{
CurrentGameplayScene = "AppleHillsOverworld";
LogDebugMessage($"Initialized with default scene: {CurrentGameplayScene}");
Logging.Debug($"Initialized with default scene: {CurrentGameplayScene}");
}
}
else
{
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
LogDebugMessage($"Broadcasting OnSceneUnloading for: {oldSceneName}");
Logging.Debug($"Broadcasting OnSceneUnloading for: {oldSceneName}");
LifecycleManager.Instance?.BroadcastSceneUnloading(oldSceneName);
// PHASE 3: Save scene-specific data via SaveLoadManager (unless skipSave is true)
@@ -316,19 +312,19 @@ namespace Core
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
if (debugSettings.useSaveLoadSystem)
{
LogDebugMessage($"Saving scene data for: {oldSceneName}");
Logging.Debug($"Saving scene data for: {oldSceneName}");
SaveLoadManager.Instance.SaveSceneData();
}
}
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
if (PuzzleS.PuzzleManager.Instance != null)
{
LogDebugMessage($"Clearing puzzle state before scene transition");
Logging.Debug($"Clearing puzzle state before scene transition");
PuzzleS.PuzzleManager.Instance.ClearPuzzleState();
}
@@ -352,7 +348,7 @@ namespace Core
}
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
LogDebugMessage($"Beginning scene load for: {newSceneName}");
Logging.Debug($"Beginning scene load for: {newSceneName}");
LifecycleManager.Instance?.BeginSceneLoad(newSceneName);
// PHASE 9: Load new gameplay scene
await LoadSceneAsync(newSceneName, progress);
CurrentGameplayScene = newSceneName;
// PHASE 10: Broadcast scene ready - processes batched components in priority order, then calls OnSceneReady
LogDebugMessage($"Broadcasting OnSceneReady for: {newSceneName}");
// PHASE 10: Broadcast scene ready - processes batched components, then calls OnSceneReady
Logging.Debug($"Broadcasting OnSceneReady for: {newSceneName}");
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);
// PHASE 11: Restore scene-specific data via SaveLoadManager
@@ -381,13 +377,14 @@ namespace Core
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
if (debugSettings.useSaveLoadSystem)
{
LogDebugMessage($"Restoring scene data for: {newSceneName}");
Logging.Debug($"Restoring scene data for: {newSceneName}");
SaveLoadManager.Instance.RestoreSceneData();
}
}
else if (skipSave)
{
LogDebugMessage($"Skipping restore for: {newSceneName} (skipSave=true)");
SaveLoadManager.Instance.RestoreSceneData();
Logging.Debug($"Skipping restore for: {newSceneName} (skipSave=true)");
}
// PHASE 12: Only hide the loading screen if autoHideLoadingScreen is true
@@ -396,13 +393,5 @@ namespace Core
_loadingScreen.HideLoadingScreen();
}
}
private void LogDebugMessage(string message)
{
if (_logVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[SceneManagerService] {message}");
}
}
}
}

View File

@@ -18,23 +18,18 @@ namespace Core
public GameObject orientationPromptPrefab;
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 70; // Platform-specific utility
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_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;
LogDebugMessage("Initialized");
Logging.Debug("Initialized");
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// Subscribe to SceneManagerService to enforce orientation on every scene load
if (SceneManagerService.Instance != null)
@@ -51,7 +46,7 @@ namespace Core
#endif
}
protected override void OnSceneReady()
internal override void OnSceneReady()
{
// Handle orientation when scene is ready (initial 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"))
{
// 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;
}
@@ -83,37 +78,35 @@ namespace Core
}
else
{
LogDebugMessage($"No orientationConfig assigned. Defaulting to Landscape for scene '{sceneName}'");
Logging.Debug($"No orientationConfig assigned. Defaulting to Landscape for scene '{sceneName}'");
}
switch (requirement)
{
case ScreenOrientationRequirement.Portrait:
LogDebugMessage($"Forcing Portrait for scene '{sceneName}'");
Logging.Debug($"Forcing Portrait for scene '{sceneName}'");
StartCoroutine(ForcePortrait());
break;
case ScreenOrientationRequirement.Landscape:
LogDebugMessage($"Forcing Landscape for scene '{sceneName}'");
Logging.Debug($"Forcing Landscape for scene '{sceneName}'");
StartCoroutine(ForceLandscape());
break;
case ScreenOrientationRequirement.NotApplicable:
default:
// 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());
break;
}
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
// Unsubscribe from events to prevent memory leaks
if (SceneManagerService.Instance != null)
{
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
}
base.OnDestroy(); // Important: call base
}
/// <summary>
@@ -127,12 +120,12 @@ namespace Core
if (!currentlyLandscape)
{
// 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;
}
else
{
LogDebugMessage($"Skipping Landscape enforcement, device already in: {Screen.orientation}");
Logging.Debug($"Skipping Landscape enforcement, device already in: {Screen.orientation}");
}
yield return null;
@@ -160,12 +153,12 @@ namespace Core
if (!currentlyPortrait)
{
// 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;
}
else
{
LogDebugMessage($"Skipping Portrait enforcement, device already in: {Screen.orientation}");
Logging.Debug($"Skipping Portrait enforcement, device already in: {Screen.orientation}");
}
yield return null;
@@ -181,13 +174,5 @@ namespace Core
// Allow device to auto-rotate to correct portrait orientation
Screen.orientation = ScreenOrientation.AutoRotation;
}
private void LogDebugMessage(string message)
{
if (_logVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[SceneOrientationEnforcer] {message}");
}
}
}
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using AppleHills.Core.Settings;
using UnityEngine;
namespace Core.Settings
{
@@ -21,7 +19,7 @@ namespace Core.Settings
public static void Register<T>(T service) where T : class
{
Services[typeof(T)] = service;
LogDebugMessage($"Service registered: {typeof(T).Name}");
Logging.Debug($"Service registered: {typeof(T).Name}");
}
/// <summary>
@@ -36,7 +34,7 @@ namespace Core.Settings
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;
}
@@ -46,16 +44,7 @@ namespace Core.Settings
public static void Clear()
{
Services.Clear();
LogDebugMessage("All services cleared");
}
private static void LogDebugMessage(string message)
{
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity <=
LogVerbosity.Debug)
{
Logging.Debug($"[ServiceLocator] {message}");
}
Logging.Debug("All services cleared");
}
}
}

View File

@@ -1,11 +1,12 @@
using UnityEngine;
using Pixelplacement;
using System;
using System.Collections;
using Core;
using Core.Lifecycle;
using Core.SaveLoad;
using UnityEngine.Audio;
public class PicnicBehaviour : MonoBehaviour
public class PicnicBehaviour : ManagedBehaviour
{
[Header("Random Call Settings")]
public float getDistractedMin = 2f;
@@ -17,7 +18,8 @@ public class PicnicBehaviour : MonoBehaviour
private Animator animator;
[Header("The FakeChocolate to destroy!")]
[SerializeField] private GameObject fakeChocolate; // Assign in Inspector
[SerializeField] private GameObject fakeChocolate;
[SerializeField] private GameObject realChocolate;
private AppleAudioSource _audioSource;
public AudioResource distractedAudioClips;
@@ -25,32 +27,44 @@ public class PicnicBehaviour : MonoBehaviour
public AudioResource feederClips;
public AudioResource moanerClips;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
StartCoroutine(StateCycleRoutine());
}
// Save system configuration
public override bool AutoRegisterForSave => true;
void Awake()
// Runtime state tracking
private bool _fakeChocolateDestroyed;
internal override void OnManagedAwake()
{
stateMachine = GetComponent<AppleMachine>();
animator = GetComponent<Animator>();
_audioSource = GetComponent<AppleAudioSource>();
}
internal override void OnSceneRestoreCompleted()
{
if (_fakeChocolateDestroyed)
{
DestroyChocolateObjects();
}
else
{
StartCoroutine(StateCycleRoutine());
}
}
private IEnumerator StateCycleRoutine()
{
while (true)
{
// Distracted state
float distractedWait = Random.Range(getDistractedMin, getDistractedMax);
float distractedWait = UnityEngine.Random.Range(getDistractedMin, getDistractedMax);
stateMachine.ChangeState("Picnic PPL Distracted");
animator.SetBool("theyDistracted", true);
_audioSource.Stop();
yield return new WaitForSeconds(distractedWait);
// Chilling state
float chillingWait = Random.Range(getFlirtyMin, getFlirtyMax);
float chillingWait = UnityEngine.Random.Range(getFlirtyMin, getFlirtyMax);
stateMachine.ChangeState("Picnic PPL Chilling");
animator.SetBool("theyDistracted", false);
_audioSource.Stop();
@@ -58,27 +72,33 @@ public class PicnicBehaviour : MonoBehaviour
}
}
void StopAudio()
{
_audioSource.Stop();
}
public void triedToStealChocolate()
{
_audioSource.Stop();
animator.SetTrigger("theyAngry");
//stateMachine.ChangeState("Picnic PPL Angry");
Logging.Debug("Hey! Don't steal my chocolate!");
_audioSource.audioSource.resource = angryAudioClips;
_audioSource.Play(0);
}
public void destroyFakeChocolate()
{
_fakeChocolateDestroyed = true;
Destroy(fakeChocolate);
Destroy(realChocolate);
}
private void DestroyChocolateObjects()
{
if (fakeChocolate != null)
{
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);
}
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;
}

View File

@@ -22,7 +22,7 @@ public class soundBird_CanFly : ManagedBehaviour
#region Save/Load Implementation
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
var saveData = new SoundBirdSaveData
{
@@ -32,7 +32,7 @@ public class soundBird_CanFly : ManagedBehaviour
return JsonUtility.ToJson(saveData);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{

View File

@@ -40,25 +40,20 @@ namespace Data.CardSystem
// Event callbacks using System.Action
public event Action<List<CardData>> OnBoosterOpened;
public event Action<CardData> OnCardCollected;
public event Action<CardData> OnCardRarityUpgraded;
public event Action<int> OnBoosterCountChanged;
public event Action<CardData> OnPendingCardAdded;
public event Action<CardData> OnCardPlacedInAlbum;
public override int ManagedAwakePriority => 60; // Data systems
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
// Load card definitions from Addressables, then register with save system
LoadCardDefinitionsFromAddressables();
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
Logging.Debug("[CardSystemManager] Initialized");
}
@@ -720,13 +715,13 @@ namespace Data.CardSystem
#region Save/Load Lifecycle Hooks
protected override string OnGlobalSaveRequested()
internal override string OnGlobalSaveRequested()
{
var state = ExportCardCollectionState();
return JsonUtility.ToJson(state);
}
protected override void OnGlobalRestoreRequested(string serializedData)
internal override void OnGlobalRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
using Interactions;
@@ -34,10 +32,7 @@ namespace Dialogue
public bool IsCompleted { get; private set; }
public string CurrentSpeakerName => dialogueGraph?.speakerName;
public override int ManagedAwakePriority => 150; // Dialogue systems
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// Get required components
appleAudioSource = GetComponent<AppleAudioSource>();
@@ -186,7 +181,7 @@ namespace Dialogue
return null;
}
private void OnDestroy()
internal override void OnManagedDestroy()
{
// Unregister from events
if (PuzzleManager.Instance != null)

View File

@@ -49,14 +49,10 @@ namespace Input
private ITouchInputConsumer defaultConsumer;
private bool isHoldActive;
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
public override int ManagedAwakePriority => 25; // Input infrastructure
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
// Load verbosity settings early
@@ -89,10 +85,10 @@ namespace Input
SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name);
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// 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)
{
SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
@@ -104,11 +100,11 @@ namespace Input
/// </summary>
private void OnSceneLoadCompleted(string sceneName)
{
LogDebugMessage($"Scene loaded: {sceneName}, restoring input mode");
Logging.Debug($"Scene loaded: {sceneName}, restoring input mode");
SwitchInputOnSceneLoaded(sceneName);
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
// Unsubscribe from SceneManagerService events
if (SceneManagerService.Instance != null)
@@ -116,7 +112,6 @@ namespace Input
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
}
base.OnDestroy();
// Input action cleanup happens automatically
}
@@ -182,24 +177,24 @@ namespace Input
Vector2 screenPos = positionAction.ReadValue<Vector2>();
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
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
if (TryDelegateToOverrideConsumer(screenPos, worldPos2D))
{
LogDebugMessage("Tap delegated to override consumer");
Logging.Debug("Tap delegated to override consumer");
return;
}
// Then try to delegate to any ITouchInputConsumer (UI or world interactable)
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);
}
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>();
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
Vector2 worldPos2D = new Vector2(worldPos.x, worldPos.y);
LogDebugMessage($"HoldMove started at {worldPos2D}");
Logging.Debug($"HoldMove started at {worldPos2D}");
// First check for override consumers
if (_overrideConsumers.Count > 0)
{
_activeHoldConsumer = _overrideConsumers[_overrideConsumers.Count - 1];
LogDebugMessage($"Hold delegated to override consumer: {_activeHoldConsumer}");
Logging.Debug($"Hold delegated to override consumer: {_activeHoldConsumer}");
_activeHoldConsumer.OnHoldStart(worldPos2D);
return;
}
@@ -238,7 +233,7 @@ namespace Input
Vector2 screenPos = positionAction.ReadValue<Vector2>();
Vector3 worldPos = Camera.main.ScreenToWorldPoint(screenPos);
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
_activeHoldConsumer?.OnHoldEnd(worldPos2D);
@@ -302,7 +297,7 @@ namespace Input
}
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);
return true;
}
@@ -331,7 +326,7 @@ namespace Input
}
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);
return true;
}
@@ -345,15 +340,15 @@ namespace Input
var consumer = hit.GetComponent<ITouchInputConsumer>();
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);
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
{
LogDebugMessage($"No Collider2D found at {worldPos} for interactable delegation.");
Logging.Debug($"No Collider2D found at {worldPos} for interactable delegation.");
}
return false;
}
@@ -368,7 +363,7 @@ namespace Input
return;
_overrideConsumers.Add(consumer);
LogDebugMessage($"Override consumer registered: {consumer}");
Logging.Debug($"Override consumer registered: {consumer}");
}
/// <summary>
@@ -386,7 +381,7 @@ namespace Input
}
_overrideConsumers.Remove(consumer);
LogDebugMessage($"Override consumer unregistered: {consumer}");
Logging.Debug($"Override consumer unregistered: {consumer}");
}
/// <summary>
@@ -396,7 +391,7 @@ namespace Input
{
_activeHoldConsumer = null;
_overrideConsumers.Clear();
LogDebugMessage("All override consumers cleared.");
Logging.Debug("All override consumers cleared.");
}
/// <summary>
@@ -409,17 +404,9 @@ namespace Input
// Get the topmost override consumer (last registered)
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);
return true;
}
private void LogDebugMessage(string message)
{
if (_logVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[InputManager] {message}");
}
}
}
}

View File

@@ -3,7 +3,6 @@ using Pathfinding;
using AppleHills.Core.Settings;
using Core;
using Core.Lifecycle;
using Core.SaveLoad;
namespace Input
{
@@ -71,9 +70,8 @@ namespace Input
public override bool AutoRegisterForSave => true;
// Scene-specific SaveId - each level has its own player state
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>();
artTransform = transform.Find("CharacterArt");
@@ -103,7 +101,7 @@ namespace Input
public void OnTap(Vector2 worldPosition)
{
InterruptMoveTo();
LogDebugMessage($"OnTap at {worldPosition}");
Logging.Debug($"OnTap at {worldPosition}");
if (aiPath != null)
{
aiPath.enabled = true;
@@ -122,7 +120,7 @@ namespace Input
public void OnHoldStart(Vector2 worldPosition)
{
InterruptMoveTo();
LogDebugMessage($"OnHoldStart at {worldPosition}");
Logging.Debug($"OnHoldStart at {worldPosition}");
lastHoldPosition = worldPosition;
isHolding = true;
if (_settings.DefaultHoldMovementMode == HoldMovementMode.Pathfinding &&
@@ -159,7 +157,7 @@ namespace Input
/// </summary>
public void OnHoldEnd(Vector2 worldPosition)
{
LogDebugMessage($"OnHoldEnd at {worldPosition}");
Logging.Debug($"OnHoldEnd at {worldPosition}");
isHolding = false;
directMoveVelocity = Vector3.zero;
if (aiPath != null && _settings.DefaultHoldMovementMode ==
@@ -335,13 +333,13 @@ namespace Input
{
_isMoving = true;
OnMovementStarted?.Invoke();
LogDebugMessage("Movement started");
Logging.Debug("Movement started");
}
else if (!isCurrentlyMoving && _isMoving)
{
_isMoving = false;
OnMovementStopped?.Invoke();
LogDebugMessage("Movement stopped");
Logging.Debug("Movement stopped");
}
}
@@ -424,18 +422,10 @@ namespace Input
OnArrivedAtTarget?.Invoke();
}
}
private void LogDebugMessage(string message)
{
if (_logVerbosity <= LogVerbosity.Debug)
{
Logging.Debug($"[PlayerTouchController] {message}");
}
}
#region Save/Load Lifecycle Hooks
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
var saveData = new PlayerSaveData
{
@@ -445,7 +435,7 @@ namespace Input
return JsonUtility.ToJson(saveData);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{

View File

@@ -41,9 +41,6 @@ namespace Interactions
// Action component system
private List<InteractionActionBase> _registeredActions = new List<InteractionActionBase>();
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 100; // Gameplay base classes
/// <summary>
/// Register an action component with this interactable

View File

@@ -68,8 +68,6 @@ namespace Interactions
public event Action<PickupItemData, PickupItemData> OnIncorrectItemSlotted;
public UnityEvent onForbiddenItemSlotted;
// Native C# event alternative for code-only subscribers
public event Action<PickupItemData, PickupItemData> OnForbiddenItemSlotted;
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
if (iconRenderer == null)
@@ -289,10 +287,8 @@ namespace Interactions
ItemManager.Instance?.RegisterItemSlot(this);
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Unregister from slot manager
ItemManager.Instance?.UnregisterItemSlot(this);
}

View File

@@ -32,9 +32,9 @@ namespace Interactions
public event Action<PickupItemData> OnItemPickedUp;
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)
iconRenderer = GetComponent<SpriteRenderer>();
@@ -44,15 +44,14 @@ namespace Interactions
// Always register with ItemManager, even if picked up
// 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);
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Unregister from ItemManager
ItemManager.Instance?.UnregisterPickup(this);
}

View File

@@ -24,7 +24,7 @@ namespace Interactions
#region Save/Load Lifecycle Hooks
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
object stateData = GetSerializableState();
if (stateData == null)
@@ -35,7 +35,7 @@ namespace Interactions
return JsonUtility.ToJson(stateData);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{

View File

@@ -21,9 +21,9 @@ namespace Levels
/// <summary>
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
/// </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}");
@@ -36,12 +36,12 @@ namespace Levels
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}");
}

View File

@@ -35,21 +35,16 @@ namespace Levels
[SerializeField] private bool startUnlocked = false;
private SpriteRenderer iconRenderer;
// Settings reference
private IInteractionSettings interactionSettings;
private bool switchActive = true;
private bool isUnlocked;
/// <summary>
/// Unity Awake callback. Sets up icon, interactable, and event handlers.
/// </summary>
protected override void Awake()
internal override void OnManagedAwake()
{
base.Awake();
switchActive = true;
base.OnManagedAwake();
if (iconRenderer == null)
iconRenderer = GetComponent<SpriteRenderer>();
@@ -64,10 +59,9 @@ namespace Levels
ApplySwitchData();
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
base.OnManagedAwake();
base.OnManagedStart();
// If startUnlocked is true, always start active
if (startUnlocked)
{
@@ -86,10 +80,8 @@ namespace Levels
}
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
if (PuzzleManager.Instance != null)
{
PuzzleManager.Instance.OnAllPuzzlesComplete -= HandleAllPuzzlesComplete;
@@ -144,15 +136,7 @@ namespace Levels
{
return base.CanBeClicked() && isUnlocked;
}
/// <summary>
/// Setup: Prevent re-entry while interaction is in progress.
/// </summary>
protected override void OnInteractionStarted()
{
switchActive = false;
}
/// <summary>
/// Main interaction logic: Spawn menu and switch input mode.
/// </summary>
@@ -203,7 +187,7 @@ namespace Levels
private void OnMenuCancel()
{
switchActive = true; // Allow interaction again if cancelled
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
}

View File

@@ -13,7 +13,6 @@ using UI.Core;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Playables;
using Svg;
namespace Minigames.DivingForPictures
{
@@ -104,13 +103,10 @@ namespace Minigames.DivingForPictures
public static DivingGameManager Instance => _instance;
public override int ManagedAwakePriority => 190;
public override bool AutoRegisterPausable => true; // Automatic GameManager registration
protected override void Awake()
internal override void OnManagedAwake()
{
base.Awake();
if (_instance == null)
{
_instance = this;
@@ -121,7 +117,7 @@ namespace Minigames.DivingForPictures
}
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
_settings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
_currentSpawnProbability = _settings?.BaseSpawnProbability ?? 0.2f;
@@ -132,7 +128,7 @@ namespace Minigames.DivingForPictures
Logging.Debug("[DivingGameManager] Initialized");
}
protected override void OnSceneReady()
internal override void OnSceneReady()
{
InitializeGame();
@@ -164,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
PlayerCollisionBehavior.OnDamageTaken -= OnPlayerDamageTaken;
OnMonsterSpawned -= DoMonsterSpawned;

View File

@@ -106,9 +106,7 @@ public class FollowerController : ManagedBehaviour
private bool _hasRestoredHeldItem; // Track if held item restoration completed
private string _expectedHeldItemSaveId; // Expected saveId during restoration
public override int ManagedAwakePriority => 110; // Follower after player
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
_aiPath = GetComponent<AIPath>();
// Find art prefab and animator
@@ -129,7 +127,7 @@ public class FollowerController : ManagedBehaviour
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
}
protected override void OnSceneReady()
internal override void OnSceneReady()
{
// Find player reference when scene is ready (called for every scene load)
FindPlayerReference();
@@ -727,7 +725,7 @@ public class FollowerController : ManagedBehaviour
#region Save/Load Lifecycle Hooks
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
var saveData = new FollowerSaveData
{
@@ -754,7 +752,7 @@ public class FollowerController : ManagedBehaviour
return JsonUtility.ToJson(saveData);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{
@@ -914,7 +912,7 @@ public class FollowerController : ManagedBehaviour
/// </summary>
public static FollowerController FindInstance()
{
return FindObjectOfType<FollowerController>();
return FindFirstObjectByType<FollowerController>();
}
#endregion Save/Load Lifecycle Hooks

View File

@@ -16,7 +16,7 @@ namespace PuzzleS
// Save system configuration
public override bool AutoRegisterForSave => true;
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// Initialize after all managers are ready
}
@@ -28,7 +28,7 @@ namespace PuzzleS
#region Save/Load Lifecycle Hooks
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
// Save scene-specific progress
var state = new BirdGameState
@@ -38,7 +38,7 @@ namespace PuzzleS
return JsonUtility.ToJson(state);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (string.IsNullOrEmpty(serializedData))
{

View File

@@ -32,7 +32,7 @@ namespace PuzzleS
// Enum for tracking proximity state (simplified to just Close and Far)
public enum ProximityState { Close, Far }
protected override void Awake()
internal override void OnManagedAwake()
{
_interactable = GetComponent<InteractableBase>();
@@ -56,14 +56,10 @@ namespace PuzzleS
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
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)
{
PuzzleManager.Instance.UnregisterStepBehaviour(this);

View File

@@ -93,18 +93,14 @@ namespace PuzzleS
// Track pending unlocks for steps that were unlocked before their behavior registered
private HashSet<string> _pendingUnlocks = new HashSet<string>();
public override int ManagedAwakePriority => 80; // Puzzle systems
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// Initialize settings reference
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
@@ -140,10 +136,8 @@ namespace PuzzleS
LoadPuzzlesForScene(sceneName);
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Unsubscribe from SceneManagerService events
if (SceneManagerService.Instance != null)
{
@@ -591,7 +585,7 @@ namespace PuzzleS
#region Save/Load Lifecycle Hooks
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
if (_currentLevelData == null)
{
@@ -611,7 +605,7 @@ namespace PuzzleS
return json;
}
protected override void OnSceneRestoreRequested(string data)
internal override void OnSceneRestoreRequested(string data)
{
Logging.Debug("[XAXA] PuzzleManager loading with data: " + data);

View File

@@ -20,15 +20,13 @@ public class AppleAudioSource : ManagedBehaviour
public int sourcePriority;
protected override void Awake()
internal override void OnManagedAwake()
{
base.Awake();
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);
_audioMixer = AudioManager.Instance.audioMixer;
InitializeAudioSource();

View File

@@ -40,19 +40,15 @@ public class AudioManager : ManagedBehaviour, IPausable
/// </summary>
public static AudioManager Instance => _instance;
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 30; // Audio infrastructure
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 so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
// Initialize lists if they were not set in inspector
criticalVOSources = criticalVOSources ?? new List<AppleAudioSource>();
@@ -72,7 +68,7 @@ public class AudioManager : ManagedBehaviour, IPausable
}
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

View File

@@ -23,7 +23,7 @@ public class BushAudioController : ManagedBehaviour
// 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.AudioStopped += PlayBirdCounter;

View File

@@ -26,7 +26,7 @@ public class LevelAudioObject : ManagedBehaviour
public override bool AutoRegisterForSave => isOneTime; // Only save if one-time audio
protected override string OnSceneSaveRequested()
internal override string OnSceneSaveRequested()
{
if (!isOneTime)
return null; // No need to save if not one-time
@@ -39,7 +39,7 @@ public class LevelAudioObject : ManagedBehaviour
return JsonUtility.ToJson(saveData);
}
protected override void OnSceneRestoreRequested(string serializedData)
internal override void OnSceneRestoreRequested(string serializedData)
{
if (!isOneTime || string.IsNullOrEmpty(serializedData))
return;
@@ -55,7 +55,7 @@ public class LevelAudioObject : ManagedBehaviour
}
}
protected override void OnSceneRestoreCompleted()
internal override void OnSceneRestoreCompleted()
{
if (isOneTime && !_hasPlayed)
{

View File

@@ -10,7 +10,7 @@ public class PulverAudioController : ManagedBehaviour
private FollowerController followerController;
public ItemManager itemManager;
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
followerController = GetComponent<FollowerController>();
followerController.PulverIsCombining.AddListener(PulverIsCombining);

View File

@@ -1,7 +1,6 @@
using System;
using UnityEngine;
using UnityEngine.UI;
using SkiaSharp.Unity;
using Input;
using AppleHills.Core;
using UI.Core;
@@ -11,8 +10,6 @@ using UI;
public class AppSwitcher : UIPage
{
public GameObject rainbowIn;
public GameObject rainbowOut;
public GameObject gameLayoutContainer;
public GameObject exitButton;
public RectMask2D rectMask;
@@ -20,26 +17,21 @@ public class AppSwitcher : UIPage
[Header("Slide Animation Settings")]
public float slideDuration = 0.5f;
private SkottiePlayerV2 rainbowInPlayer;
private SkottiePlayerV2 rainbowOutPlayer;
private TweenBase slideInTween;
private TweenBase slideOutTween;
protected override void OnManagedAwake()
internal override void OnManagedAwake()
{
base.OnManagedAwake();
PageName = "AppSwitcher";
rainbowInPlayer = rainbowIn.GetComponent<SkottiePlayerV2>();
rainbowOutPlayer = rainbowOut.GetComponent<SkottiePlayerV2>();
if (rectMask == null)
{
rectMask = GetComponent<RectMask2D>();
}
// Initially hide both
rainbowIn.SetActive(true);
// Initially hide
exitButton.SetActive(false);
}
@@ -49,27 +41,19 @@ public class AppSwitcher : UIPage
gameLayoutContainer.SetActive(true);
// Hide rainbow out, show rainbow in
rainbowOut.SetActive(false);
// Play animation on rainbow in without resetting state
rainbowInPlayer.PlayAnimation(true);
// Slide in animation - tween padding.left from 1700 to 0
slideInTween = TweenPaddingLeft(1700f, 0f, Tween.EaseOut, () =>
{
onComplete?.Invoke();
rainbowOut.SetActive(true);
exitButton.SetActive(true);
});
}
protected override void DoTransitionOut(Action onComplete)
{
rainbowIn.SetActive(false);
// Play animation on rainbow out with resetting state
rainbowOutPlayer.PlayAnimation(true);
// Hide the exit button
exitButton.SetActive(false);
@@ -77,7 +61,6 @@ public class AppSwitcher : UIPage
// Slide out animation - tween padding.left from 0 to 1700
slideOutTween = TweenPaddingLeft(0f, 1700f, Tween.EaseIn, () => {
gameLayoutContainer.SetActive(false);
rainbowIn.SetActive(true);
onComplete?.Invoke();
InputManager.Instance.SetInputMode(InputMode.GameAndUI);
});
@@ -110,10 +93,8 @@ public class AppSwitcher : UIPage
);
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Clean up tweens
slideInTween?.Stop();
slideOutTween?.Stop();

View File

@@ -44,10 +44,8 @@ namespace UI.CardSystem
private List<AlbumCardPlacementDraggable> _activeCards = new List<AlbumCardPlacementDraggable>();
private const int MAX_VISIBLE_CARDS = 3;
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
base.OnManagedAwake();
// Discover zone tabs from container
DiscoverZoneTabs();
@@ -151,7 +149,7 @@ namespace UI.CardSystem
}
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
// Unsubscribe from CardSystemManager
if (CardSystemManager.Instance != null)
@@ -183,9 +181,6 @@ namespace UI.CardSystem
// Clean up active cards
CleanupActiveCards();
// Call base implementation
base.OnDestroy();
}
private void OnExitButtonClicked()

View File

@@ -40,10 +40,8 @@ namespace UI.CardSystem
private TweenBase _activeTween;
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
base.OnManagedAwake();
// Store original scale for pulse animation
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
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
}
// Call base implementation
base.OnDestroy();
}
/// <summary>

View File

@@ -34,7 +34,6 @@ namespace UI.CardSystem
[SerializeField] private float cardSpacing = 150f;
[Header("Settings")]
[SerializeField] private float cardRevealDelay = 0.5f;
[SerializeField] private float boosterDisappearDuration = 0.5f;
[SerializeField] private CinemachineImpulseSource impulseSource;
[SerializeField] private ParticleSystem openingParticleSystem;
@@ -77,7 +76,7 @@ namespace UI.CardSystem
gameObject.SetActive(false);
}
private void OnDestroy()
internal override void OnManagedDestroy()
{
// Unsubscribe from dismiss button
if (_dismissButton != null)

View File

@@ -73,7 +73,7 @@ namespace UI.CardSystem
albumCard.SetParentSlot(this);
// Register with AlbumViewPage for enlarge/shrink handling
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.RegisterAlbumCard(albumCard);
@@ -208,7 +208,7 @@ namespace UI.CardSystem
}
// Register with AlbumViewPage for enlarge/shrink handling
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.RegisterAlbumCard(albumCard);
@@ -284,7 +284,7 @@ namespace UI.CardSystem
previewCardDisplay.transform.localScale = _previewOriginalScale;
// Get AlbumViewPage to show backdrop and reparent
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.ShowSlotPreview(this, previewCardDisplay.transform);
@@ -311,7 +311,7 @@ namespace UI.CardSystem
previewCardDisplay.SetPreviewMode(false, null);
// Get AlbumViewPage to hide backdrop
AlbumViewPage albumPage = FindObjectOfType<AlbumViewPage>();
AlbumViewPage albumPage = FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.HideSlotPreview(this, previewCardDisplay.transform, () =>

View File

@@ -307,7 +307,7 @@ namespace UI.CardSystem.DragDrop
}
#endregion
protected override void OnDestroy()
{
base.OnDestroy();

View File

@@ -106,7 +106,7 @@ namespace UI.CardSystem.DragDrop
base.OnDragEndedVisual();
// Card-specific visual effects when dragging ends
}
protected override void OnDestroy()
{
base.OnDestroy();

View File

@@ -41,7 +41,6 @@ namespace UI.CardSystem
// State
private bool _isFlipped = false;
private bool _isFlipping = false;
private bool _isHovering = false;
private TweenBase _idleHoverTween;
private CardData _cardData;
private Vector2 _originalPosition; // Track original spawn position
@@ -242,8 +241,6 @@ namespace UI.CardSystem
if (_isFlipped || _isFlipping)
return;
_isHovering = true;
// Scale up slightly on hover
Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack);
}
@@ -253,8 +250,6 @@ namespace UI.CardSystem
if (_isFlipped || _isFlipping)
return;
_isHovering = false;
// Scale back to normal
Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack);
}

View File

@@ -27,7 +27,6 @@ namespace UI.CardSystem
[Header("Animation Settings")]
[SerializeField] private float hoverAmount = 20f;
[SerializeField] private float hoverDuration = 1.5f;
[SerializeField] private float glowPulseMin = 0.9f;
[SerializeField] private float glowPulseMax = 1.1f;
[SerializeField] private float glowPulseDuration = 1.2f;

View File

@@ -15,9 +15,6 @@ namespace UI.Core
[Header("Page Settings")]
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
public event Action OnTransitionInStarted;
public event Action OnTransitionInCompleted;

View File

@@ -37,25 +37,19 @@ namespace UI.Core
private PlayerInput _playerInput;
private InputAction _cancelAction;
public override int ManagedAwakePriority => 50; // UI infrastructure
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
Logging.Debug("[UIPageController] Initialized");
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Clean up cached instances
foreach (var cachedPage in _prefabInstanceCache.Values)
{

View File

@@ -346,7 +346,7 @@ namespace UI.DragAndDrop.Core
protected virtual void FindAndSnapToSlot()
{
SlotContainer[] containers = FindObjectsOfType<SlotContainer>();
SlotContainer[] containers = FindObjectsByType<SlotContainer>(FindObjectsSortMode.None);
DraggableSlot closestSlot = null;
float closestDistance = float.MaxValue;

View File

@@ -52,15 +52,10 @@ namespace UI
/// Singleton instance of the LoadingScreenController. No longer creates an instance if one doesn't exist.
/// </summary>
public static LoadingScreenController Instance => _instance;
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 45; // UI infrastructure, before UIPageController
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
// Set up container reference early
@@ -74,7 +69,7 @@ namespace UI
}
}
protected override void OnManagedAwake()
internal override void OnManagedStart()
{
Logging.Debug("[LoadingScreenController] Initialized");
}

View File

@@ -27,15 +27,10 @@ namespace UI
[SerializeField] private UnityEngine.UI.Button devOptionsButton;
[SerializeField] private GameObject mainOptionsContainer;
[SerializeField] private GameObject devOptionsContainer;
// After UIPageController (50)
public override int ManagedAwakePriority => 55;
private new void Awake()
internal override void OnManagedAwake()
{
base.Awake(); // CRITICAL: Register with LifecycleManager!
// Set instance immediately so it's available before OnManagedAwake() is called
// Set instance immediately (early initialization)
_instance = this;
// Ensure we have a CanvasGroup for transitions
@@ -51,9 +46,9 @@ namespace UI
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
if (SceneManagerService.Instance != null)
{
@@ -73,15 +68,13 @@ namespace UI
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
}
protected override void OnDestroy()
internal override void OnManagedDestroy()
{
base.OnDestroy();
// Unsubscribe when destroyed
if (SceneManagerService.Instance != null)
{
@@ -421,6 +414,7 @@ namespace UI
{
SaveLoadManager.Instance.currentSaveData.participantStates.Clear();
SaveLoadManager.Instance.currentSaveData.unlockedMinigames.Clear();
SaveLoadManager.Instance.currentSaveData.playedDivingTutorial = false;
Logging.Debug("[PauseMenu] Cleared all save data from memory");
}

Some files were not shown because too many files have changed in this diff Show More