fixed some bugs and added destroy() methods
This commit is contained in:
parent
c241aa99bc
commit
460d0e3643
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -11,7 +11,7 @@ dist/*
|
||||||
!dist/builtin/
|
!dist/builtin/
|
||||||
|
|
||||||
# Include the hw1 assets
|
# Include the hw1 assets
|
||||||
!dist/hw1_assets/
|
!dist/hw4_assets/
|
||||||
|
|
||||||
### IF YOU ARE MAKING A PROJECT, YOU MAY WANT TO UNCOMMENT THIS LINE ###
|
### IF YOU ARE MAKING A PROJECT, YOU MAY WANT TO UNCOMMENT THIS LINE ###
|
||||||
# !dist/assets/
|
# !dist/assets/
|
||||||
|
|
BIN
dist/hw4_assets/fonts/NoPixel.ttf
vendored
Normal file
BIN
dist/hw4_assets/fonts/NoPixel.ttf
vendored
Normal file
Binary file not shown.
BIN
dist/hw4_assets/sounds/jump-3.wav
vendored
Normal file
BIN
dist/hw4_assets/sounds/jump-3.wav
vendored
Normal file
Binary file not shown.
BIN
dist/hw4_assets/sprites/2bitbackground.png
vendored
Normal file
BIN
dist/hw4_assets/sprites/2bitbackground.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
dist/hw4_assets/sprites/coin.png
vendored
Normal file
BIN
dist/hw4_assets/sprites/coin.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 190 B |
22
dist/hw4_assets/spritesheets/ghostBunny.json
vendored
Normal file
22
dist/hw4_assets/spritesheets/ghostBunny.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "GhostBunny",
|
||||||
|
"spriteSheetImage": "ghostBunny.png",
|
||||||
|
"spriteWidth": 16,
|
||||||
|
"spriteHeight": 16,
|
||||||
|
"columns": 5,
|
||||||
|
"rows": 1,
|
||||||
|
"animations": [
|
||||||
|
{
|
||||||
|
"name": "IDLE",
|
||||||
|
"frames": [ {"index": 0, "duration": 1} ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WALK",
|
||||||
|
"frames": [ {"index": 0, "duration": 16}, {"index": 1, "duration": 16}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DYING",
|
||||||
|
"frames": [ {"index": 2, "duration": 8}, {"index": 3, "duration": 8}, {"index": 4, "duration": 8}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
dist/hw4_assets/spritesheets/ghostBunny.png
vendored
Normal file
BIN
dist/hw4_assets/spritesheets/ghostBunny.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 367 B |
26
dist/hw4_assets/spritesheets/hopper.json
vendored
Normal file
26
dist/hw4_assets/spritesheets/hopper.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "Hopper",
|
||||||
|
"spriteSheetImage": "hopper.png",
|
||||||
|
"spriteWidth": 16,
|
||||||
|
"spriteHeight": 16,
|
||||||
|
"columns": 15,
|
||||||
|
"rows": 1,
|
||||||
|
"animations": [
|
||||||
|
{
|
||||||
|
"name": "IDLE",
|
||||||
|
"frames": [ {"index": 0, "duration": 1} ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WALK",
|
||||||
|
"frames": [ {"index": 0, "duration": 16}, {"index": 1, "duration": 16}, {"index": 2, "duration": 16}, {"index": 3, "duration": 16}, {"index": 4, "duration": 16}, {"index": 5, "duration": 16}, {"index": 6, "duration": 16}, {"index": 7, "duration": 16} ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JUMP",
|
||||||
|
"frames":[ {"index": 8, "duration": 8}, {"index": 9, "duration": 8}, {"index": 10, "duration": 8}, {"index": 11, "duration": 8}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DYING",
|
||||||
|
"frames":[ {"index": 12, "duration": 8}, {"index": 13, "duration": 8}, {"index": 14, "duration": 8} ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
dist/hw4_assets/spritesheets/hopper.png
vendored
Normal file
BIN
dist/hw4_assets/spritesheets/hopper.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 641 B |
27
dist/hw4_assets/spritesheets/platformPlayer.json
vendored
Normal file
27
dist/hw4_assets/spritesheets/platformPlayer.json
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "PlatformPlayer",
|
||||||
|
"spriteSheetImage": "platformPlayer.png",
|
||||||
|
"spriteWidth": 16,
|
||||||
|
"spriteHeight": 16,
|
||||||
|
"columns": 7,
|
||||||
|
"rows": 1,
|
||||||
|
"durationType": "time",
|
||||||
|
"animations": [
|
||||||
|
{
|
||||||
|
"name": "IDLE",
|
||||||
|
"frames": [ {"index": 0, "duration": 540}, {"index": 1, "duration": 16}, {"index": 2, "duration": 16}, {"index": 3, "duration": 16}, {"index": 0, "duration": 360}, {"index": 1, "duration": 16}, {"index": 2, "duration": 16}, {"index": 3, "duration": 16}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WALK",
|
||||||
|
"frames": [ {"index": 0, "duration": 16}, {"index": 4, "duration": 16}, {"index": 0, "duration": 16}, {"index": 5, "duration": 16} ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "JUMP",
|
||||||
|
"frames":[ {"index": 6, "duration": 32}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FALL",
|
||||||
|
"frames":[ {"index": 4, "duration": 32}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
dist/hw4_assets/spritesheets/platformPlayer.png
vendored
Normal file
BIN
dist/hw4_assets/spritesheets/platformPlayer.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 300 B |
558
dist/hw4_assets/tilemaps/level1.json
vendored
Normal file
558
dist/hw4_assets/tilemaps/level1.json
vendored
Normal file
|
@ -0,0 +1,558 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"editorsettings":
|
||||||
|
{
|
||||||
|
"export":
|
||||||
|
{
|
||||||
|
"format":"json",
|
||||||
|
"target":"platformer.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"height":20,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 0, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 43, 0, 44, 44, 44, 44, 44, 44, 0, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 36, 0, 36, 36, 36, 36, 36, 36, 36, 36, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 54, 55, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 62, 63, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":20,
|
||||||
|
"id":2,
|
||||||
|
"name":"Background",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Collidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":0
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":64,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 20, 20, 20, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 11, 12, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 12, 12, 12, 12, 9, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 23, 24, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 16, 12, 12, 12, 31, 32, 12, 12, 12, 12, 12, 12, 9, 12, 12, 12, 12, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 7, 20, 20, 20, 20, 20, 20, 8, 12, 12, 12, 12, 10, 12, 23, 24, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 16, 12, 12, 13, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 12, 12, 31, 32, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 19, 20, 20, 20, 20, 13, 0, 0, 0, 0, 0, 0, 11, 20, 20, 20, 20, 20, 20, 20, 8, 12, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 2, 2, 2, 2, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 10, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 11, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 2, 2, 2, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 12, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 10, 12, 12, 12, 12, 12, 12, 10, 15, 4, 4, 4, 4, 4, 4, 12, 12],
|
||||||
|
"height":20,
|
||||||
|
"id":1,
|
||||||
|
"name":"Main",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Collidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"ground"
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":64,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":4,
|
||||||
|
"name":"Coins",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":2,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":256,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":3,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":272,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":4,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":368,
|
||||||
|
"y":288
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":5,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":384,
|
||||||
|
"y":288
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":6,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":400,
|
||||||
|
"y":288
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":7,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":688,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":8,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":688,
|
||||||
|
"y":288
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":9,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":688,
|
||||||
|
"y":304
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":10,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":784,
|
||||||
|
"y":256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":11,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":832,
|
||||||
|
"y":256
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":1
|
||||||
|
}],
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 64, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 57, 0, 59, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 57, 0, 0, 0, 0, 61, 0, 40, 0, 0, 0, 0, 0, 0, 56, 59, 0, 59, 0, 0, 0, 58, 0, 0, 0, 0, 61, 0, 0, 49, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":20,
|
||||||
|
"id":3,
|
||||||
|
"name":"Foreground",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Collidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":2
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":64,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":5,
|
||||||
|
"nextobjectid":14,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.3.4",
|
||||||
|
"tileheight":16,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":8,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"platformer.png",
|
||||||
|
"imageheight":128,
|
||||||
|
"imagewidth":128,
|
||||||
|
"margin":0,
|
||||||
|
"name":"platformer_tileset",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":64,
|
||||||
|
"tileheight":16,
|
||||||
|
"tilewidth":16
|
||||||
|
}],
|
||||||
|
"tilewidth":16,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.2,
|
||||||
|
"width":64
|
||||||
|
}
|
901
dist/hw4_assets/tilemaps/level2.json
vendored
Normal file
901
dist/hw4_assets/tilemaps/level2.json
vendored
Normal file
|
@ -0,0 +1,901 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"editorsettings":
|
||||||
|
{
|
||||||
|
"export":
|
||||||
|
{
|
||||||
|
"target":"."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"height":20,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, 36, 36, 36, 36, 36, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 44, 44, 44, 0, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 0, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, 36, 36, 36, 36, 0, 0, 0, 0, 0, 0, 44, 44, 44, 54, 55, 44, 44, 44, 44, 44, 44, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 54, 55, 44, 44, 0, 0, 0, 0, 0, 0, 0, 44, 44, 62, 63, 44, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 45, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 62, 63, 44, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":20,
|
||||||
|
"id":2,
|
||||||
|
"name":"Background",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Collidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":0
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":64,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 12, 13, 0, 0, 0, 0, 0, 30, 0, 0, 3, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 3, 4, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 10, 12, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0, 11, 10, 12, 12, 13, 0, 0, 0, 0, 0, 0, 11, 12, 7, 20, 20, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 20, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 11, 10, 13, 0, 0, 14, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, 0, 0, 30, 0, 0, 0, 0, 0, 11, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 11, 12, 13, 0, 0, 14, 0, 0, 11, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 10, 13, 0, 0, 0, 0, 0, 0, 19, 20, 20, 29, 0, 14, 0, 0, 19, 20, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 4, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 27, 20, 20, 20, 20, 21, 0, 0, 0, 0, 0, 0, 3, 5, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 11, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 10, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 19, 20, 21, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 4, 5, 0, 0, 0, 0, 0, 0, 11, 12, 10, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 12, 12, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 13, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 12, 12, 4, 5, 0, 0, 0, 0, 3, 4, 4, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 11, 12, 12, 12, 9, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 20, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 20, 21, 0, 0, 0, 0, 0, 0, 11, 10, 12, 12, 12, 12, 12, 15, 4, 4, 4, 4, 16, 12, 12, 12, 12, 12, 13, 1, 1, 1, 1, 1, 1, 11, 12, 12, 12, 12, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
"height":20,
|
||||||
|
"id":3,
|
||||||
|
"name":"Main",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Collidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"ground"
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":64,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":4,
|
||||||
|
"name":"Coins",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":1,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":272,
|
||||||
|
"y":80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":2,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":288,
|
||||||
|
"y":80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":3,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":112,
|
||||||
|
"y":80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":4,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":64,
|
||||||
|
"y":128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":5,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":160,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":6,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":176,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":7,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":192,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":8,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":208,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":9,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":384,
|
||||||
|
"y":176
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":10,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":400,
|
||||||
|
"y":176
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":11,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":384,
|
||||||
|
"y":192
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":12,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":400,
|
||||||
|
"y":192
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":13,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":400,
|
||||||
|
"y":208
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":14,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":704,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":15,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":720,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":16,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":752,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":17,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":736,
|
||||||
|
"y":272
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gid":25,
|
||||||
|
"height":16,
|
||||||
|
"id":18,
|
||||||
|
"name":"",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Group",
|
||||||
|
"type":"string",
|
||||||
|
"value":"coin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"HasPhysics",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsCollidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"IsTrigger",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerGroup",
|
||||||
|
"type":"string",
|
||||||
|
"value":"player"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TriggerOnEnter",
|
||||||
|
"type":"string",
|
||||||
|
"value":"PlayerHitCoin"
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":16,
|
||||||
|
"x":768,
|
||||||
|
"y":272
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":1
|
||||||
|
}],
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 40, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 48, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 56, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 2, 2, 0, 64, 0, 0, 0, 0, 56, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 2, 2, 2, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 64, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 57, 2, 0, 0, 0, 0, 0, 0, 0, 64, 57, 0, 61, 60, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 59, 0, 0, 59, 0, 0, 0, 0, 0, 2, 59, 58, 0, 59, 0, 0, 58, 2, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
"height":20,
|
||||||
|
"id":1,
|
||||||
|
"name":"Foreground",
|
||||||
|
"opacity":1,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Collidable",
|
||||||
|
"type":"bool",
|
||||||
|
"value":false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Depth",
|
||||||
|
"type":"int",
|
||||||
|
"value":2
|
||||||
|
}],
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":64,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":5,
|
||||||
|
"nextobjectid":19,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.3.4",
|
||||||
|
"tileheight":16,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":8,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"platformer.png",
|
||||||
|
"imageheight":128,
|
||||||
|
"imagewidth":128,
|
||||||
|
"margin":0,
|
||||||
|
"name":"platformer_tileset",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":64,
|
||||||
|
"tileheight":16,
|
||||||
|
"tilewidth":16
|
||||||
|
}],
|
||||||
|
"tilewidth":16,
|
||||||
|
"type":"map",
|
||||||
|
"version":1.2,
|
||||||
|
"width":64
|
||||||
|
}
|
BIN
dist/hw4_assets/tilemaps/platformer.png
vendored
Normal file
BIN
dist/hw4_assets/tilemaps/platformer.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
71
src/Homework4/Enemies/EnemyController.ts
Normal file
71
src/Homework4/Enemies/EnemyController.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import Idle from "./Idle";
|
||||||
|
import Jump from "./Jump";
|
||||||
|
import Walk from "./Walk";
|
||||||
|
import GameNode from "../../Wolfie2D/Nodes/GameNode";
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import StateMachineAI from "../../Wolfie2D/AI/StateMachineAI";
|
||||||
|
import { HW4_Events } from "../hw4_enums";
|
||||||
|
import { EaseFunctionType } from "../../Wolfie2D/Utils/EaseFunctions";
|
||||||
|
|
||||||
|
export enum EnemyStates {
|
||||||
|
IDLE = "idle",
|
||||||
|
WALK = "walk",
|
||||||
|
JUMP = "jump",
|
||||||
|
PREVIOUS = "previous"
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class EnemyController extends StateMachineAI {
|
||||||
|
owner: GameNode;
|
||||||
|
jumpy: boolean;
|
||||||
|
direction: Vec2 = Vec2.ZERO;
|
||||||
|
velocity: Vec2 = Vec2.ZERO;
|
||||||
|
speed: number = 200;
|
||||||
|
|
||||||
|
initializeAI(owner: GameNode, options: Record<string, any>){
|
||||||
|
this.owner = owner;
|
||||||
|
this.jumpy = options.jumpy ? options.jumpy : false;
|
||||||
|
|
||||||
|
this.receiver.subscribe(HW4_Events.PLAYER_MOVE);
|
||||||
|
if(this.jumpy){
|
||||||
|
this.receiver.subscribe(HW4_Events.PLAYER_JUMP);
|
||||||
|
this.speed = 100;
|
||||||
|
|
||||||
|
// Give the owner a tween for the jump
|
||||||
|
owner.tweens.add("jump", {
|
||||||
|
startDelay: 0,
|
||||||
|
duration: 300,
|
||||||
|
effects: [
|
||||||
|
{
|
||||||
|
property: "rotation",
|
||||||
|
resetOnComplete: true,
|
||||||
|
start: -3.14/8,
|
||||||
|
end: 3.14/8,
|
||||||
|
ease: EaseFunctionType.IN_OUT_SINE
|
||||||
|
}
|
||||||
|
],
|
||||||
|
reverseOnComplete: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let idle = new Idle(this, owner);
|
||||||
|
this.addState(EnemyStates.IDLE, idle);
|
||||||
|
let walk = new Walk(this, owner);
|
||||||
|
this.addState(EnemyStates.WALK, walk);
|
||||||
|
let jump = new Jump(this, owner);
|
||||||
|
this.addState(EnemyStates.JUMP, jump);
|
||||||
|
|
||||||
|
this.initialize(EnemyStates.IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeState(stateName: string): void {
|
||||||
|
|
||||||
|
if(stateName === EnemyStates.JUMP){
|
||||||
|
this.stack.push(this.stateMap.get(stateName));
|
||||||
|
}
|
||||||
|
super.changeState(stateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
}
|
||||||
|
}
|
31
src/Homework4/Enemies/EnemyState.ts
Normal file
31
src/Homework4/Enemies/EnemyState.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import State from "../../Wolfie2D/DataTypes/State/State";
|
||||||
|
import StateMachine from "../../Wolfie2D/DataTypes/State/StateMachine";
|
||||||
|
import GameEvent from "../../Wolfie2D/Events/GameEvent";
|
||||||
|
import GameNode from "../../Wolfie2D/Nodes/GameNode";
|
||||||
|
import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import EnemyController from "./EnemyController";
|
||||||
|
|
||||||
|
export default abstract class EnemyState extends State {
|
||||||
|
owner: GameNode;
|
||||||
|
gravity: number = 1000;
|
||||||
|
parent: EnemyController
|
||||||
|
|
||||||
|
constructor(parent: StateMachine, owner: GameNode){
|
||||||
|
super(parent);
|
||||||
|
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(event: GameEvent): void {}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
// Do gravity
|
||||||
|
this.parent.velocity.y += this.gravity*deltaT;
|
||||||
|
|
||||||
|
if(this.owner.onWall){
|
||||||
|
// Flip around
|
||||||
|
this.parent.direction.x *= -1;
|
||||||
|
(<AnimatedSprite>this.owner).invertX = !(<AnimatedSprite>this.owner).invertX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/Homework4/Enemies/Idle.ts
Normal file
37
src/Homework4/Enemies/Idle.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import GameEvent from "../../Wolfie2D/Events/GameEvent";
|
||||||
|
import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { HW4_Events } from "../hw4_enums";
|
||||||
|
import { EnemyStates } from "./EnemyController";
|
||||||
|
import OnGround from "./OnGround";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The idle enemy state. Enemies don't do anything until the player comes near them.
|
||||||
|
*/
|
||||||
|
export default class Idle extends OnGround {
|
||||||
|
onEnter(): void {
|
||||||
|
this.parent.speed = this.parent.speed;
|
||||||
|
(<AnimatedSprite>this.owner).animation.play("IDLE", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
(<AnimatedSprite>this.owner).animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(event: GameEvent) {
|
||||||
|
if(event.type === HW4_Events.PLAYER_MOVE){
|
||||||
|
let pos = event.data.get("position");
|
||||||
|
if(this.owner.position.x - pos.x < (64*10)){
|
||||||
|
this.finished(EnemyStates.WALK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
this.parent.velocity.x = 0;
|
||||||
|
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
}
|
||||||
|
}
|
34
src/Homework4/Enemies/Jump.ts
Normal file
34
src/Homework4/Enemies/Jump.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { EnemyStates } from "./EnemyController";
|
||||||
|
import EnemyState from "./EnemyState";
|
||||||
|
|
||||||
|
export default class Jump extends EnemyState {
|
||||||
|
|
||||||
|
onEnter(): void {
|
||||||
|
(<AnimatedSprite>this.owner).animation.play("JUMP", true);
|
||||||
|
(<AnimatedSprite>this.owner).tweens.play("jump", true);
|
||||||
|
this.gravity = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
if(this.owner.onGround){
|
||||||
|
this.finished(EnemyStates.PREVIOUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.owner.onCeiling){
|
||||||
|
this.parent.velocity.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.velocity.x += this.parent.direction.x * this.parent.speed/3.5 - 0.3*this.parent.velocity.x;
|
||||||
|
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
(<AnimatedSprite>this.owner).animation.stop();
|
||||||
|
(<AnimatedSprite>this.owner).tweens.stop("jump");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
26
src/Homework4/Enemies/OnGround.ts
Normal file
26
src/Homework4/Enemies/OnGround.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import GameEvent from "../../Wolfie2D/Events/GameEvent";
|
||||||
|
import { EnemyStates } from "./EnemyController";
|
||||||
|
import EnemyState from "./EnemyState";
|
||||||
|
|
||||||
|
export default class OnGround extends EnemyState {
|
||||||
|
onEnter(): void {}
|
||||||
|
|
||||||
|
handleInput(event: GameEvent): void {
|
||||||
|
super.handleInput(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
if(this.parent.velocity.y > 0){
|
||||||
|
this.parent.velocity.y = 0;
|
||||||
|
}
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
if(!this.owner.onGround && this.parent.jumpy && this.owner.active){
|
||||||
|
this.finished(EnemyStates.JUMP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
37
src/Homework4/Enemies/Walk.ts
Normal file
37
src/Homework4/Enemies/Walk.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { EnemyStates } from "./EnemyController";
|
||||||
|
import OnGround from "./OnGround";
|
||||||
|
|
||||||
|
export default class Walk extends OnGround {
|
||||||
|
time: number;
|
||||||
|
|
||||||
|
onEnter(): void {
|
||||||
|
if(this.parent.direction.isZero()){
|
||||||
|
this.parent.direction = new Vec2(-1, 0);
|
||||||
|
(<AnimatedSprite>this.owner).invertX = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
(<AnimatedSprite>this.owner).animation.play("WALK", true);
|
||||||
|
|
||||||
|
this.time = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
if(this.parent.jumpy && (Date.now() - this.time > 500)){
|
||||||
|
this.finished(EnemyStates.JUMP);
|
||||||
|
this.parent.velocity.y = -300;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.velocity.x = this.parent.direction.x * this.parent.speed;
|
||||||
|
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
(<AnimatedSprite>this.owner).animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
89
src/Homework4/Player/PlayerController.ts
Normal file
89
src/Homework4/Player/PlayerController.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import StateMachineAI from "../../Wolfie2D/AI/StateMachineAI";
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import Debug from "../../Wolfie2D/Debug/Debug";
|
||||||
|
import GameNode from "../../Wolfie2D/Nodes/GameNode";
|
||||||
|
import Sprite from "../../Wolfie2D/Nodes/Sprites/Sprite";
|
||||||
|
import OrthogonalTilemap from "../../Wolfie2D/Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
|
import Fall from "./PlayerStates/Fall";
|
||||||
|
import Idle from "./PlayerStates/Idle";
|
||||||
|
import InAir from "./PlayerStates/InAir";
|
||||||
|
import Jump from "./PlayerStates/Jump";
|
||||||
|
import Run from "./PlayerStates/Run";
|
||||||
|
import Walk from "./PlayerStates/Walk";
|
||||||
|
|
||||||
|
export enum PlayerType {
|
||||||
|
PLATFORMER = "platformer",
|
||||||
|
TOPDOWN = "topdown"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PlayerStates {
|
||||||
|
IDLE = "idle",
|
||||||
|
WALK = "walk",
|
||||||
|
RUN = "run",
|
||||||
|
JUMP = "jump",
|
||||||
|
FALL = "fall",
|
||||||
|
PREVIOUS = "previous"
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PlayerController extends StateMachineAI {
|
||||||
|
protected owner: GameNode;
|
||||||
|
velocity: Vec2 = Vec2.ZERO;
|
||||||
|
speed: number = 200;
|
||||||
|
MIN_SPEED: number = 200;
|
||||||
|
MAX_SPEED: number = 300;
|
||||||
|
tilemap: OrthogonalTilemap;
|
||||||
|
coin: Sprite;
|
||||||
|
|
||||||
|
initializeAI(owner: GameNode, options: Record<string, any>){
|
||||||
|
this.owner = owner;
|
||||||
|
|
||||||
|
this.initializePlatformer();
|
||||||
|
|
||||||
|
this.tilemap = this.owner.getScene().getTilemap(options.tilemap) as OrthogonalTilemap;
|
||||||
|
this.coin = this.owner.getScene().add.sprite("coin", "coinLayer");
|
||||||
|
this.coin.scale.set(2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializePlatformer(): void {
|
||||||
|
this.speed = 400;
|
||||||
|
|
||||||
|
let idle = new Idle(this, this.owner);
|
||||||
|
this.addState(PlayerStates.IDLE, idle);
|
||||||
|
let walk = new Walk(this, this.owner);
|
||||||
|
this.addState(PlayerStates.WALK, walk);
|
||||||
|
let run = new Run(this, this.owner);
|
||||||
|
this.addState(PlayerStates.RUN, run);
|
||||||
|
let jump = new Jump(this, this.owner);
|
||||||
|
this.addState(PlayerStates.JUMP, jump);
|
||||||
|
let fall = new Fall(this, this.owner);
|
||||||
|
this.addState(PlayerStates.FALL, fall);
|
||||||
|
|
||||||
|
this.initialize(PlayerStates.IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeState(stateName: string): void {
|
||||||
|
// If we jump or fall, push the state so we can go back to our current state later
|
||||||
|
// unless we're going from jump to fall or something
|
||||||
|
if((stateName === PlayerStates.JUMP || stateName === PlayerStates.FALL) && !(this.stack.peek() instanceof InAir)){
|
||||||
|
this.stack.push(this.stateMap.get(stateName));
|
||||||
|
}
|
||||||
|
|
||||||
|
super.changeState(stateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
if(this.currentState instanceof Jump){
|
||||||
|
Debug.log("playerstate", "Player State: Jump");
|
||||||
|
} else if (this.currentState instanceof Walk){
|
||||||
|
Debug.log("playerstate", "Player State: Walk");
|
||||||
|
} else if (this.currentState instanceof Run){
|
||||||
|
Debug.log("playerstate", "Player State: Run");
|
||||||
|
} else if (this.currentState instanceof Idle){
|
||||||
|
Debug.log("playerstate", "Player State: Idle");
|
||||||
|
} else if(this.currentState instanceof Fall){
|
||||||
|
Debug.log("playerstate", "Player State: Fall");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/Homework4/Player/PlayerStates/Fall.ts
Normal file
18
src/Homework4/Player/PlayerStates/Fall.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import GameEvent from "../../../Wolfie2D/Events/GameEvent";
|
||||||
|
import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import InAir from "./InAir";
|
||||||
|
|
||||||
|
export default class Fall extends InAir {
|
||||||
|
owner: AnimatedSprite;
|
||||||
|
|
||||||
|
onEnter(options: Record<string, any>): void {
|
||||||
|
this.owner.animation.play("FALL", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(event: GameEvent): void {}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
this.owner.animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
36
src/Homework4/Player/PlayerStates/Idle.ts
Normal file
36
src/Homework4/Player/PlayerStates/Idle.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import Input from "../../../Wolfie2D/Input/Input";
|
||||||
|
import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { PlayerStates } from "../PlayerController";
|
||||||
|
import OnGround from "./OnGround";
|
||||||
|
|
||||||
|
export default class Idle extends OnGround {
|
||||||
|
owner: AnimatedSprite;
|
||||||
|
|
||||||
|
onEnter(options: Record<string, any>): void {
|
||||||
|
this.parent.speed = this.parent.MIN_SPEED;
|
||||||
|
this.owner.animation.play("IDLE", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
let dir = this.getInputDirection();
|
||||||
|
|
||||||
|
if(!dir.isZero() && dir.y === 0){
|
||||||
|
if(Input.isPressed("run")){
|
||||||
|
this.finished(PlayerStates.RUN);
|
||||||
|
} else {
|
||||||
|
this.finished(PlayerStates.WALK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.velocity.x = 0;
|
||||||
|
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
this.owner.animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
19
src/Homework4/Player/PlayerStates/InAir.ts
Normal file
19
src/Homework4/Player/PlayerStates/InAir.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import GameEvent from "../../../Wolfie2D/Events/GameEvent";
|
||||||
|
import { PlayerStates } from "../PlayerController";
|
||||||
|
import PlayerState from "./PlayerState";
|
||||||
|
|
||||||
|
export default abstract class InAir extends PlayerState {
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
let dir = this.getInputDirection();
|
||||||
|
|
||||||
|
this.parent.velocity.x += dir.x * this.parent.speed/3.5 - 0.3*this.parent.velocity.x;
|
||||||
|
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
|
||||||
|
if(this.owner.onGround){
|
||||||
|
this.finished(PlayerStates.PREVIOUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
src/Homework4/Player/PlayerStates/Jump.ts
Normal file
108
src/Homework4/Player/PlayerStates/Jump.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import GameEvent from "../../../Wolfie2D/Events/GameEvent";
|
||||||
|
import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { EaseFunctionType } from "../../../Wolfie2D/Utils/EaseFunctions";
|
||||||
|
import { HW4_Events } from "../../hw4_enums";
|
||||||
|
import { PlayerStates } from "../PlayerController";
|
||||||
|
import InAir from "./InAir";
|
||||||
|
|
||||||
|
export default class Jump extends InAir {
|
||||||
|
owner: AnimatedSprite;
|
||||||
|
|
||||||
|
onEnter(options: Record<string, any>): void {
|
||||||
|
this.owner.animation.play("JUMP", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(event: GameEvent): void {}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
if(this.owner.onCeiling){
|
||||||
|
this.parent.velocity.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're falling, go to the fall state
|
||||||
|
if(this.parent.velocity.y >= 0){
|
||||||
|
this.finished(PlayerStates.FALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.owner.collidedWithTilemap && this.owner.onCeiling){
|
||||||
|
// We collided with a tilemap above us. First, get the tile right above us
|
||||||
|
this.handleCoinblockCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
this.owner.animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCoinblockCollision(){
|
||||||
|
let pos = this.owner.position.clone();
|
||||||
|
|
||||||
|
// Go up plus some extra
|
||||||
|
pos.y -= (this.owner.collisionShape.halfSize.y + 10);
|
||||||
|
pos.x -= 16;
|
||||||
|
let rowCol = this.parent.tilemap.getColRowAt(pos);
|
||||||
|
let tile1 = this.parent.tilemap.getTileAtRowCol(rowCol);
|
||||||
|
pos.x += 16;
|
||||||
|
rowCol = this.parent.tilemap.getColRowAt(pos);
|
||||||
|
let tile2 = this.parent.tilemap.getTileAtRowCol(rowCol);
|
||||||
|
pos.x += 16;
|
||||||
|
rowCol = this.parent.tilemap.getColRowAt(pos);
|
||||||
|
let tile3 = this.parent.tilemap.getTileAtRowCol(rowCol);
|
||||||
|
|
||||||
|
let t1 = tile1 === 17;
|
||||||
|
let t2 = tile2 === 17;
|
||||||
|
let t3 = tile3 === 17;
|
||||||
|
let air1 = tile1 === 0;
|
||||||
|
let air2 = tile2 === 0;
|
||||||
|
let air3 = tile3 === 0;
|
||||||
|
let majority = (t1 && t2) || (t1 && t3) || (t2 && t3) || (t1 && t2 && t3);
|
||||||
|
let minorityButAir = (t1 && air2 && air3) || (air1 && t2 && air3) || (air1 && air2 && t3);
|
||||||
|
|
||||||
|
// If coin block, change to empty coin block
|
||||||
|
if(majority || minorityButAir){
|
||||||
|
if(minorityButAir){
|
||||||
|
// Get the correct position
|
||||||
|
if(t1){
|
||||||
|
pos.x -= 32;
|
||||||
|
} else if(t2){
|
||||||
|
pos.x -= 16;
|
||||||
|
}
|
||||||
|
rowCol = this.parent.tilemap.getColRowAt(pos);
|
||||||
|
} else {
|
||||||
|
pos.x -= 16;
|
||||||
|
rowCol = this.parent.tilemap.getColRowAt(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.tilemap.setTileAtRowCol(rowCol, 18);
|
||||||
|
this.emitter.fireEvent(HW4_Events.PLAYER_HIT_COIN_BLOCK);
|
||||||
|
|
||||||
|
let tileSize = this.parent.tilemap.getTileSize();
|
||||||
|
this.parent.coin.position.copy(rowCol.scale(tileSize.x, tileSize.y).add(tileSize.scaled(0.5)));
|
||||||
|
|
||||||
|
// Animate collision
|
||||||
|
this.parent.coin.tweens.add("coin", {
|
||||||
|
startDelay: 0,
|
||||||
|
duration: 300,
|
||||||
|
effects: [{
|
||||||
|
property: "positionY",
|
||||||
|
resetOnComplete: false,
|
||||||
|
start: this.parent.coin.position.y,
|
||||||
|
end: this.parent.coin.position.y - 2*tileSize.y,
|
||||||
|
ease: EaseFunctionType.OUT_SINE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: "alpha",
|
||||||
|
resetOnComplete: false,
|
||||||
|
start: 1,
|
||||||
|
end: 0,
|
||||||
|
ease: EaseFunctionType.OUT_SINE
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.parent.coin.tweens.play("coin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/Homework4/Player/PlayerStates/OnGround.ts
Normal file
38
src/Homework4/Player/PlayerStates/OnGround.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import GameEvent from "../../../Wolfie2D/Events/GameEvent";
|
||||||
|
import Input from "../../../Wolfie2D/Input/Input";
|
||||||
|
import Sprite from "../../../Wolfie2D/Nodes/Sprites/Sprite";
|
||||||
|
import MathUtils from "../../../Wolfie2D/Utils/MathUtils";
|
||||||
|
import PlayerState from "./PlayerState";
|
||||||
|
|
||||||
|
export default class OnGround extends PlayerState {
|
||||||
|
onEnter(options: Record<string, any>): void {}
|
||||||
|
|
||||||
|
handleInput(event: GameEvent): void {}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
if(this.parent.velocity.y > 0){
|
||||||
|
this.parent.velocity.y = 0;
|
||||||
|
}
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
let direction = this.getInputDirection();
|
||||||
|
|
||||||
|
if(direction.x !== 0){
|
||||||
|
(<Sprite>this.owner).invertX = MathUtils.sign(direction.x) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Input.isJustPressed("jump")){
|
||||||
|
this.finished("jump");
|
||||||
|
this.parent.velocity.y = -500;
|
||||||
|
if(this.parent.velocity.x !== 0){
|
||||||
|
this.owner.tweens.play("flip");
|
||||||
|
}
|
||||||
|
} else if(!this.owner.onGround){
|
||||||
|
this.finished("jump");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
30
src/Homework4/Player/PlayerStates/PlayerState.ts
Normal file
30
src/Homework4/Player/PlayerStates/PlayerState.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import State from "../../../Wolfie2D/DataTypes/State/State";
|
||||||
|
import StateMachine from "../../../Wolfie2D/DataTypes/State/StateMachine";
|
||||||
|
import Vec2 from "../../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import Input from "../../../Wolfie2D/Input/Input";
|
||||||
|
import GameNode from "../../../Wolfie2D/Nodes/GameNode";
|
||||||
|
import PlayerController from "../PlayerController";
|
||||||
|
|
||||||
|
|
||||||
|
export default abstract class PlayerState extends State {
|
||||||
|
owner: GameNode;
|
||||||
|
gravity: number = 1000;
|
||||||
|
parent: PlayerController;
|
||||||
|
|
||||||
|
constructor(parent: StateMachine, owner: GameNode){
|
||||||
|
super(parent);
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInputDirection(): Vec2 {
|
||||||
|
let direction = Vec2.ZERO;
|
||||||
|
direction.x = (Input.isPressed("left") ? -1 : 0) + (Input.isPressed("right") ? 1 : 0);
|
||||||
|
direction.y = (Input.isJustPressed("jump") ? -1 : 0);
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
// Do gravity
|
||||||
|
this.parent.velocity.y += this.gravity*deltaT;
|
||||||
|
}
|
||||||
|
}
|
38
src/Homework4/Player/PlayerStates/Run.ts
Normal file
38
src/Homework4/Player/PlayerStates/Run.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import Input from "../../../Wolfie2D/Input/Input";
|
||||||
|
import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { HW4_Events } from "../../hw4_enums";
|
||||||
|
import { PlayerStates } from "../PlayerController";
|
||||||
|
import OnGround from "./OnGround";
|
||||||
|
|
||||||
|
export default class Run extends OnGround {
|
||||||
|
owner: AnimatedSprite;
|
||||||
|
|
||||||
|
onEnter(options: Record<string, any>): void {
|
||||||
|
this.parent.speed = this.parent.MAX_SPEED;
|
||||||
|
this.owner.animation.play("WALK", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
let dir = this.getInputDirection();
|
||||||
|
|
||||||
|
if(dir.isZero()){
|
||||||
|
this.finished(PlayerStates.IDLE);
|
||||||
|
} else {
|
||||||
|
if(!Input.isPressed("run")){
|
||||||
|
this.finished(PlayerStates.WALK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.velocity.x = dir.x * this.parent.speed
|
||||||
|
|
||||||
|
this.emitter.fireEvent(HW4_Events.PLAYER_MOVE, {position: this.owner.position.clone()});
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
this.owner.animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
38
src/Homework4/Player/PlayerStates/Walk.ts
Normal file
38
src/Homework4/Player/PlayerStates/Walk.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import Input from "../../../Wolfie2D/Input/Input";
|
||||||
|
import AnimatedSprite from "../../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import { HW4_Events } from "../../hw4_enums";
|
||||||
|
import { PlayerStates } from "../PlayerController";
|
||||||
|
import OnGround from "./OnGround";
|
||||||
|
|
||||||
|
export default class Walk extends OnGround {
|
||||||
|
owner: AnimatedSprite;
|
||||||
|
|
||||||
|
onEnter(options: Record<string, any>): void {
|
||||||
|
this.parent.speed = this.parent.MIN_SPEED;
|
||||||
|
this.owner.animation.play("WALK", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
super.update(deltaT);
|
||||||
|
|
||||||
|
let dir = this.getInputDirection();
|
||||||
|
|
||||||
|
if(dir.isZero()){
|
||||||
|
this.finished(PlayerStates.IDLE);
|
||||||
|
} else {
|
||||||
|
if(Input.isPressed("run")){
|
||||||
|
this.finished(PlayerStates.RUN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.velocity.x = dir.x * this.parent.speed
|
||||||
|
|
||||||
|
this.emitter.fireEvent(HW4_Events.PLAYER_MOVE, {position: this.owner.position.clone()});
|
||||||
|
this.owner.move(this.parent.velocity.scaled(deltaT));
|
||||||
|
}
|
||||||
|
|
||||||
|
onExit(): Record<string, any> {
|
||||||
|
this.owner.animation.stop();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
362
src/Homework4/Scenes/GameLevel.ts
Normal file
362
src/Homework4/Scenes/GameLevel.ts
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import Input from "../../Wolfie2D/Input/Input";
|
||||||
|
import { TweenableProperties } from "../../Wolfie2D/Nodes/GameNode";
|
||||||
|
import { GraphicType } from "../../Wolfie2D/Nodes/Graphics/GraphicTypes";
|
||||||
|
import Rect from "../../Wolfie2D/Nodes/Graphics/Rect";
|
||||||
|
import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import Label from "../../Wolfie2D/Nodes/UIElements/Label";
|
||||||
|
import { UIElementType } from "../../Wolfie2D/Nodes/UIElements/UIElementTypes";
|
||||||
|
import Scene from "../../Wolfie2D/Scene/Scene";
|
||||||
|
import Timer from "../../Wolfie2D/Timing/Timer";
|
||||||
|
import Color from "../../Wolfie2D/Utils/Color";
|
||||||
|
import { EaseFunctionType } from "../../Wolfie2D/Utils/EaseFunctions";
|
||||||
|
import EnemyController from "../Enemies/EnemyController";
|
||||||
|
import { HW4_Events } from "../hw4_enums";
|
||||||
|
import PlayerController from "../Player/PlayerController";
|
||||||
|
|
||||||
|
export default class GameLevel extends Scene {
|
||||||
|
// Every level will have a player, which will be an animated sprite
|
||||||
|
protected playerSpawn: Vec2;
|
||||||
|
protected player: AnimatedSprite;
|
||||||
|
|
||||||
|
// Labels for the UI
|
||||||
|
protected static coinCount: number = 0;
|
||||||
|
protected coinCountLabel: Label;
|
||||||
|
protected static livesCount: number = 3;
|
||||||
|
protected livesCountLabel: Label;
|
||||||
|
|
||||||
|
// Stuff to end the level and go to the next level
|
||||||
|
protected levelEndArea: Rect;
|
||||||
|
protected nextLevel: new (...args: any) => GameLevel;
|
||||||
|
protected levelEndTimer: Timer;
|
||||||
|
protected levelEndLabel: Label;
|
||||||
|
|
||||||
|
// Screen fade in/out for level start and end
|
||||||
|
protected levelTransitionTimer: Timer;
|
||||||
|
protected levelTransitionScreen: Rect;
|
||||||
|
|
||||||
|
startScene(): void {
|
||||||
|
// Do the game level standard initializations
|
||||||
|
this.initLayers();
|
||||||
|
this.initViewport();
|
||||||
|
this.initPlayer();
|
||||||
|
this.subscribeToEvents();
|
||||||
|
this.addUI();
|
||||||
|
|
||||||
|
// Initialize the timers
|
||||||
|
this.levelTransitionTimer = new Timer(500);
|
||||||
|
this.levelEndTimer = new Timer(3000, () => {
|
||||||
|
// After the level end timer ends, fade to black and then go to the next scene
|
||||||
|
this.levelTransitionScreen.tweens.play("fadeIn");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the black screen fade out
|
||||||
|
this.levelTransitionScreen.tweens.play("fadeOut");
|
||||||
|
|
||||||
|
// Initially disable player movement
|
||||||
|
Input.disableInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScene(deltaT: number){
|
||||||
|
// Handle events and update the UI if needed
|
||||||
|
while(this.receiver.hasNextEvent()){
|
||||||
|
let event = this.receiver.getNextEvent();
|
||||||
|
|
||||||
|
switch(event.type){
|
||||||
|
case HW4_Events.PLAYER_HIT_COIN:
|
||||||
|
{
|
||||||
|
// Hit a coin
|
||||||
|
let coin;
|
||||||
|
if(event.data.get("node") === this.player.id){
|
||||||
|
// Other is coin, disable
|
||||||
|
coin = this.sceneGraph.getNode(event.data.get("other"));
|
||||||
|
} else {
|
||||||
|
// Node is coin, disable
|
||||||
|
coin = this.sceneGraph.getNode(event.data.get("node"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from physics and scene
|
||||||
|
coin.active = false;
|
||||||
|
coin.visible = false;
|
||||||
|
|
||||||
|
// Increment our number of coins
|
||||||
|
this.incPlayerCoins(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HW4_Events.PLAYER_HIT_COIN_BLOCK:
|
||||||
|
{
|
||||||
|
// Hit a coin block, so increment our number of coins
|
||||||
|
this.incPlayerCoins(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HW4_Events.PLAYER_HIT_ENEMY:
|
||||||
|
{
|
||||||
|
let node = this.sceneGraph.getNode(event.data.get("node"));
|
||||||
|
let other = this.sceneGraph.getNode(event.data.get("other"));
|
||||||
|
|
||||||
|
if(node === this.player){
|
||||||
|
// Node is player, other is enemy
|
||||||
|
this.handlePlayerEnemyCollision(<AnimatedSprite>node, <AnimatedSprite>other);
|
||||||
|
} else {
|
||||||
|
// Other is player, node is enemy
|
||||||
|
this.handlePlayerEnemyCollision(<AnimatedSprite>other,<AnimatedSprite>node);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HW4_Events.ENEMY_DIED:
|
||||||
|
{
|
||||||
|
// An enemy finished its dying animation, hide it
|
||||||
|
let node = this.sceneGraph.getNode(event.data.get("owner"));
|
||||||
|
node.visible = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HW4_Events.PLAYER_ENTERED_LEVEL_END:
|
||||||
|
{
|
||||||
|
if(!this.levelEndTimer.hasRun() && this.levelEndTimer.isStopped()){
|
||||||
|
// The player has reached the end of the level
|
||||||
|
this.levelEndTimer.start();
|
||||||
|
this.levelEndLabel.tweens.play("slideIn");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HW4_Events.LEVEL_START:
|
||||||
|
{
|
||||||
|
// Re-enable controls
|
||||||
|
console.log("Enabling input");
|
||||||
|
Input.enableInput();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HW4_Events.LEVEL_END:
|
||||||
|
{
|
||||||
|
// Go to the next level
|
||||||
|
if(this.nextLevel){
|
||||||
|
console.log("Going to next level!");
|
||||||
|
let sceneOptions = {
|
||||||
|
physics: {
|
||||||
|
groupNames: ["ground", "player", "enemy", "coin"],
|
||||||
|
collisions:
|
||||||
|
[
|
||||||
|
[0, 1, 1, 0],
|
||||||
|
[1, 0, 0, 1],
|
||||||
|
[1, 0, 0, 0],
|
||||||
|
[0, 1, 0, 0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sceneManager.changeToScene(this.nextLevel, {}, sceneOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If player falls into a pit, kill them off and reset their position
|
||||||
|
if(this.player.position.y > 100*64){
|
||||||
|
this.incPlayerLife(-1);
|
||||||
|
this.respawnPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initLayers(): void {
|
||||||
|
// Add a layer behind the tilemap for coinblock animation
|
||||||
|
this.addLayer("coinLayer", -50);
|
||||||
|
|
||||||
|
// Add a layer for UI
|
||||||
|
this.addUILayer("UI");
|
||||||
|
|
||||||
|
// Add a layer for players and enemies
|
||||||
|
this.addLayer("primary", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initViewport(): void {
|
||||||
|
this.viewport.enableZoom();
|
||||||
|
this.viewport.setZoomLevel(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected subscribeToEvents(){
|
||||||
|
this.receiver.subscribe([
|
||||||
|
HW4_Events.PLAYER_HIT_COIN,
|
||||||
|
HW4_Events.PLAYER_HIT_COIN_BLOCK,
|
||||||
|
HW4_Events.PLAYER_HIT_ENEMY,
|
||||||
|
HW4_Events.ENEMY_DIED,
|
||||||
|
HW4_Events.PLAYER_ENTERED_LEVEL_END,
|
||||||
|
HW4_Events.LEVEL_START,
|
||||||
|
HW4_Events.LEVEL_END
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addUI(){
|
||||||
|
// In-game labels
|
||||||
|
this.coinCountLabel = <Label>this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(80, 30), text: "Coins: " + GameLevel.coinCount});
|
||||||
|
this.coinCountLabel.textColor = Color.WHITE
|
||||||
|
this.coinCountLabel.font = "NoPixel";
|
||||||
|
this.livesCountLabel = <Label>this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(500, 30), text: "Lives: " + GameLevel.livesCount});
|
||||||
|
this.livesCountLabel.textColor = Color.WHITE
|
||||||
|
this.livesCountLabel.font = "NoPixel";
|
||||||
|
|
||||||
|
// End of level label (start off screen)
|
||||||
|
this.levelEndLabel = <Label>this.add.uiElement(UIElementType.LABEL, "UI", {position: new Vec2(-300, 200), text: "Level Complete"});
|
||||||
|
this.levelEndLabel.size.set(1200, 60);
|
||||||
|
this.levelEndLabel.borderRadius = 0;
|
||||||
|
this.levelEndLabel.backgroundColor = new Color(34, 32, 52);
|
||||||
|
this.levelEndLabel.textColor = Color.WHITE;
|
||||||
|
this.levelEndLabel.fontSize = 48;
|
||||||
|
this.levelEndLabel.font = "NoPixel";
|
||||||
|
|
||||||
|
// Add a tween to move the label on screen
|
||||||
|
this.levelEndLabel.tweens.add("slideIn", {
|
||||||
|
startDelay: 0,
|
||||||
|
duration: 1000,
|
||||||
|
effects: [
|
||||||
|
{
|
||||||
|
property: TweenableProperties.posX,
|
||||||
|
start: -300,
|
||||||
|
end: 300,
|
||||||
|
ease: EaseFunctionType.OUT_SINE
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.levelTransitionScreen = <Rect>this.add.graphic(GraphicType.RECT, "UI", {position: new Vec2(300, 200), size: new Vec2(600, 400)});
|
||||||
|
this.levelTransitionScreen.color = new Color(34, 32, 52);
|
||||||
|
this.levelTransitionScreen.alpha = 1;
|
||||||
|
|
||||||
|
this.levelTransitionScreen.tweens.add("fadeIn", {
|
||||||
|
startDelay: 0,
|
||||||
|
duration: 1000,
|
||||||
|
effects: [
|
||||||
|
{
|
||||||
|
property: TweenableProperties.alpha,
|
||||||
|
start: 0,
|
||||||
|
end: 1,
|
||||||
|
ease: EaseFunctionType.IN_OUT_QUAD
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onEnd: HW4_Events.LEVEL_END
|
||||||
|
});
|
||||||
|
|
||||||
|
this.levelTransitionScreen.tweens.add("fadeOut", {
|
||||||
|
startDelay: 0,
|
||||||
|
duration: 1000,
|
||||||
|
effects: [
|
||||||
|
{
|
||||||
|
property: TweenableProperties.alpha,
|
||||||
|
start: 1,
|
||||||
|
end: 0,
|
||||||
|
ease: EaseFunctionType.IN_OUT_QUAD
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onEnd: HW4_Events.LEVEL_START
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected initPlayer(): void {
|
||||||
|
// Add the player
|
||||||
|
this.player = this.add.animatedSprite("player", "primary");
|
||||||
|
this.player.scale.set(2, 2);
|
||||||
|
if(!this.playerSpawn){
|
||||||
|
console.warn("Player spawn was never set - setting spawn to (0, 0)");
|
||||||
|
this.playerSpawn = Vec2.ZERO;
|
||||||
|
}
|
||||||
|
this.player.position.copy(this.playerSpawn);
|
||||||
|
this.player.addPhysics();
|
||||||
|
this.player.addAI(PlayerController, {playerType: "platformer", tilemap: "Main"});
|
||||||
|
|
||||||
|
// Add triggers on colliding with coins or coinBlocks
|
||||||
|
this.player.setGroup("player");
|
||||||
|
|
||||||
|
// Add a tween animation for the player jump
|
||||||
|
this.player.tweens.add("flip", {
|
||||||
|
startDelay: 0,
|
||||||
|
duration: 500,
|
||||||
|
effects: [
|
||||||
|
{
|
||||||
|
property: "rotation",
|
||||||
|
start: 0,
|
||||||
|
end: 2*Math.PI,
|
||||||
|
ease: EaseFunctionType.IN_OUT_QUAD
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.viewport.follow(this.player);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addLevelEnd(startingTile: Vec2, size: Vec2): void {
|
||||||
|
this.levelEndArea = <Rect>this.add.graphic(GraphicType.RECT, "primary", {position: startingTile.add(size.scaled(0.5)).scale(32), size: size.scale(32)});
|
||||||
|
this.levelEndArea.addPhysics(undefined, undefined, false, true);
|
||||||
|
this.levelEndArea.setTrigger("player", HW4_Events.PLAYER_ENTERED_LEVEL_END, null);
|
||||||
|
this.levelEndArea.color = new Color(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addEnemy(spriteKey: string, tilePos: Vec2, aiOptions: Record<string, any>): void {
|
||||||
|
let enemy = this.add.animatedSprite(spriteKey, "primary");
|
||||||
|
enemy.position.set(tilePos.x*32, tilePos.y*32);
|
||||||
|
enemy.scale.set(2, 2);
|
||||||
|
enemy.addPhysics();
|
||||||
|
enemy.addAI(EnemyController, aiOptions);
|
||||||
|
enemy.setGroup("enemy");
|
||||||
|
enemy.setTrigger("player", HW4_Events.PLAYER_HIT_ENEMY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handlePlayerEnemyCollision(player: AnimatedSprite, enemy: AnimatedSprite) {
|
||||||
|
// Get the vector of the direction from the player to the enemy
|
||||||
|
let dir = player.position.dirTo(enemy.position);
|
||||||
|
|
||||||
|
if((<EnemyController>enemy.ai).jumpy){
|
||||||
|
// If it's a jumpy enemy, we want to hit it from the bottom
|
||||||
|
if(dir.dot(Vec2.UP) > 0.5){
|
||||||
|
enemy.disablePhysics();
|
||||||
|
enemy.tweens.stopAll();
|
||||||
|
enemy.animation.play("DYING", false, HW4_Events.ENEMY_DIED);
|
||||||
|
|
||||||
|
// Stop the player's jump for some feedback
|
||||||
|
(<PlayerController>player.ai).velocity.y = 0;
|
||||||
|
} else {
|
||||||
|
this.incPlayerLife(-1);
|
||||||
|
this.respawnPlayer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If not, we want to hit it from the top
|
||||||
|
if(dir.dot(Vec2.DOWN) > 0.5){
|
||||||
|
enemy.disablePhysics();
|
||||||
|
enemy.animation.play("DYING", false, HW4_Events.ENEMY_DIED);
|
||||||
|
|
||||||
|
// Give the player a slight jump boost
|
||||||
|
let playerVel = (<PlayerController>player.ai).velocity;
|
||||||
|
if(playerVel.y < 0){
|
||||||
|
// We're going up - unlikely, but still check
|
||||||
|
playerVel.y += 0.2*(<PlayerController>player.ai).velocity.y;
|
||||||
|
} else {
|
||||||
|
// We're going down, invert our bounce, but dampen it
|
||||||
|
playerVel.y = -0.5 * (<PlayerController>player.ai).velocity.y;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.incPlayerLife(-1);
|
||||||
|
this.respawnPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected incPlayerLife(amt: number): void {
|
||||||
|
GameLevel.livesCount += amt;
|
||||||
|
this.livesCountLabel.text = "Lives: " + GameLevel.livesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected incPlayerCoins(amt: number): void {
|
||||||
|
GameLevel.coinCount += amt;
|
||||||
|
this.coinCountLabel.text = "Coins: " + GameLevel.coinCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected respawnPlayer(): void {
|
||||||
|
this.player.position.copy(this.playerSpawn);
|
||||||
|
}
|
||||||
|
}
|
51
src/Homework4/Scenes/Level1.ts
Normal file
51
src/Homework4/Scenes/Level1.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import Debug from "../../Wolfie2D/Debug/Debug";
|
||||||
|
import GameLevel from "./GameLevel";
|
||||||
|
import Level2 from "./Level2";
|
||||||
|
|
||||||
|
export default class Level1 extends GameLevel {
|
||||||
|
loadScene(): void {
|
||||||
|
this.load.image("background", "hw4_assets/sprites/2bitbackground.png");
|
||||||
|
this.load.image("coin", "hw4_assets/sprites/coin.png");
|
||||||
|
this.load.tilemap("level1", "hw4_assets/tilemaps/level1.json");
|
||||||
|
this.load.spritesheet("player", "hw4_assets/spritesheets/platformPlayer.json");
|
||||||
|
this.load.spritesheet("hopper", "hw4_assets/spritesheets/hopper.json");
|
||||||
|
this.load.spritesheet("bunny", "hw4_assets/spritesheets/ghostBunny.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
startScene(): void {
|
||||||
|
// Add a background layer and set the background image on it
|
||||||
|
this.addParallaxLayer("bg", new Vec2(0.25, 0), -100);
|
||||||
|
let bg = this.add.sprite("background", "bg");
|
||||||
|
bg.scale.set(2, 2);
|
||||||
|
bg.position.set(bg.boundary.halfSize.x, 76);
|
||||||
|
|
||||||
|
// Add the level 1 tilemap
|
||||||
|
this.add.tilemap("level1", new Vec2(2, 2));
|
||||||
|
this.viewport.setBounds(0, 0, 64*32, 20*32);
|
||||||
|
|
||||||
|
this.playerSpawn = new Vec2(5*32, 18*32);
|
||||||
|
|
||||||
|
// Do generic setup for a GameLevel
|
||||||
|
super.startScene();
|
||||||
|
|
||||||
|
this.addLevelEnd(new Vec2(58, 17), new Vec2(2, 2));
|
||||||
|
|
||||||
|
this.nextLevel = Level2;
|
||||||
|
|
||||||
|
// Add enemies of various types
|
||||||
|
for(let pos of [new Vec2(24, 18)]){
|
||||||
|
this.addEnemy("bunny", pos, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let pos of [new Vec2(51, 17)]){
|
||||||
|
this.addEnemy("hopper", pos, {jumpy: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScene(deltaT: number): void {
|
||||||
|
super.updateScene(deltaT);
|
||||||
|
|
||||||
|
Debug.log("playerpos", this.player.position.toString());
|
||||||
|
}
|
||||||
|
}
|
49
src/Homework4/Scenes/Level2.ts
Normal file
49
src/Homework4/Scenes/Level2.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import Debug from "../../Wolfie2D/Debug/Debug";
|
||||||
|
import GameLevel from "./GameLevel";
|
||||||
|
|
||||||
|
export default class Level2 extends GameLevel {
|
||||||
|
loadScene(): void {
|
||||||
|
this.load.image("background", "hw4_assets/sprites/2bitbackground.png");
|
||||||
|
this.load.image("coin", "hw4_assets/sprites/coin.png");
|
||||||
|
this.load.tilemap("level2", "hw4_assets/tilemaps/level2.json");
|
||||||
|
this.load.spritesheet("player", "hw4_assets/spritesheets/platformPlayer.json");
|
||||||
|
this.load.spritesheet("hopper", "hw4_assets/spritesheets/hopper.json");
|
||||||
|
this.load.spritesheet("bunny", "hw4_assets/spritesheets/ghostBunny.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
startScene(): void {
|
||||||
|
console.log("Starting level2");
|
||||||
|
// Add a background layer and set the background image on it
|
||||||
|
this.addParallaxLayer("bg", new Vec2(0.25, 0), -100);
|
||||||
|
let bg = this.add.sprite("background", "bg");
|
||||||
|
bg.scale.set(2, 2);
|
||||||
|
bg.position.set(bg.boundary.halfSize.x, 96);
|
||||||
|
|
||||||
|
// Add the level 1 tilemap
|
||||||
|
this.add.tilemap("level2", new Vec2(2, 2));
|
||||||
|
this.viewport.setBounds(0, 0, 64*32, 20*32);
|
||||||
|
|
||||||
|
this.playerSpawn = new Vec2(5*32, 18*32);
|
||||||
|
|
||||||
|
// Do generic setup for a GameLevel
|
||||||
|
super.startScene();
|
||||||
|
|
||||||
|
this.addLevelEnd(new Vec2(58, 17), new Vec2(2, 2));
|
||||||
|
|
||||||
|
// Add enemies of various types
|
||||||
|
for(let pos of [new Vec2(24, 18)]){
|
||||||
|
this.addEnemy("bunny", pos, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let pos of [new Vec2(51, 17)]){
|
||||||
|
this.addEnemy("hopper", pos, {jumpy: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScene(deltaT: number): void {
|
||||||
|
super.updateScene(deltaT);
|
||||||
|
|
||||||
|
Debug.log("playerpos", this.player.position.toString());
|
||||||
|
}
|
||||||
|
}
|
63
src/Homework4/Scenes/MainMenu.ts
Normal file
63
src/Homework4/Scenes/MainMenu.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import Vec2 from "../../Wolfie2D/DataTypes/Vec2";
|
||||||
|
import AnimatedSprite from "../../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
import Button from "../../Wolfie2D/Nodes/UIElements/Button";
|
||||||
|
import { UIElementType } from "../../Wolfie2D/Nodes/UIElements/UIElementTypes";
|
||||||
|
import Scene from "../../Wolfie2D/Scene/Scene";
|
||||||
|
import Color from "../../Wolfie2D/Utils/Color";
|
||||||
|
import Level1 from "./Level1";
|
||||||
|
|
||||||
|
export default class MainMenu extends Scene {
|
||||||
|
|
||||||
|
animatedSprite: AnimatedSprite;
|
||||||
|
|
||||||
|
loadScene(): void {}
|
||||||
|
|
||||||
|
startScene(): void {
|
||||||
|
this.addUILayer("Main");
|
||||||
|
|
||||||
|
let size = this.viewport.getHalfSize();
|
||||||
|
this.viewport.setFocus(size);
|
||||||
|
|
||||||
|
let playBtn = <Button>this.add.uiElement(UIElementType.BUTTON, "Main", {position: new Vec2(size.x, size.y), text: "Play Game"});
|
||||||
|
playBtn.setBackgroundColor(Color.GREEN);
|
||||||
|
playBtn.setPadding(new Vec2(50, 10));
|
||||||
|
playBtn.font = "NoPixel";
|
||||||
|
|
||||||
|
// When the play button is clicked, go to the next scene
|
||||||
|
playBtn.onClick = () => {
|
||||||
|
/*
|
||||||
|
Init the next scene with physics collisions:
|
||||||
|
|
||||||
|
ground player enemy coin
|
||||||
|
ground No -- -- --
|
||||||
|
player Yes No -- --
|
||||||
|
enemy Yes No No --
|
||||||
|
coin No Yes No No
|
||||||
|
|
||||||
|
Each layer becomes a number. In this case, 4 bits matter for each
|
||||||
|
|
||||||
|
ground: self - 0001, collisions - 0110
|
||||||
|
player: self - 0010, collisions - 1001
|
||||||
|
enemy: self - 0100, collisions - 0001
|
||||||
|
coin: self - 1000, collisions - 0010
|
||||||
|
*/
|
||||||
|
|
||||||
|
let sceneOptions = {
|
||||||
|
physics: {
|
||||||
|
groupNames: ["ground", "player", "enemy", "coin"],
|
||||||
|
collisions:
|
||||||
|
[
|
||||||
|
[0, 1, 1, 0],
|
||||||
|
[1, 0, 0, 1],
|
||||||
|
[1, 0, 0, 0],
|
||||||
|
[0, 1, 0, 0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sceneManager.changeToScene(Level1, {}, sceneOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScene(): void {}
|
||||||
|
}
|
||||||
|
|
11
src/Homework4/hw4_enums.ts
Normal file
11
src/Homework4/hw4_enums.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export enum HW4_Events {
|
||||||
|
PLAYER_MOVE = "PlayerMove",
|
||||||
|
PLAYER_JUMP = "PlayerJump",
|
||||||
|
PLAYER_HIT_COIN = "PlayerHitCoin",
|
||||||
|
PLAYER_HIT_COIN_BLOCK = "PlayerHitCoinBlock",
|
||||||
|
PLAYER_HIT_ENEMY = "PlayerHitEnemy",
|
||||||
|
ENEMY_DIED = "EnemyDied",
|
||||||
|
PLAYER_ENTERED_LEVEL_END = "PlayerEnteredLevelEnd",
|
||||||
|
LEVEL_START = "LevelStart",
|
||||||
|
LEVEL_END = "LevelEnd",
|
||||||
|
}
|
|
@ -23,10 +23,17 @@ export default class AIManager implements Updateable {
|
||||||
* @param actor The actor to register
|
* @param actor The actor to register
|
||||||
*/
|
*/
|
||||||
registerActor(actor: Actor): void {
|
registerActor(actor: Actor): void {
|
||||||
actor.actorId = this.actors.length;
|
|
||||||
this.actors.push(actor);
|
this.actors.push(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeActor(actor: Actor): void {
|
||||||
|
let index = this.actors.indexOf(actor);
|
||||||
|
|
||||||
|
if(index !== -1){
|
||||||
|
this.actors.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an AI with the AIManager for use later on
|
* Registers an AI with the AIManager for use later on
|
||||||
* @param name The name of the AI to register
|
* @param name The name of the AI to register
|
||||||
|
|
21
src/Wolfie2D/AI/ControllerAI.ts
Normal file
21
src/Wolfie2D/AI/ControllerAI.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import AI from "../DataTypes/Interfaces/AI";
|
||||||
|
import GameEvent from "../Events/GameEvent";
|
||||||
|
import GameNode from "../Nodes/GameNode";
|
||||||
|
|
||||||
|
export default abstract class ControllerAI implements AI {
|
||||||
|
/** The owner of this controller */
|
||||||
|
owner: GameNode;
|
||||||
|
|
||||||
|
/** Removes the instance of the owner of this AI */
|
||||||
|
destroy(): void {
|
||||||
|
delete this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract initializeAI(owner: GameNode, options: Record<string, any>): void;
|
||||||
|
|
||||||
|
abstract activate(options: Record<string, any>): void;
|
||||||
|
|
||||||
|
abstract handleEvent(event: GameEvent): void;
|
||||||
|
|
||||||
|
abstract update(deltaT: number): void;
|
||||||
|
}
|
|
@ -13,5 +13,12 @@ export default class StateMachineAI extends StateMachine implements AI {
|
||||||
// @implemented
|
// @implemented
|
||||||
initializeAI(owner: GameNode, config: Record<string, any>): void {}
|
initializeAI(owner: GameNode, config: Record<string, any>): void {}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
|
destroy(){
|
||||||
|
// Get rid of our reference to the owner
|
||||||
|
delete this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @implemented
|
||||||
activate(options: Record<string, any>): void {}
|
activate(options: Record<string, any>): void {}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import GameEvent from "../../Events/GameEvent";
|
import GameEvent from "../../Events/GameEvent";
|
||||||
import GameNode from "../../Nodes/GameNode";
|
import GameNode from "../../Nodes/GameNode";
|
||||||
import Actor from "./Actor";
|
|
||||||
import Updateable from "./Updateable";
|
import Updateable from "./Updateable";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,6 +9,9 @@ export default interface AI extends Updateable {
|
||||||
/** Initializes the AI with the actor and any additional config */
|
/** Initializes the AI with the actor and any additional config */
|
||||||
initializeAI(owner: GameNode, options: Record<string, any>): void;
|
initializeAI(owner: GameNode, options: Record<string, any>): void;
|
||||||
|
|
||||||
|
/** Clears references from to the owner */
|
||||||
|
destroy(): void;
|
||||||
|
|
||||||
/** Activates this AI from a stopped state and allows variables to be passed in */
|
/** Activates this AI from a stopped state and allows variables to be passed in */
|
||||||
activate(options: Record<string, any>): void;
|
activate(options: Record<string, any>): void;
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,6 @@ export default interface Actor {
|
||||||
/** The activity status of the actor */
|
/** The activity status of the actor */
|
||||||
aiActive: boolean;
|
aiActive: boolean;
|
||||||
|
|
||||||
/** The id of the actor according to the @reference[AIManager] */
|
|
||||||
actorId: number;
|
|
||||||
|
|
||||||
/** The path that navigation will follow */
|
/** The path that navigation will follow */
|
||||||
path: NavigationPath;
|
path: NavigationPath;
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,14 @@ export default interface Physical {
|
||||||
/** Represnts whether this object is a trigger or not. */
|
/** Represnts whether this object is a trigger or not. */
|
||||||
isTrigger: boolean;
|
isTrigger: boolean;
|
||||||
|
|
||||||
/** The physics group of this object. Used for triggers and for selective collisions. */
|
/** The trigger mask for this node */
|
||||||
group: string;
|
triggerMask: number;
|
||||||
|
|
||||||
/** Associates different groups with trigger events. */
|
/** Events to trigger for collision enters. */
|
||||||
triggers: Map<string>;
|
triggerEnters: Array<string>;
|
||||||
|
|
||||||
|
/** Events to trigger for collision exits */
|
||||||
|
triggerExits: Array<string>;
|
||||||
|
|
||||||
/** A vector that allows velocity to be passed to the physics engine */
|
/** A vector that allows velocity to be passed to the physics engine */
|
||||||
_velocity: Vec2;
|
_velocity: Vec2;
|
||||||
|
@ -55,8 +58,8 @@ export default interface Physical {
|
||||||
/** A boolean representing whether or not the node just collided with the tilemap */
|
/** A boolean representing whether or not the node just collided with the tilemap */
|
||||||
collidedWithTilemap: boolean;
|
collidedWithTilemap: boolean;
|
||||||
|
|
||||||
/** The physics layer this node belongs to */
|
/** The physics group this node belongs to */
|
||||||
physicsLayer: number;
|
group: number;
|
||||||
|
|
||||||
isPlayer: boolean;
|
isPlayer: boolean;
|
||||||
|
|
||||||
|
@ -84,18 +87,28 @@ export default interface Physical {
|
||||||
*/
|
*/
|
||||||
addPhysics(collisionShape?: Shape, colliderOffset?: Vec2, isCollidable?: boolean, isStatic?: boolean): void;
|
addPhysics(collisionShape?: Shape, colliderOffset?: Vec2, isCollidable?: boolean, isStatic?: boolean): void;
|
||||||
|
|
||||||
|
/** Removes this object from the physics system */
|
||||||
|
removePhysics(): void;
|
||||||
|
|
||||||
|
/** Prevents this object from participating in all collisions and triggers. It can still move. */
|
||||||
|
disablePhysics(): void;
|
||||||
|
|
||||||
|
/** Enables this object to participate in collisions and triggers. This is only necessary if disablePhysics was called */
|
||||||
|
enablePhysics(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a trigger to this object for a specific group
|
* Sets this object to be a trigger for a specific group
|
||||||
* @param group The name of the group that activates the trigger
|
* @param group The name of the group that activates the trigger
|
||||||
* @param eventType The name of the event to send when this trigger is activated
|
* @param onEnter The name of the event to send when this trigger is activated
|
||||||
|
* @param onExit The name of the event to send when this trigger stops being activated
|
||||||
*/
|
*/
|
||||||
addTrigger(group: string, eventType: string): void;
|
setTrigger(group: string, onEnter: string, onExit: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the physics layer of this node
|
* Sets the physics group of this node
|
||||||
* @param layer The name of the layer
|
* @param group The name of the group
|
||||||
*/
|
*/
|
||||||
setPhysicsLayer(layer: string): void;
|
setGroup(group: string): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If used before "move()", it will tell you the velocity of the node after its last movement
|
* If used before "move()", it will tell you the velocity of the node after its last movement
|
||||||
|
|
63
src/Wolfie2D/DataTypes/List.ts
Normal file
63
src/Wolfie2D/DataTypes/List.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import Collection from "./Collection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A doubly linked list
|
||||||
|
*/
|
||||||
|
export default class List<T> implements Collection {
|
||||||
|
|
||||||
|
private head: ListItem<T>;
|
||||||
|
private tail: ListItem<T>;
|
||||||
|
private _size: number;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
this.head = null;
|
||||||
|
this.tail = null;
|
||||||
|
this._size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size(): number {
|
||||||
|
return this._size;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(value: T){
|
||||||
|
if(this._size === 0){
|
||||||
|
// There were no items in the list previously, so head and tail are the same
|
||||||
|
this.head = new ListItem(value, null, null);
|
||||||
|
this.tail = this.head;
|
||||||
|
} else {
|
||||||
|
this.tail.next = new ListItem(value, this.tail, null);
|
||||||
|
this.tail = this.tail.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the size
|
||||||
|
this._size += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(func: Function): void {
|
||||||
|
let p = this.head;
|
||||||
|
|
||||||
|
while(p !== null){
|
||||||
|
func(p.value);
|
||||||
|
p = p.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.head = null
|
||||||
|
this.tail = null
|
||||||
|
this._size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListItem<T> {
|
||||||
|
value: T;
|
||||||
|
next: ListItem<T>;
|
||||||
|
prev: ListItem<T>;
|
||||||
|
|
||||||
|
constructor(value: T, next: ListItem<T>, prev: ListItem<T>){
|
||||||
|
this.value = value;
|
||||||
|
this.next = next;
|
||||||
|
this.prev = prev;
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,6 @@ export default abstract class State implements Updateable {
|
||||||
* @param stateName The name of the state to transition to
|
* @param stateName The name of the state to transition to
|
||||||
*/
|
*/
|
||||||
protected finished(stateName: string): void {
|
protected finished(stateName: string): void {
|
||||||
console.log("Finished");
|
|
||||||
this.parent.changeState(stateName);
|
this.parent.changeState(stateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,26 @@ export default class EventQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribes the specified receiver from all events, or from whatever events are provided
|
||||||
|
* @param receiver The receiver to unsubscribe
|
||||||
|
* @param keys The events to unsubscribe from. If none are provided, unsubscribe from all
|
||||||
|
*/
|
||||||
|
unsubscribe(receiver: Receiver, ...events: Array<string>): void {
|
||||||
|
this.receivers.forEach(eventName => {
|
||||||
|
// If keys were provided, only continue if this key is one of them
|
||||||
|
if(events !== undefined && events.indexOf(eventName) === -1) return;
|
||||||
|
|
||||||
|
// Find the index of our receiver for this key
|
||||||
|
let index = this.receivers.get(eventName).indexOf(receiver);
|
||||||
|
|
||||||
|
// If an index was found, remove the receiver
|
||||||
|
if(index !== -1){
|
||||||
|
this.receivers.get(eventName).splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Associate the receiver and the type
|
// Associate the receiver and the type
|
||||||
private addListener(receiver: Receiver, type: string): void {
|
private addListener(receiver: Receiver, type: string): void {
|
||||||
if(this.receivers.has(type)){
|
if(this.receivers.has(type)){
|
||||||
|
|
|
@ -17,6 +17,10 @@ export default class Receiver {
|
||||||
this.MAX_SIZE = 100;
|
this.MAX_SIZE = 100;
|
||||||
this.q = new Queue(this.MAX_SIZE);
|
this.q = new Queue(this.MAX_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy(){
|
||||||
|
EventQueue.getInstance().unsubscribe(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds these types of events to this receiver's queue every update.
|
* Adds these types of events to this receiver's queue every update.
|
||||||
|
@ -24,6 +28,7 @@ export default class Receiver {
|
||||||
*/
|
*/
|
||||||
subscribe(eventTypes: string | Array<string>): void {
|
subscribe(eventTypes: string | Array<string>): void {
|
||||||
EventQueue.getInstance().subscribe(this, eventTypes);
|
EventQueue.getInstance().subscribe(this, eventTypes);
|
||||||
|
this.q.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,6 +28,9 @@ export default class Input {
|
||||||
|
|
||||||
private static keyMap: Map<Array<string>>;
|
private static keyMap: Map<Array<string>>;
|
||||||
|
|
||||||
|
private static keysDisabled: boolean;
|
||||||
|
private static mouseDisabled: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the Input object
|
* Initializes the Input object
|
||||||
* @param viewport A reference to the viewport of the game
|
* @param viewport A reference to the viewport of the game
|
||||||
|
@ -43,6 +46,8 @@ export default class Input {
|
||||||
Input.mousePressPosition = new Vec2(0, 0);
|
Input.mousePressPosition = new Vec2(0, 0);
|
||||||
Input.scrollDirection = 0;
|
Input.scrollDirection = 0;
|
||||||
Input.justScrolled = false;
|
Input.justScrolled = false;
|
||||||
|
Input.keysDisabled = false;
|
||||||
|
Input.mouseDisabled = false;
|
||||||
|
|
||||||
// Initialize the keymap
|
// Initialize the keymap
|
||||||
Input.keyMap = new Map();
|
Input.keyMap = new Map();
|
||||||
|
@ -132,6 +137,8 @@ export default class Input {
|
||||||
* @returns True if the key was just pressed, false otherwise
|
* @returns True if the key was just pressed, false otherwise
|
||||||
*/
|
*/
|
||||||
static isKeyJustPressed(key: string): boolean {
|
static isKeyJustPressed(key: string): boolean {
|
||||||
|
if(Input.keysDisabled) return false;
|
||||||
|
|
||||||
if(Input.keyJustPressed.has(key)){
|
if(Input.keyJustPressed.has(key)){
|
||||||
return Input.keyJustPressed.get(key)
|
return Input.keyJustPressed.get(key)
|
||||||
} else {
|
} else {
|
||||||
|
@ -145,6 +152,8 @@ export default class Input {
|
||||||
* @returns An array of all of the newly pressed keys.
|
* @returns An array of all of the newly pressed keys.
|
||||||
*/
|
*/
|
||||||
static getKeysJustPressed(): Array<string> {
|
static getKeysJustPressed(): Array<string> {
|
||||||
|
if(Input.keysDisabled) return [];
|
||||||
|
|
||||||
let keys = Array<string>();
|
let keys = Array<string>();
|
||||||
Input.keyJustPressed.forEach(key => {
|
Input.keyJustPressed.forEach(key => {
|
||||||
if(Input.keyJustPressed.get(key)){
|
if(Input.keyJustPressed.get(key)){
|
||||||
|
@ -160,6 +169,8 @@ export default class Input {
|
||||||
* @returns True if the key is currently pressed, false otherwise
|
* @returns True if the key is currently pressed, false otherwise
|
||||||
*/
|
*/
|
||||||
static isKeyPressed(key: string): boolean {
|
static isKeyPressed(key: string): boolean {
|
||||||
|
if(Input.keysDisabled) return false;
|
||||||
|
|
||||||
if(Input.keyPressed.has(key)){
|
if(Input.keyPressed.has(key)){
|
||||||
return Input.keyPressed.get(key)
|
return Input.keyPressed.get(key)
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,6 +200,8 @@ export default class Input {
|
||||||
* @returns True if the input was just pressed, false otherwise
|
* @returns True if the input was just pressed, false otherwise
|
||||||
*/
|
*/
|
||||||
static isJustPressed(inputName: string): boolean {
|
static isJustPressed(inputName: string): boolean {
|
||||||
|
if(Input.keysDisabled) return false;
|
||||||
|
|
||||||
if(Input.keyMap.has(inputName)){
|
if(Input.keyMap.has(inputName)){
|
||||||
const keys = Input.keyMap.get(inputName);
|
const keys = Input.keyMap.get(inputName);
|
||||||
let justPressed = false;
|
let justPressed = false;
|
||||||
|
@ -209,6 +222,8 @@ export default class Input {
|
||||||
* @returns True if the input is pressed, false otherwise
|
* @returns True if the input is pressed, false otherwise
|
||||||
*/
|
*/
|
||||||
static isPressed(inputName: string): boolean {
|
static isPressed(inputName: string): boolean {
|
||||||
|
if(Input.keysDisabled) return false;
|
||||||
|
|
||||||
if(Input.keyMap.has(inputName)){
|
if(Input.keyMap.has(inputName)){
|
||||||
const keys = Input.keyMap.get(inputName);
|
const keys = Input.keyMap.get(inputName);
|
||||||
let pressed = false;
|
let pressed = false;
|
||||||
|
@ -228,7 +243,7 @@ export default class Input {
|
||||||
* @returns True if the mouse was just pressed, false otherwise
|
* @returns True if the mouse was just pressed, false otherwise
|
||||||
*/
|
*/
|
||||||
static isMouseJustPressed(): boolean {
|
static isMouseJustPressed(): boolean {
|
||||||
return Input.mouseJustPressed;
|
return Input.mouseJustPressed && !Input.mouseDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,7 +251,7 @@ export default class Input {
|
||||||
* @returns True if the mouse is currently pressed, false otherwise
|
* @returns True if the mouse is currently pressed, false otherwise
|
||||||
*/
|
*/
|
||||||
static isMousePressed(): boolean {
|
static isMousePressed(): boolean {
|
||||||
return Input.mousePressed;
|
return Input.mousePressed && !Input.mouseDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -244,7 +259,7 @@ export default class Input {
|
||||||
* @returns True if the user just scrolled Input frame, false otherwise
|
* @returns True if the user just scrolled Input frame, false otherwise
|
||||||
*/
|
*/
|
||||||
static didJustScroll(): boolean {
|
static didJustScroll(): boolean {
|
||||||
return Input.justScrolled;
|
return Input.justScrolled && !Input.mouseDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -288,4 +303,20 @@ export default class Input {
|
||||||
static getGlobalMousePressPosition(): Vec2 {
|
static getGlobalMousePressPosition(): Vec2 {
|
||||||
return Input.mousePressPosition.clone().add(Input.viewport.getOrigin());
|
return Input.mousePressPosition.clone().add(Input.viewport.getOrigin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables all keypress and mouse click inputs
|
||||||
|
*/
|
||||||
|
static disableInput(): void {
|
||||||
|
Input.keysDisabled = true;
|
||||||
|
Input.mouseDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables all keypress and mouse click inputs
|
||||||
|
*/
|
||||||
|
static enableInput(): void {
|
||||||
|
Input.keysDisabled = false;
|
||||||
|
Input.mouseDisabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -45,6 +45,9 @@ export default class FixedUpdateGameLoop extends GameLoop {
|
||||||
|
|
||||||
/** The status of whether or not the game loop has started. */
|
/** The status of whether or not the game loop has started. */
|
||||||
private started: boolean;
|
private started: boolean;
|
||||||
|
|
||||||
|
/** The status of whether or not the game loop is paused */
|
||||||
|
private paused: boolean;
|
||||||
|
|
||||||
/** The status of whether or not the game loop is currently running. */
|
/** The status of whether or not the game loop is currently running. */
|
||||||
private running: boolean;
|
private running: boolean;
|
||||||
|
@ -65,6 +68,7 @@ export default class FixedUpdateGameLoop extends GameLoop {
|
||||||
this.lastFpsUpdate = 0;
|
this.lastFpsUpdate = 0;
|
||||||
this.framesSinceLastFpsUpdate = 0;
|
this.framesSinceLastFpsUpdate = 0;
|
||||||
this.started = false;
|
this.started = false;
|
||||||
|
this.paused = false;
|
||||||
this.running = false;
|
this.running = false;
|
||||||
this.numUpdateSteps = 0;
|
this.numUpdateSteps = 0;
|
||||||
}
|
}
|
||||||
|
@ -125,6 +129,14 @@ export default class FixedUpdateGameLoop extends GameLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pause(): void {
|
||||||
|
this.paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resume(): void {
|
||||||
|
this.paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first game frame - initializes the first frame time and begins the render
|
* The first game frame - initializes the first frame time and begins the render
|
||||||
* @param timestamp The current time in ms
|
* @param timestamp The current time in ms
|
||||||
|
@ -167,12 +179,17 @@ export default class FixedUpdateGameLoop extends GameLoop {
|
||||||
* @param timestamp The current time in ms
|
* @param timestamp The current time in ms
|
||||||
*/
|
*/
|
||||||
protected doFrame = (timestamp: number): void => {
|
protected doFrame = (timestamp: number): void => {
|
||||||
|
// If a pause was executed, stop doing the loop.
|
||||||
|
if(this.paused){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Request animation frame to prepare for another update or render
|
// Request animation frame to prepare for another update or render
|
||||||
window.requestAnimationFrame((t) => this.doFrame(t));
|
window.requestAnimationFrame((t) => this.doFrame(t));
|
||||||
|
|
||||||
// If we are trying to render too soon, do nothing.
|
// If we are trying to render too soon, do nothing.
|
||||||
if(timestamp < this.lastFrameTime + this.minFrameDelay){
|
if(timestamp < this.lastFrameTime + this.minFrameDelay){
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A frame is actually happening
|
// A frame is actually happening
|
||||||
|
|
|
@ -145,7 +145,7 @@ export default class Game {
|
||||||
this.resourceManager.loadResourcesFromQueue(() => {
|
this.resourceManager.loadResourcesFromQueue(() => {
|
||||||
// When we're done loading, start the loop
|
// When we're done loading, start the loop
|
||||||
console.log("Finished Preload - loading first scene");
|
console.log("Finished Preload - loading first scene");
|
||||||
this.sceneManager.addScene(InitialScene, options);
|
this.sceneManager.changeToScene(InitialScene, options);
|
||||||
this.loop.start();
|
this.loop.start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -155,48 +155,60 @@ export default class Game {
|
||||||
* @param deltaT The time sine the last update
|
* @param deltaT The time sine the last update
|
||||||
*/
|
*/
|
||||||
update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
// Handle all events that happened since the start of the last loop
|
try{
|
||||||
this.eventQueue.update(deltaT);
|
// Handle all events that happened since the start of the last loop
|
||||||
|
this.eventQueue.update(deltaT);
|
||||||
|
|
||||||
// Update the input data structures so game objects can see the input
|
// Update the input data structures so game objects can see the input
|
||||||
Input.update(deltaT);
|
Input.update(deltaT);
|
||||||
|
|
||||||
// Update the recording of the game
|
// Update the recording of the game
|
||||||
this.recorder.update(deltaT);
|
this.recorder.update(deltaT);
|
||||||
|
|
||||||
// Update all scenes
|
// Update all scenes
|
||||||
this.sceneManager.update(deltaT);
|
this.sceneManager.update(deltaT);
|
||||||
|
|
||||||
// Update all sounds
|
// Update all sounds
|
||||||
this.audioManager.update(deltaT);
|
this.audioManager.update(deltaT);
|
||||||
|
|
||||||
// Load or unload any resources if needed
|
// Load or unload any resources if needed
|
||||||
this.resourceManager.update(deltaT);
|
this.resourceManager.update(deltaT);
|
||||||
|
} catch(e){
|
||||||
|
this.loop.pause();
|
||||||
|
console.warn("Uncaught Error in Update - Crashing gracefully");
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the canvas and defers scene rendering to the sceneManager. Renders the debug canvas
|
* Clears the canvas and defers scene rendering to the sceneManager. Renders the debug canvas
|
||||||
*/
|
*/
|
||||||
render(): void {
|
render(): void {
|
||||||
// Clear the canvases
|
try{
|
||||||
Debug.clearCanvas();
|
// Clear the canvases
|
||||||
|
Debug.clearCanvas();
|
||||||
|
|
||||||
this.renderingManager.clear(this.clearColor);
|
this.renderingManager.clear(this.clearColor);
|
||||||
|
|
||||||
this.sceneManager.render();
|
this.sceneManager.render();
|
||||||
|
|
||||||
// Hacky debug mode
|
// Hacky debug mode
|
||||||
if(Input.isKeyJustPressed("g")){
|
if(Input.isKeyJustPressed("g")){
|
||||||
this.showDebug = !this.showDebug;
|
this.showDebug = !this.showDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug render
|
// Debug render
|
||||||
if(this.showDebug){
|
if(this.showDebug){
|
||||||
Debug.render();
|
Debug.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.showStats){
|
if(this.showStats){
|
||||||
Stats.render();
|
Stats.render();
|
||||||
|
}
|
||||||
|
} catch(e){
|
||||||
|
this.loop.pause();
|
||||||
|
console.warn("Uncaught Error in Render - Crashing gracefully");
|
||||||
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -32,6 +32,16 @@ export default abstract class GameLoop {
|
||||||
*/
|
*/
|
||||||
abstract start(): void;
|
abstract start(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the game loop, usually for an error condition.
|
||||||
|
*/
|
||||||
|
abstract pause(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes the game loop.
|
||||||
|
*/
|
||||||
|
abstract resume(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the first frame of the game. No update occurs here, only a render.
|
* Runs the first frame of the game. No update occurs here, only a render.
|
||||||
* This is needed to initialize delta time values
|
* This is needed to initialize delta time values
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class CanvasNode extends GameNode implements Region {
|
||||||
private _boundary: AABB;
|
private _boundary: AABB;
|
||||||
private _hasCustomShader: boolean;
|
private _hasCustomShader: boolean;
|
||||||
private _customShaderKey: string;
|
private _customShaderKey: string;
|
||||||
|
private _alpha: number;
|
||||||
|
|
||||||
/** A flag for whether or not the CanvasNode is visible */
|
/** A flag for whether or not the CanvasNode is visible */
|
||||||
visible: boolean = true;
|
visible: boolean = true;
|
||||||
|
@ -30,6 +31,14 @@ export default abstract class CanvasNode extends GameNode implements Region {
|
||||||
this._hasCustomShader = false;
|
this._hasCustomShader = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get alpha(): number {
|
||||||
|
return this._alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
set alpha(a: number) {
|
||||||
|
this._alpha = a;
|
||||||
|
}
|
||||||
|
|
||||||
get size(): Vec2 {
|
get size(): Vec2 {
|
||||||
return this._size;
|
return this._size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,9 @@ import Updateable from "../DataTypes/Interfaces/Updateable";
|
||||||
import DebugRenderable from "../DataTypes/Interfaces/DebugRenderable";
|
import DebugRenderable from "../DataTypes/Interfaces/DebugRenderable";
|
||||||
import Actor from "../DataTypes/Interfaces/Actor";
|
import Actor from "../DataTypes/Interfaces/Actor";
|
||||||
import Shape from "../DataTypes/Shapes/Shape";
|
import Shape from "../DataTypes/Shapes/Shape";
|
||||||
import Map from "../DataTypes/Map";
|
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import NavigationPath from "../Pathfinding/NavigationPath";
|
import NavigationPath from "../Pathfinding/NavigationPath";
|
||||||
import TweenManager from "../Rendering/Animations/TweenManager";
|
import TweenController from "../Rendering/Animations/TweenController";
|
||||||
import Debug from "../Debug/Debug";
|
import Debug from "../Debug/Debug";
|
||||||
import Color from "../Utils/Color";
|
import Color from "../Utils/Color";
|
||||||
import Circle from "../DataTypes/Shapes/Circle";
|
import Circle from "../DataTypes/Shapes/Circle";
|
||||||
|
@ -43,19 +42,19 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
isStatic: boolean;
|
isStatic: boolean;
|
||||||
isCollidable: boolean;
|
isCollidable: boolean;
|
||||||
isTrigger: boolean;
|
isTrigger: boolean;
|
||||||
group: string;
|
triggerMask: number;
|
||||||
triggers: Map<string>;
|
triggerEnters: Array<string>;
|
||||||
|
triggerExits: Array<string>;
|
||||||
_velocity: Vec2;
|
_velocity: Vec2;
|
||||||
sweptRect: AABB;
|
sweptRect: AABB;
|
||||||
collidedWithTilemap: boolean;
|
collidedWithTilemap: boolean;
|
||||||
physicsLayer: number;
|
group: number;
|
||||||
isPlayer: boolean;
|
isPlayer: boolean;
|
||||||
isColliding: boolean = false;
|
isColliding: boolean = false;
|
||||||
|
|
||||||
/*---------- ACTOR ----------*/
|
/*---------- ACTOR ----------*/
|
||||||
_ai: AI;
|
_ai: AI;
|
||||||
aiActive: boolean;
|
aiActive: boolean;
|
||||||
actorId: number;
|
|
||||||
path: NavigationPath;
|
path: NavigationPath;
|
||||||
pathfinding: boolean = false;
|
pathfinding: boolean = false;
|
||||||
|
|
||||||
|
@ -69,11 +68,13 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
/** The visual layer this GameNode resides in. */
|
/** The visual layer this GameNode resides in. */
|
||||||
protected layer: Layer;
|
protected layer: Layer;
|
||||||
/** A utility that allows the use of tweens on this GameNode */
|
/** A utility that allows the use of tweens on this GameNode */
|
||||||
tweens: TweenManager;
|
tweens: TweenController;
|
||||||
/** A tweenable property for rotation. Does not affect the bounding box of this GameNode - Only rendering. */
|
/** A tweenable property for rotation. Does not affect the bounding box of this GameNode - Only rendering. */
|
||||||
rotation: number;
|
rotation: number;
|
||||||
/** The opacity value of this GameNode */
|
/** The opacity value of this GameNode */
|
||||||
alpha: number;
|
abstract set alpha(a: number);
|
||||||
|
|
||||||
|
abstract get alpha(): number;
|
||||||
|
|
||||||
// Constructor docs are ignored, as the user should NOT create new GameNodes with a raw constructor
|
// Constructor docs are ignored, as the user should NOT create new GameNodes with a raw constructor
|
||||||
constructor(){
|
constructor(){
|
||||||
|
@ -81,9 +82,27 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
this._position.setOnChange(() => this.positionChanged());
|
this._position.setOnChange(() => this.positionChanged());
|
||||||
this.receiver = new Receiver();
|
this.receiver = new Receiver();
|
||||||
this.emitter = new Emitter();
|
this.emitter = new Emitter();
|
||||||
this.tweens = new TweenManager(this);
|
this.tweens = new TweenController(this);
|
||||||
this.rotation = 0;
|
this.rotation = 0;
|
||||||
this.alpha = 1;
|
}
|
||||||
|
|
||||||
|
destroy(){
|
||||||
|
this.tweens.destroy();
|
||||||
|
this.receiver.destroy();
|
||||||
|
|
||||||
|
if(this.hasPhysics){
|
||||||
|
this.removePhysics();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this._ai){
|
||||||
|
this._ai.destroy();
|
||||||
|
delete this._ai;
|
||||||
|
this.scene.getAIManager().removeActor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene.remove(this);
|
||||||
|
|
||||||
|
this.layer.removeNode(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------- POSITIONED ----------*/
|
/*---------- POSITIONED ----------*/
|
||||||
|
@ -176,12 +195,13 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
this.isCollidable = isCollidable;
|
this.isCollidable = isCollidable;
|
||||||
this.isStatic = isStatic;
|
this.isStatic = isStatic;
|
||||||
this.isTrigger = false;
|
this.isTrigger = false;
|
||||||
this.group = "";
|
this.triggerMask = 0;
|
||||||
this.triggers = new Map();
|
this.triggerEnters = new Array(32);
|
||||||
|
this.triggerExits = new Array(32);
|
||||||
this._velocity = Vec2.ZERO;
|
this._velocity = Vec2.ZERO;
|
||||||
this.sweptRect = new AABB();
|
this.sweptRect = new AABB();
|
||||||
this.collidedWithTilemap = false;
|
this.collidedWithTilemap = false;
|
||||||
this.physicsLayer = -1;
|
this.group = -1; // The default group, collides with everything
|
||||||
|
|
||||||
// Set the collision shape if provided, or simply use the the region if there is one.
|
// Set the collision shape if provided, or simply use the the region if there is one.
|
||||||
if(collisionShape){
|
if(collisionShape){
|
||||||
|
@ -208,6 +228,43 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
this.scene.getPhysicsManager().registerObject(this);
|
this.scene.getPhysicsManager().registerObject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Removes this object from the physics system */
|
||||||
|
removePhysics(): void {
|
||||||
|
// Nullify all physics fields
|
||||||
|
this.hasPhysics = false;
|
||||||
|
this.moving = false;
|
||||||
|
this.onGround = false;
|
||||||
|
this.onWall = false;
|
||||||
|
this.onCeiling = false;
|
||||||
|
this.active = false;
|
||||||
|
this.isCollidable = false;
|
||||||
|
this.isStatic = false;
|
||||||
|
this.isTrigger = false;
|
||||||
|
this.triggerMask = 0;
|
||||||
|
this.triggerEnters = null;
|
||||||
|
this.triggerExits = null;
|
||||||
|
this._velocity = Vec2.ZERO;
|
||||||
|
this.sweptRect = null;
|
||||||
|
this.collidedWithTilemap = false;
|
||||||
|
this.group = -1;
|
||||||
|
this.collisionShape = null;
|
||||||
|
this.colliderOffset = Vec2.ZERO;
|
||||||
|
this.sweptRect = null;
|
||||||
|
|
||||||
|
// Remove this from the physics manager
|
||||||
|
this.scene.getPhysicsManager().deregisterObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prevents this object from participating in all collisions and triggers. It can still move. */
|
||||||
|
disablePhysics(): void {
|
||||||
|
this.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enables this object to participate in collisions and triggers. This is only necessary if disablePhysics was called */
|
||||||
|
enablePhysics(): void {
|
||||||
|
this.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the collider for this GameNode
|
* Sets the collider for this GameNode
|
||||||
* @param collider The new collider to use
|
* @param collider The new collider to use
|
||||||
|
@ -219,20 +276,40 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
|
|
||||||
// @implemented
|
// @implemented
|
||||||
/**
|
/**
|
||||||
* @param group The name of the group that will activate the trigger
|
* Sets this object to be a trigger for a specific group
|
||||||
* @param eventType The type of this event to send when this trigger is activated
|
* @param group The name of the group that activates the trigger
|
||||||
*/
|
* @param onEnter The name of the event to send when this trigger is activated
|
||||||
addTrigger(group: string, eventType: string): void {
|
* @param onExit The name of the event to send when this trigger stops being activated
|
||||||
|
*/
|
||||||
|
setTrigger(group: string, onEnter: string, onExit: string): void {
|
||||||
|
// Make this object a trigger
|
||||||
this.isTrigger = true;
|
this.isTrigger = true;
|
||||||
this.triggers.add(group, eventType);
|
|
||||||
|
// Get the number of the physics layer
|
||||||
|
let layerNumber = this.scene.getPhysicsManager().getGroupNumber(group);
|
||||||
|
|
||||||
|
if(layerNumber === 0){
|
||||||
|
console.warn(`Trigger for GameNode ${this.id} not set - group "${group}" was not recognized by the physics manager.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this to the trigger mask
|
||||||
|
this.triggerMask |= layerNumber;
|
||||||
|
|
||||||
|
// Layer numbers are bits, so get which bit it is
|
||||||
|
let index = Math.log2(layerNumber);
|
||||||
|
|
||||||
|
// Set the event names
|
||||||
|
this.triggerEnters[index] = onEnter;
|
||||||
|
this.triggerExits[index] = onExit;
|
||||||
};
|
};
|
||||||
|
|
||||||
// @implemented
|
// @implemented
|
||||||
/**
|
/**
|
||||||
* @param layer The physics layer this node should belong to
|
* @param group The physics group this node should belong to
|
||||||
*/
|
*/
|
||||||
setPhysicsLayer(layer: string): void {
|
setGroup(group: string): void {
|
||||||
this.scene.getPhysicsManager().setLayer(this, layer);
|
this.scene.getPhysicsManager().setGroup(this, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @implemened
|
// @implemened
|
||||||
|
@ -347,9 +424,6 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
||||||
while(this.receiver.hasNextEvent()){
|
while(this.receiver.hasNextEvent()){
|
||||||
this._ai.handleEvent(this.receiver.getNextEvent());
|
this._ai.handleEvent(this.receiver.getNextEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our tweens
|
|
||||||
this.tweens.update(deltaT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @implemented
|
// @implemented
|
||||||
|
|
|
@ -13,6 +13,14 @@ export default abstract class Graphic extends CanvasNode {
|
||||||
this.color = Color.RED;
|
this.color = Color.RED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get alpha(): number {
|
||||||
|
return this.color.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
set alpha(a: number) {
|
||||||
|
this.color.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
// @deprecated
|
// @deprecated
|
||||||
/**
|
/**
|
||||||
* Sets the color of the Graphic. DEPRECATED
|
* Sets the color of the Graphic. DEPRECATED
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Vec2 from "../DataTypes/Vec2";
|
||||||
import Tileset from "../DataTypes/Tilesets/Tileset";
|
import Tileset from "../DataTypes/Tilesets/Tileset";
|
||||||
import { TiledTilemapData, TiledLayerData } from "../DataTypes/Tilesets/TiledData"
|
import { TiledTilemapData, TiledLayerData } from "../DataTypes/Tilesets/TiledData"
|
||||||
import CanvasNode from "./CanvasNode";
|
import CanvasNode from "./CanvasNode";
|
||||||
|
import PhysicsManager from "../Physics/PhysicsManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The representation of a tilemap - this can consist of a combination of tilesets in one layer
|
* The representation of a tilemap - this can consist of a combination of tilesets in one layer
|
||||||
|
@ -74,6 +75,9 @@ export default abstract class Tilemap extends CanvasNode {
|
||||||
* Adds this tilemap to the physics system
|
* Adds this tilemap to the physics system
|
||||||
*/
|
*/
|
||||||
addPhysics(): void {
|
addPhysics(): void {
|
||||||
|
this.hasPhysics = true;
|
||||||
|
this.active = true;
|
||||||
|
this.group = -1;
|
||||||
this.scene.getPhysicsManager().registerTilemap(this);
|
this.scene.getPhysicsManager().registerTilemap(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,16 +162,27 @@ export default class OrthogonalTilemap extends Tilemap {
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
debugRender(){
|
debugRender(){
|
||||||
let tileSize = this.getTileSizeWithZoom();
|
// Half of the tile size
|
||||||
let origin = this.relativePosition.sub(this.sizeWithZoom);
|
let zoomedHalfTileSize = this.getTileSizeWithZoom().scaled(0.5);
|
||||||
|
let halfTileSize = this.getTileSize().scaled(0.5);
|
||||||
|
|
||||||
|
// The center of the top left tile
|
||||||
|
let topLeft = this.position.clone().sub(this.size.scaled(0.5));
|
||||||
|
|
||||||
|
// A vec to store the center
|
||||||
|
let center = Vec2.ZERO;
|
||||||
|
|
||||||
for(let col = 0; col < this.numCols; col++){
|
for(let col = 0; col < this.numCols; col++){
|
||||||
|
// Calculate the x-position
|
||||||
|
center.x = topLeft.x + col*2*halfTileSize.x + halfTileSize.x;
|
||||||
|
|
||||||
for(let row = 0; row < this.numRows; row++){
|
for(let row = 0; row < this.numRows; row++){
|
||||||
if(this.isCollidable && this.isTileCollidable(col, row)){
|
if(this.isCollidable && this.isTileCollidable(col, row)){
|
||||||
// Draw a box for this tile
|
// Calculate the y-position
|
||||||
let center = new Vec2(origin.x + (col + 0.5)*tileSize.x, origin.y + (row + 0.5)*tileSize.y);
|
center.y = topLeft.y + row*2*halfTileSize.y + halfTileSize.y;
|
||||||
|
|
||||||
Debug.drawBox(center, tileSize.scaled(0.5), false, Color.BLUE);
|
// Draw a box for this tile
|
||||||
|
Debug.drawBox(this.inRelativeCoordinates(center), zoomedHalfTileSize, false, Color.BLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ export default class Label extends UIElement{
|
||||||
/** The value of the text of this UIElement */
|
/** The value of the text of this UIElement */
|
||||||
text: string;
|
text: string;
|
||||||
/** The name of the font */
|
/** The name of the font */
|
||||||
protected font: string;
|
font: string;
|
||||||
/** The size of the font */
|
/** The size of the font */
|
||||||
protected fontSize: number;
|
fontSize: number;
|
||||||
/** The horizontal alignment of the text within the label */
|
/** The horizontal alignment of the text within the label */
|
||||||
protected hAlign: string;
|
protected hAlign: string;
|
||||||
/** The vertical alignment of text within the label */
|
/** The vertical alignment of text within the label */
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Vec2 from "../DataTypes/Vec2";
|
||||||
import AABB from "../DataTypes/Shapes/AABB";
|
import AABB from "../DataTypes/Shapes/AABB";
|
||||||
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
import AreaCollision from "../DataTypes/Physics/AreaCollision";
|
import AreaCollision from "../DataTypes/Physics/AreaCollision";
|
||||||
|
import Unique from "../DataTypes/Interfaces/Unique";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ALGORITHM:
|
* ALGORITHM:
|
||||||
|
@ -52,15 +53,50 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
/** The array of tilemaps */
|
/** The array of tilemaps */
|
||||||
protected tilemaps: Array<Tilemap>;
|
protected tilemaps: Array<Tilemap>;
|
||||||
|
|
||||||
|
/** An array of the collision masks for each group */
|
||||||
|
protected collisionMasks: Array<number>;
|
||||||
|
|
||||||
constructor(options: Record<string, any>){
|
constructor(options: Record<string, any>){
|
||||||
super();
|
super();
|
||||||
this.staticNodes = new Array();
|
this.staticNodes = new Array();
|
||||||
this.dynamicNodes = new Array();
|
this.dynamicNodes = new Array();
|
||||||
this.tilemaps = new Array();
|
this.tilemaps = new Array();
|
||||||
|
this.collisionMasks = new Array(32);
|
||||||
|
|
||||||
|
// Parse options
|
||||||
|
this.parseOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the options for constructing the physics manager
|
||||||
|
* @param options A record of options
|
||||||
|
*/
|
||||||
|
protected parseOptions(options: Record<string, any>): void {
|
||||||
|
console.log("Parsing physics options: ", options);
|
||||||
|
if(options.groupNames !== undefined && options.collisions !== undefined){
|
||||||
|
for(let i = 0; i < options.groupNames.length; i++){
|
||||||
|
let group = options.groupNames[i];
|
||||||
|
|
||||||
|
// Register the group name and number
|
||||||
|
this.groupNames[i] = group;
|
||||||
|
|
||||||
|
this.groupMap.set(group, 1 << i);
|
||||||
|
|
||||||
|
let collisionMask = 0;
|
||||||
|
|
||||||
|
for(let j = 0; j < options.collisions[i].length; j++){
|
||||||
|
if(options.collisions[i][j]){
|
||||||
|
collisionMask |= 1 << j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.collisionMasks[i] = collisionMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
registerObject(node: GameNode): void {
|
registerObject(node: Physical): void {
|
||||||
if(node.isStatic){
|
if(node.isStatic){
|
||||||
// Static and not collidable
|
// Static and not collidable
|
||||||
this.staticNodes.push(node);
|
this.staticNodes.push(node);
|
||||||
|
@ -70,14 +106,28 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
|
deregisterObject(node: Physical): void {
|
||||||
|
if(node.isStatic){
|
||||||
|
// Remove the node from the static list
|
||||||
|
const index = this.staticNodes.indexOf(node);
|
||||||
|
this.staticNodes.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
// Remove the node from the dynamic list
|
||||||
|
const index = this.dynamicNodes.indexOf(node);
|
||||||
|
this.dynamicNodes.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
registerTilemap(tilemap: Tilemap): void {
|
registerTilemap(tilemap: Tilemap): void {
|
||||||
this.tilemaps.push(tilemap);
|
this.tilemaps.push(tilemap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
setLayer(node: GameNode, layer: string): void {
|
deregisterTilemap(tilemap: Tilemap): void {
|
||||||
node.physicsLayer = this.layerMap.get(layer);
|
const index = this.tilemaps.indexOf(tilemap);
|
||||||
|
this.tilemaps.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
|
@ -91,6 +141,11 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
node.collidedWithTilemap = false;
|
node.collidedWithTilemap = false;
|
||||||
node.isColliding = false;
|
node.isColliding = false;
|
||||||
|
|
||||||
|
// If this node is not active, don't process it
|
||||||
|
if(!node.active){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the swept shapes of each node
|
// Update the swept shapes of each node
|
||||||
if(node.moving){
|
if(node.moving){
|
||||||
// If moving, reflect that in the swept shape
|
// If moving, reflect that in the swept shape
|
||||||
|
@ -105,8 +160,13 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
// Gather a set of overlaps
|
// Gather a set of overlaps
|
||||||
let overlaps = new Array<AreaCollision>();
|
let overlaps = new Array<AreaCollision>();
|
||||||
|
|
||||||
|
let groupIndex = Math.log2(node.group);
|
||||||
|
|
||||||
// First, check this node against every static node (order doesn't actually matter here, since we sort anyways)
|
// First, check this node against every static node (order doesn't actually matter here, since we sort anyways)
|
||||||
for(let other of this.staticNodes){
|
for(let other of this.staticNodes){
|
||||||
|
// Ignore inactive nodes
|
||||||
|
if(!other.active) continue;
|
||||||
|
|
||||||
let collider = other.collisionShape.getBoundingRect();
|
let collider = other.collisionShape.getBoundingRect();
|
||||||
let area = node.sweptRect.overlapArea(collider);
|
let area = node.sweptRect.overlapArea(collider);
|
||||||
if(area > 0){
|
if(area > 0){
|
||||||
|
@ -117,6 +177,12 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
|
|
||||||
// Then, check it against every dynamic node
|
// Then, check it against every dynamic node
|
||||||
for(let other of this.dynamicNodes){
|
for(let other of this.dynamicNodes){
|
||||||
|
// Ignore ourselves
|
||||||
|
if(node === other) continue;
|
||||||
|
|
||||||
|
// Ignore inactive nodes
|
||||||
|
if(!other.active) continue;
|
||||||
|
|
||||||
let collider = other.collisionShape.getBoundingRect();
|
let collider = other.collisionShape.getBoundingRect();
|
||||||
let area = node.sweptRect.overlapArea(collider);
|
let area = node.sweptRect.overlapArea(collider);
|
||||||
if(area > 0){
|
if(area > 0){
|
||||||
|
@ -128,6 +194,9 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
// Lastly, gather a set of AABBs from the tilemap.
|
// Lastly, gather a set of AABBs from the tilemap.
|
||||||
// This step involves the most extra work, so it is abstracted into a method
|
// This step involves the most extra work, so it is abstracted into a method
|
||||||
for(let tilemap of this.tilemaps){
|
for(let tilemap of this.tilemaps){
|
||||||
|
// Ignore inactive tilemaps
|
||||||
|
if(!tilemap.active) continue;
|
||||||
|
|
||||||
if(tilemap instanceof OrthogonalTilemap){
|
if(tilemap instanceof OrthogonalTilemap){
|
||||||
this.collideWithOrthogonalTilemap(node, tilemap, overlaps);
|
this.collideWithOrthogonalTilemap(node, tilemap, overlaps);
|
||||||
}
|
}
|
||||||
|
@ -142,6 +211,9 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
/*---------- RESOLUTION PHASE ----------*/
|
/*---------- RESOLUTION PHASE ----------*/
|
||||||
// For every overlap, determine if we need to collide with it and when
|
// For every overlap, determine if we need to collide with it and when
|
||||||
for(let overlap of overlaps){
|
for(let overlap of overlaps){
|
||||||
|
// Ignore nodes we don't interact with
|
||||||
|
if((this.collisionMasks[groupIndex] & overlap.other.group) === 0) continue;
|
||||||
|
|
||||||
// Do a swept line test on the static AABB with this AABB size as padding (this is basically using a minkowski sum!)
|
// Do a swept line test on the static AABB with this AABB size as padding (this is basically using a minkowski sum!)
|
||||||
// Start the sweep at the position of this node with a delta of _velocity
|
// Start the sweep at the position of this node with a delta of _velocity
|
||||||
const point = node.collisionShape.center;
|
const point = node.collisionShape.center;
|
||||||
|
@ -188,21 +260,46 @@ export default class BasicPhysicsManager extends PhysicsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*---------- INFORMATION/TRIGGER PHASE ----------*/
|
||||||
// Check if we ended up on the ground, ceiling or wall
|
// Check if we ended up on the ground, ceiling or wall
|
||||||
|
// Also check for triggers
|
||||||
for(let overlap of overlaps){
|
for(let overlap of overlaps){
|
||||||
let collisionSide = overlap.collider.touchesAABBWithoutCorners(node.collisionShape.getBoundingRect());
|
// Check for a trigger. If we care about the trigger, react
|
||||||
if(collisionSide !== null){
|
if(overlap.other.isTrigger && (overlap.other.triggerMask & node.group)){
|
||||||
// If we touch, not including corner cases, check the collision normal
|
// Get the bit that this group is represented by
|
||||||
if(overlap.hit !== null){
|
let index = Math.floor(Math.log2(node.group));
|
||||||
if(collisionSide.y === -1){
|
|
||||||
// Node is on top of overlap, so onGround
|
// Extract the triggerEnter event name
|
||||||
node.onGround = true;
|
this.emitter.fireEvent(overlap.other.triggerEnters[index], {
|
||||||
} else if(collisionSide.y === 1){
|
node: (<GameNode>node).id,
|
||||||
// Node is on bottom of overlap, so onCeiling
|
other: (<GameNode>overlap.other).id
|
||||||
node.onCeiling = true;
|
});
|
||||||
} else {
|
}
|
||||||
// Node wasn't touching on y, so it is touching on x
|
|
||||||
node.onWall = true;
|
// Ignore collision sides for nodes we don't interact with
|
||||||
|
if((this.collisionMasks[groupIndex] & overlap.other.group) === 0) continue;
|
||||||
|
|
||||||
|
// Only check for direction if the overlap was collidable
|
||||||
|
if(overlap.type === "Tilemap" || overlap.other.isCollidable){
|
||||||
|
let collisionSide = overlap.collider.touchesAABBWithoutCorners(node.collisionShape.getBoundingRect());
|
||||||
|
if(collisionSide !== null){
|
||||||
|
// If we touch, not including corner cases, check the collision normal
|
||||||
|
if(overlap.hit !== null){
|
||||||
|
// If we hit a tilemap, keep track of it
|
||||||
|
if(overlap.type == "Tilemap"){
|
||||||
|
node.collidedWithTilemap = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(collisionSide.y === -1){
|
||||||
|
// Node is on top of overlap, so onGround
|
||||||
|
node.onGround = true;
|
||||||
|
} else if(collisionSide.y === 1){
|
||||||
|
// Node is on bottom of overlap, so onCeiling
|
||||||
|
node.onCeiling = true;
|
||||||
|
} else {
|
||||||
|
// Node wasn't touching on y, so it is touching on x
|
||||||
|
node.onWall = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import GameNode from "../Nodes/GameNode";
|
|
||||||
import Updateable from "../DataTypes/Interfaces/Updateable";
|
import Updateable from "../DataTypes/Interfaces/Updateable";
|
||||||
import Tilemap from "../Nodes/Tilemap";
|
import Tilemap from "../Nodes/Tilemap";
|
||||||
import Receiver from "../Events/Receiver";
|
import Receiver from "../Events/Receiver";
|
||||||
import Emitter from "../Events/Emitter";
|
import Emitter from "../Events/Emitter";
|
||||||
import Map from "../DataTypes/Map";
|
import Map from "../DataTypes/Map";
|
||||||
|
import Physical from "../DataTypes/Interfaces/Physical";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract physics manager.
|
* An abstract physics manager.
|
||||||
|
@ -16,25 +16,35 @@ export default abstract class PhysicsManager implements Updateable {
|
||||||
protected emitter: Emitter;
|
protected emitter: Emitter;
|
||||||
|
|
||||||
/** Maps layer names to numbers */
|
/** Maps layer names to numbers */
|
||||||
protected layerMap: Map<number>;
|
protected groupMap: Map<number>;
|
||||||
|
|
||||||
/** Maps layer numbers to names */
|
/** Maps layer numbers to names */
|
||||||
protected layerNames: Array<string>;
|
protected groupNames: Array<string>;
|
||||||
|
|
||||||
|
/** The default group name */
|
||||||
|
protected static readonly DEFAULT_GROUP = "Default";
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
this.receiver = new Receiver();
|
this.receiver = new Receiver();
|
||||||
this.emitter = new Emitter();
|
this.emitter = new Emitter();
|
||||||
|
|
||||||
// The creation and implementation of layers is deferred to the subclass
|
// The creation and implementation of layers is deferred to the subclass
|
||||||
this.layerMap = new Map();
|
this.groupMap = new Map();
|
||||||
this.layerNames = new Array();
|
this.groupNames = new Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a gamenode with this physics manager
|
* Registers a gamenode with this physics manager
|
||||||
* @param object The object to register
|
* @param object The object to register
|
||||||
*/
|
*/
|
||||||
abstract registerObject(object: GameNode): void;
|
abstract registerObject(object: Physical): void;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes references to this object from the physics managerr
|
||||||
|
* @param object The object to deregister
|
||||||
|
*/
|
||||||
|
abstract deregisterObject(object: Physical): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a tilemap with this physics manager
|
* Registers a tilemap with this physics manager
|
||||||
|
@ -42,14 +52,57 @@ export default abstract class PhysicsManager implements Updateable {
|
||||||
*/
|
*/
|
||||||
abstract registerTilemap(tilemap: Tilemap): void;
|
abstract registerTilemap(tilemap: Tilemap): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes references to this tilemap from the physics managerr
|
||||||
|
* @param tilemap The object to deregister
|
||||||
|
*/
|
||||||
|
abstract deregisterTilemap(tilemap: Tilemap): void;
|
||||||
|
|
||||||
abstract update(deltaT: number): void;
|
abstract update(deltaT: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the physics layer of the GameNode
|
* Sets the physics layer of the GameNode
|
||||||
* @param node The GameNode
|
* @param node The GameNode
|
||||||
* @param layer The layer that the GameNode should be on
|
* @param group The group that the GameNode should be on
|
||||||
*/
|
*/
|
||||||
setLayer(node: GameNode, layer: string): void {
|
setGroup(node: Physical, group: string): void {
|
||||||
node.physicsLayer = this.layerMap.get(layer);
|
node.group = this.groupMap.get(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the layer number associated with the provided name
|
||||||
|
* @param layer The name of the layer
|
||||||
|
* @returns The layer number, or 0 if there is not a layer with that name registered
|
||||||
|
*/
|
||||||
|
getGroupNumber(group: string): number {
|
||||||
|
if(this.groupMap.has(group)){
|
||||||
|
return this.groupMap.get(group);
|
||||||
|
} else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all group names associated with the number provided
|
||||||
|
* @param groups A mask of groups
|
||||||
|
* @returns All groups contained in the mask
|
||||||
|
*/
|
||||||
|
getGroupNames(groups: number): Array<string> {
|
||||||
|
if(groups === -1){
|
||||||
|
return [PhysicsManager.DEFAULT_GROUP];
|
||||||
|
} else {
|
||||||
|
let g = 1;
|
||||||
|
let names = [];
|
||||||
|
|
||||||
|
for(let i = 0; i < 32; i++){
|
||||||
|
if(g & groups){
|
||||||
|
// This group is in the groups number
|
||||||
|
names.push(this.groupNames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift the bit over
|
||||||
|
g = g << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
0
src/Wolfie2D/Registry/Registries/FontRegistry.ts
Normal file
0
src/Wolfie2D/Registry/Registries/FontRegistry.ts
Normal file
|
@ -80,7 +80,7 @@ export default class AnimationManager {
|
||||||
return this.animations.get(this.currentAnimation).frames[this.currentFrame].index;
|
return this.animations.get(this.currentAnimation).frames[this.currentFrame].index;
|
||||||
} else {
|
} else {
|
||||||
// No current animation, warn the user
|
// No current animation, warn the user
|
||||||
console.warn("Animation index was requested, but the current animation was invalid");
|
console.warn(`Animation index was requested, but the current animation: ${this.currentAnimation} was invalid`);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ export default class AnimationManager {
|
||||||
return index;
|
return index;
|
||||||
} else {
|
} else {
|
||||||
// No current animation, can't advance. Warn the user
|
// No current animation, can't advance. Warn the user
|
||||||
console.warn("Animation index and advance was requested, but the current animation was invalid");
|
console.warn(`Animation index and advance was requested, but the current animation (${this.currentAnimation}) in node with id: ${this.owner.id} was invalid`);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ export default class AnimationManager {
|
||||||
this.animationState = AnimationState.STOPPED;
|
this.animationState = AnimationState.STOPPED;
|
||||||
|
|
||||||
if(this.onEndEvent !== null){
|
if(this.onEndEvent !== null){
|
||||||
this.emitter.fireEvent(this.onEndEvent, {owner: this.owner, animation: this.currentAnimation});
|
this.emitter.fireEvent(this.onEndEvent, {owner: this.owner.id, animation: this.currentAnimation});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a pending animation, play it
|
// If there is a pending animation, play it
|
||||||
|
|
|
@ -15,6 +15,26 @@ export class AnimationData {
|
||||||
repeat: boolean = false;
|
repeat: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TweenEffect {
|
||||||
|
/** The property to tween */
|
||||||
|
property: TweenableProperties;
|
||||||
|
|
||||||
|
/** Whether or not the Tween should reset the property to its original value after playing */
|
||||||
|
resetOnComplete: boolean;
|
||||||
|
|
||||||
|
/** The starting value for the tween */
|
||||||
|
start: any;
|
||||||
|
|
||||||
|
/** The ending value for the tween */
|
||||||
|
end: any;
|
||||||
|
|
||||||
|
/** The ease function to use */
|
||||||
|
ease: EaseFunctionType;
|
||||||
|
|
||||||
|
/** DO NOT MODIFY - The original value of the property - set automatically */
|
||||||
|
initialValue: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class TweenData {
|
export class TweenData {
|
||||||
// Members for initialization by the user
|
// Members for initialization by the user
|
||||||
/** The amount of time in ms to wait before executing the tween */
|
/** The amount of time in ms to wait before executing the tween */
|
||||||
|
@ -22,18 +42,13 @@ export class TweenData {
|
||||||
/** The duration of time over which the value with change from start to end */
|
/** The duration of time over which the value with change from start to end */
|
||||||
duration: number;
|
duration: number;
|
||||||
/** An array of the effects on the properties of the object */
|
/** An array of the effects on the properties of the object */
|
||||||
effects: [{
|
effects: Array<TweenEffect>;
|
||||||
property: TweenableProperties;
|
|
||||||
resetOnComplete: boolean;
|
|
||||||
initialValue: number;
|
|
||||||
start: any;
|
|
||||||
end: any;
|
|
||||||
ease: EaseFunctionType;
|
|
||||||
}];
|
|
||||||
/** Whether or not this tween should reverse from end to start for each property when it finishes */
|
/** Whether or not this tween should reverse from end to start for each property when it finishes */
|
||||||
reverseOnComplete: boolean;
|
reverseOnComplete: boolean;
|
||||||
/** Whether or not this tween should loop when it completes */
|
/** Whether or not this tween should loop when it completes */
|
||||||
loop: boolean;
|
loop: boolean;
|
||||||
|
/** The name of the event to send (if any) when the tween finishes playing */
|
||||||
|
onEnd: string
|
||||||
|
|
||||||
// Members for management by the tween manager
|
// Members for management by the tween manager
|
||||||
/** The progress of this tween through its effects */
|
/** The progress of this tween through its effects */
|
||||||
|
|
213
src/Wolfie2D/Rendering/Animations/TweenController.ts
Normal file
213
src/Wolfie2D/Rendering/Animations/TweenController.ts
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
import Map from "../../DataTypes/Map";
|
||||||
|
import GameNode from "../../Nodes/GameNode";
|
||||||
|
import { AnimationState, TweenData } from "./AnimationTypes";
|
||||||
|
import EaseFunctions from "../../Utils/EaseFunctions";
|
||||||
|
import MathUtils from "../../Utils/MathUtils";
|
||||||
|
import TweenManager from "./TweenManager";
|
||||||
|
import Emitter from "../../Events/Emitter";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manager for the tweens of a GameNode.
|
||||||
|
* Tweens are short animations played by interpolating between two properties using an easing function.
|
||||||
|
* For a good visual representation of easing functions, check out @link(https://easings.net/)(https://easings.net/).
|
||||||
|
* Multiple tween can be played at the same time, as long as they don't change the same property.
|
||||||
|
* This allows for some interesting polishes or animations that may be very difficult to do with sprite work alone
|
||||||
|
* - especially pixel art (such as rotations or scaling).
|
||||||
|
*/
|
||||||
|
export default class TweenController {
|
||||||
|
/** The GameNode this TweenController acts upon */
|
||||||
|
protected owner: GameNode;
|
||||||
|
/** The list of created tweens */
|
||||||
|
protected tweens: Map<TweenData>;
|
||||||
|
/** An event emitter */
|
||||||
|
protected emitter: Emitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new TweenController
|
||||||
|
* @param owner The owner of the TweenController
|
||||||
|
*/
|
||||||
|
constructor(owner: GameNode){
|
||||||
|
this.owner = owner;
|
||||||
|
this.tweens = new Map();
|
||||||
|
this.emitter = new Emitter();
|
||||||
|
|
||||||
|
// Give ourselves to the TweenManager
|
||||||
|
TweenManager.getInstance().registerTweenController(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys this TweenController
|
||||||
|
*/
|
||||||
|
destroy(){
|
||||||
|
// Only the gamenode and the tween manager should have a reference to this
|
||||||
|
delete this.owner.tweens;
|
||||||
|
TweenManager.getInstance().deregisterTweenController(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a tween to this game node
|
||||||
|
* @param key The name of the tween
|
||||||
|
* @param tween The data of the tween
|
||||||
|
*/
|
||||||
|
add(key: string, tween: Record<string, any> | TweenData): void {
|
||||||
|
let typedTween = <TweenData>tween;
|
||||||
|
|
||||||
|
// Initialize members that we need (and the user didn't provide)
|
||||||
|
typedTween.progress = 0;
|
||||||
|
typedTween.elapsedTime = 0;
|
||||||
|
typedTween.animationState = AnimationState.STOPPED;
|
||||||
|
|
||||||
|
this.tweens.add(key, typedTween);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play a tween with a certain name
|
||||||
|
* @param key The name of the tween to play
|
||||||
|
* @param loop Whether or not the tween should loop
|
||||||
|
*/
|
||||||
|
play(key: string, loop?: boolean): void {
|
||||||
|
if(this.tweens.has(key)){
|
||||||
|
let tween = this.tweens.get(key);
|
||||||
|
|
||||||
|
// Set loop if needed
|
||||||
|
if(loop !== undefined){
|
||||||
|
tween.loop = loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the initial values
|
||||||
|
for(let effect of tween.effects){
|
||||||
|
if(effect.resetOnComplete){
|
||||||
|
effect.initialValue = this.owner[effect.property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the tween running
|
||||||
|
tween.animationState = AnimationState.PLAYING;
|
||||||
|
tween.elapsedTime = 0;
|
||||||
|
tween.progress = 0;
|
||||||
|
tween.reversing = false;
|
||||||
|
} else {
|
||||||
|
console.warn(`Tried to play tween "${key}" on node with id ${this.owner.id}, but no such tween exists`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses a playing tween. Does not affect tweens that are stopped.
|
||||||
|
* @param key The name of the tween to pause.
|
||||||
|
*/
|
||||||
|
pause(key: string): void {
|
||||||
|
if(this.tweens.has(key)){
|
||||||
|
this.tweens.get(key).animationState = AnimationState.PAUSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes a paused tween.
|
||||||
|
* @param key The name of the tween to resume
|
||||||
|
*/
|
||||||
|
resume(key: string): void {
|
||||||
|
if(this.tweens.has(key)){
|
||||||
|
let tween = this.tweens.get(key);
|
||||||
|
if(tween.animationState === AnimationState.PAUSED)
|
||||||
|
tween.animationState = AnimationState.PLAYING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops a currently playing tween
|
||||||
|
* @param key The key of the tween
|
||||||
|
*/
|
||||||
|
stop(key: string): void {
|
||||||
|
if(this.tweens.has(key)){
|
||||||
|
let tween = this.tweens.get(key);
|
||||||
|
tween.animationState = AnimationState.STOPPED;
|
||||||
|
|
||||||
|
// Return to the initial values
|
||||||
|
for(let effect of tween.effects){
|
||||||
|
if(effect.resetOnComplete){
|
||||||
|
this.owner[effect.property] = effect.initialValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The natural stop of a currently playing tween
|
||||||
|
* @param key The key of the tween
|
||||||
|
*/
|
||||||
|
protected end(key: string): void {
|
||||||
|
this.stop(key);
|
||||||
|
if(this.tweens.has(key)){
|
||||||
|
// Get the tween
|
||||||
|
let tween = this.tweens.get(key);
|
||||||
|
|
||||||
|
// If it has an onEnd, send an event
|
||||||
|
if(tween.onEnd){
|
||||||
|
this.emitter.fireEvent(tween.onEnd, {key: key, node: this.owner.id});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all currently playing tweens
|
||||||
|
*/
|
||||||
|
stopAll(): void {
|
||||||
|
this.tweens.forEach(key => this.stop(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
update(deltaT: number): void {
|
||||||
|
this.tweens.forEach(key => {
|
||||||
|
let tween = this.tweens.get(key);
|
||||||
|
if(tween.animationState === AnimationState.PLAYING){
|
||||||
|
// Update the progress of the tween
|
||||||
|
tween.elapsedTime += deltaT*1000;
|
||||||
|
|
||||||
|
// If we're past the startDelay, do the tween
|
||||||
|
if(tween.elapsedTime >= tween.startDelay){
|
||||||
|
if(!tween.reversing && tween.elapsedTime >= tween.startDelay + tween.duration){
|
||||||
|
// If we're over time, stop the tween, loop, or reverse
|
||||||
|
if(tween.reverseOnComplete){
|
||||||
|
// If we're over time and can reverse, do so
|
||||||
|
tween.reversing = true;
|
||||||
|
} else if(tween.loop){
|
||||||
|
// If we can't reverse and can loop, do so
|
||||||
|
tween.elapsedTime -= tween.duration;
|
||||||
|
} else {
|
||||||
|
// We aren't looping and can't reverse, so stop
|
||||||
|
this.end(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the end of reversing
|
||||||
|
if(tween.reversing && tween.elapsedTime >= tween.startDelay + 2*tween.duration){
|
||||||
|
if(tween.loop){
|
||||||
|
tween.reversing = false;
|
||||||
|
tween.elapsedTime -= 2*tween.duration;
|
||||||
|
} else {
|
||||||
|
this.end(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the progress, make sure it is between 0 and 1. Errors from this should never be large
|
||||||
|
if(tween.reversing){
|
||||||
|
tween.progress = MathUtils.clamp01((2*tween.duration - (tween.elapsedTime- tween.startDelay))/tween.duration);
|
||||||
|
} else {
|
||||||
|
tween.progress = MathUtils.clamp01((tween.elapsedTime - tween.startDelay)/tween.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let effect of tween.effects){
|
||||||
|
|
||||||
|
// Get the value from the ease function that corresponds to our progress
|
||||||
|
let ease = EaseFunctions[effect.ease](tween.progress);
|
||||||
|
|
||||||
|
// Use the value to lerp the property
|
||||||
|
let value = MathUtils.lerp(effect.start, effect.end, ease);
|
||||||
|
|
||||||
|
// Assign the value of the property
|
||||||
|
this.owner[effect.property] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,169 +1,40 @@
|
||||||
import Map from "../../DataTypes/Map";
|
import Updateable from "../../DataTypes/Interfaces/Updateable";
|
||||||
import GameNode from "../../Nodes/GameNode";
|
import TweenController from "./TweenController";
|
||||||
import { AnimationState, TweenData } from "./AnimationTypes";
|
|
||||||
import EaseFunctions from "../../Utils/EaseFunctions";
|
|
||||||
import MathUtils from "../../Utils/MathUtils";
|
|
||||||
|
|
||||||
/**
|
export default class TweenManager implements Updateable {
|
||||||
* A manager for the tweens of a GameNode.
|
|
||||||
* Tweens are short animations played by interpolating between two properties using an easing function.
|
|
||||||
* For a good visual representation of easing functions, check out @link(https://easings.net/)(https://easings.net/).
|
|
||||||
* Multiple tween can be played at the same time, as long as they don't change the same property.
|
|
||||||
* This allows for some interesting polishes or animations that may be very difficult to do with sprite work alone
|
|
||||||
* - especially pixel art (such as rotations or scaling).
|
|
||||||
*/
|
|
||||||
export default class TweenManager {
|
|
||||||
/** The GameNode this TweenManager acts upon */
|
|
||||||
protected owner: GameNode;
|
|
||||||
/** The list of created tweens */
|
|
||||||
protected tweens: Map<TweenData>;
|
|
||||||
|
|
||||||
/**
|
private static instance: TweenManager = null;
|
||||||
* Creates a new TweenManager
|
|
||||||
* @param owner The owner of the TweenManager
|
protected tweenControllers: Array<TweenController>;
|
||||||
*/
|
|
||||||
constructor(owner: GameNode){
|
private constructor(){
|
||||||
this.owner = owner;
|
this.tweenControllers = new Array();
|
||||||
this.tweens = new Map();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static getInstance(): TweenManager {
|
||||||
* Add a tween to this game node
|
if(TweenManager.instance === null){
|
||||||
* @param key The name of the tween
|
TweenManager.instance = new TweenManager();
|
||||||
* @param tween The data of the tween
|
|
||||||
*/
|
|
||||||
add(key: string, tween: Record<string, any> | TweenData): void {
|
|
||||||
let typedTween = <TweenData>tween;
|
|
||||||
|
|
||||||
// Initialize members that we need (and the user didn't provide)
|
|
||||||
typedTween.progress = 0;
|
|
||||||
typedTween.elapsedTime = 0;
|
|
||||||
typedTween.animationState = AnimationState.STOPPED;
|
|
||||||
|
|
||||||
this.tweens.add(key, typedTween);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Play a tween with a certain name
|
|
||||||
* @param key The name of the tween to play
|
|
||||||
* @param loop Whether or not the tween should loop
|
|
||||||
*/
|
|
||||||
play(key: string, loop?: boolean): void {
|
|
||||||
if(this.tweens.has(key)){
|
|
||||||
let tween = this.tweens.get(key);
|
|
||||||
|
|
||||||
// Set loop if needed
|
|
||||||
if(loop !== undefined){
|
|
||||||
tween.loop = loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the initial values
|
|
||||||
for(let effect of tween.effects){
|
|
||||||
if(effect.resetOnComplete){
|
|
||||||
effect.initialValue = this.owner[effect.property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the tween running
|
|
||||||
tween.animationState = AnimationState.PLAYING;
|
|
||||||
tween.elapsedTime = 0;
|
|
||||||
tween.progress = 0;
|
|
||||||
tween.reversing = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TweenManager.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
registerTweenController(controller: TweenController){
|
||||||
* Pauses a playing tween. Does not affect tweens that are stopped.
|
this.tweenControllers.push(controller);
|
||||||
* @param key The name of the tween to pause.
|
|
||||||
*/
|
|
||||||
pause(key: string): void {
|
|
||||||
if(this.tweens.has(key)){
|
|
||||||
this.tweens.get(key).animationState = AnimationState.PAUSED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
deregisterTweenController(controller: TweenController){
|
||||||
* Resumes a paused tween.
|
let index = this.tweenControllers.indexOf(controller);
|
||||||
* @param key The name of the tween to resume
|
this.tweenControllers.splice(index, 1);
|
||||||
*/
|
|
||||||
resume(key: string): void {
|
|
||||||
if(this.tweens.has(key)){
|
|
||||||
let tween = this.tweens.get(key);
|
|
||||||
if(tween.animationState === AnimationState.PAUSED)
|
|
||||||
tween.animationState = AnimationState.PLAYING;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
clearTweenControllers(){
|
||||||
* Stops a currently playing tween
|
this.tweenControllers = new Array();
|
||||||
* @param key The key of the tween
|
|
||||||
*/
|
|
||||||
stop(key: string): void {
|
|
||||||
if(this.tweens.has(key)){
|
|
||||||
let tween = this.tweens.get(key);
|
|
||||||
tween.animationState = AnimationState.STOPPED;
|
|
||||||
|
|
||||||
// Return to the initial values
|
|
||||||
for(let effect of tween.effects){
|
|
||||||
if(effect.resetOnComplete){
|
|
||||||
this.owner[effect.property] = effect.initialValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaT: number): void {
|
update(deltaT: number): void {
|
||||||
this.tweens.forEach(key => {
|
for(let tweenController of this.tweenControllers){
|
||||||
let tween = this.tweens.get(key);
|
tweenController.update(deltaT);
|
||||||
if(tween.animationState === AnimationState.PLAYING){
|
}
|
||||||
// Update the progress of the tween
|
|
||||||
tween.elapsedTime += deltaT*1000;
|
|
||||||
|
|
||||||
// If we're past the startDelay, do the tween
|
|
||||||
if(tween.elapsedTime >= tween.startDelay){
|
|
||||||
if(!tween.reversing && tween.elapsedTime >= tween.startDelay + tween.duration){
|
|
||||||
// If we're over time, stop the tween, loop, or reverse
|
|
||||||
if(tween.reverseOnComplete){
|
|
||||||
// If we're over time and can reverse, do so
|
|
||||||
tween.reversing = true;
|
|
||||||
} else if(tween.loop){
|
|
||||||
// If we can't reverse and can loop, do so
|
|
||||||
tween.elapsedTime -= tween.duration;
|
|
||||||
} else {
|
|
||||||
// We aren't looping and can't reverse, so stop
|
|
||||||
this.stop(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for the end of reversing
|
|
||||||
if(tween.reversing && tween.elapsedTime >= tween.startDelay + 2*tween.duration){
|
|
||||||
if(tween.loop){
|
|
||||||
tween.reversing = false;
|
|
||||||
tween.elapsedTime -= 2*tween.duration;
|
|
||||||
} else {
|
|
||||||
this.stop(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the progress, make sure it is between 0 and 1. Errors from this should never be large
|
|
||||||
if(tween.reversing){
|
|
||||||
tween.progress = MathUtils.clamp01((2*tween.duration - (tween.elapsedTime- tween.startDelay))/tween.duration);
|
|
||||||
} else {
|
|
||||||
tween.progress = MathUtils.clamp01((tween.elapsedTime - tween.startDelay)/tween.duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let effect of tween.effects){
|
|
||||||
// Get the value from the ease function that corresponds to our progress
|
|
||||||
let ease = EaseFunctions[effect.ease](tween.progress);
|
|
||||||
|
|
||||||
// Use the value to lerp the property
|
|
||||||
let value = MathUtils.lerp(effect.start, effect.end, ease);
|
|
||||||
|
|
||||||
// Assign the value of the property
|
|
||||||
this.owner[effect.property] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -153,7 +153,10 @@ export default class CanvasRenderer extends RenderingManager {
|
||||||
this.ctx.setTransform(xScale, 0, 0, yScale, (node.position.x - this.origin.x)*this.zoom, (node.position.y - this.origin.y)*this.zoom);
|
this.ctx.setTransform(xScale, 0, 0, yScale, (node.position.x - this.origin.x)*this.zoom, (node.position.y - this.origin.y)*this.zoom);
|
||||||
this.ctx.rotate(-node.rotation);
|
this.ctx.rotate(-node.rotation);
|
||||||
let globalAlpha = this.ctx.globalAlpha;
|
let globalAlpha = this.ctx.globalAlpha;
|
||||||
this.ctx.globalAlpha = ((<any>node).color ? (<any>node).color.a : 1) * node.alpha;
|
if(node instanceof Rect){
|
||||||
|
Debug.log("node" + node.id, "Node" + node.id + " Alpha: " + node.alpha);
|
||||||
|
}
|
||||||
|
this.ctx.globalAlpha = node.alpha;
|
||||||
|
|
||||||
if(node instanceof AnimatedSprite){
|
if(node instanceof AnimatedSprite){
|
||||||
this.renderAnimatedSprite(<AnimatedSprite>node);
|
this.renderAnimatedSprite(<AnimatedSprite>node);
|
||||||
|
|
|
@ -110,6 +110,14 @@ export default class TilemapFactory {
|
||||||
// Register tilemap with physics if it's collidable
|
// Register tilemap with physics if it's collidable
|
||||||
if(tilemap.isCollidable){
|
if(tilemap.isCollidable){
|
||||||
tilemap.addPhysics();
|
tilemap.addPhysics();
|
||||||
|
|
||||||
|
if(layer.properties){
|
||||||
|
for(let item of layer.properties){
|
||||||
|
if(item.name === "Group"){
|
||||||
|
tilemap.setGroup(item.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -150,6 +158,9 @@ export default class TilemapFactory {
|
||||||
let hasPhysics = false;
|
let hasPhysics = false;
|
||||||
let isCollidable = false;
|
let isCollidable = false;
|
||||||
let isTrigger = false;
|
let isTrigger = false;
|
||||||
|
let onEnter = null;
|
||||||
|
let onExit = null;
|
||||||
|
let triggerGroup = null;
|
||||||
let group = "";
|
let group = "";
|
||||||
|
|
||||||
if(obj.properties){
|
if(obj.properties){
|
||||||
|
@ -158,10 +169,16 @@ export default class TilemapFactory {
|
||||||
hasPhysics = prop.value;
|
hasPhysics = prop.value;
|
||||||
} else if(prop.name === "Collidable"){
|
} else if(prop.name === "Collidable"){
|
||||||
isCollidable = prop.value;
|
isCollidable = prop.value;
|
||||||
} else if(prop.name === "IsTrigger"){
|
|
||||||
isTrigger = prop.value;
|
|
||||||
} else if(prop.name === "Group"){
|
} else if(prop.name === "Group"){
|
||||||
group = prop.value;
|
group = prop.value;
|
||||||
|
} else if(prop.name === "IsTrigger"){
|
||||||
|
isTrigger = prop.value;
|
||||||
|
} else if(prop.name === "TriggerGroup"){
|
||||||
|
triggerGroup = prop.value;
|
||||||
|
} else if(prop.name === "TriggerOnEnter"){
|
||||||
|
onEnter = prop.value;
|
||||||
|
} else if(prop.name === "TriggerOnExit"){
|
||||||
|
onExit = prop.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,8 +216,10 @@ export default class TilemapFactory {
|
||||||
if(hasPhysics){
|
if(hasPhysics){
|
||||||
// Make the sprite a static physics object
|
// Make the sprite a static physics object
|
||||||
sprite.addPhysics(sprite.boundary.clone(), Vec2.ZERO, isCollidable, true);
|
sprite.addPhysics(sprite.boundary.clone(), Vec2.ZERO, isCollidable, true);
|
||||||
sprite.group = group;
|
sprite.setGroup(group);
|
||||||
sprite.isTrigger = isTrigger;
|
if(isTrigger && triggerGroup !== null){
|
||||||
|
sprite.setTrigger(triggerGroup, onEnter, onExit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,17 +164,14 @@ export default class Layer {
|
||||||
* @param node The node to remove
|
* @param node The node to remove
|
||||||
* @returns true if the node was removed, false otherwise
|
* @returns true if the node was removed, false otherwise
|
||||||
*/
|
*/
|
||||||
removeNode(node: GameNode): boolean {
|
removeNode(node: GameNode): void {
|
||||||
// Find and remove the node
|
// Find and remove the node
|
||||||
for(let i = 0; i < this.items.length; i++){
|
let index = this.items.indexOf(node);
|
||||||
if(this.items[i].id === node.id){
|
|
||||||
this.items.splice(i, 1);
|
|
||||||
node.setLayer(null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
if(index !== -1){
|
||||||
|
this.items.splice(index, 1);
|
||||||
|
node.setLayer(undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,6 +24,7 @@ import SceneOptions from "./SceneOptions";
|
||||||
import RenderingManager from "../Rendering/RenderingManager";
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
import Debug from "../Debug/Debug";
|
import Debug from "../Debug/Debug";
|
||||||
import TimerManager from "../Timing/TimerManager";
|
import TimerManager from "../Timing/TimerManager";
|
||||||
|
import TweenManager from "../Rendering/Animations/TweenManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scenes are the main container in the game engine.
|
* Scenes are the main container in the game engine.
|
||||||
|
@ -87,7 +88,7 @@ export default class Scene implements Updateable {
|
||||||
public sceneOptions: SceneOptions;
|
public sceneOptions: SceneOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Scene. To add a new Scene in your game, use addScene() in @reference[SceneManager]
|
* Creates a new Scene. To add a new Scene in your game, use changeToScene() in @reference[SceneManager]
|
||||||
* @param viewport The viewport of the game
|
* @param viewport The viewport of the game
|
||||||
* @param sceneManager The SceneManager that owns this Scene
|
* @param sceneManager The SceneManager that owns this Scene
|
||||||
* @param renderingManager The RenderingManager that will handle this Scene's rendering
|
* @param renderingManager The RenderingManager that will handle this Scene's rendering
|
||||||
|
@ -95,7 +96,7 @@ export default class Scene implements Updateable {
|
||||||
* @param options The options for Scene initialization
|
* @param options The options for Scene initialization
|
||||||
*/
|
*/
|
||||||
constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, options: Record<string, any>){
|
constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, options: Record<string, any>){
|
||||||
this.sceneOptions = SceneOptions.parse(options? options : {});
|
this.sceneOptions = SceneOptions.parse(options === undefined ? {} : options);
|
||||||
|
|
||||||
this.worldSize = new Vec2(500, 500);
|
this.worldSize = new Vec2(500, 500);
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
|
@ -164,6 +165,9 @@ export default class Scene implements Updateable {
|
||||||
tilemap.update(deltaT);
|
tilemap.update(deltaT);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update all tweens
|
||||||
|
TweenManager.getInstance().update(deltaT);
|
||||||
|
|
||||||
// Update viewport
|
// Update viewport
|
||||||
this.viewport.update(deltaT);
|
this.viewport.update(deltaT);
|
||||||
|
@ -210,6 +214,31 @@ export default class Scene implements Updateable {
|
||||||
return this.running;
|
return this.running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a node from this Scene
|
||||||
|
* @param node The node to remove
|
||||||
|
*/
|
||||||
|
remove(node: GameNode): void {
|
||||||
|
// Remove from the scene graph
|
||||||
|
if(node instanceof CanvasNode){
|
||||||
|
this.sceneGraph.removeNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Destroys this scene and all nodes in it */
|
||||||
|
destroy(): void {
|
||||||
|
for(let node of this.sceneGraph.getAllNodes()){
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.sceneGraph;
|
||||||
|
delete this.physicsManager;
|
||||||
|
delete this.navManager;
|
||||||
|
delete this.aiManager;
|
||||||
|
delete this.receiver;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new layer to the scene and returns it
|
* Adds a new layer to the scene and returns it
|
||||||
* @param name The name of the new layer
|
* @param name The name of the new layer
|
||||||
|
|
|
@ -23,6 +23,9 @@ export default class SceneManager {
|
||||||
/** The RenderingManager of the game */
|
/** The RenderingManager of the game */
|
||||||
protected renderingManager: RenderingManager;
|
protected renderingManager: RenderingManager;
|
||||||
|
|
||||||
|
/** For consistency, only change scenes at the beginning of the update cycle */
|
||||||
|
protected pendingScene: Scene;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new SceneManager
|
* Creates a new SceneManager
|
||||||
* @param viewport The Viewport of the game
|
* @param viewport The Viewport of the game
|
||||||
|
@ -42,8 +45,16 @@ export default class SceneManager {
|
||||||
* @param constr The constructor of the scene to add
|
* @param constr The constructor of the scene to add
|
||||||
* @param init An object to pass to the init function of the new scene
|
* @param init An object to pass to the init function of the new scene
|
||||||
*/
|
*/
|
||||||
public addScene<T extends Scene>(constr: new (...args: any) => T, init?: Record<string, any>, options?: Record<string, any>): void {
|
public changeToScene<T extends Scene>(constr: new (...args: any) => T, init?: Record<string, any>, options?: Record<string, any>): void {
|
||||||
|
this.viewport.setCenter(this.viewport.getHalfSize().x, this.viewport.getHalfSize().y);
|
||||||
|
|
||||||
let scene = new constr(this.viewport, this, this.renderingManager, options);
|
let scene = new constr(this.viewport, this, this.renderingManager, options);
|
||||||
|
|
||||||
|
if(this.currentScene){
|
||||||
|
console.log("Destroying Old Scene");
|
||||||
|
this.currentScene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
this.currentScene = scene;
|
this.currentScene = scene;
|
||||||
|
|
||||||
scene.initScene(init);
|
scene.initScene(init);
|
||||||
|
@ -61,19 +72,7 @@ export default class SceneManager {
|
||||||
|
|
||||||
this.renderingManager.setScene(scene);
|
this.renderingManager.setScene(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Change from the current scene to this new scene.
|
|
||||||
* Use this method if you've created a subclass of Scene, and you want to add it as the main Scene.
|
|
||||||
* @param constr The constructor of the scene to change to
|
|
||||||
* @param init An object to pass to the init function of the new scene
|
|
||||||
*/
|
|
||||||
public changeScene<T extends Scene>(constr: new (...args: any) => T, init?: Record<string, any>, options?: Record<string, any>): void {
|
|
||||||
this.viewport.setCenter(this.viewport.getHalfSize().x, this.viewport.getHalfSize().y);
|
|
||||||
|
|
||||||
this.addScene(constr, init, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a unique ID
|
* Generates a unique ID
|
||||||
* @returns A new ID
|
* @returns A new ID
|
||||||
|
|
|
@ -7,24 +7,17 @@ import ArrayUtils from "../Utils/ArrayUtils";
|
||||||
*/
|
*/
|
||||||
export default class SceneOptions {
|
export default class SceneOptions {
|
||||||
physics: {
|
physics: {
|
||||||
numPhysicsLayers: number,
|
groups: Array<string>,
|
||||||
physicsLayerNames: Array<string>,
|
collisions: Array<Array<number>>;
|
||||||
physicsLayerCollisions: Array<Array<number>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static parse(options: Record<string, any>): SceneOptions{
|
static parse(options: Record<string, any>): SceneOptions{
|
||||||
let sOpt = new SceneOptions();
|
let sOpt = new SceneOptions();
|
||||||
|
|
||||||
sOpt.physics = {
|
if(options.physics === undefined){
|
||||||
numPhysicsLayers: 10,
|
sOpt.physics = {groups: undefined, collisions: undefined};
|
||||||
physicsLayerNames: null,
|
} else {
|
||||||
physicsLayerCollisions: ArrayUtils.ones2d(10, 10)
|
sOpt.physics = options.physics;
|
||||||
};
|
|
||||||
|
|
||||||
if(options.physics){
|
|
||||||
if(options.physics.numPhysicsLayers) sOpt.physics.numPhysicsLayers = options.physics.numPhysicsLayers;
|
|
||||||
if(options.physics.physicsLayerNames) sOpt.physics.physicsLayerNames = options.physics.physicsLayerNames;
|
|
||||||
if(options.physics.physicsLayerCollisions) sOpt.physics.physicsLayerCollisions = options.physics.physicsLayerCollisions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sOpt;
|
return sOpt;
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default abstract class SceneGraph {
|
||||||
/** A reference to the viewport */
|
/** A reference to the viewport */
|
||||||
protected viewport: Viewport;
|
protected viewport: Viewport;
|
||||||
/** A map of CanvasNodes in this SceneGraph */
|
/** A map of CanvasNodes in this SceneGraph */
|
||||||
protected nodeMap: Map<CanvasNode>;
|
protected nodeMap: Array<CanvasNode>;
|
||||||
/** A counter of IDs for nodes in this SceneGraph */
|
/** A counter of IDs for nodes in this SceneGraph */
|
||||||
protected idCounter: number;
|
protected idCounter: number;
|
||||||
/** A reference to the Scene this SceneGraph belongs to */
|
/** A reference to the Scene this SceneGraph belongs to */
|
||||||
|
@ -28,7 +28,7 @@ export default abstract class SceneGraph {
|
||||||
constructor(viewport: Viewport, scene: Scene){
|
constructor(viewport: Viewport, scene: Scene){
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.nodeMap = new Map<CanvasNode>();
|
this.nodeMap = new Array();
|
||||||
this.idCounter = 0;
|
this.idCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ export default abstract class SceneGraph {
|
||||||
* @returns The SceneGraph ID of this newly added CanvasNode
|
* @returns The SceneGraph ID of this newly added CanvasNode
|
||||||
*/
|
*/
|
||||||
addNode(node: CanvasNode): number {
|
addNode(node: CanvasNode): number {
|
||||||
this.nodeMap.add(this.idCounter.toString(), node);
|
this.nodeMap[node.id] = node;
|
||||||
this.addNodeSpecific(node, this.idCounter.toString());
|
this.addNodeSpecific(node, this.idCounter);
|
||||||
this.idCounter += 1;
|
this.idCounter += 1;
|
||||||
return this.idCounter - 1;
|
return this.idCounter - 1;
|
||||||
};
|
};
|
||||||
|
@ -49,7 +49,7 @@ export default abstract class SceneGraph {
|
||||||
* @param node The node to add to the data structure
|
* @param node The node to add to the data structure
|
||||||
* @param id The id of the CanvasNode
|
* @param id The id of the CanvasNode
|
||||||
*/
|
*/
|
||||||
protected abstract addNodeSpecific(node: CanvasNode, id: string): void;
|
protected abstract addNodeSpecific(node: CanvasNode, id: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a node from the SceneGraph
|
* Removes a node from the SceneGraph
|
||||||
|
@ -57,12 +57,8 @@ export default abstract class SceneGraph {
|
||||||
*/
|
*/
|
||||||
removeNode(node: CanvasNode): void {
|
removeNode(node: CanvasNode): void {
|
||||||
// Find and remove node in O(n)
|
// Find and remove node in O(n)
|
||||||
// TODO: Can this be better?
|
this.nodeMap[node.id] = undefined;
|
||||||
let id = this.nodeMap.keys().filter((key: string) => this.nodeMap.get(key) === node)[0];
|
this.removeNodeSpecific(node, node.id);
|
||||||
if(id !== undefined){
|
|
||||||
this.nodeMap.set(id, undefined);
|
|
||||||
this.removeNodeSpecific(node, id);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,15 +66,15 @@ export default abstract class SceneGraph {
|
||||||
* @param node The node to remove
|
* @param node The node to remove
|
||||||
* @param id The id of the node to remove
|
* @param id The id of the node to remove
|
||||||
*/
|
*/
|
||||||
protected abstract removeNodeSpecific(node: CanvasNode, id: string): void;
|
protected abstract removeNodeSpecific(node: CanvasNode, id: number): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific node using its id
|
* Get a specific node using its id
|
||||||
* @param id The id of the CanvasNode to retrieve
|
* @param id The id of the CanvasNode to retrieve
|
||||||
* @returns The node with this ID
|
* @returns The node with this ID
|
||||||
*/
|
*/
|
||||||
getNode(id: string): CanvasNode {
|
getNode(id: number): CanvasNode {
|
||||||
return this.nodeMap.get(id);
|
return this.nodeMap[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,7 +104,11 @@ export default abstract class SceneGraph {
|
||||||
*/
|
*/
|
||||||
getAllNodes(): Array<CanvasNode> {
|
getAllNodes(): Array<CanvasNode> {
|
||||||
let arr = new Array<CanvasNode>();
|
let arr = new Array<CanvasNode>();
|
||||||
this.nodeMap.forEach(key => arr.push(this.nodeMap.get(key)));
|
for(let i = 0; i < this.nodeMap.length; i++){
|
||||||
|
if(this.nodeMap[i] !== undefined){
|
||||||
|
arr.push(this.nodeMap[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,12 @@ export default class SceneGraphArray extends SceneGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
protected addNodeSpecific(node: CanvasNode, id: string): void {
|
protected addNodeSpecific(node: CanvasNode, id: number): void {
|
||||||
this.nodeList.push(node);
|
this.nodeList.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
protected removeNodeSpecific(node: CanvasNode, id: string): void {
|
protected removeNodeSpecific(node: CanvasNode, id: number): void {
|
||||||
let index = this.nodeList.indexOf(node);
|
let index = this.nodeList.indexOf(node);
|
||||||
if(index > -1){
|
if(index > -1){
|
||||||
this.nodeList.splice(index, 1);
|
this.nodeList.splice(index, 1);
|
||||||
|
|
|
@ -31,12 +31,12 @@ export default class SceneGraphQuadTree extends SceneGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
protected addNodeSpecific(node: CanvasNode, id: string): void {
|
protected addNodeSpecific(node: CanvasNode, id: number): void {
|
||||||
this.nodes.push(node);
|
this.nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
protected removeNodeSpecific(node: CanvasNode, id: string): void {
|
protected removeNodeSpecific(node: CanvasNode, id: number): void {
|
||||||
let index = this.nodes.indexOf(node);
|
let index = this.nodes.indexOf(node);
|
||||||
if(index >= 0){
|
if(index >= 0){
|
||||||
this.nodes.splice(index, 1);
|
this.nodes.splice(index, 1);
|
||||||
|
|
|
@ -4,12 +4,24 @@ import TimerManager from "./TimerManager";
|
||||||
|
|
||||||
export default class Timer implements Updateable {
|
export default class Timer implements Updateable {
|
||||||
|
|
||||||
|
/** The current state of this timer */
|
||||||
protected state: TimerState;
|
protected state: TimerState;
|
||||||
|
|
||||||
|
/** The function to call when this timer ends */
|
||||||
protected onEnd: Function;
|
protected onEnd: Function;
|
||||||
|
|
||||||
|
/** Whether or not this timer should loop */
|
||||||
protected loop: boolean;
|
protected loop: boolean;
|
||||||
|
|
||||||
|
/** The total amount of time this timer runs for */
|
||||||
protected totalTime: number;
|
protected totalTime: number;
|
||||||
|
|
||||||
|
/** The amount of time left on the current run */
|
||||||
protected timeLeft: number;
|
protected timeLeft: number;
|
||||||
|
|
||||||
|
/** The number of times this timer has been run */
|
||||||
|
protected numRuns: number;
|
||||||
|
|
||||||
constructor(time: number, onEnd?: Function, loop: boolean = false){
|
constructor(time: number, onEnd?: Function, loop: boolean = false){
|
||||||
// Register this timer
|
// Register this timer
|
||||||
TimerManager.getInstance().addTimer(this);
|
TimerManager.getInstance().addTimer(this);
|
||||||
|
@ -19,6 +31,7 @@ export default class Timer implements Updateable {
|
||||||
this.onEnd = onEnd;
|
this.onEnd = onEnd;
|
||||||
this.loop = loop;
|
this.loop = loop;
|
||||||
this.state = TimerState.STOPPED;
|
this.state = TimerState.STOPPED;
|
||||||
|
this.numRuns = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
isStopped(){
|
isStopped(){
|
||||||
|
@ -29,6 +42,14 @@ export default class Timer implements Updateable {
|
||||||
return this.state === TimerState.PAUSED;
|
return this.state === TimerState.PAUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not this timer has been run before
|
||||||
|
* @returns true if it has been run at least once (after the latest reset), and false otherwise
|
||||||
|
*/
|
||||||
|
hasRun(): boolean {
|
||||||
|
return this.numRuns > 0;
|
||||||
|
}
|
||||||
|
|
||||||
start(time?: number){
|
start(time?: number){
|
||||||
if(time !== undefined){
|
if(time !== undefined){
|
||||||
this.totalTime = time;
|
this.totalTime = time;
|
||||||
|
@ -37,6 +58,12 @@ export default class Timer implements Updateable {
|
||||||
this.timeLeft = this.totalTime;
|
this.timeLeft = this.totalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resets this timer. Sets the progress back to zero, and sets the number of runs back to zero */
|
||||||
|
reset(){
|
||||||
|
this.timeLeft = this.totalTime;
|
||||||
|
this.numRuns = 0;
|
||||||
|
}
|
||||||
|
|
||||||
pause(): void {
|
pause(): void {
|
||||||
this.state = TimerState.PAUSED;
|
this.state = TimerState.PAUSED;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +82,7 @@ export default class Timer implements Updateable {
|
||||||
protected end(){
|
protected end(){
|
||||||
// Update the state
|
// Update the state
|
||||||
this.state = TimerState.STOPPED;
|
this.state = TimerState.STOPPED;
|
||||||
|
this.numRuns += 1;
|
||||||
|
|
||||||
// Call the end function if there is one
|
// Call the end function if there is one
|
||||||
if(this.onEnd){
|
if(this.onEnd){
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import ControllerAI from "../Wolfie2D/AI/ControllerAI";
|
||||||
import AI from "../Wolfie2D/DataTypes/Interfaces/AI";
|
import AI from "../Wolfie2D/DataTypes/Interfaces/AI";
|
||||||
import Emitter from "../Wolfie2D/Events/Emitter";
|
import Emitter from "../Wolfie2D/Events/Emitter";
|
||||||
import GameEvent from "../Wolfie2D/Events/GameEvent";
|
import GameEvent from "../Wolfie2D/Events/GameEvent";
|
||||||
|
@ -5,8 +6,8 @@ import { GameEventType } from "../Wolfie2D/Events/GameEventType";
|
||||||
import Input from "../Wolfie2D/Input/Input";
|
import Input from "../Wolfie2D/Input/Input";
|
||||||
import AnimatedSprite from "../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
import AnimatedSprite from "../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||||
|
|
||||||
export default class PlayerController implements AI {
|
export default class PlayerController extends ControllerAI {
|
||||||
protected owner: AnimatedSprite;
|
public owner: AnimatedSprite;
|
||||||
protected jumpSoundKey: string;
|
protected jumpSoundKey: string;
|
||||||
protected emitter: Emitter;
|
protected emitter: Emitter;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,11 @@
|
||||||
left: 0px;
|
left: 0px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'NoPixel';
|
||||||
|
src: url('hw4_assets/fonts/NoPixel.ttf');
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
14
src/main.ts
14
src/main.ts
|
@ -1,5 +1,5 @@
|
||||||
import Game from "./Wolfie2D/Loop/Game";
|
import Game from "./Wolfie2D/Loop/Game";
|
||||||
import default_scene from "./default_scene";
|
import MainMenu from "./Homework4/Scenes/MainMenu";
|
||||||
|
|
||||||
// The main function is your entrypoint into Wolfie2D. Specify your first scene and any options here.
|
// The main function is your entrypoint into Wolfie2D. Specify your first scene and any options here.
|
||||||
(function main(){
|
(function main(){
|
||||||
|
@ -9,8 +9,14 @@ import default_scene from "./default_scene";
|
||||||
// Set up options for our game
|
// Set up options for our game
|
||||||
let options = {
|
let options = {
|
||||||
canvasSize: {x: 1200, y: 800}, // The size of the game
|
canvasSize: {x: 1200, y: 800}, // The size of the game
|
||||||
clearColor: {r: 0.1, g: 0.1, b: 0.1}, // The color the game clears to
|
clearColor: {r: 34, g: 32, b: 52}, // The color the game clears to
|
||||||
useWebGL: true, // Tell the game we want to use webgl
|
inputs: [
|
||||||
|
{name: "left", keys: ["a"]},
|
||||||
|
{name: "right", keys: ["d"]},
|
||||||
|
{name: "jump", keys: ["w", "space"]},
|
||||||
|
{name: "run", keys: ["shift"]}
|
||||||
|
],
|
||||||
|
useWebGL: false, // Tell the game we want to use webgl
|
||||||
showDebug: false // Whether to show debug messages. You can change this to true if you want
|
showDebug: false // Whether to show debug messages. You can change this to true if you want
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +24,7 @@ import default_scene from "./default_scene";
|
||||||
const game = new Game(options);
|
const game = new Game(options);
|
||||||
|
|
||||||
// Start our game
|
// Start our game
|
||||||
game.start(default_scene, {});
|
game.start(MainMenu, {});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function runTests(){};
|
function runTests(){};
|
Loading…
Reference in New Issue
Block a user