6 Commits

Author SHA1 Message Date
97b473cb01 docs: UML updates (#58) 2025-05-08 17:50:45 +02:00
8fe04b974a chore(release): v1.0.0-staging.3 2025-04-28 22:25:40 +02:00
9287a16610 ci: improvements to release process 2025-04-28 22:25:26 +02:00
edc6504e10 feat: export level (#57) 2025-04-28 21:51:37 +02:00
02ef8cdf74 feat: map editor (#56) 2025-04-28 16:52:13 +02:00
5cd536be84 chore(release): v1.0.0-staging.2 2025-04-28 15:21:50 +02:00
65 changed files with 9712 additions and 356 deletions

View File

@ -50,6 +50,7 @@ jobs:
- name: Upload Windows Build Artifact
id: upload
if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4.4.3
with:
name: build-windows
@ -81,7 +82,8 @@ jobs:
- name: Upload macOS Build Artifact
id: upload
uses: actions/upload-artifact@v4.4.3
if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4.3.0
with:
name: build-macos
path: build/
@ -112,6 +114,7 @@ jobs:
- name: Upload Linux Build Artifact
id: upload
if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4.6.2
with:
name: build-linux
@ -122,6 +125,15 @@ jobs:
if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Get version from commit message
id: get_version
run: |
COMMIT_MESSAGE="${{ github.event.head_commit.message }}"
# Extract the version number after "chore(release): "
VERSION=$(echo "$COMMIT_MESSAGE" | sed -n 's/^chore(release): \(v[0-9]*\.[0-9]*\.[0-9]*[-a-zA-Z0-9.]*\).*$/\1/p')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
shell: bash
- name: Download Windows Build Artifact
uses: actions/download-artifact@v4.3.0
with:
@ -153,14 +165,10 @@ jobs:
id: create_release
uses: softprops/action-gh-release@v2.2.2
with:
tag_name: v${{ github.run_number }}
name: |
${{ github.ref == 'refs/heads/main' && 'Production Release' || 'Pre-release' }} v${{ github.run_number }}
tag_name: ${{ steps.get_version.outputs.version }}
name: ${{ steps.get_version.outputs.version }}
body: |
🎮 Automatic ${{ github.ref == 'refs/heads/main' && 'Production' || 'Pre-release' }} build
Branch: `${{ github.ref_name }}`
Commit: `${{ github.sha }}`
prerelease: ${{ github.ref != 'refs/heads/main' }}
files: |
build/windows.zip

View File

@ -1,9 +1,9 @@
{
"recommendations": [
"editorconfig.editorconfig",
"jebbs.plantuml",
"visualstudiotoolsforunity.vstuc",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit"
]
"recommendations": [
"editorconfig.editorconfig",
"jebbs.plantuml",
"visualstudiotoolsforunity.vstuc",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit"
]
}

16
.vscode/launch.json vendored
View File

@ -1,10 +1,10 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Unity",
"type": "vstuc",
"request": "attach"
}
]
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Unity",
"type": "vstuc",
"request": "attach"
}
]
}

16
.vscode/settings.json vendored
View File

@ -1,10 +1,10 @@
{
"editor.bracketPairColorization.enabled": true,
"editor.wordWrap": "on",
"[csharp]": {
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.formatOnType": true
},
"omnisharp.useModernNet": false
"editor.bracketPairColorization.enabled": true,
"editor.wordWrap": "on",
"[csharp]": {
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.formatOnType": true
},
"omnisharp.useModernNet": false
}

View File

@ -97,7 +97,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1

View File

@ -1,10 +1,10 @@
fileFormatVersion: 2
guid: b51d2de513d090b4485816dbca782498
guid: e0426acdd6d763946bab617252a0f5aa
TextureImporter:
internalIDToNameTable:
- first:
213: 6385790085498839582
second: BlankSquare_0
213: 1925945982471551178
second: BlankSquare 1_0
externalObjects: {}
serializedVersion: 13
mipmaps:
@ -96,24 +96,11 @@ TextureImporter:
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: BlankSquare_0
name: BlankSquare 1_0
rect:
serializedVersion: 2
x: 0
@ -128,8 +115,8 @@ TextureImporter:
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: e12ae58df32ee9850800000000000000
internalID: 6385790085498839582
spriteID: ac8becdbaa55aba10800000000000000
internalID: 1925945982471551178
vertices: []
indices:
edges: []
@ -148,7 +135,7 @@ TextureImporter:
spriteCustomMetadata:
entries: []
nameFileIdTable:
BlankSquare_0: 6385790085498839582
BlankSquare 1_0: 1925945982471551178
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: fa5971280ed90e04cafadbd76400bed2
TextureImporter:
internalIDToNameTable:
- first:
213: 6385790085498839582
second: BlankSquare_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: BlankSquare_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 512
height: 512
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: e12ae58df32ee9850800000000000000
internalID: 6385790085498839582
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
BlankSquare_0: 6385790085498839582
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: ada92be653a1d184391ae7f1f61e32bb
TextureImporter:
internalIDToNameTable:
- first:
213: 4829622404114481491
second: NewLevelButton 1_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: NewLevelButton 1_0
rect:
serializedVersion: 2
x: 15
y: 14
width: 483
height: 483
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 3510384fcf3460340800000000000000
internalID: 4829622404114481491
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
NewLevelButton 1_0: 4829622404114481491
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: afd28cff37c57da4090ac3ddfdb8ee28
TextureImporter:
internalIDToNameTable:
- first:
213: -4781373940957465942
second: exit_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: exit_0
rect:
serializedVersion: 2
x: 31
y: 31
width: 298
height: 298
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: aa2bbe6cbb525adb0800000000000000
internalID: -4781373940957465942
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
exit_0: -4781373940957465942
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: f34830d17508a4b478c164611c202e12
TextureImporter:
internalIDToNameTable:
- first:
213: 229215520534054086
second: RegularBlock01_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: RegularBlock01_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 122
height: 122
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 6c4407e2d465e2300800000000000000
internalID: 229215520534054086
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
RegularBlock01_0: 229215520534054086
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1d7f8c8f8305efa4c8b78f4dd391c49b
guid: 1b21bd4ccae70934eb86a3a2a6928e98
TextureImporter:
internalIDToNameTable:
- first:

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 7fe0358ef92ff8a449d0bee123119795
TextureImporter:
internalIDToNameTable:
- first:
213: 2079131731516339571
second: SaveButton_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: SaveButton_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 980
height: 982
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 375364a4b4f8adc10800000000000000
internalID: 2079131731516339571
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
SaveButton_0: 2079131731516339571
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7bbd15eb9b940f4c59561753a3d516ce
guid: bcd6c4da7709ff9458a5784f7f807e30
TextureImporter:
internalIDToNameTable:
- first:

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 6b64c942662170e44b2f80d605a405f4
TextureImporter:
internalIDToNameTable:
- first:
213: -2073137460773038770
second: PauseIcon_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: PauseIcon_0
rect:
serializedVersion: 2
x: 17
y: 19
width: 239
height: 218
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: e455110a67cba33e0800000000000000
internalID: -2073137460773038770
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
PauseIcon_0: -2073137460773038770
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -97,7 +97,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1

View File

@ -97,7 +97,7 @@ TextureImporter:
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 79b56765ff1452848bd947d19c642244
TextureImporter:
internalIDToNameTable:
- first:
213: -6564193165698205349
second: Remove_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: Remove_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 512
height: 512
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: b59f289281d47e4a0800000000000000
internalID: -6564193165698205349
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
Remove_0: -6564193165698205349
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -147,7 +147,8 @@ TextureImporter:
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
nameFileIdTable:
Logo_0: 1404028623067837608
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:

View File

@ -306,6 +306,9 @@ GameObject:
- component: {fileID: 678214373}
- component: {fileID: 678214375}
- component: {fileID: 678214374}
- component: {fileID: 678214377}
- component: {fileID: 678214376}
- component: {fileID: 678214378}
m_Layer: 5
m_Name: Save
m_TagString: Untagged
@ -370,6 +373,90 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 678214372}
m_CullTransparentMesh: 1
--- !u!114 &678214376
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 678214372}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 678214374}
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 678214377}
m_TargetAssemblyTypeName: JSONExporter, Assembly-CSharp
m_MethodName: ExportJSON
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
--- !u!114 &678214377
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 678214372}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d3227fe18f4441647bdd19d3131efa02, type: 3}
m_Name:
m_EditorClassIdentifier:
editor: {fileID: 0}
--- !u!114 &678214378
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 678214372}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f5bf592d3d8430744a08058675701934, type: 3}
m_Name:
m_EditorClassIdentifier:
mapParent: {fileID: 0}
blockGroupContainer: {fileID: 394350961}
buttonPrefabTemplate: {fileID: 1993500743}
--- !u!1 &739948034
GameObject:
m_ObjectHideFlags: 0

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 73b983a44d701df4bb6d8ceb94e05a2b
guid: bdddba7e4ac5baa4e833a5e67122d0d3
DefaultImporter:
externalObjects: {}
userData:

View File

@ -859,7 +859,7 @@ MonoBehaviour:
m_Calls:
- m_Target: {fileID: 1858033414}
m_TargetAssemblyTypeName: MainMenu, Assembly-CSharp
m_MethodName: CreateLevel
m_MethodName: LevelEditor
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}

View File

@ -155,7 +155,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -110}
m_AnchoredPosition: {x: 8.3, y: -154.1}
m_SizeDelta: {x: 160, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &39408930
@ -562,6 +562,7 @@ RectTransform:
- {fileID: 666344588}
- {fileID: 39408929}
- {fileID: 1809963736}
- {fileID: 780585405}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@ -605,7 +606,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -45}
m_AnchoredPosition: {x: 8.3, y: -95.7}
m_SizeDelta: {x: 160, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &666344589
@ -702,6 +703,81 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 666344587}
m_CullTransparentMesh: 1
--- !u!1 &780585404
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 780585405}
- component: {fileID: 780585407}
- component: {fileID: 780585406}
m_Layer: 5
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &780585405
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 780585404}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 481473977}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 8.3, y: 82.4}
m_SizeDelta: {x: 206.4181, y: 202.9899}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &780585406
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 780585404}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 1404028623067837608, guid: 7941bec4ab82ba5f8a978cf1b4d91010, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &780585407
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 780585404}
m_CullTransparentMesh: 1
--- !u!1 &948896765
GameObject:
m_ObjectHideFlags: 0
@ -953,7 +1029,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 15.000004}
m_AnchoredPosition: {x: 8.3, y: -40.7}
m_SizeDelta: {x: 160, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1587889809
@ -1164,7 +1240,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -4781373940957465942, guid: 1d7f8c8f8305efa4c8b78f4dd391c49b, type: 3}
m_Sprite: {fileID: -4781373940957465942, guid: 1b21bd4ccae70934eb86a3a2a6928e98, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -544,6 +544,7 @@ MonoBehaviour:
normalMinYFollow: 2
shipMinYFollow: 6
smoothSpeed: 5
isPlaying: 1
--- !u!1 &521952199
GameObject:
m_ObjectHideFlags: 0
@ -6221,7 +6222,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -2073137460773038770, guid: 7bbd15eb9b940f4c59561753a3d516ce, type: 3}
m_Sprite: {fileID: -2073137460773038770, guid: bcd6c4da7709ff9458a5784f7f807e30, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
@ -7317,7 +7318,6 @@ MonoBehaviour:
levelsLoader: {fileID: 0}
audioSource: {fileID: 1999482188}
progressionText: {fileID: 245275149}
groundWidth: 1
--- !u!4 &1999482187
Transform:
m_ObjectHideFlags: 0

View File

@ -0,0 +1,20 @@
using UnityEngine;
public class CameraController : MonoBehaviour
{
public float moveSpeed = 10f;
void Update()
{
float horizontalInput = Input.GetAxisRaw("Horizontal"); // ← → ou A D
float verticalInput = Input.GetAxisRaw("Vertical"); // ↑ ↓ ou W S
Vector3 movement = new Vector3(
horizontalInput * moveSpeed * Time.deltaTime,
verticalInput * moveSpeed * Time.deltaTime,
0f
);
Camera.main.transform.position += movement;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 74af3e3f2c02bde43b24c2f56589d071

View File

@ -42,7 +42,10 @@ public class NormalGameMode : IGameMode
{
player.RigidBody.linearVelocity = new Vector2(player.RigidBody.linearVelocity.x, 0);
player.RigidBody.AddForce(Vector2.up * JumpForce, ForceMode2D.Impulse);
player.LevelsLoader.IncreaseTotalJumps();
if (player.LevelsLoader != null)
{
player.LevelsLoader.IncreaseTotalJumps();
}
isRotating = true;
targetRotationAngle = player.transform.eulerAngles.z - 90f;
}

View File

@ -59,7 +59,10 @@ public class ShipGameMode : IGameMode
{
player.RigidBody.linearVelocity = new Vector2(player.RigidBody.linearVelocity.x, 0);
player.RigidBody.AddForce(Vector2.up * JumpForce, ForceMode2D.Impulse);
player.LevelsLoader.IncreaseTotalJumps();
if (player.LevelsLoader != null)
{
player.LevelsLoader.IncreaseTotalJumps();
}
}
public void OnCollisionEnter(Player player, Collision2D collision)

View File

@ -0,0 +1,147 @@
using UnityEngine;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using SimpleFileBrowser;
using TMPro;
[RequireComponent(typeof(LevelEditor))]
public class JSONExporter : MonoBehaviour
{
public TMP_Text statusText;
private LevelEditor editor;
private string levelsFolder;
private void Awake()
{
editor = GetComponent<LevelEditor>();
levelsFolder = Path.Combine(Application.dataPath, "Resources/Levels");
if (!Directory.Exists(levelsFolder))
Directory.CreateDirectory(levelsFolder);
if (statusText == null)
{
var statusObj = GameObject.Find("StatusText");
if (statusObj != null)
statusText = statusObj.GetComponent<TMP_Text>();
}
}
private void Start()
{
SetStatus("Ready to export...", Color.white);
}
public void ExportJSON()
{
SetStatus("Exporting...", Color.yellow);
StartCoroutine(ShowSaveDialog());
}
private IEnumerator ShowSaveDialog()
{
yield return FileBrowser.WaitForSaveDialog(
FileBrowser.PickMode.Files,
false,
levelsFolder,
"NewLevel.json",
"Save Level JSON",
"Save"
);
if (!FileBrowser.Success)
{
SetStatus("Save canceled.", Color.red);
yield break;
}
string chosenPath = FileBrowser.Result[0];
string fileName = Path.GetFileNameWithoutExtension(chosenPath);
string destPath = Path.Combine(levelsFolder, fileName + ".json");
var elements = new List<SerializableElement>();
var allCols = Object.FindObjectsByType<Collider2D>(FindObjectsSortMode.None);
foreach (var col in allCols)
{
var go = col.gameObject;
if (!go.name.Contains("(Clone)") || go.name.ToLower().Contains("ground"))
continue;
Vector3 scale = go.transform.localScale;
Vector3 pos = go.transform.position;
elements.Add(new SerializableElement
{
type = go.name.Replace("(Clone)", ""),
x = Mathf.Round(pos.x * 100f) / 100f,
y = Mathf.Round(pos.y * 100f) / 100f,
scaleX = Mathf.Round(scale.x * 100f) / 100f,
scaleY = Mathf.Round(scale.y * 100f) / 100f
});
}
if (elements.Count == 0)
{
SetStatus("No elements to export.", Color.red);
yield break;
}
LevelData data = new LevelData
{
name = fileName,
musicName = "",
order = 0,
elements = elements.ToArray()
};
string json = JsonUtility.ToJson(data, prettyPrint: true);
try
{
File.WriteAllText(destPath, json);
SetStatus("Export successful: " + fileName + ".json", Color.green);
}
catch (System.Exception e)
{
Debug.LogError("Export error: " + e);
SetStatus("Export error. See console.", Color.red);
}
#if UNITY_EDITOR
UnityEditor.AssetDatabase.Refresh();
#endif
var loader = Object.FindAnyObjectByType<LevelsLoader>();
if (loader != null)
loader.RefreshLevels();
}
private void SetStatus(string message, Color color)
{
if (statusText != null)
{
statusText.text = message;
statusText.color = color;
statusText.gameObject.SetActive(false);
statusText.gameObject.SetActive(true);
Canvas.ForceUpdateCanvases();
}
}
[System.Serializable]
private class SerializableElement
{
public string type;
public float x;
public float y;
public float scaleX;
public float scaleY;
}
[System.Serializable]
private class LevelData
{
public string name;
public string musicName;
public int order;
public SerializableElement[] elements;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d3227fe18f4441647bdd19d3131efa02

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
public class LevelEditor : MonoBehaviour
{
[Header("Placement")]
public Transform mapParent;
private GameObject currentBlock;
private bool isPlacingBlock = false;
private Vector3 currentScale = new Vector3(1f, 1f, 1);
@ -42,10 +41,26 @@ public class LevelEditor : MonoBehaviour
void GenerateButtons()
{
if (buttonPrefabTemplate == null)
{
Debug.LogError("LevelEditor.GenerateButtons(): buttonPrefabTemplate nest pas assigné !");
return;
}
if (blockGroupContainer == null)
{
Debug.LogError("LevelEditor.GenerateButtons(): blockGroupContainer nest pas assigné !");
return;
}
ClearCurrentButtons();
Transform container = blockGroupContainer;
if (container == null || buttonPrefabTemplate == null)
{
Debug.LogError("UI Container ou prefab de bouton manquant.");
return;
}
int start = currentPage * buttonsPerPage;
int end = Mathf.Min(start + buttonsPerPage, blockPrefabs.Count);
@ -76,13 +91,9 @@ public class LevelEditor : MonoBehaviour
string prefabName = blockPrefabs[i].name.ToLower();
if (prefabName.Contains("smallspike") || prefabName.Contains("smallobstacle"))
{
icon.GetComponent<RectTransform>().sizeDelta = new Vector2(50, 25);
}
else
{
icon.GetComponent<RectTransform>().sizeDelta = new Vector2(50, 50);
}
GameObject prefab = blockPrefabs[i];
button.GetComponent<Button>().onClick.AddListener(() => SelectPrefab(prefab));
@ -93,9 +104,7 @@ public class LevelEditor : MonoBehaviour
void ClearCurrentButtons()
{
foreach (var button in currentButtons)
{
Destroy(button);
}
currentButtons.Clear();
}
@ -103,6 +112,7 @@ public class LevelEditor : MonoBehaviour
public void NextPage()
{
int maxPage = 3;
Debug.Log(currentPage);
if (currentPage < maxPage - 1)
{
currentPage++;
@ -112,6 +122,7 @@ public class LevelEditor : MonoBehaviour
public void PreviousPage()
{
Debug.Log(currentPage);
if (currentPage > 0)
{
currentPage--;
@ -121,49 +132,38 @@ public class LevelEditor : MonoBehaviour
void SelectPrefab(GameObject prefab)
{
if (isPlacingBlock)
{
return;
}
if (isPlacingBlock) return;
string name = prefab.name.ToLower();
if (name.Contains("portal"))
{
currentScale = new Vector3(0.5f, 0.5f, 1);
}
else if (name.Contains("small"))
{
currentScale = new Vector3(0.15f, 0.07f, 1);
}
else if (name.Contains("spike"))
{
currentScale = new Vector3(0.15f, 0.15f, 1);
}
else if (name.Contains("block"))
{
currentScale = new Vector3(0.2f, 0.2f, 1);
}
else if (name.Contains("bonus"))
{
currentScale = new Vector3(0.3f, 0.3f, 1);
}
else
{
currentScale = new Vector3(1f, 1f, 1);
}
InstantiateAndPrepare(prefab, currentScale);
}
void Update()
{
// Déplacement de l'objet en cours de placement
if (isPlacingBlock && currentBlock != null)
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
currentBlock.transform.position = new Vector3(Mathf.Round(mousePos.x), Mathf.Round(mousePos.y), -1);
if (currentBlock != null && Input.GetKeyDown(KeyCode.R))
{
HandleBlockRotation(); // ✅ Nouvelle rotation
}
if (!currentBlock.name.ToLower().Contains("portal"))
{
float scroll = Input.GetAxis("Mouse ScrollWheel");
@ -185,19 +185,40 @@ public class LevelEditor : MonoBehaviour
if (overlaps.Length > 1)
{
Debug.Log("Placement annulé : un objet est déjà présent à cet endroit.");
return;
}
PlaceBlock();
}
}
if (Input.GetMouseButtonDown(0) && !isPlacingBlock)
else if (Input.GetMouseButtonDown(0)) // Clic gauche pour reprendre un objet déjà placé
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Collider2D hit = Physics2D.OverlapPoint(mousePos);
if (hit != null)
if (hit != null && hit.transform != null)
{
if (hit.CompareTag("Ground"))
{
Debug.Log("Impossible de déplacer le sol (tag Ground).");
return;
}
currentBlock = hit.gameObject;
isPlacingBlock = true;
currentScale = currentBlock.transform.localScale;
Debug.Log($"Déplacement de l'objet : {currentBlock.name}");
return;
}
}
// Redimensionnement d'un objet déjà placé
if (Input.GetMouseButtonDown(0) && Input.GetKey(KeyCode.LeftShift) && !isPlacingBlock)
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Collider2D hit = Physics2D.OverlapPoint(mousePos);
if (hit != null && hit.transform != null && !hit.CompareTag("Ground"))
{
resizingTarget = hit.gameObject;
originalMousePos = mousePos;
@ -212,6 +233,7 @@ public class LevelEditor : MonoBehaviour
: ResizeAxis.Vertical;
isResizing = true;
Debug.Log($"Début de redimensionnement : {resizingTarget.name}, axe = {currentResizeAxis}");
}
}
@ -220,15 +242,34 @@ public class LevelEditor : MonoBehaviour
Vector3 currentMousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 delta = currentMousePos - originalMousePos;
Vector3 newScale = originalScale;
if (currentResizeAxis == ResizeAxis.Horizontal)
{
float newScaleX = Mathf.Max(0.1f, originalScale.x + delta.x);
resizingTarget.transform.localScale = new Vector3(newScaleX, originalScale.y, 1);
}
newScale.x = Mathf.Max(0.1f, originalScale.x + delta.x);
else if (currentResizeAxis == ResizeAxis.Vertical)
newScale.y = Mathf.Max(0.1f, originalScale.y + delta.y);
// Temporarily apply the new scale for collision testing
Vector3 originalPos = resizingTarget.transform.position;
resizingTarget.transform.localScale = newScale;
Bounds bounds = resizingTarget.GetComponent<Collider2D>().bounds;
Collider2D[] overlaps = Physics2D.OverlapBoxAll(bounds.center, bounds.size, 0f);
bool hasCollision = false;
foreach (var col in overlaps)
{
float newScaleY = Mathf.Max(0.1f, originalScale.y + delta.y);
resizingTarget.transform.localScale = new Vector3(originalScale.x, newScaleY, 1);
if (col.gameObject != resizingTarget)
{
hasCollision = true;
break;
}
}
if (hasCollision)
{
resizingTarget.transform.localScale = originalScale; // revert
Debug.Log("Étirement annulé : collision détectée.");
}
if (Input.GetMouseButtonUp(0))
@ -238,30 +279,175 @@ public class LevelEditor : MonoBehaviour
currentResizeAxis = ResizeAxis.None;
}
}
// Clic droit pour supprimer un objet déjà placé (sauf le sol)
if (Input.GetMouseButtonDown(1))
{
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Collider2D hit = Physics2D.OverlapPoint(mousePos);
if (hit != null && hit.transform != null)
{
if (hit.CompareTag("Ground"))
{
Debug.Log("Impossible de supprimer le sol (tag Ground).");
return;
}
Destroy(hit.gameObject);
Debug.Log($"Objet supprimé : {hit.name}");
}
}
}
void PlaceBlock()
{
bool skipVerticalSnap = false;
if (currentBlock.name.ToLower().Contains("smallobstacle") || currentBlock.name.ToLower().Contains("portal"))
{
skipVerticalSnap = true; // On saute l'alignement vertical pour ces cas-là
}
if (!skipVerticalSnap)
{
Vector2 origin = currentBlock.transform.position;
RaycastHit2D[] hitsBelow = Physics2D.RaycastAll(origin, Vector2.down, 100f);
float highestY = -Mathf.Infinity;
GameObject bestTargetBelow = null;
foreach (var hit in hitsBelow)
{
if (hit.collider != null && hit.collider.gameObject != currentBlock)
{
float topOfObject = hit.collider.bounds.max.y;
if (topOfObject > highestY)
{
highestY = topOfObject;
bestTargetBelow = hit.collider.gameObject;
}
}
}
if (bestTargetBelow != null)
{
float height = currentBlock.GetComponent<Collider2D>().bounds.size.y;
currentBlock.transform.position = new Vector3(currentBlock.transform.position.x, highestY + height / 2f, -1);
}
else
{
float height = currentBlock.GetComponent<Collider2D>().bounds.size.y;
currentBlock.transform.position = new Vector3(currentBlock.transform.position.x, height / 2f, -1);
}
}
// ➔ Toujours essayer de snap sur la droite et en bas même pour Portal et SmallObstacle
TrySnapToNearbyBlock();
isPlacingBlock = false;
currentBlock = null;
}
private void TrySnapToNearbyBlock()
{
if (currentBlock == null)
return;
Collider2D blockCollider = currentBlock.GetComponent<Collider2D>();
Bounds bounds = blockCollider.bounds;
float snapDistance = 1f; // Distance de snap (en Unity units)
// Zone de scan à droite
Vector2 rightAreaStart = new Vector2(bounds.max.x, bounds.min.y);
Vector2 rightAreaEnd = new Vector2(bounds.max.x + snapDistance, bounds.max.y);
// Zone de scan à gauche
Vector2 leftAreaStart = new Vector2(bounds.min.x - snapDistance, bounds.min.y);
Vector2 leftAreaEnd = new Vector2(bounds.min.x, bounds.max.y);
// Zone de scan en dessous
Vector2 bottomAreaStart = new Vector2(bounds.min.x, bounds.min.y - snapDistance);
Vector2 bottomAreaEnd = new Vector2(bounds.max.x, bounds.min.y);
// Zone de scan au dessus
Vector2 topAreaStart = new Vector2(bounds.min.x, bounds.max.y);
Vector2 topAreaEnd = new Vector2(bounds.max.x, bounds.max.y + snapDistance);
Collider2D[] hitsRight = Physics2D.OverlapAreaAll(rightAreaStart, rightAreaEnd);
Collider2D[] hitsLeft = Physics2D.OverlapAreaAll(leftAreaStart, leftAreaEnd);
Collider2D[] hitsBelow = Physics2D.OverlapAreaAll(bottomAreaStart, bottomAreaEnd);
Collider2D[] hitsAbove = Physics2D.OverlapAreaAll(topAreaStart, topAreaEnd);
// ➔ Priorité : droite > gauche > bas > haut
foreach (var hit in hitsRight)
{
if (hit != null && hit.gameObject != currentBlock)
{
float theirLeft = hit.bounds.min.x;
float ourWidth = bounds.size.x;
currentBlock.transform.position = new Vector3(theirLeft - ourWidth / 2f, currentBlock.transform.position.y, -1);
Debug.Log("✅ Snap automatique à droite !");
return;
}
}
foreach (var hit in hitsLeft)
{
if (hit != null && hit.gameObject != currentBlock)
{
float theirRight = hit.bounds.max.x;
float ourWidth = bounds.size.x;
currentBlock.transform.position = new Vector3(theirRight + ourWidth / 2f, currentBlock.transform.position.y, -1);
Debug.Log("✅ Snap automatique à gauche !");
return;
}
}
foreach (var hit in hitsBelow)
{
if (hit != null && hit.gameObject != currentBlock)
{
float theirTop = hit.bounds.max.y;
float ourHeight = bounds.size.y;
currentBlock.transform.position = new Vector3(currentBlock.transform.position.x, theirTop + ourHeight / 2f, -1);
Debug.Log("✅ Snap automatique en bas !");
return;
}
}
foreach (var hit in hitsAbove)
{
if (hit != null && hit.gameObject != currentBlock)
{
float theirBottom = hit.bounds.min.y;
float ourHeight = bounds.size.y;
currentBlock.transform.position = new Vector3(currentBlock.transform.position.x, theirBottom - ourHeight / 2f, -1);
Debug.Log("✅ Snap automatique en haut !");
return;
}
}
}
void InstantiateAndPrepare(GameObject prefab, Vector3? scaleOverride = null)
{
GameObject obj = Instantiate(prefab);
obj.transform.position = new Vector3(0, 0, -1);
obj.transform.localScale = scaleOverride ?? currentScale;
if (mapParent != null)
{
obj.transform.SetParent(mapParent);
}
try { obj.tag = prefab.name; }
catch { Debug.LogWarning($"Le tag '{prefab.name}' n'existe pas. Ajoutez-le dans Project Settings > Tags."); }
currentBlock = obj;
isPlacingBlock = true;
}
public void Save()
private void HandleBlockRotation()
{
currentBlock.transform.Rotate(0f, 0f, -90f); // ➔ Rotation de 90° dans le sens horaire
Debug.Log("🔄 Bloc pivoté de 90° !");
}
}

View File

@ -18,6 +18,11 @@ public class MainMenu : MonoBehaviour
Application.Quit();
}
public void LevelEditor()
{
SceneManager.LoadSceneAsync("LevelEditorScene");
}
public void EditorChoice()
{
SceneManager.LoadSceneAsync("EditorChoiceScene");

View File

@ -6,9 +6,11 @@ public class Player : MonoBehaviour
public Rigidbody2D RigidBody { get; private set; }
public Transform Transform { get; private set; }
public ParticleSystem Particle { get; private set; }
public LevelsLoader LevelsLoader { get; private set; }
public LevelsLoader LevelsLoader { get; set; }
public SpriteRenderer SpriteRenderer { get; private set; }
public bool IsColliding { get; set; } = true;
public bool HasStarted { get; set; } = false;
public bool CanJump { get; set; } = true;
public IGameMode CurrentGameMode { get; set; }
public float SpeedMultiplier = 1f;
@ -19,7 +21,12 @@ public class Player : MonoBehaviour
Transform = transform;
Particle = GetComponentInChildren<ParticleSystem>();
SpriteRenderer = GetComponentInChildren<SpriteRenderer>();
LevelsLoader = GameObject.FindGameObjectWithTag("LevelsLoader").GetComponent<LevelsLoader>();
GameObject loaderObj = GameObject.FindGameObjectWithTag("LevelsLoader");
if (loaderObj != null)
LevelsLoader = loaderObj.GetComponent<LevelsLoader>();
else
Debug.LogWarning("LevelsLoader introuvable : Progression désactivée pour ce niveau.");
}
public void Start()
@ -33,21 +40,24 @@ public class Player : MonoBehaviour
public void Update()
{
CurrentGameMode.Update(this);
LevelsLoader.CalculateCurrentProgressionPercent(transform.position);
if (CurrentGameMode != null)
CurrentGameMode.Update(this);
if (LevelsLoader != null)
LevelsLoader.CalculateCurrentProgressionPercent(transform.position);
}
public void OnCollisionEnter2D(Collision2D collision)
public virtual void OnCollisionEnter2D(Collision2D collision)
{
CurrentGameMode.OnCollisionEnter(this, collision);
CurrentGameMode?.OnCollisionEnter(this, collision);
}
public void OnCollisionExit2D(Collision2D collision)
{
CurrentGameMode.OnCollisionExit(this, collision);
CurrentGameMode?.OnCollisionExit(this, collision);
}
private void OnTriggerEnter2D(Collider2D collision)
public virtual void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("ShipPortal"))
{

View File

@ -8,6 +8,9 @@ public class PlayerCamera : MonoBehaviour
public float smoothSpeed = 5.0f;
private float initialY;
[Header("References")]
public bool isPlaying;
private void Start()
{
initialY = transform.position.y;
@ -15,22 +18,25 @@ public class PlayerCamera : MonoBehaviour
private void Update()
{
Player player = playerObject.GetComponent<Player>();
float minYFollow = normalMinYFollow;
if (player.CurrentGameMode is ShipGameMode)
if (isPlaying)
{
minYFollow = shipMinYFollow;
Player player = playerObject.GetComponent<Player>();
float minYFollow = normalMinYFollow;
if (player.CurrentGameMode is ShipGameMode)
{
minYFollow = shipMinYFollow;
}
float targetY = initialY;
if (playerObject.transform.position.y > minYFollow)
{
targetY = playerObject.transform.position.y;
}
float newY = Mathf.Lerp(transform.position.y, targetY, smoothSpeed * Time.deltaTime);
transform.position = new Vector3(playerObject.transform.position.x, newY, transform.position.z);
}
float targetY = initialY;
if (playerObject.transform.position.y > minYFollow)
{
targetY = playerObject.transform.position.y;
}
float newY = Mathf.Lerp(transform.position.y, targetY, smoothSpeed * Time.deltaTime);
transform.position = new Vector3(playerObject.transform.position.x, newY, transform.position.z);
}
}

View File

@ -0,0 +1,127 @@
using UnityEngine;
public class TestManager : MonoBehaviour
{
[Header("References")]
public IGameMode gameMode;
public Player currentPlayer;
public Transform spawnPoint;
public GameObject editorUI;
public PlayerCamera playerCamera;
private bool isTesting = false;
void Start()
{
if (spawnPoint == null)
{
GameObject spawn = new GameObject("AutoSpawnPoint");
spawn.transform.position = new Vector3(-16, -3, 0f);
spawnPoint = spawn.transform;
}
if (currentPlayer == null)
{
Debug.LogError("[TestManager] Aucun Player assigné !");
}
else
{
gameMode = new NormalGameMode();
currentPlayer.ChangeGameMode(gameMode);
currentPlayer.SpeedMultiplier = 0f;
if (currentPlayer.SpriteRenderer != null)
currentPlayer.SpriteRenderer.enabled = false;
if (currentPlayer.Particle != null)
currentPlayer.Particle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // 🛑 Stop propre
}
if (playerCamera != null)
{
playerCamera.isPlaying = false;
}
}
void Update()
{
if (isTesting && currentPlayer == null)
{
StopTest();
}
}
public void StartOrStop()
{
if (isTesting)
StopTest();
else
StartTest();
}
public void StartTest()
{
if (currentPlayer == null)
{
Debug.LogError("[TestManager] Player manquant pour lancer le test !");
return;
}
if (editorUI != null)
editorUI.SetActive(false);
currentPlayer.transform.position = spawnPoint.position;
currentPlayer.RigidBody.linearVelocity = Vector2.zero;
currentPlayer.SpeedMultiplier = 1f;
currentPlayer.SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/BaseSquare");
currentPlayer.ChangeGameMode(gameMode);
isTesting = true;
if (playerCamera != null)
{
playerCamera.playerObject = currentPlayer.gameObject;
playerCamera.isPlaying = true;
}
if (currentPlayer.SpriteRenderer != null)
currentPlayer.SpriteRenderer.enabled = true;
if (currentPlayer.Particle != null)
currentPlayer.Particle.Play(); // ✅ Démarrer la particule
Debug.Log("[TestManager] Test du niveau démarré !");
}
public void StopTest()
{
if (currentPlayer != null)
{
currentPlayer.transform.position = spawnPoint.position;
currentPlayer.RigidBody.linearVelocity = Vector2.zero;
currentPlayer.RigidBody.angularVelocity = 0f;
currentPlayer.transform.rotation = Quaternion.identity;
currentPlayer.SpriteRenderer.sprite = Resources.Load<Sprite>("Shapes/BaseSquare");
currentPlayer.SpeedMultiplier = 0f;
if (currentPlayer.Particle != null)
currentPlayer.Particle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // ✅ Arrêter proprement
if (currentPlayer.SpriteRenderer != null)
currentPlayer.SpriteRenderer.enabled = false;
}
if (editorUI != null)
editorUI.SetActive(true);
if (playerCamera != null)
{
playerCamera.isPlaying = false;
playerCamera.transform.position = new Vector3(0f, 0f, -10f);
}
isTesting = false;
Debug.Log("[TestManager] Test du niveau arrêté, joueur reset et caméra recentrée !");
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2c6d38f8c53ec314c9827a842e6f17d8

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 KiB

View File

@ -8,11 +8,11 @@ repeat
if (Le joueur percute un obstacle ?) then (Oui)
:Le joueur meurt;
:Augmenter le nombre d'essais de 1;
:Perdre toutes les pièces et bonus;
:Perdre toutes les bonus;
else (Non)
partition "Actions du joueur" {
group Saut
if (L'utilisateur clique sur l'écran ?) then (Oui)
if (L'utilisateur appuie sur espace ?) then (Oui)
:Le joueur effectue un saut;
endif
end group
@ -27,15 +27,15 @@ repeat
end group
endif
if (Le joueur clique sur un bonus dans son inventaire ?) then (Oui)
if (Le joueur percute un bonus ?) then (Oui)
group Activation de Bonus
:Le joueur clique sur un bonus dans l'inventaire;
switch (Type de bonus)
case (Temps ralenti)
:Activer ralentissement du temps;
:Temps ralenti : 3 secondes;
case (Annulation d'obstacle)
:Supprimer tous les obstacles;
:Durée : 3 secondes;
case (Temps accéléré)
:Activer accélération du temps;
:Durée : 3 secondes;
endswitch
end group
@ -54,9 +54,6 @@ repeat
:Quitter le niveau;
:Retour au menu principal;
stop
case (Mode Checkpoint)
:Activer/Désactiver le mode Checkpoint;
:Reprendre la partie;
case (Volume)
:Modifier le volume sonore;
:Revenir au menu de pause;
@ -72,7 +69,6 @@ repeat
if (Le joueur touche la zone de fin ?) then (Oui)
:Terminer le niveau;
:Animation de fin de niveau;
:Conserver les pièces obtenues;
break
endif
}

View File

@ -11,7 +11,7 @@ repeat
:Perdre toutes les pièces et bonus;
else (Non)
partition "Actions du joueur" {
if (L'utilisateur clique sur l'écran ?) then (Oui)
if (L'utilisateur appuie sur espace ?) then (Oui)
:Le joueur effectue un saut;
note right
**Saut**
@ -29,12 +29,12 @@ repeat
end note
endif
if (Le joueur clique sur un bonus dans son inventaire ?) then (Oui)
if (Le joueur percute un bonus ?) then (Oui)
:Activer un bonus;
note right
**Activation de Bonus**
- Déclenche l'effet du bonus sélectionné.
- Exemple : Invincibilité, Double saut.
- Exemple : Ralentissement du temps.
end note
endif
}
@ -44,7 +44,7 @@ repeat
:Arriver sur le menu de pause;
note right
**Menu de pause**
- Affiche les options : Reprendre, Quitter, Mode Checkpoint, Volume.
- Affiche les options : Reprendre, Quitter, Volume.
end note
endif
}
@ -53,12 +53,6 @@ repeat
if (Le joueur touche la zone de fin ?) then (Oui)
:Terminer le niveau;
:Afficher une animation de fin de niveau;
:Conserver les pièces obtenues;
note right
**Fin du Niveau**
- Succès enregistré.
- Les pièces collectées sont sauvegardées.
end note
endif
}
endif

View File

@ -5,9 +5,9 @@ start
switch (Type de bonus)
case (Temps ralenti)
:Activer ralentissement du temps;
:Temps ralenti : 3 secondes;
case (Annulation d'obstacle)
:Supprimer tous les obstacles;
:Durée : 3 secondes;
case (Temps accéléré)
:Activer accélération du temps;
:Durée : 3 secondes;
endswitch
stop

View File

@ -1,7 +1,7 @@
@startuml sub-bonus
start
if (Le joueur touche un bonus ?) then (Oui)
if (Le joueur percute un bonus ?) then (Oui)
:Ajouter le bonus à l'inventaire;
else (Non)
endif

View File

@ -1,10 +0,0 @@
@startuml sub-bumper
start
if (Le joueur touche un bumper ?) then (Oui)
:Effectuer un grand saut;
else (Non)
endif
stop
@enduml

View File

@ -7,10 +7,6 @@ switch (Forme)
:Mode fusée;
case (Forme roue)
:Mode roue;
case (Forme sens inversé)
:Mode sens inversé;
case (Forme gravité inversée)
:Mode gravité inversée;
endswitch
stop

View File

@ -11,13 +11,10 @@ else (Quitter)
:Quitter le niveau;
:Retour au menu principal;
stop
else (Mode Checkpoint)
:Activer/Désactiver le mode Checkpoint;
:Revenir au menu de pause;
else (Volume)
:Modifier le volume sonore;
:Revenir au menu de pause;
else (Menu de sleection de niveau)
else (Menu de sélection de niveau)
:Revenir au menu de selection de niveau;
endif

View File

@ -1,10 +0,0 @@
@startuml sub-pieces
start
if (Le joueur touche une pièce ?) then (Oui)
:Ajouter la pièce au niveau;
else (Non)
endif
stop
@enduml

View File

@ -0,0 +1,303 @@
@startuml Geometry Dash - Diagramme de classe léger
skinparam style strictuml
skinparam classAttributeIconSize 0
skinparam classFontStyle Bold
hide enum methods
class PlayerCamera {
{field} + playerObject: GameObject
{field} + normalMinYFollow: float
{field} + shipMinYFollow: float
{field} + smoothSpeed: float
{field} - initialY: float
{field} + isPlaying: bool
{method} - Start()
{method} - Update()
}
class Player {
{field} + RigidBody: Rigidbody2D {get} {private set}
{field} + Transform: Transform {get} {private set}
{field} + Particle: ParticleSystem {get} {private set}
{field} + LevelsLoader: LevelsLoader {get} {set}
{field} + SpriteRenderer: SpriteRenderer {get} {private set}
{field} + IsColliding: bool {get} {set}
{field} + HasStarted: bool {get} {set}
{field} + CanJump: bool {get} {set}
{field} + CurrentGameMode: IGameMode {get} {set}
{field} + SpeedMultiplier: float
{method} + Awake()
{method} + Start()
{method} + Update()
{method} + OnCollisionEnter2D(collision: Collision2D)
{method} + OnCollisionExit2D(collision: Collision2D)
{method} + OnTriggerEnter2D(collision: Collider2D)
{method} + ChangeGameMode(newMode: IGameMode)
}
Player o--> LevelsLoader : <<References>>
Player o--> IGameMode : <<Has>>
class PauseMenu {
{field} + pauseMenu: GameObject
{field} + pauseButton: GameObject
{field} + levelLoader: LevelLoader
{field} + volumeSlider: Slider
{method} + Start()
{method} + ChangeVolume()
{method} + Pause()
{method} + Home()
{method} + Resume()
}
PauseMenu --> LevelLoader : <<References>>
PauseMenu --> GameObject : <<References>>
PauseMenu --> Slider : <<References>>
class MainMenu {
{method} + LaunchGame()
{method} + OpenImport()
{method} + QuitGame()
{method} + LevelEditor()
{method} + EditorChoice()
{method} + CreateLevel()
}
class LevelStat {
{field} + JsonName: string {get} {set}
{field} + totalJumps: Integer
{field} + totalAttempts: Integer
{field} + progressionPercent: Integer
{method} + {static} CreateFromJSON(jsonString: string): LevelStat
}
class LevelsLoader {
{field} + levels: List<Level>
{field} + levelCurrent: Level
{method} - Start()
{method} - LoadAllLevels()
{method} - SaveLevelCurrent()
{method} + NextLevel()
{method} + PreviousLevel()
{method} + IncreaseTotalJumps()
{method} + IncreaseTotalAttempts()
{method} + CalculateCurrentProgressionPercent(playerPosition: Vector3): Integer
{method} + RefreshLevels()
}
LevelsLoader *--> Level : <<Owns>>
class LevelLoader {
{field} + levelsLoader: LevelsLoader
{field} + audioSource: AudioSource
{field} + progressionText: Text
{field} - groundY: float
{method} - GetPrefab(type: string): GameObject
{method} - LoadAudio()
{method} - LoadElements()
{method} + Start()
{method} + Update()
}
LevelLoader o--> LevelsLoader : <<References>>
LevelLoader *--> AudioSource : <<Owns>>
LevelLoader --> Text : <<References>>
class LevelEditor {
{field} - currentBlock: GameObject
{field} - isPlacingBlock: bool
{field} - currentScale: Vector3
{field} - scaleStep: float
{field} + blockGroupContainer: Transform
{field} + buttonPrefabTemplate: GameObject
{field} - currentPage: Integer
{field} - buttonsPerPage: Integer {constant}
{field} - blockPrefabs: List<GameObject>
{field} - currentButtons: List<GameObject>
{field} - resizingTarget: GameObject
{field} - isResizing: bool
{field} - originalMousePos: Vector3
{field} - originalScale: Vector3
{field} - currentResizeAxis: ResizeAxis
{method} - Start()
{method} - LoadPrefabs()
{method} - GenerateButtons()
{method} - ClearCurrentButtons()
{method} + NextPage()
{method} + PreviousPage()
{method} - SelectPrefab(prefab: GameObject)
{method} - Update()
{method} - PlaceBlock()
{method} - TrySnapToNearbyBlock()
{method} - InstantiateAndPrepare(prefab: GameObject, scaleOverride: Vector3?)
{method} - HandleBlockRotation()
}
LevelEditor o--> blockGroupContainer : <<References>>
LevelEditor --> buttonPrefabTemplate : <<References>>
LevelEditor *--> blockPrefabs : <<Owns>>
LevelEditor *--> currentButtons : <<Owns>>
LevelEditor o--> currentBlock : <<References>>
LevelEditor o--> resizingTarget : <<References>>
LevelEditor *--> ResizeAxis : <<Has>>
enum ResizeAxis <<enumeration>> {
None
Horizontal
Vertical
}
class LevelElement {
{field} + type: string
{field} + x: float
{field} + y: float
{field} + scaleX: float
{field} + scaleY: float
}
class Level {
{field} + {static} {readOnly} LAST_X: Integer = 15
{field} + JsonName: string {get} {set}
{field} + TotalJumps: Integer {get} {set}
{field} + TotalAttempts: Integer {get} {set}
{field} + ProgressionPercent: Integer {get} {set}
{field} + ProgressionPercentMax: Integer {get} {set}
{field} + name: string
{field} + musicName: string
{field} + order: Integer
{field} + elements: List<LevelElement>
{field} + LastX: float {get}
{method} + {static} CreateFromJSON(jsonString: string): Level
}
Level *--> LevelElement : <<Composed of>>
class JSONImporter {
{field} + statusText: TMP_Text
{method} - Awake()
{method} - Start()
{method} + ImportJSON()
{method} - ShowFileBrowser(): IEnumerator
{method} - UpdateStatus(message: string, color: Color)
}
JSONImporter --> TMP_Text : <<References>>
class JSONExporter {
{field} + statusText: TMP_Text
{field} - editor: LevelEditor
{field} - levelsFolder: string
{method} - Awake()
{method} - Start()
{method} + ExportJSON()
{method} - ShowSaveDialog(): IEnumerator
{method} - SetStatus(message: string, color: Color)
}
JSONExporter o--> LevelEditor : <<References>>
JSONExporter --> TMP_Text : <<References>>
class SerializableElement <<private>> {
{field} + type: string
{field} + x: float
{field} + y: float
{field} + scaleX: float
{field} + scaleY: float
}
JSONExporter ..> SerializableElement : <<Uses>>
class LevelData <<private>> {
{field} + name: string
{field} + musicName: string
{field} + order: Integer
{field} + elements: SerializableElement[*]
}
LevelData *--> SerializableElement : <<Composed of>>
JSONExporter ..> LevelData : <<Uses>>
interface IGameMode <<interface>> {
{method} + Update(player: Player) {abstract}
{method} + OnCollisionEnter(player: Player, collision: Collision2D) {abstract}
{method} + OnCollisionExit(player: Player, collision: Collision2D) {abstract}
}
class ShipGameMode implements IGameMode {
{field} - HorizontalSpeed: float {constant}
{field} - JumpForce: float {constant}
{field} - JumpKey: KeyCode {constant}
{field} - UpperAngle: float {constant}
{field} - LowerAngle: float {constant}
{field} - RotationTransitionDuration: float {constant}
{method} + Update(player: Player)
{method} - GetCurrentZAngle(player: Player): float
{method} - Jump(player: Player)
{method} + OnCollisionEnter(player: Player, collision: Collision2D)
{method} + OnCollisionExit(player: Player, collision: Collision2D)
}
ShipGameMode ..|> IGameMode
ShipGameMode ..> Player : <<Uses>>
class NormalGameMode implements IGameMode {
{field} - HorizontalSpeed: float {constant}
{field} - JumpForce: float {constant}
{field} - JumpKey: KeyCode {constant}
{field} - isRotating: bool
{field} - targetRotationAngle: float
{field} - rotationSpeed: float {readOnly}
{method} + Update(player: Player)
{method} - Jump(player: Player)
{method} - PerformRotation(player: Player)
{method} - IsJumping(player: Player): bool
{method} - AlignRotation(player: Player)
{method} - UpdateParticlePositionAndRotation(player: Player)
{method} + OnCollisionEnter(player: Player, collision: Collision2D)
{method} + OnCollisionExit(player: Player, collision: Collision2D)
}
NormalGameMode ..|> IGameMode
NormalGameMode ..> Player : <<Uses>>
' Tags
class ShipPortal { }
class CubePortal { }
class BonusBoostSpeed { }
class BonusSlowSpeed { }
class Kill { }
class Win { }
class Ground { }
class WinnerWall { }
class Block { }
class SmallObstacle { }
class Spike { }
class Portal { }
class Bonus { }
Player ..> ShipPortal : <<Uses Tag>>
Player ..> CubePortal : <<Uses Tag>>
Player ..> BonusBoostSpeed : <<Uses Tag>>
Player ..> BonusSlowSpeed : <<Uses Tag>>
ShipGameMode ..> Kill : <<Uses Tag>>
ShipGameMode ..> Win : <<Uses Tag>>
ShipGameMode ..> Ground : <<Uses Tag>>
NormalGameMode ..> Kill : <<Uses Tag>>
NormalGameMode ..> Win : <<Uses Tag>>
LevelLoader ..> WinnerWall : <<Uses Prefab>>
LevelLoader ..> Ground : <<Uses Prefab>>
LevelEditor ..> Ground : <<Uses Tag>>
LevelEditor ..> Block : <<Uses Tag>>
LevelEditor ..> SmallObstacle : <<Uses Tag>>
LevelEditor ..> Spike : <<Uses Tag>>
LevelEditor ..> Portal : <<Uses Tag>>
LevelEditor ..> Bonus : <<Uses Tag>>
LevelsLoader ..> WinnerWall : <<Uses Prefab>>
@enduml

View File

@ -5,107 +5,581 @@ skinparam classAttributeIconSize 0
skinparam classFontStyle Bold
hide enum methods
class GameManager {
- score: int
- isPaused: Boolean
+ StartGame()
+ RestartLevel()
+ PauseGame()
+ ResumeGame()
+ UpdateScore(points: int)
package UnityEngine {
class GameObject {
}
class MonoBehaviour {
}
class Rigidbody2D {
}
class Transform {
}
class ParticleSystem {
}
class SpriteRenderer {
}
class Collider2D {
}
class Collision2D {
}
class Vector3 {
}
class Vector2 {
}
class Quaternion {
}
class Mathf {
{method} + Lerp(...) {static}
{method} + Round(...) {static}
{method} + Clamp(...) {static}
{method} + Sin(...) {static}
{method} + Abs(...) {static}
{method} + DeltaAngle(...) {static}
{method} + MoveTowardsAngle(...) {static}
{method} + RoundToInt(...) {static}
}
class Time {
{method} + deltaTime: float {static} {readOnly}
{method} + timeScale: float {static}
}
class Input {
{method} + GetKey(...) {static}
{method} + GetKeyDown(...) {static}
{method} + GetMouseButtonDown(...) {static}
{method} + GetMouseButtonUp(...) {static}
{method} + GetAxis(...) {static}
}
class Camera {
{method} + main: Camera {static} {readOnly}
{method} + ScreenToWorldPoint(...)
}
class Physics2D {
{method} + OverlapBoxAll(...) {static}
{method} + RaycastAll(...) {static}
{method} + OverlapPoint(...) {static}
{method} + OverlapAreaAll(...) {static}
}
class Resources {
{method} + Load<T>(...) {static}
{method} + LoadAll<T>(...) {static}
}
class PlayerPrefs {
{method} + GetFloat(...) {static}
{method} + SetFloat(...) {static}
{method} + HasKey(...) {static}
}
class AudioSource {
{method} + volume: float
{method} + clip: AudioClip
{method} + Play()
{method} + Pause()
}
class AudioClip {
}
class Slider {
}
class Button {
}
class Image {
}
class RectTransform {
}
class Bounds {
}
class Debug {
{method} + Log(...) {static}
{method} + LogWarning(...) {static}
{method} + LogError(...) {static}
}
class Application {
{method} + persistentDataPath: string {static} {readOnly}
{method} + dataPath: string {static} {readOnly}
{method} + Quit() {static}
}
class Canvas {
{method} + ForceUpdateCanvases() {static}
}
class UnityEngine.Object {
{method} + FindAnyObjectByType<T>(...) {static}
}
class Renderer {
}
}
package UnityEngine.SceneManagement {
class SceneManager {
{method} + LoadScene(...) {static}
{method} + LoadSceneAsync(...) {static}
{method} + GetActiveScene(...) {static}
}
}
package UnityEngine.UI {
class Text {
}
}
package TMPro {
class TMP_Text {
}
}
package System {
package IO {
class Path {
{method} + Combine(...) {static}
{method} + GetFileNameWithoutExtension(...) {static}
}
class File {
{method} + Exists(...) {static}
{method} + ReadAllText(...) {static}
{method} + WriteAllText(...) {static}
{method} + Copy(...) {static}
{method} + GetExtension(...) {static}
{method} + GetFileName(...) {static}
{method} + GetFileNameWithoutExtension(...) {static}
}
class Directory {
{method} + Exists(...) {static}
{method} + CreateDirectory(...) {static}
}
}
package Collections {
class IEnumerator {
}
package Generic {
class List<T> {
}
}
}
}
package SimpleFileBrowser {
class FileBrowser {
{method} + WaitForLoadDialog(...) {static}
{method} + WaitForSaveDialog(...) {static}
{method} + Success: bool {static} {readOnly}
{method} + Result: string[*] {static} {readOnly}
{method} + PickMode: enumeration {static} {readOnly}
}
}
class PlayerCamera {
{field} + playerObject: UnityEngine.GameObject
{field} + normalMinYFollow: float
{field} + shipMinYFollow: float
{field} + smoothSpeed: float
{field} - initialY: float
{field} + isPlaying: bool
{method} - Start()
{method} - Update()
}
PlayerCamera --|> UnityEngine.MonoBehaviour
PlayerCamera --> UnityEngine.GameObject : <<References>>
PlayerCamera ..> UnityEngine.Mathf : <<Uses>>
PlayerCamera ..> UnityEngine.Time : <<Uses>>
PlayerCamera ..> Player : <<Uses>>
PlayerCamera ..> ShipGameMode : <<Uses>>
PlayerCamera ..> UnityEngine.Transform : <<Uses>>
PlayerCamera ..> UnityEngine.Vector3 : <<Uses>>
class Player {
{field} + RigidBody: UnityEngine.Rigidbody2D {get} {private set}
{field} + Transform: UnityEngine.Transform {get} {private set}
{field} + Particle: UnityEngine.ParticleSystem {get} {private set}
{field} + LevelsLoader: LevelsLoader {get} {set}
{field} + SpriteRenderer: UnityEngine.SpriteRenderer {get} {private set}
{field} + IsColliding: bool {get} {set}
{field} + HasStarted: bool {get} {set}
{field} + CanJump: bool {get} {set}
{field} + CurrentGameMode: IGameMode {get} {set}
{field} + SpeedMultiplier: float
{method} + Awake()
{method} + Start()
{method} + Update()
{method} + OnCollisionEnter2D(collision: UnityEngine.Collision2D)
{method} + OnCollisionExit2D(collision: UnityEngine.Collision2D)
{method} + OnTriggerEnter2D(collision: UnityEngine.Collider2D)
{method} + ChangeGameMode(newMode: IGameMode)
}
Player --|> UnityEngine.MonoBehaviour
Player *--> UnityEngine.Rigidbody2D : <<Owns>>
Player *--> UnityEngine.Transform : <<Owns>>
Player *--> UnityEngine.ParticleSystem : <<Owns>>
Player o--> LevelsLoader : <<References>>
Player *--> UnityEngine.SpriteRenderer : <<Owns>>
Player o--> IGameMode : <<Has>>
Player ..> UnityEngine.Resources : <<Uses>>
Player ..> UnityEngine.Debug : <<Uses>>
Player ..> ShipPortal : <<Uses>>
Player ..> CubePortal : <<Uses>>
Player ..> BonusBoostSpeed : <<Uses>>
Player ..> BonusSlowSpeed : <<Uses>>
Player ..> NormalGameMode : <<Uses>>
Player ..> UnityEngine.Collision2D : <<Uses>>
Player ..> UnityEngine.Collider2D : <<Uses>>
class PauseMenu {
{field} + pauseMenu: UnityEngine.GameObject
{field} + pauseButton: UnityEngine.GameObject
{field} + levelLoader: LevelLoader
{field} + volumeSlider: UnityEngine.UI.Slider
{method} + Start()
{method} + ChangeVolume()
{method} + Pause()
{method} + Home()
{method} + Resume()
}
PauseMenu --|> UnityEngine.MonoBehaviour
PauseMenu --> UnityEngine.GameObject : <<References>>
PauseMenu --> LevelLoader : <<References>>
PauseMenu --> UnityEngine.UI.Slider : <<References>>
PauseMenu ..> UnityEngine.PlayerPrefs : <<Uses>>
PauseMenu ..> UnityEngine.Time : <<Uses>>
PauseMenu ..> UnityEngine.SceneManagement.SceneManager : <<Uses>>
class MainMenu {
{method} + LaunchGame()
{method} + OpenImport()
{method} + QuitGame()
{method} + LevelEditor()
{method} + EditorChoice()
{method} + CreateLevel()
}
MainMenu --|> UnityEngine.MonoBehaviour
MainMenu ..> UnityEngine.SceneManagement.SceneManager : <<Uses>>
MainMenu ..> UnityEngine.Application : <<Uses>>
class LevelStat {
{field} + JsonName: string {get} {set}
{field} + totalJumps: Integer
{field} + totalAttempts: Integer
{field} + progressionPercent: Integer
{method} + {static} CreateFromJSON(jsonString: string): LevelStat
}
LevelStat ..> UnityEngine.JsonUtility : <<Uses>>
class LevelsLoader {
{field} + levels: System.Collections.Generic.List<Level>
{field} + levelCurrent: Level
{method} - Start()
{method} - LoadAllLevels()
{method} - SaveLevelCurrent()
{method} + NextLevel()
{method} + PreviousLevel()
{method} + IncreaseTotalJumps()
{method} + IncreaseTotalAttempts()
{method} + CalculateCurrentProgressionPercent(playerPosition: UnityEngine.Vector3): Integer
{method} + RefreshLevels()
}
LevelsLoader --|> UnityEngine.MonoBehaviour
LevelsLoader *--> Level : <<Owns>>
LevelsLoader ..> UnityEngine.TextAsset : <<Uses>>
LevelsLoader ..> UnityEngine.Resources : <<Uses>>
LevelsLoader ..> System.IO.Path : <<Uses>>
LevelsLoader ..> UnityEngine.Application : <<Uses>>
LevelsLoader ..> System.IO.File : <<Uses>>
LevelsLoader ..> UnityEngine.JsonUtility : <<Uses>>
LevelsLoader ..> LevelStat : <<Uses>>
LevelsLoader ..> UnityEngine.Vector3 : <<Uses>>
LevelsLoader ..> UnityEngine.Mathf : <<Uses>>
LevelsLoader ..> WinnerWall : <<Uses>>
LevelsLoader ..> UnityEngine.GameObject : <<Uses>>
LevelsLoader ..> UnityEngine.Renderer : <<Uses>>
LevelsLoader ..> System.Collections.Generic.List : <<Uses>>
class LevelLoader {
{field} + levelsLoader: LevelsLoader
{field} + audioSource: UnityEngine.AudioSource
{field} + progressionText: UnityEngine.UI.Text
{field} - groundY: float
{method} - GetPrefab(type: string): UnityEngine.GameObject
{method} - LoadAudio()
{method} - LoadElements()
{method} + Start()
{method} + Update()
}
LevelLoader --|> UnityEngine.MonoBehaviour
LevelLoader o--> LevelsLoader : <<References>>
LevelLoader *--> UnityEngine.AudioSource : <<Owns>>
LevelLoader --> UnityEngine.UI.Text : <<References>>
LevelLoader ..> System.IO.Path : <<Uses>>
LevelLoader ..> UnityEngine.Resources : <<Uses>>
LevelLoader ..> UnityEngine.PlayerPrefs : <<Uses>>
LevelLoader ..> UnityEngine.GameObject : <<Uses>>
LevelLoader ..> UnityEngine.Vector3 : <<Uses>>
LevelLoader ..> UnityEngine.Quaternion : <<Uses>>
LevelLoader ..> UnityEngine.Transform : <<Uses>>
LevelLoader ..> WinnerWall : <<Uses>>
LevelLoader ..> Level : <<Uses>>
LevelLoader ..> Ground : <<Uses>>
LevelLoader ..> UnityEngine.AudioClip : <<Uses>>
LevelLoader ..> UnityEngine.Renderer : <<Uses>>
class LevelEditor {
{field} - currentBlock: UnityEngine.GameObject
{field} - isPlacingBlock: bool
{field} - currentScale: UnityEngine.Vector3
{field} - scaleStep: float
{field} + blockGroupContainer: UnityEngine.Transform
{field} + buttonPrefabTemplate: UnityEngine.GameObject
{field} - currentPage: Integer
{field} - buttonsPerPage: Integer {constant}
{field} - blockPrefabs: System.Collections.Generic.List<UnityEngine.GameObject>
{field} - currentButtons: System.Collections.Generic.List<UnityEngine.GameObject>
{field} - resizingTarget: UnityEngine.GameObject
{field} - isResizing: bool
{field} - originalMousePos: UnityEngine.Vector3
{field} - originalScale: UnityEngine.Vector3
{field} - currentResizeAxis: ResizeAxis
{method} - Start()
{method} - LoadPrefabs()
{method} - GenerateButtons()
{method} - ClearCurrentButtons()
{method} + NextPage()
{method} + PreviousPage()
{method} - SelectPrefab(prefab: UnityEngine.GameObject)
{method} - Update()
{method} - PlaceBlock()
{method} - TrySnapToNearbyBlock()
{method} - InstantiateAndPrepare(prefab: UnityEngine.GameObject, scaleOverride: UnityEngine.Vector3?)
{method} - HandleBlockRotation()
}
LevelEditor --|> UnityEngine.MonoBehaviour
LevelEditor o--> blockGroupContainer : <<References>>
LevelEditor --> buttonPrefabTemplate : <<References>>
LevelEditor *--> blockPrefabs : <<Owns>>
LevelEditor *--> currentButtons : <<Owns>>
LevelEditor o--> currentBlock : <<References>>
LevelEditor o--> resizingTarget : <<References>>
LevelEditor ..> UnityEngine.GameObject : <<Uses>>
LevelEditor ..> UnityEngine.Vector3 : <<Uses>>
LevelEditor ..> UnityEngine.Mathf : <<Uses>>
LevelEditor ..> UnityEngine.Input : <<Uses>>
LevelEditor ..> UnityEngine.Camera : <<Uses>>
LevelEditor ..> UnityEngine.Physics2D : <<Uses>>
LevelEditor ..> UnityEngine.Collider2D : <<Uses>>
LevelEditor ..> UnityEngine.Bounds : <<Uses>>
LevelEditor ..> UnityEngine.SpriteRenderer : <<Uses>>
LevelEditor ..> UnityEngine.UI.Button : <<Uses>>
LevelEditor ..> UnityEngine.UI.Image : <<Uses>>
LevelEditor ..> UnityEngine.RectTransform : <<Uses>>
LevelEditor ..> UnityEngine.Debug : <<Uses>>
LevelEditor ..> System.Collections.Generic.List : <<Uses>>
LevelEditor ..> UnityEngine.Resources : <<Uses>>
LevelEditor ..> Player : <<Uses>> ' (via tags)
LevelEditor ..> Ground : <<Uses>> ' (via tags)
LevelEditor ..> Portal : <<Uses>> ' (via tags)
LevelEditor ..> SmallObstacle : <<Uses>> ' (via tags)
LevelEditor ..> Spike : <<Uses>> ' (via tags)
LevelEditor ..> Block : <<Uses>> ' (via tags)
LevelEditor ..> Bonus : <<Uses>> ' (via tags)
enum ResizeAxis <<enumeration>> {
None
Horizontal
Vertical
}
LevelEditor *--> ResizeAxis : <<Has>>
class LevelElement {
{field} + type: string
{field} + x: float
{field} + y: float
{field} + scaleX: float
{field} + scaleY: float
}
class Level {
- name: String
- musicName: String
+ StartLevel()
+ EndLevel()
+ CheckCompletion(): Boolean
{field} + {static} {readOnly} LAST_X: Integer = 15
{field} + JsonName: string {get} {set}
{field} + TotalJumps: Integer {get} {set}
{field} + TotalAttempts: Integer {get} {set}
{field} + ProgressionPercent: Integer {get} {set}
{field} + ProgressionPercentMax: Integer {get} {set}
{field} + name: string
{field} + musicName: string
{field} + order: Integer
{field} + elements: System.Collections.Generic.List<LevelElement>
{field} + LastX: float {get}
{method} + {static} CreateFromJSON(jsonString: string): Level
}
Level *--> LevelElement : <<Composed of>>
Level ..> UnityEngine.JsonUtility : <<Uses>>
Level ..> System.Collections.Generic.List : <<Uses>>
class JSONImporter {
{field} + statusText: TMPro.TMP_Text
{method} - Awake()
{method} - Start()
{method} + ImportJSON()
{method} - ShowFileBrowser(): System.Collections.IEnumerator
{method} - UpdateStatus(message: string, color: UnityEngine.Color)
}
JSONImporter --|> UnityEngine.MonoBehaviour
JSONImporter --> TMPro.TMP_Text : <<References>>
JSONImporter ..> System.Collections.IEnumerator : <<Uses>>
JSONImporter ..> SimpleFileBrowser.FileBrowser : <<Uses>>
JSONImporter ..> System.IO.Path : <<Uses>>
JSONImporter ..> System.IO.File : <<Uses>>
JSONImporter ..> UnityEngine.Color : <<Uses>>
JSONImporter ..> LevelsLoader : <<Uses>>
JSONImporter ..> UnityEngine.Object : <<Uses>>
JSONImporter ..> UnityEngine.Canvas : <<Uses>>
JSONImporter ..> UnityEngine.SceneManagement.SceneManager : <<Uses>>
class JSONExporter {
{field} + statusText: TMPro.TMP_Text
{field} - editor: LevelEditor
{field} - levelsFolder: string
{method} - Awake()
{method} - Start()
{method} + ExportJSON()
{method} - ShowSaveDialog(): System.Collections.IEnumerator
{method} - SetStatus(message: string, color: UnityEngine.Color)
}
JSONExporter --|> UnityEngine.MonoBehaviour
JSONExporter o--> LevelEditor : <<References>>
JSONExporter --> TMPro.TMP_Text : <<References>>
JSONExporter ..> System.Collections.IEnumerator : <<Uses>>
JSONExporter ..> System.Collections.Generic.List : <<Uses>>
JSONExporter ..> SimpleFileBrowser.FileBrowser : <<Uses>>
JSONExporter ..> System.IO.Path : <<Uses>>
JSONExporter ..> System.IO.File : <<Uses>>
JSONExporter ..> System.IO.Directory : <<Uses>>
JSONExporter ..> UnityEngine.Collider2D : <<Uses>>
JSONExporter ..> UnityEngine.Object : <<Uses>>
JSONExporter ..> UnityEngine.Vector3 : <<Uses>>
JSONExporter ..> UnityEngine.Mathf : <<Uses>>
JSONExporter ..> UnityEngine.JsonUtility : <<Uses>>
JSONExporter ..> UnityEngine.Color : <<Uses>>
JSONExporter ..> LevelsLoader : <<Uses>>
JSONExporter ..> UnityEngine.Canvas : <<Uses>>
class SerializableElement <<private>> {
{field} + type: string
{field} + x: float
{field} + y: float
{field} + scaleX: float
{field} + scaleY: float
}
JSONExporter ..> SerializableElement : <<Uses>>
class LevelData <<private>> {
{field} + name: string
{field} + musicName: string
{field} + order: Integer
{field} + elements: SerializableElement[*]
}
LevelData *--> SerializableElement : <<Composed of>>
JSONExporter ..> LevelData : <<Uses>>
interface IGameMode <<interface>> {
{method} + Update(player: Player) {abstract}
{method} + OnCollisionEnter(player: Player, collision: UnityEngine.Collision2D) {abstract}
{method} + OnCollisionExit(player: Player, collision: UnityEngine.Collision2D) {abstract}
}
abstract class LevelElement {
- x: Float
- y: Float
}
class ShipGameMode implements IGameMode {
{field} - HorizontalSpeed: float {constant}
{field} - JumpForce: float {constant}
{field} - JumpKey: UnityEngine.KeyCode {constant}
{field} - UpperAngle: float {constant}
{field} - LowerAngle: float {constant}
{field} - RotationTransitionDuration: float {constant}
abstract class Obstacle extends LevelElement {
+ TriggerEffect(player: Player)
{method} + Update(player: Player)
{method} - GetCurrentZAngle(player: Player): float
{method} - Jump(player: Player)
{method} + OnCollisionEnter(player: Player, collision: UnityEngine.Collision2D)
{method} + OnCollisionExit(player: Player, collision: UnityEngine.Collision2D)
}
ShipGameMode ..|> IGameMode
ShipGameMode ..> Player : <<Uses>>
ShipGameMode ..> UnityEngine.Vector2 : <<Uses>>
ShipGameMode ..> UnityEngine.Input : <<Uses>>
ShipGameMode ..> UnityEngine.KeyCode : <<Uses>>
ShipGameMode ..> UnityEngine.Mathf : <<Uses>>
ShipGameMode ..> UnityEngine.Time : <<Uses>>
ShipGameMode ..> UnityEngine.Quaternion : <<Uses>>
ShipGameMode ..> UnityEngine.SceneManagement.SceneManager : <<Uses>>
ShipGameMode ..> UnityEngine.Collision2D : <<Uses>>
ShipGameMode ..> LevelsLoader : <<Uses>>
class Platform extends LevelElement {
class NormalGameMode implements IGameMode {
{field} - HorizontalSpeed: float {constant}
{field} - JumpForce: float {constant}
{field} - JumpKey: UnityEngine.KeyCode {constant}
{field} - isRotating: bool
{field} - targetRotationAngle: float
{field} - rotationSpeed: float {readOnly}
{method} + Update(player: Player)
{method} - Jump(player: Player)
{method} - PerformRotation(player: Player)
{method} - IsJumping(player: Player): bool
{method} - AlignRotation(player: Player)
{method} - UpdateParticlePositionAndRotation(player: Player)
{method} + OnCollisionEnter(player: Player, collision: UnityEngine.Collision2D)
{method} + OnCollisionExit(player: Player, collision: UnityEngine.Collision2D)
}
NormalGameMode ..|> IGameMode
NormalGameMode ..> Player : <<Uses>>
NormalGameMode ..> UnityEngine.Vector2 : <<Uses>>
NormalGameMode ..> UnityEngine.Input : <<Uses>>
NormalGameMode ..> UnityEngine.KeyCode : <<Uses>>
NormalGameMode ..> UnityEngine.Mathf : <<Uses>>
NormalGameMode ..> UnityEngine.Time : <<Uses>>
NormalGameMode ..> UnityEngine.Quaternion : <<Uses>>
NormalGameMode ..> UnityEngine.SceneManagement.SceneManager : <<Uses>>
NormalGameMode ..> UnityEngine.Collision2D : <<Uses>>
NormalGameMode ..> LevelsLoader : <<Uses>>
NormalGameMode ..> UnityEngine.Vector3 : <<Uses>>
class Spike extends Obstacle {
+ TriggerEffect(player: Player)
}
class Portal extends LevelElement {
- destination: Vector2
- type: PortalType
+ Teleport(player: Player)
}
' Tags
class ShipPortal { }
class CubePortal { }
class BonusBoostSpeed { }
class BonusSlowSpeed { }
class Kill { }
class Win { }
class Ground { }
class WinnerWall { }
class Block { }
class SmallObstacle { }
class Spike { }
class Portal { }
class Bonus { }
enum PortalType {
Normal, Gravity, Speed
}
class Bumper extends LevelElement {
- bounceForce: Float
+ Bounce(player: Player)
}
class LevelEnd extends LevelElement {
+ TriggerEndGame()
}
class Collectible extends LevelElement {
- points: int
+ Collect(player: Player)
}
class Player {
- JUMP_FORCE: static const Float = 26.6581
- SPEED: static const Float = 8.6
- rigidBody: Rigidbody2D
- isColliding: Boolean
- isGrounded: Boolean
- gravityScale: Float
+ Jump()
+ IsJumping(): Boolean
+ OnCollisionEnter2D(collision: Collision2D)
+ OnCollisionExit2D(collision: Collision2D)
+ ChangeGravity()
+ Die()
+ Respawn()
+ CollectItem(item: Collectible)
}
class LevelLoader {
+ LoadLevel(jsonPath: String): Level
+ UnloadLevel(level: Level)
}
class LevelParser {
+ ParseJSON(jsonData: String): Level
}
class MusicManager {
- currentTrack: String
- volume: Float
+ PlayMusic(musicPath: String)
+ StopMusic()
+ SetVolume(level: Float)
}
GameManager *--> Level : <<Manages>>
GameManager --> LevelLoader : <<Uses>>
LevelLoader --> LevelParser : <<Uses>>
Level *--> LevelElement : <<Contains>>
GameManager *--> Player : <<Owns>>
GameManager --> MusicManager : <<Controls>>
Player --> LevelEnd : <<Triggers>>
Player --> Portal : <<Interacts>>
Player --> Collectible : <<Collects>>
Obstacle --> Player : <<Affects>>
Player ..> ShipPortal : <<Uses Tag>>
Player ..> CubePortal : <<Uses Tag>>
Player ..> BonusBoostSpeed : <<Uses Tag>>
Player ..> BonusSlowSpeed : <<Uses Tag>>
ShipGameMode ..> Kill : <<Uses Tag>>
ShipGameMode ..> Win : <<Uses Tag>>
ShipGameMode ..> Ground : <<Uses Tag>>
NormalGameMode ..> Kill : <<Uses Tag>>
NormalGameMode ..> Win : <<Uses Tag>>
LevelLoader ..> WinnerWall : <<Uses Prefab>>
LevelLoader ..> Ground : <<Uses Prefab>>
LevelEditor ..> Ground : <<Uses Tag>>
LevelEditor ..> Block : <<Uses Tag>>
LevelEditor ..> SmallObstacle : <<Uses Tag>>
LevelEditor ..> Spike : <<Uses Tag>>
LevelEditor ..> Portal : <<Uses Tag>>
LevelEditor ..> Bonus : <<Uses Tag>>
LevelsLoader ..> WinnerWall : <<Uses Prefab>>
@enduml

View File

@ -1,4 +1,4 @@
@startuml Geometry Dash - Diagramme d'etats du joueur
@startuml Geometry Dash - Diagramme d'états du joueur
start
@ -16,12 +16,6 @@ repeat
switch (Forme)
case (Forme fusée)
:Mode fusée;
case (Forme roue)
:Mode roue;
case (Forme sens inversé)
:Mode sens inversé;
case (Forme gravité inversée)
:Mode gravité inversée;
endswitch
if (Le joueur percute un obstacle ?) then (Oui)
:Le joueur meurt;
@ -38,8 +32,8 @@ repeat
:Le joueur meurt;
stop
endif
case (Annulation d'obstacle)
:Supprimer tous les obstacles;
case (Temps accéléré)
:Activer accélération du temps;
:Durée : 3 secondes;
endswitch
}

View File

@ -4,100 +4,64 @@ left to right direction
actor Joueur as Joueur
rectangle "Menu principal" {
usecase "Configurer les paramètres" as ConfigurerParametres
usecase "Accéder à la sélection des niveaux" as SelectionNiveau
usecase "Accéder à l'éditeur de cartes" as EditeurCartes
usecase "Consulter les statistiques" as ConsulterStats
usecase "Voir les trophées et récompenses" as VoirTrophees
usecase "Accéder à l'éditeur de niveau" as EditeurNiveau
usecase "Importer un niveau" as ImporterNiveau
usecase "Quitter le jeu" as QuitterJeu
}
rectangle "Sélection des niveaux" {
usecase "Parcourir les niveaux disponibles" as ParcourirNiveaux
usecase "Afficher les détails d'un niveau" as AfficherDetailsNiveau
usecase "Parcourir les niveaux disponibles (passer au niveau suivant ou précédent)" as ParcourirNiveaux
usecase "Afficher les statistiques d'un niveau (nombre d'essais, nombre de sauts et progression record)" as AfficherDetailsNiveau
usecase "Commencer un niveau" as CommencerNiveau
}
rectangle "Pendant un niveau" {
usecase "Sauter" as Sauter
usecase "Appuyer sur la barre d'espace" as Sauter
usecase "Voir la progression dans le niveau" as VoirProgression
usecase "Mettre le jeu en pause" as MettrePause
}
rectangle "Paramètres" {
usecase "Régler le volume du jeu" as ReglerVolume
usecase "Changer la musique de fond" as ChangerMusique
usecase "Lire les instructions d'aide" as LireAide
}
rectangle "Création de cartes" {
usecase "Créer une nouvelle carte" as CreerNouvelleCarte
usecase "Modifier une carte existante" as ModifierCarte
usecase "Tester une carte" as TesterCarte
usecase "Partager une carte" as PartagerCarte
usecase "Télécharger des cartes de la communauté" as TelechargerCartes
}
rectangle "Statistiques" {
usecase "Afficher les statistiques globales" as AfficherStatsGlobales
usecase "Afficher les statistiques de session" as AfficherStatsSession
}
rectangle "Trophées et récompenses" {
usecase "Voir les trophées débloqués" as VoirTropheesDebloques
usecase "Voir les pièces collectées" as VoirPieces
}
rectangle "Menu Pause" {
usecase "Continuer" as Continuer
usecase "Redémarrer la partie" as RedemarrerPartie
usecase "Retour à la sélection des maps" as RetourSelection
usecase "Revenir au menu principal" as RevenirMenuPrincipal
usecase "Régler le volume de la musique" as ReglerVolumeMusique
usecase "Voir le nombre d'essais, nombre de sauts et progression record" as StatistiquesNiveau
}
Joueur --> ConfigurerParametres : <<extends>>
Joueur --> SelectionNiveau : <<extends>>
Joueur --> EditeurCartes : <<extends>>
Joueur --> ConsulterStats : <<extends>>
Joueur --> VoirTrophees : <<extends>>
rectangle "Création de niveaux" {
usecase "Créer un nouveau niveau" as CreerNouveauNiveau
usecase "Modifier un niveau existante" as ModifierNiveau
usecase "Tester un niveau" as TesterNiveau
usecase "Exporter un niveau" as ExporterNiveau
}
SelectionNiveau --> ParcourirNiveaux : <<include>>
ParcourirNiveaux --> AfficherDetailsNiveau : <<extends>>
ParcourirNiveaux --> CommencerNiveau : <<extends>>
Joueur --> SelectionNiveau : <<extends>>
Joueur --> EditeurNiveau : <<extends>>
Joueur --> ImporterNiveau : <<extends>>
Joueur --> QuitterJeu : <<extends>>
SelectionNiveau --> AfficherDetailsNiveau : <<include>>
SelectionNiveau --> ParcourirNiveaux : <<extends>>
SelectionNiveau --> CommencerNiveau : <<extends>>
CommencerNiveau --> Sauter : <<extends>>
CommencerNiveau --> VoirProgression : <<include>>
CommencerNiveau --> MettrePause : <<extends>>
ConfigurerParametres --> ReglerVolume : <<extends>>
ConfigurerParametres --> ChangerMusique : <<extends>>
ConfigurerParametres --> LireAide : <<extends>>
EditeurCartes --> CreerNouvelleCarte : <<extends>>
EditeurCartes --> ModifierCarte : <<extends>>
EditeurCartes --> TelechargerCartes : <<extends>>
CreerNouvelleCarte --> TesterCarte : <<extends>>
ModifierCarte --> TesterCarte : <<extends>>
CreerNouvelleCarte --> PartagerCarte : <<extends>>
ModifierCarte --> PartagerCarte : <<extends>>
TesterCarte--> Sauter : <<extends>>
TesterCarte--> MettrePause : <<extends>>
ConsulterStats --> AfficherStatsGlobales : <<extends>>
ConsulterStats --> AfficherStatsSession : <<extends>>
VoirTrophees --> VoirTropheesDebloques : <<extends>>
VoirTrophees --> VoirPieces : <<extends>>
MettrePause --> Continuer : <<extends>>
MettrePause --> RedemarrerPartie : <<extends>>
MettrePause --> RetourSelection : <<extends>>
MettrePause --> RevenirMenuPrincipal : <<extends>>
MettrePause --> ReglerVolumeMusique : <<extends>>
MettrePause --> StatistiquesNiveau : <<include>>
RedemarrerPartie --> Sauter : <<extends>>
RedemarrerPartie --> MettrePause : <<extends>>
EditeurNiveau --> CreerNouveauNiveau : <<extends>>
EditeurNiveau --> ModifierNiveau : <<extends>>
Continuer --> Sauter : <<extends>>
Continuer --> MettrePause : <<extends>>
CreerNouveauNiveau --> TesterNiveau : <<extends>>
ModifierNiveau --> TesterNiveau : <<extends>>
RetourSelection --> ParcourirNiveaux : <<include>>
CreerNouveauNiveau --> ExporterNiveau : <<extends>>
ModifierNiveau --> ExporterNiveau : <<extends>>
@enduml

View File

@ -7,7 +7,7 @@ Les modélisations UML du projet sont générés à partir des fichiers `.puml`
- [Diagramme de classes](./UML/class-diagram.puml)
- [Diagramme d'activités](./UML/activity-diagram/activity-diagram.puml)
- [Diagramme de cas d'utilisation](./UML/use-case-diagram.puml)
- [Diagramme d'états](./UML/state-diagram/state-diagram.puml)
- [Diagramme d'états](./UML/state-diagram.puml)
## Diagramme de Gantt

View File

@ -11,18 +11,21 @@ EditorBuildSettings:
- enabled: 1
path: Assets/Scenes/ImportScene.unity
guid: 079203ac02d460d48a1208a03134d373
- enabled: 1
path: Assets/Scenes/EditorChoiceScene.unity
guid: 200c916866fde6f4bb0123b72f22771f
- enabled: 1
path: Assets/Scenes/SelectLevelScene.unity
guid: ff897c289bbe86f499c0c88b6177ab5c
- enabled: 1
path: Assets/Scenes/LevelScene.unity
guid: 8c9cfa26abfee488c85f1582747f6a02
- enabled: 1
path: Assets/Scenes/CreateLevelScene.unity
- enabled: 0
path: Assets/Scenes/LevelEditorScene.unity
guid: 73b983a44d701df4bb6d8ceb94e05a2b
- enabled: 1
path: Assets/Scenes/LevelEditorScene.unity
guid: 73b983a44d701df4bb6d8ceb94e05a2b
- enabled: 1
path: Assets/Scenes/EditorChoiceScene.unity
guid: 200c916866fde6f4bb0123b72f22771f
m_configObjects:
com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 2bcd2660ca9b64942af0de543d8d7100, type: 3}
m_UseUCBPForAssetBundles: 0

View File

@ -140,7 +140,7 @@ PlayerSettings:
loadStoreDebugModeEnabled: 0
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
bundleVersion: 1.0.0-staging.1
bundleVersion: v1.0.0-staging.3
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0

View File

@ -18,6 +18,7 @@ TagManager:
- PortalButtonGroup
- BonusBoostSpeed
- BonusSlowSpeed
- Ground
layers:
- Default
- TransparentFX

View File

@ -35,3 +35,11 @@ cd cnam-geometry-dash
# Ouvrir le projet avec Unity Hub
```
## Screenshots
![Homepage](./Documentation/Screenshots/home-page.png)
![Gameplay](./Documentation/Screenshots/gameplay.png)
![Levels selection](./Documentation/Screenshots/levels-selection.png)