Compare commits

..

7 Commits

Author SHA1 Message Date
Michal Pikulski
363eb99a91 Add docs to the logger 2025-11-11 09:39:57 +01:00
Michal Pikulski
4dfe6202fd Update logging calls to new system 2025-11-11 09:00:05 +01:00
Michal Pikulski
f8805dabe7 Custom logger 2025-11-11 08:44:15 +01:00
Michal Pikulski
961da5e729 Fix deprecation and other warnings 2025-11-10 23:18:01 +01:00
Michal Pikulski
a049c6a750 Update methods to be internal, remove invocation bloat 2025-11-10 21:59:47 +01:00
Michal Pikulski
01caca1878 Add docs and clear the zero-width-backspace-formatting 2025-11-10 15:56:30 +01:00
Michal Pikulski
7565b189b9 Add distinction between managed awake and managed start 2025-11-10 15:52:53 +01:00
104 changed files with 1648 additions and 5728 deletions

3
.gitignore vendored
View File

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

View File

@@ -1,8 +0,0 @@
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: AnimSM_Trafalgar
m_Name: trafalgar_0002
serializedVersion: 5
m_AnimatorParameters:
- m_Name: Speed

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 KiB

View File

@@ -1,570 +0,0 @@
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

@@ -1,33 +0,0 @@
#
# 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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 KiB

View File

@@ -1,455 +0,0 @@
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

@@ -1,28 +0,0 @@
#
# 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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 KiB

View File

@@ -1,570 +0,0 @@
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

@@ -1,33 +0,0 @@
#
# 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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -1,202 +0,0 @@
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

@@ -1,17 +0,0 @@
#
# 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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -1,156 +0,0 @@
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

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

View File

@@ -1,748 +0,0 @@
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

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

View File

@@ -560,15 +560,6 @@ 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);
@@ -879,121 +870,6 @@ 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,9 +1,5 @@
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
@@ -34,10 +30,6 @@ 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
@@ -96,9 +88,5 @@ normalmapFileNames:
-
-
-
-
-
-
-
- Assets/External/Plugins/codeandweb.com/Example/SpriteSheet/sprites_n.png
enableDebugOutput: 0

View File

@@ -313,22 +313,14 @@ 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: 4.97
value: 3.8700001
objectReference: {fileID: 0}
- target: {fileID: 6109476811019011833, guid: 361ccc9ef82acef4784b24b72013d971, type: 3}
propertyPath: m_Size.y
value: 7.53
value: 7.55122
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
@@ -337,10 +329,6 @@ 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,9 +725,10 @@ 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
@@ -762,6 +763,53 @@ 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
@@ -802,7 +850,7 @@ MonoBehaviour:
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 0}
m_TargetGraphic: {fileID: 8081783206361873868}
m_OnClick:
m_PersistentCalls:
m_Calls:
@@ -832,36 +880,6 @@ 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,7 +260,6 @@ 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,6 +148,8 @@ 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}
@@ -400,8 +402,8 @@ MonoBehaviour:
m_EditorClassIdentifier: '::'
PageName:
transitionDuration: 0.3
rainbowIn: {fileID: 0}
rainbowOut: {fileID: 0}
rainbowIn: {fileID: 5382650426034128680}
rainbowOut: {fileID: 3983328028282460839}
gameLayoutContainer: {fileID: 904161782565348054}
exitButton: {fileID: 8427602740714176801}
rectMask: {fileID: 7425566603516801919}
@@ -526,6 +528,99 @@ 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
@@ -841,6 +936,99 @@ 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

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

View File

@@ -30,6 +30,8 @@ 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;
internal override void OnManagedAwake()
{
@@ -81,8 +83,10 @@ namespace Bootstrap
Invoke(nameof(StartLoadingMainMenu), minDelayAfterBoot);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Manual cleanup for events
if (initialLoadingScreen != null)
{

View File

@@ -37,6 +37,8 @@ namespace Cinematics
public PlayableDirector playableDirector;
public override int ManagedAwakePriority => 170; // Cinematic systems
internal override void OnManagedAwake()
{
// Set instance immediately (early initialization)

View File

@@ -15,6 +15,8 @@ namespace Cinematics
private float _holdStartTime;
private bool _isHolding;
private bool _skipPerformed;
public override int ManagedAwakePriority => 180; // Cinematic UI
internal override void OnManagedStart()
{
@@ -30,8 +32,10 @@ namespace Cinematics
Logging.Debug("[SkipCinematic] Initialized");
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Clean up subscriptions
UnsubscribeFromCinematicsEvents();
}

View File

@@ -34,6 +34,8 @@ namespace Core
public event Action OnGamePaused;
public event Action OnGameResumed;
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 10; // Core infrastructure - runs early
internal override void OnManagedAwake()
{

View File

@@ -47,6 +47,8 @@ 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
internal override void OnManagedAwake()
{
@@ -65,8 +67,10 @@ namespace Core
ClearAllRegistrations();
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Ensure we clean up any subscriptions from registered items when the manager is destroyed
ClearAllRegistrations();
}

View File

@@ -6,19 +6,12 @@
/// </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>
ManagedStart,
ManagedAwake,
/// <summary>
/// Called before a scene is unloaded.

View File

@@ -59,11 +59,11 @@ namespace Core.Lifecycle
#region State Flags
private bool isBootComplete;
private bool isBootComplete = false;
private string currentSceneReady = "";
// Scene loading state tracking
private bool isLoadingScene;
private bool isLoadingScene = false;
private string sceneBeingLoaded = "";
private List<ManagedBehaviour> pendingSceneComponents = new List<ManagedBehaviour>();
@@ -120,13 +120,17 @@ namespace Core.Lifecycle
// Track which scene this component belongs to
componentScenes[component] = sceneName;
// 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);
// ALWAYS add to managedAwakeList - this is the master list used for save/load
InsertSorted(managedAwakeList, component, component.ManagedAwakePriority);
// 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);
// Call OnManagedAwake immediately after registration (early initialization hook)
try
{
component.OnManagedAwake();
@@ -142,7 +146,7 @@ namespace Core.Lifecycle
// Check if we're currently loading a scene
if (isLoadingScene && sceneName == sceneBeingLoaded)
{
// Batch this component - will be processed when scene load completes
// Batch this component - will be processed in priority order when scene load completes
pendingSceneComponents.Add(component);
LogDebug($"Batched component for scene load: {component.gameObject.name} (Scene: {sceneName})");
}
@@ -278,7 +282,10 @@ namespace Core.Lifecycle
LogDebug($"Processing {pendingSceneComponents.Count} batched components for scene: {sceneBeingLoaded}");
// Call OnManagedStart in registration order
// Sort by ManagedAwake priority (lower values first)
pendingSceneComponents.Sort((a, b) => a.ManagedAwakePriority.CompareTo(b.ManagedAwakePriority));
// Call OnManagedStart in priority order
foreach (var component in pendingSceneComponents)
{
if (component == null) continue;
@@ -287,7 +294,7 @@ namespace Core.Lifecycle
{
component.OnManagedStart();
HandleAutoRegistrations(component);
LogDebug($"Processed batched component: {component.gameObject.name}");
LogDebug($"Processed batched component: {component.gameObject.name} (Priority: {component.ManagedAwakePriority})");
}
catch (Exception ex)
{
@@ -302,7 +309,7 @@ namespace Core.Lifecycle
}
/// <summary>
/// Broadcast OnSceneUnloading to components in the specified scene.
/// Broadcast OnSceneUnloading to components in the specified scene (reverse priority order).
/// </summary>
public void BroadcastSceneUnloading(string sceneName)
{
@@ -329,8 +336,8 @@ namespace Core.Lifecycle
}
/// <summary>
/// Broadcast OnSceneReady to components in the specified scene.
/// Processes batched components first, then calls OnSceneReady on all components in that scene.
/// Broadcast OnSceneReady to components in the specified scene (priority order).
/// If scene loading mode is active, processes batched components first.
/// </summary>
public void BroadcastSceneReady(string sceneName)
{
@@ -614,6 +621,42 @@ 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

@@ -8,6 +8,46 @@ namespace Core.Lifecycle
/// </summary>
public abstract class ManagedBehaviour : MonoBehaviour
{
#region Priority Properties
/// <summary>
/// Priority for OnManagedStart (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>
@@ -27,19 +67,14 @@ 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
{
if (_cachedSaveId == null)
{
string sceneName = gameObject.scene.IsValid() ? gameObject.scene.name : "UnknownScene";
string componentType = GetType().Name;
_cachedSaveId = $"{sceneName}/{gameObject.name}/{componentType}";
}
return _cachedSaveId;
string sceneName = gameObject.scene.IsValid() ? gameObject.scene.name : "UnknownScene";
string componentType = GetType().Name;
return $"{sceneName}/{gameObject.name}/{componentType}";
}
}
@@ -48,7 +83,6 @@ namespace Core.Lifecycle
#region Private Fields
private bool _isRegistered;
private string _cachedSaveId;
#endregion
@@ -73,16 +107,13 @@ namespace Core.Lifecycle
/// <summary>
/// Unity OnDestroy - automatically unregisters and cleans up.
/// SEALED: Cannot be overridden. Use OnManagedDestroy() for custom cleanup logic.
/// IMPORTANT: Derived classes that override OnDestroy MUST call base.OnDestroy()
/// </summary>
private void OnDestroy()
protected virtual void OnDestroy()
{
if (!_isRegistered)
return;
// Call managed destroy hook
OnManagedDestroy();
// Unregister from LifecycleManager
if (LifecycleManager.Instance != null)
{
@@ -118,7 +149,7 @@ namespace Core.Lifecycle
/// <summary>
/// Called once per component after bootstrap completes.
/// GUARANTEE: Bootstrap resources are available, all managers are initialized.
/// For boot-time components: Called during LifecycleManager.BroadcastManagedStart (registration order).
/// For boot-time components: Called during LifecycleManager.BroadcastManagedStart (priority ordered).
/// For late-registered components: Called immediately upon registration (bootstrap already complete).
/// Use for initialization that depends on other systems.
/// NOTE: Internal visibility allows LifecycleManager to call directly. Override in derived classes.
@@ -130,6 +161,7 @@ namespace Core.Lifecycle
/// <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>
@@ -140,6 +172,7 @@ namespace Core.Lifecycle
/// <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>
@@ -279,6 +312,7 @@ namespace Core.Lifecycle
/// <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.

View File

@@ -24,6 +24,9 @@ namespace AppleHills.Core
#endregion Singleton Setup
// Very early initialization - QuickAccess should be available immediately
public override int ManagedAwakePriority => 5;
#region Manager Instances
// Core Managers

View File

@@ -43,6 +43,8 @@ namespace Core.SaveLoad
public event Action<string> OnLoadCompleted;
public event Action OnParticipantStatesRestored;
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 20; // After GameManager and SceneManagerService
internal override void OnManagedAwake()
{
@@ -93,8 +95,10 @@ namespace Core.SaveLoad
// ...existing code...
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy(); // Important: call base to unregister from LifecycleManager
if (_instance == this)
{
_instance = null;

View File

@@ -42,8 +42,10 @@ namespace Core
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
if (_director != null)
{
_director.stopped -= OnDirectorStopped;

View File

@@ -44,6 +44,8 @@ namespace Core
private LogVerbosity _logVerbosity = LogVerbosity.Debug;
private const string BootstrapSceneName = "BootstrapScene";
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 15; // Core infrastructure, after GameManager
internal override void OnManagedAwake()
{
@@ -367,7 +369,7 @@ namespace Core
await LoadSceneAsync(newSceneName, progress);
CurrentGameplayScene = newSceneName;
// PHASE 10: Broadcast scene ready - processes batched components, then calls OnSceneReady
// PHASE 10: Broadcast scene ready - processes batched components in priority order, then calls OnSceneReady
Logging.Debug($"Broadcasting OnSceneReady for: {newSceneName}");
LifecycleManager.Instance?.BroadcastSceneReady(newSceneName);

View File

@@ -18,6 +18,9 @@ namespace Core
public GameObject orientationPromptPrefab;
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
// ManagedBehaviour configuration
public override int ManagedAwakePriority => 70; // Platform-specific utility
internal override void OnManagedAwake()
{
// Set instance immediately (early initialization)
@@ -100,13 +103,15 @@ namespace Core
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
// Unsubscribe from events to prevent memory leaks
if (SceneManagerService.Instance != null)
{
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
}
base.OnDestroy(); // Important: call base
}
/// <summary>

View File

@@ -43,6 +43,8 @@ namespace Data.CardSystem
public event Action<int> OnBoosterCountChanged;
public event Action<CardData> OnPendingCardAdded;
public event Action<CardData> OnCardPlacedInAlbum;
public override int ManagedAwakePriority => 60; // Data systems
internal override void OnManagedAwake()
{

View File

@@ -32,6 +32,9 @@ namespace Dialogue
public bool IsCompleted { get; private set; }
public string CurrentSpeakerName => dialogueGraph?.speakerName;
public override int ManagedAwakePriority => 150; // Dialogue systems
internal override void OnManagedStart()
{
// Get required components
@@ -181,8 +184,10 @@ namespace Dialogue
return null;
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unregister from events
if (PuzzleManager.Instance != null)
PuzzleManager.Instance.OnStepCompleted -= OnAnyPuzzleStepCompleted;

View File

@@ -49,6 +49,8 @@ namespace Input
private ITouchInputConsumer defaultConsumer;
private bool isHoldActive;
private LogVerbosity _logVerbosity = LogVerbosity.Warning;
public override int ManagedAwakePriority => 25; // Input infrastructure
internal override void OnManagedAwake()
{
@@ -104,7 +106,7 @@ namespace Input
SwitchInputOnSceneLoaded(sceneName);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
// Unsubscribe from SceneManagerService events
if (SceneManagerService.Instance != null)
@@ -112,6 +114,7 @@ namespace Input
SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
}
base.OnDestroy();
// Input action cleanup happens automatically
}

View File

@@ -70,6 +70,7 @@ 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
internal override void OnManagedStart()
{

View File

@@ -41,6 +41,9 @@ 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

@@ -287,8 +287,10 @@ namespace Interactions
ItemManager.Instance?.RegisterItemSlot(this);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unregister from slot manager
ItemManager.Instance?.UnregisterItemSlot(this);
}

View File

@@ -50,8 +50,10 @@ namespace Interactions
ItemManager.Instance?.RegisterPickup(this);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unregister from ItemManager
ItemManager.Instance?.UnregisterPickup(this);
}

View File

@@ -80,8 +80,10 @@ namespace Levels
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
if (PuzzleManager.Instance != null)
{
PuzzleManager.Instance.OnAllPuzzlesComplete -= HandleAllPuzzlesComplete;

View File

@@ -13,6 +13,7 @@ using UI.Core;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Playables;
using Svg;
namespace Minigames.DivingForPictures
{
@@ -103,6 +104,7 @@ namespace Minigames.DivingForPictures
public static DivingGameManager Instance => _instance;
public override int ManagedAwakePriority => 190;
public override bool AutoRegisterPausable => true; // Automatic GameManager registration
internal override void OnManagedAwake()
@@ -160,8 +162,10 @@ namespace Minigames.DivingForPictures
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy(); // Handles auto-unregister from GameManager
// Unsubscribe from events when the manager is destroyed
PlayerCollisionBehavior.OnDamageTaken -= OnPlayerDamageTaken;
OnMonsterSpawned -= DoMonsterSpawned;

View File

@@ -106,6 +106,8 @@ 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
internal override void OnManagedStart()
{
_aiPath = GetComponent<AIPath>();

View File

@@ -83,8 +83,10 @@ namespace PuzzleS
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
if (PuzzleManager.Instance != null && stepData != null)
{
PuzzleManager.Instance.UnregisterStepBehaviour(this);

View File

@@ -93,6 +93,8 @@ 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
internal override void OnManagedAwake()
{
@@ -136,8 +138,10 @@ namespace PuzzleS
LoadPuzzlesForScene(sceneName);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unsubscribe from SceneManagerService events
if (SceneManagerService.Instance != null)
{

View File

@@ -40,6 +40,8 @@ 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
internal override void OnManagedAwake()

View File

@@ -1,6 +1,7 @@
using System;
using UnityEngine;
using UnityEngine.UI;
using SkiaSharp.Unity;
using Input;
using AppleHills.Core;
using UI.Core;
@@ -10,6 +11,8 @@ using UI;
public class AppSwitcher : UIPage
{
public GameObject rainbowIn;
public GameObject rainbowOut;
public GameObject gameLayoutContainer;
public GameObject exitButton;
public RectMask2D rectMask;
@@ -17,6 +20,8 @@ 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;
@@ -25,13 +30,16 @@ public class AppSwitcher : UIPage
base.OnManagedAwake();
PageName = "AppSwitcher";
rainbowInPlayer = rainbowIn.GetComponent<SkottiePlayerV2>();
rainbowOutPlayer = rainbowOut.GetComponent<SkottiePlayerV2>();
if (rectMask == null)
{
rectMask = GetComponent<RectMask2D>();
}
// Initially hide
// Initially hide both
rainbowIn.SetActive(true);
exitButton.SetActive(false);
}
@@ -41,19 +49,27 @@ 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);
@@ -61,6 +77,7 @@ 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);
});
@@ -93,8 +110,10 @@ public class AppSwitcher : UIPage
);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Clean up tweens
slideInTween?.Stop();
slideOutTween?.Stop();

View File

@@ -149,7 +149,7 @@ namespace UI.CardSystem
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
// Unsubscribe from CardSystemManager
if (CardSystemManager.Instance != null)
@@ -181,6 +181,9 @@ namespace UI.CardSystem
// Clean up active cards
CleanupActiveCards();
// Call base implementation
base.OnDestroy();
}
private void OnExitButtonClicked()

View File

@@ -70,13 +70,16 @@ namespace UI.CardSystem
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
// Unsubscribe from CardSystemManager events to prevent memory leaks
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
}
// Call base implementation
base.OnDestroy();
}
/// <summary>

View File

@@ -76,8 +76,10 @@ namespace UI.CardSystem
gameObject.SetActive(false);
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unsubscribe from dismiss button
if (_dismissButton != null)
{

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

@@ -15,6 +15,9 @@ 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,6 +37,8 @@ namespace UI.Core
private PlayerInput _playerInput;
private InputAction _cancelAction;
public override int ManagedAwakePriority => 50; // UI infrastructure
internal override void OnManagedAwake()
{
// Set instance immediately (early initialization)
@@ -48,8 +50,10 @@ namespace UI.Core
Logging.Debug("[UIPageController] Initialized");
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Clean up cached instances
foreach (var cachedPage in _prefabInstanceCache.Values)
{

View File

@@ -52,6 +52,9 @@ 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
internal override void OnManagedAwake()
{

View File

@@ -27,6 +27,9 @@ namespace UI
[SerializeField] private UnityEngine.UI.Button devOptionsButton;
[SerializeField] private GameObject mainOptionsContainer;
[SerializeField] private GameObject devOptionsContainer;
// After UIPageController (50)
public override int ManagedAwakePriority => 55;
internal override void OnManagedAwake()
{
@@ -73,8 +76,10 @@ namespace UI
// This only fires once for DontDestroyOnLoad objects, so we handle scene loads in OnManagedAwake
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unsubscribe when destroyed
if (SceneManagerService.Instance != null)
{

View File

@@ -172,8 +172,10 @@ namespace UI
}
}
internal override void OnManagedDestroy()
protected override void OnDestroy()
{
base.OnDestroy();
// Unsubscribe from events
if (_uiPageController != null)
{

View File

@@ -30,6 +30,7 @@ namespace UI.Tutorial
private bool _canAcceptInput;
private Coroutine _waitLoopCoroutine;
public override int ManagedAwakePriority => 200; // Tutorial runs late, after other systems
internal override void OnManagedStart()
{

View File

@@ -1,5 +1,6 @@
{
"dependencies": {
"com.ammariqais.skiaforunity": "https://github.com/ammariqais/SkiaForUnity.git?path=SkiaUnity/Assets/SkiaSharp",
"com.coplaydev.unity-mcp": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity",
"com.moolt.packages.net": "git+https://github.com/Moolt/UnityAudioSourceEvents?path=Packages/AudioSourceEvents",
"com.unity.2d.sprite": "1.0.0",

View File

@@ -1,5 +1,14 @@
{
"dependencies": {
"com.ammariqais.skiaforunity": {
"version": "https://github.com/ammariqais/SkiaForUnity.git?path=SkiaUnity/Assets/SkiaSharp",
"depth": 0,
"source": "git",
"dependencies": {
"com.unity.nuget.newtonsoft-json": "3.2.0"
},
"hash": "11e82b71012bf8b4f68172080d6e4969d42120ef"
},
"com.coplaydev.unity-mcp": {
"version": "https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity",
"depth": 0,

View File

@@ -12,8 +12,8 @@ PlayerSettings:
targetDevice: 2
useOnDemandResources: 0
accelerometerFrequency: 60
companyName: DR
productName: "DR \xC6blerup"
companyName: DefaultCompany
productName: AppleHills
defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
@@ -142,7 +142,7 @@ PlayerSettings:
loadStoreDebugModeEnabled: 0
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
bundleVersion: 1.1
bundleVersion: 1.0.2
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
@@ -168,7 +168,7 @@ PlayerSettings:
Android: com.DefaultCompany.com.unity.template.mobile2D
Lumin: com.DefaultCompany.com.unity.template.mobile2D
Standalone: com.DefaultCompany.com.unity.template.mobile2D
iPhone: dk.dr.ramasjang.aeblerup
iPhone: com.DefaultCompany.com.unity.template.mobile2D
tvOS: com.DefaultCompany.com.unity.template.mobile2D
buildNumber:
Standalone: 0
@@ -403,31 +403,25 @@ PlayerSettings:
m_SubKind:
- m_BuildTarget: iPhone
m_Icons:
- m_Textures:
- {fileID: 2800000, guid: a8ae11f948f0cfa4791e954c315376ee, type: 3}
m_Width: 1024
m_Height: 1024
m_Kind: 4
m_SubKind: App Store
- m_Textures: []
m_Width: 60
m_Height: 60
m_Kind: 2
m_Width: 120
m_Height: 120
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 20
m_Height: 20
m_Kind: 2
m_Width: 40
m_Height: 40
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 87
@@ -455,25 +449,30 @@ PlayerSettings:
m_Kind: 1
m_SubKind: iPad
- m_Textures: []
m_Width: 120
m_Height: 120
m_Kind: 3
m_Width: 60
m_Height: 60
m_Kind: 2
m_SubKind: iPhone
- m_Textures: []
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPhone
- m_Textures: []
m_Width: 80
m_Height: 80
m_Kind: 3
m_SubKind: iPad
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 3
m_Kind: 2
m_SubKind: iPhone
- m_Textures: []
m_Width: 40
m_Height: 40
m_Kind: 2
m_SubKind: iPad
- m_Textures: []
m_Width: 20
m_Height: 20
m_Kind: 2
m_SubKind: iPad
- m_Textures: []
m_Width: 1024
m_Height: 1024
m_Kind: 4
m_SubKind: App Store
- m_Textures:
- {fileID: 2800000, guid: a8ae11f948f0cfa4791e954c315376ee, type: 3}
m_Width: 180
@@ -508,9 +507,6 @@ PlayerSettings:
- m_BuildTarget: Android
m_StaticBatching: 1
m_DynamicBatching: 0
- m_BuildTarget: iPhone
m_StaticBatching: 1
m_DynamicBatching: 0
m_BuildTargetShaderSettings: []
m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport
@@ -840,7 +836,7 @@ PlayerSettings:
webEnableSubmoduleStrippingCompatibility: 0
scriptingDefineSymbols:
Android: ENABLE_LOG
iPhone:
iPhone: __UNIFIED__;__IOS__
additionalCompilerArguments: {}
platformArchitecture: {}
scriptingBackend:

View File

@@ -1,573 +0,0 @@
# Interactables System - Code Reference
## Table of Contents
1. [Overview](#overview)
2. [Class Hierarchy](#class-hierarchy)
3. [InteractableBase - The Template Method](#interactablebase---the-template-method)
- [Interaction Flow](#interaction-flow)
- [Virtual Methods to Override](#virtual-methods-to-override)
4. [Creating Custom Interactables](#creating-custom-interactables)
- [Example 1: Simple Button (OneClickInteraction)](#example-1-simple-button-oneclickinteraction)
- [Example 2: Item Pickup](#example-2-item-pickup)
- [Example 3: Item Slot with Validation](#example-3-item-slot-with-validation)
5. [Character Movement](#character-movement)
6. [Action Component System](#action-component-system)
7. [Events System](#events-system)
8. [Save/Load System Integration](#saveload-system-integration)
9. [Integration with Puzzle System](#integration-with-puzzle-system)
10. [Advanced Patterns](#advanced-patterns)
---
## Overview
Simple, centrally orchestrated interaction system for player and follower characters.
### Core Concepts
- **Template Method Pattern**: `InteractableBase` defines the interaction flow; subclasses override specific steps
- **Action Component System**: Modular actions respond to interaction events independently
- **Async/Await Flow**: Character movement and timeline playback use async patterns
- **Save/Load Integration**: `SaveableInteractable` provides persistence for interaction state
---
## Class Hierarchy
```
ManagedBehaviour
└── InteractableBase
├── OneClickInteraction
└── SaveableInteractable
├── Pickup
└── ItemSlot
```
### Class Descriptions
- **InteractableBase** - Abstract base class that orchestrates the complete interaction flow using the Template Method pattern. Handles tap input, character movement, validation, and event dispatching for all interactables.
- **SaveableInteractable** - Extends InteractableBase with save/load capabilities, integrating with the ManagedBehaviour save system. Provides abstract methods for JSON serialization and deserialization of state.
- **OneClickInteraction** - Simplest concrete interactable that completes immediately when character arrives with no additional logic. All functionality comes from UnityEvents configured in the Inspector.
- **Pickup** - Represents items that can be picked up by the follower, handling item combination and state tracking. Integrates with ItemManager and supports bilateral restoration with ItemSlots.
- **ItemSlot** - Container that accepts specific items with validation for correct/incorrect/forbidden items. Manages item placement, swapping, and supports combination with special puzzle integration that allows swapping when locked.
---
## InteractableBase - The Template Method
### Interaction Flow
When a player taps an interactable, the following flow executes:
```csharp
OnTap() CanBeClicked() StartInteractionFlowAsync()
1. Find Characters (player, follower)
2. OnInteractionStarted() [Virtual Hook]
3. Fire interactionStarted events
4. MoveCharactersAsync()
5. OnInteractingCharacterArrived() [Virtual Hook]
6. Fire characterArrived events
7. ValidateInteraction()
8. DoInteraction() [Virtual Hook - OVERRIDE THIS]
9. OnInteractionFinished() [Virtual Hook]
10. Fire interactionComplete events
```
### Virtual Methods to Override
#### 1. `CanBeClicked()` - Pre-Interaction Validation
```csharp
protected virtual bool CanBeClicked()
{
if (!isActive) return false;
// Add custom checks here
return true;
}
```
**When to override:** Add high-level validation before interaction starts (cooldowns, prerequisites, etc.)
#### 2. `OnInteractionStarted()` - Setup Logic
```csharp
protected virtual void OnInteractionStarted()
{
// Called after characters found, before movement
// Setup animations, sound effects, etc.
}
```
**When to override:** Perform setup that needs to happen before character movement
#### 3. `DoInteraction()` - Main Logic ⭐ **OVERRIDE THIS**
```csharp
protected override bool DoInteraction()
{
// Your interaction logic here
return true; // Return true for success, false for failure
}
```
**When to override:** **Always override this** - this is your main interaction logic
#### 4. `OnInteractingCharacterArrived()` - Arrival Reaction
```csharp
protected virtual void OnInteractingCharacterArrived()
{
// Called when character reaches interaction point
// Trigger arrival animations, sounds, etc.
}
```
**When to override:** React to character arrival with visuals/audio
#### 5. `OnInteractionFinished()` - Cleanup Logic
```csharp
protected virtual void OnInteractionFinished(bool success)
{
// Called after interaction completes
// Cleanup, reset state, etc.
}
```
**When to override:** Perform cleanup after interaction completes
#### 6. `CanProceedWithInteraction()` - Validation
```csharp
protected virtual (bool canProceed, string errorMessage) CanProceedWithInteraction()
{
// Validate if interaction can proceed
// Return error message to show to player
return (true, null);
}
```
**When to override:** Add validation that shows error messages to player
---
## Creating Custom Interactables
### Example 1: Simple Button (OneClickInteraction)
The simplest interactable just completes when the character arrives:
```csharp
using Interactions;
public class OneClickInteraction : InteractableBase
{
protected override bool DoInteraction()
{
// Simply return success - no additional logic needed
return true;
}
}
```
**Use Case:** Triggers, pressure plates, simple activators
**Configuration:**
- Set `characterToInteract` to define which character activates it
- Use UnityEvents in inspector to trigger game logic
---
### Example 2: Item Pickup
From `Pickup.cs` - demonstrates validation and follower interaction:
```csharp
public class Pickup : SaveableInteractable
{
public PickupItemData itemData;
public bool IsPickedUp { get; internal set; }
protected override bool DoInteraction()
{
// Try combination first if follower is holding something
var heldItemObject = FollowerController?.GetHeldPickupObject();
var heldItemData = heldItemObject?.GetComponent<Pickup>()?.itemData;
var combinationResult = FollowerController.TryCombineItems(
this, out var resultItem
);
if (combinationResult == FollowerController.CombinationResult.Successful)
{
IsPickedUp = true;
FireCombinationEvent(resultItem, heldItemData);
return true;
}
// No combination - do regular pickup
FollowerController?.TryPickupItem(gameObject, itemData);
IsPickedUp = true;
OnItemPickedUp?.Invoke(itemData);
return true;
}
}
```
**Key Patterns:**
- Access `FollowerController` directly (set by base class)
- Return `true` for successful pickup
- Use custom events (`OnItemPickedUp`) for specific notifications
---
### Example 3: Item Slot with Validation
From `ItemSlot.cs` - demonstrates complex validation and state management:
```csharp
public class ItemSlot : SaveableInteractable
{
public PickupItemData itemData; // What item should go here
private ItemSlotState currentState = ItemSlotState.None;
protected override (bool canProceed, string errorMessage) CanProceedWithInteraction()
{
var heldItem = FollowerController?.CurrentlyHeldItemData;
// Can't interact with empty slot and no item
if (heldItem == null && currentlySlottedItemObject == null)
return (false, "This requires an item.");
// Check forbidden items
if (heldItem != null && currentlySlottedItemObject == null)
{
var config = interactionSettings?.GetSlotItemConfig(itemData);
var forbidden = config?.forbiddenItems ?? new List<PickupItemData>();
if (PickupItemData.ListContainsEquivalent(forbidden, heldItem))
return (false, "Can't place that here.");
}
return (true, null);
}
protected override bool DoInteraction()
{
var heldItemData = FollowerController.CurrentlyHeldItemData;
var heldItemObj = FollowerController.GetHeldPickupObject();
// Scenario 1: Slot empty + holding item = Slot it
if (heldItemData != null && currentlySlottedItemObject == null)
{
SlotItem(heldItemObj, heldItemData);
FollowerController.ClearHeldItem();
return IsSlottedItemCorrect(); // Returns true only if correct item
}
// Scenario 2: Slot full + holding item = Try combine or swap
if (currentlySlottedItemObject != null)
{
// Try combination...
// Or swap items...
}
return false;
}
}
```
**Key Patterns:**
- `CanProceedWithInteraction()` shows error messages to player
- `DoInteraction()` returns true only for correct item (affects puzzle completion)
- Access settings via `GameManager.GetSettingsObject<T>()`
---
## Character Movement
### Character Types
```csharp
public enum CharacterToInteract
{
None, // No character movement
Trafalgar, // Player only
Pulver, // Follower only (player moves to range first)
Both // Both characters move
}
```
Set in Inspector on `InteractableBase`.
### Custom Movement Targets
Add `CharacterMoveToTarget` component as child of your interactable:
```csharp
// Automatically used if present
var moveTarget = GetComponentInChildren<CharacterMoveToTarget>();
Vector3 targetPos = moveTarget.GetTargetPosition();
```
See [Editor Reference](editor_reference.md#character-movement-targets) for details.
---
## Action Component System
Add modular behaviors to interactables via `InteractionActionBase` components.
### Creating an Action Component
```csharp
using Interactions;
using System.Threading.Tasks;
public class MyCustomAction : InteractionActionBase
{
protected override async Task<bool> ExecuteAsync(
InteractionEventType eventType,
PlayerTouchController player,
FollowerController follower)
{
// Your action logic here
if (eventType == InteractionEventType.InteractionStarted)
{
// Play sound, spawn VFX, etc.
await Task.Delay(1000); // Simulate async work
}
return true; // Return success
}
protected override bool ShouldExecute(
InteractionEventType eventType,
PlayerTouchController player,
FollowerController follower)
{
// Add conditions for when this action should run
return base.ShouldExecute(eventType, player, follower);
}
}
```
### Configuring in Inspector
![Action Component Setup](../media/interactable_action_component_inspector.png)
- **Respond To Events**: Select which events trigger this action
- **Pause Interaction Flow**: If true, interaction waits for this action to complete
### Built-in Action: Timeline Playback
`InteractionTimelineAction` plays Unity Timeline sequences in response to events:
```csharp
// Automatically configured via Inspector
// See Editor Reference for details
```
**Features:**
- Character binding to timeline tracks
- Sequential timeline playback
- Loop options (loop all, loop last)
- Timeout protection
---
## Events System
### UnityEvents (Inspector-Configurable)
Available on all `InteractableBase`:
```csharp
[Header("Interaction Events")]
public UnityEvent<PlayerTouchController, FollowerController> interactionStarted;
public UnityEvent interactionInterrupted;
public UnityEvent characterArrived;
public UnityEvent<bool> interactionComplete; // bool = success
```
### C# Events (Code Subscribers)
Pickup example:
```csharp
public event Action<PickupItemData> OnItemPickedUp;
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
```
ItemSlot example:
```csharp
public event Action<PickupItemData> OnItemSlotRemoved;
public event Action<PickupItemData, PickupItemData> OnCorrectItemSlotted;
public event Action<PickupItemData, PickupItemData> OnIncorrectItemSlotted;
```
### Subscribing to Events
```csharp
void Start()
{
var pickup = GetComponent<Pickup>();
pickup.OnItemPickedUp += HandleItemPickedUp;
}
void HandleItemPickedUp(PickupItemData itemData)
{
Debug.Log($"Picked up: {itemData.itemName}");
}
void OnDestroy()
{
var pickup = GetComponent<Pickup>();
if (pickup != null)
pickup.OnItemPickedUp -= HandleItemPickedUp;
}
```
---
## Save/Load System Integration
### Making an Interactable Saveable
1. Inherit from `SaveableInteractable` instead of `InteractableBase`
2. Define a serializable data structure
3. Override `GetSerializableState()` and `ApplySerializableState()`
### Example Implementation
```csharp
using Interactions;
using UnityEngine;
// 1. Define save data structure
[System.Serializable]
public class MyInteractableSaveData
{
public bool hasBeenActivated;
public int activationCount;
}
// 2. Inherit from SaveableInteractable
public class MyInteractable : SaveableInteractable
{
private bool hasBeenActivated = false;
private int activationCount = 0;
// 3. Serialize state
protected override object GetSerializableState()
{
return new MyInteractableSaveData
{
hasBeenActivated = this.hasBeenActivated,
activationCount = this.activationCount
};
}
// 4. Deserialize state
protected override void ApplySerializableState(string serializedData)
{
var data = JsonUtility.FromJson<MyInteractableSaveData>(serializedData);
if (data == null) return;
this.hasBeenActivated = data.hasBeenActivated;
this.activationCount = data.activationCount;
// IMPORTANT: Don't fire events during restoration
// Don't re-run initialization logic
}
protected override bool DoInteraction()
{
hasBeenActivated = true;
activationCount++;
return true;
}
}
```
---
## Integration with Puzzle System
Interactables can be puzzle steps by adding `ObjectiveStepBehaviour`:
```csharp
// On GameObject with Interactable component
var stepBehaviour = gameObject.AddComponent<ObjectiveStepBehaviour>();
stepBehaviour.stepData = myPuzzleStepSO;
```
### Automatic Puzzle Integration
`InteractableBase` automatically checks for puzzle locks:
```csharp
private (bool, string) ValidateInteractionBase()
{
var step = GetComponent<PuzzleS.ObjectiveStepBehaviour>();
if (step != null && !step.IsStepUnlocked())
{
// Special case: ItemSlots can swap even when locked
if (!(this is ItemSlot))
{
return (false, "This step is locked!");
}
}
return (true, null);
}
```
**Result:** Locked puzzle steps can't be interacted with (except ItemSlots for item swapping).
---
## Advanced Patterns
### Async Validation
For complex validation that requires async operations:
```csharp
protected override (bool canProceed, string errorMessage) CanProceedWithInteraction()
{
// Synchronous validation only
// Async validation should be done in OnInteractionStarted
return (true, null);
}
protected override void OnInteractionStarted()
{
// Can perform async checks here if needed
// But interaction flow continues automatically
}
```
### Interrupting Interactions
Interactions auto-interrupt if player cancels movement:
```csharp
// Automatically handled in MoveCharactersAsync()
playerRef.OnMoveToCancelled += () => {
interactionInterrupted?.Invoke();
// Flow stops here
};
```
### One-Time Interactions
```csharp
[Header("Interaction Settings")]
public bool isOneTime = true;
// Automatically disabled after first successful interaction
// No override needed
```
### Cooldown Systems
```csharp
[Header("Interaction Settings")]
public float cooldown = 5f; // Seconds
// Automatically handled by base class
// Interaction disabled for 5 seconds after completion
```

View File

@@ -1,305 +0,0 @@
# Interactables System - Editor Reference
## Table of Contents
1. [Overview](#overview)
2. [Adding Interactables to Scene](#adding-interactables-to-scene)
3. [InteractableBase Inspector](#interactablebase-inspector)
- [Interaction Settings](#interaction-settings)
- [Interaction Events](#interaction-events-unityevents)
4. [Character Movement Targets](#character-movement-targets)
5. [Pickup Inspector](#pickup-inspector)
6. [ItemSlot Inspector](#itemslot-inspector)
7. [OneClickInteraction Inspector](#oneclickinteraction-inspector)
8. [Interaction Action Components](#interaction-action-components)
9. [Custom Action Components](#custom-action-components)
10. [Puzzle Integration](#puzzle-integration)
11. [Save System Configuration](#save-system-configuration)
---
## Overview
This guide covers configuring interactables using the Unity Inspector and scene tools. It might be helpful, although
not necessary to be familiar with the code architecture covered in the [Code Reference](code_reference.md).
---
## Adding Interactables to Scene
### Method 1: Add Component Manually
Select GameObject → Add Component → Search "Interactable" → Choose type
### Method 2: Use Interactable Editor
`AppleHills > Interactable Editor` → Scene tab → Select GameObject → Click button for desired type
See [Editor Tools Reference](editor_tools_reference.md#interactable-editor) for details.
---
## InteractableBase Inspector
![InteractableBase Inspector](../media/interactable_base_inspector.png)
### Interaction Settings
**Is One Time** - Disable after first successful interaction (switches, consumables)
**Cooldown** - Temporarily disable after use, in seconds. `-1` = no cooldown (levers, buttons)
**Character To Interact** - Which character(s) move to activate:
- **None** - No movement, instant interaction
- **Trafalgar** - Player moves to point
- **Pulver** - Follower moves (player moves to range first)
- **Both** - Both characters move
### Interaction Events (UnityEvents)
![Interaction Events](../media/interactable_events_inspector.png)
**Interaction Started** `<PlayerTouchController, FollowerController>` - Fires after tap, before movement
**Interaction Interrupted** - Player cancels or validation fails
**Character Arrived** - Character reaches destination
**Interaction Complete** `<bool>` - After DoInteraction(), bool = success
### Example Event Configuration
![Event Configuration Example](../media/interactable_event_configuration_example.png)
**Door that opens when player arrives:**
- Character To Interact: `Trafalgar`
- Character Arrived: `DoorAnimator.SetTrigger("Open")`, `AudioSource.Play()`
- Interaction Complete: `PuzzleStep.CompleteStep()` (if success)
---
## Character Movement Targets
### Default Movement
Without `CharacterMoveToTarget`, characters move to default distances configured in `GameManager`:
- `PlayerStopDistance` - Follower interactions (~1.5 units)
- `PlayerStopDistanceDirectInteraction` - Player interactions (~0.5 units)
### Custom Movement Targets
Add `CharacterMoveToTarget` component to child GameObject:
![Character Move Target Setup](../media/character_move_target_setup.png)
**Fields:**
- **Character Type** - Which character (Trafalgar/Pulver/Both/None)
- **Position Offset** - Offset from transform position
### Scene Gizmos
![Movement Target Gizmos](../media/movement_target_gizmos.png)
**Colors:** 🔵 Blue (Trafalgar), 🟠 Orange (Pulver), 🟣 Purple (Both), ⚪ Gray (None)
---
## Pickup Inspector
![Pickup Inspector](../media/pickup_inspector.png)
**Required Fields:**
**Item Data** - `PickupItemData` ScriptableObject defining the item. Create via `Assets > Create > AppleHills > Items + Puzzles > Pickup Item Data`
**Icon Renderer** - `SpriteRenderer` displaying item icon (auto-assigned if not set)
### PickupItemData ScriptableObject
![PickupItemData Inspector](../media/pickup_item_data_inspector.png)
**Fields:** Item Name, Description, Map Sprite, Pick Up Sound, Drop Sound
**Item ID** (Read-Only) - Auto-generated unique identifier for save/load
---
## ItemSlot Inspector
![ItemSlot Inspector](../media/item_slot_inspector.png)
**Required Fields:**
**Item Data** - `PickupItemData` defining the **correct** item for this slot
**Icon Renderer** - `SpriteRenderer` showing slot icon (background/outline)
**Slotted Item Renderer** - `SpriteRenderer` showing currently slotted item (usually child GameObject)
### Slot Events
![ItemSlot Events](../media/item_slot_events.png)
**On Item Slotted** - Any item placed
**On Item Slot Removed** - Item removed
**On Correct Item Slotted** - Correct item placed (also fires `interactionComplete(true)`)
**On Incorrect Item Slotted** - Wrong item placed
**On Forbidden Item Slotted** - Forbidden item attempted
### Slot Item Configuration (Settings)
![Slot Item Config Settings](../media/slot_item_config_settings.png)
Configured in `InteractionSettings` at `Assets/Settings/InteractionSettings`:
- **Correct Items** - List of accepted items
- **Forbidden Items** - Items that can't be placed
- **Incorrect Items** - Items that slot but aren't correct
---
## OneClickInteraction Inspector
![OneClickInteraction Inspector](../media/oneclick_inspector.png)
**No additional fields** - only inherits `InteractableBase` settings.
### Typical Configuration
- **Character To Interact:** `Trafalgar` or `Pulver`
- **Is One Time:** Depends on use case
- **Interaction Complete Event:** Configure to trigger game logic
### Example Use Cases
**Pressure Plate:**
- Character To Interact: `Pulver`
- Is One Time: `false`
- Interaction Complete: Call `Door.Open()`
**Tutorial Trigger:**
- Character To Interact: `None`
- Is One Time: `true`
- Interaction Started: Call `TutorialManager.ShowTip()`
**Dialogue Starter:**
- Character To Interact: `Both`
- Is One Time: `false`
- Character Arrived: Call `DialogueManager.StartDialogue()`
---
## Interaction Action Components
### InteractionTimelineAction
![InteractionTimelineAction Inspector](../media/interaction_timeline_action_inspector.png)
Plays Unity Timeline sequences in response to interaction events.
#### Required Fields
**Playable Director**
- **Type:** `PlayableDirector` component
- **Purpose:** Timeline player
- **Setup:** Auto-assigned from same GameObject if present
**Timeline Mappings** (Array)
Each element maps an interaction event to timeline(s):
![Timeline Mapping Element](../media/timeline_mapping_element.png)
##### Event Type
- **Type:** `InteractionEventType` enum
- **Options:**
- `InteractionStarted`
- `PlayerArrived`
- `InteractingCharacterArrived`
- `InteractionComplete`
- `InteractionInterrupted`
- **Purpose:** When to play this timeline
##### Timelines (Array)
- **Type:** `PlayableAsset[]`
- **Purpose:** Timeline(s) to play for this event
- **Note:** Plays sequentially if multiple
##### Bind Player Character
- **Type:** `bool`
- **Purpose:** Automatically bind player to timeline track
- **Track Name:** `Player` (customizable via Player Track Name field)
##### Bind Pulver Character
- **Type:** `bool`
- **Purpose:** Automatically bind follower to timeline track
- **Track Name:** `Pulver` (customizable via Pulver Track Name field)
##### Player Track Name / Pulver Track Name
- **Type:** `string`
- **Default:** `"Player"` / `"Pulver"`
- **Purpose:** Name of timeline track to bind character to
- **Note:** Must match track name in Timeline asset exactly
##### Timeout Seconds
- **Type:** `float`
- **Default:** `30`
- **Purpose:** Safety timeout - auto-complete if timeline doesn't finish
- **Use Case:** Prevent stuck interactions if timeline errors
##### Loop Last / Loop All
- **Type:** `bool`
- **Purpose:** Loop behavior for timeline sequence
- **Loop Last:** Replays final timeline on next interaction
- **Loop All:** Cycles through all timelines repeatedly
---
## Custom Action Components
See [Code Reference - Action Component System](code_reference.md#action-component-system).
**Base Fields:**
- **Respond To Events** - Which events trigger this action
- **Pause Interaction Flow** - Wait for completion (`true`) or run in background (`false`)
---
## Puzzle Integration
### Adding Puzzle Step to Interactable
1. Select interactable GameObject
2. Add Component → `ObjectiveStepBehaviour`
3. Assign `Step Data` (PuzzleStepSO asset)
![Puzzle Step Integration](../media/puzzle_step_integration.png)
**GameObject has two components:**
- **ItemSlot** (or other Interactable type)
- **ObjectiveStepBehaviour**
**Behavior:**
- Interactable locked until puzzle step unlocked
- Successful interaction (return `true` from `DoInteraction()`) completes puzzle step
- ItemSlots can still swap items when locked (special case)
### Automatic Step Completion
**For Pickup:**
```csharp
protected override bool DoInteraction()
{
// ...pickup logic...
return true; // Automatically completes puzzle step if present
}
```
**For ItemSlot:**
```csharp
protected override bool DoInteraction()
{
// ...slot logic...
return IsSlottedItemCorrect(); // Only completes if correct item
}
```
No additional code needed - `InteractableBase` handles step completion automatically.

View File

@@ -1,251 +0,0 @@
# Editor Tools Reference
## Overview
AppleHills provides two specialized editor tools for managing puzzles and interactables:
- **Interactable Editor** - Manage scene interactables and debug interactions
- **Puzzle Editor** - Manage puzzle steps and debug puzzle flow
Both tools are accessible via the `AppleHills` menu and follow a consistent two-tab design pattern for editing and debugging.
---
## Interactable Editor
**Menu:** `AppleHills > Interactable Editor`
The Interactable Editor provides scene-based management and runtime debugging for interactable objects in your scenes.
### Edit Tab
![Interactable Editor - Edit Tab](../media/interactable_editor_edit.png)
The Edit tab lets you browse, select, and modify all interactables in the current scene with a real-time inspector.
#### Left Panel - Scene Interactable List
**Refresh Button** - Manual refresh (auto-refreshes on scene changes, hierarchy changes, and play mode toggle)
**Found Count** - Displays total number of interactables found in the active scene
**Search Field** - Filter interactables by GameObject name for quick access
**Interactable Cards** - Each card shows:
- GameObject name
- Interactable component type
- Item/Slot data reference (for Pickup and ItemSlot types)
- Ping button to highlight in scene hierarchy
**Auto-Discovery:** Automatically scans for all `InteractableBase` components when:
- Scene loads or unloads
- GameObjects or components are added/removed
- Play mode is toggled
#### Right Panel - Dynamic Inspector
The right panel adapts based on what's selected:
**Nothing Selected State:**
- Shows a help message prompting you to select an interactable from the list
**GameObject Without Interactable State:**
- Displays "Add Interactable Component" buttons for:
- OneClickInteraction
- Pickup
- ItemSlot
- SaveableInteractable
- InteractableBase
- Clicking any button adds the component with full undo support and auto-refreshes the list
**Interactable Selected State:**
- Shows full Unity inspector for the selected component
- All changes auto-save with undo/redo support (Ctrl+Z / Ctrl+Y)
- Additional Information section displays:
- Component type
- Attached action components with Ping buttons
- Type-specific data (Item Data for Pickup, Slot Data for ItemSlot)
- In Play Mode: shows slotted object for ItemSlot types
**Selection Synchronization:**
- Bidirectional sync between editor list and scene hierarchy
- Selecting in the list highlights in hierarchy and vice versa
- Selecting a GameObject without an interactable shows the "Add Component" interface
---
![Interactable Editor - Debug Tab](../media/interactable_editor_debug.png)
### Debug Tab
**Availability:** Play Mode only
The Debug tab provides runtime testing tools for triggering interactions and events on interactables.
#### Interactable Debug Cards
Each debug card represents one interactable in the scene and includes:
**Header Section:**
- GameObject name (bold text)
- Interactable component type (gray text)
- Ping button (locates object in hierarchy)
**Full Interaction Button:**
- Simulates complete `OnTap()` flow
- Triggers character movement and full event chain
- Tests end-to-end interaction behavior
**Individual Event Triggers:**
- **Started** - Calls `OnInteractionStarted()` and fires `interactionStarted` event
- **Arrived** - Calls `OnInteractingCharacterArrived()` and fires `characterArrived` event
- **Do Interaction** - Calls `DoInteraction()` directly to test core interaction logic
- **Complete (Success)** - Calls `OnInteractionFinished(true)` and triggers puzzle completion
- **Complete (Fail)** - Calls `OnInteractionFinished(false)` to test failure handling
- **Interrupted** - Invokes `interactionInterrupted` event
**Registered Actions Display:**
- Lists all action components registered to this interactable
- Shows which events each action responds to
#### Common Testing Workflows
**Test Full Interaction:**
1. Enter Play Mode
2. Find target interactable in debug list
3. Click **Full Interaction** button
4. Verify complete behavior chain
**Test Specific Event:**
1. Enter Play Mode
2. Locate interactable
3. Click individual event button (e.g., **Started** or **Arrived**)
4. Verify specific event behavior
**Test Event Sequence:**
1. Click **Started**
2. Click **Arrived**
3. Click **Do Interaction**
4. Click **Complete (Success)**
5. Verify full event chain executes correctly
**Test Action Integration:**
1. Find interactable with timeline or dialogue action
2. Check Registered Actions to confirm action is attached
3. Click **Started** or appropriate event trigger
4. Verify action executes (timeline plays, dialogue shows, etc.)
**Test Puzzle Integration:**
1. Open both Interactable Editor and Puzzle Editor
2. Verify required puzzle step is unlocked in Puzzle Editor
3. Click **Full Interaction** in Interactable Editor
4. Switch to Puzzle Editor and verify step marked as completed
---
## Puzzle Editor
**Menu:** `AppleHills > Puzzle Editor`
![Puzzle Editor - Edit Tab](../media/puzzle_editor_edit.png)
The Puzzle Editor manages puzzle step assets and provides runtime debugging for the puzzle progression system.
### Edit Tab
The Edit tab displays all `PuzzleStepSO` assets in your project with full editing capabilities.
#### Left Panel - Puzzle Step List
**Search Field** - Filter puzzle steps by name
**Folder Organization:**
- Steps are grouped by their asset folder location
- Click folder headers to expand/collapse groups
- Helps organize large numbers of puzzle steps
**Step Cards** - Each card displays:
- Display name (user-friendly identifier)
- Step ID (unique technical identifier)
- Dependency information (unlocked by / unlocks)
**Toolbar Actions:**
- **Refresh** - Reloads all puzzle step assets from project
- **Create New** - Opens creation dialog
**Creating New Steps:**
1. Click **Create New** button
2. Enter step name (stepId auto-generates from name)
3. Select destination folder
4. Click Create
5. New step appears in list and is auto-selected
#### Right Panel - Step Inspector
When a puzzle step is selected, the inspector shows:
**Basic Properties:**
- **Display Name** - Editable user-friendly name for the step
- **Step ID** - Read-only unique identifier (lowercase, underscored format)
**Dependencies Configuration:**
- **Unlocked By** - List of steps that must complete before this step unlocks
- Drag and drop `PuzzleStepSO` assets to add dependencies
- Empty list means this is an initial step (unlocked by default)
- **Unlocks** - List of steps that this step will unlock when completed
- Bidirectional relationship (automatically syncs with "Unlocked By" on other steps)
- Edit from either side of the relationship
**Asset Management:**
- **Asset Path** - Shows full file path to the .asset file
- **Delete Button** - Permanently deletes the step asset
- Shows confirmation dialog before deletion
- Cannot be undone after confirmation
**Auto-Save:** All changes save automatically to the asset with full undo/redo support (Ctrl+Z / Ctrl+Y)
![Puzzle Editor - Debug Tab](../media/puzzle_editor_debug.png)
---
### Debug Tab
**Availability:** Play Mode only
The Debug tab provides runtime testing and debugging tools for the puzzle progression system.
#### Toolbar
**Current Level Display:**
- Shows the name of the currently loaded puzzle level
- Updates automatically when scenes change
- Displays "No level loaded" if puzzle system is inactive
**Unlock All Button:**
- Unlocks and completes all puzzle steps in the current level
- Processes steps in dependency order using iterative algorithm
- Logs progression to console for debugging
- Useful for testing late-game content or verifying completion flow
#### Step List
Each step in the current level displays:
**Step Header:**
- Display name in bold text
- Step ID in gray text below name
**State Indicators:**
- 🔒 **Locked** (gray background) - Dependencies not met, step unavailable
- 🔓 **Unlocked** (yellow background) - Available for interaction but not completed
-**Completed** (green background) - Successfully completed
**Action Buttons:**
- **Toggle Lock** - Manually lock/unlock the step
- Bypasses normal dependency requirements
- Useful for testing specific scenarios
- Does not affect dependent steps automatically
- **Complete** - Marks step as completed
- Only enabled when step is unlocked
- Fires completion events
- Automatically unlocks dependent steps
- Updates state indicators in real-time

View File

@@ -0,0 +1,253 @@
# Apple Hills Interaction System
A concise, code-first guide to creating and extending interactions using `Interactable` and modular action/requirement components. Designed to match the style of the other updated docs (TOC, inline code, case studies).
## Table of Contents
- [What This Solves](#what-this-solves)
- [Architecture at a Glance](#architecture-at-a-glance)
- [Quick Start (Code-First)](#quick-start-code-first)
- [Subscribe to Interaction Events](#subscribe-to-interaction-events)
- [Create a Custom Action](#create-a-custom-action)
- [Trigger Programmatically](#trigger-programmatically)
- [Core Components](#core-components)
- [`Interactable`](#interactable)
- [`CharacterMoveToTarget`](#charactermovetotarget)
- [`InteractionActionBase` and concrete actions](#interactionactionbase-and-concrete-actions)
- [`InteractionRequirementBase`](#interactionrequirementbase)
- [Interaction Event Flow](#interaction-event-flow)
- [Case Studies](#case-studies)
- [Open a Door on Arrival](#open-a-door-on-arrival)
- [Pick Up an Item then Play Timeline](#pick-up-an-item-then-play-timeline)
- [Kick Off Dialogue When Player Arrives](#kick-off-dialogue-when-player-arrives)
- [Troubleshooting / FAQ](#troubleshooting--faq)
- [Paths & Namespaces](#paths--namespaces)
- [Change Log](#change-log)
## What This Solves
- Standardized interaction lifecycle with reliable events (`InteractionStarted`, `PlayerArrived`, `InteractingCharacterArrived`, `InteractionComplete`, `InteractionInterrupted`).
- Composable behavior via components derived from `InteractionActionBase` and `InteractionRequirementBase`.
- Clean separation of input, locomotion-to-target, cinematic timelines, and game logic.
## Architecture at a Glance
- Driver: `Interactable` — owns lifecycle, input hook, character selection via `CharacterToInteract`, oneshot/cooldown, and event dispatch.
- Targets: `CharacterMoveToTarget` — editor-authored world points for `Trafalgar`/`Pulver` to path to before executing actions.
- Actions: `InteractionActionBase` (abstract) — modular responses to specific `InteractionEventType` values; can pause the flow with async tasks.
- Requirements: `InteractionRequirementBase` (abstract) — gatekeepers for availability; multiple can be attached.
- Cinematics: `InteractionTimelineAction` — plays one or more `PlayableAsset` timelines per event; optional character auto-binding.
## Quick Start (Code-First)
### Subscribe to Interaction Events
```csharp
using Interactions;
using UnityEngine;
public class InteractDebugHooks : MonoBehaviour
{
[SerializeField] private Interactable interactable;
private void OnEnable()
{
interactable.interactionStarted.AddListener(OnStarted);
interactable.characterArrived.AddListener(OnCharacterArrived);
interactable.interactionInterrupted.AddListener(OnInterrupted);
interactable.interactionComplete.AddListener(OnComplete);
}
private void OnDisable()
{
interactable.interactionStarted.RemoveListener(OnStarted);
interactable.characterArrived.RemoveListener(OnCharacterArrived);
interactable.interactionInterrupted.RemoveListener(OnInterrupted);
interactable.interactionComplete.RemoveListener(OnComplete);
}
private void OnStarted(Input.PlayerTouchController player, FollowerController follower)
=> Debug.Log("Interaction started");
private void OnCharacterArrived() => Debug.Log("Character arrived");
private void OnInterrupted() => Debug.Log("Interaction interrupted");
private void OnComplete(bool success) => Debug.Log($"Interaction complete: {success}");
}
```
### Create a Custom Action
```csharp
using System.Threading.Tasks;
using Interactions;
using Input;
using UnityEngine;
public class PlaySfxOnArrivalAction : InteractionActionBase
{
[SerializeField] private AudioSource sfx;
private void Reset()
{
// React to the arrival event; don't block the flow
respondToEvents = new() { InteractionEventType.InteractingCharacterArrived };
pauseInteractionFlow = false;
}
protected override bool ShouldExecute(InteractionEventType evt, PlayerTouchController player, FollowerController follower)
{
return sfx != null;
}
protected override async Task<bool> ExecuteAsync(InteractionEventType evt, PlayerTouchController player, FollowerController follower)
{
sfx.Play();
// non-blocking action returns immediately when pauseInteractionFlow == false
return true;
}
}
```
Attach this component under the same hierarchy as an `Interactable`. Registration is automatic via `OnEnable()`/`OnDisable()` in `InteractionActionBase`.
### Trigger Programmatically
Normally input goes through `ITouchInputConsumer.OnTap(...)`. For testing, you can call the public tap handler:
```csharp
using UnityEngine;
using Interactions;
public class TestTrigger : MonoBehaviour
{
[SerializeField] private Interactable interactable;
[ContextMenu("Trigger Interact (dev)")]
private void Trigger()
{
interactable.OnTap(interactable.transform.position);
}
}
```
## Core Components
### `Interactable`
- Handles input, cooldowns (`cooldown`), oneshot (`isOneTime`), and which character participates (`characterToInteract`).
- Exposes events: `interactionStarted`, `characterArrived`, `interactionInterrupted`, `interactionComplete`.
- Discovers and dispatches to child `InteractionActionBase` components; awaits those that request to pause.
![Interactable Inspector](media/interactable_inspector.png)
### `CharacterMoveToTarget`
Defines the world positions characters should reach before actions evaluate.
- Can target `Trafalgar`, `Pulver`, or `Both` via configuration.
- Supports offsets and editor gizmos; multiple instances allowed.
![Character Move Target Inspector](media/character_move_target_inspector.png)
### `InteractionActionBase` and concrete actions
- Filter by `InteractionEventType` using `respondToEvents`.
- Control flow with `pauseInteractionFlow` and async `ExecuteAsync(...)`.
- Builtin example: `InteractionTimelineAction` for cinematics.
![InteractionTimelineAction Inspector](media/interaction_timeline_action_inspector.png)
### `InteractionRequirementBase`
- Attach one or more to gate the interaction based on items, puzzles, proximity, etc.
## Interaction Event Flow
1. `InteractionStarted`
2. `PlayerArrived`
3. `InteractingCharacterArrived`
4. `InteractionComplete` (bool success)
5. `InteractionInterrupted`
Actions receive these events in order and may run concurrently; those with `pauseInteractionFlow` true are awaited.
## Case Studies
### Open a Door on Arrival
```csharp
using System.Threading.Tasks;
using Interactions;
using Input;
using UnityEngine;
public class DoorOpenOnArrival : InteractionActionBase
{
[SerializeField] private Animator animator; // expects a bool parameter "Open"
private void Reset()
{
respondToEvents = new() { InteractionEventType.InteractingCharacterArrived };
pauseInteractionFlow = false;
}
protected override async Task<bool> ExecuteAsync(InteractionEventType evt, PlayerTouchController p, FollowerController f)
{
animator.SetBool("Open", true);
return true;
}
}
```
### Pick Up an Item then Play Timeline
Attach two actions: your `PickupItemAction` that pauses until the item is collected, and an `InteractionTimelineAction` mapped to `InteractionEventType.InteractionComplete` to celebrate.
### Kick Off Dialogue When Player Arrives
```csharp
using System.Threading.Tasks;
using Dialogue;
using Input;
using Interactions;
using UnityEngine;
public class StartDialogueOnArrival : InteractionActionBase
{
[SerializeField] private DialogueComponent dialogue;
private void Reset()
{
respondToEvents = new() { InteractionEventType.PlayerArrived };
pauseInteractionFlow = false;
}
protected override async Task<bool> ExecuteAsync(InteractionEventType evt, PlayerTouchController p, FollowerController f)
{
dialogue.StartDialogue();
return true;
}
}
```
## Troubleshooting / FAQ
- Interaction doesnt fire:
- Confirm `Interactable` is active and not in cooldown or already completed (`isOneTime`).
- Ensure `CharacterMoveToTarget` exists for the selected `CharacterToInteract`.
- Actions not running:
- Verify `respondToEvents` includes the lifecycle moment you expect.
- Check that the component sits under the same hierarchy so it registers with the `Interactable`.
- Timeline never finishes:
- Make sure `InteractionTimelineAction` has valid `PlayableAsset` entries and binding flags.
- Double triggers:
- Guard reentry in your actions or check `_interactionInProgress` usage in `Interactable` by following logs.
## Paths & Namespaces
- Scripts: `Assets/Scripts/Interactions/`
- `Interactable.cs`
- `InteractionActionBase.cs`
- `InteractionTimelineAction.cs`
- `InteractionEventType.cs`
- `InteractionRequirementBase.cs`
- Editor tooling: `Assets/Editor/InteractableEditor.cs`
- Primary namespace: `Interactions`
## Additional Editor Visuals
- Timeline mapping configuration UI:
![Timeline Mapping Editor](media/timeline_mapping_editor.png)
- Unity Timeline editor when authoring cinematics for interactions:
![Timeline Editor](media/timeline_editor.png)
- Example target placement in Scene view:
![Target Positioning In Scene](media/target_positioning_scene.png)
## Change Log
- v1.1: Added Table of Contents, code-first snippets, case studies, standardized inline code references, preserved existing editor images, and added troubleshooting/paths.
- v1.0: Original overview and setup guide.

View File

@@ -1,93 +0,0 @@
# ManagedBehaviour System - Architecture Overview
**Version:** 2.0 <br>
**Updated:** 11.11.2025
---
## What is the ManagedBehaviour System?
Lifecycle orchestration framework that provides guaranteed execution order and deterministic lifecycle management for Unity components.
### Problems Solved
We've had quite a few things shoe-stringed together in various ways and dependant on references to each other.
Due to undefined initialization order - null references during access to yet uninitialized resources was becoming a problem.
### What You Get
- **Guaranteed Initialization Order** - Managers ready before components that depend on them
- **Deterministic Lifecycle Hooks** - Predictable callbacks at key moments
- **Automatic Registration** - No boilerplate for wiring up systems
- **Scene Lifecycle Events** - Built-in hooks for scene load/unload
- **Save/Load Coordination** - Centralized collection of save data
- **Bootstrap Integration** - Components know when bootstrap completes
---
## Architecture Principles
### 1. Centralized Orchestration
Single `LifecycleManager` singleton coordinates all lifecycle events. Components auto-register and receive callbacks at appropriate times.
### 2. Sealed Framework Methods
`Awake()` and `OnDestroy()` are sealed. Use `OnManagedAwake()` and `OnManagedDestroy()` instead. Prevents forgetting to call `base.Awake()`.
### 3. Two-Phase Initialization
- **Early (`OnManagedAwake()`)**: During Unity's Awake, before bootstrap. Use for singleton setup.
- **Late (`OnManagedStart()`)**: After bootstrap completes. All managers guaranteed ready.
### 4. Registration Order Execution
Execution follows Unity's natural Awake order. No priority numbers to manage.
### 5. Automatic Cleanup
Framework handles unregistration automatically. Override `OnManagedDestroy()` only if you need custom cleanup.
---
## Lifecycle Flow Diagrams
### Boot Sequence
![Boot Sequence](../media/boot_sequence.png)
### Scene Transition Flow
![Scene Transition Flow](../media/Scene_transition_flow.png)
### Component Lifecycle (Individual Component)
![Component Lifecycle](../media/component_lifecycle.png)
---
## Class Diagram
![Class Diagram](../media/class_diagram.png)
---
## Key Guarantees
### Guaranteed
1. **Bootstrap Completion** - `OnManagedStart()` always fires after bootstrap completes
2. **Manager Availability** - All manager singletons exist when `OnManagedStart()` is called
3. **Scene Lifecycle** - `OnSceneReady()` fires after scene load, `OnSceneUnloading()` before unload
4. **Automatic Registration** - Can't forget to register (Awake is sealed)
5. **Automatic Cleanup** - Can't forget to unregister (OnDestroy is sealed)
6. **Save/Load Coordination** - All save participants called in one pass
### Not Guaranteed
1. **Initialization Order Between Components** - `OnManagedAwake()` follows Unity's unpredictable Awake order
2. **Thread Safety** - All methods must run on main thread
3. **Performance** - Broadcasting to 1000+ components may have overhead
4. **SaveId Uniqueness** - Developer responsible for unique SaveIds

View File

@@ -1,377 +0,0 @@
# ManagedBehaviour System - Technical Reference
**Version:** 2.0 <br>
**Updated:** 11.11.2025
---
## Overview
The ManagedBehaviour system provides a deterministic, ordered lifecycle management framework for Unity MonoBehaviours. This document provides complete technical documentation of all classes, methods, and APIs.
---
## Core Classes
### ManagedBehaviour (Abstract Base Class)
**Namespace:** `Core.Lifecycle`
**Inherits:** `MonoBehaviour`
**Location:** `Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs` → [View Source](../../Assets/Scripts/Core/Lifecycle/ManagedBehaviour.cs)
Abstract base class that all managed components must inherit from. Provides automatic registration with LifecycleManager and ordered lifecycle callbacks.
#### Lifecycle Hook Methods
Override these `internal virtual` methods to customize component behavior at different lifecycle stages. Called automatically by `LifecycleManager`.
<details>
<summary>Click to see more details</summary>
##### `OnManagedAwake()`
```csharp
internal virtual void OnManagedAwake()
```
**Called:** During registration (within Unity's Awake phase)
**Execution Order:** Natural Unity script execution order (not guaranteed between components)
**Use For:**
- Setting singleton instances (`_instance = this`)
- Early GetComponent calls
- One-time initialization that doesn't depend on other systems
**Timing Guarantees:**
- ✅ GameObject and component exist
- ✅ Scene is loaded
- ❌ Other components may not be initialized yet
- ❌ Bootstrap may not be complete
##### `OnManagedStart()`
```csharp
internal virtual void OnManagedStart()
```
**Called:** After bootstrap completes (for boot components) or immediately after registration (for late-registered components)
**Execution Order:** Registration order
**Use For:**
- Initialization that depends on other managers
- Accessing singleton instances safely
- Setting up cross-system dependencies
**Timing Guarantees:**
- ✅ All managers are initialized
- ✅ Bootstrap resources are available
- ✅ Safe to access `GameManager.Instance`, `AudioManager.Instance`, etc.
##### `OnSceneUnloading()`
```csharp
internal virtual void OnSceneUnloading()
```
**Called:** Before scene unload
**Execution Order:** Registration order
**Use For:**
- Scene-specific cleanup
- Saving temporary scene state
**Timing Guarantees:**
- ✅ Scene is still loaded
- ✅ Other components in scene still exist
##### `OnSceneReady()`
```csharp
internal virtual void OnSceneReady()
```
**Called:** After scene load completes
**Execution Order:** Registration order
**Use For:**
- Scene-specific initialization
- Finding scene objects
- Setting up scene-specific state
**Timing Guarantees:**
- ✅ All scene GameObjects are loaded
- ✅ Batched components have received `OnManagedStart()`
##### `OnSceneSaveRequested()`
```csharp
internal virtual string OnSceneSaveRequested()
```
**Called:** Before scene unload during scene transitions
**Returns:** Serialized scene-specific data (JSON string), or `null` if nothing to save
**Use For:**
- Level progress
- Object positions
- Puzzle states
- Temporary scene data
**⚠️ Important:** Must return synchronously.
##### `OnSceneRestoreRequested(string serializedData)`
```csharp
internal virtual void OnSceneRestoreRequested(string serializedData)
```
**Called:** After scene load, during `OnSceneReady` phase
**Parameters:**
- `serializedData` - Previously saved data from `OnSceneSaveRequested()`
**Use For:**
- Restoring level progress
- Setting object positions
- Restoring puzzle states
**⚠️ Important:** Must execute synchronously. Do not use coroutines or async/await.
##### `OnSceneRestoreCompleted()`
```csharp
internal virtual void OnSceneRestoreCompleted()
```
**Called:** After all `OnSceneRestoreRequested()` calls complete
**Timing:** Always called after scene load, whether save data exists or not
**Use For:**
- Post-restore initialization
- First-time initialization (when no save data exists)
- Triggering events after state is restored
**Common Pattern:**
```csharp
internal override void OnSceneRestoreCompleted()
{
if (!_hasPlayed) // Check if this is first time
{
PlayIntroAudio();
_hasPlayed = true;
}
}
```
##### `OnGlobalSaveRequested()`
```csharp
internal virtual string OnGlobalSaveRequested()
```
**Called:** Once before save file is written to disk
**Returns:** Serialized global persistent data (JSON string), or `null`
**Use For:**
- Player inventory
- Unlocked features
- Card collections
- Persistent progression
**Timing:** Called once per game save (not per scene transition)
##### `OnGlobalRestoreRequested(string serializedData)`
```csharp
internal virtual void OnGlobalRestoreRequested(string serializedData)
```
**Called:** Once on game boot after save file is read
**Parameters:**
- `serializedData` - Previously saved data from `OnGlobalSaveRequested()`
**Use For:**
- Restoring player inventory
- Restoring unlocked features
- Restoring persistent progression
**Timing:** Called once on boot, not during scene transitions
##### `OnGlobalLoadCompleted()`
```csharp
internal virtual void OnGlobalLoadCompleted()
```
**Called:** Once on game boot after all global restore operations complete
**Use For:**
- Triggering UI updates after load
- Broadcasting load events
- Post-load initialization
##### `OnGlobalSaveStarted()`
```csharp
internal virtual void OnGlobalSaveStarted()
```
**Called:** Once before save file is written
**Use For:**
- Final validation before save
- Cleanup operations before save
##### `OnManagedDestroy()`
```csharp
internal virtual void OnManagedDestroy()
```
**Called:** During `OnDestroy`, before unregistration
**Execution Order:** Registration order
**Use For:**
- Unsubscribing from events
- Releasing resources
- Custom cleanup logic
**Note:** Most cleanup is automatic (auto-registrations are handled by framework).
</details>
#### Configuration Properties
Virtual properties that control automatic behaviors like pause registration and save system participation.
<details>
<summary>Click to see more details</summary>
##### `AutoRegisterPausable`
```csharp
public virtual bool AutoRegisterPausable => false;
```
**Type:** `bool`
**Default:** `false`
**Description:** If true and component implements `IPausable`, automatically registers with `GameManager.Instance` during initialization. Automatic unregistration occurs on destruction.
##### `AutoRegisterForSave`
```csharp
public virtual bool AutoRegisterForSave => false;
```
**Type:** `bool`
**Default:** `false`
**Description:** If true, component participates in the save/load system. Should override `OnSceneSaveRequested()` and `OnSceneRestoreRequested()` or global equivalents.
##### `SaveId`
```csharp
public virtual string SaveId { get; }
```
**Type:** `string`
**Default:** `"{SceneName}/{GameObjectName}/{ComponentType}"`
**Description:** Unique identifier for this component in the save system. Cached on first access for performance. Override for singletons (e.g., `"PlayerController"`) or custom IDs.
**⚠️ Warning:** GameObject name changes at runtime will NOT update the cached SaveId.
</details>
### Private Lifecycle Methods
<details>
<summary>Click to see more details</summary>
##### `Awake()`
```csharp
private void Awake()
```
**Visibility:** `private` (sealed, cannot be overridden)
**Called By:** Unity
**Description:** Automatically registers component with `LifecycleManager`. Calls `OnManagedAwake()` during registration.
**⚠️ Important:** This method is sealed. Use `OnManagedAwake()` for early initialization.
##### `OnDestroy()`
```csharp
private void OnDestroy()
```
**Visibility:** `private` (sealed, cannot be overridden)
**Called By:** Unity
**Description:** Calls `OnManagedDestroy()`, unregisters from `LifecycleManager`, and handles auto-unregistrations.
**⚠️ Important:** This method is sealed. Use `OnManagedDestroy()` for custom cleanup.
</details>
---
## LifecycleManager (Singleton Orchestrator)
**Namespace:** `Core.Lifecycle`
**Inherits:** `MonoBehaviour`
**Location:** `Assets/Scripts/Core/Lifecycle/LifecycleManager.cs` → [View Source](../../Assets/Scripts/Core/Lifecycle/LifecycleManager.cs)
Central orchestrator that manages all `ManagedBehaviour` components and broadcasts lifecycle events.
### Static Properties
##### `Instance`
Singleton instance. Created automatically by `CustomBoot` before bootstrap begins.
### Public Methods
Core methods for registration, lifecycle broadcasting, and save/restore operations. Most are called automatically by the framework.
<details>
<summary>Click to see more details</summary>
##### `Register(ManagedBehaviour component)`
```csharp
public void Register(ManagedBehaviour component)
```
Registers a component with the lifecycle system. **Called automatically from `ManagedBehaviour.Awake()`.**
##### `Unregister(ManagedBehaviour component)`
```csharp
public void Unregister(ManagedBehaviour component)
```
Unregisters a component. **Called automatically from `ManagedBehaviour.OnDestroy()`.**
##### `OnBootCompletionTriggered()`
```csharp
public void OnBootCompletionTriggered()
```
Called by `CustomBoot` after bootstrap completes. Broadcasts `OnManagedStart()` to all registered components.
##### `BeginSceneLoad(string sceneName)`
```csharp
public void BeginSceneLoad(string sceneName)
```
Activates scene loading batching mode. Called by `SceneManagerService` when loading a scene.
##### `BroadcastSceneReady(string sceneName)`
```csharp
public void BroadcastSceneReady(string sceneName)
```
Processes batched components and broadcasts `OnSceneReady()` to all components in the scene. Called by `SceneManagerService` after scene load.
##### `BroadcastSceneUnloading(string sceneName)`
```csharp
public void BroadcastSceneUnloading(string sceneName)
```
Broadcasts `OnSceneUnloading()` to all components in the specified scene. Called by `SceneManagerService` before scene unload.
##### `BroadcastSceneSaveRequested()`
```csharp
public Dictionary<string, string> BroadcastSceneSaveRequested()
```
Broadcasts `OnSceneSaveRequested()` to all components with `AutoRegisterForSave == true`. Returns dictionary of SaveId → serialized data.
##### `BroadcastSceneRestoreRequested(Dictionary<string, string> saveData)`
```csharp
public void BroadcastSceneRestoreRequested(Dictionary<string, string> saveData)
```
Distributes save data to components by matching `SaveId`, then broadcasts `OnSceneRestoreCompleted()`.
##### `BroadcastGlobalSaveRequested()`
```csharp
public Dictionary<string, string> BroadcastGlobalSaveRequested()
```
Broadcasts `OnGlobalSaveRequested()` to all components with `AutoRegisterForSave == true`. Returns dictionary of SaveId → serialized data.
##### `BroadcastGlobalRestoreRequested(Dictionary<string, string> saveData)`
```csharp
public void BroadcastGlobalRestoreRequested(Dictionary<string, string> saveData)
```
Distributes save data to components by matching `SaveId`, then broadcasts `OnGlobalLoadCompleted()`.
##### `BroadcastGlobalSaveStarted()`
```csharp
public void BroadcastGlobalSaveStarted()
```
Broadcasts `OnGlobalSaveStarted()` to all components with `AutoRegisterForSave == true`.
## LifecyclePhase (Enum)
**Namespace:** `Core.Lifecycle`
**Location:** `Assets/Scripts/Core/Lifecycle/LifecycleEnums.cs` → [View Source](../../Assets/Scripts/Core/Lifecycle/LifecycleEnums.cs)
Defines the different lifecycle phases for documentation and tooling purposes.
```csharp
public enum LifecyclePhase
{
ManagedAwake, // During registration (Awake)
ManagedStart, // After bootstrap or late registration
SceneUnloading, // Before scene unload
SceneReady, // After scene load
SaveRequested, // Before scene unload (save)
RestoreRequested, // After scene load (restore)
ManagedDestroy // During OnDestroy
}
```
---

View File

@@ -1,522 +0,0 @@
# ManagedBehaviour System - Quick Start & Use Cases
**TL;DR:** Inherit from `ManagedBehaviour` instead of `MonoBehaviour`. Override lifecycle hooks instead of Awake/OnDestroy. Get guaranteed initialization order and automatic registration.
---
## Table of Contents
1. [Lifecycle Methods Summary](#lifecycle-methods-summary)
2. [Quick Reference - Common Use Cases](#quick-reference---common-use-cases)
3. [Getting Started Examples](#getting-started-examples)
4. [Detailed Use Cases](#detailed-use-cases)
5. [Common Patterns](#common-patterns)
6. [Migration Checklist](#migration-checklist)
7. [Troubleshooting](#troubleshooting)
8. [Best Practices](#best-practices)
9. [FAQ](#faq)
---
## Summary
**ManagedBehaviour in 3 Sentences:**
Inherit from `ManagedBehaviour` to get automatic lifecycle management with guaranteed initialization order. Use `OnManagedStart()` to safely access manager singletons, and use built-in save/load hooks for persistence. The framework handles registration and cleanup automatically - you just override the hooks you need.
**When to Use:**
- ✅ Singleton managers
- ✅ Components that access managers
- ✅ Components that need save/load
- ✅ Components that need scene lifecycle events
**When to Skip:**
- ❌ Simple self-contained components
- ❌ Third-party code (can't change base class)
- ❌ Performance-critical code with no dependencies
**But can I still use regular MonoBehaviour?!** <br>
_Yes!_ Only use ManagedBehaviour when you need its features (manager access, save/load, etc.)
---
## Lifecycle Methods Summary
1. **OnManagedAwake()** - Called during Unity's Awake phase; use for **internal setup** and GetComponent calls.
2. **OnManagedStart()** - Called after bootstrap completes; **safe to access** manager singletons.
3. **OnSceneReady()** - Called after scene finishes loading; use for scene-specific initialization.
4. **OnSceneUnloading()** - Called before scene unloads; use for scene cleanup.
5. **OnSceneSaveRequested()** - Returns serialized scene-specific data before scene transitions.
6. **OnSceneRestoreRequested(data)** - Receives saved scene data after scene loads.
7. **OnSceneRestoreCompleted()** - Called after all scene restore operations complete.
8. **OnGlobalSaveRequested()** - Returns serialized persistent data when game saves.
9. **OnGlobalRestoreRequested(data)** - Receives persistent data on game boot.
10. **OnGlobalLoadCompleted()** - Called after all global restore operations complete on boot.
11. **OnGlobalSaveStarted()** - Called before save file is written; use for pre-save validation.
12. **OnManagedDestroy()** - Called during OnDestroy; use for cleanup and event unsubscription.
---
## Quick Reference - Common Use Cases
### Access Manager Singleton Safely
```csharp
internal override void OnManagedStart()
{
GameManager.Instance.DoSomething(); // Safe - managers guaranteed ready
}
```
### Create Singleton Manager
```csharp
private static MyManager _instance;
public static MyManager Instance => _instance;
internal override void OnManagedAwake()
{
_instance = this;
}
```
### Save Scene Progress
```csharp
public override bool AutoRegisterForSave => true;
internal override string OnSceneSaveRequested()
{
return JsonUtility.ToJson(myData);
}
internal override void OnSceneRestoreRequested(string data)
{
myData = JsonUtility.FromJson<MyData>(data);
}
```
### Auto-Register as Pausable
```csharp
public class MyComponent : ManagedBehaviour, IPausable
{
public override bool AutoRegisterPausable => true;
public void Pause() { /* pause logic */ }
public void Resume() { /* resume logic */ }
}
```
### Scene-Specific Initialization
```csharp
internal override void OnSceneReady()
{
FindObjectsInScene();
InitializeLevel();
}
```
### Cleanup on Destroy
```csharp
internal override void OnManagedDestroy()
{
EventBus.OnSomething -= HandleEvent;
}
```
---
## Getting Started Examples
### 1. Basic Component
```csharp
using Core.Lifecycle;
public class MyComponent : ManagedBehaviour
{
internal override void OnManagedAwake()
{
// Early initialization (singleton setup, GetComponent)
Debug.Log("MyComponent awakened");
}
internal override void OnManagedStart()
{
// Late initialization (safe to access managers)
Debug.Log("MyComponent started - managers are ready");
}
}
```
### 2. Singleton Manager
```csharp
using Core.Lifecycle;
public class MyManager : ManagedBehaviour
{
private static MyManager _instance;
public static MyManager Instance => _instance;
internal override void OnManagedAwake()
{
_instance = this; // Set singleton early
}
internal override void OnManagedStart()
{
// All other managers are ready here
AudioManager.Instance.PlaySound("ManagerReady");
}
}
```
### 3. Component with Save/Load
```csharp
using Core.Lifecycle;
public class PuzzleComponent : ManagedBehaviour
{
public override bool AutoRegisterForSave => true;
public override string SaveId => "MyPuzzle"; // Custom ID
private bool _isSolved;
internal override string OnSceneSaveRequested()
{
return JsonUtility.ToJson(new { isSolved = _isSolved });
}
internal override void OnSceneRestoreRequested(string data)
{
var saveData = JsonUtility.FromJson<SaveData>(data);
_isSolved = saveData.isSolved;
}
[System.Serializable]
private class SaveData
{
public bool isSolved;
}
}
```
### 4. Component with Cleanup
```csharp
using Core.Lifecycle;
public class EventSubscriber : ManagedBehaviour
{
internal override void OnManagedStart()
{
GameManager.Instance.OnGamePaused += HandlePause;
}
internal override void OnManagedDestroy()
{
// Automatic cleanup
if (GameManager.Instance != null)
GameManager.Instance.OnGamePaused -= HandlePause;
}
private void HandlePause() { }
}
```
---
## Detailed Use Cases
### Use Case 1: Accessing Singleton Managers Safely
**Problem:** Accessing `GameManager.Instance` in `Awake()` might fail if GameManager hasn't initialized yet.
**Solution:**
```csharp
public class Player : ManagedBehaviour
{
// ❌ DON'T: Risky in OnManagedAwake (managers may not be ready)
internal override void OnManagedAwake()
{
// var settings = GameManager.Instance.GetSettings(); // RISKY!
}
// ✅ DO: Safe in OnManagedStart (managers guaranteed ready)
internal override void OnManagedStart()
{
var settings = GameManager.Instance.GetSettings(); // SAFE!
ApplySettings(settings);
}
}
```
---
### Use Case 2: Scene-Specific Initialization
**Problem:** Need to initialize something after a scene finishes loading.
**Solution:**
```csharp
public class LevelManager : ManagedBehaviour
{
internal override void OnSceneReady()
{
// Scene is fully loaded, all objects exist
FindObjectiveMarkers();
SpawnEnemies();
PlayLevelMusic();
}
}
```
---
### Use Case 3: Saving Player Progress
**Problem:** Need to save level progress when transitioning between scenes.
**Solution:**
```csharp
public class LevelProgress : ManagedBehaviour
{
public override bool AutoRegisterForSave => true;
public override string SaveId => "LevelProgress";
private int _checkpointIndex;
private float _timeElapsed;
internal override string OnSceneSaveRequested()
{
return JsonUtility.ToJson(new
{
checkpoint = _checkpointIndex,
time = _timeElapsed
});
}
internal override void OnSceneRestoreRequested(string data)
{
var save = JsonUtility.FromJson<SaveData>(data);
_checkpointIndex = save.checkpoint;
_timeElapsed = save.time;
}
internal override void OnSceneRestoreCompleted()
{
// Restore complete, trigger UI update
UpdateProgressUI();
}
}
```
---
### Use Case 4: Global Persistent Data (Inventory)
**Problem:** Need to save player inventory across all scenes.
**Solution:**
```csharp
public class InventoryManager : ManagedBehaviour
{
public override bool AutoRegisterForSave => true;
public override string SaveId => "Inventory";
private List<string> _items = new List<string>();
internal override string OnGlobalSaveRequested()
{
// Save to global save file (not scene-specific)
return JsonUtility.ToJson(new { items = _items });
}
internal override void OnGlobalRestoreRequested(string data)
{
// Restore from global save file on boot
var save = JsonUtility.FromJson<SaveData>(data);
_items = new List<string>(save.items);
}
internal override void OnGlobalLoadCompleted()
{
// All global data loaded, safe to initialize UI
RefreshInventoryUI();
}
}
```
---
### Use Case 5: Auto-Registering as Pausable
**Problem:** Component implements `IPausable` and needs to register with `GameManager`.
**Solution:**
```csharp
public class AnimatedCharacter : ManagedBehaviour, IPausable
{
public override bool AutoRegisterPausable => true;
// IPausable implementation
public void Pause()
{
// Pause animations
}
public void Resume()
{
// Resume animations
}
}
// No manual registration needed - automatic!
```
---
### Use Case 6: Scene Cleanup
**Problem:** Need to clean up scene-specific state before transitioning.
**Solution:**
```csharp
public class ParticleSpawner : ManagedBehaviour
{
private List<GameObject> _activeParticles = new List<GameObject>();
internal override void OnSceneUnloading()
{
// Clean up before scene unloads
foreach (var particle in _activeParticles)
{
if (particle != null)
Destroy(particle);
}
_activeParticles.Clear();
}
}
```
---
### Use Case 7: First-Time vs Restored State
**Problem:** Need to play intro animation only on first visit, not when restoring from save.
**Solution:**
```csharp
public class LevelIntro : ManagedBehaviour
{
public override bool AutoRegisterForSave => true;
private bool _hasPlayedIntro;
internal override string OnSceneSaveRequested()
{
return JsonUtility.ToJson(new { hasPlayed = _hasPlayedIntro });
}
internal override void OnSceneRestoreRequested(string data)
{
var save = JsonUtility.FromJson<SaveData>(data);
_hasPlayedIntro = save.hasPlayed;
}
internal override void OnSceneRestoreCompleted()
{
// This fires whether we restored or not
if (!_hasPlayedIntro)
{
PlayIntroAnimation();
_hasPlayedIntro = true;
}
}
}
```
---
## Common Patterns
### Pattern: Manager Singleton
```csharp
public class MyManager : ManagedBehaviour
{
private static MyManager _instance;
public static MyManager Instance => _instance;
internal override void OnManagedAwake()
{
_instance = this;
}
}
```
### Pattern: Event Subscription
```csharp
internal override void OnManagedStart()
{
EventBus.OnSomething += HandleEvent;
}
internal override void OnManagedDestroy()
{
EventBus.OnSomething -= HandleEvent;
}
```
### Pattern: Save/Restore
```csharp
public override bool AutoRegisterForSave => true;
internal override string OnSceneSaveRequested()
{
return JsonUtility.ToJson(myData);
}
internal override void OnSceneRestoreRequested(string data)
{
myData = JsonUtility.FromJson<MyData>(data);
}
```
### Pattern: Custom SaveId
```csharp
// For singletons or special cases
public override string SaveId => "PlayerInventory";
// For scene-specific instances
public override string SaveId => $"{gameObject.scene.name}/MySpecialObject";
```
## Troubleshooting
### "NullReferenceException when accessing Manager.Instance"
**Problem:** Accessing singleton in `OnManagedAwake()`
**Solution:** Move to `OnManagedStart()` where managers are guaranteed ready
### "My SaveId is colliding with another component"
**Problem:** Two components have same GameObject name and type
**Solution:** Override `SaveId` property with unique value
### "My component isn't receiving lifecycle callbacks"
**Problem:** Not inheriting from `ManagedBehaviour`
**Solution:** Ensure class inherits `ManagedBehaviour` (not plain `MonoBehaviour`)
### "Save data isn't persisting"
**Problem:** `AutoRegisterForSave` is false
**Solution:** Set `public override bool AutoRegisterForSave => true;`
### "OnSceneRestoreCompleted isn't firing"
**Problem:** Missing implementation
**Solution:** Override the method even if just logging for now
---
**Quick Links:**
- [Technical Reference](01_technical_reference.md) - Complete API documentation
- [Architecture Overview](02_architecture_overview.md) - System design and principles

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

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