Compare commits
103 Commits
UIMainScre
...
HappyWheel
| Author | SHA1 | Date | |
|---|---|---|---|
| 31522cda01 | |||
|
|
213e894640 | ||
| dea21e2af5 | |||
| 4f9aa12cad | |||
|
|
abc5801b18 | ||
|
|
f427dbb6e7 | ||
|
|
2d5b6fae60 | ||
|
|
3b2a5a0677 | ||
| e776b9476e | |||
| 37797d5f7d | |||
| fed08d08ad | |||
| 843c121666 | |||
| a15a9790b5 | |||
| dfec8df767 | |||
| 5b8e4eaeac | |||
| 9571be257c | |||
| 03c0b158a4 | |||
| b0945c9bdb | |||
| 28a81b6014 | |||
| ab6938e6cf | |||
| c5703fc92a | |||
|
|
e4970c738f | ||
| 82c120f362 | |||
| 099f3ec939 | |||
|
|
bd646df706 | ||
|
|
8ab0a350f3 | ||
| e0a4cf31b2 | |||
| 1aa3d2a787 | |||
|
|
f9d2a951da | ||
|
|
059cfa6d28 | ||
|
|
f2931c1c0b | ||
|
|
176bb7a704 | ||
| d7211e62de | |||
|
|
2c13a899d7 | ||
| a2a55a7135 | |||
| a5e657ef05 | |||
| 2040b59593 | |||
| 8bec2f0cf8 | |||
|
|
9a99405f4b | ||
|
|
b4b746de25 | ||
|
|
d2e3adbacc | ||
| 023bddc91b | |||
| c8d8b6b802 | |||
| 1082fc9ad0 | |||
| 2ae5d28cc9 | |||
|
|
dd5aefcb49 | ||
|
|
6d660f5d89 | ||
| 37c6d7a552 | |||
| e55aa6b258 | |||
| d7838c0a04 | |||
| 90fd3514fb | |||
| 9a736e1d53 | |||
| 2f86bab336 | |||
| 8948cbdb14 | |||
|
|
207f997254 | ||
| f9ceea4992 | |||
|
|
d886f97e14 | ||
| e0b808faed | |||
| 666f731b6d | |||
|
|
3ac90ed7b6 | ||
| 700e6bfbfc | |||
| ab6263cb10 | |||
| f800e78f14 | |||
| e29581cc21 | |||
| 208696487e | |||
| 3879c0879d | |||
| 1de91b0d57 | |||
| b0e90221dc | |||
| dcb1066d80 | |||
| d80ac111c2 | |||
| b872b52632 | |||
| 4fdfdea5cf | |||
| 32c7589ab3 | |||
|
|
c11ca05ea8 | ||
| 44155796d0 | |||
| 5b166244b2 | |||
| 114a0d3997 | |||
| ce6e4450e6 | |||
| be595da357 | |||
| 95f2f63259 | |||
| dc5ed7d49f | |||
|
|
2fadf819cc | ||
| a1465de9a0 | |||
| a1b40ad102 | |||
| ff9a2cebd3 | |||
| f7926a218e | |||
| 9defaa314a | |||
| e14a3ddf2b | |||
| 5bd6eabec6 | |||
| f2ebd125f3 | |||
| 2fdfabe2b8 | |||
| 4a84e729f3 | |||
| 055e8aa426 | |||
| 1957d26b1f | |||
| 8f62dc8873 | |||
| 4b8e4c69f5 | |||
| e086bedb19 | |||
| 13300e885b | |||
| a73f75ffa4 | |||
| e9beb05083 | |||
| 7294466604 | |||
| 67d3ee76c1 | |||
|
|
9f71b6a84a |
4
.gitignore
vendored
@@ -186,7 +186,6 @@ StyleCopReport.xml
|
|||||||
*_p.c
|
*_p.c
|
||||||
*_h.h
|
*_h.h
|
||||||
*.ilk
|
*.ilk
|
||||||
*.meta
|
|
||||||
*.obj
|
*.obj
|
||||||
*.iobj
|
*.iobj
|
||||||
*.pch
|
*.pch
|
||||||
@@ -305,7 +304,6 @@ PublishScripts/
|
|||||||
# NuGet Symbol Packages
|
# NuGet Symbol Packages
|
||||||
*.snupkg
|
*.snupkg
|
||||||
# The packages folder can be ignored because of Package Restore
|
# The packages folder can be ignored because of Package Restore
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
# except build/, which is used as an MSBuild target.
|
||||||
!**/[Pp]ackages/build/
|
!**/[Pp]ackages/build/
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
@@ -501,3 +499,5 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/unity,visualstudiocode,visualstudio,vim
|
# End of https://www.toptal.com/developers/gitignore/api/unity,visualstudiocode,visualstudio,vim
|
||||||
|
|
||||||
|
|
||||||
|
.utmp/
|
||||||
|
|||||||
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"visualstudiotoolsforunity.vstuc"
|
||||||
|
]
|
||||||
|
}
|
||||||
10
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Attach to Unity",
|
||||||
|
"type": "vstuc",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
72
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.DS_Store": true,
|
||||||
|
"**/.git": true,
|
||||||
|
"**/.vs": true,
|
||||||
|
"**/.gitmodules": true,
|
||||||
|
"**/.vsconfig": true,
|
||||||
|
"**/*.booproj": true,
|
||||||
|
"**/*.pidb": true,
|
||||||
|
"**/*.suo": true,
|
||||||
|
"**/*.user": true,
|
||||||
|
"**/*.userprefs": true,
|
||||||
|
"**/*.unityproj": true,
|
||||||
|
"**/*.dll": true,
|
||||||
|
"**/*.exe": true,
|
||||||
|
"**/*.pdf": true,
|
||||||
|
"**/*.mid": true,
|
||||||
|
"**/*.midi": true,
|
||||||
|
"**/*.wav": true,
|
||||||
|
"**/*.gif": true,
|
||||||
|
"**/*.ico": true,
|
||||||
|
"**/*.jpg": true,
|
||||||
|
"**/*.jpeg": true,
|
||||||
|
"**/*.png": true,
|
||||||
|
"**/*.psd": true,
|
||||||
|
"**/*.tga": true,
|
||||||
|
"**/*.tif": true,
|
||||||
|
"**/*.tiff": true,
|
||||||
|
"**/*.3ds": true,
|
||||||
|
"**/*.3DS": true,
|
||||||
|
"**/*.fbx": true,
|
||||||
|
"**/*.FBX": true,
|
||||||
|
"**/*.lxo": true,
|
||||||
|
"**/*.LXO": true,
|
||||||
|
"**/*.ma": true,
|
||||||
|
"**/*.MA": true,
|
||||||
|
"**/*.obj": true,
|
||||||
|
"**/*.OBJ": true,
|
||||||
|
"**/*.asset": true,
|
||||||
|
"**/*.cubemap": true,
|
||||||
|
"**/*.flare": true,
|
||||||
|
"**/*.mat": true,
|
||||||
|
"**/*.meta": true,
|
||||||
|
"**/*.prefab": true,
|
||||||
|
"**/*.unity": true,
|
||||||
|
"build/": true,
|
||||||
|
"Build/": true,
|
||||||
|
"Library/": true,
|
||||||
|
"library/": true,
|
||||||
|
"obj/": true,
|
||||||
|
"Obj/": true,
|
||||||
|
"Logs/": true,
|
||||||
|
"logs/": true,
|
||||||
|
"ProjectSettings/": true,
|
||||||
|
"UserSettings/": true,
|
||||||
|
"temp/": true,
|
||||||
|
"Temp/": true
|
||||||
|
},
|
||||||
|
"files.associations": {
|
||||||
|
"*.asset": "yaml",
|
||||||
|
"*.meta": "yaml",
|
||||||
|
"*.prefab": "yaml",
|
||||||
|
"*.unity": "yaml",
|
||||||
|
},
|
||||||
|
"explorer.fileNesting.enabled": true,
|
||||||
|
"explorer.fileNesting.patterns": {
|
||||||
|
"*.sln": "*.csproj",
|
||||||
|
"*.slnx": "*.csproj"
|
||||||
|
},
|
||||||
|
"dotnet.defaultSolution": "GeoSusGame.slnx",
|
||||||
|
"dotnet.enableWorkspaceBasedDevelopment": false
|
||||||
|
}
|
||||||
BIN
Assets/2026-04-26 13-58-02.mp3.mkv
Normal file
7
Assets/2026-04-26 13-58-02.mp3.mkv.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d6a21cbd9c8f68f4fbe65763566ca7c9
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/2026-04-26 13-58-02.mp3.mp3
Normal file
23
Assets/2026-04-26 13-58-02.mp3.mp3.meta
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6ab90ac2243447c478fbb930f101d94a
|
||||||
|
AudioImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 8
|
||||||
|
defaultSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
loadType: 0
|
||||||
|
sampleRateSetting: 0
|
||||||
|
sampleRateOverride: 44100
|
||||||
|
compressionFormat: 1
|
||||||
|
quality: 1
|
||||||
|
conversionMode: 0
|
||||||
|
preloadAudioData: 0
|
||||||
|
platformSettingOverrides: {}
|
||||||
|
forceToMono: 0
|
||||||
|
normalize: 1
|
||||||
|
loadInBackground: 0
|
||||||
|
ambisonic: 0
|
||||||
|
3D: 1
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/202604261352.mp4
Normal file
18
Assets/202604261352.mp4.meta
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4c92126c07bfe7e4f8eea9bf78d8f29f
|
||||||
|
VideoClipImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 3
|
||||||
|
frameRange: 0
|
||||||
|
startFrame: -1
|
||||||
|
endFrame: -1
|
||||||
|
colorSpace: 0
|
||||||
|
deinterlace: 0
|
||||||
|
encodeAlpha: 0
|
||||||
|
flipVertical: 0
|
||||||
|
flipHorizontal: 0
|
||||||
|
importAudio: 1
|
||||||
|
targetSettings: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Adaptive Performance.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7eaf47040f6a6ba4bb9df4eab675de30
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &-4008054574566821997
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 2
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 536372c49e1ca914d822849d36de938c, type: 3}
|
||||||
|
m_Name: Standalone Providers
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_AutomaticLoading: 0
|
||||||
|
m_AutomaticRunning: 0
|
||||||
|
m_Loaders: []
|
||||||
|
--- !u!114 &-1024531111154556285
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 2
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 179fc3111e144bc4688dca4038b3265d, type: 3}
|
||||||
|
m_Name: Standalone Settings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_LoaderManagerInstance: {fileID: -4008054574566821997}
|
||||||
|
m_InitManagerOnStart: 1
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 2
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: cb0ece14d1f711a4fb9325ca819dee95, type: 3}
|
||||||
|
m_Name: AdaptivePerformanceGeneralSettings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
Keys: 01000000
|
||||||
|
Values:
|
||||||
|
- {fileID: -1024531111154556285}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 83147ac123bf5e149ba22b1e8722b8a2
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Adaptive Performance/Settings.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a369fde97a303eb4ebfe7de3af10fac4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 2
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: b592865877cb54284a5d1d88aec9cfbb, type: 3}
|
||||||
|
m_Name: Simulator Provider Settings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_Logging: 1
|
||||||
|
m_AutomaticPerformanceModeEnabled: 1
|
||||||
|
m_AutomaticGameModeEnabled: 0
|
||||||
|
m_EnableBoostOnStartup: 1
|
||||||
|
m_StatsLoggingFrequencyInFrames: 50
|
||||||
|
m_IndexerSettings:
|
||||||
|
m_Active: 1
|
||||||
|
m_ThermalActionDelay: 10
|
||||||
|
m_PerformanceActionDelay: 4
|
||||||
|
m_ScalerSettings:
|
||||||
|
m_AdaptiveFramerate:
|
||||||
|
m_Name: Adaptive Framerate
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 7
|
||||||
|
m_MaxLevel: 45
|
||||||
|
m_MinBound: 15
|
||||||
|
m_MaxBound: 60
|
||||||
|
m_AdaptiveResolution:
|
||||||
|
m_Name: Adaptive Resolution
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 6
|
||||||
|
m_MaxLevel: 9
|
||||||
|
m_MinBound: 0.5
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveBatching:
|
||||||
|
m_Name: Adaptive Batching
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveLOD:
|
||||||
|
m_Name: Adaptive LOD
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0.4
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveLut:
|
||||||
|
m_Name: Adaptive Lut
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 3
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveMSAA:
|
||||||
|
m_Name: Adaptive MSAA
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 6
|
||||||
|
m_MaxLevel: 2
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowCascade:
|
||||||
|
m_Name: Adaptive Shadow Cascade
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 3
|
||||||
|
m_MaxLevel: 2
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowDistance:
|
||||||
|
m_Name: Adaptive Shadow Distance
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0.15
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowmapResolution:
|
||||||
|
m_Name: Adaptive Shadowmap Resolution
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0.15
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowQuality:
|
||||||
|
m_Name: Adaptive Shadow Quality
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 3
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveSorting:
|
||||||
|
m_Name: Adaptive Sorting
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveTransparency:
|
||||||
|
m_Name: Adaptive Transparency
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveViewDistance:
|
||||||
|
m_Name: Adaptive View Distance
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 40
|
||||||
|
m_MinBound: 50
|
||||||
|
m_MaxBound: 1000
|
||||||
|
m_AdaptivePhysics:
|
||||||
|
m_Name: Adaptive Physics
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 5
|
||||||
|
m_MinBound: 0.5
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveDecals:
|
||||||
|
m_Name: Adaptive Decals
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 20
|
||||||
|
m_MinBound: 0.01
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveLayerCulling:
|
||||||
|
m_Name: Adaptive Layer Culling
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 40
|
||||||
|
m_MinBound: 0.01
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_scalerProfileList:
|
||||||
|
- m_AdaptiveFramerate:
|
||||||
|
m_Name: Adaptive Framerate
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 7
|
||||||
|
m_MaxLevel: 45
|
||||||
|
m_MinBound: 15
|
||||||
|
m_MaxBound: 60
|
||||||
|
m_AdaptiveResolution:
|
||||||
|
m_Name: Adaptive Resolution
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 6
|
||||||
|
m_MaxLevel: 9
|
||||||
|
m_MinBound: 0.5
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveBatching:
|
||||||
|
m_Name: Adaptive Batching
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveLOD:
|
||||||
|
m_Name: Adaptive LOD
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0.4
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveLut:
|
||||||
|
m_Name: Adaptive Lut
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 3
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveMSAA:
|
||||||
|
m_Name: Adaptive MSAA
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 6
|
||||||
|
m_MaxLevel: 2
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowCascade:
|
||||||
|
m_Name: Adaptive Shadow Cascade
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 3
|
||||||
|
m_MaxLevel: 2
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowDistance:
|
||||||
|
m_Name: Adaptive Shadow Distance
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0.15
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowmapResolution:
|
||||||
|
m_Name: Adaptive Shadowmap Resolution
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0.15
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveShadowQuality:
|
||||||
|
m_Name: Adaptive Shadow Quality
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 3
|
||||||
|
m_MaxLevel: 3
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveSorting:
|
||||||
|
m_Name: Adaptive Sorting
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveTransparency:
|
||||||
|
m_Name: Adaptive Transparency
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 1
|
||||||
|
m_MinBound: 0
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveViewDistance:
|
||||||
|
m_Name: Adaptive View Distance
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 2
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 40
|
||||||
|
m_MinBound: 50
|
||||||
|
m_MaxBound: 1000
|
||||||
|
m_AdaptivePhysics:
|
||||||
|
m_Name: Adaptive Physics
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 0
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 5
|
||||||
|
m_MinBound: 0.5
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveDecals:
|
||||||
|
m_Name: Adaptive Decals
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 2
|
||||||
|
m_MaxLevel: 20
|
||||||
|
m_MinBound: 0.01
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_AdaptiveLayerCulling:
|
||||||
|
m_Name: Adaptive Layer Culling
|
||||||
|
m_Enabled: 0
|
||||||
|
m_Scale: 1
|
||||||
|
m_VisualImpact: 1
|
||||||
|
m_Target: 1
|
||||||
|
m_MaxLevel: 40
|
||||||
|
m_MinBound: 0.01
|
||||||
|
m_MaxBound: 1
|
||||||
|
m_Name: Default Scaler Profile
|
||||||
|
m_DefaultScalerProfilerIndex: 0
|
||||||
|
k_AssetVersion: 2
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a9fb757fd9f29fb4f9be1be9c492abbc
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 156 KiB |
@@ -1,226 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
<title>Rounded Rect Mask 2D</title>
|
|
||||||
<link href="https://fonts.cdnfonts.com/css/inter" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
/* outline: 1px solid red; */
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* THESE ENSURE THE WEBVIEW WILL BE OF CORRECT HEIGHT
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
html {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
font-family: 'Inter';
|
|
||||||
background-color: #F7F5EB;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body :first-child {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body :last-child {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXES SCROLLING
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MAIN STYLING
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--default-text-color: #333333;
|
|
||||||
--muted-text-color: #666666;
|
|
||||||
--link-color: #f05675;
|
|
||||||
|
|
||||||
--muted-border-color: #dddddd;
|
|
||||||
--muted-background-color: #eeeeee;
|
|
||||||
|
|
||||||
--codeblock-background-color: #222222;
|
|
||||||
--codeblock-text-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 16px;
|
|
||||||
|
|
||||||
line-height: 1.5rem;
|
|
||||||
color: var(--default-text-color);
|
|
||||||
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 1.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 1.05rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2 {
|
|
||||||
padding-bottom: 0.4rem;
|
|
||||||
border-bottom: 1px solid var(--muted-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
padding-inline-start: 1.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration-thickness: 1px;
|
|
||||||
text-underline-offset: 2px;
|
|
||||||
color: var(--link-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
del {
|
|
||||||
color: var(--muted-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
color: var(--codeblock-text-color);
|
|
||||||
background-color: var(--codeblock-background-color);
|
|
||||||
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(pre)>code {
|
|
||||||
background-color: var(--muted-background-color);
|
|
||||||
padding: 0.1rem 0.4rem;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin-inline: 0px;
|
|
||||||
padding-block: 12px;
|
|
||||||
padding-left: 16px;
|
|
||||||
|
|
||||||
color: var(--muted-text-color);
|
|
||||||
border-left: 2px solid var(--muted-border-color);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote :last-child {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
table {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
padding: 0.6rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border: 1px solid var(--muted-border-color)
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
font-size: 1rem;
|
|
||||||
background-color: var(--muted-background-color);
|
|
||||||
border-bottom-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1 id="rounded-rect-mask-2d">Rounded Rect Mask 2D</h1>
|
|
||||||
<p><img src="./Resources/RRM2D_Banner.png" alt="screenshot" /><br />
|
|
||||||
available on the <a href="https://assetstore.unity.com/packages/slug/326028" rel="noopener noreferrer" target="_blank">Asset Store</a></p>
|
|
||||||
<h2 id="documentation">Documentation</h2>
|
|
||||||
<p><img src="./Resources/RRM2D_ScreenshotScene.png" alt="screenshot" /></p>
|
|
||||||
<h3 id="basics">Basics</h3>
|
|
||||||
<ul>
|
|
||||||
<li>All Graphics (<a href="https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/comp-UIVisual.html" rel="noopener noreferrer" target="_blank">Visuals</a> & <a href="https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/comp-UIInteraction.html" rel="noopener noreferrer" target="_blank">Interactions</a>) components child of the Rounded Mask need the <em>Default UI (Rounded Mask)</em> material to work.</li>
|
|
||||||
<li>All TextMeshPro texts need the <em>TMP SDF-Mobile (Rounded Mask)</em> material inside the TMP_FontAsset that needs to work with the rounded corners. If your text is only clipped on the straight edges of the mask, they don't need to be changed.</li>
|
|
||||||
<li>You will need one <em>Default UI (Rounded Mask)</em> material per RoundedRectMask2D radius configuration. If you always use the same corners throughout your project, you can use the Material provided in the package. Same for the TMP_FontAssets.</li>
|
|
||||||
<li>If you have different radii in your project, either <strong>duplicate the materials</strong>, or tick the checkbox <strong>Clone maskable materials on Start</strong> to automatically create new instances of the materials (for both Default UI & TMP_FontAsset's material).</li>
|
|
||||||
</ul>
|
|
||||||
<h3 id="limitations">Limitations</h3>
|
|
||||||
<ul>
|
|
||||||
<li>As Rounded RectMask2D uses a special UI shader to work, it can only be used with normal UI (ie: Images with no special material). If you have custom UI shaders for your UI you will need to tweak them to support the RoundedRectMask2D component (otherwise the corners will not be correctly clipped).</li>
|
|
||||||
<li>Moreover, you will need one Material per corners configuration. If you always use the same values, you can use one Material for all of your UI, but otherwise, RoundedRectMask2D can auto instantiate your materials at runtime, allowing you to have any mask values.</li>
|
|
||||||
<li>Softness doesn't work with RoundedRectMask2D. If you need softness in your mask, you will have to use both RoundedRectMask2D & a regular RectMask2D nested into each other.</li>
|
|
||||||
</ul>
|
|
||||||
<h3 id="custom-shaders">Custom Shaders</h3>
|
|
||||||
<p>If you have custom UI shaders and would like them to work with the RoundedRectMask2D component, you will need to edit them, to support the feature. You can check how it's done in the custom Shaders (UI Default & TMP_SDF Mobile). <br />
|
|
||||||
Here is what you need to do:</p>
|
|
||||||
<ol>
|
|
||||||
<li>Either move your shaders to the AntoineCherel/RoundedRectMask2D/Shaders folder, or copy & paste the <em>RoundedMaskCommon.hlsl</em> library to your custom shader's folder</li>
|
|
||||||
<li>in the <em>Pass</em>, include the library <br />
|
|
||||||
<code>#include "RoundedMaskCommon.hlsl"</code></li>
|
|
||||||
<li>in the Vertex to Fragment or Vertext to Pixel struct, add <br />
|
|
||||||
<code>float3 posLocal : TEXCOORD8;</code></li>
|
|
||||||
<li>fill it in the V2F or V2P function <br />
|
|
||||||
<code>OUT.posLocal = v.vertex.xyz;</code></li>
|
|
||||||
<li>in the frag or pixel return function, replace the existing clipping method by<br />
|
|
||||||
<code>clip( RMUnityUIClipRect(IN.posLocal.xy, _ClipRect, _ClipRectRadii) - 0.5);</code><br />
|
|
||||||
all of these added lines should be encapsulated inside <code>#ifdef UNITY_UI_CLIP_RECT</code> statements.</li>
|
|
||||||
</ol>
|
|
||||||
<p>If something goes wrong double check with the implemented shaders inside the Shaders folder.<br />
|
|
||||||
You can also reach out to me contact (at) antoinecherel.dev</p>
|
|
||||||
<h2 id="credits">Credits</h2>
|
|
||||||
<p>Unity Plugin, developped by <a href="https://www.antoinecherel.dev/" target="_blank">Antoine Cherel</a></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
Before Width: | Height: | Size: 529 B |
@@ -1,124 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEditor.UI;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace ACRoundedRectMask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Custom editor for the RoundedRectMask2D component.
|
|
||||||
/// </summary>
|
|
||||||
[CustomEditor(typeof(RoundedRectMask2D), true)]
|
|
||||||
[CanEditMultipleObjects]
|
|
||||||
public class RoundedRectMask2DInspector : RectMask2DEditor
|
|
||||||
{
|
|
||||||
private static GUIContent radiusContent = new GUIContent("Corner Radius", "Determines the radius of the rect mask's corners.");
|
|
||||||
private static GUIContent radiiContent = new GUIContent("Corner Radii", "Determines the radius of each corner of the rect mask.");
|
|
||||||
private static GUIContent topLeftContent = new GUIContent("Top Left", "Local -X, +Y corner.");
|
|
||||||
private static GUIContent topRightContent = new GUIContent("Top Right", "Local +X, +Y corner.");
|
|
||||||
private static GUIContent bottomLeftContent = new GUIContent("Bottom Left", "Local -X, -Y corner.");
|
|
||||||
private static GUIContent bottomRightContent = new GUIContent("Bottom Right", "Local +X, -Y corner.");
|
|
||||||
private static bool showRadii = false;
|
|
||||||
|
|
||||||
private static GUIContent paddingContent = new GUIContent("Padding");
|
|
||||||
private static GUIContent leftContent = new GUIContent("Left");
|
|
||||||
private static GUIContent rightContent = new GUIContent("Right");
|
|
||||||
private static GUIContent topContent = new GUIContent("Top");
|
|
||||||
private static GUIContent bottomContent = new GUIContent("Bottom");
|
|
||||||
private static bool showOffsets = false;
|
|
||||||
|
|
||||||
private SerializedProperty independentRadii;
|
|
||||||
private SerializedProperty radii;
|
|
||||||
private SerializedProperty padding;
|
|
||||||
private SerializedProperty cloneMaterials;
|
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override void OnEnable()
|
|
||||||
{
|
|
||||||
independentRadii = serializedObject.FindProperty("independantRadii");
|
|
||||||
radii = serializedObject.FindProperty("radii");
|
|
||||||
padding = serializedObject.FindProperty("m_Padding");
|
|
||||||
cloneMaterials = serializedObject.FindProperty("cloneMaskableMaterialsOnStart");
|
|
||||||
|
|
||||||
base.OnEnable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Renders a custom inspector GUI that displays radius options and hides softness properties.
|
|
||||||
/// </summary>
|
|
||||||
public override void OnInspectorGUI()
|
|
||||||
{
|
|
||||||
EditorGUILayout.PropertyField(independentRadii);
|
|
||||||
|
|
||||||
if (independentRadii.boolValue)
|
|
||||||
{
|
|
||||||
showRadii = EditorGUILayout.Foldout(showRadii, radiiContent, true);
|
|
||||||
|
|
||||||
|
|
||||||
if (showRadii)
|
|
||||||
{
|
|
||||||
using (var check = new EditorGUI.ChangeCheckScope())
|
|
||||||
{
|
|
||||||
EditorGUI.indentLevel++;
|
|
||||||
Vector4 newRadii = radii.vector4Value;
|
|
||||||
|
|
||||||
newRadii.x = Mathf.Max(0.0f, EditorGUILayout.FloatField(topLeftContent, newRadii.x));
|
|
||||||
newRadii.y = Mathf.Max(0.0f, EditorGUILayout.FloatField(topRightContent, newRadii.y));
|
|
||||||
newRadii.z = Mathf.Max(0.0f, EditorGUILayout.FloatField(bottomLeftContent, newRadii.z));
|
|
||||||
newRadii.w = Mathf.Max(0.0f, EditorGUILayout.FloatField(bottomRightContent, newRadii.w));
|
|
||||||
|
|
||||||
if (check.changed)
|
|
||||||
{
|
|
||||||
radii.vector4Value = newRadii;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using (var check = new EditorGUI.ChangeCheckScope())
|
|
||||||
{
|
|
||||||
float newRadius = radii.vector4Value.x;
|
|
||||||
|
|
||||||
newRadius = Mathf.Max(0.0f, EditorGUILayout.FloatField(radiusContent, newRadius));
|
|
||||||
|
|
||||||
if (check.changed)
|
|
||||||
{
|
|
||||||
Vector4 newRadii = radii.vector4Value;
|
|
||||||
newRadii = Vector4.one * newRadius;
|
|
||||||
radii.vector4Value = newRadii;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rather than call base.OnInspectorGUI() manually showing padding so that softness can be hidden since it is not supported by rounded rect masks.
|
|
||||||
showOffsets = EditorGUILayout.Foldout(showOffsets, paddingContent, true);
|
|
||||||
|
|
||||||
if (showOffsets)
|
|
||||||
{
|
|
||||||
using (var check = new EditorGUI.ChangeCheckScope())
|
|
||||||
{
|
|
||||||
EditorGUI.indentLevel++;
|
|
||||||
Vector4 newPadding = padding.vector4Value;
|
|
||||||
|
|
||||||
newPadding.x = EditorGUILayout.FloatField(leftContent, newPadding.x);
|
|
||||||
newPadding.z = EditorGUILayout.FloatField(rightContent, newPadding.z);
|
|
||||||
newPadding.w = EditorGUILayout.FloatField(topContent, newPadding.w);
|
|
||||||
newPadding.y = EditorGUILayout.FloatField(bottomContent, newPadding.y);
|
|
||||||
|
|
||||||
if (check.changed)
|
|
||||||
{
|
|
||||||
padding.vector4Value = newPadding;
|
|
||||||
}
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUILayout.PropertyField(cloneMaterials);
|
|
||||||
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,359 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using System.Reflection;
|
|
||||||
using TMPro;
|
|
||||||
|
|
||||||
|
|
||||||
#if UNITY_2021_1_OR_NEWER
|
|
||||||
using UnityEngine.Pool;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ACRoundedRectMask
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Overrides the RectMask2D.PerformClipping method to add extra checks before doing exhaustive culling on
|
|
||||||
/// each maskable target.
|
|
||||||
/// </summary>
|
|
||||||
public class RoundedRectMask2D : RectMask2D
|
|
||||||
{
|
|
||||||
public static readonly string RadiiPropertyName = "_ClipRectRadii";
|
|
||||||
|
|
||||||
|
|
||||||
[SerializeField]
|
|
||||||
private bool independantRadii;
|
|
||||||
[Tooltip("The four corner radii of the rounded rect. (x: top left, y: top right, z: bottom left, w: bottom right)")]
|
|
||||||
[SerializeField]
|
|
||||||
private Vector4 radii = Vector4.one * 10.0f;
|
|
||||||
public Vector4 Radii
|
|
||||||
{
|
|
||||||
get => radii;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
radii = value;
|
|
||||||
MaskUtilities.Notify2DMaskStateChanged(this);
|
|
||||||
ForceClip = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Tooltip("If not set to true, you will need to handle that all masked UI elements have their own material instances")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool cloneMaskableMaterialsOnStart = true;
|
|
||||||
|
|
||||||
private static int clipRectRadiiID = 0;
|
|
||||||
|
|
||||||
|
|
||||||
private HashSet<IClippable> clipTargets = null;
|
|
||||||
private HashSet<MaskableGraphic> maskableTargets = null;
|
|
||||||
private int lastclipTargetsCount = 0;
|
|
||||||
private int lastmaskableTargetsCount = 0;
|
|
||||||
private bool shouldRecalculateClipRects = false;
|
|
||||||
|
|
||||||
private Canvas cachedCanvas = null;
|
|
||||||
private Vector3[] cachedCorners = new Vector3[4];
|
|
||||||
private Rect lastClipRectCanvasSpace = new Rect();
|
|
||||||
private Vector2Int lastSoftness = new Vector2Int();
|
|
||||||
private List<RectMask2D> clippers = new List<RectMask2D>();
|
|
||||||
|
|
||||||
#region MonoBehaviour Implementation
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnEnable()
|
|
||||||
{
|
|
||||||
base.OnEnable();
|
|
||||||
shouldRecalculateClipRects = true;
|
|
||||||
ForceClip = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnValidate()
|
|
||||||
{
|
|
||||||
base.OnValidate();
|
|
||||||
shouldRecalculateClipRects = true;
|
|
||||||
ForceClip = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnDidApplyAnimationProperties()
|
|
||||||
{
|
|
||||||
base.OnDidApplyAnimationProperties();
|
|
||||||
|
|
||||||
shouldRecalculateClipRects = true;
|
|
||||||
ForceClip = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion MonoBehaviour Implementation
|
|
||||||
|
|
||||||
#region RectMask2D Implementation
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnTransformParentChanged()
|
|
||||||
{
|
|
||||||
base.OnTransformParentChanged();
|
|
||||||
shouldRecalculateClipRects = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void OnCanvasHierarchyChanged()
|
|
||||||
{
|
|
||||||
cachedCanvas = null;
|
|
||||||
base.OnCanvasHierarchyChanged();
|
|
||||||
shouldRecalculateClipRects = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected override void Start()
|
|
||||||
{
|
|
||||||
base.Start();
|
|
||||||
|
|
||||||
shouldRecalculateClipRects = true;
|
|
||||||
PerformClipping();
|
|
||||||
|
|
||||||
if (cloneMaskableMaterialsOnStart && maskableTargets != null)
|
|
||||||
{
|
|
||||||
foreach (MaskableGraphic mg in maskableTargets)
|
|
||||||
{
|
|
||||||
if (mg.materialForRendering.Equals(mg.material))
|
|
||||||
{
|
|
||||||
Material m = new Material(mg.material);
|
|
||||||
mg.material = m;
|
|
||||||
}
|
|
||||||
else if (mg is TMP_Text tmpText)
|
|
||||||
{
|
|
||||||
Material m = new Material(tmpText.fontMaterial);
|
|
||||||
tmpText.fontMaterial = m;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Log("[RoundedRectMask2d] Can't clone material for " + mg.name + ". This will result in same rounded corners for all assets sharing its materiel " + mg.materialForRendering);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnSetClipRect(mg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Improves the base class method by:
|
|
||||||
/// - Checks if the canvas renderer has moved before exhaustive culling.
|
|
||||||
/// - Interleaves UpdateClipSoftness so objects are not iterated over twice.
|
|
||||||
/// - Adds a OnSetClipRect callback for derived classes to use.
|
|
||||||
/// </summary>
|
|
||||||
public override void PerformClipping()
|
|
||||||
{
|
|
||||||
// Not calling the base class method intentionally to provide a more optimal version.
|
|
||||||
//base.PerformClipping();
|
|
||||||
|
|
||||||
if (clipRectRadiiID == 0)
|
|
||||||
{
|
|
||||||
clipRectRadiiID = Shader.PropertyToID(RadiiPropertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Initialize();
|
|
||||||
|
|
||||||
if (ReferenceEquals(Canvas, null))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO See if an IsActive() test would work well here or whether it might cause unexpected side effects (re case 776771)
|
|
||||||
|
|
||||||
// if the parents are changed
|
|
||||||
// or something similar we
|
|
||||||
// do a recalculate here
|
|
||||||
if (shouldRecalculateClipRects || ForceClip)
|
|
||||||
{
|
|
||||||
MaskUtilities.GetRectMasksForClip(this, clippers);
|
|
||||||
shouldRecalculateClipRects = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the compound rects from
|
|
||||||
// the clippers that are valid
|
|
||||||
bool validRect = true;
|
|
||||||
Rect clipRect = Clipping.FindCullAndClipWorldRect(clippers, out validRect);
|
|
||||||
|
|
||||||
// If the mask is in ScreenSpaceOverlay/Camera render mode, its content is only rendered when its rect
|
|
||||||
// overlaps that of the root canvas.
|
|
||||||
RenderMode renderMode = Canvas.rootCanvas.renderMode;
|
|
||||||
bool maskIsCulled =
|
|
||||||
(renderMode == RenderMode.ScreenSpaceCamera || renderMode == RenderMode.ScreenSpaceOverlay) &&
|
|
||||||
!clipRect.Overlaps(RootCanvasRect, true);
|
|
||||||
|
|
||||||
if (maskIsCulled)
|
|
||||||
{
|
|
||||||
// Children are only displayed when inside the mask. If the mask is culled, then the children
|
|
||||||
// inside the mask are also culled. In that situation, we pass an invalid rect to allow callees
|
|
||||||
// to avoid some processing.
|
|
||||||
clipRect = Rect.zero;
|
|
||||||
validRect = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clipRect != lastClipRectCanvasSpace || softness != lastSoftness)
|
|
||||||
{
|
|
||||||
foreach (IClippable clipTarget in clipTargets)
|
|
||||||
{
|
|
||||||
clipTarget.SetClipRect(clipRect, validRect);
|
|
||||||
clipTarget.SetClipSoftness(softness);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (MaskableGraphic maskableTarget in maskableTargets)
|
|
||||||
{
|
|
||||||
maskableTarget.SetClipRect(clipRect, validRect);
|
|
||||||
maskableTarget.SetClipSoftness(softness);
|
|
||||||
OnSetClipRect(maskableTarget);
|
|
||||||
|
|
||||||
maskableTarget.Cull(clipRect, validRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ForceClip)
|
|
||||||
{
|
|
||||||
foreach (IClippable clipTarget in clipTargets)
|
|
||||||
{
|
|
||||||
clipTarget.SetClipRect(clipRect, validRect);
|
|
||||||
clipTarget.SetClipSoftness(softness);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (MaskableGraphic maskableTarget in maskableTargets)
|
|
||||||
{
|
|
||||||
maskableTarget.SetClipRect(clipRect, validRect);
|
|
||||||
maskableTarget.SetClipSoftness(softness);
|
|
||||||
OnSetClipRect(maskableTarget);
|
|
||||||
|
|
||||||
if (maskableTarget.canvasRenderer.hasMoved)
|
|
||||||
{
|
|
||||||
maskableTarget.Cull(clipRect, validRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (MaskableGraphic maskableTarget in maskableTargets)
|
|
||||||
{
|
|
||||||
if (!maskableTarget.canvasRenderer.hasMoved)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
maskableTarget.Cull(clipRect, validRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ForceClip = false;
|
|
||||||
lastClipRectCanvasSpace = clipRect;
|
|
||||||
lastSoftness = softness;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion RectMask2D Implementation
|
|
||||||
|
|
||||||
public bool ForceClip
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// This is an imprecise check if a clip or mask target gets added then removed on the same frame.
|
|
||||||
// But... the alternative is we reflect into m_ForceClip base member which would be a per frame allocation due to it being a value type.
|
|
||||||
// If this check is return false negatives in your scenario, then set ForceClip to true.
|
|
||||||
return clipTargets.Count != lastclipTargetsCount ||
|
|
||||||
maskableTargets.Count != lastmaskableTargetsCount;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == true)
|
|
||||||
{
|
|
||||||
lastclipTargetsCount = 0;
|
|
||||||
lastmaskableTargetsCount = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Initialize();
|
|
||||||
|
|
||||||
lastclipTargetsCount = clipTargets.Count;
|
|
||||||
lastmaskableTargetsCount = maskableTargets.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Callback whenever the clip rect is mutated.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnSetClipRect(IClippable clippable)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Callback whenever the clip rect is mutated.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnSetClipRect(MaskableGraphic maskableTarget)
|
|
||||||
{
|
|
||||||
Material targetMaterial = maskableTarget.materialForRendering;
|
|
||||||
|
|
||||||
if (targetMaterial != null)
|
|
||||||
{
|
|
||||||
targetMaterial.SetVector(clipRectRadiiID, Radii);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Log("Setting clip rect for " + maskableTarget.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize()
|
|
||||||
{
|
|
||||||
// Check if we have already initialized.
|
|
||||||
if (clipTargets != null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Many of the properties we need access to for clipping are not exposed. So, we have to do reflection to get access to them.
|
|
||||||
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
|
||||||
clipTargets = (HashSet<IClippable>)typeof(RectMask2D).GetField("m_ClipTargets", bindFlags).GetValue(this);
|
|
||||||
maskableTargets = (HashSet<MaskableGraphic>)typeof(RectMask2D).GetField("m_MaskableTargets", bindFlags).GetValue(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Canvas Canvas
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (cachedCanvas == null)
|
|
||||||
{
|
|
||||||
#if UNITY_2021_1_OR_NEWER
|
|
||||||
var list = ListPool<Canvas>.Get();
|
|
||||||
gameObject.GetComponentsInParent(false, list);
|
|
||||||
if (list.Count > 0)
|
|
||||||
cachedCanvas = list[list.Count - 1];
|
|
||||||
else
|
|
||||||
cachedCanvas = null;
|
|
||||||
ListPool<Canvas>.Release(list);
|
|
||||||
#else
|
|
||||||
var list = gameObject.GetComponentsInParent<Canvas>(false);
|
|
||||||
if (list.Length > 0)
|
|
||||||
cachedCanvas = list[list.Length - 1];
|
|
||||||
else
|
|
||||||
cachedCanvas = null;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return cachedCanvas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Rect RootCanvasRect
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
rectTransform.GetWorldCorners(cachedCorners);
|
|
||||||
|
|
||||||
if (!ReferenceEquals(Canvas, null))
|
|
||||||
{
|
|
||||||
Canvas rootCanvas = Canvas.rootCanvas;
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
cachedCorners[i] = rootCanvas.transform.InverseTransformPoint(cachedCorners[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Rect(cachedCorners[0].x, cachedCorners[0].y, cachedCorners[2].x - cachedCorners[0].x, cachedCorners[2].y - cachedCorners[0].y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
#ifndef RM_COMMON
|
|
||||||
#define RM_COMMON
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constants
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
#define RM_PI 3.14159265359
|
|
||||||
#define RM_DEGREES_TO_RADIANS (RM_PI / 180.0)
|
|
||||||
|
|
||||||
#define RM_HALF_MIN 6.103515625e-5 // 2^-14, the same value for 10, 11 and 16-bit: https://www.khronos.org/opengl/wiki/Small_Float_Formats
|
|
||||||
|
|
||||||
#define RM_MIN_CORNER_VALUE 1e-3
|
|
||||||
#define RM_MIN_CORNER_VALUE_RECT 1e-3
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SDF methods.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
inline float RMPointVsRoundedBox(in float2 position, in float2 cornerCircleDistance, in float cornerCircleRadius)
|
|
||||||
{
|
|
||||||
return length(max(abs(position) - cornerCircleDistance, 0.0)) - cornerCircleRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float FilterDistance(in float distance)
|
|
||||||
{
|
|
||||||
float pixelDistance = distance / fwidth(distance);
|
|
||||||
|
|
||||||
#if defined(_INDEPENDENT_CORNERS) || defined(_UI_CLIP_RECT_ROUNDED_INDEPENDENT)
|
|
||||||
// To avoid artifacts at discontinuities in the SDF distance increase the pixel width.
|
|
||||||
return saturate(1.0 - pixelDistance);
|
|
||||||
#else
|
|
||||||
return saturate(0.5 - pixelDistance);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMRoundCornersSmooth(in float2 position, in float2 cornerCircleDistance, in float cornerCircleRadius, in float smoothingValue)
|
|
||||||
{
|
|
||||||
float distance = RMPointVsRoundedBox(position, cornerCircleDistance, cornerCircleRadius);
|
|
||||||
#if defined(_EDGE_SMOOTHING_AUTOMATIC)
|
|
||||||
return FilterDistance(distance);
|
|
||||||
#else
|
|
||||||
return smoothstep(1.0, 0.0, distance / smoothingValue);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMRoundCorners(in float2 position, in float2 cornerCircleDistance, in float cornerCircleRadius, in float smoothingValue)
|
|
||||||
{
|
|
||||||
#if defined(_TRANSPARENT)
|
|
||||||
return RMRoundCornersSmooth(position, cornerCircleDistance, cornerCircleRadius, smoothingValue);
|
|
||||||
#else
|
|
||||||
return (RMPointVsRoundedBox(position, cornerCircleDistance, cornerCircleRadius) < 0.0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMFindCornerRadius(in float2 uv, in float4 radii)
|
|
||||||
{
|
|
||||||
if (uv.x < 0.5)
|
|
||||||
{
|
|
||||||
if (uv.y > 0.5) { return radii.x; } // Top left.
|
|
||||||
else { return radii.z; } // Bottom left.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (uv.y > 0.5) { return radii.y; } // Top right.
|
|
||||||
else { return radii.w; } // Bottom right.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UnityUI methods.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
inline float RMGet2DClippingRounded(in float2 position, in float4 clipRect, in float radius)
|
|
||||||
{
|
|
||||||
float2 halfSize = (clipRect.zw - clipRect.xy) * 0.5;
|
|
||||||
float2 center = clipRect.xy + halfSize;
|
|
||||||
float2 offset = position - center;
|
|
||||||
|
|
||||||
return RMPointVsRoundedBox(offset, halfSize - radius, radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMGet2DClippingRoundedSoft(in float2 position, in float4 clipRect, in float radius)
|
|
||||||
{
|
|
||||||
return saturate(FilterDistance(RMGet2DClippingRounded(position, clipRect, radius)));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMGet2DClippingRoundedIndependent(in float2 position, in float4 clipRect, in float4 radii)
|
|
||||||
{
|
|
||||||
float2 halfSize = (clipRect.zw - clipRect.xy) * 0.5;
|
|
||||||
float2 center = clipRect.xy + halfSize;
|
|
||||||
float2 offset = position - center;
|
|
||||||
float radius = RMFindCornerRadius(offset, radii);
|
|
||||||
|
|
||||||
return RMPointVsRoundedBox(offset, halfSize - radius, radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMGet2DClippingRoundedIndependentSoft(in float2 position, in float4 clipRect, in float4 radii)
|
|
||||||
{
|
|
||||||
return saturate(FilterDistance(RMGet2DClippingRoundedIndependent(position, clipRect, radii)));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float RMUnityUIClipRect(in float2 position, in float4 clipRect, in float4 radii)
|
|
||||||
{
|
|
||||||
radii = max(radii, RM_MIN_CORNER_VALUE_RECT);
|
|
||||||
#if defined(UNITY_UI_ALPHACLIP)
|
|
||||||
return RMGet2DClippingRoundedIndependent(position, clipRect, radii) <= 0.0;
|
|
||||||
#else
|
|
||||||
return RMGet2DClippingRoundedIndependentSoft(position, clipRect, radii);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif // RM_COMMON
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
|
|
||||||
|
|
||||||
Shader "RoundedMask/UI/Default (RoundedMask)"
|
|
||||||
{
|
|
||||||
Properties
|
|
||||||
{
|
|
||||||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
|
||||||
_Color ("Tint", Color) = (1,1,1,1)
|
|
||||||
|
|
||||||
_StencilComp ("Stencil Comparison", Float) = 8
|
|
||||||
_Stencil ("Stencil ID", Float) = 0
|
|
||||||
_StencilOp ("Stencil Operation", Float) = 0
|
|
||||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
|
||||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
|
||||||
|
|
||||||
_ColorMask ("Color Mask", Float) = 15
|
|
||||||
|
|
||||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
|
||||||
[HideInInspector] _ClipRect("Clip Rect", Vector) = (-32767.0, -32767.0, 32767.0, 32767.0)
|
|
||||||
[HideInInspector] _ClipRectRadii("Clip Rect Radii", Vector) = (10.0, 10.0, 10.0, 10.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
SubShader
|
|
||||||
{
|
|
||||||
Tags
|
|
||||||
{
|
|
||||||
"Queue"="Transparent"
|
|
||||||
"IgnoreProjector"="True"
|
|
||||||
"RenderType"="Transparent"
|
|
||||||
"PreviewType"="Plane"
|
|
||||||
"CanUseSpriteAtlas"="True"
|
|
||||||
}
|
|
||||||
|
|
||||||
Stencil
|
|
||||||
{
|
|
||||||
Ref [_Stencil]
|
|
||||||
Comp [_StencilComp]
|
|
||||||
Pass [_StencilOp]
|
|
||||||
ReadMask [_StencilReadMask]
|
|
||||||
WriteMask [_StencilWriteMask]
|
|
||||||
}
|
|
||||||
|
|
||||||
Cull Off
|
|
||||||
Lighting Off
|
|
||||||
ZWrite Off
|
|
||||||
ZTest [unity_GUIZTestMode]
|
|
||||||
Blend SrcAlpha OneMinusSrcAlpha
|
|
||||||
ColorMask [_ColorMask]
|
|
||||||
|
|
||||||
Pass
|
|
||||||
{
|
|
||||||
Name "Default"
|
|
||||||
CGPROGRAM
|
|
||||||
#pragma vertex vert
|
|
||||||
#pragma fragment frag
|
|
||||||
#pragma target 2.0
|
|
||||||
|
|
||||||
#include "UnityCG.cginc"
|
|
||||||
#include "UnityUI.cginc"
|
|
||||||
#include "RoundedMaskCommon.hlsl"
|
|
||||||
|
|
||||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
|
||||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
|
||||||
|
|
||||||
struct appdata_t
|
|
||||||
{
|
|
||||||
float4 vertex : POSITION;
|
|
||||||
float4 color : COLOR;
|
|
||||||
float2 texcoord : TEXCOORD0;
|
|
||||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
||||||
};
|
|
||||||
|
|
||||||
struct v2f
|
|
||||||
{
|
|
||||||
float4 vertex : SV_POSITION;
|
|
||||||
fixed4 color : COLOR;
|
|
||||||
float2 texcoord : TEXCOORD0;
|
|
||||||
float4 worldPosition : TEXCOORD1;
|
|
||||||
#ifdef UNITY_UI_CLIP_RECT
|
|
||||||
float3 posLocal : TEXCOORD8;
|
|
||||||
#endif
|
|
||||||
UNITY_VERTEX_OUTPUT_STEREO
|
|
||||||
};
|
|
||||||
|
|
||||||
sampler2D _MainTex;
|
|
||||||
fixed4 _Color;
|
|
||||||
fixed4 _TextureSampleAdd;
|
|
||||||
float4 _ClipRect;
|
|
||||||
float4 _ClipRectRadii;
|
|
||||||
float4 _MainTex_ST;
|
|
||||||
|
|
||||||
v2f vert(appdata_t v)
|
|
||||||
{
|
|
||||||
v2f OUT;
|
|
||||||
UNITY_SETUP_INSTANCE_ID(v);
|
|
||||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
|
||||||
OUT.worldPosition = v.vertex;
|
|
||||||
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
|
|
||||||
|
|
||||||
OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
|
|
||||||
#ifdef UNITY_UI_CLIP_RECT
|
|
||||||
OUT.posLocal = v.vertex.xyz;
|
|
||||||
#endif
|
|
||||||
OUT.color = v.color * _Color;
|
|
||||||
return OUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed4 frag(v2f IN) : SV_Target
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef UNITY_UI_CLIP_RECT
|
|
||||||
clip( RMUnityUIClipRect(IN.posLocal.xy, _ClipRect, _ClipRectRadii) - 0.5);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
|
|
||||||
|
|
||||||
#ifdef UNITY_UI_ALPHACLIP
|
|
||||||
clip (color.a - 0.001);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
ENDCG
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
// Simplified SDF shader:
|
|
||||||
// - No Shading Option (bevel / bump / env map)
|
|
||||||
// - No Glow Option
|
|
||||||
// - Softness is applied on both side of the outline
|
|
||||||
|
|
||||||
Shader "RoundedMask/TextMeshPro/Mobile/Distance Field (RoundedMask)" {
|
|
||||||
|
|
||||||
Properties {
|
|
||||||
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
|
|
||||||
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
|
|
||||||
|
|
||||||
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
|
|
||||||
_OutlineWidth ("Outline Thickness", Range(0,1)) = 0
|
|
||||||
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
|
|
||||||
|
|
||||||
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0,.5)
|
|
||||||
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
|
|
||||||
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
|
|
||||||
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
|
|
||||||
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
|
|
||||||
|
|
||||||
_WeightNormal ("Weight Normal", float) = 0
|
|
||||||
_WeightBold ("Weight Bold", float) = .5
|
|
||||||
|
|
||||||
_ShaderFlags ("Flags", float) = 0
|
|
||||||
_ScaleRatioA ("Scale RatioA", float) = 1
|
|
||||||
_ScaleRatioB ("Scale RatioB", float) = 1
|
|
||||||
_ScaleRatioC ("Scale RatioC", float) = 1
|
|
||||||
|
|
||||||
_MainTex ("Font Atlas", 2D) = "white" {}
|
|
||||||
_TextureWidth ("Texture Width", float) = 512
|
|
||||||
_TextureHeight ("Texture Height", float) = 512
|
|
||||||
_GradientScale ("Gradient Scale", float) = 5
|
|
||||||
_ScaleX ("Scale X", float) = 1
|
|
||||||
_ScaleY ("Scale Y", float) = 1
|
|
||||||
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
|
|
||||||
_Sharpness ("Sharpness", Range(-1,1)) = 0
|
|
||||||
|
|
||||||
_VertexOffsetX ("Vertex OffsetX", float) = 0
|
|
||||||
_VertexOffsetY ("Vertex OffsetY", float) = 0
|
|
||||||
|
|
||||||
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
|
|
||||||
[HideInInspector]
|
|
||||||
_ClipRectRadii ("Clip Rect Radii", vector) = (10.0, 10.0, 10.0, 10.0)
|
|
||||||
_MaskSoftnessX ("Mask SoftnessX", float) = 0
|
|
||||||
_MaskSoftnessY ("Mask SoftnessY", float) = 0
|
|
||||||
|
|
||||||
_StencilComp ("Stencil Comparison", Float) = 8
|
|
||||||
_Stencil ("Stencil ID", Float) = 0
|
|
||||||
_StencilOp ("Stencil Operation", Float) = 0
|
|
||||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
|
||||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
|
||||||
|
|
||||||
_CullMode ("Cull Mode", Float) = 0
|
|
||||||
_ColorMask ("Color Mask", Float) = 15
|
|
||||||
}
|
|
||||||
|
|
||||||
SubShader {
|
|
||||||
Tags
|
|
||||||
{
|
|
||||||
"Queue"="Transparent"
|
|
||||||
"IgnoreProjector"="True"
|
|
||||||
"RenderType"="Transparent"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Stencil
|
|
||||||
{
|
|
||||||
Ref [_Stencil]
|
|
||||||
Comp [_StencilComp]
|
|
||||||
Pass [_StencilOp]
|
|
||||||
ReadMask [_StencilReadMask]
|
|
||||||
WriteMask [_StencilWriteMask]
|
|
||||||
}
|
|
||||||
|
|
||||||
Cull [_CullMode]
|
|
||||||
ZWrite Off
|
|
||||||
Lighting Off
|
|
||||||
Fog { Mode Off }
|
|
||||||
ZTest [unity_GUIZTestMode]
|
|
||||||
Blend One OneMinusSrcAlpha
|
|
||||||
ColorMask [_ColorMask]
|
|
||||||
|
|
||||||
Pass {
|
|
||||||
CGPROGRAM
|
|
||||||
#pragma vertex VertShader
|
|
||||||
#pragma fragment PixShader
|
|
||||||
#pragma shader_feature __ OUTLINE_ON
|
|
||||||
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
|
|
||||||
|
|
||||||
#pragma multi_compile __ UNITY_UI_CLIP_RECT
|
|
||||||
#pragma multi_compile __ UNITY_UI_ALPHACLIP
|
|
||||||
|
|
||||||
#include "UnityCG.cginc"
|
|
||||||
#include "UnityUI.cginc"
|
|
||||||
#include "RoundedMaskCommon.hlsl"
|
|
||||||
#include "Assets/TextMesh Pro/Shaders/TMPro_Properties.cginc"
|
|
||||||
|
|
||||||
struct vertex_t {
|
|
||||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
||||||
float4 vertex : POSITION;
|
|
||||||
float3 normal : NORMAL;
|
|
||||||
fixed4 color : COLOR;
|
|
||||||
float2 texcoord0 : TEXCOORD0;
|
|
||||||
float2 texcoord1 : TEXCOORD1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pixel_t {
|
|
||||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
||||||
UNITY_VERTEX_OUTPUT_STEREO
|
|
||||||
float4 vertex : SV_POSITION;
|
|
||||||
fixed4 faceColor : COLOR;
|
|
||||||
fixed4 outlineColor : COLOR1;
|
|
||||||
float4 texcoord0 : TEXCOORD0; // Texture UV, Mask UV
|
|
||||||
half4 param : TEXCOORD1; // Scale(x), BiasIn(y), BiasOut(z), Bias(w)
|
|
||||||
half4 mask : TEXCOORD2; // Position in clip space(xy), Softness(zw)
|
|
||||||
#if (UNDERLAY_ON | UNDERLAY_INNER)
|
|
||||||
float4 texcoord1 : TEXCOORD3; // Texture UV, alpha, reserved
|
|
||||||
half2 underlayParam : TEXCOORD4; // Scale(x), Bias(y)
|
|
||||||
#endif
|
|
||||||
#ifdef UNITY_UI_CLIP_RECT
|
|
||||||
float3 posLocal : TEXCOORD8;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
float4 _ClipRectRadii;
|
|
||||||
|
|
||||||
pixel_t VertShader(vertex_t input)
|
|
||||||
{
|
|
||||||
pixel_t output;
|
|
||||||
|
|
||||||
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
|
|
||||||
UNITY_SETUP_INSTANCE_ID(input);
|
|
||||||
UNITY_TRANSFER_INSTANCE_ID(input, output);
|
|
||||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
|
|
||||||
|
|
||||||
float bold = step(input.texcoord1.y, 0);
|
|
||||||
|
|
||||||
float4 vert = input.vertex;
|
|
||||||
vert.x += _VertexOffsetX;
|
|
||||||
vert.y += _VertexOffsetY;
|
|
||||||
float4 vPosition = UnityObjectToClipPos(vert);
|
|
||||||
|
|
||||||
float2 pixelSize = vPosition.w;
|
|
||||||
pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
|
|
||||||
|
|
||||||
float scale = rsqrt(dot(pixelSize, pixelSize));
|
|
||||||
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
|
|
||||||
if(UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
|
|
||||||
|
|
||||||
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
|
|
||||||
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
|
|
||||||
|
|
||||||
float layerScale = scale;
|
|
||||||
|
|
||||||
scale /= 1 + (_OutlineSoftness * _ScaleRatioA * scale);
|
|
||||||
float bias = (0.5 - weight) * scale - 0.5;
|
|
||||||
float outline = _OutlineWidth * _ScaleRatioA * 0.5 * scale;
|
|
||||||
|
|
||||||
float opacity = input.color.a;
|
|
||||||
#if (UNDERLAY_ON | UNDERLAY_INNER)
|
|
||||||
opacity = 1.0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fixed4 faceColor = fixed4(input.color.rgb, opacity) * _FaceColor;
|
|
||||||
faceColor.rgb *= faceColor.a;
|
|
||||||
|
|
||||||
fixed4 outlineColor = _OutlineColor;
|
|
||||||
outlineColor.a *= opacity;
|
|
||||||
outlineColor.rgb *= outlineColor.a;
|
|
||||||
outlineColor = lerp(faceColor, outlineColor, sqrt(min(1.0, (outline * 2))));
|
|
||||||
|
|
||||||
#if (UNDERLAY_ON | UNDERLAY_INNER)
|
|
||||||
layerScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * layerScale);
|
|
||||||
float layerBias = (.5 - weight) * layerScale - .5 - ((_UnderlayDilate * _ScaleRatioC) * .5 * layerScale);
|
|
||||||
|
|
||||||
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
|
|
||||||
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
|
|
||||||
float2 layerOffset = float2(x, y);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Generate UV for the Masking Texture
|
|
||||||
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
|
|
||||||
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
|
|
||||||
|
|
||||||
// Populate structure for pixel shader
|
|
||||||
output.vertex = vPosition;
|
|
||||||
output.faceColor = faceColor;
|
|
||||||
output.outlineColor = outlineColor;
|
|
||||||
output.texcoord0 = float4(input.texcoord0.x, input.texcoord0.y, maskUV.x, maskUV.y);
|
|
||||||
output.param = half4(scale, bias - outline, bias + outline, bias);
|
|
||||||
output.mask = half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
|
|
||||||
#if (UNDERLAY_ON || UNDERLAY_INNER)
|
|
||||||
output.texcoord1 = float4(input.texcoord0 + layerOffset, input.color.a, 0);
|
|
||||||
output.underlayParam = half2(layerScale, layerBias);
|
|
||||||
#endif
|
|
||||||
#ifdef UNITY_UI_CLIP_RECT
|
|
||||||
output.posLocal = vert.xyz;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// PIXEL SHADER
|
|
||||||
fixed4 PixShader(pixel_t input) : SV_Target
|
|
||||||
{
|
|
||||||
UNITY_SETUP_INSTANCE_ID(input);
|
|
||||||
|
|
||||||
half d = tex2D(_MainTex, input.texcoord0.xy).a * input.param.x;
|
|
||||||
half4 c = input.faceColor * saturate(d - input.param.w);
|
|
||||||
|
|
||||||
#ifdef OUTLINE_ON
|
|
||||||
c = lerp(input.outlineColor, input.faceColor, saturate(d - input.param.z));
|
|
||||||
c *= saturate(d - input.param.y);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UNDERLAY_ON
|
|
||||||
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
|
|
||||||
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * saturate(d - input.underlayParam.y) * (1 - c.a);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UNDERLAY_INNER
|
|
||||||
half sd = saturate(d - input.param.z);
|
|
||||||
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
|
|
||||||
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * (1 - saturate(d - input.underlayParam.y)) * sd * (1 - c.a);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Alternative implementation to UnityGet2DClipping with support for softness.
|
|
||||||
#if UNITY_UI_CLIP_RECT
|
|
||||||
clip( RMUnityUIClipRect(input.posLocal.xy, _ClipRect, _ClipRectRadii) - 0.5);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (UNDERLAY_ON | UNDERLAY_INNER)
|
|
||||||
c *= input.texcoord1.z;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UNITY_UI_ALPHACLIP
|
|
||||||
clip(c.a - 0.001);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
ENDCG
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
|
|
||||||
}
|
|
||||||
267
Assets/ArenaRoot.prefab
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &25324321885539938
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1256957957520000306}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: ProjectileSpawn
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &1256957957520000306
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 25324321885539938}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 2.1, y: -2.86, z: 1.87}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 7228744653633915258}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &306349634079512810
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 7055327180212611754}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: ButtonTarget
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &7055327180212611754
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 306349634079512810}
|
||||||
|
serializedVersion: 2
|
||||||
|
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:
|
||||||
|
- {fileID: 6009521584277000886}
|
||||||
|
m_Father: {fileID: 7228744653633915258}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &578482260246237550
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 6009521584277000886}
|
||||||
|
- component: {fileID: 3827426293549565123}
|
||||||
|
- component: {fileID: 1470073872372251261}
|
||||||
|
- component: {fileID: 3778101033228877197}
|
||||||
|
- component: {fileID: 8782929001941168503}
|
||||||
|
- component: {fileID: 8076614220732688013}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: CenterButtonTarget
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &6009521584277000886
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 578482260246237550}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067}
|
||||||
|
m_LocalPosition: {x: 2.03, y: -0.089999914, z: 1.9}
|
||||||
|
m_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 7055327180212611754}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!33 &3827426293549565123
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 578482260246237550}
|
||||||
|
m_Mesh: {fileID: 5687779609372477813, guid: 8ee80b1e2cfa1c747877549e20403fd3, type: 3}
|
||||||
|
--- !u!23 &1470073872372251261
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 578482260246237550}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 1
|
||||||
|
m_LightProbeUsage: 1
|
||||||
|
m_ReflectionProbeUsage: 1
|
||||||
|
m_RayTracingMode: 2
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||||
|
m_RayTracingAccelStructBuildFlags: 1
|
||||||
|
m_SmallMeshCulling: 1
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 2100000, guid: d88c7dcf650af2c4e812eaa19f43e2e4, type: 2}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
|
--- !u!114 &3778101033228877197
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 578482260246237550}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 9d8029baff330b94d836a23c421021a8, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
owner: {fileID: 0}
|
||||||
|
--- !u!64 &8782929001941168503
|
||||||
|
MeshCollider:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 578482260246237550}
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_IsTrigger: 0
|
||||||
|
m_ProvidesContacts: 0
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 5
|
||||||
|
m_Convex: 1
|
||||||
|
m_CookingOptions: 30
|
||||||
|
m_Mesh: {fileID: 5687779609372477813, guid: 8ee80b1e2cfa1c747877549e20403fd3, type: 3}
|
||||||
|
--- !u!114 &8076614220732688013
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 578482260246237550}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 20fa9c796cd377047ba2c43230717531, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
rotationSpeed: 90
|
||||||
|
--- !u!1 &1062886443160141632
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 6403106612870554802}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: AimPoint
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &6403106612870554802
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1062886443160141632}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||||
|
m_LocalPosition: {x: 2.032, y: -0.23, z: 1.87}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 7228744653633915258}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &9110341383532608413
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 7228744653633915258}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: ArenaRoot
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &7228744653633915258
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 9110341383532608413}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children:
|
||||||
|
- {fileID: 6403106612870554802}
|
||||||
|
- {fileID: 1256957957520000306}
|
||||||
|
- {fileID: 7055327180212611754}
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
7
Assets/ArenaRoot.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5091877278b8b6a47a9afa6f50d6b2a6
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Build Profiles.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92df50b8fba934144a4c4dcaf506f9b4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
18
Assets/CenterButtonTarget.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class CenterButtonTarget : MonoBehaviour
|
||||||
|
{
|
||||||
|
public TimingWheelShooter owner;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (owner == null)
|
||||||
|
owner = FindFirstObjectByType<TimingWheelShooter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyHit(ProjectileBehaviour projectile)
|
||||||
|
{
|
||||||
|
if (owner != null)
|
||||||
|
owner.NotifyButtonHit(projectile);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/CenterButtonTarget.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7bc9c797f71ebf4c84a6c6698a8dfd9
|
||||||
8
Assets/ClientSDK.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 50a0b21c151e150428fd2803d6b95db0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
2
Assets/ClientSDK/Encryption.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bc06bb57786c7e142b06ec231e5cf709
|
||||||
2
Assets/ClientSDK/EventDispatcher.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1d2251b279edb0147bd274a884ac878b
|
||||||
@@ -132,7 +132,20 @@ public class GameClient : IDisposable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Disconnect(string reason = "User disconnected")
|
/// <summary>
|
||||||
|
/// Tears down the socket and crypto session. When `transient` is true
|
||||||
|
/// (network drop, decrypt-failure cascade, anything we expect to retry),
|
||||||
|
/// the lobby/role/task/state caches are preserved so the post-reconnect
|
||||||
|
/// flow can re-associate via Reconnect(LobbyId). Default false matches
|
||||||
|
/// pre-P9 behavior (full state wipe) for explicit user disconnects.
|
||||||
|
///
|
||||||
|
/// Critical for the P9 reconnect bug: previously every Disconnect path
|
||||||
|
/// nuked LobbyId, so by the time GameManager_Network's reconnect coroutine
|
||||||
|
/// fired, the client had no idea which lobby it had been in - the
|
||||||
|
/// post-handshake Reconnect call had nothing to send and the server
|
||||||
|
/// answered the next vote/action with NOT_IN_LOBBY.
|
||||||
|
/// </summary>
|
||||||
|
public void Disconnect(string reason = "User disconnected", bool transient = false)
|
||||||
{
|
{
|
||||||
_cts?.Cancel();
|
_cts?.Cancel();
|
||||||
_tcpClient?.Close();
|
_tcpClient?.Close();
|
||||||
@@ -140,15 +153,22 @@ public class GameClient : IDisposable
|
|||||||
_stream = null;
|
_stream = null;
|
||||||
_encryption?.Dispose();
|
_encryption?.Dispose();
|
||||||
_encryption = null;
|
_encryption = null;
|
||||||
|
|
||||||
LobbyId = null;
|
if (!transient)
|
||||||
JoinCode = null;
|
{
|
||||||
CurrentLobbyState = null;
|
LobbyId = null;
|
||||||
MyRole = null;
|
JoinCode = null;
|
||||||
MyTasks.Clear();
|
CurrentLobbyState = null;
|
||||||
PlayerPositions.Clear();
|
MyRole = null;
|
||||||
Bodies.Clear();
|
MyTasks.Clear();
|
||||||
|
PlayerPositions.Clear();
|
||||||
|
Bodies.Clear();
|
||||||
|
}
|
||||||
|
// PlayerPositions are stale anyway after a drop, but we keep them so
|
||||||
|
// the UI doesn't blink avatars off-map mid-meeting; the next position
|
||||||
|
// broadcast overwrites them. LastEventId is intentionally preserved
|
||||||
|
// so the Reconnect message can ask the server for missed events.
|
||||||
|
|
||||||
Dispatcher.Post(() => OnDisconnected?.Invoke(reason));
|
Dispatcher.Post(() => OnDisconnected?.Invoke(reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +256,8 @@ public class GameClient : IDisposable
|
|||||||
decryptFailures++;
|
decryptFailures++;
|
||||||
if (decryptFailures >= 3)
|
if (decryptFailures >= 3)
|
||||||
{
|
{
|
||||||
Disconnect("Too many decryption failures");
|
// Transient: keep LobbyId for the reconnect coroutine.
|
||||||
|
Disconnect("Too many decryption failures", transient: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -253,7 +274,9 @@ public class GameClient : IDisposable
|
|||||||
}
|
}
|
||||||
catch (Exception ex) when (!ct.IsCancellationRequested)
|
catch (Exception ex) when (!ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Disconnect($"Connection error: {ex.Message}");
|
// Transient: TCP RST / read failure is exactly what reconnect was
|
||||||
|
// designed for. Keep LobbyId so post-reconnect flow can re-attach.
|
||||||
|
Disconnect($"Connection error: {ex.Message}", transient: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +316,35 @@ public class GameClient : IDisposable
|
|||||||
{
|
{
|
||||||
LobbyId = r.LobbyId;
|
LobbyId = r.LobbyId;
|
||||||
JoinCode = r.JoinCode;
|
JoinCode = r.JoinCode;
|
||||||
CurrentLobbyState = r.LobbyState;
|
// Ensure we always have a valid lobby state with the creator as owner
|
||||||
|
if (r.LobbyState != null)
|
||||||
|
{
|
||||||
|
CurrentLobbyState = r.LobbyState;
|
||||||
|
if (string.IsNullOrEmpty(CurrentLobbyState.OwnerId))
|
||||||
|
CurrentLobbyState.OwnerId = ClientUuid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CurrentLobbyState = new LobbyState
|
||||||
|
{
|
||||||
|
LobbyId = r.LobbyId ?? "",
|
||||||
|
JoinCode = r.JoinCode ?? "",
|
||||||
|
OwnerId = ClientUuid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Make sure creator appears in the player list
|
||||||
|
if (CurrentLobbyState.Players == null)
|
||||||
|
CurrentLobbyState.Players = new System.Collections.Generic.List<PlayerInfo>();
|
||||||
|
if (!CurrentLobbyState.Players.Any(p => p.ClientUuid == ClientUuid))
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Players.Insert(0, new PlayerInfo
|
||||||
|
{
|
||||||
|
ClientUuid = ClientUuid,
|
||||||
|
DisplayName = DisplayName,
|
||||||
|
IsOwner = true,
|
||||||
|
State = PlayerState.Alive
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -303,6 +354,22 @@ public class GameClient : IDisposable
|
|||||||
LobbyId = r.LobbyId;
|
LobbyId = r.LobbyId;
|
||||||
CurrentLobbyState = r.LobbyState;
|
CurrentLobbyState = r.LobbyState;
|
||||||
JoinCode = r.LobbyState?.JoinCode;
|
JoinCode = r.LobbyState?.JoinCode;
|
||||||
|
// Ensure self is in the player list
|
||||||
|
if (CurrentLobbyState != null)
|
||||||
|
{
|
||||||
|
if (CurrentLobbyState.Players == null)
|
||||||
|
CurrentLobbyState.Players = new System.Collections.Generic.List<PlayerInfo>();
|
||||||
|
if (!CurrentLobbyState.Players.Any(p => p.ClientUuid == ClientUuid))
|
||||||
|
{
|
||||||
|
CurrentLobbyState.Players.Add(new PlayerInfo
|
||||||
|
{
|
||||||
|
ClientUuid = ClientUuid,
|
||||||
|
DisplayName = DisplayName,
|
||||||
|
IsOwner = CurrentLobbyState.OwnerId == ClientUuid,
|
||||||
|
State = PlayerState.Alive
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -344,7 +411,6 @@ public class GameClient : IDisposable
|
|||||||
var joinedPayload = evt.GetPayload<PlayerJoinedPayload>();
|
var joinedPayload = evt.GetPayload<PlayerJoinedPayload>();
|
||||||
if (joinedPayload != null && CurrentLobbyState?.Players != null)
|
if (joinedPayload != null && CurrentLobbyState?.Players != null)
|
||||||
{
|
{
|
||||||
// Check if player already exists
|
|
||||||
bool exists = CurrentLobbyState.Players.Any(p => p.ClientUuid == joinedPayload.ClientUuid);
|
bool exists = CurrentLobbyState.Players.Any(p => p.ClientUuid == joinedPayload.ClientUuid);
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
@@ -352,7 +418,7 @@ public class GameClient : IDisposable
|
|||||||
{
|
{
|
||||||
ClientUuid = joinedPayload.ClientUuid,
|
ClientUuid = joinedPayload.ClientUuid,
|
||||||
DisplayName = joinedPayload.DisplayName,
|
DisplayName = joinedPayload.DisplayName,
|
||||||
IsOwner = false,
|
IsOwner = joinedPayload.ClientUuid == CurrentLobbyState.OwnerId,
|
||||||
IsReady = false,
|
IsReady = false,
|
||||||
State = PlayerState.Alive
|
State = PlayerState.Alive
|
||||||
});
|
});
|
||||||
@@ -465,24 +531,32 @@ public class GameClient : IDisposable
|
|||||||
|
|
||||||
#region Game Actions
|
#region Game Actions
|
||||||
|
|
||||||
public void CreateLobby(Position? center = null, int impostorCount = 1, int taskCount = 5, string? password = null, double playAreaRadius = 500)
|
public void CreateLobby(Position? center = null, int impostorCount = 1, int taskCount = 5, string? password = null, double playAreaRadius = 500, GameSettingsOverrides? settings = null)
|
||||||
{
|
{
|
||||||
|
// DisplayName is sent on every CreateLobby/JoinLobby so the server
|
||||||
|
// picks up the live nickname (typed into the input field after the
|
||||||
|
// ClientHello handshake fired). Without this the server uses the
|
||||||
|
// ClientHello-time name, which is the GameManager prefab default
|
||||||
|
// for any user who immediately created/joined a lobby.
|
||||||
Send(new CreateLobby
|
Send(new CreateLobby
|
||||||
{
|
{
|
||||||
PlayAreaCenter = center,
|
PlayAreaCenter = center,
|
||||||
PlayAreaRadius = playAreaRadius,
|
PlayAreaRadius = playAreaRadius,
|
||||||
ImpostorCount = impostorCount,
|
ImpostorCount = impostorCount,
|
||||||
TaskCount = taskCount,
|
TaskCount = taskCount,
|
||||||
Password = password
|
Password = password,
|
||||||
|
Settings = settings,
|
||||||
|
DisplayName = DisplayName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void JoinLobby(string joinCode, string? password = null)
|
public void JoinLobby(string joinCode, string? password = null)
|
||||||
{
|
{
|
||||||
Send(new JoinLobby
|
Send(new JoinLobby
|
||||||
{
|
{
|
||||||
JoinCode = joinCode.ToUpperInvariant(),
|
JoinCode = joinCode.ToUpperInvariant(),
|
||||||
Password = password
|
Password = password,
|
||||||
|
DisplayName = DisplayName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +565,7 @@ public class GameClient : IDisposable
|
|||||||
Send(new LeaveLobby());
|
Send(new LeaveLobby());
|
||||||
LobbyId = null;
|
LobbyId = null;
|
||||||
JoinCode = null;
|
JoinCode = null;
|
||||||
|
CurrentLobbyState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartGame()
|
public void StartGame()
|
||||||
|
|||||||
2
Assets/ClientSDK/GameClient.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 91e0f647c37b0b94b83f53bb854db28c
|
||||||
@@ -9,38 +9,39 @@ namespace GeoSus.Client
|
|||||||
{
|
{
|
||||||
#region Základní typy
|
#region Základní typy
|
||||||
|
|
||||||
public struct Position
|
public struct Position
|
||||||
{
|
|
||||||
[JsonProperty("lat")]
|
|
||||||
public double Lat { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("lon")]
|
|
||||||
public double Lon { get; set; }
|
|
||||||
|
|
||||||
public Position(double lat, double lon)
|
|
||||||
{
|
{
|
||||||
Lat = lat;
|
[JsonProperty("lat")]
|
||||||
Lon = lon;
|
public double Lat { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
// Haversine vzdálenost v metrech
|
|
||||||
public double DistanceTo(Position other)
|
|
||||||
{
|
|
||||||
const double R = 6371000;
|
|
||||||
var lat1 = Lat * Math.PI / 180;
|
|
||||||
var lat2 = other.Lat * Math.PI / 180;
|
|
||||||
var dLat = (other.Lat - Lat) * Math.PI / 180;
|
|
||||||
var dLon = (other.Lon - Lon) * Math.PI / 180;
|
|
||||||
|
|
||||||
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
|
|
||||||
Math.Cos(lat1) * Math.Cos(lat2) *
|
|
||||||
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
|
|
||||||
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
|
|
||||||
|
|
||||||
return R * c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
[JsonProperty("lon")]
|
||||||
|
public double Lon { get; set; }
|
||||||
|
|
||||||
|
public Position(double lat, double lon)
|
||||||
|
{
|
||||||
|
Lat = lat;
|
||||||
|
Lon = lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Haversine vzdálenost v metrech
|
||||||
|
public double DistanceTo(Position other)
|
||||||
|
{
|
||||||
|
const double R = 6371000;
|
||||||
|
var lat1 = Lat * Math.PI / 180;
|
||||||
|
var lat2 = other.Lat * Math.PI / 180;
|
||||||
|
var dLat = (other.Lat - Lat) * Math.PI / 180;
|
||||||
|
var dLon = (other.Lon - Lon) * Math.PI / 180;
|
||||||
|
|
||||||
|
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
|
||||||
|
Math.Cos(lat1) * Math.Cos(lat2) *
|
||||||
|
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
|
||||||
|
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
|
||||||
|
|
||||||
|
return R * c;
|
||||||
|
}
|
||||||
|
public static bool operator ==(Position left, Position right) { if (left.Lat == right.Lat && left.Lon == right.Lon) { return true; } else { return false; } }
|
||||||
|
public static bool operator !=(Position left, Position right) { return !(left == right); }
|
||||||
|
}
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
public enum PlayerRole { Crew, Impostor }
|
public enum PlayerRole { Crew, Impostor }
|
||||||
|
|
||||||
@@ -48,6 +49,11 @@ public enum PlayerRole { Crew, Impostor }
|
|||||||
public enum PlayerState { Alive, Dead }
|
public enum PlayerState { Alive, Dead }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
// NOTE: `Voting` is reserved-but-unused on the wire as of 2026. The server
|
||||||
|
// keeps the entire vote cycle inside `Meeting` and uses MeetingStartedPayload
|
||||||
|
// timestamps (DiscussionEndTime / VotingEndTime) to distinguish sub-phases.
|
||||||
|
// The enum value is preserved here for serialization compatibility with old
|
||||||
|
// saves; new code should not assign it.
|
||||||
public enum GamePhase { Lobby, Loading, Playing, Meeting, Voting, Ended }
|
public enum GamePhase { Lobby, Loading, Playing, Meeting, Voting, Ended }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
@@ -183,6 +189,24 @@ public class CreateLobby : Message
|
|||||||
|
|
||||||
[JsonProperty("taskCount")]
|
[JsonProperty("taskCount")]
|
||||||
public int TaskCount { get; set; } = 5;
|
public int TaskCount { get; set; } = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// P13b: optional per-lobby settings overrides supplied by the host.
|
||||||
|
/// Any field left null falls through to the server's current default
|
||||||
|
/// (snapshotted at lobby creation, immutable thereafter for this lobby).
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("settings")]
|
||||||
|
public GameSettingsOverrides? Settings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional. Live host display name from the nickname input field at
|
||||||
|
/// the moment of CreateLobby. ClientHello-time name is stale because
|
||||||
|
/// the handshake fires before the user has typed anything; this lets
|
||||||
|
/// the server pick up the freshly-typed name without a separate
|
||||||
|
/// rename round-trip.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("displayName")]
|
||||||
|
public string? DisplayName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateLobbyResponse : Message
|
public class CreateLobbyResponse : Message
|
||||||
@@ -208,12 +232,19 @@ public class CreateLobbyResponse : Message
|
|||||||
public class JoinLobby : Message
|
public class JoinLobby : Message
|
||||||
{
|
{
|
||||||
public override string Type => "JoinLobby";
|
public override string Type => "JoinLobby";
|
||||||
|
|
||||||
[JsonProperty("joinCode")]
|
[JsonProperty("joinCode")]
|
||||||
public string JoinCode { get; set; } = "";
|
public string JoinCode { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("password")]
|
[JsonProperty("password")]
|
||||||
public string? Password { get; set; }
|
public string? Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional. Live joiner display name from the nickname input field
|
||||||
|
/// at the moment of Join. See CreateLobby.DisplayName for rationale.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("displayName")]
|
||||||
|
public string? DisplayName { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JoinLobbyResponse : Message
|
public class JoinLobbyResponse : Message
|
||||||
@@ -622,17 +653,26 @@ public class PlayerEjectedPayload
|
|||||||
public PlayerRole Role { get; set; }
|
public PlayerRole Role { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TaskStartedPayload
|
||||||
|
{
|
||||||
|
[JsonProperty("clientUuid")]
|
||||||
|
public string ClientUuid { get; set; } = "";
|
||||||
|
|
||||||
|
[JsonProperty("taskId")]
|
||||||
|
public string TaskId { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
public class TaskCompletedPayload
|
public class TaskCompletedPayload
|
||||||
{
|
{
|
||||||
[JsonProperty("clientUuid")]
|
[JsonProperty("clientUuid")]
|
||||||
public string ClientUuid { get; set; } = "";
|
public string ClientUuid { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("taskId")]
|
[JsonProperty("taskId")]
|
||||||
public string TaskId { get; set; } = "";
|
public string TaskId { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("totalCompleted")]
|
[JsonProperty("totalCompleted")]
|
||||||
public int TotalCompleted { get; set; }
|
public int TotalCompleted { get; set; }
|
||||||
|
|
||||||
[JsonProperty("totalTasks")]
|
[JsonProperty("totalTasks")]
|
||||||
public int TotalTasks { get; set; }
|
public int TotalTasks { get; set; }
|
||||||
}
|
}
|
||||||
@@ -712,10 +752,10 @@ public class RepairStartedPayload
|
|||||||
{
|
{
|
||||||
[JsonProperty("sabotageId")]
|
[JsonProperty("sabotageId")]
|
||||||
public string SabotageId { get; set; } = "";
|
public string SabotageId { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("stationId")]
|
[JsonProperty("stationId")]
|
||||||
public string StationId { get; set; } = "";
|
public string StationId { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("playerId")]
|
[JsonProperty("playerId")]
|
||||||
public string PlayerId { get; set; } = "";
|
public string PlayerId { get; set; } = "";
|
||||||
}
|
}
|
||||||
@@ -724,10 +764,10 @@ public class RepairStoppedPayload
|
|||||||
{
|
{
|
||||||
[JsonProperty("sabotageId")]
|
[JsonProperty("sabotageId")]
|
||||||
public string SabotageId { get; set; } = "";
|
public string SabotageId { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("stationId")]
|
[JsonProperty("stationId")]
|
||||||
public string StationId { get; set; } = "";
|
public string StationId { get; set; } = "";
|
||||||
|
|
||||||
[JsonProperty("playerId")]
|
[JsonProperty("playerId")]
|
||||||
public string PlayerId { get; set; } = "";
|
public string PlayerId { get; set; } = "";
|
||||||
}
|
}
|
||||||
@@ -789,6 +829,162 @@ public class LobbyState
|
|||||||
/// <summary>True if map data has been loaded (or Overpass is disabled)</summary>
|
/// <summary>True if map data has been loaded (or Overpass is disabled)</summary>
|
||||||
[JsonProperty("mapDataReady")]
|
[JsonProperty("mapDataReady")]
|
||||||
public bool MapDataReady { get; set; } = true;
|
public bool MapDataReady { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// P13b: full per-lobby settings snapshot. Clients use this for HUD
|
||||||
|
/// (button visibility, countdown timings, etc.) instead of hardcoded
|
||||||
|
/// values. Always populated for new server builds; old client builds
|
||||||
|
/// can ignore the field.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("settings")]
|
||||||
|
public GameSettings? Settings { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// P13b: per-lobby gameplay settings on the wire. Server populates this from
|
||||||
|
/// its per-lobby snapshot so clients can drive HUD logic from authoritative
|
||||||
|
/// values rather than hardcoded constants.
|
||||||
|
/// </summary>
|
||||||
|
public class GameSettings
|
||||||
|
{
|
||||||
|
// Round shape
|
||||||
|
[JsonProperty("maxPlayers")]
|
||||||
|
public int MaxPlayers { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("impostorCount")]
|
||||||
|
public int ImpostorCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("taskCount")]
|
||||||
|
public int TaskCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("tiePolicy")]
|
||||||
|
public string TiePolicy { get; set; } = "NoEject";
|
||||||
|
|
||||||
|
// Distances (m)
|
||||||
|
[JsonProperty("killDistanceM")]
|
||||||
|
public double KillDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("reportDistanceM")]
|
||||||
|
public double ReportDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("taskStartDistanceM")]
|
||||||
|
public double TaskStartDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("meetingArrivalRadiusM")]
|
||||||
|
public double MeetingArrivalRadiusM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("emergencyMeetingCallRadiusM")]
|
||||||
|
public double EmergencyMeetingCallRadiusM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("repairStationDistanceM")]
|
||||||
|
public double RepairStationDistanceM { get; set; }
|
||||||
|
|
||||||
|
// Cooldowns / counts
|
||||||
|
[JsonProperty("killCooldownMs")]
|
||||||
|
public int KillCooldownMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("emergencyMeetingCooldownMs")]
|
||||||
|
public int EmergencyMeetingCooldownMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("maxEmergencyMeetingsPerPlayer")]
|
||||||
|
public int MaxEmergencyMeetingsPerPlayer { get; set; }
|
||||||
|
|
||||||
|
// Meeting phases (ms)
|
||||||
|
[JsonProperty("arrivalBaseMs")]
|
||||||
|
public int ArrivalBaseMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("allowedLateMs")]
|
||||||
|
public int AllowedLateMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("discussionPhaseMs")]
|
||||||
|
public int DiscussionPhaseMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("votingPhaseMs")]
|
||||||
|
public int VotingPhaseMs { get; set; }
|
||||||
|
|
||||||
|
// Sabotage
|
||||||
|
[JsonProperty("sabotageCooldownMs")]
|
||||||
|
public int SabotageCooldownMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("commsBlackoutDurationMs")]
|
||||||
|
public int CommsBlackoutDurationMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("criticalMeltdownDeadlineMs")]
|
||||||
|
public int CriticalMeltdownDeadlineMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("repairStationHoldMs")]
|
||||||
|
public int RepairStationHoldMs { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// P13b: host-supplied overrides at CreateLobby. Every field is nullable so
|
||||||
|
/// the host can opt into changing only what they care about; null = use the
|
||||||
|
/// server's current default at the moment of lobby creation.
|
||||||
|
/// </summary>
|
||||||
|
public class GameSettingsOverrides
|
||||||
|
{
|
||||||
|
[JsonProperty("maxPlayers")]
|
||||||
|
public int? MaxPlayers { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("impostorCount")]
|
||||||
|
public int? ImpostorCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("taskCount")]
|
||||||
|
public int? TaskCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("tiePolicy")]
|
||||||
|
public string? TiePolicy { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("killDistanceM")]
|
||||||
|
public double? KillDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("reportDistanceM")]
|
||||||
|
public double? ReportDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("taskStartDistanceM")]
|
||||||
|
public double? TaskStartDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("meetingArrivalRadiusM")]
|
||||||
|
public double? MeetingArrivalRadiusM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("emergencyMeetingCallRadiusM")]
|
||||||
|
public double? EmergencyMeetingCallRadiusM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("repairStationDistanceM")]
|
||||||
|
public double? RepairStationDistanceM { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("killCooldownMs")]
|
||||||
|
public int? KillCooldownMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("emergencyMeetingCooldownMs")]
|
||||||
|
public int? EmergencyMeetingCooldownMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("maxEmergencyMeetingsPerPlayer")]
|
||||||
|
public int? MaxEmergencyMeetingsPerPlayer { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("arrivalBaseMs")]
|
||||||
|
public int? ArrivalBaseMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("allowedLateMs")]
|
||||||
|
public int? AllowedLateMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("discussionPhaseMs")]
|
||||||
|
public int? DiscussionPhaseMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("votingPhaseMs")]
|
||||||
|
public int? VotingPhaseMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sabotageCooldownMs")]
|
||||||
|
public int? SabotageCooldownMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("commsBlackoutDurationMs")]
|
||||||
|
public int? CommsBlackoutDurationMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("criticalMeltdownDeadlineMs")]
|
||||||
|
public int? CriticalMeltdownDeadlineMs { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("repairStationHoldMs")]
|
||||||
|
public int? RepairStationHoldMs { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map data classes for rendering - compact format from server
|
// Map data classes for rendering - compact format from server
|
||||||
|
|||||||
2
Assets/ClientSDK/Protocol.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 14463228dfea2264ebfc36c3a7dc4b99
|
||||||
2
Assets/ClientSDK/SimulatorClient.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 80ef0979df5d1fe489225f3e5edadc5c
|
||||||
8
Assets/ClientSDK/bin.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a4035bdb812fee4f96cb1aa1b24c999
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/ClientSDK/obj.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 131d9de257c8edc49991d792c6e702f6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/Dancing triangle 1 Hour [YB3yHfqdw3Y].mp3
Normal file
23
Assets/Dancing triangle 1 Hour [YB3yHfqdw3Y].mp3.meta
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 58ebc3ffa125a9949953f6704e0a8c39
|
||||||
|
AudioImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 8
|
||||||
|
defaultSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
loadType: 0
|
||||||
|
sampleRateSetting: 0
|
||||||
|
sampleRateOverride: 44100
|
||||||
|
compressionFormat: 1
|
||||||
|
quality: 1
|
||||||
|
conversionMode: 0
|
||||||
|
preloadAudioData: 0
|
||||||
|
platformSettingOverrides: {}
|
||||||
|
forceToMono: 0
|
||||||
|
normalize: 1
|
||||||
|
loadInBackground: 0
|
||||||
|
ambisonic: 0
|
||||||
|
3D: 1
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/DataTransfer scene and assets.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 16c3692935d75294f9404be0a4ba0039
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
6503
Assets/DataTransfer scene and assets/Upload.unity
Normal file
7
Assets/DataTransfer scene and assets/Upload.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1b8722ddfeb323a4da4a18797ed7df32
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
6522
Assets/DataTransfer scene and assets/download.unity
Normal file
7
Assets/DataTransfer scene and assets/download.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 83edd2ecead106542bc862143208dd4c
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/DataTransfer scene and assets/minigame.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5145a323a08373d4a9074774f7f3c501
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f5e4c6e6f8367342893fd7030d1b4cb
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b02f5e5a2bd2df479219d58104b58e4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9f4fa73205ab4db41871cc3e9260180f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/DataTransfer scene and assets/sprites.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d08b4a9b983113c4a9c56b2738a85291
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!74 &7400000
|
||||||
|
AnimationClip:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: border anim
|
||||||
|
serializedVersion: 7
|
||||||
|
m_Legacy: 0
|
||||||
|
m_Compressed: 0
|
||||||
|
m_UseHighQualityCurve: 1
|
||||||
|
m_RotationCurves: []
|
||||||
|
m_CompressedRotationCurves: []
|
||||||
|
m_EulerCurves: []
|
||||||
|
m_PositionCurves: []
|
||||||
|
m_ScaleCurves: []
|
||||||
|
m_FloatCurves: []
|
||||||
|
m_PPtrCurves: []
|
||||||
|
m_SampleRate: 60
|
||||||
|
m_WrapMode: 0
|
||||||
|
m_Bounds:
|
||||||
|
m_Center: {x: 0, y: 0, z: 0}
|
||||||
|
m_Extent: {x: 0, y: 0, z: 0}
|
||||||
|
m_ClipBindingConstant:
|
||||||
|
genericBindings: []
|
||||||
|
pptrCurveMapping: []
|
||||||
|
m_AnimationClipSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||||
|
m_AdditiveReferencePoseTime: 0
|
||||||
|
m_StartTime: 0
|
||||||
|
m_StopTime: 1
|
||||||
|
m_OrientationOffsetY: 0
|
||||||
|
m_Level: 0
|
||||||
|
m_CycleOffset: 0
|
||||||
|
m_HasAdditiveReferencePose: 0
|
||||||
|
m_LoopTime: 1
|
||||||
|
m_LoopBlend: 0
|
||||||
|
m_LoopBlendOrientation: 0
|
||||||
|
m_LoopBlendPositionY: 0
|
||||||
|
m_LoopBlendPositionXZ: 0
|
||||||
|
m_KeepOriginalOrientation: 0
|
||||||
|
m_KeepOriginalPositionY: 1
|
||||||
|
m_KeepOriginalPositionXZ: 0
|
||||||
|
m_HeightFromFeet: 0
|
||||||
|
m_Mirror: 0
|
||||||
|
m_EditorCurves: []
|
||||||
|
m_EulerEditorCurves: []
|
||||||
|
m_HasGenericRootTransform: 0
|
||||||
|
m_HasMotionFloatCurves: 0
|
||||||
|
m_Events: []
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f5b8b3d1765137a40a4094e14ea0b1c8
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 7400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1102 &-7814012930283619509
|
||||||
|
AnimatorState:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: New Animation
|
||||||
|
m_Speed: 1
|
||||||
|
m_CycleOffset: 0
|
||||||
|
m_Transitions: []
|
||||||
|
m_StateMachineBehaviours: []
|
||||||
|
m_Position: {x: 50, y: 50, z: 0}
|
||||||
|
m_IKOnFeet: 0
|
||||||
|
m_WriteDefaultValues: 1
|
||||||
|
m_Mirror: 0
|
||||||
|
m_SpeedParameterActive: 0
|
||||||
|
m_MirrorParameterActive: 0
|
||||||
|
m_CycleOffsetParameterActive: 0
|
||||||
|
m_TimeParameterActive: 0
|
||||||
|
m_Motion: {fileID: 7400000, guid: f5b8b3d1765137a40a4094e14ea0b1c8, type: 2}
|
||||||
|
m_Tag:
|
||||||
|
m_SpeedParameter:
|
||||||
|
m_MirrorParameter:
|
||||||
|
m_CycleOffsetParameter:
|
||||||
|
m_TimeParameter:
|
||||||
|
--- !u!91 &9100000
|
||||||
|
AnimatorController:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: border
|
||||||
|
serializedVersion: 5
|
||||||
|
m_AnimatorParameters: []
|
||||||
|
m_AnimatorLayers:
|
||||||
|
- serializedVersion: 5
|
||||||
|
m_Name: Base Layer
|
||||||
|
m_StateMachine: {fileID: 2563971018880681404}
|
||||||
|
m_Mask: {fileID: 0}
|
||||||
|
m_Motions: []
|
||||||
|
m_Behaviours: []
|
||||||
|
m_BlendingMode: 0
|
||||||
|
m_SyncedLayerIndex: -1
|
||||||
|
m_DefaultWeight: 0
|
||||||
|
m_IKPass: 0
|
||||||
|
m_SyncedLayerAffectsTiming: 0
|
||||||
|
m_Controller: {fileID: 9100000}
|
||||||
|
--- !u!1107 &2563971018880681404
|
||||||
|
AnimatorStateMachine:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 1
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: Base Layer
|
||||||
|
m_ChildStates:
|
||||||
|
- serializedVersion: 1
|
||||||
|
m_State: {fileID: -7814012930283619509}
|
||||||
|
m_Position: {x: 270, y: 0, z: 0}
|
||||||
|
m_ChildStateMachines: []
|
||||||
|
m_AnyStateTransitions: []
|
||||||
|
m_EntryTransitions: []
|
||||||
|
m_StateMachineTransitions: {}
|
||||||
|
m_StateMachineBehaviours: []
|
||||||
|
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||||
|
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||||
|
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||||
|
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||||
|
m_DefaultState: {fileID: -7814012930283619509}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cecdd3ffd08949d49bfa9bad93dddd3b
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 9100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading0.png
Normal file
|
After Width: | Height: | Size: 469 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading0.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 79c3437643e68be4e88c3bf039f0680d
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading1.png
Normal file
|
After Width: | Height: | Size: 435 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading1.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d962c88742b40ec4594c568cba2848e4
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading2.png
Normal file
|
After Width: | Height: | Size: 405 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading2.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d68a7660c51d4454f915a1c427cb01ce
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading3.png
Normal file
|
After Width: | Height: | Size: 371 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading3.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 805221047ed3e7c48a13ff21d97f6c66
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading4.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading4.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b3f2382597d46c640ab466c1609bd193
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading5.png
Normal file
|
After Width: | Height: | Size: 308 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading5.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c1f13902211756a4d9b7246f52ac5005
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading6.png
Normal file
|
After Width: | Height: | Size: 276 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading6.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 997acda7ef7df0e4eaeb2a8dff863abf
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading7.png
Normal file
|
After Width: | Height: | Size: 248 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading7.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8292414d4bad4364f874555af2f7e712
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/loading8.png
Normal file
|
After Width: | Height: | Size: 248 KiB |
117
Assets/DataTransfer scene and assets/sprites/loading8.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3b63bdfb82042f94887a00a48094f69a
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/sipka 1.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
117
Assets/DataTransfer scene and assets/sprites/sipka 1.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ae21ad83b0f7d5941822a82c37238864
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/sipka 2.png
Normal file
|
After Width: | Height: | Size: 189 KiB |
117
Assets/DataTransfer scene and assets/sprites/sipka 2.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7a6ffeb1058a6f8409a669fbd1d5c463
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/sipka 3.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
117
Assets/DataTransfer scene and assets/sprites/sipka 3.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 68ab2eea03d99d544b9c5c607019b2c0
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Assets/DataTransfer scene and assets/sprites/sipka 4.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
117
Assets/DataTransfer scene and assets/sprites/sipka 4.png.meta
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7a206d138cef964aa45af4cfa97fa9a
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
After Width: | Height: | Size: 43 KiB |
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a04092104e630434a84804e17040195a
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
After Width: | Height: | Size: 51 KiB |
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 982be63b1292049488295e60ce74abe2
|
||||||
|
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: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
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
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8fa0d9c695119af49bd1693054cf3174
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Editor/com.unity.mobile.notifications.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 70729d202603eef42955f52bd64f7c69
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 0863bf92b4fcc45b0b9267325249bf0f, type: 3}
|
||||||
|
m_Name: NotificationSettings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
toolbarInt: 0
|
||||||
|
iOSNotificationEditorSettingsValues:
|
||||||
|
keys:
|
||||||
|
- UnityNotificationRequestAuthorizationOnAppLaunch
|
||||||
|
- UnityNotificationDefaultAuthorizationOptions
|
||||||
|
- UnityAddRemoteNotificationCapability
|
||||||
|
- UnityNotificationRequestAuthorizationForRemoteNotificationsOnAppLaunch
|
||||||
|
- UnityRemoteNotificationForegroundPresentationOptions
|
||||||
|
- UnityUseAPSReleaseEnvironment
|
||||||
|
- UnityUseLocationNotificationTrigger
|
||||||
|
values:
|
||||||
|
- True
|
||||||
|
- 7
|
||||||
|
- False
|
||||||
|
- False
|
||||||
|
- -1
|
||||||
|
- False
|
||||||
|
- False
|
||||||
|
AndroidNotificationEditorSettingsValues:
|
||||||
|
keys:
|
||||||
|
- UnityNotificationAndroidRescheduleOnDeviceRestart
|
||||||
|
- UnityNotificationAndroidUseCustomActivity
|
||||||
|
- UnityNotificationAndroidCustomActivityString
|
||||||
|
values:
|
||||||
|
- False
|
||||||
|
- False
|
||||||
|
- com.unity3d.player.UnityPlayerActivity
|
||||||
|
TrackedResourceAssets: []
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 55822530f24ba9b4c9950ed46293252f
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/GameManager.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bbd26b895bc2b894b8989c08d9fd9197
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
92
Assets/GameManager/AreaMat.mat
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: AreaMat
|
||||||
|
m_Shader: {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords: []
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_LockedProperties:
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _AlphaTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _BumpMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailAlbedoMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailMask:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _DetailNormalMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _EmissionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MetallicGlossMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _OcclusionMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _ParallaxMap:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Ints: []
|
||||||
|
m_Floats:
|
||||||
|
- PixelSnap: 0
|
||||||
|
- _BumpScale: 1
|
||||||
|
- _Cutoff: 0.5
|
||||||
|
- _DetailNormalMapScale: 1
|
||||||
|
- _DstBlend: 0
|
||||||
|
- _EnableExternalAlpha: 0
|
||||||
|
- _GlossMapScale: 1
|
||||||
|
- _Glossiness: 0.5
|
||||||
|
- _GlossyReflections: 1
|
||||||
|
- _Metallic: 0
|
||||||
|
- _Mode: 0
|
||||||
|
- _OcclusionStrength: 1
|
||||||
|
- _Parallax: 0.02
|
||||||
|
- _SmoothnessTextureChannel: 0
|
||||||
|
- _SpecularHighlights: 1
|
||||||
|
- _SrcBlend: 1
|
||||||
|
- _UVSec: 0
|
||||||
|
- _ZWrite: 1
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 0.0813297, g: 1, b: 0, a: 1}
|
||||||
|
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||||
|
- _Flip: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _RendererColor: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
8
Assets/GameManager/AreaMat.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5a46533bdf4003449bc9146ccef44e27
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,91 +1,755 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using GeoSus.Client;
|
using GeoSus.Client;
|
||||||
using Subsystems;
|
using Subsystems;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
/*
|
using UnityEngine.SceneManagement;
|
||||||
GameManager - hlavní tøida pro správu hry
|
|
||||||
GameManager_Network - subsystém pro správu komunikace se serverem
|
|
||||||
GameManager_Game - subsystém pro správu logiky hry (sabotáže, tasky, atd.)
|
|
||||||
GameManager_Map - subsystém pro správu mapy a prostøedí
|
|
||||||
GameManager_Input - subsystém pro správu vstupu od hráèe
|
|
||||||
GameManager_UI - subsystém pro správu uživatelského rozhraní
|
|
||||||
GamaManager_Stats - subsystém pro správu statistik pro server
|
|
||||||
*/
|
|
||||||
public class GameManager : MonoBehaviour
|
public class GameManager : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
// Singleton
|
||||||
|
public static GameManager Instance { get; private set; }
|
||||||
|
|
||||||
[Header("Subsystems")]
|
[Header("Subsystems")]
|
||||||
protected GameManager_Network networkSubsystem;
|
public GameManager_Network networkSubsystem;
|
||||||
protected GameManager_UI uiSubsystem;
|
public GameManager_UI uiSubsystem;
|
||||||
|
public GameManager_Map mapSubsystem;
|
||||||
|
public GameManager_Input inputSubsystem;
|
||||||
|
public GameManager_Tasks taskSubsystem;
|
||||||
|
|
||||||
|
public GameClient gameClient;
|
||||||
|
|
||||||
protected GameClient gameClient;
|
|
||||||
[Header("Player Info")]
|
[Header("Player Info")]
|
||||||
public string displayName;
|
public string displayName;
|
||||||
|
|
||||||
[Header("UI Elements")]
|
[Header("Scene Management")]
|
||||||
public Canvas JoinCreateLobby;
|
[SerializeField] public string firstMenuScene = "main menu asi idk lol";
|
||||||
public Canvas InLobby;
|
|
||||||
|
[Header("UI Elements (Client.unity)")]
|
||||||
|
// Canvas names in Client.unity — found at runtime in OnSceneLoaded
|
||||||
|
private const string CanvasNameJoinCreate = "LobbySelector";
|
||||||
|
private const string CanvasNameInLobby = "InLobby";
|
||||||
|
private const string CanvasNameLoading = "LoadingScreen";
|
||||||
|
private const string CanvasNameGame = "InGame";
|
||||||
|
|
||||||
|
[Header("Map")]
|
||||||
|
// MapCenterPoint and Player are in Client.unity — wired at runtime in OnSceneLoaded.
|
||||||
|
// buildingSettings/pathwaySettings/areaSettings must be assigned in SampleScene Inspector.
|
||||||
|
public BuildingSettings buildingSettings;
|
||||||
|
public PathwaySettings pathwaySettings;
|
||||||
|
public AreaSettings areaSettings;
|
||||||
|
|
||||||
|
[Header("Lobby Settings")]
|
||||||
|
public double pendingRadius = 500;
|
||||||
|
public int pendingImpostorCount = 1;
|
||||||
|
public int pendingTaskCount = 5;
|
||||||
|
/// <summary>
|
||||||
|
/// P13b/c: full settings overrides accumulated by HostLobbyUI before the
|
||||||
|
/// host taps "Create". Null = host didn't change anything beyond the three
|
||||||
|
/// flat fields above; server falls through to its current defaults for
|
||||||
|
/// every field. Each field is independently nullable so the host can
|
||||||
|
/// opt into changing only what they care about.
|
||||||
|
/// </summary>
|
||||||
|
public GameSettingsOverrides pendingSettings;
|
||||||
|
|
||||||
|
[Header("Task Minigames (round-robin)")]
|
||||||
|
// Names MUST match the scene file names in Assets/Scenes (case-sensitive)
|
||||||
|
// and each one MUST be enabled in EditorBuildSettings, or LoadSceneAsync
|
||||||
|
// will silently fail and the task button will appear dead.
|
||||||
|
[SerializeField] public string[] minigameScenes = {
|
||||||
|
"MiniGame-Kabely V10",
|
||||||
|
"MiniGame-insertkeys",
|
||||||
|
"MiniGame-FlappyBird",
|
||||||
|
"MiniGame-ThrowInHole",
|
||||||
|
"MiniGame-Satelit"
|
||||||
|
// Add minigame scene name here
|
||||||
|
};
|
||||||
|
|
||||||
|
[Header("Debug")]
|
||||||
|
public bool testMode = false;
|
||||||
|
/// <summary>
|
||||||
|
/// When true, draw a small GPS status banner across the top of every
|
||||||
|
/// screen. Useful for diagnosing why CreateLobby is blocked or why a
|
||||||
|
/// joiner's position isn't updating - failures otherwise only show up
|
||||||
|
/// in logcat which most users can't reach. Toggle off for release.
|
||||||
|
/// </summary>
|
||||||
|
public bool showGPSDebugOverlay = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of in-process test client bots to spawn alongside the host
|
||||||
|
/// when testMode is on. Each gets its own GameClient + Network and
|
||||||
|
/// joins the host's lobby automatically. Bots are switchable via
|
||||||
|
/// number keys 1..N (host = 0). Default 3 keeps memory reasonable;
|
||||||
|
/// bump for stress-testing voting / sabotage flows.
|
||||||
|
/// </summary>
|
||||||
|
public int testClientCount = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Per-bot network + display-name + sim-position state. The active slot
|
||||||
|
/// (host = 0, bots = 1..N) gets WASD on the next tick.
|
||||||
|
/// </summary>
|
||||||
|
private class TestBot
|
||||||
|
{
|
||||||
|
public GameClient Client;
|
||||||
|
public GameManager_Network Network;
|
||||||
|
public string DisplayName;
|
||||||
|
public GeoSus.Client.Position SimPosition;
|
||||||
|
public bool Joined;
|
||||||
|
public float LastSendTime;
|
||||||
|
}
|
||||||
|
private System.Collections.Generic.List<TestBot> _testBots = new System.Collections.Generic.List<TestBot>();
|
||||||
|
/// <summary>Slot 0 = host (real player), 1..N = test bot index.</summary>
|
||||||
|
private int _activeClientSlot = 0;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
if (Instance != null && Instance != this)
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Instance = this;
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
|
||||||
|
// Keep the screen on while the player is in the app. A geographic
|
||||||
|
// social-deduction game asks the user to walk around for 5-15 minutes
|
||||||
|
// staring at the map; default Android sleep timeout (15-60s) blacks
|
||||||
|
// the screen out mid-round, drops GPS updates, and requires the
|
||||||
|
// player to re-unlock the phone. Two layers of belt-and-suspenders:
|
||||||
|
// (1) Unity's Screen.sleepTimeout, which works on most devices and
|
||||||
|
// is one line, but is overridden by some MIUI/EMUI ROMs.
|
||||||
|
// (2) Android FLAG_KEEP_SCREEN_ON on the activity window, harder for
|
||||||
|
// OEM ROMs to override and the standard pattern for navigation/maps
|
||||||
|
// apps. Wrapped in #if UNITY_ANDROID so editor/iOS skip it.
|
||||||
|
Screen.sleepTimeout = SleepTimeout.NeverSleep;
|
||||||
|
AcquireAndroidWakelock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set FLAG_KEEP_SCREEN_ON on the Unity activity's window. This is the
|
||||||
|
/// standard navigation/maps-app pattern and survives ROM-level overrides
|
||||||
|
/// of Unity's Screen.sleepTimeout. No-op on non-Android platforms.
|
||||||
|
/// </summary>
|
||||||
|
private static void AcquireAndroidWakelock()
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var player = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
|
||||||
|
using (var activity = player.GetStatic<AndroidJavaObject>("currentActivity"))
|
||||||
|
{
|
||||||
|
// addFlags must run on the UI thread. Capture activity into a
|
||||||
|
// local for the closure - AndroidJavaObject can be reused.
|
||||||
|
var act = activity;
|
||||||
|
act.Call("runOnUiThread", new AndroidJavaRunnable(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var window = act.Call<AndroidJavaObject>("getWindow"))
|
||||||
|
{
|
||||||
|
// WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||||
|
const int FLAG_KEEP_SCREEN_ON = 0x00000080;
|
||||||
|
window.Call("addFlags", FLAG_KEEP_SCREEN_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Wakelock] addFlags failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Wakelock] Android JNI bridge failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
DontDestroyOnLoad(this);
|
// The prefab default in SampleScene.unity is "Hrac" (Czech for
|
||||||
if (displayName == null || displayName == "")
|
// "player"). Treat it as equivalent to "no name set" so users who
|
||||||
|
// never customize their name don't all show up identically. This
|
||||||
|
// override only fires at startup; users who explicitly type "Hrac"
|
||||||
|
// into the nickname field will still send "Hrac" via the live
|
||||||
|
// DisplayName payload field.
|
||||||
|
if (string.IsNullOrEmpty(displayName) || displayName == "Hrac")
|
||||||
|
displayName = PlayerPrefs.GetString("PlayerName", GenerateUsername());
|
||||||
|
|
||||||
|
gameClient = new GameClient(GenerateUUID(), displayName);
|
||||||
|
networkSubsystem = new GameManager_Network(gameClient, this);
|
||||||
|
mapSubsystem = new GameManager_Map(gameClient, null, buildingSettings, pathwaySettings, areaSettings);
|
||||||
|
uiSubsystem = new GameManager_UI(gameClient);
|
||||||
|
inputSubsystem = new GameManager_Input(gameClient, null, testMode);
|
||||||
|
taskSubsystem = new GameManager_Tasks(gameClient, minigameScenes, this);
|
||||||
|
|
||||||
|
if (testMode)
|
||||||
{
|
{
|
||||||
displayName = "Player_" + Random.Range(1000, 9999).ToString();
|
int n = Mathf.Max(0, testClientCount);
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
var bot = new TestBot
|
||||||
|
{
|
||||||
|
DisplayName = "TestBot" + (i + 1),
|
||||||
|
};
|
||||||
|
bot.Client = new GameClient(GenerateUUID(), bot.DisplayName);
|
||||||
|
bot.Network = new GameManager_Network(bot.Client, null);
|
||||||
|
bot.Network.OpenConnection();
|
||||||
|
_testBots.Add(bot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gameClient = new GameClient(GenerateUUID(), /*displayName*/ GenerateUsername());
|
|
||||||
uiSubsystem = new GameManager_UI(gameClient, JoinCreateLobby, InLobby);
|
networkSubsystem.OpenConnection();
|
||||||
networkSubsystem = new GameManager_Network(gameClient);
|
|
||||||
networkSubsystem.OpenConection();
|
// Start GPS immediately at app launch. Acquiring a fix on a cold
|
||||||
|
// device can take 5-30 seconds; if we wait until CreateLobby is
|
||||||
|
// pressed, the lobby will be seeded with bad coords. Starting here
|
||||||
|
// means the user's normal navigation through the menus gives the
|
||||||
|
// GPS subsystem time to settle.
|
||||||
|
inputSubsystem?.EnsureGPSStarted();
|
||||||
|
|
||||||
|
// Load main menu after GameManager is ready
|
||||||
|
if (!string.IsNullOrEmpty(firstMenuScene))
|
||||||
|
SceneManager.LoadScene(firstMenuScene, LoadSceneMode.Single);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws a GPS status banner across the top of every screen. We use OnGUI
|
||||||
|
/// rather than a uGUI Canvas element because OnGUI works without any
|
||||||
|
/// scene wiring - we want this visible from the very first frame, on
|
||||||
|
/// every screen, even if the lobby canvas hasn't been bound yet. This is
|
||||||
|
/// a debug overlay; toggle showGPSDebugOverlay off for release builds.
|
||||||
|
/// </summary>
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
if (!showGPSDebugOverlay) return;
|
||||||
|
if (inputSubsystem == null) return;
|
||||||
|
|
||||||
|
var diag = inputSubsystem.GpsDiagnostic;
|
||||||
|
var label = "GPS: " + diag;
|
||||||
|
|
||||||
|
// Scale font size to screen so it's legible on phones (HDPI) and
|
||||||
|
// editor (lower DPI) alike. Phones tend to have ~400dpi; the
|
||||||
|
// editor game view runs at ~100dpi.
|
||||||
|
int fontSize = Mathf.Max(14, Screen.width / 50);
|
||||||
|
|
||||||
|
var style = new GUIStyle(GUI.skin.label)
|
||||||
|
{
|
||||||
|
fontSize = fontSize,
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
alignment = TextAnchor.MiddleLeft,
|
||||||
|
wordWrap = false,
|
||||||
|
normal = { textColor = Color.white }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Width covers most of the screen so longer error strings don't get
|
||||||
|
// clipped. Height auto-fits the chosen font size.
|
||||||
|
float pad = fontSize * 0.5f;
|
||||||
|
float bannerH = fontSize * 2f;
|
||||||
|
var rect = new Rect(pad, pad, Screen.width - pad * 2, bannerH);
|
||||||
|
|
||||||
|
// Translucent black background for legibility against the map.
|
||||||
|
var prevColor = GUI.color;
|
||||||
|
GUI.color = new Color(0f, 0f, 0f, 0.65f);
|
||||||
|
GUI.Box(rect, GUIContent.none);
|
||||||
|
GUI.color = prevColor;
|
||||||
|
|
||||||
|
// Indent the label inside the box.
|
||||||
|
var textRect = new Rect(rect.x + pad, rect.y, rect.width - pad * 2, rect.height);
|
||||||
|
GUI.Label(textRect, label, style);
|
||||||
|
|
||||||
|
// Second row: position-source picker (tap to cycle) + active client
|
||||||
|
// indicator (testMode only). Both are diagnostic; the source picker
|
||||||
|
// is the recovery path when one backend silently fails on a phone.
|
||||||
|
float row2Y = rect.y + bannerH + pad * 0.5f;
|
||||||
|
var btnStyle = new GUIStyle(GUI.skin.button)
|
||||||
|
{
|
||||||
|
fontSize = Mathf.Max(12, fontSize - 2),
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
alignment = TextAnchor.MiddleCenter,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Source button: shows current source name + invites tap.
|
||||||
|
string sourceLabel = "Source: " + inputSubsystem.CurrentSourceName + " [tap to cycle]";
|
||||||
|
// Width sized to the text so the touch area matches the label.
|
||||||
|
Vector2 sourceSize = btnStyle.CalcSize(new GUIContent(sourceLabel));
|
||||||
|
float sourceW = Mathf.Min(Screen.width - pad * 2, sourceSize.x + pad * 2);
|
||||||
|
var sourceRect = new Rect(pad, row2Y, sourceW, bannerH);
|
||||||
|
if (GUI.Button(sourceRect, sourceLabel, btnStyle))
|
||||||
|
{
|
||||||
|
inputSubsystem.CycleNextPositionSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active-client indicator (only when we have test bots).
|
||||||
|
if (testMode && _testBots.Count > 0)
|
||||||
|
{
|
||||||
|
string slot = _activeClientSlot == 0 ? "Host" : ("Bot " + _activeClientSlot);
|
||||||
|
string indicator = $"WASD: {slot} (0..{_testBots.Count} to switch)";
|
||||||
|
var indStyle = new GUIStyle(GUI.skin.label)
|
||||||
|
{
|
||||||
|
fontSize = Mathf.Max(12, fontSize - 2),
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
alignment = TextAnchor.MiddleLeft,
|
||||||
|
normal = { textColor = new Color(0.9f, 1f, 0.4f) },
|
||||||
|
};
|
||||||
|
Vector2 indSize = indStyle.CalcSize(new GUIContent(indicator));
|
||||||
|
var indRect = new Rect(sourceRect.xMax + pad, row2Y, indSize.x + pad * 2, bannerH);
|
||||||
|
GUI.color = new Color(0f, 0f, 0f, 0.65f);
|
||||||
|
GUI.Box(indRect, GUIContent.none);
|
||||||
|
GUI.color = prevColor;
|
||||||
|
GUI.Label(new Rect(indRect.x + pad, indRect.y, indRect.width, indRect.height), indicator, indStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (gameClient.CurrentLobbyState != null)
|
// Tick the SDK dispatcher so callbacks fire on main thread
|
||||||
|
gameClient?.Update();
|
||||||
|
if (testMode)
|
||||||
{
|
{
|
||||||
uiSubsystem.UpdateLobbyUI();
|
for (int i = 0; i < _testBots.Count; i++)
|
||||||
|
_testBots[i].Client?.Update();
|
||||||
|
HandleTestBotInput();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (gameClient?.CurrentLobbyState != null)
|
||||||
protected string GenerateUUID()
|
|
||||||
{
|
|
||||||
string UUID = System.Guid.NewGuid().ToString();
|
|
||||||
Debug.Log(UUID);
|
|
||||||
return UUID;
|
|
||||||
}
|
|
||||||
protected string GenerateUsername()
|
|
||||||
{
|
|
||||||
string Username = Random.Range(0,10).ToString() + Random.Range(0, 10).ToString() + Random.Range(0, 10).ToString() + Random.Range(0, 10).ToString();
|
|
||||||
Debug.Log(Username);
|
|
||||||
return Username;
|
|
||||||
}
|
|
||||||
public void CreateLobbyButton()
|
|
||||||
{
|
|
||||||
networkSubsystem.CrateLobby(50.0755, 14.4378);
|
|
||||||
}
|
|
||||||
public void JoinLobbyButton()
|
|
||||||
{
|
|
||||||
TMP_InputField joinCode = JoinCreateLobby.transform.Find("InputCode").GetComponent<TMP_InputField>();
|
|
||||||
if (joinCode.text != null && joinCode.text != "")
|
|
||||||
{
|
{
|
||||||
networkSubsystem.JoinLobby(joinCode.text);
|
uiSubsystem?.UpdateLobbyUI();
|
||||||
|
taskSubsystem?.UpdateProximity();
|
||||||
|
}
|
||||||
|
if (gameClient?.MyRole == PlayerRole.Impostor)
|
||||||
|
UpdateKillCooldown();
|
||||||
|
|
||||||
|
inputSubsystem?.positionCheck();
|
||||||
|
|
||||||
|
if (testMode) StepActiveTestBot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number-key handling for slot switching. 0 = host, 1..N = test bot N.
|
||||||
|
/// Suppress host WASD when a non-host bot is active so the host capsule
|
||||||
|
/// doesn't drift while the user is moving a bot. Only fires when
|
||||||
|
/// testMode is on; release builds never see this path.
|
||||||
|
/// </summary>
|
||||||
|
private void HandleTestBotInput()
|
||||||
|
{
|
||||||
|
// 0 = host. 1..9 = bots (capped by Unity KeyCode.Alpha9).
|
||||||
|
if (Input.GetKeyDown(KeyCode.Alpha0)) _activeClientSlot = 0;
|
||||||
|
for (int i = 1; i <= 9 && i <= _testBots.Count; i++)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Alpha0 + i)) _activeClientSlot = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the host's input subsystem to ignore WASD when a bot is active.
|
||||||
|
if (inputSubsystem != null)
|
||||||
|
inputSubsystem.SuppressWasd = (_activeClientSlot != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the active slot is a bot, step its sim position from WASD axes
|
||||||
|
/// and send to the server. Idle bots get a periodic keep-alive so their
|
||||||
|
/// avatars don't time out.
|
||||||
|
/// </summary>
|
||||||
|
private void StepActiveTestBot()
|
||||||
|
{
|
||||||
|
if (_testBots.Count == 0) return;
|
||||||
|
var state = gameClient?.CurrentLobbyState;
|
||||||
|
if (state == null || state.MapData == null) return;
|
||||||
|
|
||||||
|
// Lazy-init each bot's sim position to the lobby's map center on
|
||||||
|
// first lobby state. Until the bot has joined a lobby it can't
|
||||||
|
// send position updates.
|
||||||
|
for (int i = 0; i < _testBots.Count; i++)
|
||||||
|
{
|
||||||
|
var bot = _testBots[i];
|
||||||
|
if (!bot.Joined) continue;
|
||||||
|
if (bot.SimPosition.Lat == 0 && bot.SimPosition.Lon == 0)
|
||||||
|
{
|
||||||
|
// Spawn each bot in a small ring around the map center so
|
||||||
|
// they don't all stack on top of each other on frame one.
|
||||||
|
double offsetLat = 0.00003 * Mathf.Cos(i * Mathf.PI * 2f / Mathf.Max(1, _testBots.Count));
|
||||||
|
double offsetLon = 0.00003 * Mathf.Sin(i * Mathf.PI * 2f / Mathf.Max(1, _testBots.Count));
|
||||||
|
bot.SimPosition = new GeoSus.Client.Position(
|
||||||
|
state.MapData.Center.Lat + offsetLat,
|
||||||
|
state.MapData.Center.Lon + offsetLon);
|
||||||
|
bot.Client.UpdatePosition(bot.SimPosition);
|
||||||
|
bot.LastSendTime = Time.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WASD only drives the active bot.
|
||||||
|
if (_activeClientSlot >= 1 && _activeClientSlot <= _testBots.Count)
|
||||||
|
{
|
||||||
|
var bot = _testBots[_activeClientSlot - 1];
|
||||||
|
if (bot.Joined)
|
||||||
|
{
|
||||||
|
float dx = Input.GetAxis("Horizontal");
|
||||||
|
float dy = Input.GetAxis("Vertical");
|
||||||
|
const double speed = 0.00001;
|
||||||
|
bool moved = Mathf.Abs(dx) > 0.001f || Mathf.Abs(dy) > 0.001f;
|
||||||
|
if (moved)
|
||||||
|
{
|
||||||
|
bot.SimPosition = new GeoSus.Client.Position(
|
||||||
|
bot.SimPosition.Lat + dy * speed,
|
||||||
|
bot.SimPosition.Lon + dx * speed);
|
||||||
|
}
|
||||||
|
// Send on movement OR on keep-alive cadence so the server
|
||||||
|
// doesn't drop our presence.
|
||||||
|
bool dueKeepAlive = (Time.time - bot.LastSendTime) >= 1.0f;
|
||||||
|
if (moved || dueKeepAlive)
|
||||||
|
{
|
||||||
|
bot.Client.UpdatePosition(bot.SimPosition);
|
||||||
|
bot.LastSendTime = Time.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Log("Join code is empty!");
|
// No bot is active. All bots get keep-alive only.
|
||||||
|
for (int i = 0; i < _testBots.Count; i++)
|
||||||
|
{
|
||||||
|
var bot = _testBots[i];
|
||||||
|
if (!bot.Joined) continue;
|
||||||
|
if ((Time.time - bot.LastSendTime) >= 1.0f)
|
||||||
|
{
|
||||||
|
bot.Client.UpdatePosition(bot.SimPosition);
|
||||||
|
bot.LastSendTime = Time.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OnEnable()
|
||||||
|
{
|
||||||
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||||
|
}
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// After Client.unity loads, re-bind all canvas/HUD references because
|
||||||
|
/// those GameObjects don't exist in the Art menu scenes.
|
||||||
|
/// </summary>
|
||||||
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||||
|
{
|
||||||
|
if (scene.name == "Client")
|
||||||
|
{
|
||||||
|
var roots = scene.GetRootGameObjects();
|
||||||
|
|
||||||
|
// Find a root or deep GameObject by name in the loaded scene
|
||||||
|
GameObject FindGO(string n) {
|
||||||
|
foreach (var go in roots) {
|
||||||
|
if (go.name == n) return go;
|
||||||
|
var found = go.transform.Find(n);
|
||||||
|
if (found != null) return found.gameObject;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Canvas FindCanvas(string n) {
|
||||||
|
var go = FindGO(n);
|
||||||
|
return go != null ? go.GetComponent<Canvas>() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Build HUD BEFORE BindClientScene so FindTMP/Find can locate new elements ──
|
||||||
|
var inGameGO = FindGO("InGame");
|
||||||
|
if (inGameGO != null)
|
||||||
|
{
|
||||||
|
var builder = inGameGO.GetComponent<InGameHUDBuilder>()
|
||||||
|
?? inGameGO.AddComponent<InGameHUDBuilder>();
|
||||||
|
builder.BuildNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Wire canvases (after HUD is built) ──
|
||||||
|
// Apply our standard CanvasScaler (1080x1920 reference, match=0.5)
|
||||||
|
// to every canvas in the scene before binding so layouts scale
|
||||||
|
// identically across phones and tablets without per-device tweaks.
|
||||||
|
var cJoin = FindCanvas(CanvasNameJoinCreate);
|
||||||
|
var cLobby = FindCanvas(CanvasNameInLobby);
|
||||||
|
var cLoad = FindCanvas(CanvasNameLoading);
|
||||||
|
var cGame = FindCanvas(CanvasNameGame);
|
||||||
|
InGameHUDBuilder.ConfigureCanvasScaler(cJoin);
|
||||||
|
InGameHUDBuilder.ConfigureCanvasScaler(cLobby);
|
||||||
|
InGameHUDBuilder.ConfigureCanvasScaler(cLoad);
|
||||||
|
InGameHUDBuilder.ConfigureCanvasScaler(cGame);
|
||||||
|
uiSubsystem?.BindClientScene(cJoin, cLobby, cLoad, cGame);
|
||||||
|
|
||||||
|
// ── Wire map center point and player capsule ──
|
||||||
|
var mapCenter = FindGO("MapCenterPoint");
|
||||||
|
var player = FindGO("Capsule");
|
||||||
|
mapSubsystem?.SetMapCenterPoint(mapCenter);
|
||||||
|
inputSubsystem?.SetPlayerObject(player);
|
||||||
|
|
||||||
|
// ── Attach camera controller to Main Camera ──
|
||||||
|
var mainCamGO = FindGO("Main Camera");
|
||||||
|
if (mainCamGO != null)
|
||||||
|
{
|
||||||
|
var camCtrl = mainCamGO.GetComponent<MapCameraController>()
|
||||||
|
?? mainCamGO.AddComponent<MapCameraController>();
|
||||||
|
camCtrl.SetTarget(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If MapDataReady arrived before Client scene finished loading,
|
||||||
|
// this will build the map now that scene references are valid.
|
||||||
|
networkSubsystem?.OnClientSceneReady();
|
||||||
|
}
|
||||||
|
else if (scene.name == "create" || scene.name == "join loading")
|
||||||
|
{
|
||||||
|
// Lobby scene just loaded — ensure LobbyDisplayUI refreshes once
|
||||||
|
// its Start() has run and registered itself (happens before Update).
|
||||||
|
uiSubsystem?.NotifyLobbyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _killCooldownSeconds = 0f;
|
||||||
|
private const float KillCooldownDuration = 20f;
|
||||||
|
|
||||||
|
private void UpdateKillCooldown()
|
||||||
|
{
|
||||||
|
if (_killCooldownSeconds > 0)
|
||||||
|
{
|
||||||
|
_killCooldownSeconds -= Time.deltaTime;
|
||||||
|
// Mirror into GameState so UI reads from the single source of truth
|
||||||
|
if (networkSubsystem?.State != null)
|
||||||
|
networkSubsystem.State.KillCooldownRemaining = _killCooldownSeconds;
|
||||||
|
uiSubsystem?.SetKillCooldownText($"Kill: {Mathf.CeilToInt(_killCooldownSeconds)}s");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_killCooldownSeconds = 0f;
|
||||||
|
if (networkSubsystem?.State != null)
|
||||||
|
networkSubsystem.State.KillCooldownRemaining = 0;
|
||||||
|
uiSubsystem?.SetKillCooldownText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called by the ActionButton. Routes to kill / report / emergency / use-task
|
||||||
|
/// depending on current proximity state.
|
||||||
|
/// </summary>
|
||||||
|
public void PerformAction()
|
||||||
|
{
|
||||||
|
if (uiSubsystem == null || uiSubsystem.IsPlayerDead) return;
|
||||||
|
|
||||||
|
bool isImpostor = gameClient?.MyRole == PlayerRole.Impostor;
|
||||||
|
|
||||||
|
// P13b: pull per-lobby distances from the server-snapshotted settings
|
||||||
|
// instead of hardcoding 5m for every check. ?? fallback keeps the
|
||||||
|
// pre-P13b behavior on old server builds that don't ship settings.
|
||||||
|
var settings = networkSubsystem?.State?.Settings;
|
||||||
|
double reportDist = settings?.ReportDistanceM ?? 5.0;
|
||||||
|
double emergencyDist = settings?.EmergencyMeetingCallRadiusM ?? 5.0;
|
||||||
|
double killDist = settings?.KillDistanceM ?? 5.0;
|
||||||
|
|
||||||
|
// 1. Nearby task → USE
|
||||||
|
var nearbyTask = taskSubsystem?.NearbyTask;
|
||||||
|
if (nearbyTask != null && !isImpostor)
|
||||||
|
{
|
||||||
|
taskSubsystem.TriggerNearbyTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Nearby body → REPORT
|
||||||
|
if (!uiSubsystem.IsCommsBlackout)
|
||||||
|
{
|
||||||
|
var nearbyBody = gameClient?.FindNearbyBody(reportDist);
|
||||||
|
if (nearbyBody != null)
|
||||||
|
{
|
||||||
|
gameClient.ReportBody(nearbyBody.BodyId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Near map centre → EMERGENCY
|
||||||
|
if (gameClient?.CurrentLobbyState?.MapData != null)
|
||||||
|
{
|
||||||
|
double distToCenter = gameClient.MyPosition.DistanceTo(gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
if (distToCenter <= emergencyDist)
|
||||||
|
{
|
||||||
|
gameClient.CallEmergencyMeeting();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Impostor kill
|
||||||
|
if (isImpostor && _killCooldownSeconds <= 0)
|
||||||
|
{
|
||||||
|
var targetUuid = gameClient?.FindNearbyPlayer(killDist);
|
||||||
|
if (!string.IsNullOrEmpty(targetUuid))
|
||||||
|
{
|
||||||
|
gameClient.Kill(targetUuid);
|
||||||
|
_killCooldownSeconds = KillCooldownDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called by Impostor sabotage buttons.</summary>
|
||||||
|
public void StartSabotage(int typeIndex)
|
||||||
|
{
|
||||||
|
gameClient?.Send(new GeoSus.Client.StartSabotage { SabotageType = (SabotageType)typeIndex });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called by the meeting vote buttons. Pass null to skip.</summary>
|
||||||
|
public void CastVote(string targetUuid)
|
||||||
|
{
|
||||||
|
gameClient?.Vote(targetUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string GenerateUUID()
|
||||||
|
{
|
||||||
|
return System.Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
protected string GenerateUsername()
|
||||||
|
{
|
||||||
|
return "Player" + UnityEngine.Random.Range(1000, 9999).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pull the nickname input field's current text into displayName +
|
||||||
|
/// gameClient.DisplayName + PlayerPrefs before sending a network
|
||||||
|
/// action. Defensive against any TMP_InputField / soft-keyboard race
|
||||||
|
/// where the user types and immediately taps a button: onValueChanged
|
||||||
|
/// normally fires before the click handler in the same frame, but
|
||||||
|
/// some Android keyboards batch text events oddly. Call this at the
|
||||||
|
/// top of any Create/Join/Rename flow. No-op if the input field
|
||||||
|
/// doesn't exist in the current scene.
|
||||||
|
/// </summary>
|
||||||
|
private void CommitNicknameFromInput()
|
||||||
|
{
|
||||||
|
var nameGO = GameObject.Find("name");
|
||||||
|
if (nameGO == null) return;
|
||||||
|
var field = nameGO.GetComponent<TMPro.TMP_InputField>();
|
||||||
|
if (field == null) return;
|
||||||
|
// Force the InputField to flush any pending soft-keyboard text.
|
||||||
|
// ForceLabelUpdate() is harmless if there's nothing pending.
|
||||||
|
field.ForceLabelUpdate();
|
||||||
|
string typed = (field.text ?? "").Trim();
|
||||||
|
if (string.IsNullOrEmpty(typed)) return;
|
||||||
|
if (typed == displayName) return; // already in sync, skip the writes
|
||||||
|
displayName = typed;
|
||||||
|
if (gameClient != null) gameClient.DisplayName = typed;
|
||||||
|
PlayerPrefs.SetString("PlayerName", typed);
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by HostLobbyUI
|
||||||
|
public void CreateLobbyButton()
|
||||||
|
{
|
||||||
|
CommitNicknameFromInput();
|
||||||
|
// Refuse to create a lobby without a real GPS fix. The previous
|
||||||
|
// behavior of silently using a hardcoded Czechia fallback meant the
|
||||||
|
// game always started at the same place no matter where the host was,
|
||||||
|
// and the player capsule would spawn miles away in coordinate space
|
||||||
|
// because they're at their real GPS while the map was built around
|
||||||
|
// the fallback. Both bugs share this single gate.
|
||||||
|
if (inputSubsystem?.LastKnownPosition == null)
|
||||||
|
{
|
||||||
|
// testMode bypasses the GPS gate entirely so debug runs still work.
|
||||||
|
if (!testMode)
|
||||||
|
{
|
||||||
|
// Surface the actual GPS state in both logs and the toast
|
||||||
|
// instead of the generic "Waiting for GPS fix..." that hides
|
||||||
|
// permission/timeout/device-disabled distinctions.
|
||||||
|
string diag = inputSubsystem?.GpsDiagnostic ?? "no input subsystem";
|
||||||
|
Debug.LogWarning("[GameManager] CreateLobby blocked. " + diag);
|
||||||
|
uiSubsystem?.ShowToast("Cannot create lobby. " + diag);
|
||||||
|
inputSubsystem?.EnsureGPSStarted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = inputSubsystem?.LastKnownPosition;
|
||||||
|
double lat = pos?.Lat ?? 0;
|
||||||
|
double lon = pos?.Lon ?? 0;
|
||||||
|
networkSubsystem.CreateLobby(lat, lon, pendingRadius, pendingImpostorCount, pendingTaskCount, pendingSettings);
|
||||||
|
if (testMode) StartCoroutine(ConnectTestClients());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by JoinLobbyUI with the code from the input field
|
||||||
|
public void JoinLobbyButton(string code)
|
||||||
|
{
|
||||||
|
CommitNicknameFromInput();
|
||||||
|
if (!string.IsNullOrEmpty(code))
|
||||||
|
networkSubsystem.JoinLobby(code);
|
||||||
|
else
|
||||||
|
Debug.LogWarning("Join code is empty!");
|
||||||
|
}
|
||||||
|
|
||||||
public void LeaveLobbyButton()
|
public void LeaveLobbyButton()
|
||||||
{
|
{
|
||||||
networkSubsystem.LeaveLobby();
|
networkSubsystem.LeaveLobby();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StartGameButton()
|
||||||
|
{
|
||||||
|
networkSubsystem.StartGame();
|
||||||
|
}
|
||||||
|
|
||||||
void OnApplicationQuit()
|
void OnApplicationQuit()
|
||||||
{
|
{
|
||||||
gameClient.Disconnect();
|
gameClient?.Disconnect();
|
||||||
|
for (int i = 0; i < _testBots.Count; i++)
|
||||||
|
_testBots[i].Client?.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator ConnectTestClients()
|
||||||
|
{
|
||||||
|
if (_testBots.Count == 0) yield break;
|
||||||
|
|
||||||
|
// Wait until host lobby code exists
|
||||||
|
float wait = 0f;
|
||||||
|
while ((gameClient?.CurrentLobbyState == null || string.IsNullOrEmpty(gameClient.CurrentLobbyState.JoinCode)) && wait < 20f)
|
||||||
|
{
|
||||||
|
wait += 0.25f;
|
||||||
|
yield return new WaitForSeconds(0.25f);
|
||||||
|
}
|
||||||
|
|
||||||
|
var joinCode = gameClient?.CurrentLobbyState?.JoinCode;
|
||||||
|
if (string.IsNullOrEmpty(joinCode))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[TestMode] Could not join test bots: join code not available.");
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until every bot's client has finished its TCP handshake.
|
||||||
|
// IsReady flips once ClientHello + ClientHelloAck round-trip.
|
||||||
|
wait = 0f;
|
||||||
|
bool allReady;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
allReady = true;
|
||||||
|
for (int i = 0; i < _testBots.Count; i++)
|
||||||
|
{
|
||||||
|
if (_testBots[i].Client == null || !_testBots[i].Client.IsReady)
|
||||||
|
{
|
||||||
|
allReady = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allReady)
|
||||||
|
{
|
||||||
|
wait += 0.25f;
|
||||||
|
yield return new WaitForSeconds(0.25f);
|
||||||
|
}
|
||||||
|
} while (!allReady && wait < 20f);
|
||||||
|
|
||||||
|
if (!allReady)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[TestMode] Some test bots not ready, joining the ready ones only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _testBots.Count; i++)
|
||||||
|
{
|
||||||
|
var bot = _testBots[i];
|
||||||
|
if (bot.Client != null && bot.Client.IsReady)
|
||||||
|
{
|
||||||
|
bot.Network?.JoinLobby(joinCode);
|
||||||
|
bot.Joined = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.Log($"[TestMode] {_testBots.Count} bot(s) joined lobby with code {joinCode}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
Assets/GameManager/GameManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 22bf82e679cf6e1419440d236360ba3b
|
||||||
926
Assets/GameManager/GameManager_Input.cs
Normal file
@@ -0,0 +1,926 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using GeoSus.Client;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Subsystems
|
||||||
|
{
|
||||||
|
internal class CoroutineHost : MonoBehaviour
|
||||||
|
{
|
||||||
|
public CoroutineHost() { }
|
||||||
|
}
|
||||||
|
internal enum GPSState
|
||||||
|
{
|
||||||
|
Uninitialized,
|
||||||
|
Initializing,
|
||||||
|
Running,
|
||||||
|
Failed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Position source backend. Selectable at runtime via the GPS overlay
|
||||||
|
/// "Source" button so the user can recover when one path misbehaves on
|
||||||
|
/// their phone:
|
||||||
|
/// Auto - JNI: subscribe to gps + network, pick most recent fix.
|
||||||
|
/// GpsOnly - JNI: subscribe to gps only (network's frequent indoor
|
||||||
|
/// fixes don't drown out the slower-but-precise gps fix).
|
||||||
|
/// NetworkOnly - JNI: subscribe to network only (cell tower / WiFi).
|
||||||
|
/// Useful indoors when no satellite lock is possible.
|
||||||
|
/// UnityInput - Unity's Input.location wrapper. Verified to hang on
|
||||||
|
/// Mi 9T / A20e (which is why JNI exists), but works on
|
||||||
|
/// newer Android where the JNI streaming-callbacks path
|
||||||
|
/// silently doesn't fire (MIUI/HyperOS battery saver,
|
||||||
|
/// approximate-vs-precise permission split, minDistance
|
||||||
|
/// gating on stationary phones).
|
||||||
|
/// EditorWasd - WASD-driven simulated position. Available regardless
|
||||||
|
/// of testMode flag so desktop builds and editor sessions
|
||||||
|
/// can navigate the map without real GPS.
|
||||||
|
/// </summary>
|
||||||
|
public enum PositionSource
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
GpsOnly,
|
||||||
|
NetworkOnly,
|
||||||
|
UnityInput,
|
||||||
|
EditorWasd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Bridges android.location.LocationListener to managed code. The method
|
||||||
|
/// names here must match Java's LocationListener interface exactly so
|
||||||
|
/// AndroidJavaProxy's reflection dispatcher can find them.
|
||||||
|
/// </summary>
|
||||||
|
internal class AndroidLocationProxy : AndroidJavaProxy
|
||||||
|
{
|
||||||
|
public AndroidLocationProvider Owner { get; set; }
|
||||||
|
public AndroidLocationProxy() : base("android.location.LocationListener") { }
|
||||||
|
|
||||||
|
// Called by Android each time a new fix arrives from the registered provider.
|
||||||
|
public void onLocationChanged(AndroidJavaObject location)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (location == null) return;
|
||||||
|
double lat = location.Call<double>("getLatitude");
|
||||||
|
double lon = location.Call<double>("getLongitude");
|
||||||
|
long t = location.Call<long>("getTime");
|
||||||
|
string provider = "";
|
||||||
|
try { provider = location.Call<string>("getProvider"); } catch { }
|
||||||
|
// Streaming callbacks are LIVE (never cached). The cached path
|
||||||
|
// calls UpdateLocation directly with isCached=true.
|
||||||
|
Owner?.UpdateLocation(lat, lon, t, provider, isCached: false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[GPS-JNI] onLocationChanged failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required by the LocationListener interface even if we don't use them.
|
||||||
|
// Missing methods cause java.lang.AbstractMethodError at runtime.
|
||||||
|
public void onStatusChanged(string provider, int status, AndroidJavaObject extras) { }
|
||||||
|
public void onProviderEnabled(string provider) { }
|
||||||
|
public void onProviderDisabled(string provider) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Direct wrapper around android.location.LocationManager via JNI, used as
|
||||||
|
/// a replacement for Unity's Input.location on Android when the user picks
|
||||||
|
/// Auto/GpsOnly/NetworkOnly. Subscribed providers are configurable so the
|
||||||
|
/// position-source picker can rewire live without restart.
|
||||||
|
/// </summary>
|
||||||
|
internal class AndroidLocationProvider
|
||||||
|
{
|
||||||
|
private AndroidJavaObject _activity;
|
||||||
|
private AndroidJavaObject _locationManager;
|
||||||
|
private AndroidLocationProxy _gpsListener;
|
||||||
|
private AndroidLocationProxy _networkListener;
|
||||||
|
private double _lat, _lon;
|
||||||
|
private long _lastTimeMillis;
|
||||||
|
private long _lastLiveTimeMillis; // Time of most recent NON-cached fix.
|
||||||
|
private bool _hasFix;
|
||||||
|
private bool _hasLiveFix; // True once any streaming callback fired.
|
||||||
|
private string _activeProvider = "";
|
||||||
|
|
||||||
|
// Captured at Initialize() so the diagnostic can report
|
||||||
|
// "GPS provider DISABLED, only network enabled" etc.
|
||||||
|
private bool _gpsProviderEnabled;
|
||||||
|
private bool _networkProviderEnabled;
|
||||||
|
private bool _gpsLastKnownExists;
|
||||||
|
private bool _networkLastKnownExists;
|
||||||
|
private string _enabledProvidersList = "";
|
||||||
|
|
||||||
|
// Subscription scope - set in Initialize, used in Shutdown to know
|
||||||
|
// which listeners we registered.
|
||||||
|
private bool _subscribedGps;
|
||||||
|
private bool _subscribedNetwork;
|
||||||
|
|
||||||
|
public bool HasFix => _hasFix;
|
||||||
|
public bool HasLiveFix => _hasLiveFix;
|
||||||
|
public long LastLiveTimeMillis => _lastLiveTimeMillis;
|
||||||
|
public long LastTimeMillis => _lastTimeMillis;
|
||||||
|
public double Lat => _lat;
|
||||||
|
public double Lon => _lon;
|
||||||
|
public string ActiveProvider => _activeProvider;
|
||||||
|
public bool GpsProviderEnabled => _gpsProviderEnabled;
|
||||||
|
public bool NetworkProviderEnabled => _networkProviderEnabled;
|
||||||
|
public bool GpsLastKnownExists => _gpsLastKnownExists;
|
||||||
|
public bool NetworkLastKnownExists => _networkLastKnownExists;
|
||||||
|
public string EnabledProvidersList => _enabledProvidersList;
|
||||||
|
public bool SubscribedGps => _subscribedGps;
|
||||||
|
public bool SubscribedNetwork => _subscribedNetwork;
|
||||||
|
|
||||||
|
public bool Initialize(out string error, bool useGps, bool useNetwork)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
|
||||||
|
{
|
||||||
|
_activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
|
||||||
|
}
|
||||||
|
if (_activity == null) { error = "no current activity"; return false; }
|
||||||
|
|
||||||
|
_locationManager = _activity.Call<AndroidJavaObject>("getSystemService", "location");
|
||||||
|
if (_locationManager == null) { error = "getSystemService(\"location\") returned null"; return false; }
|
||||||
|
|
||||||
|
// Capture provider enable state up front so the diagnostic
|
||||||
|
// can distinguish "provider disabled at OS level" from
|
||||||
|
// "provider enabled but produced no fix yet".
|
||||||
|
_gpsProviderEnabled = SafeIsProviderEnabled("gps");
|
||||||
|
_networkProviderEnabled = SafeIsProviderEnabled("network");
|
||||||
|
_enabledProvidersList = SafeGetEnabledProviders();
|
||||||
|
|
||||||
|
Debug.Log($"[GPS-JNI] init useGps={useGps} useNetwork={useNetwork} gps enabled={_gpsProviderEnabled} network enabled={_networkProviderEnabled} all enabled=[{_enabledProvidersList}]");
|
||||||
|
|
||||||
|
// Try cached last-known fixes from the providers we're about
|
||||||
|
// to subscribe to. If the OS already knows where we are
|
||||||
|
// (e.g. from another app that recently used GPS), we get a
|
||||||
|
// fix at zero cost and zero wait time. Tagged isCached so
|
||||||
|
// the diagnostic can mark them and we know we still need
|
||||||
|
// to wait for a streaming callback.
|
||||||
|
if (useNetwork) TryLastKnown("network", out _networkLastKnownExists);
|
||||||
|
if (useGps) TryLastKnown("gps", out _gpsLastKnownExists);
|
||||||
|
|
||||||
|
_subscribedGps = useGps;
|
||||||
|
_subscribedNetwork = useNetwork;
|
||||||
|
|
||||||
|
if (useGps) _gpsListener = new AndroidLocationProxy { Owner = this };
|
||||||
|
if (useNetwork) _networkListener = new AndroidLocationProxy { Owner = this };
|
||||||
|
|
||||||
|
// requestLocationUpdates must be called on a thread with a
|
||||||
|
// Looper. Use the Activity's UI thread, which always has one.
|
||||||
|
// minTime=1000ms, minDistance=0f - we want updates on every
|
||||||
|
// fix the OS produces. Previously this was 1f which gated
|
||||||
|
// out updates from a stationary phone (MIUI/newer Android
|
||||||
|
// are stricter about this and that's the suspected cause of
|
||||||
|
// "via gps (cached)" sticking forever).
|
||||||
|
_activity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
|
||||||
|
{
|
||||||
|
if (useGps)
|
||||||
|
{
|
||||||
|
try { _locationManager.Call("requestLocationUpdates", "gps", 1000L, 0f, _gpsListener); }
|
||||||
|
catch (Exception ex) { Debug.LogWarning("[GPS-JNI] gps subscribe failed: " + ex.Message); }
|
||||||
|
}
|
||||||
|
if (useNetwork)
|
||||||
|
{
|
||||||
|
try { _locationManager.Call("requestLocationUpdates", "network", 1000L, 0f, _networkListener); }
|
||||||
|
catch (Exception ex) { Debug.LogWarning("[GPS-JNI] network subscribe failed: " + ex.Message); }
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = "JNI init exception: " + ex.Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryLastKnown(string provider, out bool nonNullReturned)
|
||||||
|
{
|
||||||
|
nonNullReturned = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var loc = _locationManager.Call<AndroidJavaObject>("getLastKnownLocation", provider);
|
||||||
|
if (loc != null)
|
||||||
|
{
|
||||||
|
nonNullReturned = true;
|
||||||
|
double lat = loc.Call<double>("getLatitude");
|
||||||
|
double lon = loc.Call<double>("getLongitude");
|
||||||
|
long t = loc.Call<long>("getTime");
|
||||||
|
UpdateLocation(lat, lon, t, provider, isCached: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[GPS-JNI] getLastKnownLocation({provider}) failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SafeIsProviderEnabled(string provider)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _locationManager.Call<bool>("isProviderEnabled", provider);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[GPS-JNI] isProviderEnabled({provider}) failed: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a comma-separated list of currently-enabled providers via
|
||||||
|
// LocationManager.getProviders(true). We iterate the returned
|
||||||
|
// java.util.List by index because AndroidJavaObject does not
|
||||||
|
// implement IEnumerable.
|
||||||
|
string SafeGetEnabledProviders()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var list = _locationManager.Call<AndroidJavaObject>("getProviders", true);
|
||||||
|
if (list == null) return "";
|
||||||
|
int size = list.Call<int>("size");
|
||||||
|
var parts = new System.Text.StringBuilder();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
var name = list.Call<string>("get", i);
|
||||||
|
if (i > 0) parts.Append(",");
|
||||||
|
parts.Append(name);
|
||||||
|
}
|
||||||
|
return parts.ToString();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[GPS-JNI] getProviders failed: " + ex.Message);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLocation(double lat, double lon, long timeMillis, string provider, bool isCached)
|
||||||
|
{
|
||||||
|
// Ignore older fixes if a newer one is already in hand. This lets
|
||||||
|
// both gps + network listeners feed us without ping-ponging
|
||||||
|
// between stale and fresh data.
|
||||||
|
if (timeMillis < _lastTimeMillis) return;
|
||||||
|
_lat = lat;
|
||||||
|
_lon = lon;
|
||||||
|
_lastTimeMillis = timeMillis;
|
||||||
|
// Active-provider name carries cached/live state in the diagnostic
|
||||||
|
// banner so the user can see at a glance whether streaming has
|
||||||
|
// kicked in or we're still on the initial cached snapshot.
|
||||||
|
_activeProvider = (provider ?? "") + (isCached ? " (cached)" : "");
|
||||||
|
_hasFix = true;
|
||||||
|
if (!isCached)
|
||||||
|
{
|
||||||
|
_hasLiveFix = true;
|
||||||
|
_lastLiveTimeMillis = timeMillis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_locationManager != null)
|
||||||
|
{
|
||||||
|
if (_gpsListener != null) _locationManager.Call("removeUpdates", _gpsListener);
|
||||||
|
if (_networkListener != null) _locationManager.Call("removeUpdates", _networkListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[GPS-JNI] Shutdown failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
_gpsListener = null;
|
||||||
|
_networkListener = null;
|
||||||
|
_locationManager = null;
|
||||||
|
_activity = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
public static class PositonExtensions
|
||||||
|
{
|
||||||
|
public static Position ToLocal(this Position position, Position center)
|
||||||
|
{
|
||||||
|
double latDiff = position.Lat - center.Lat;
|
||||||
|
double lonDiff = position.Lon - center.Lon;
|
||||||
|
double metersPerDegreeLat = 111320.0;
|
||||||
|
double metersPerDegreeLon = 111320.0 * Math.Cos(center.Lat * Math.PI / 180.0);
|
||||||
|
float x = (float)(lonDiff * metersPerDegreeLon);
|
||||||
|
float z = (float)(latDiff * metersPerDegreeLat);
|
||||||
|
return new Position(z, x);
|
||||||
|
}
|
||||||
|
public static Vector3 ToLocalVector3(this Position position, Position center)
|
||||||
|
{
|
||||||
|
return position.ToLocal(center).ToVector3(); //TODO: Implementace v subsystemech
|
||||||
|
}
|
||||||
|
public static Vector3 ToVector3(this Position position)
|
||||||
|
{
|
||||||
|
return new Vector3((float)position.Lon, 0, (float)position.Lat); //TODO: Implementace v subsystemech
|
||||||
|
}
|
||||||
|
public static double DistanceTo(this Vector3 pos, Vector3 other)
|
||||||
|
{
|
||||||
|
return Math.Sqrt((other.x - pos.x) * (other.x - pos.x) + (other.z - pos.z) * (other.z - pos.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameManager_Input
|
||||||
|
{
|
||||||
|
private GameClient _gameClient;
|
||||||
|
private Position _currentPosition;
|
||||||
|
private Position _lastSentPosition;
|
||||||
|
private GameObject _player;
|
||||||
|
private bool _testMode;
|
||||||
|
|
||||||
|
// PlayerPrefs key for the user's chosen position source. Persists
|
||||||
|
// across app restarts so a user who flipped to UnityInput because
|
||||||
|
// their phone hated the JNI path doesn't have to flip again every
|
||||||
|
// launch.
|
||||||
|
private const string PrefsSourceKey = "PositionSource_v1";
|
||||||
|
private PositionSource _currentSource = PositionSource.Auto;
|
||||||
|
|
||||||
|
// When the multi-client editor test mode picks a non-host bot as
|
||||||
|
// active, we need the host's WASD path to NOT also move. Set true
|
||||||
|
// by GameManager when active slot != 0.
|
||||||
|
public bool SuppressWasd = false;
|
||||||
|
|
||||||
|
private GPSState _GPSState = GPSState.Uninitialized;
|
||||||
|
private float _speed = 0.00001f;
|
||||||
|
private Position _mapCenter;
|
||||||
|
private CoroutineHost _coroutineHost;
|
||||||
|
|
||||||
|
private int _gpsRetryCount = 0;
|
||||||
|
private const int _maxGpsRetries = 5;
|
||||||
|
private float _lastPositionSendTime;
|
||||||
|
private const float _positionKeepAliveSeconds = 1.0f;
|
||||||
|
|
||||||
|
// Diagnostic state. We capture *why* GPS init failed so the UI can
|
||||||
|
// surface it to the user without requiring logcat. Older Android
|
||||||
|
// phones (Mi 9T, A20e) hit silent failure modes that are impossible
|
||||||
|
// to distinguish from "still warming up" without this.
|
||||||
|
private string _lastGpsError = "";
|
||||||
|
private float _gpsInitStartTime = -1f;
|
||||||
|
// Bump from the original 20s. Cold-start GPS on older Android can
|
||||||
|
// easily exceed 20s indoors or under cloud cover - by the time the
|
||||||
|
// user notices nothing is happening, we've already given up.
|
||||||
|
private const int _gpsInitTimeoutSeconds = 60;
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
// JNI-backed location provider, used for Auto/GpsOnly/NetworkOnly.
|
||||||
|
// UnityInput uses Input.location instead and leaves this null.
|
||||||
|
private AndroidLocationProvider _androidProvider;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>Last known GPS position (for CreateLobby centre point)</summary>
|
||||||
|
public Position? LastKnownPosition => _currentPosition.Lat != 0 || _currentPosition.Lon != 0 ? _currentPosition : (Position?)null;
|
||||||
|
|
||||||
|
/// <summary>Current GPS state machine value (debug/diagnostic).</summary>
|
||||||
|
public string GpsStateName => _GPSState.ToString();
|
||||||
|
|
||||||
|
/// <summary>Last GPS error reason captured during init (empty if none).</summary>
|
||||||
|
public string LastGpsError => _lastGpsError ?? "";
|
||||||
|
|
||||||
|
/// <summary>Retry count out of max (debug/diagnostic).</summary>
|
||||||
|
public string GpsRetryProgress => $"{_gpsRetryCount}/{_maxGpsRetries}";
|
||||||
|
|
||||||
|
/// <summary>Currently selected position source (for UI cycle button).</summary>
|
||||||
|
public PositionSource CurrentSource => _currentSource;
|
||||||
|
|
||||||
|
/// <summary>Display name for the current source (for UI label).</summary>
|
||||||
|
public string CurrentSourceName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (_currentSource)
|
||||||
|
{
|
||||||
|
case PositionSource.Auto: return "Auto (GPS+Net)";
|
||||||
|
case PositionSource.GpsOnly: return "GPS only";
|
||||||
|
case PositionSource.NetworkOnly: return "Network only";
|
||||||
|
case PositionSource.UnityInput: return "Unity Input";
|
||||||
|
case PositionSource.EditorWasd: return "WASD";
|
||||||
|
default: return _currentSource.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Human-readable one-line GPS status for on-screen overlay. Designed
|
||||||
|
/// to be visible without ADB so users can self-diagnose permission
|
||||||
|
/// vs. timeout vs. device-disabled vs. running-but-no-fix-yet.
|
||||||
|
/// </summary>
|
||||||
|
public string GpsDiagnostic
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_currentSource == PositionSource.EditorWasd)
|
||||||
|
{
|
||||||
|
if (_currentPosition.Lat == 0 && _currentPosition.Lon == 0)
|
||||||
|
return "WASD: waiting for map center";
|
||||||
|
return $"WASD lat={_currentPosition.Lat:F5} lon={_currentPosition.Lon:F5}";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_GPSState)
|
||||||
|
{
|
||||||
|
case GPSState.Uninitialized:
|
||||||
|
return "Uninitialized (will start on first lobby action)";
|
||||||
|
case GPSState.Initializing:
|
||||||
|
{
|
||||||
|
float elapsed = _gpsInitStartTime >= 0 ? Time.time - _gpsInitStartTime : 0;
|
||||||
|
string providers = "";
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
if (_androidProvider != null && !string.IsNullOrEmpty(_androidProvider.EnabledProvidersList))
|
||||||
|
providers = $" providers=[{_androidProvider.EnabledProvidersList}]";
|
||||||
|
#endif
|
||||||
|
return $"Initializing ({elapsed:F1}s / max {_gpsInitTimeoutSeconds}s){providers}";
|
||||||
|
}
|
||||||
|
case GPSState.Running:
|
||||||
|
{
|
||||||
|
string suffix = "";
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
if (_androidProvider != null)
|
||||||
|
{
|
||||||
|
string p = _androidProvider.ActiveProvider;
|
||||||
|
if (!string.IsNullOrEmpty(p)) suffix = " via " + p;
|
||||||
|
// Show how stale the most recent fix is (ms-level
|
||||||
|
// resolution) so "stuck on cached" is obvious at
|
||||||
|
// a glance: "via gps (cached) [no live, 47s old]".
|
||||||
|
if (!_androidProvider.HasLiveFix)
|
||||||
|
{
|
||||||
|
long now = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
|
||||||
|
long ageMs = now - _androidProvider.LastTimeMillis;
|
||||||
|
if (_androidProvider.LastTimeMillis > 0 && ageMs > 0)
|
||||||
|
suffix += $" [no live, {ageMs / 1000}s old]";
|
||||||
|
else
|
||||||
|
suffix += " [no live]";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long now = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
|
||||||
|
long ageMs = now - _androidProvider.LastLiveTimeMillis;
|
||||||
|
if (ageMs > 5000) suffix += $" [live {ageMs / 1000}s old]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (_currentPosition.Lat == 0 && _currentPosition.Lon == 0)
|
||||||
|
return "Running but no fix yet (waiting for satellites)" + suffix;
|
||||||
|
return $"Running lat={_currentPosition.Lat:F5} lon={_currentPosition.Lon:F5}" + suffix;
|
||||||
|
}
|
||||||
|
case GPSState.Failed:
|
||||||
|
return $"Failed: {(_lastGpsError ?? "unknown")} (retries {GpsRetryProgress})";
|
||||||
|
default:
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameManager_Input(GameClient gameClient, GameObject player, bool testMode)
|
||||||
|
{
|
||||||
|
_gameClient = gameClient;
|
||||||
|
_player = player;
|
||||||
|
_testMode = testMode;
|
||||||
|
// CoroutineHost needs a MonoBehaviour on a real GameObject
|
||||||
|
var hostGO = new UnityEngine.GameObject("_CoroutineHost");
|
||||||
|
UnityEngine.Object.DontDestroyOnLoad(hostGO);
|
||||||
|
_coroutineHost = hostGO.AddComponent<CoroutineHost>();
|
||||||
|
|
||||||
|
// Restore the user's last picked source. Default depends on
|
||||||
|
// platform: editor defaults to EditorWasd (no GPS hardware in
|
||||||
|
// editor anyway); device defaults to Auto.
|
||||||
|
string saved = PlayerPrefs.GetString(PrefsSourceKey, "");
|
||||||
|
if (!string.IsNullOrEmpty(saved) && Enum.TryParse(saved, out PositionSource parsed))
|
||||||
|
{
|
||||||
|
_currentSource = parsed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
_currentSource = PositionSource.EditorWasd;
|
||||||
|
#else
|
||||||
|
_currentSource = PositionSource.Auto;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy testMode flag forces EditorWasd. New code paths should
|
||||||
|
// use SwitchPositionSource(EditorWasd) instead, but we keep the
|
||||||
|
// old behavior for backward compatibility with the inspector flag.
|
||||||
|
if (_testMode) _currentSource = PositionSource.EditorWasd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called from OnSceneLoaded when Client.unity loads so the
|
||||||
|
/// Player capsule (which lives in Client.unity) can be wired at runtime.</summary>
|
||||||
|
public void SetPlayerObject(GameObject player) { _player = player; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Switch the active position source backend live. Tears down the
|
||||||
|
/// current backend's listeners (JNI proxies, Input.location), resets
|
||||||
|
/// the state machine, and kicks off init for the new source. Persists
|
||||||
|
/// the choice to PlayerPrefs.
|
||||||
|
/// </summary>
|
||||||
|
public void SwitchPositionSource(PositionSource newSource)
|
||||||
|
{
|
||||||
|
if (_currentSource == newSource) return;
|
||||||
|
Debug.Log($"[GPS] SwitchPositionSource {_currentSource} -> {newSource}");
|
||||||
|
|
||||||
|
// Tear down whatever's running.
|
||||||
|
ShutdownCurrentBackend();
|
||||||
|
|
||||||
|
_currentSource = newSource;
|
||||||
|
PlayerPrefs.SetString(PrefsSourceKey, newSource.ToString());
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
|
||||||
|
_GPSState = GPSState.Uninitialized;
|
||||||
|
_gpsRetryCount = 0;
|
||||||
|
_lastGpsError = "";
|
||||||
|
_gpsInitStartTime = -1f;
|
||||||
|
// Don't clear _currentPosition - the user has presumably been
|
||||||
|
// playing somewhere. Map markers/avatar position can stay until
|
||||||
|
// the next fix arrives from the new source.
|
||||||
|
|
||||||
|
EnsureGPSStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Cycle through the available sources for tap-to-cycle UI.</summary>
|
||||||
|
public void CycleNextPositionSource()
|
||||||
|
{
|
||||||
|
var values = (PositionSource[])Enum.GetValues(typeof(PositionSource));
|
||||||
|
int idx = Array.IndexOf(values, _currentSource);
|
||||||
|
var next = values[(idx + 1) % values.Length];
|
||||||
|
SwitchPositionSource(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShutdownCurrentBackend()
|
||||||
|
{
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
if (_androidProvider != null)
|
||||||
|
{
|
||||||
|
_androidProvider.Shutdown();
|
||||||
|
_androidProvider = null;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Stop Unity Input.location too, in case it was running.
|
||||||
|
try { Input.location.Stop(); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kick off GPS initialization if it hasn't started yet. Safe to call
|
||||||
|
/// repeatedly. Hosts must call this from the lobby setup screen so
|
||||||
|
/// that by the time they click "Create Lobby" we have a real GPS
|
||||||
|
/// fix to use as the play-area center, instead of falling back to
|
||||||
|
/// the hardcoded coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public void EnsureGPSStarted()
|
||||||
|
{
|
||||||
|
if (_currentSource == PositionSource.EditorWasd) return;
|
||||||
|
if (_coroutineHost == null) return;
|
||||||
|
// Allow tapping "Create Lobby" again (or any caller of this
|
||||||
|
// method) to retry from Failed up to _maxGpsRetries times.
|
||||||
|
if (_GPSState == GPSState.Uninitialized)
|
||||||
|
{
|
||||||
|
_coroutineHost.StartCoroutine(InitiallizeGPS());
|
||||||
|
}
|
||||||
|
else if (_GPSState == GPSState.Failed && _gpsRetryCount < _maxGpsRetries)
|
||||||
|
{
|
||||||
|
_gpsRetryCount++;
|
||||||
|
_coroutineHost.StartCoroutine(InitiallizeGPS());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void positionCheck()
|
||||||
|
{
|
||||||
|
var state = _gameClient?.CurrentLobbyState;
|
||||||
|
if (state == null || state.Phase != GamePhase.Playing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_currentSource == PositionSource.EditorWasd)
|
||||||
|
{
|
||||||
|
if (_currentPosition == new Position(0, 0))
|
||||||
|
{
|
||||||
|
if (state.MapData == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Init blok
|
||||||
|
_currentPosition = state.MapData.Center;
|
||||||
|
_mapCenter = state.MapData.Center;
|
||||||
|
_lastSentPosition = _currentPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SuppressWasd)
|
||||||
|
TestPlayerPosition();
|
||||||
|
else
|
||||||
|
TrySendCurrentPosition(); // keep-alive only
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_GPSState == GPSState.Uninitialized)
|
||||||
|
{
|
||||||
|
_coroutineHost.StartCoroutine(InitiallizeGPS());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_GPSState == GPSState.Initializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_GPSState == GPSState.Running)
|
||||||
|
{
|
||||||
|
EnsureMapCenter();
|
||||||
|
TrySendCurrentPosition();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("GPS failed, trying again...");
|
||||||
|
if (_gpsRetryCount < _maxGpsRetries)
|
||||||
|
{
|
||||||
|
_gpsRetryCount++;
|
||||||
|
_GPSState = GPSState.Uninitialized;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("GPS unavailable after max retries. Using last known position.");
|
||||||
|
// Keep _GPSState = Failed so we stop retrying
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[Input] positionCheck failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureMapCenter()
|
||||||
|
{
|
||||||
|
if (_mapCenter.Lat != 0 || _mapCenter.Lon != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var md = _gameClient?.CurrentLobbyState?.MapData;
|
||||||
|
if (md != null)
|
||||||
|
_mapCenter = md.Center;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrySendCurrentPosition()
|
||||||
|
{
|
||||||
|
bool moved = _currentPosition != _lastSentPosition;
|
||||||
|
bool keepAliveDue = (Time.time - _lastPositionSendTime) >= _positionKeepAliveSeconds;
|
||||||
|
if (!moved && !keepAliveDue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var previous = _lastSentPosition;
|
||||||
|
_gameClient.UpdatePosition(_currentPosition);
|
||||||
|
_lastSentPosition = _currentPosition;
|
||||||
|
_lastPositionSendTime = Time.time;
|
||||||
|
|
||||||
|
if (_player == null || (_mapCenter.Lat == 0 && _mapCenter.Lon == 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var localCurrent = _currentPosition.ToLocalVector3(_mapCenter);
|
||||||
|
_player.transform.position = localCurrent;
|
||||||
|
|
||||||
|
if (previous.Lat == 0 && previous.Lon == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var heading = CalculateHeading(previous.ToLocalVector3(_mapCenter), localCurrent);
|
||||||
|
if (heading.HasValue)
|
||||||
|
_player.transform.rotation = Quaternion.Euler(0, (float)heading.Value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestPlayerPosition()
|
||||||
|
{
|
||||||
|
double x = Input.GetAxis("Horizontal");
|
||||||
|
double y = Input.GetAxis("Vertical");
|
||||||
|
_currentPosition = new Position( _lastSentPosition.Lat + y * _speed, _lastSentPosition.Lon + x * _speed);
|
||||||
|
var localCurrent = _currentPosition.ToLocalVector3(_mapCenter);
|
||||||
|
var heading = CalculateHeading(_lastSentPosition.ToLocalVector3(_mapCenter), localCurrent);
|
||||||
|
if (heading != null)
|
||||||
|
{
|
||||||
|
if (_player != null)
|
||||||
|
_player.transform.rotation = Quaternion.Euler(0, (float)heading, 0);
|
||||||
|
}
|
||||||
|
if (_player != null)
|
||||||
|
_player.transform.position = localCurrent;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TrySendCurrentPosition();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_gameClient.UpdatePosition(_currentPosition);
|
||||||
|
_lastSentPosition = _currentPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private double? CalculateHeading(Vector3 first, Vector3 second)
|
||||||
|
{
|
||||||
|
if ((first - second).magnitude < 0.0001f) return null;
|
||||||
|
float dx = second.x - first.x;
|
||||||
|
float dz = second.z - first.z;
|
||||||
|
float heading = Mathf.Atan2(dx, dz) * Mathf.Rad2Deg;
|
||||||
|
if (heading < 0) heading += 360f;
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
IEnumerator InitiallizeGPS()
|
||||||
|
{
|
||||||
|
_GPSState = GPSState.Initializing;
|
||||||
|
_gpsInitStartTime = Time.time;
|
||||||
|
_lastGpsError = "";
|
||||||
|
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
// Request fine location permission if not already granted.
|
||||||
|
// On Android 12+ a "precise" toggle exists separately from coarse,
|
||||||
|
// but Unity's FineLocation request covers both for our purposes.
|
||||||
|
if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.FineLocation))
|
||||||
|
{
|
||||||
|
UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.FineLocation);
|
||||||
|
// Wait up to 10 seconds for user to respond to the permission dialog
|
||||||
|
float waited = 0f;
|
||||||
|
while (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.FineLocation) && waited < 10f)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(0.5f);
|
||||||
|
waited += 0.5f;
|
||||||
|
}
|
||||||
|
if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.FineLocation))
|
||||||
|
{
|
||||||
|
_lastGpsError = "Permission denied (fine location)";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
// Choose subscription scope based on selected source. UnityInput
|
||||||
|
// skips JNI entirely and falls through to the Input.location path
|
||||||
|
// below (the same path iOS / editor use).
|
||||||
|
if (_currentSource == PositionSource.Auto ||
|
||||||
|
_currentSource == PositionSource.GpsOnly ||
|
||||||
|
_currentSource == PositionSource.NetworkOnly)
|
||||||
|
{
|
||||||
|
bool useGps = (_currentSource != PositionSource.NetworkOnly);
|
||||||
|
bool useNetwork = (_currentSource != PositionSource.GpsOnly);
|
||||||
|
|
||||||
|
if (_androidProvider != null)
|
||||||
|
{
|
||||||
|
_androidProvider.Shutdown();
|
||||||
|
_androidProvider = null;
|
||||||
|
}
|
||||||
|
_androidProvider = new AndroidLocationProvider();
|
||||||
|
if (!_androidProvider.Initialize(out var initError, useGps, useNetwork))
|
||||||
|
{
|
||||||
|
_lastGpsError = "Native LocationManager failed: " + initError;
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_androidProvider = null;
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast-fail if neither subscribed provider is enabled at OS
|
||||||
|
// level. Waiting 60s for fixes from disabled providers is
|
||||||
|
// pointless - tell the user immediately what's wrong.
|
||||||
|
bool anyUsableEnabled =
|
||||||
|
(useGps && _androidProvider.GpsProviderEnabled) ||
|
||||||
|
(useNetwork && _androidProvider.NetworkProviderEnabled);
|
||||||
|
if (!anyUsableEnabled)
|
||||||
|
{
|
||||||
|
string which = useGps && useNetwork ? "gps + network"
|
||||||
|
: useGps ? "gps"
|
||||||
|
: "network";
|
||||||
|
_lastGpsError = $"{which} provider DISABLED at OS level. Open Settings > Location and switch it ON. Or tap [Source] to try a different backend.";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_androidProvider.Shutdown();
|
||||||
|
_androidProvider = null;
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the first fix (cached or live).
|
||||||
|
int maxWaitJni = _gpsInitTimeoutSeconds;
|
||||||
|
while (!_androidProvider.HasFix && maxWaitJni > 0)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(1);
|
||||||
|
maxWaitJni--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_androidProvider.HasFix)
|
||||||
|
{
|
||||||
|
string enabled = _androidProvider.EnabledProvidersList ?? "";
|
||||||
|
string gpsState = _androidProvider.GpsProviderEnabled ? "ON" : "OFF";
|
||||||
|
string netState = _androidProvider.NetworkProviderEnabled ? "ON" : "OFF";
|
||||||
|
string lastKnown = $"lastKnown[gps={(_androidProvider.GpsLastKnownExists ? "yes" : "no")}, net={(_androidProvider.NetworkLastKnownExists ? "yes" : "no")}]";
|
||||||
|
|
||||||
|
_lastGpsError = $"Timeout {_gpsInitTimeoutSeconds}s on {_currentSource}. enabled=[{enabled}] gps={gpsState} net={netState} {lastKnown}. Try [Source] cycle to switch backends.";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_androidProvider.Shutdown();
|
||||||
|
_androidProvider = null;
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPosition = new Position(_androidProvider.Lat, _androidProvider.Lon);
|
||||||
|
_GPSState = GPSState.Running;
|
||||||
|
_gpsRetryCount = 0;
|
||||||
|
_coroutineHost.StartCoroutine(AndroidGPSService());
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _currentSource == UnityInput on Android: fall through to the
|
||||||
|
// Input.location path below. This is the recovery path for
|
||||||
|
// newer Android phones where JNI's streaming-callbacks don't
|
||||||
|
// fire (MIUI/HyperOS background restrictions, approximate-vs-
|
||||||
|
// precise permission, minDistance gating on stationary phones).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// iOS / editor / non-Android / Android-with-UnityInput-source:
|
||||||
|
// use Unity's Input.location.
|
||||||
|
if (!Input.location.isEnabledByUser)
|
||||||
|
{
|
||||||
|
_lastGpsError = "Location services not enabled by user";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float desiredAccuracyInMeters = 5f;
|
||||||
|
float updateDistanceInMeters = 1f;
|
||||||
|
|
||||||
|
Input.location.Start(desiredAccuracyInMeters, updateDistanceInMeters);
|
||||||
|
|
||||||
|
int maxWait = _gpsInitTimeoutSeconds;
|
||||||
|
while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(1);
|
||||||
|
maxWait--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxWait < 1)
|
||||||
|
{
|
||||||
|
_lastGpsError = $"Timed out after {_gpsInitTimeoutSeconds}s waiting for first fix (try moving outdoors, or tap [Source] to try a different backend)";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input.location.status == LocationServiceStatus.Failed)
|
||||||
|
{
|
||||||
|
_lastGpsError = "Unity Input.location reported Failed status";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_GPSState = GPSState.Running;
|
||||||
|
_gpsRetryCount = 0;
|
||||||
|
_coroutineHost.StartCoroutine(GPSService());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Mirrors the JNI provider's most recent fix into _currentPosition
|
||||||
|
/// every 0.5s so the rest of the game (which polls _currentPosition
|
||||||
|
/// indirectly via LastKnownPosition / TrySendCurrentPosition) keeps
|
||||||
|
/// working unchanged. Replaces GPSService on Android.
|
||||||
|
/// </summary>
|
||||||
|
IEnumerator AndroidGPSService()
|
||||||
|
{
|
||||||
|
while (_GPSState == GPSState.Running && _androidProvider != null)
|
||||||
|
{
|
||||||
|
if (_androidProvider.HasFix)
|
||||||
|
{
|
||||||
|
_currentPosition = new Position(_androidProvider.Lat, _androidProvider.Lon);
|
||||||
|
}
|
||||||
|
yield return new WaitForSeconds(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop ended (state != Running or provider disposed). Clean up
|
||||||
|
// listeners so we don't leak across retries.
|
||||||
|
if (_androidProvider != null)
|
||||||
|
{
|
||||||
|
_androidProvider.Shutdown();
|
||||||
|
_androidProvider = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
IEnumerator GPSService()
|
||||||
|
{
|
||||||
|
while (_GPSState == GPSState.Running)
|
||||||
|
{
|
||||||
|
if (Input.location.status == LocationServiceStatus.Failed)
|
||||||
|
{
|
||||||
|
_lastGpsError = "Location service died after init (provider stopped)";
|
||||||
|
Debug.LogError("[GPS] " + _lastGpsError);
|
||||||
|
_GPSState = GPSState.Failed;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep current GPS position fresh; sending is throttled in positionCheck().
|
||||||
|
var data = Input.location.lastData;
|
||||||
|
_currentPosition = new Position(data.latitude, data.longitude);
|
||||||
|
yield return new WaitForSeconds(0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/GameManager/GameManager_Input.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ef1abfb1e85a7943925f9dc3cfea742
|
||||||
808
Assets/GameManager/GameManager_Map.cs
Normal file
@@ -0,0 +1,808 @@
|
|||||||
|
using GeoSus.Client;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Subsystems{
|
||||||
|
[System.Serializable]
|
||||||
|
public class BuildingSettings
|
||||||
|
{
|
||||||
|
public Material ResidentialBuildingsMat;
|
||||||
|
public float ResidentialBuildingHeight;
|
||||||
|
public Material CommercialBuildingsMat;
|
||||||
|
public float CommercialBuildingHeight;
|
||||||
|
public Material IndustrialBuildingsMat;
|
||||||
|
public float IndustrialBuildingHeight;
|
||||||
|
public Material DefaultBuildingMat;
|
||||||
|
public float DefaultBuildingHeight;
|
||||||
|
}
|
||||||
|
[System.Serializable]
|
||||||
|
public class PathwaySettings
|
||||||
|
{
|
||||||
|
public Material FootwayMat;
|
||||||
|
public float FootwayWidth;
|
||||||
|
public Material PathMat;
|
||||||
|
public float PathWidth;
|
||||||
|
public Material StepsMat;
|
||||||
|
public float StepsWidth;
|
||||||
|
public Material CyclewayMat;
|
||||||
|
public float CyclewayWidth;
|
||||||
|
public Material PedestrianMat;
|
||||||
|
public float PedestrianWidth;
|
||||||
|
public Material RoadMat;
|
||||||
|
public float RoadWidth;
|
||||||
|
public Material ServiceMat;
|
||||||
|
public float ServiceWidth;
|
||||||
|
public Material ResidentialMat;
|
||||||
|
public float ResidentialWidth;
|
||||||
|
public Material TrackMat;
|
||||||
|
public float TrackWidth;
|
||||||
|
public Material DefaultMat;
|
||||||
|
public float DefaultWidth;
|
||||||
|
}
|
||||||
|
[System.Serializable]
|
||||||
|
public class AreaSettings
|
||||||
|
{
|
||||||
|
public Material ParkMat;
|
||||||
|
public Material GardenMat;
|
||||||
|
public Material PlaygroundMat;
|
||||||
|
public Material ForestMat;
|
||||||
|
public Material GrassMat;
|
||||||
|
public Material WaterMat;
|
||||||
|
public Material DefaultMat;
|
||||||
|
}
|
||||||
|
public class GameManager_Map
|
||||||
|
{
|
||||||
|
private GameClient _gameClient;
|
||||||
|
private GameObject _mapCenterPoint;
|
||||||
|
private Position _centerPosition;
|
||||||
|
private BuildingSettings _buildingSettings;
|
||||||
|
private PathwaySettings _pathwaySettings;
|
||||||
|
private AreaSettings _areaSettings;
|
||||||
|
private const float _metersPerUnit = 1f;
|
||||||
|
|
||||||
|
// ── Layer Y separation (single source of truth for vertical stacking) ───
|
||||||
|
// Areas at the bottom, paths above areas, buildings extruded upward from
|
||||||
|
// their own base, POIs floating well above everything else. Z-fighting
|
||||||
|
// happens when adjacent geometry shares a Y; these constants keep each
|
||||||
|
// logical layer at a distinct elevation.
|
||||||
|
private const float kAreaBaseY = 0.10f;
|
||||||
|
private const float kPathY = 0.30f;
|
||||||
|
private const float kBuildingBaseY = 0.50f;
|
||||||
|
private const float kPoiY = 2.00f;
|
||||||
|
|
||||||
|
// Render-queue forcing was tried in P3 to disambiguate same-Y geometry
|
||||||
|
// but turned out to be the cause of the "blank map in mobile game view,
|
||||||
|
// fine in scene view" regression: forcing transparent-class shaders
|
||||||
|
// (default queue 3000+) into the Geometry range (2000-2150) breaks
|
||||||
|
// their depth-write/blend assumptions on mobile shader paths. The
|
||||||
|
// editor's scene view masks it because it uses different render paths
|
||||||
|
// and post-process is off there. Queue forcing removed in P8;
|
||||||
|
// disambiguation is now via Y-layering + per-area Y-stagger alone,
|
||||||
|
// which the depth buffer resolves correctly even on weak mobile GPUs.
|
||||||
|
|
||||||
|
// ── Marker sizing (top-down camera, units = meters) ─────────────────
|
||||||
|
// The camera's orthographic size pushes "1 meter" to a small fraction
|
||||||
|
// of the screen. Markers need to be visibly larger than buildings'
|
||||||
|
// footprints for instant recognition.
|
||||||
|
private const float kMarkerHeight = 8f; // pillar height
|
||||||
|
private const float kMarkerRadius = 3f; // pillar radius (cylinder X/Z)
|
||||||
|
private const float kMarkerY = 4f; // base Y so pillar centers ~mid-height
|
||||||
|
private const float kLabelY = 9f; // text label sits above pillar top
|
||||||
|
private const float kLabelFontSize = 14f; // 3D text size in world units
|
||||||
|
|
||||||
|
// Runtime marker collections
|
||||||
|
private Dictionary<string, GameObject> _taskMarkers = new Dictionary<string, GameObject>();
|
||||||
|
private Dictionary<string, GameObject> _bodyMarkers = new Dictionary<string, GameObject>();
|
||||||
|
private Dictionary<string, GameObject> _playerAvatars = new Dictionary<string, GameObject>();
|
||||||
|
private List<GameObject> _sabotageMarkers = new List<GameObject>();
|
||||||
|
|
||||||
|
public GameManager_Map(GameClient gameClient, GameObject mapCenterPoint, BuildingSettings buildingSettings, PathwaySettings pathwaySettings, AreaSettings areaSettings)
|
||||||
|
{
|
||||||
|
_gameClient = gameClient;
|
||||||
|
_mapCenterPoint = mapCenterPoint;
|
||||||
|
_buildingSettings = buildingSettings;
|
||||||
|
_pathwaySettings = pathwaySettings;
|
||||||
|
_areaSettings = areaSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSceneReady => _mapCenterPoint != null;
|
||||||
|
|
||||||
|
/// <summary>Called from OnSceneLoaded when Client.unity is loaded so the
|
||||||
|
/// MapCenterPoint (which lives in Client.unity) can be wired at runtime.</summary>
|
||||||
|
public void SetMapCenterPoint(GameObject go) { _mapCenterPoint = go; }
|
||||||
|
public void BuildMap()
|
||||||
|
{
|
||||||
|
if (_mapCenterPoint == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Map] BuildMap skipped: MapCenterPoint is not yet bound.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_gameClient?.CurrentLobbyState?.MapData == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Map] BuildMap skipped: no MapData in CurrentLobbyState.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearChildren();
|
||||||
|
_centerPosition = _gameClient.CurrentLobbyState.MapData.Center;
|
||||||
|
GameObject buildingsRoot = new GameObject("Buildings");
|
||||||
|
buildingsRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
GameObject pathRoot = new GameObject("Pathways");
|
||||||
|
pathRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
GameObject areaRoot = new GameObject("Areas");
|
||||||
|
areaRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
foreach (var building in _gameClient.CurrentLobbyState.MapData.GetBuildings())
|
||||||
|
{
|
||||||
|
string buildingType = "Unknown";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
buildingType = _gameClient.CurrentLobbyState.MapData.BuildingTypes[_gameClient.CurrentLobbyState.MapData.GetBuildings().IndexOf(building)];
|
||||||
|
}
|
||||||
|
catch (Exception ex) { Debug.Log($"Error: {ex.Message}"); }
|
||||||
|
building.Name = buildingType;
|
||||||
|
GameObject b = BuildBuildingMesh(building);
|
||||||
|
b.transform.parent = buildingsRoot.transform;
|
||||||
|
}
|
||||||
|
foreach (var path in _gameClient.CurrentLobbyState.MapData.GetPathways())
|
||||||
|
{
|
||||||
|
GameObject p = BuildPathwayMesh(path);
|
||||||
|
p.transform.parent = pathRoot.transform;
|
||||||
|
}
|
||||||
|
foreach (var area in _gameClient.CurrentLobbyState.MapData.GetAreas())
|
||||||
|
{
|
||||||
|
GameObject a = BuildAreaMesh(area);
|
||||||
|
a.transform.parent = areaRoot.transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject poiRoot = new GameObject("POIs");
|
||||||
|
poiRoot.transform.parent = _mapCenterPoint.transform;
|
||||||
|
int poiCount = 0;
|
||||||
|
foreach (var poi in _gameClient.CurrentLobbyState.MapData.GetPOIs())
|
||||||
|
{
|
||||||
|
GameObject p = BuildPOIMarker(poi);
|
||||||
|
if (p != null) { p.transform.parent = poiRoot.transform; poiCount++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnostic - if the user reports "map missing in game view" but
|
||||||
|
// the counts here are non-zero, the bug is camera/culling related,
|
||||||
|
// not a build issue.
|
||||||
|
int buildings = _gameClient.CurrentLobbyState.MapData.GetBuildings()?.Count ?? 0;
|
||||||
|
int paths = _gameClient.CurrentLobbyState.MapData.GetPathways()?.Count ?? 0;
|
||||||
|
int areas = _gameClient.CurrentLobbyState.MapData.GetAreas()?.Count ?? 0;
|
||||||
|
Debug.Log($"[Map] BuildMap done: {buildings} buildings, {paths} paths, " +
|
||||||
|
$"{areas} areas, {poiCount} POIs. MapCenterPoint={_mapCenterPoint.name} " +
|
||||||
|
$"layer={_mapCenterPoint.layer} pos={_mapCenterPoint.transform.position} " +
|
||||||
|
$"scale={_mapCenterPoint.transform.localScale}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build a tall, brightly-colored pillar for a Point of Interest with
|
||||||
|
/// a 3D text label above it (e.g. "FOOD", "SHOP"). The label is laid
|
||||||
|
/// flat on the XZ plane facing UP so it reads correctly under the
|
||||||
|
/// orthogonal top-down camera.
|
||||||
|
/// </summary>
|
||||||
|
private GameObject BuildPOIMarker(MapPOI poi)
|
||||||
|
{
|
||||||
|
if (poi == null) return null;
|
||||||
|
var color = ColorForPOI(poi.POIType);
|
||||||
|
string label = LabelForPOI(poi.POIType);
|
||||||
|
var pos = poi.Location.ToLocalVector3(_centerPosition);
|
||||||
|
return CreateMarkerWithLabel($"POI_{poi.POIType}_{poi.Id}", pos, color, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shared marker builder: tall colored cylinder pillar + 3D text label
|
||||||
|
/// above it. Used by POIs, tasks, bodies, and sabotage stations so
|
||||||
|
/// they all share a visual language ("colored pillar with a name").
|
||||||
|
/// </summary>
|
||||||
|
private GameObject CreateMarkerWithLabel(string name, Vector3 worldPos, Color color, string label)
|
||||||
|
{
|
||||||
|
var go = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||||
|
go.name = name;
|
||||||
|
|
||||||
|
// Strip the auto-added collider - markers are visual only.
|
||||||
|
var col = go.GetComponent<Collider>();
|
||||||
|
if (col != null) UnityEngine.Object.Destroy(col);
|
||||||
|
|
||||||
|
go.transform.position = worldPos + Vector3.up * kMarkerY;
|
||||||
|
// Cylinder's default unit is 2 tall, 1 wide. Scale Y by half of
|
||||||
|
// kMarkerHeight (built-in is 2 units), X/Z by kMarkerRadius.
|
||||||
|
go.transform.localScale = new Vector3(kMarkerRadius, kMarkerHeight * 0.5f, kMarkerRadius);
|
||||||
|
|
||||||
|
var mr = go.GetComponent<MeshRenderer>();
|
||||||
|
if (mr != null)
|
||||||
|
{
|
||||||
|
// One .material access -> single clone of the primitive's
|
||||||
|
// default mat. Don't touch renderQueue (P3 regression cause).
|
||||||
|
var inst = mr.material;
|
||||||
|
if (inst != null) inst.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3D text label - lays flat on top of the pillar facing up.
|
||||||
|
// Parented to the marker so it follows position changes.
|
||||||
|
var labelGO = new GameObject("Label");
|
||||||
|
labelGO.transform.SetParent(go.transform, worldPositionStays: false);
|
||||||
|
// Local Y offset: pillar's local scale Y is kMarkerHeight/2, but
|
||||||
|
// the cylinder primitive is 2 units tall in local space, so its
|
||||||
|
// top is at local +1. Label sits a hair above that.
|
||||||
|
labelGO.transform.localPosition = new Vector3(0, 1.05f, 0);
|
||||||
|
// Rotate 90 around X so the text quad's normal points +Y (toward
|
||||||
|
// the top-down camera). The default TMP forward is +Z.
|
||||||
|
labelGO.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
|
||||||
|
// Compensate for the cylinder's non-uniform parent scale so the
|
||||||
|
// text size in world units matches kLabelFontSize regardless of
|
||||||
|
// how the pillar was scaled.
|
||||||
|
labelGO.transform.localScale = new Vector3(
|
||||||
|
1f / kMarkerRadius,
|
||||||
|
1f / (kMarkerHeight * 0.5f),
|
||||||
|
1f / kMarkerRadius);
|
||||||
|
|
||||||
|
var tmp = labelGO.AddComponent<TextMeshPro>();
|
||||||
|
tmp.text = label;
|
||||||
|
tmp.fontSize = kLabelFontSize;
|
||||||
|
tmp.color = Color.white;
|
||||||
|
tmp.fontStyle = FontStyles.Bold;
|
||||||
|
tmp.alignment = TextAlignmentOptions.Center;
|
||||||
|
tmp.outlineColor = Color.black;
|
||||||
|
tmp.outlineWidth = 0.25f;
|
||||||
|
// Reasonable bounds so the text mesh isn't auto-clipped.
|
||||||
|
var rt = tmp.rectTransform;
|
||||||
|
rt.sizeDelta = new Vector2(20, 4);
|
||||||
|
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color ColorForPOI(MapPOIType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MapPOIType.FoodDrink: return new Color(1.00f, 0.55f, 0.00f); // orange
|
||||||
|
case MapPOIType.Shop: return new Color(0.20f, 0.60f, 1.00f); // blue
|
||||||
|
case MapPOIType.Health: return new Color(0.96f, 0.27f, 0.27f); // red
|
||||||
|
case MapPOIType.Transport: return new Color(0.85f, 0.85f, 0.20f); // yellow
|
||||||
|
case MapPOIType.Culture: return new Color(0.65f, 0.30f, 0.95f); // purple
|
||||||
|
case MapPOIType.Landmark: return new Color(0.95f, 0.85f, 0.40f); // gold
|
||||||
|
case MapPOIType.Recreation: return new Color(0.30f, 0.85f, 0.30f); // green
|
||||||
|
default: return new Color(0.75f, 0.75f, 0.80f); // muted grey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string LabelForPOI(MapPOIType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MapPOIType.FoodDrink: return "FOOD";
|
||||||
|
case MapPOIType.Shop: return "SHOP";
|
||||||
|
case MapPOIType.Health: return "HEALTH";
|
||||||
|
case MapPOIType.Transport: return "TRANSIT";
|
||||||
|
case MapPOIType.Culture: return "CULTURE";
|
||||||
|
case MapPOIType.Landmark: return "LANDMARK";
|
||||||
|
case MapPOIType.Recreation: return "PARK";
|
||||||
|
default: return "POI";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ClearChildren()
|
||||||
|
{
|
||||||
|
List<GameObject> toDestroy = new List<GameObject>();
|
||||||
|
foreach (Transform t in _mapCenterPoint.transform)
|
||||||
|
toDestroy.Add(t.gameObject);
|
||||||
|
foreach (var g in toDestroy)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.DestroyImmediate(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#region Mesh Building
|
||||||
|
GameObject BuildBuildingMesh(MapBuilding b)
|
||||||
|
{
|
||||||
|
var building = new GameObject($"Building_{b.Name ?? "Unknown"}");
|
||||||
|
|
||||||
|
// Výpočet středu budovy. Lift the base above kPathY so building
|
||||||
|
// walls visibly extrude *upward* from above the road/area layer
|
||||||
|
// instead of starting at ground (which made them clip into paved
|
||||||
|
// areas that share their footprint).
|
||||||
|
Vector3 center = CalculatePolygonCenter(b.Outline);
|
||||||
|
building.transform.position = center + Vector3.up * kBuildingBaseY;
|
||||||
|
|
||||||
|
// Vytvoření mesh pro budovu
|
||||||
|
MeshFilter meshFilter = building.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer meshRenderer = building.AddComponent<MeshRenderer>();
|
||||||
|
|
||||||
|
float height;
|
||||||
|
Material mat;
|
||||||
|
switch (b.BuildingType.ToLower())
|
||||||
|
{
|
||||||
|
case "residential":
|
||||||
|
mat = _buildingSettings.ResidentialBuildingsMat;
|
||||||
|
height = _buildingSettings.ResidentialBuildingHeight;
|
||||||
|
break;
|
||||||
|
case "commercial":
|
||||||
|
mat = _buildingSettings.CommercialBuildingsMat;
|
||||||
|
height = _buildingSettings.CommercialBuildingHeight;
|
||||||
|
break;
|
||||||
|
case "industrial":
|
||||||
|
mat = _buildingSettings.IndustrialBuildingsMat;
|
||||||
|
height = _buildingSettings.IndustrialBuildingHeight;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mat = _buildingSettings.DefaultBuildingMat;
|
||||||
|
height = _buildingSettings.DefaultBuildingHeight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Mesh mesh = CreateExtrudedPolygonMesh(b.Outline, height);
|
||||||
|
meshFilter.mesh = mesh;
|
||||||
|
|
||||||
|
//TODO: material by type
|
||||||
|
// Použijeme barvu podle typu budovy. Use sharedMaterial to keep
|
||||||
|
// the project's Material asset reference - no clone, no leak.
|
||||||
|
// Y-position alone disambiguates building geometry from area/path
|
||||||
|
// layers; we don't need renderQueue overrides (which broke mobile
|
||||||
|
// rendering for transparent-class shaders in P3).
|
||||||
|
meshRenderer.sharedMaterial = mat;
|
||||||
|
|
||||||
|
// Přidání collideru pro interakci
|
||||||
|
building.AddComponent<MeshCollider>();
|
||||||
|
return building;
|
||||||
|
}
|
||||||
|
GameObject BuildPathwayMesh(MapPathway w)
|
||||||
|
{
|
||||||
|
var path = new GameObject($"Path_{w.Name ?? "Unknown"}");
|
||||||
|
|
||||||
|
// Použijeme LineRenderer pro jednoduchost
|
||||||
|
LineRenderer line = path.AddComponent<LineRenderer>();
|
||||||
|
float width;
|
||||||
|
Material mat;
|
||||||
|
|
||||||
|
switch (w.PathType)
|
||||||
|
{
|
||||||
|
case PathType.Footway:
|
||||||
|
mat = _pathwaySettings.FootwayMat;
|
||||||
|
width = _pathwaySettings.FootwayWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Path:
|
||||||
|
mat = _pathwaySettings.PathMat;
|
||||||
|
width = _pathwaySettings.PathWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Steps:
|
||||||
|
mat = _pathwaySettings.StepsMat;
|
||||||
|
width = _pathwaySettings.PathWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Cycleway:
|
||||||
|
mat = _pathwaySettings.CyclewayMat;
|
||||||
|
width = _pathwaySettings.CyclewayWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Pedestrian:
|
||||||
|
mat = _pathwaySettings.PedestrianMat;
|
||||||
|
width = _pathwaySettings.PedestrianWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Road:
|
||||||
|
mat = _pathwaySettings.RoadMat;
|
||||||
|
width = _pathwaySettings.RoadWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Service:
|
||||||
|
mat = _pathwaySettings.ServiceMat;
|
||||||
|
width = _pathwaySettings.ServiceWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Residential:
|
||||||
|
mat = _pathwaySettings.ResidentialMat;
|
||||||
|
width = _pathwaySettings.ResidentialWidth;
|
||||||
|
break;
|
||||||
|
case PathType.Track:
|
||||||
|
mat = _pathwaySettings.TrackMat;
|
||||||
|
width = _pathwaySettings.TrackWidth;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mat = _pathwaySettings.DefaultMat;
|
||||||
|
width = _pathwaySettings.DefaultWidth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sharedMaterial avoids the LineRenderer cloning the project's
|
||||||
|
// shared path Material on every BuildMap call. Queue overrides
|
||||||
|
// dropped (P3 mobile-render regression cause).
|
||||||
|
line.sharedMaterial = mat;
|
||||||
|
line.widthMultiplier = width;
|
||||||
|
|
||||||
|
// Nastavení bodů cesty - kPathY sits above all area polygons but
|
||||||
|
// below building bases, so paths visibly run on top of areas.
|
||||||
|
line.positionCount = w.Points.Count;
|
||||||
|
for (int i = 0; i < w.Points.Count; i++)
|
||||||
|
{
|
||||||
|
Vector3 pos = w.Points[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
pos.y = kPathY;
|
||||||
|
line.SetPosition(i, pos);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
GameObject BuildAreaMesh(MapArea a)
|
||||||
|
{
|
||||||
|
var area = new GameObject($"Area_{a.Name ?? "Unknown"}");
|
||||||
|
|
||||||
|
MeshFilter meshFilter = area.AddComponent<MeshFilter>();
|
||||||
|
MeshRenderer meshRenderer = area.AddComponent<MeshRenderer>();
|
||||||
|
|
||||||
|
// Vytvoření plochého mesh
|
||||||
|
Mesh mesh = CreateFlatPolygonMesh(a.Outline);
|
||||||
|
meshFilter.mesh = mesh;
|
||||||
|
|
||||||
|
|
||||||
|
Material mat;
|
||||||
|
switch (a.AreaType)
|
||||||
|
{
|
||||||
|
case MapAreaType.Park:
|
||||||
|
mat = _areaSettings.ParkMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Garden:
|
||||||
|
mat = _areaSettings.GardenMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Playground:
|
||||||
|
mat = _areaSettings.PlaygroundMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Forest:
|
||||||
|
mat = _areaSettings.ForestMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Grass:
|
||||||
|
mat = _areaSettings.GrassMat;
|
||||||
|
break;
|
||||||
|
case MapAreaType.Water:
|
||||||
|
mat = _areaSettings.WaterMat;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mat = _areaSettings.DefaultMat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sharedMaterial: no per-area material clone. Render-queue forcing
|
||||||
|
// dropped in P8 (caused mobile-render regression). The Y-stagger
|
||||||
|
// below alone now drives "smaller polygon on top of larger one"
|
||||||
|
// depth ordering - which is what the depth buffer was always
|
||||||
|
// designed to do, and works on mobile GPUs with weak precision
|
||||||
|
// because the stagger spread (0.04 units) is well above any
|
||||||
|
// reasonable depth-buffer epsilon.
|
||||||
|
meshRenderer.sharedMaterial = mat;
|
||||||
|
|
||||||
|
// Y stagger: smaller polygons sit a hair higher than larger ones,
|
||||||
|
// so depth-test draws them on top of bigger area polygons they sit
|
||||||
|
// inside (e.g. a playground inside a park). Total spread is 0.04
|
||||||
|
// units - visually invisible but plenty for the depth buffer.
|
||||||
|
float yStagger = ComputeAreaYStagger(a.Outline);
|
||||||
|
area.transform.position = new Vector3(0, kAreaBaseY + yStagger, 0);
|
||||||
|
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a non-negative size proxy used to bucket areas by footprint.
|
||||||
|
/// Larger polygons return higher numbers; used inversely for queue/Y.
|
||||||
|
/// </summary>
|
||||||
|
private float AreaSizeBucket(List<Position> outline)
|
||||||
|
{
|
||||||
|
if (outline == null || outline.Count < 3) return 1f;
|
||||||
|
// Cheap bbox area in lat-lon space scaled by 1e6 - we only need a
|
||||||
|
// monotonic ordering, not a real geographic area.
|
||||||
|
double minLat = outline[0].Lat, maxLat = outline[0].Lat;
|
||||||
|
double minLon = outline[0].Lon, maxLon = outline[0].Lon;
|
||||||
|
for (int i = 1; i < outline.Count; i++)
|
||||||
|
{
|
||||||
|
if (outline[i].Lat < minLat) minLat = outline[i].Lat;
|
||||||
|
if (outline[i].Lat > maxLat) maxLat = outline[i].Lat;
|
||||||
|
if (outline[i].Lon < minLon) minLon = outline[i].Lon;
|
||||||
|
if (outline[i].Lon > maxLon) maxLon = outline[i].Lon;
|
||||||
|
}
|
||||||
|
double bbox = (maxLat - minLat) * (maxLon - minLon) * 1e6;
|
||||||
|
return (float)System.Math.Max(0.001, bbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Smaller areas get a higher Y so they render on top of any larger
|
||||||
|
/// area they overlap. Returns a value in [0, 0.04] units.
|
||||||
|
/// </summary>
|
||||||
|
private float ComputeAreaYStagger(List<Position> outline)
|
||||||
|
{
|
||||||
|
float bucket = AreaSizeBucket(outline);
|
||||||
|
// Inverse mapping: huge area -> 0, tiny area -> 0.04.
|
||||||
|
float t = Mathf.Clamp01(1f - bucket / (bucket + 50f));
|
||||||
|
return t * 0.04f;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region Polygon Utils
|
||||||
|
private Vector3 CalculatePolygonCenter(List<Position> points)
|
||||||
|
{
|
||||||
|
Vector3 center = Vector3.zero;
|
||||||
|
foreach (var point in points)
|
||||||
|
{
|
||||||
|
center += point.ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center);
|
||||||
|
}
|
||||||
|
return center / points.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signed XZ shoelace area for a polygon expressed in local Vector3.
|
||||||
|
/// Positive = CCW (Unity left-handed Y-up: upward-facing normal),
|
||||||
|
/// negative = CW (downward-facing normal -> top face invisible from
|
||||||
|
/// above unless we reverse the winding before triangulating).
|
||||||
|
/// </summary>
|
||||||
|
private static float PolygonSignedAreaXZ(List<Vector3> verts)
|
||||||
|
{
|
||||||
|
float area = 0f;
|
||||||
|
int n = verts.Count;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
var a = verts[i];
|
||||||
|
var b = verts[(i + 1) % n];
|
||||||
|
area += (b.x - a.x) * (a.z + b.z);
|
||||||
|
}
|
||||||
|
return area * 0.5f;
|
||||||
|
}
|
||||||
|
private Mesh CreateExtrudedPolygonMesh(List<Position> outline, float height)
|
||||||
|
{
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
|
||||||
|
// Reject degenerates - Recast/Overpass can hand back 1-2 vertex
|
||||||
|
// outlines on broken ways. Empty mesh -> renderer draws nothing,
|
||||||
|
// safer than a malformed triangle list.
|
||||||
|
if (outline == null || outline.Count < 3) return mesh;
|
||||||
|
|
||||||
|
// Convert to local space first so we can run a winding check, then
|
||||||
|
// reverse if needed. Without this, CW outlines from Overpass yield
|
||||||
|
// downward-facing top normals and the building roof is invisible
|
||||||
|
// from the top-down map camera.
|
||||||
|
int vertexCount = outline.Count;
|
||||||
|
var localVerts = new List<Vector3>(vertexCount);
|
||||||
|
Vector3 center = CalculatePolygonCenter(outline);
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
localVerts.Add(outline[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center) - center);
|
||||||
|
|
||||||
|
if (PolygonSignedAreaXZ(localVerts) < 0f)
|
||||||
|
localVerts.Reverse();
|
||||||
|
|
||||||
|
// Vertices - spodní a horní podstava
|
||||||
|
Vector3[] vertices = new Vector3[vertexCount * 2];
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
Vector3 pos = localVerts[i];
|
||||||
|
vertices[i] = pos; // Spodní
|
||||||
|
vertices[i + vertexCount] = pos + Vector3.up * height; // Horní
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangles - jen boční stěny pro jednoduchost
|
||||||
|
List<int> triangles = new List<int>();
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
int next = (i + 1) % vertexCount;
|
||||||
|
|
||||||
|
// Boční stěna - dva trojúhelníky
|
||||||
|
triangles.Add(i);
|
||||||
|
triangles.Add(i + vertexCount);
|
||||||
|
triangles.Add(next);
|
||||||
|
|
||||||
|
triangles.Add(next);
|
||||||
|
triangles.Add(i + vertexCount);
|
||||||
|
triangles.Add(next + vertexCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horní podstava - zjednodušená triangulace (fan)
|
||||||
|
if (vertexCount >= 3)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < vertexCount - 1; i++)
|
||||||
|
{
|
||||||
|
triangles.Add(vertexCount); // Střed (první bod horní)
|
||||||
|
triangles.Add(vertexCount + i);
|
||||||
|
triangles.Add(vertexCount + i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.vertices = vertices;
|
||||||
|
mesh.triangles = triangles.ToArray();
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
mesh.RecalculateBounds();
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
private Mesh CreateFlatPolygonMesh(List<Position> outline)
|
||||||
|
{
|
||||||
|
Mesh mesh = new Mesh();
|
||||||
|
|
||||||
|
// Reject degenerates (matches CreateExtrudedPolygonMesh).
|
||||||
|
if (outline == null || outline.Count < 3) return mesh;
|
||||||
|
|
||||||
|
int vertexCount = outline.Count;
|
||||||
|
var localVerts = new List<Vector3>(vertexCount);
|
||||||
|
Vector3 center = CalculatePolygonCenter(outline);
|
||||||
|
for (int i = 0; i < vertexCount; i++)
|
||||||
|
localVerts.Add(outline[i].ToLocalVector3(_gameClient.CurrentLobbyState.MapData.Center) - center);
|
||||||
|
|
||||||
|
// Force CCW so RecalculateNormals produces an upward-facing normal.
|
||||||
|
// CW polygons from Overpass would otherwise render as black voids
|
||||||
|
// when the top-down camera looks at their back face.
|
||||||
|
if (PolygonSignedAreaXZ(localVerts) < 0f)
|
||||||
|
localVerts.Reverse();
|
||||||
|
|
||||||
|
Vector3[] vertices = localVerts.ToArray();
|
||||||
|
|
||||||
|
// Triangulace - fan pattern
|
||||||
|
List<int> triangles = new List<int>();
|
||||||
|
for (int i = 1; i < vertexCount - 1; i++)
|
||||||
|
{
|
||||||
|
triangles.Add(0);
|
||||||
|
triangles.Add(i);
|
||||||
|
triangles.Add(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh.vertices = vertices;
|
||||||
|
mesh.triangles = triangles.ToArray();
|
||||||
|
mesh.RecalculateNormals();
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#region Markers
|
||||||
|
|
||||||
|
public void CreateTaskMarkers(List<GeoSus.Client.GameTask> tasks)
|
||||||
|
{
|
||||||
|
if (_mapCenterPoint == null) return;
|
||||||
|
if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0)
|
||||||
|
{
|
||||||
|
var md = _gameClient?.CurrentLobbyState?.MapData;
|
||||||
|
if (md != null) _centerPosition = md.Center;
|
||||||
|
}
|
||||||
|
if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0) return;
|
||||||
|
var taskColor = new Color(0.20f, 0.95f, 0.55f); // bright green - "GO HERE"
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
if (_taskMarkers.ContainsKey(task.TaskId)) continue;
|
||||||
|
var pos = task.Location.ToLocalVector3(_centerPosition);
|
||||||
|
var go = CreateMarkerWithLabel($"Task_{task.TaskId}", pos, taskColor, "TASK");
|
||||||
|
go.transform.parent = _mapCenterPoint.transform;
|
||||||
|
|
||||||
|
// Pulsing point light so the task literally glows on the map.
|
||||||
|
var light = go.AddComponent<Light>();
|
||||||
|
light.color = taskColor;
|
||||||
|
light.intensity = 3f;
|
||||||
|
light.range = 25f;
|
||||||
|
_taskMarkers[task.TaskId] = go;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveTaskMarker(string taskId)
|
||||||
|
{
|
||||||
|
if (_taskMarkers.TryGetValue(taskId, out var go))
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(go);
|
||||||
|
_taskMarkers.Remove(taskId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateBodyMarker(string bodyId, Position location)
|
||||||
|
{
|
||||||
|
if (_mapCenterPoint == null) return;
|
||||||
|
if (_bodyMarkers.ContainsKey(bodyId)) return;
|
||||||
|
var pos = location.ToLocalVector3(_centerPosition);
|
||||||
|
// Bright red pillar with "BODY" label - players need to see this
|
||||||
|
// from across the map to call it in.
|
||||||
|
var go = CreateMarkerWithLabel($"Body_{bodyId}", pos,
|
||||||
|
new Color(0.96f, 0.18f, 0.18f), "BODY");
|
||||||
|
go.transform.parent = _mapCenterPoint?.transform;
|
||||||
|
_bodyMarkers[bodyId] = go;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearBodyMarkers()
|
||||||
|
{
|
||||||
|
foreach (var go in _bodyMarkers.Values)
|
||||||
|
if (go) UnityEngine.Object.Destroy(go);
|
||||||
|
_bodyMarkers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Player avatar sizing ────────────────────────────────────────────
|
||||||
|
// The default Unity capsule primitive is 2m tall in local space. The
|
||||||
|
// map camera defaults to 150m orthographic-ish height (see
|
||||||
|
// MapCameraController), so anything smaller than ~3m world-size is a
|
||||||
|
// pixel on screen. Original code used scale=0.4 (~0.8m capsule) which
|
||||||
|
// was invisible. Markers (POIs/tasks/bodies) are 8m pillars; players
|
||||||
|
// need to be visibly distinct from those AND from each other. The
|
||||||
|
// local player gets a halo light + larger scale so the user can find
|
||||||
|
// themselves on the map at a glance.
|
||||||
|
private const float kLocalPlayerScale = 4f; // ~8m capsule (matches marker height)
|
||||||
|
private const float kRemotePlayerScale = 2f; // ~4m capsule (smaller than markers)
|
||||||
|
private const float kLocalPlayerHaloRange = 18f;
|
||||||
|
private const float kLocalPlayerHaloIntensity = 2.5f;
|
||||||
|
|
||||||
|
public void UpdatePlayerAvatars(Dictionary<string, PlayerPositionInfo> positions, string myUuid)
|
||||||
|
{
|
||||||
|
if (_mapCenterPoint == null) return;
|
||||||
|
if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0)
|
||||||
|
{
|
||||||
|
var md = _gameClient?.CurrentLobbyState?.MapData;
|
||||||
|
if (md != null) _centerPosition = md.Center;
|
||||||
|
}
|
||||||
|
if (_centerPosition.Lat == 0 && _centerPosition.Lon == 0) return;
|
||||||
|
foreach (var kvp in positions)
|
||||||
|
{
|
||||||
|
string uuid = kvp.Key;
|
||||||
|
var info = kvp.Value;
|
||||||
|
bool isLocal = uuid == myUuid;
|
||||||
|
if (!_playerAvatars.TryGetValue(uuid, out var go) || go == null)
|
||||||
|
{
|
||||||
|
go = GameObject.CreatePrimitive(PrimitiveType.Capsule);
|
||||||
|
go.name = $"Player_{uuid.Substring(0, Mathf.Min(8, uuid.Length))}";
|
||||||
|
go.transform.parent = _mapCenterPoint?.transform;
|
||||||
|
// Strip the auto-collider - avatars are visual only and the
|
||||||
|
// collider would interact with the map's MeshColliders.
|
||||||
|
var col = go.GetComponent<Collider>();
|
||||||
|
if (col != null) UnityEngine.Object.Destroy(col);
|
||||||
|
|
||||||
|
float scale = isLocal ? kLocalPlayerScale : kRemotePlayerScale;
|
||||||
|
go.transform.localScale = Vector3.one * scale;
|
||||||
|
|
||||||
|
if (isLocal)
|
||||||
|
{
|
||||||
|
// Halo light around the local player so the user can
|
||||||
|
// find themselves at a glance even at the widest zoom.
|
||||||
|
// Range/intensity tuned so it reads as "this is me"
|
||||||
|
// without bleeding far enough to drown POI markers.
|
||||||
|
var halo = go.AddComponent<Light>();
|
||||||
|
halo.color = new Color(0.30f, 1.00f, 0.55f); // matches green capsule color
|
||||||
|
halo.intensity = kLocalPlayerHaloIntensity;
|
||||||
|
halo.range = kLocalPlayerHaloRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
_playerAvatars[uuid] = go;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lift the avatar so the bottom of the capsule sits roughly at
|
||||||
|
// ground level despite the larger scale. Capsule's local pivot
|
||||||
|
// is at center, height = 2 * localScale.y world units, so we
|
||||||
|
// raise by half the local height.
|
||||||
|
float halfHeight = (isLocal ? kLocalPlayerScale : kRemotePlayerScale);
|
||||||
|
go.transform.position = info.Position.ToLocalVector3(_centerPosition)
|
||||||
|
+ Vector3.up * halfHeight;
|
||||||
|
|
||||||
|
var mr = go.GetComponent<MeshRenderer>();
|
||||||
|
if (mr)
|
||||||
|
{
|
||||||
|
if (isLocal) mr.material.color = new Color(0.30f, 1.00f, 0.55f);
|
||||||
|
else if (info.State == GeoSus.Client.PlayerState.Dead) mr.material.color = Color.grey;
|
||||||
|
else mr.material.color = Color.white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateSabotageMarkers(List<RepairStationInfo> stations)
|
||||||
|
{
|
||||||
|
var color = new Color(1.0f, 0.55f, 0.0f); // strong orange = repair urgency
|
||||||
|
foreach (var station in stations)
|
||||||
|
{
|
||||||
|
var pos = station.Location.ToLocalVector3(_centerPosition);
|
||||||
|
var go = CreateMarkerWithLabel($"Sabotage_{station.StationId}", pos,
|
||||||
|
color, "REPAIR");
|
||||||
|
go.transform.parent = _mapCenterPoint?.transform;
|
||||||
|
|
||||||
|
// Repair stations also pulse light so impostors and crew see
|
||||||
|
// the urgency from across the map.
|
||||||
|
var light = go.AddComponent<Light>();
|
||||||
|
light.color = color;
|
||||||
|
light.intensity = 4f;
|
||||||
|
light.range = 30f;
|
||||||
|
_sabotageMarkers.Add(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearSabotageMarkers()
|
||||||
|
{
|
||||||
|
foreach (var go in _sabotageMarkers)
|
||||||
|
if (go) UnityEngine.Object.Destroy(go);
|
||||||
|
_sabotageMarkers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||