.
This commit is contained in:
@@ -10,4 +10,9 @@
|
||||
<ProjectReference Include="..\Engine\Engine.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="asset\model\" />
|
||||
<Folder Include="data\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
210
DoomDeathmatch/asset/map/default/colliders.obj
Normal file
210
DoomDeathmatch/asset/map/default/colliders.obj
Normal file
@@ -0,0 +1,210 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Cube.001
|
||||
v -24.000000 -6.000000 -2.000000
|
||||
v -24.000000 -6.000000 6.000000
|
||||
v -24.000000 -4.000000 -2.000000
|
||||
v -24.000000 -4.000000 6.000000
|
||||
v 24.000000 -6.000000 -2.000000
|
||||
v 24.000000 -6.000000 6.000000
|
||||
v 24.000000 -4.000000 -2.000000
|
||||
v 24.000000 -4.000000 6.000000
|
||||
s 0
|
||||
f 1 2 4 3
|
||||
f 3 4 8 7
|
||||
f 7 8 6 5
|
||||
f 5 6 2 1
|
||||
f 3 7 5 1
|
||||
f 8 4 2 6
|
||||
o Cube.002
|
||||
v -24.000000 4.000000 -2.000000
|
||||
v -24.000000 4.000000 6.000000
|
||||
v -24.000000 6.000000 -2.000000
|
||||
v -24.000000 6.000000 6.000000
|
||||
v -8.000000 4.000000 -2.000000
|
||||
v -8.000000 4.000000 6.000000
|
||||
v -8.000000 6.000000 -2.000000
|
||||
v -8.000000 6.000000 6.000000
|
||||
s 0
|
||||
f 9 10 12 11
|
||||
f 11 12 16 15
|
||||
f 15 16 14 13
|
||||
f 13 14 10 9
|
||||
f 11 15 13 9
|
||||
f 16 12 10 14
|
||||
o Cube.003
|
||||
v -24.000000 -6.000000 -2.000000
|
||||
v -24.000000 -6.000000 6.000000
|
||||
v -26.000000 -6.000000 -2.000000
|
||||
v -26.000000 -6.000000 6.000000
|
||||
v -24.000000 6.000000 -2.000000
|
||||
v -24.000000 6.000000 6.000000
|
||||
v -26.000000 6.000000 -2.000000
|
||||
v -26.000000 6.000000 6.000000
|
||||
s 0
|
||||
f 17 18 20 19
|
||||
f 19 20 24 23
|
||||
f 23 24 22 21
|
||||
f 21 22 18 17
|
||||
f 19 23 21 17
|
||||
f 24 20 18 22
|
||||
o Cube.004
|
||||
v -8.000000 6.000000 -2.000000
|
||||
v -8.000000 6.000000 6.000000
|
||||
v -10.000000 6.000000 -2.000000
|
||||
v -10.000000 6.000000 6.000000
|
||||
v -8.000000 45.031250 -2.000000
|
||||
v -8.000000 45.031250 6.000000
|
||||
v -10.000000 45.031250 -2.000000
|
||||
v -10.000000 45.031250 6.000000
|
||||
s 0
|
||||
f 25 26 28 27
|
||||
f 27 28 32 31
|
||||
f 31 32 30 29
|
||||
f 29 30 26 25
|
||||
f 27 31 29 25
|
||||
f 32 28 26 30
|
||||
o Cube.005
|
||||
v 50.000000 47.031250 -2.000000
|
||||
v 50.000000 47.031250 6.000000
|
||||
v 50.000000 45.031250 -2.000000
|
||||
v 50.000000 45.031250 6.000000
|
||||
v -10.000002 47.031250 -2.000000
|
||||
v -10.000002 47.031250 6.000000
|
||||
v -10.000002 45.031250 -2.000000
|
||||
v -10.000002 45.031250 6.000000
|
||||
s 0
|
||||
f 33 34 36 35
|
||||
f 35 36 40 39
|
||||
f 39 40 38 37
|
||||
f 37 38 34 33
|
||||
f 35 39 37 33
|
||||
f 40 36 34 38
|
||||
o Cube.006
|
||||
v 12.450001 4.000000 -2.000000
|
||||
v 12.450001 4.000000 6.000000
|
||||
v 8.000000 4.000000 -2.000000
|
||||
v 8.000000 4.000000 6.000000
|
||||
v 12.450001 8.031248 -2.000000
|
||||
v 12.450001 8.031248 6.000000
|
||||
v 8.000000 8.031248 -2.000000
|
||||
v 8.000000 8.031248 6.000000
|
||||
s 0
|
||||
f 41 42 44 43
|
||||
f 43 44 48 47
|
||||
f 47 48 46 45
|
||||
f 45 46 42 41
|
||||
f 43 47 45 41
|
||||
f 48 44 42 46
|
||||
o Cube.007
|
||||
v 11.200001 8.031250 -2.000000
|
||||
v 11.200001 8.031250 6.000000
|
||||
v 8.000000 8.031248 -2.000000
|
||||
v 8.000000 8.031248 6.000000
|
||||
v 11.200001 37.031250 -2.000000
|
||||
v 11.200001 37.031250 6.000000
|
||||
v 8.000000 37.031250 -2.000000
|
||||
v 8.000000 37.031250 6.000000
|
||||
s 0
|
||||
f 49 50 52 51
|
||||
f 51 52 56 55
|
||||
f 55 56 54 53
|
||||
f 53 54 50 49
|
||||
f 51 55 53 49
|
||||
f 56 52 50 54
|
||||
o Cube.008
|
||||
v 26.200001 27.031250 -2.000000
|
||||
v 26.200001 27.031250 6.000000
|
||||
v 11.200001 27.031250 -2.000000
|
||||
v 11.200001 27.031250 6.000000
|
||||
v 26.200001 37.031250 -2.000000
|
||||
v 26.200001 37.031250 6.000000
|
||||
v 11.200001 37.031250 -2.000000
|
||||
v 11.200001 37.031250 6.000000
|
||||
s 0
|
||||
f 57 58 60 59
|
||||
f 59 60 64 63
|
||||
f 63 64 62 61
|
||||
f 61 62 58 57
|
||||
f 59 63 61 57
|
||||
f 64 60 58 62
|
||||
o Cube.009
|
||||
v 26.000000 -6.000000 -2.000000
|
||||
v 26.000000 -6.000000 6.000000
|
||||
v 24.000000 -6.000000 -2.000000
|
||||
v 24.000000 -6.000000 6.000000
|
||||
v 26.000000 4.000000 -2.000000
|
||||
v 26.000000 4.000000 6.000000
|
||||
v 24.000000 4.000000 -2.000000
|
||||
v 24.000000 4.000000 6.000000
|
||||
s 0
|
||||
f 65 66 68 67
|
||||
f 67 68 72 71
|
||||
f 71 72 70 69
|
||||
f 69 70 66 65
|
||||
f 67 71 69 65
|
||||
f 72 68 66 70
|
||||
o Cube.010
|
||||
v 31.799999 4.000002 -2.000000
|
||||
v 31.799999 4.000002 6.000000
|
||||
v 17.450001 4.000000 -2.000000
|
||||
v 17.450001 4.000000 6.000000
|
||||
v 31.799999 8.031251 -2.000000
|
||||
v 31.799999 8.031251 6.000000
|
||||
v 17.450001 8.031249 -2.000000
|
||||
v 17.450001 8.031249 6.000000
|
||||
s 0
|
||||
f 73 74 76 75
|
||||
f 75 76 80 79
|
||||
f 79 80 78 77
|
||||
f 77 78 74 73
|
||||
f 75 79 77 73
|
||||
f 80 76 74 78
|
||||
o Cube.011
|
||||
v 33.799999 6.000000 -2.000000
|
||||
v 33.799999 6.000000 6.000000
|
||||
v 31.799999 6.000000 -2.000000
|
||||
v 31.799999 6.000000 6.000000
|
||||
v 33.799999 37.031250 -2.000000
|
||||
v 33.799999 37.031250 6.000000
|
||||
v 31.799999 37.031250 -2.000000
|
||||
v 31.799999 37.031250 6.000000
|
||||
s 0
|
||||
f 81 82 84 83
|
||||
f 83 84 88 87
|
||||
f 87 88 86 85
|
||||
f 85 86 82 81
|
||||
f 83 87 85 81
|
||||
f 88 84 82 86
|
||||
o Cube.012
|
||||
v 33.799999 35.031250 -2.000000
|
||||
v 33.799999 35.031250 6.000000
|
||||
v 33.799999 37.031250 -2.000000
|
||||
v 33.799999 37.031250 6.000000
|
||||
v 50.000000 35.031250 -2.000000
|
||||
v 50.000000 35.031250 6.000000
|
||||
v 50.000000 37.031250 -2.000000
|
||||
v 50.000000 37.031250 6.000000
|
||||
s 0
|
||||
f 89 90 92 91
|
||||
f 91 92 96 95
|
||||
f 95 96 94 93
|
||||
f 93 94 90 89
|
||||
f 91 95 93 89
|
||||
f 96 92 90 94
|
||||
o Cube.013
|
||||
v 52.000000 35.031250 -2.000000
|
||||
v 52.000000 35.031250 6.000000
|
||||
v 50.000000 35.031250 -2.000000
|
||||
v 50.000000 35.031250 6.000000
|
||||
v 52.000000 47.031250 -2.000000
|
||||
v 52.000000 47.031250 6.000000
|
||||
v 50.000000 47.031250 -2.000000
|
||||
v 50.000000 47.031250 6.000000
|
||||
s 0
|
||||
f 97 98 100 99
|
||||
f 99 100 104 103
|
||||
f 103 104 102 101
|
||||
f 101 102 98 97
|
||||
f 99 103 101 97
|
||||
f 104 100 98 102
|
||||
238
DoomDeathmatch/asset/map/default/mesh.obj
Normal file
238
DoomDeathmatch/asset/map/default/mesh.obj
Normal file
@@ -0,0 +1,238 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Plane
|
||||
v -24.000000 -4.000000 0.000000
|
||||
v 24.000000 -4.000000 0.000000
|
||||
v -24.000000 4.000000 0.000000
|
||||
v 24.000000 4.000000 0.000000
|
||||
v -8.000000 -4.000000 0.000000
|
||||
v 8.000000 -4.000000 0.000000
|
||||
v 8.000000 4.000000 0.000000
|
||||
v -8.000000 4.000000 0.000000
|
||||
v 8.000000 37.031250 0.000000
|
||||
v -8.000000 37.031250 0.000000
|
||||
v 8.000000 45.031250 0.000000
|
||||
v -8.000000 45.031250 0.000000
|
||||
v 50.000000 37.031250 0.000000
|
||||
v 50.000000 45.031250 0.000000
|
||||
v 26.200001 37.031250 0.000000
|
||||
v 31.799999 37.031250 0.000000
|
||||
v 31.799999 45.031250 0.000000
|
||||
v 26.200001 45.031250 0.000000
|
||||
v 26.200001 27.031250 0.000000
|
||||
v 31.799999 27.031250 0.000000
|
||||
v 26.200001 8.031250 0.000000
|
||||
v 31.799999 8.031250 0.000000
|
||||
v 11.200001 27.031250 0.000000
|
||||
v 11.200001 8.031250 0.000000
|
||||
v 17.450001 27.031250 0.000000
|
||||
v 12.450001 27.031250 0.000000
|
||||
v 12.450001 8.031250 0.000000
|
||||
v 17.450001 8.031250 0.000000
|
||||
v 12.450001 4.000000 0.000000
|
||||
v 17.450001 4.000000 0.000000
|
||||
v 12.450001 -4.000000 0.000000
|
||||
v 17.450001 -4.000000 0.000000
|
||||
v -24.000000 -4.000000 4.000000
|
||||
v 24.000000 -4.000000 4.000000
|
||||
v -24.000000 4.000000 4.000000
|
||||
v 24.000000 4.000000 4.000000
|
||||
v -8.000000 -4.000000 4.000000
|
||||
v 8.000000 -4.000000 4.000000
|
||||
v 8.000000 4.000000 4.000000
|
||||
v -8.000000 4.000000 4.000000
|
||||
v 8.000000 37.031250 4.000000
|
||||
v -8.000000 37.031250 4.000000
|
||||
v 8.000000 45.031250 4.000000
|
||||
v -8.000000 45.031250 4.000000
|
||||
v 50.000000 37.031250 4.000000
|
||||
v 50.000000 45.031250 4.000000
|
||||
v 26.200001 37.031250 4.000000
|
||||
v 31.799999 37.031250 4.000000
|
||||
v 31.799999 45.031250 4.000000
|
||||
v 26.200001 45.031250 4.000000
|
||||
v 26.200001 27.031250 4.000000
|
||||
v 31.799999 27.031250 4.000000
|
||||
v 26.200001 8.031250 4.000000
|
||||
v 31.799999 8.031250 4.000000
|
||||
v 11.200001 27.031250 4.000000
|
||||
v 11.200001 8.031250 4.000000
|
||||
v 17.450001 27.031250 4.000000
|
||||
v 12.450001 27.031250 4.000000
|
||||
v 12.450001 8.031250 4.000000
|
||||
v 17.450001 8.031250 4.000000
|
||||
v 12.450001 4.000000 4.000000
|
||||
v 17.450001 4.000000 4.000000
|
||||
v 12.450001 -4.000000 4.000000
|
||||
v 17.450001 -4.000000 4.000000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vt 0.757888 0.095816
|
||||
vt 0.900966 0.310434
|
||||
vt 0.757888 0.310434
|
||||
vt 0.829427 0.095816
|
||||
vt 0.829427 0.310434
|
||||
vt 0.900966 0.095816
|
||||
vt 0.871139 0.898505
|
||||
vt 0.787715 0.773370
|
||||
vt 0.829427 0.773370
|
||||
vt 0.787715 0.898505
|
||||
vt 0.871139 0.773370
|
||||
vt 0.829427 0.898505
|
||||
vt 0.123250 0.042329
|
||||
vt 0.123250 0.985848
|
||||
vt 0.074426 0.042329
|
||||
vt 0.074426 0.985846
|
||||
vt 0.178788 0.042329
|
||||
vt 0.178788 0.985846
|
||||
vt 0.123250 0.985846
|
||||
vt 0.025602 0.042329
|
||||
vt 0.025602 0.985848
|
||||
vt 0.195876 0.042329
|
||||
vt 0.195876 0.985848
|
||||
vt 0.136830 0.042329
|
||||
vt 0.136830 0.985848
|
||||
vt 0.152087 0.042329
|
||||
vt 0.172074 0.985848
|
||||
vt 0.172074 0.042329
|
||||
vt 0.178788 0.985848
|
||||
vt 0.152087 0.985848
|
||||
vt 0.251414 0.042329
|
||||
vt 0.251414 0.985846
|
||||
vt 0.074426 0.985848
|
||||
vt 0.195876 0.985846
|
||||
vt 0.133015 0.042329
|
||||
vt 0.133015 0.985848
|
||||
s 0
|
||||
f 4/1/1 32/2/1 30/2/1
|
||||
f 3/3/1 5/4/1 1/1/1
|
||||
f 8/5/1 6/6/1 5/4/1
|
||||
f 10/5/1 7/2/1 8/5/1
|
||||
f 12/5/1 9/2/1 10/5/1
|
||||
f 14/2/1 16/2/1 17/2/1
|
||||
f 18/2/1 9/2/1 11/2/1
|
||||
f 17/2/1 15/2/1 18/2/1
|
||||
f 20/2/1 15/2/1 16/2/1
|
||||
f 22/2/1 19/2/1 20/2/1
|
||||
f 24/2/1 26/2/1 27/2/1
|
||||
f 28/2/1 19/2/1 21/2/1
|
||||
f 27/2/1 25/2/1 28/2/1
|
||||
f 30/2/1 27/2/1 28/2/1
|
||||
f 32/2/1 29/2/1 30/2/1
|
||||
f 7/2/1 31/2/1 6/6/1
|
||||
f 64/7/2 36/8/2 62/7/2
|
||||
f 37/9/2 35/10/2 33/8/2
|
||||
f 38/11/2 40/12/2 37/9/2
|
||||
f 39/7/2 42/12/2 40/12/2
|
||||
f 41/7/2 44/12/2 42/12/2
|
||||
f 48/7/2 46/7/2 49/7/2
|
||||
f 41/7/2 50/7/2 43/7/2
|
||||
f 47/7/2 49/7/2 50/7/2
|
||||
f 47/7/2 52/7/2 48/7/2
|
||||
f 51/7/2 54/7/2 52/7/2
|
||||
f 58/7/2 56/7/2 59/7/2
|
||||
f 51/7/2 60/7/2 53/7/2
|
||||
f 57/7/2 59/7/2 60/7/2
|
||||
f 59/7/2 62/7/2 60/7/2
|
||||
f 61/7/2 64/7/2 62/7/2
|
||||
f 63/7/2 39/7/2 38/11/2
|
||||
f 9/13/3 39/14/3 7/13/3
|
||||
f 8/15/4 42/16/4 10/15/4
|
||||
f 19/17/4 47/18/4 15/17/4
|
||||
f 12/15/5 43/19/5 11/13/5
|
||||
f 1/20/4 35/21/4 3/20/4
|
||||
f 16/22/3 52/23/3 20/22/3
|
||||
f 29/24/4 59/25/4 27/24/4
|
||||
f 30/26/5 36/27/5 4/28/5
|
||||
f 22/22/6 53/29/6 21/17/6
|
||||
f 10/15/4 44/16/4 12/15/4
|
||||
f 28/26/3 62/30/3 30/26/3
|
||||
f 4/28/3 34/27/3 2/28/3
|
||||
f 14/31/3 45/32/3 13/31/3
|
||||
f 32/26/6 63/25/6 31/24/6
|
||||
f 3/20/5 40/33/5 8/15/5
|
||||
f 20/22/3 54/23/3 22/22/3
|
||||
f 13/31/6 48/34/6 16/22/6
|
||||
f 5/15/6 33/21/6 1/20/6
|
||||
f 24/35/4 55/36/4 23/35/4
|
||||
f 11/13/5 50/18/5 18/17/5
|
||||
f 6/13/6 37/33/6 5/15/6
|
||||
f 23/35/5 58/25/5 26/24/5
|
||||
f 15/17/6 41/19/6 9/13/6
|
||||
f 31/24/6 38/14/6 6/13/6
|
||||
f 2/28/6 64/30/6 32/26/6
|
||||
f 21/17/6 60/30/6 28/26/6
|
||||
f 7/13/5 61/25/5 29/24/5
|
||||
f 25/26/5 51/29/5 19/17/5
|
||||
f 17/22/5 46/32/5 14/31/5
|
||||
f 26/24/5 57/30/5 25/26/5
|
||||
f 18/17/5 49/34/5 17/22/5
|
||||
f 27/24/6 56/36/6 24/35/6
|
||||
f 4/1/1 2/1/1 32/2/1
|
||||
f 3/3/1 8/5/1 5/4/1
|
||||
f 8/5/1 7/2/1 6/6/1
|
||||
f 10/5/1 9/2/1 7/2/1
|
||||
f 12/5/1 11/2/1 9/2/1
|
||||
f 14/2/1 13/2/1 16/2/1
|
||||
f 18/2/1 15/2/1 9/2/1
|
||||
f 17/2/1 16/2/1 15/2/1
|
||||
f 20/2/1 19/2/1 15/2/1
|
||||
f 22/2/1 21/2/1 19/2/1
|
||||
f 24/2/1 23/2/1 26/2/1
|
||||
f 28/2/1 25/2/1 19/2/1
|
||||
f 27/2/1 26/2/1 25/2/1
|
||||
f 30/2/1 29/2/1 27/2/1
|
||||
f 32/2/1 31/2/1 29/2/1
|
||||
f 7/2/1 29/2/1 31/2/1
|
||||
f 64/7/2 34/8/2 36/8/2
|
||||
f 37/9/2 40/12/2 35/10/2
|
||||
f 38/11/2 39/7/2 40/12/2
|
||||
f 39/7/2 41/7/2 42/12/2
|
||||
f 41/7/2 43/7/2 44/12/2
|
||||
f 48/7/2 45/7/2 46/7/2
|
||||
f 41/7/2 47/7/2 50/7/2
|
||||
f 47/7/2 48/7/2 49/7/2
|
||||
f 47/7/2 51/7/2 52/7/2
|
||||
f 51/7/2 53/7/2 54/7/2
|
||||
f 58/7/2 55/7/2 56/7/2
|
||||
f 51/7/2 57/7/2 60/7/2
|
||||
f 57/7/2 58/7/2 59/7/2
|
||||
f 59/7/2 61/7/2 62/7/2
|
||||
f 61/7/2 63/7/2 64/7/2
|
||||
f 63/7/2 61/7/2 39/7/2
|
||||
f 9/13/3 41/19/3 39/14/3
|
||||
f 8/15/4 40/33/4 42/16/4
|
||||
f 19/17/4 51/29/4 47/18/4
|
||||
f 12/15/5 44/16/5 43/19/5
|
||||
f 1/20/4 33/21/4 35/21/4
|
||||
f 16/22/3 48/34/3 52/23/3
|
||||
f 29/24/4 61/25/4 59/25/4
|
||||
f 30/26/5 62/30/5 36/27/5
|
||||
f 22/22/6 54/23/6 53/29/6
|
||||
f 10/15/4 42/16/4 44/16/4
|
||||
f 28/26/3 60/30/3 62/30/3
|
||||
f 4/28/3 36/27/3 34/27/3
|
||||
f 14/31/3 46/32/3 45/32/3
|
||||
f 32/26/6 64/30/6 63/25/6
|
||||
f 3/20/5 35/21/5 40/33/5
|
||||
f 20/22/3 52/23/3 54/23/3
|
||||
f 13/31/6 45/32/6 48/34/6
|
||||
f 5/15/6 37/33/6 33/21/6
|
||||
f 24/35/4 56/36/4 55/36/4
|
||||
f 11/13/5 43/19/5 50/18/5
|
||||
f 6/13/6 38/14/6 37/33/6
|
||||
f 23/35/5 55/36/5 58/25/5
|
||||
f 15/17/6 47/18/6 41/19/6
|
||||
f 31/24/6 63/25/6 38/14/6
|
||||
f 2/28/6 34/27/6 64/30/6
|
||||
f 21/17/6 53/29/6 60/30/6
|
||||
f 7/13/5 39/14/5 61/25/5
|
||||
f 25/26/5 57/30/5 51/29/5
|
||||
f 17/22/5 49/34/5 46/32/5
|
||||
f 26/24/5 58/25/5 57/30/5
|
||||
f 18/17/5 50/18/5 49/34/5
|
||||
f 27/24/6 59/25/6 56/36/6
|
||||
10
DoomDeathmatch/asset/map/default/monster_spawners.obj
Normal file
10
DoomDeathmatch/asset/map/default/monster_spawners.obj
Normal file
@@ -0,0 +1,10 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Spawners
|
||||
v -21.000000 0.000000 0.000000
|
||||
v -5.000000 41.000000 0.000000
|
||||
v 48.000000 41.000000 0.000000
|
||||
v 20.000000 0.000000 0.000000
|
||||
v 15.000000 23.000000 0.000000
|
||||
s 0
|
||||
f 5 3 4 1 2
|
||||
BIN
DoomDeathmatch/asset/map/default/texture.png
Normal file
BIN
DoomDeathmatch/asset/map/default/texture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
12
DoomDeathmatch/asset/map/default/valuable_spawners.obj
Normal file
12
DoomDeathmatch/asset/map/default/valuable_spawners.obj
Normal file
@@ -0,0 +1,12 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Valuables
|
||||
v 0.000000 0.000000 0.000000
|
||||
v 25.000000 40.000000 0.000000
|
||||
v 0.000000 40.000000 0.000000
|
||||
v 0.000000 20.000000 0.000000
|
||||
v 21.000000 17.000000 0.000000
|
||||
v 15.000000 0.000000 0.000000
|
||||
v -16.000000 0.000000 0.000000
|
||||
s 0
|
||||
f 3 2 5 6 1 7 4
|
||||
@@ -1,101 +0,0 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Plane
|
||||
v -14.316629 -4.684887 0.000000
|
||||
v 14.316629 -4.684887 0.000000
|
||||
v -14.316629 4.684887 0.000000
|
||||
v 14.316629 4.684887 0.000000
|
||||
v 6.822107 -4.684887 0.000000
|
||||
v 6.822107 4.684887 0.000000
|
||||
v 14.316629 33.607601 0.000000
|
||||
v 6.822107 33.607601 0.000000
|
||||
v 14.316629 25.280016 0.000000
|
||||
v 6.822107 25.280016 0.000000
|
||||
v -6.847844 33.607601 0.000000
|
||||
v -6.847844 25.280016 0.000000
|
||||
v -25.254845 24.277735 0.000000
|
||||
v -18.173466 20.114567 0.000000
|
||||
v -26.336294 7.837210 0.000000
|
||||
v -19.225933 10.395360 0.000000
|
||||
v -14.316629 -4.684887 3.000000
|
||||
v 14.316629 -4.684887 3.000000
|
||||
v 14.316629 4.684887 3.000000
|
||||
v 6.822107 -4.684887 3.000000
|
||||
v 14.316629 33.607601 3.000000
|
||||
v 6.822107 33.607601 3.000000
|
||||
v 14.316629 25.280016 3.000000
|
||||
v -6.847844 33.607601 3.000000
|
||||
v -25.254845 24.277735 3.000000
|
||||
v -26.336294 7.837210 3.000000
|
||||
v -14.316629 4.684887 3.000000
|
||||
v 6.822107 4.684887 3.000000
|
||||
v 6.822107 25.280016 3.000000
|
||||
v -6.847844 25.280016 3.000000
|
||||
v -18.173466 20.114567 3.000000
|
||||
v -19.225933 10.395360 3.000000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn 0.7214 0.6925 -0.0000
|
||||
vn 0.9978 -0.0656 -0.0000
|
||||
vn 0.4521 -0.8920 -0.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vn -0.7583 -0.6519 -0.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.4150 0.9098 -0.0000
|
||||
vn -0.9942 0.1077 -0.0000
|
||||
vt 1.000000 0.000000
|
||||
vt 0.738258 1.000000
|
||||
vt 0.738258 0.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
s 0
|
||||
f 2/1/1 6/2/1 5/3/1
|
||||
f 5/3/1 3/4/1 1/5/1
|
||||
f 9/6/1 8/2/1 10/2/1
|
||||
f 4/6/1 10/2/1 6/2/1
|
||||
f 8/2/1 12/2/1 10/2/1
|
||||
f 11/2/1 14/2/1 12/2/1
|
||||
f 14/2/1 15/2/1 16/2/1
|
||||
f 3/4/1 15/2/1 1/5/1
|
||||
f 2/1/2 19/6/2 4/6/2
|
||||
f 15/2/3 17/5/3 1/5/3
|
||||
f 13/2/4 26/2/4 15/2/4
|
||||
f 4/6/2 23/6/2 9/6/2
|
||||
f 11/2/5 25/2/5 13/2/5
|
||||
f 7/6/6 22/2/6 8/2/6
|
||||
f 8/2/6 24/2/6 11/2/6
|
||||
f 1/5/7 20/3/7 5/3/7
|
||||
f 5/3/7 18/1/7 2/1/7
|
||||
f 9/6/2 21/6/2 7/6/2
|
||||
f 12/2/7 29/2/7 10/2/7
|
||||
f 3/4/8 32/2/8 16/2/8
|
||||
f 10/2/9 28/2/9 6/2/9
|
||||
f 14/2/10 30/2/10 12/2/10
|
||||
f 6/2/6 27/4/6 3/4/6
|
||||
f 16/2/11 31/2/11 14/2/11
|
||||
f 2/1/1 4/6/1 6/2/1
|
||||
f 5/3/1 6/2/1 3/4/1
|
||||
f 9/6/1 7/6/1 8/2/1
|
||||
f 4/6/1 9/6/1 10/2/1
|
||||
f 8/2/1 11/2/1 12/2/1
|
||||
f 11/2/1 13/2/1 14/2/1
|
||||
f 14/2/1 13/2/1 15/2/1
|
||||
f 3/4/1 16/2/1 15/2/1
|
||||
f 2/1/2 18/1/2 19/6/2
|
||||
f 15/2/3 26/2/3 17/5/3
|
||||
f 13/2/4 25/2/4 26/2/4
|
||||
f 4/6/2 19/6/2 23/6/2
|
||||
f 11/2/5 24/2/5 25/2/5
|
||||
f 7/6/6 21/6/6 22/2/6
|
||||
f 8/2/6 22/2/6 24/2/6
|
||||
f 1/5/7 17/5/7 20/3/7
|
||||
f 5/3/7 20/3/7 18/1/7
|
||||
f 9/6/2 23/6/2 21/6/2
|
||||
f 12/2/7 30/2/7 29/2/7
|
||||
f 3/4/8 27/4/8 32/2/8
|
||||
f 10/2/9 29/2/9 28/2/9
|
||||
f 14/2/10 31/2/10 30/2/10
|
||||
f 6/2/6 28/2/6 27/4/6
|
||||
f 16/2/11 32/2/11 31/2/11
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Cube
|
||||
v -1.000000 -1.000000 1.000000
|
||||
v -1.000000 1.000000 1.000000
|
||||
v -1.000000 -1.000000 -1.000000
|
||||
v -1.000000 1.000000 -1.000000
|
||||
v 1.000000 -1.000000 1.000000
|
||||
v 1.000000 1.000000 1.000000
|
||||
v 1.000000 -1.000000 -1.000000
|
||||
v 1.000000 1.000000 -1.000000
|
||||
vn -1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 -1.0000
|
||||
vn 1.0000 -0.0000 -0.0000
|
||||
vn -0.0000 -0.0000 1.0000
|
||||
vn -0.0000 -1.0000 -0.0000
|
||||
vn -0.0000 1.0000 -0.0000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
s 0
|
||||
f 2/1/1 3/2/1 1/3/1
|
||||
f 4/4/2 7/5/2 3/2/2
|
||||
f 8/6/3 5/7/3 7/5/3
|
||||
f 6/8/4 1/9/4 5/7/4
|
||||
f 7/5/5 1/10/5 3/11/5
|
||||
f 4/12/6 6/8/6 8/6/6
|
||||
f 2/1/1 4/4/1 3/2/1
|
||||
f 4/4/2 8/6/2 7/5/2
|
||||
f 8/6/3 6/8/3 5/7/3
|
||||
f 6/8/4 2/13/4 1/9/4
|
||||
f 7/5/5 5/7/5 1/10/5
|
||||
f 4/12/6 2/14/6 6/8/6
|
||||
@@ -1,38 +0,0 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Cube
|
||||
v -1.000000 -1.000000 1.000000
|
||||
v -1.000000 1.000000 1.000000
|
||||
v -1.000000 -1.000000 -1.000000
|
||||
v -1.000000 1.000000 -1.000000
|
||||
v 1.000000 -1.000000 1.000000
|
||||
v 1.000000 1.000000 1.000000
|
||||
v 1.000000 -1.000000 -1.000000
|
||||
v 1.000000 1.000000 -1.000000
|
||||
vt 0.625000 0.000000
|
||||
vt 0.375000 0.250000
|
||||
vt 0.375000 0.000000
|
||||
vt 0.625000 0.250000
|
||||
vt 0.375000 0.500000
|
||||
vt 0.625000 0.500000
|
||||
vt 0.375000 0.750000
|
||||
vt 0.625000 0.750000
|
||||
vt 0.375000 1.000000
|
||||
vt 0.125000 0.750000
|
||||
vt 0.125000 0.500000
|
||||
vt 0.875000 0.500000
|
||||
vt 0.625000 1.000000
|
||||
vt 0.875000 0.750000
|
||||
s 0
|
||||
f 2/1 3/2 1/3
|
||||
f 4/4 7/5 3/2
|
||||
f 8/6 5/7 7/5
|
||||
f 6/8 1/9 5/7
|
||||
f 7/5 1/10 3/11
|
||||
f 4/12 6/8 8/6
|
||||
f 2/1 4/4 3/2
|
||||
f 4/4 8/6 7/5
|
||||
f 8/6 6/8 5/7
|
||||
f 6/8 2/13 1/9
|
||||
f 7/5 5/7 1/10
|
||||
f 4/12 2/14 6/8
|
||||
@@ -1,24 +0,0 @@
|
||||
# Blender 4.2.3 LTS
|
||||
# www.blender.org
|
||||
o Cube
|
||||
v -1.000000 -1.000000 1.000000
|
||||
v -1.000000 1.000000 1.000000
|
||||
v -1.000000 -1.000000 -1.000000
|
||||
v -1.000000 1.000000 -1.000000
|
||||
v 1.000000 -1.000000 1.000000
|
||||
v 1.000000 1.000000 1.000000
|
||||
v 1.000000 -1.000000 -1.000000
|
||||
v 1.000000 1.000000 -1.000000
|
||||
s 0
|
||||
f 2 3 1
|
||||
f 4 7 3
|
||||
f 8 5 7
|
||||
f 6 1 5
|
||||
f 7 1 3
|
||||
f 4 6 8
|
||||
f 2 4 3
|
||||
f 4 8 7
|
||||
f 8 6 5
|
||||
f 6 2 1
|
||||
f 7 5 1
|
||||
f 4 2 6
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 116 KiB |
BIN
DoomDeathmatch/asset/texture/fireball.png
Normal file
BIN
DoomDeathmatch/asset/texture/fireball.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,75 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class EnemyController : Engine.Scene.Component.Component
|
||||
{
|
||||
public HealthController HealthController => _healthController;
|
||||
|
||||
private GameController _gameController = null!;
|
||||
private HealthController _healthController = null!;
|
||||
private EnemyView _enemyView = null!;
|
||||
private MovementController _movementController = null!;
|
||||
private AttackBehavior _attackBehavior = null!;
|
||||
|
||||
private readonly EnemyData _enemyData;
|
||||
|
||||
public EnemyController(EnemyData parEnemyData)
|
||||
{
|
||||
_enemyData = parEnemyData;
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_gameController = GameObject.Scene!.FindFirstComponent<GameController>()!;
|
||||
_healthController = GameObject.GetComponent<HealthController>()!;
|
||||
_enemyView = GameObject.GetComponent<EnemyView>()!;
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_gameController);
|
||||
ArgumentNullException.ThrowIfNull(_healthController);
|
||||
ArgumentNullException.ThrowIfNull(_enemyView);
|
||||
ArgumentNullException.ThrowIfNull(_movementController);
|
||||
|
||||
_attackBehavior = _enemyData.AttackBehaviorCreator.Create(this, _gameController.PlayerController.HealthController);
|
||||
|
||||
_healthController.SetMaxHealth(_enemyData.BaseHealth);
|
||||
_healthController.OnDeath += OnDeath;
|
||||
_enemyView.UpdateView(_enemyData);
|
||||
_movementController.Speed = _enemyData.BaseSpeed;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
var billboardComponent = GameObject.GetComponentInChildren<BillboardComponent>();
|
||||
if (billboardComponent == null)
|
||||
return;
|
||||
|
||||
billboardComponent.Target = _gameController.PlayerController.Camera.GameObject.Transform;
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
var playerPosition = _gameController.PlayerController.GameObject.Transform.Translation;
|
||||
var enemyPosition = GameObject.Transform.Translation;
|
||||
|
||||
var nextPosition = _enemyData.MovementBehavior.GetNextPosition(enemyPosition, playerPosition);
|
||||
|
||||
if (enemyPosition != nextPosition)
|
||||
{
|
||||
var direction = (nextPosition - enemyPosition).Normalized();
|
||||
_movementController.ApplyMovement(direction);
|
||||
}
|
||||
|
||||
_attackBehavior.Attack(parDeltaTime);
|
||||
}
|
||||
|
||||
private void OnDeath()
|
||||
{
|
||||
GameObject.Scene!.Remove(GameObject);
|
||||
_gameController.ScoreController.AddScore(_enemyData.BaseScore);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using Engine.Input;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class GameController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; set; } = false;
|
||||
|
||||
public PlayerController PlayerController => _playerController;
|
||||
public ScoreController ScoreController => _scoreController;
|
||||
|
||||
private ScoreController _scoreController = null!;
|
||||
private TimerController _timerController = null!;
|
||||
private PlayerController _playerController = null!;
|
||||
|
||||
private readonly MenuControllerComponent _menuController;
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
|
||||
public GameController(MenuControllerComponent parMenuController)
|
||||
{
|
||||
_menuController = parMenuController;
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_scoreController = GameObject.GetComponent<ScoreController>()!;
|
||||
_timerController = GameObject.GetComponent<TimerController>()!;
|
||||
_playerController = GameObject.Scene!.FindFirstComponent<PlayerController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_scoreController);
|
||||
ArgumentNullException.ThrowIfNull(_timerController);
|
||||
ArgumentNullException.ThrowIfNull(_playerController);
|
||||
}
|
||||
|
||||
public void Unpause()
|
||||
{
|
||||
GameObject.Scene!.TimeScale = 1.0f;
|
||||
IsPaused = false;
|
||||
_menuController.SelectMenuItem("play");
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
GameObject.Scene!.TimeScale = 0.0f;
|
||||
IsPaused = true;
|
||||
_menuController.SelectMenuItem("escape");
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Escape))
|
||||
{
|
||||
if (IsPaused)
|
||||
{
|
||||
Unpause();
|
||||
}
|
||||
else
|
||||
{
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,19 @@ namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class HealthController : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly HealthModel _healthModel = new(100);
|
||||
public event Action? OnDeath;
|
||||
|
||||
private readonly HealthModel _healthModel;
|
||||
private HealthView? _healthView;
|
||||
|
||||
public HealthController(float parHealth = 100)
|
||||
{
|
||||
_healthModel = new HealthModel(parHealth);
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_healthModel.HealthChanged += OnHealthChanged;
|
||||
_healthView = GameObject.GetComponent<HealthView>();
|
||||
|
||||
if (_healthView != null)
|
||||
@@ -19,6 +27,12 @@ public class HealthController : Engine.Scene.Component.Component
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMaxHealth(float parMaxHealth)
|
||||
{
|
||||
_healthModel.MaxHealth = parMaxHealth;
|
||||
_healthModel.Health = parMaxHealth;
|
||||
}
|
||||
|
||||
public void TakeDamage(float parDamage)
|
||||
{
|
||||
_healthModel.Health -= parDamage;
|
||||
@@ -28,4 +42,12 @@ public class HealthController : Engine.Scene.Component.Component
|
||||
{
|
||||
_healthModel.Health += parHeal;
|
||||
}
|
||||
|
||||
private void OnHealthChanged(HealthModel parHealthModel)
|
||||
{
|
||||
if (parHealthModel.Health <= 0)
|
||||
{
|
||||
OnDeath?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class MovementController : Engine.Scene.Component.Component
|
||||
{
|
||||
public float Speed { get; set; } = 10.0f;
|
||||
|
||||
private RigidbodyComponent _rigidbody = null!;
|
||||
private DragComponent _dragComponent = null!;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_rigidbody = GameObject.GetComponent<RigidbodyComponent>()!;
|
||||
_dragComponent = GameObject.GetComponent<DragComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_rigidbody);
|
||||
ArgumentNullException.ThrowIfNull(_dragComponent);
|
||||
}
|
||||
|
||||
public void ApplyMovement(Vector3 parDirection)
|
||||
{
|
||||
_rigidbody.AddForce(_dragComponent.Drag * Speed * parDirection);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,45 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Input;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class PlayerController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; set; } = false;
|
||||
public HealthController HealthController => _healthController;
|
||||
public PerspectiveCamera Camera => _camera;
|
||||
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
|
||||
private HealthController _healthController = null!;
|
||||
private WeaponController _weaponController = null!;
|
||||
private ScoreController _scoreController = null!;
|
||||
private readonly PerspectiveCamera _camera;
|
||||
|
||||
public PlayerController(PerspectiveCamera parCamera)
|
||||
{
|
||||
_camera = parCamera;
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_healthController = GameObject.GetComponent<HealthController>()!;
|
||||
_weaponController = GameObject.GetComponent<WeaponController>()!;
|
||||
_scoreController = GameObject.GetComponent<ScoreController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_healthController);
|
||||
ArgumentNullException.ThrowIfNull(_weaponController);
|
||||
ArgumentNullException.ThrowIfNull(_scoreController);
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (IsPaused)
|
||||
return;
|
||||
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.C))
|
||||
_weaponController.AddWeapon(WeaponData.Shotgun);
|
||||
|
||||
@@ -40,6 +56,42 @@ public class PlayerController : Engine.Scene.Component.Component
|
||||
}
|
||||
|
||||
if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Space))
|
||||
_weaponController.TryShoot();
|
||||
{
|
||||
if (!_weaponController.TryShoot())
|
||||
return;
|
||||
|
||||
var position = _camera.GameObject.Transform.GetFullTranslation();
|
||||
var forward = (_camera.Forward - position).Normalized();
|
||||
var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized();
|
||||
|
||||
var collisionManager = GameObject.Scene!.FindFirstComponent<CollisionManager>();
|
||||
|
||||
var offsets = _weaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right);
|
||||
foreach (var offset in offsets)
|
||||
{
|
||||
var direction = forward + offset;
|
||||
if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result))
|
||||
continue;
|
||||
|
||||
var hitDisplayObject = GameObjectUtil.CreateGameObject(GameObject.Scene,
|
||||
new Transform { Translation = result.HitPoint, Size = new Vector3(0.1f) },
|
||||
[
|
||||
new Box2DRenderer { Color = new Vector4(0, 0, 1, 1), RenderLayer = RenderLayer.DEFAULT },
|
||||
new BillboardComponent { Target = _camera.GameObject.Transform }
|
||||
]
|
||||
);
|
||||
|
||||
var hitNormalDisplayObject = GameObjectUtil.CreateGameObject(GameObject.Scene,
|
||||
new Transform { Translation = result.HitPoint + result.Normal, Size = new Vector3(0.1f) },
|
||||
[
|
||||
new Box2DRenderer { Color = new Vector4(1, 0, 1, 1), RenderLayer = RenderLayer.DEFAULT },
|
||||
new BillboardComponent { Target = _camera.GameObject.Transform }
|
||||
]
|
||||
);
|
||||
|
||||
var enemyController = result.HitObject.GetComponent<EnemyController>();
|
||||
enemyController?.HealthController.TakeDamage(_weaponController.WeaponData.Damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class ScoreController : Engine.Scene.Component.Component
|
||||
{
|
||||
public int Score => _scoreModel.Score;
|
||||
|
||||
private readonly ScoreModel _scoreModel = new();
|
||||
private ScoreView _scoreView = null!;
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
public class TimerController : Engine.Scene.Component.Component
|
||||
{
|
||||
public bool IsPaused { get; set; } = false;
|
||||
public event Action? OnFinished;
|
||||
|
||||
private readonly TickableTimer _tickableTimer = new(60 + 10);
|
||||
private TimerView _timerView = null!;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_timerView = GameObject.GetComponent<TimerView>()!;
|
||||
_timerView.UpdateView(_tickableTimer.CurrentTime);
|
||||
|
||||
_tickableTimer.OnUpdate += OnTimeChanged;
|
||||
_tickableTimer.OnFinished += OnFinished;
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (IsPaused)
|
||||
return;
|
||||
|
||||
_tickableTimer.Update(parDeltaTime);
|
||||
}
|
||||
|
||||
private void OnTimeChanged(double parTime)
|
||||
{
|
||||
_timerView.UpdateView(parTime);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
@@ -6,6 +7,7 @@ namespace DoomDeathmatch.Component.MVC.Controller;
|
||||
public class WeaponController : Engine.Scene.Component.Component
|
||||
{
|
||||
public event Action<WeaponData>? OnWeaponShot;
|
||||
public WeaponData WeaponData => _weaponModel.SelectedWeapon;
|
||||
|
||||
private readonly WeaponModel _weaponModel = new();
|
||||
private WeaponView _weaponView = null!;
|
||||
@@ -24,7 +26,7 @@ public class WeaponController : Engine.Scene.Component.Component
|
||||
if (_weaponModel.SelectedWeapon.Ammo <= 0)
|
||||
return false;
|
||||
|
||||
_weaponModel.SelectedWeapon.Ammo--;
|
||||
// _weaponModel.SelectedWeapon.Ammo--;
|
||||
|
||||
OnWeaponShot?.Invoke(_weaponModel.SelectedWeapon);
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public abstract class AttackBehavior(EnemyController parEnemyController, HealthController parHealthController)
|
||||
{
|
||||
protected readonly EnemyController _enemyController = parEnemyController;
|
||||
protected readonly HealthController _healthController = parHealthController;
|
||||
|
||||
public abstract bool Attack(double parDeltaTime);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public class CloseContinuousAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
HealthController parHealthController,
|
||||
float parRadius,
|
||||
float parDamage)
|
||||
: AttackBehavior(parEnemyController, parHealthController)
|
||||
{
|
||||
public override bool Attack(double parDeltaTime)
|
||||
{
|
||||
var distanceSquared =
|
||||
(_enemyController.GameObject.Transform.Translation - _healthController.GameObject.Transform.Translation)
|
||||
.LengthSquared;
|
||||
|
||||
if (distanceSquared <= parRadius * parRadius)
|
||||
{
|
||||
_healthController.TakeDamage(parDamage * (float)parDeltaTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public class CloseCooldownAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
HealthController parHealthController,
|
||||
float parRadius,
|
||||
float parCooldown,
|
||||
float parDamage)
|
||||
: CooldownAttackBehavior(parEnemyController, parHealthController, parCooldown)
|
||||
{
|
||||
protected override bool CanAttack()
|
||||
{
|
||||
var distanceSquared =
|
||||
(_enemyController.GameObject.Transform.Translation - _healthController.GameObject.Transform.Translation)
|
||||
.LengthSquared;
|
||||
|
||||
return distanceSquared <= parRadius * parRadius;
|
||||
}
|
||||
|
||||
protected override bool ActivateAttack()
|
||||
{
|
||||
_healthController.TakeDamage(parDamage);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public class CompositeAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
HealthController parHealthController,
|
||||
List<AttackBehavior> parBehaviors)
|
||||
: AttackBehavior(parEnemyController, parHealthController)
|
||||
{
|
||||
public override bool Attack(double parDeltaTime)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
foreach (var behavior in parBehaviors)
|
||||
{
|
||||
result |= behavior.Attack(parDeltaTime);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using Engine.Util;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public abstract class CooldownAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
HealthController parHealthController,
|
||||
float parCooldown)
|
||||
: AttackBehavior(parEnemyController, parHealthController)
|
||||
{
|
||||
private readonly TickableTimer _tickableTimer = new(parCooldown);
|
||||
|
||||
public sealed override bool Attack(double parDeltaTime)
|
||||
{
|
||||
_tickableTimer.Update(parDeltaTime);
|
||||
|
||||
if (CanAttack())
|
||||
{
|
||||
if (!_tickableTimer.IsFinished)
|
||||
return false;
|
||||
|
||||
var result = ActivateAttack();
|
||||
|
||||
_tickableTimer.Reset();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected abstract bool CanAttack();
|
||||
protected abstract bool ActivateAttack();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public class FuncAttackBehaviorCreator(Func<EnemyController, HealthController, AttackBehavior> parFunc)
|
||||
: IAttackBehaviorCreator
|
||||
{
|
||||
public AttackBehavior Create(EnemyController parEnemyController, HealthController parHealthController)
|
||||
{
|
||||
return parFunc(parEnemyController, parHealthController);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public interface IAttackBehaviorCreator
|
||||
{
|
||||
public AttackBehavior Create(EnemyController parEnemyController, HealthController parHealthController);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using Engine.Scene;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
|
||||
public class ObjectSpawnAttackBehavior(
|
||||
EnemyController parEnemyController,
|
||||
HealthController parHealthController,
|
||||
float parCooldown,
|
||||
Func<EnemyController, HealthController, GameObject> parObjectSpawnFunc
|
||||
)
|
||||
: CooldownAttackBehavior(parEnemyController, parHealthController, parCooldown)
|
||||
{
|
||||
protected override bool CanAttack()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ActivateAttack()
|
||||
{
|
||||
var enemyObject = parObjectSpawnFunc(_enemyController, _healthController);
|
||||
_enemyController.GameObject.Scene!.Add(enemyObject);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
83
DoomDeathmatch/src/Component/MVC/Model/Enemy/EnemyData.cs
Normal file
83
DoomDeathmatch/src/Component/MVC/Model/Enemy/EnemyData.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy.Attack;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
using DoomDeathmatch.Scene.Play;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
|
||||
public class EnemyData
|
||||
{
|
||||
public static EnemyData Demon =>
|
||||
new()
|
||||
{
|
||||
Id = "demon",
|
||||
Name = "Демон",
|
||||
Texture = "texture/demon.png",
|
||||
BaseHealth = 150,
|
||||
BaseScore = 50,
|
||||
BaseSpeed = 8,
|
||||
MovementBehavior = new FollowPlayerMovementBehavior(1.5f),
|
||||
AttackBehaviorCreator = new FuncAttackBehaviorCreator(
|
||||
(parEnemyController, parHealthController) =>
|
||||
new CloseCooldownAttackBehavior(parEnemyController, parHealthController, 1.75f, 2.5f, 10)
|
||||
)
|
||||
};
|
||||
|
||||
public static EnemyData Imp =>
|
||||
new()
|
||||
{
|
||||
Id = "imp",
|
||||
Name = "Имп",
|
||||
Texture = "texture/imp.png",
|
||||
BaseHealth = 300,
|
||||
BaseScore = 200,
|
||||
BaseSpeed = 7,
|
||||
MovementBehavior = new FollowPlayerMovementBehavior(10f),
|
||||
AttackBehaviorCreator = new FuncAttackBehaviorCreator(
|
||||
(parEnemyController, parHealthController) =>
|
||||
new CompositeAttackBehavior(parEnemyController, parHealthController,
|
||||
[
|
||||
new CloseContinuousAttackBehavior(parEnemyController, parHealthController, 1.75f, 10f),
|
||||
new ObjectSpawnAttackBehavior(parEnemyController, parHealthController, 4f,
|
||||
(parEnemyController, parHealthController) =>
|
||||
{
|
||||
var direction =
|
||||
(parHealthController.GameObject.Transform.Translation -
|
||||
parEnemyController.GameObject.Transform.Translation).Normalized();
|
||||
|
||||
var fireballObject = EnemyObject.CreateImpFireball(parEnemyController.GameObject.Scene!,
|
||||
parEnemyController.GameObject.Transform.Translation + new Vector3(0, 0f, 1.75f),
|
||||
direction * 25,
|
||||
35,
|
||||
parHealthController.GameObject.Transform
|
||||
);
|
||||
|
||||
return fireballObject;
|
||||
}
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
public string Id { get; private init; } = "";
|
||||
public string Name { get; private init; } = "";
|
||||
public string Texture { get; private init; } = "";
|
||||
public float BaseHealth { get; private init; }
|
||||
public int BaseScore { get; private init; }
|
||||
public float BaseSpeed { get; private init; }
|
||||
public IMovementBehavior MovementBehavior { get; private init; }
|
||||
public IAttackBehaviorCreator AttackBehaviorCreator { get; private init; }
|
||||
|
||||
private EnemyData() { }
|
||||
|
||||
public override bool Equals(object? parObj)
|
||||
{
|
||||
return parObj is EnemyData enemyData && Id == enemyData.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
|
||||
public class FollowPlayerMovementBehavior(float parRadius) : IMovementBehavior
|
||||
{
|
||||
public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition)
|
||||
{
|
||||
var direction = (parPosition - parPlayerPosition).Normalized();
|
||||
return parPlayerPosition + parRadius * direction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
|
||||
public interface IMovementBehavior
|
||||
{
|
||||
public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Enemy.Movement;
|
||||
|
||||
public class StandingMovementBehavior : IMovementBehavior
|
||||
{
|
||||
public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition)
|
||||
{
|
||||
return parPosition;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ public class HealthModel
|
||||
set
|
||||
{
|
||||
_maxHealth = Math.Max(value, 1);
|
||||
_health = Math.Clamp(_health, 0, _maxHealth);
|
||||
HealthChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +18,11 @@ public class HealthModel
|
||||
get => _health;
|
||||
set
|
||||
{
|
||||
_health = Math.Clamp(value, 0, MaxHealth);
|
||||
value = Math.Clamp(value, 0, MaxHealth);
|
||||
if (Math.Abs(_health - value) < float.Epsilon)
|
||||
return;
|
||||
|
||||
_health = value;
|
||||
HealthChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
|
||||
public interface IShootPattern
|
||||
{
|
||||
public IEnumerable<Vector3> GetShootPattern(Vector3 parForward, Vector3 parUp, Vector3 parRight);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
|
||||
public class LineShootPattern : IShootPattern
|
||||
{
|
||||
public IEnumerable<Vector3> GetShootPattern(Vector3 parForward, Vector3 parUp, Vector3 parRight)
|
||||
{
|
||||
return [Vector3.Zero];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
|
||||
public class RandomFlatSpreadShootPattern(float parAngle, uint parCount) : IShootPattern
|
||||
{
|
||||
private readonly Random _random = new();
|
||||
|
||||
public IEnumerable<Vector3> GetShootPattern(Vector3 parForward, Vector3 parUp, Vector3 parRight)
|
||||
{
|
||||
for (var i = 0; i < parCount; i++)
|
||||
{
|
||||
var angle = parAngle * ((float)_random.NextDouble() * 2 - 1);
|
||||
var delta = MathF.Tan(angle);
|
||||
|
||||
var offset = parRight * delta;
|
||||
|
||||
yield return offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,35 @@
|
||||
namespace DoomDeathmatch.Component.MVC.Model;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
|
||||
public class WeaponData
|
||||
{
|
||||
public static WeaponData Pistol =>
|
||||
new(30) { Id = "pistol", Name = "Пистолет", Texture = "texture/pistol.png", Damage = 10 };
|
||||
new(30)
|
||||
{
|
||||
Id = "pistol",
|
||||
Name = "Пистолет",
|
||||
Texture = "texture/pistol.png",
|
||||
Damage = 30,
|
||||
ShootPattern = new LineShootPattern()
|
||||
};
|
||||
|
||||
public static WeaponData Shotgun =>
|
||||
new(10) { Id = "shotgun", Name = "Дробовик", Texture = "texture/shotgun.png", Damage = 50 };
|
||||
new(10)
|
||||
{
|
||||
Id = "shotgun",
|
||||
Name = "Дробовик",
|
||||
Texture = "texture/shotgun.png",
|
||||
Damage = 5,
|
||||
ShootPattern = new RandomFlatSpreadShootPattern(MathHelper.DegreesToRadians(20), 40)
|
||||
};
|
||||
|
||||
public string Id { get; private init; } = "";
|
||||
public string Name { get; private init; } = "";
|
||||
public string Texture { get; private init; } = "";
|
||||
public int Damage { get; private init; }
|
||||
public int MaxAmmo { get; }
|
||||
public IShootPattern ShootPattern { get; private init; }
|
||||
|
||||
public int Ammo
|
||||
{
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.Model;
|
||||
|
||||
public class WeaponModel
|
||||
{
|
||||
|
||||
16
DoomDeathmatch/src/Component/MVC/View/EnemyHealthView.cs
Normal file
16
DoomDeathmatch/src/Component/MVC/View/EnemyHealthView.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
|
||||
public class EnemyHealthView : HealthView
|
||||
{
|
||||
public EnemyHealthView(TextRenderer parHealthTextRenderer) : base(parHealthTextRenderer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void UpdateView(HealthModel parHealthModel)
|
||||
{
|
||||
_healthTextRenderer.Text = $"Здоровье: {parHealthModel.Health:000}";
|
||||
}
|
||||
}
|
||||
20
DoomDeathmatch/src/Component/MVC/View/EnemyView.cs
Normal file
20
DoomDeathmatch/src/Component/MVC/View/EnemyView.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
|
||||
public class EnemyView : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly Box2DRenderer _box2DRenderer;
|
||||
|
||||
public EnemyView(Box2DRenderer parBox2DRenderer)
|
||||
{
|
||||
_box2DRenderer = parBox2DRenderer;
|
||||
}
|
||||
|
||||
public void UpdateView(EnemyData parEnemyData)
|
||||
{
|
||||
_box2DRenderer.Texture = Engine.Engine.Instance.AssetResourceManager.Load<Texture>(parEnemyData.Texture);
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,14 @@ namespace DoomDeathmatch.Component.MVC.View;
|
||||
|
||||
public class HealthView : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly TextRenderer _healthTextRenderer;
|
||||
protected readonly TextRenderer _healthTextRenderer;
|
||||
|
||||
public HealthView(TextRenderer parHealthTextRenderer)
|
||||
{
|
||||
_healthTextRenderer = parHealthTextRenderer;
|
||||
}
|
||||
|
||||
public void UpdateView(HealthModel parHealthModel)
|
||||
public virtual void UpdateView(HealthModel parHealthModel)
|
||||
{
|
||||
var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100;
|
||||
if (parHealthModel.Health != 0)
|
||||
|
||||
21
DoomDeathmatch/src/Component/MVC/View/TimerView.cs
Normal file
21
DoomDeathmatch/src/Component/MVC/View/TimerView.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
namespace DoomDeathmatch.Component.MVC.View;
|
||||
|
||||
public class TimerView : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly TextRenderer _timerTextRenderer;
|
||||
|
||||
public TimerView(TextRenderer parTimerTextRenderer)
|
||||
{
|
||||
_timerTextRenderer = parTimerTextRenderer;
|
||||
}
|
||||
|
||||
public void UpdateView(double parTime)
|
||||
{
|
||||
var seconds = Math.Floor(parTime) % 60;
|
||||
var minutes = Math.Floor(parTime / 60) % 60;
|
||||
|
||||
_timerTextRenderer.Text = $"Время: {minutes:00}:{seconds:00}";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using DoomDeathmatch.Component.MVC.Model;
|
||||
using DoomDeathmatch.Component.MVC.Model.Weapon;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
|
||||
|
||||
26
DoomDeathmatch/src/Component/UI/MenuControllerComponent.cs
Normal file
26
DoomDeathmatch/src/Component/UI/MenuControllerComponent.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Engine.Scene;
|
||||
|
||||
namespace DoomDeathmatch.Component.UI;
|
||||
|
||||
public class MenuControllerComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly Dictionary<string, GameObject> _menuItems = new();
|
||||
|
||||
public void AddMenuItem(string parName, GameObject parGameObject)
|
||||
{
|
||||
_menuItems.Add(parName, parGameObject);
|
||||
}
|
||||
|
||||
public void RemoveMenuItem(string parName)
|
||||
{
|
||||
_menuItems.Remove(parName);
|
||||
}
|
||||
|
||||
public void SelectMenuItem(string parName)
|
||||
{
|
||||
foreach (var (name, menuItem) in _menuItems)
|
||||
{
|
||||
menuItem.IsEnabled = name == parName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,11 @@ public class BillboardComponent : Engine.Scene.Component.Component
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPosition = Target.TransformMatrix.ExtractTranslation();
|
||||
var currentPosition = GameObject.Transform.Translation;
|
||||
var targetPosition = Target.GetFullTranslation();
|
||||
var currentPosition = GameObject.Transform.GetFullTranslation();
|
||||
|
||||
var forward = targetPosition - currentPosition;
|
||||
forward -= Vector3.Dot(forward, Up) * Up;
|
||||
if (forward.LengthSquared > 0)
|
||||
forward.Normalize();
|
||||
|
||||
@@ -26,7 +27,7 @@ public class BillboardComponent : Engine.Scene.Component.Component
|
||||
if (right.LengthSquared > 0)
|
||||
right.Normalize();
|
||||
|
||||
var recalculatedUp = Vector3.Cross(forward, right);
|
||||
var recalculatedUp = Vector3.Cross(forward, right).Normalized();
|
||||
|
||||
var rotationMatrix = new Matrix3(
|
||||
right.X, recalculatedUp.X, forward.X,
|
||||
@@ -34,8 +35,6 @@ public class BillboardComponent : Engine.Scene.Component.Component
|
||||
right.Z, recalculatedUp.Z, forward.Z
|
||||
);
|
||||
|
||||
var rotation = Quaternion.FromMatrix(rotationMatrix);
|
||||
|
||||
GameObject.Transform.Rotation = rotation;
|
||||
GameObject.Transform.Rotation = Quaternion.FromMatrix(rotationMatrix);
|
||||
}
|
||||
}
|
||||
53
DoomDeathmatch/src/Component/Util/Collision/AABBCollider.cs
Normal file
53
DoomDeathmatch/src/Component/Util/Collision/AABBCollider.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
|
||||
public class AABBCollider
|
||||
{
|
||||
public Vector3 Size { get; set; }
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
public Vector3 Max => Position + Size / 2;
|
||||
public Vector3 Min => Position - Size / 2;
|
||||
|
||||
public bool Intersects(AABBCollider parCollider)
|
||||
{
|
||||
var max = Max;
|
||||
var min = Min;
|
||||
var otherMax = parCollider.Max;
|
||||
var otherMin = parCollider.Min;
|
||||
|
||||
return max.X >= otherMin.X && min.X <= otherMax.X && max.Y >= otherMin.Y && min.Y <= otherMax.Y &&
|
||||
max.Z >= otherMin.Z && min.Z <= otherMax.Z;
|
||||
}
|
||||
|
||||
public Vector3 GetCollisionNormal(AABBCollider parOther)
|
||||
{
|
||||
var normal = Vector3.Zero;
|
||||
var diff = parOther.Position - Position;
|
||||
|
||||
// Calculate penetration depths for each axis
|
||||
var penX = (Size.X / 2 + parOther.Size.X / 2) - Math.Abs(diff.X);
|
||||
var penY = (Size.Y / 2 + parOther.Size.Y / 2) - Math.Abs(diff.Y);
|
||||
var penZ = (Size.Z / 2 + parOther.Size.Z / 2) - Math.Abs(diff.Z);
|
||||
|
||||
// Use the axis with the smallest penetration
|
||||
if (penX < penY && penX < penZ)
|
||||
{
|
||||
var sign = Math.Sign(diff.X);
|
||||
normal.X = sign == 0 ? 1 : sign;
|
||||
}
|
||||
else if (penY < penX && penY < penZ)
|
||||
{
|
||||
var sign = Math.Sign(diff.Y);
|
||||
normal.Y = sign == 0 ? 1 : sign;
|
||||
}
|
||||
else
|
||||
{
|
||||
var sign = Math.Sign(diff.Z);
|
||||
normal.Z = sign == 0 ? 1 : sign;
|
||||
}
|
||||
|
||||
return normal.Normalized();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
|
||||
public class AABBColliderComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public event Action<AABBColliderComponent>? OnCollision;
|
||||
|
||||
public AABBCollider Collider { get; set; } = new();
|
||||
public Vector3 Offset { get; set; } = Vector3.Zero;
|
||||
public ISet<string> ColliderGroups => _colliderGroups;
|
||||
public ISet<string> ExcludeColliderCollideGroups => _excludeColliderCollideGroups;
|
||||
|
||||
private readonly HashSet<string> _colliderGroups = ["default"];
|
||||
private readonly HashSet<string> _excludeColliderCollideGroups = [];
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
Collider.Position = GameObject.Transform.GetFullTranslation() + Offset;
|
||||
}
|
||||
|
||||
public void CollideWith(AABBColliderComponent parCollider)
|
||||
{
|
||||
OnCollision?.Invoke(parCollider);
|
||||
}
|
||||
|
||||
public bool InColliderGroup(string parGroup)
|
||||
{
|
||||
return ColliderGroups.Contains(parGroup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
|
||||
public class ColliderForceFieldComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
private AABBColliderComponent _collider = null!;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_collider = GameObject.GetComponent<AABBColliderComponent>()!;
|
||||
_collider.OnCollision += OnCollision;
|
||||
}
|
||||
|
||||
private void OnCollision(AABBColliderComponent parCollider)
|
||||
{
|
||||
var rigidbody = parCollider.GameObject.GetComponent<RigidbodyComponent>();
|
||||
if (rigidbody == null)
|
||||
return;
|
||||
|
||||
var normal = _collider.Collider.GetCollisionNormal(parCollider.Collider);
|
||||
var speedAlongNormal = Vector3.Dot(rigidbody.Velocity, normal);
|
||||
if (speedAlongNormal >= 0)
|
||||
return;
|
||||
|
||||
rigidbody.AddVelocity(-normal * speedAlongNormal);
|
||||
}
|
||||
}
|
||||
164
DoomDeathmatch/src/Component/Util/Collision/CollisionManager.cs
Normal file
164
DoomDeathmatch/src/Component/Util/Collision/CollisionManager.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
|
||||
public class CollisionManager : Engine.Scene.Component.Component
|
||||
{
|
||||
private List<AABBColliderComponent> _colliders = [];
|
||||
|
||||
public override void PreUpdate()
|
||||
{
|
||||
_colliders = GameObject.Scene!.FindAllComponents<AABBColliderComponent>();
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
for (var i = 0; i < _colliders.Count; i++)
|
||||
{
|
||||
var colliderA = _colliders[i];
|
||||
|
||||
for (var j = i + 1; j < _colliders.Count; j++)
|
||||
{
|
||||
var colliderB = _colliders[j];
|
||||
|
||||
var canCollideAB = colliderA.ExcludeColliderCollideGroups.Count == 0 ||
|
||||
!colliderA.ExcludeColliderCollideGroups.IsSubsetOf(colliderB.ColliderGroups);
|
||||
var canCollideBA = colliderB.ExcludeColliderCollideGroups.Count == 0 ||
|
||||
!colliderB.ExcludeColliderCollideGroups.IsSubsetOf(colliderA.ColliderGroups);
|
||||
|
||||
if (!canCollideAB && !canCollideBA)
|
||||
continue;
|
||||
|
||||
if (!colliderA.Collider.Intersects(colliderB.Collider))
|
||||
continue;
|
||||
|
||||
if (canCollideAB)
|
||||
colliderA.CollideWith(colliderB);
|
||||
|
||||
if (canCollideBA)
|
||||
colliderB.CollideWith(colliderA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Raycast(Vector3 parStart, Vector3 parDirection, HashSet<string> parColliderGroups,
|
||||
[MaybeNullWhen(false)] out RaycastResult parResult)
|
||||
{
|
||||
var start = parStart;
|
||||
var direction = parDirection.Normalized();
|
||||
|
||||
var closestDistance = float.MaxValue;
|
||||
|
||||
parResult = null;
|
||||
foreach (var collider in _colliders)
|
||||
{
|
||||
if (!collider.ColliderGroups.Overlaps(parColliderGroups))
|
||||
continue;
|
||||
|
||||
if (!RaycastAABB(start, direction, collider.Collider, out var hitPoint, out var normal))
|
||||
continue;
|
||||
|
||||
var distance = (start - hitPoint).Length;
|
||||
if (distance > closestDistance)
|
||||
continue;
|
||||
|
||||
closestDistance = distance;
|
||||
|
||||
parResult = new RaycastResult
|
||||
{
|
||||
HitPoint = hitPoint, Normal = normal, Distance = distance, HitObject = collider.GameObject
|
||||
};
|
||||
}
|
||||
|
||||
return parResult != null;
|
||||
}
|
||||
|
||||
private static bool RaycastAABB(Vector3 parOrigin, Vector3 parDirection, AABBCollider parCollider,
|
||||
out Vector3 parHitPoint,
|
||||
out Vector3 parHitNormal)
|
||||
{
|
||||
const int RIGHT = 0;
|
||||
const int LEFT = 1;
|
||||
const int MIDDLE = 2;
|
||||
|
||||
parHitPoint = Vector3.Zero;
|
||||
parHitNormal = Vector3.Zero;
|
||||
|
||||
var minB = parCollider.Min;
|
||||
var maxB = parCollider.Max;
|
||||
|
||||
// Initialize arrays to store the 3D components
|
||||
var inside = true;
|
||||
var quadrant = new int[3];
|
||||
var maxT = new float[3];
|
||||
var candidatePlane = new float[3];
|
||||
|
||||
// Find candidate planes
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
if (parOrigin[i] < minB[i])
|
||||
{
|
||||
quadrant[i] = LEFT;
|
||||
candidatePlane[i] = minB[i];
|
||||
inside = false;
|
||||
}
|
||||
else if (parOrigin[i] > maxB[i])
|
||||
{
|
||||
quadrant[i] = RIGHT;
|
||||
candidatePlane[i] = maxB[i];
|
||||
inside = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
quadrant[i] = MIDDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// Ray origin inside bounding box
|
||||
if (inside)
|
||||
{
|
||||
parHitPoint = parOrigin;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate T distances to candidate planes
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
if (quadrant[i] != MIDDLE && parDirection[i] != 0.0f)
|
||||
maxT[i] = (candidatePlane[i] - parOrigin[i]) / parDirection[i];
|
||||
else
|
||||
maxT[i] = -1.0f;
|
||||
}
|
||||
|
||||
// Get largest of the maxT's for final choice of intersection
|
||||
var whichPlane = 0;
|
||||
for (var i = 1; i < 3; i++)
|
||||
{
|
||||
if (maxT[whichPlane] < maxT[i])
|
||||
whichPlane = i;
|
||||
}
|
||||
|
||||
// Check final candidate actually inside box
|
||||
if (maxT[whichPlane] < 0.0f)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
if (whichPlane != i)
|
||||
{
|
||||
parHitPoint[i] = parOrigin[i] + maxT[whichPlane] * parDirection[i];
|
||||
if (parHitPoint[i] < minB[i] || parHitPoint[i] > maxB[i])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
parHitPoint[i] = candidatePlane[i];
|
||||
// Calculate normal for the intersection plane
|
||||
parHitNormal[i] = (quadrant[i] == LEFT) ? -1.0f : 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
12
DoomDeathmatch/src/Component/Util/Collision/RaycastResult.cs
Normal file
12
DoomDeathmatch/src/Component/Util/Collision/RaycastResult.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Engine.Scene;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util.Collision;
|
||||
|
||||
public class RaycastResult
|
||||
{
|
||||
public float Distance { get; init; }
|
||||
public Vector3 HitPoint { get; init; }
|
||||
public Vector3 Normal { get; init; }
|
||||
public GameObject HitObject { get; init; }
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
using Engine.Input;
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using Engine.Input;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class ControllerComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public float Speed { get; set; } = 10.0f;
|
||||
public float RotationSpeed { get; set; } = 70.0f;
|
||||
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
private RigidbodyComponent _rigidbody;
|
||||
private MovementController _movementController = null!;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_rigidbody = GameObject.GetComponent<RigidbodyComponent>()!;
|
||||
_movementController = GameObject.GetComponent<MovementController>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_movementController);
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
@@ -40,14 +42,7 @@ public class ControllerComponent : Engine.Scene.Component.Component
|
||||
movement.Normalize();
|
||||
movement = GameObject.Transform.Rotation * movement;
|
||||
|
||||
_rigidbody.AddVelocity(Speed * movement);
|
||||
}
|
||||
|
||||
var velocityXy = _rigidbody.Velocity.Xy;
|
||||
if (velocityXy.LengthSquared > Speed * Speed)
|
||||
{
|
||||
var length = velocityXy.Length;
|
||||
_rigidbody.AddVelocity(new Vector3(-(length - Speed) / length * velocityXy));
|
||||
_movementController.ApplyMovement(movement);
|
||||
}
|
||||
|
||||
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, MathHelper.DegreesToRadians(rotation) *
|
||||
|
||||
@@ -7,11 +7,13 @@ public class DragComponent : Engine.Scene.Component.Component
|
||||
public float Drag { get; set; } = 1f;
|
||||
public Vector3 Coefficient { get; set; } = Vector3.One;
|
||||
|
||||
private RigidbodyComponent _rigidbody;
|
||||
private RigidbodyComponent _rigidbody = null!;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_rigidbody = GameObject.GetComponent<RigidbodyComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_rigidbody);
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
|
||||
26
DoomDeathmatch/src/Component/Util/FireballComponent.cs
Normal file
26
DoomDeathmatch/src/Component/Util/FireballComponent.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class FireballComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
public float Damage { get; set; }
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
var collider = GameObject.GetComponent<AABBColliderComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(collider);
|
||||
|
||||
collider.OnCollision += OnCollision;
|
||||
}
|
||||
|
||||
private void OnCollision(AABBColliderComponent parCollider)
|
||||
{
|
||||
var healthController = parCollider.GameObject.GetComponent<HealthController>();
|
||||
healthController?.TakeDamage(Damage);
|
||||
|
||||
GameObject.Scene!.Remove(GameObject);
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ public class GravityComponent : Engine.Scene.Component.Component
|
||||
|
||||
public bool IsInAir { get; private set; } = false;
|
||||
|
||||
private RigidbodyComponent _rigidbody;
|
||||
private RigidbodyComponent _rigidbody = null!;
|
||||
private Vector3 _direction = -Vector3.UnitZ;
|
||||
|
||||
public override void Awake()
|
||||
{
|
||||
_rigidbody = GameObject.GetComponent<RigidbodyComponent>()!;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(_rigidbody);
|
||||
}
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using Engine.Input;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Component.Util;
|
||||
|
||||
public class RotateComponent : Engine.Scene.Component.Component
|
||||
{
|
||||
private readonly IInputHandler _inputHandler = Engine.Engine.Instance.InputHandler!;
|
||||
|
||||
public override void Update(double parDeltaTime)
|
||||
{
|
||||
if (_inputHandler.IsKeyPressed(KeyboardButtonCode.Q))
|
||||
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitY, (float)parDeltaTime * 0.5f);
|
||||
else if (_inputHandler.IsKeyPressed(KeyboardButtonCode.E))
|
||||
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitY, -(float)parDeltaTime * 0.5f);
|
||||
|
||||
if (_inputHandler.IsMouseButtonPressed(MouseButtonCode.Left))
|
||||
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, (float)parDeltaTime * 0.5f);
|
||||
else if (_inputHandler.IsMouseButtonPressed(MouseButtonCode.Right))
|
||||
GameObject.Transform.Rotation *= Quaternion.FromAxisAngle(Vector3.UnitZ, -(float)parDeltaTime * 0.5f);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using DoomDeathmatch.Component;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.Main;
|
||||
using DoomDeathmatch.Scene.Rules;
|
||||
using Engine.Asset.Font;
|
||||
using Engine.Asset.Font.Metadata;
|
||||
using Engine.Asset.Mesh;
|
||||
using Engine.Asset.Mesh.Loader;
|
||||
using Engine.Graphics.Camera;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
using DoomDeathmatch.Scene;
|
||||
|
||||
namespace DoomDeathmatch;
|
||||
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
using Engine.Scene;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch;
|
||||
|
||||
public static class GameObjectUtil
|
||||
{
|
||||
public static GameObject CreateGameObject(Engine.Scene.Scene parScene)
|
||||
{
|
||||
var gameObject = new GameObject();
|
||||
|
||||
parScene.Add(gameObject);
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public static GameObject CreateGameObject(Engine.Scene.Scene parScene, Transform parTransform)
|
||||
{
|
||||
var gameObject = new GameObject(parTransform);
|
||||
|
||||
parScene.Add(gameObject);
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public static GameObject CreateGameObject(Engine.Scene.Scene parScene,
|
||||
List<Engine.Scene.Component.Component> parComponents)
|
||||
{
|
||||
@@ -32,4 +53,92 @@ public static class GameObjectUtil
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public static GameObject CreateColliderForceField(Engine.Scene.Scene parScene, AABBColliderComponent parCollider)
|
||||
{
|
||||
var offset = parCollider.Offset;
|
||||
parCollider.Offset = Vector3.Zero;
|
||||
|
||||
var forceFieldObject = CreateGameObject(parScene,
|
||||
new Transform { Translation = offset, Size = parCollider.Collider.Size },
|
||||
[
|
||||
parCollider,
|
||||
new ColliderForceFieldComponent()
|
||||
]
|
||||
);
|
||||
|
||||
// var color = new Vector4(1, 0, 0, 0.5f);
|
||||
// var size = parCollider.Collider.Size;
|
||||
//
|
||||
// var frontPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(0, -size.Y / 2, 0),
|
||||
// Size = new Vector3(size.X, size.Z, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var backPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(0, size.Y / 2, 0),
|
||||
// Size = new Vector3(size.X, size.Z, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitX, -MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var leftPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(-size.X / 2, 0, 0),
|
||||
// Size = new Vector3(size.Z, size.Y, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var rightPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform
|
||||
// {
|
||||
// Translation = new Vector3(size.X / 2, 0, 0),
|
||||
// Size = new Vector3(size.Z, size.Y, 0),
|
||||
// Rotation = Quaternion.FromAxisAngle(Vector3.UnitY, -MathF.PI / 2)
|
||||
// },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var topPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform { Translation = new Vector3(0, 0, size.Z / 2), Size = new Vector3(size.X, size.Y, 0) },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// var bottomPlaneObject = CreateGameObject(parScene,
|
||||
// new Transform { Translation = new Vector3(0, 0, -size.Z / 2), Size = new Vector3(size.X, size.Y, 0) },
|
||||
// [
|
||||
// new Box2DRenderer { Color = color }
|
||||
// ]
|
||||
// );
|
||||
//
|
||||
// parScene.AddChild(forceFieldObject, frontPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, backPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, leftPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, rightPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, topPlaneObject);
|
||||
// parScene.AddChild(forceFieldObject, bottomPlaneObject);
|
||||
|
||||
return forceFieldObject;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.Main;
|
||||
using Engine.Asset.Font;
|
||||
using Engine.Input;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Leaders;
|
||||
|
||||
public static class LeadersScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
|
||||
|
||||
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(scene, uiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад");
|
||||
backUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = uiContainer, Children = { backUi } });
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene, new SelectorComponent { Children = { backUi } });
|
||||
|
||||
scene.AddChild(uiContainerObject, selectorObject);
|
||||
scene.AddChild(uiContainerObject, logoObject);
|
||||
scene.AddChild(uiContainerObject, stackObject);
|
||||
scene.AddChild(stackObject, backUiObject);
|
||||
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.Leaders;
|
||||
using DoomDeathmatch.Scene.Play;
|
||||
using DoomDeathmatch.Scene.Rules;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Main;
|
||||
|
||||
public static class MainScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
|
||||
|
||||
var (uiContainerObject, uiContainer) =
|
||||
UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
|
||||
|
||||
var (playUiObject, playUi, _) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Играть");
|
||||
|
||||
var (leadersUiObject, leadersUi, _) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Лидеры");
|
||||
|
||||
var (rulesUiObject, rulesUi, _) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Правила");
|
||||
|
||||
var (exitUiObject, exitUi, _) =
|
||||
UiUtil.CreateTextUi(scene, uiContainer, UiUtil.GetDoomFont(parEngine), "Выход");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
|
||||
new StackComponent
|
||||
{
|
||||
Offset = new Vector2(0, -1f), Container = uiContainer, Children = { playUi, leadersUi, rulesUi, exitUi }
|
||||
});
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
playUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => PlayScene.Create(parEngine));
|
||||
leadersUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => LeadersScene.Create(parEngine));
|
||||
rulesUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => RulesScene.Create(parEngine));
|
||||
exitUi.OnClick += _ => parEngine.Close();
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene,
|
||||
new SelectorComponent { Children = { playUi, leadersUi, rulesUi, exitUi } });
|
||||
|
||||
scene.AddChild(uiContainerObject, selectorObject);
|
||||
|
||||
scene.AddChild(uiContainerObject, stackObject);
|
||||
scene.AddChild(uiContainerObject, logoObject);
|
||||
|
||||
scene.AddChild(stackObject, playUiObject);
|
||||
scene.AddChild(stackObject, leadersUiObject);
|
||||
scene.AddChild(stackObject, rulesUiObject);
|
||||
scene.AddChild(stackObject, exitUiObject);
|
||||
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
138
DoomDeathmatch/src/Scene/MainScene.cs
Normal file
138
DoomDeathmatch/src/Scene/MainScene.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.Play;
|
||||
using Engine.Scene;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene;
|
||||
|
||||
public static class MainScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
|
||||
|
||||
var (uiContainerObject, uiContainer) =
|
||||
UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
scene.AddChild(uiContainerObject, logoObject);
|
||||
|
||||
var menuController = new MenuControllerComponent();
|
||||
var menuControllerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
menuController
|
||||
]);
|
||||
|
||||
var mainMenu = CreateMainMenu(parEngine, scene, uiContainer, menuController);
|
||||
menuController.AddMenuItem("main", mainMenu);
|
||||
|
||||
var leadersMenu = CreateLeadersMenu(parEngine, scene, uiContainer, menuController);
|
||||
leadersMenu.IsEnabled = false;
|
||||
menuController.AddMenuItem("leaders", leadersMenu);
|
||||
|
||||
var rulesMenu = CreateRulesMenu(parEngine, scene, uiContainer, menuController);
|
||||
rulesMenu.IsEnabled = false;
|
||||
menuController.AddMenuItem("rules", rulesMenu);
|
||||
|
||||
scene.AddChild(uiContainerObject, mainMenu);
|
||||
scene.AddChild(uiContainerObject, leadersMenu);
|
||||
scene.AddChild(uiContainerObject, rulesMenu);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
private static GameObject CreateMainMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (playUiObject, playUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Играть");
|
||||
|
||||
var (leadersUiObject, leadersUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Лидеры");
|
||||
|
||||
var (rulesUiObject, rulesUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Правила");
|
||||
|
||||
var (exitUiObject, exitUi, _) =
|
||||
UiUtil.CreateTextUi(parScene, parUiContainer, UiUtil.GetDoomFont(parEngine), "Выход");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent
|
||||
{
|
||||
Offset = new Vector2(0, -1f), Container = parUiContainer, Children = { playUi, leadersUi, rulesUi, exitUi }
|
||||
});
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
playUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => PlayScene.Create(parEngine));
|
||||
leadersUi.OnClick += _ => parMenuController.SelectMenuItem("leaders");
|
||||
rulesUi.OnClick += _ => parMenuController.SelectMenuItem("rules");
|
||||
exitUi.OnClick += _ => parEngine.Close();
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(parScene,
|
||||
new SelectorComponent { Children = { playUi, leadersUi, rulesUi, exitUi } });
|
||||
|
||||
parScene.AddChild(parentObject, selectorObject);
|
||||
parScene.AddChild(parentObject, stackObject);
|
||||
|
||||
parScene.AddChild(stackObject, playUiObject);
|
||||
parScene.AddChild(stackObject, leadersUiObject);
|
||||
parScene.AddChild(stackObject, rulesUiObject);
|
||||
parScene.AddChild(stackObject, exitUiObject);
|
||||
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
private static GameObject CreateLeadersMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(parScene, parUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад");
|
||||
backUi.OnClick += _ => parMenuController.SelectMenuItem("main");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = parUiContainer, Children = { backUi } });
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(parScene, new SelectorComponent { Children = { backUi } });
|
||||
|
||||
parScene.AddChild(parentObject, selectorObject);
|
||||
parScene.AddChild(parentObject, stackObject);
|
||||
parScene.AddChild(stackObject, backUiObject);
|
||||
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
private static GameObject CreateRulesMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(parScene, parUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад");
|
||||
backUi.OnClick += _ => parMenuController.SelectMenuItem("main");
|
||||
|
||||
var (rulesObject, rulesUi, _) = UiUtil.CreateTextUi(parScene, parUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Правила");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = parUiContainer, Children = { rulesUi, backUi } });
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(parScene, new SelectorComponent { Children = { backUi } });
|
||||
|
||||
parScene.AddChild(parentObject, selectorObject);
|
||||
|
||||
parScene.AddChild(parentObject, stackObject);
|
||||
|
||||
parScene.AddChild(stackObject, rulesObject);
|
||||
parScene.AddChild(stackObject, backUiObject);
|
||||
|
||||
return parentObject;
|
||||
}
|
||||
}
|
||||
90
DoomDeathmatch/src/Scene/Play/EnemyObject.cs
Normal file
90
DoomDeathmatch/src/Scene/Play/EnemyObject.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Play;
|
||||
|
||||
public static class EnemyObject
|
||||
{
|
||||
public static GameObject CreateEnemy(Engine.Engine parEngine, Engine.Scene.Scene parScene, EnemyData parEnemyData)
|
||||
{
|
||||
var enemyHealthTextRenderer = new TextRenderer { Font = UiUtil.GetDoomFont(parEngine), Text = "Здоровье: 000" };
|
||||
var displayBox2DRenderer = new Box2DRenderer();
|
||||
|
||||
var demonObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
[
|
||||
new EnemyController(parEnemyData),
|
||||
new EnemyView(displayBox2DRenderer),
|
||||
|
||||
new HealthController(parEnemyData.BaseHealth),
|
||||
new EnemyHealthView(enemyHealthTextRenderer),
|
||||
|
||||
new MovementController(),
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 5f, Coefficient = new Vector3(1, 1, 0) },
|
||||
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(1, 1, 2) },
|
||||
Offset = new Vector3(0, 0f, 1f),
|
||||
ColliderGroups = { "enemy" },
|
||||
ExcludeColliderCollideGroups = { "enemy" }
|
||||
},
|
||||
]);
|
||||
var demonHealthContainer = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Translation = new Vector3(0, 1.25f, 0f), Scale = new Vector3(0.2f) }
|
||||
);
|
||||
|
||||
var demonHealthObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
enemyHealthTextRenderer,
|
||||
new TextAlignComponent { Alignment = TextAlignComponent.Align.Center }
|
||||
]
|
||||
);
|
||||
var demonVisualObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Translation = new Vector3(0, 0f, 1f), Size = new Vector3(1, 2, 1) }, [
|
||||
displayBox2DRenderer,
|
||||
new BillboardComponent(),
|
||||
]
|
||||
);
|
||||
|
||||
parScene.AddChild(demonObject, demonVisualObject);
|
||||
parScene.AddChild(demonVisualObject, demonHealthContainer);
|
||||
parScene.AddChild(demonHealthContainer, demonHealthObject);
|
||||
|
||||
return demonObject;
|
||||
}
|
||||
|
||||
public static GameObject CreateImpFireball(Engine.Scene.Scene parScene, Vector3 parPosition,
|
||||
Vector3 parVelocity, float parDamage, Transform? parBillboardTarget = null)
|
||||
{
|
||||
var rigidbodyComponent = new RigidbodyComponent();
|
||||
rigidbodyComponent.AddVelocity(parVelocity);
|
||||
|
||||
var fireballObject = GameObjectUtil.CreateGameObject(parScene,
|
||||
new Transform { Translation = parPosition, Size = new Vector3(0.5f) },
|
||||
[
|
||||
rigidbodyComponent,
|
||||
new Box2DRenderer
|
||||
{
|
||||
Texture = Engine.Engine.Instance.AssetResourceManager.Load<Texture>("texture/fireball.png")
|
||||
},
|
||||
new BillboardComponent { Target = parBillboardTarget },
|
||||
new FireballComponent { Damage = parDamage },
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(0.25f) }, ExcludeColliderCollideGroups = { "enemy" }
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
return fireballObject;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using DoomDeathmatch.Component;
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Controller;
|
||||
using DoomDeathmatch.Component.MVC.Model.Enemy;
|
||||
using DoomDeathmatch.Component.MVC.View;
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Component.Util;
|
||||
using DoomDeathmatch.Component.Util.Collision;
|
||||
using Engine.Asset.Mesh;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Graphics.Texture;
|
||||
using Engine.Scene;
|
||||
using Engine.Scene.Component.BuiltIn;
|
||||
using Engine.Scene.Component.BuiltIn.Renderer;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
@@ -23,8 +23,141 @@ public static class PlayScene
|
||||
var (uiContainerObject, uiContainer) =
|
||||
UiUtil.CreateContainerUi(scene, new UiContainerComponent { Camera = hudCamera });
|
||||
|
||||
var menuController = new MenuControllerComponent();
|
||||
var menuControllerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
menuController
|
||||
]);
|
||||
|
||||
var (playUiObject, (playWeaponView, playHealthView, playScoreView, plaTimerView)) =
|
||||
CreateGameUi(parEngine, scene, uiContainer, menuController);
|
||||
menuController.AddMenuItem("play", playUiObject);
|
||||
scene.AddChild(uiContainerObject, playUiObject);
|
||||
|
||||
var escapeUiObject = CreateEscapeMenu(parEngine, scene, uiContainer, menuController);
|
||||
escapeUiObject.IsEnabled = false;
|
||||
menuController.AddMenuItem("escape", escapeUiObject);
|
||||
scene.AddChild(uiContainerObject, escapeUiObject);
|
||||
|
||||
var (perspectiveCameraObject, perspectiveCamera) = UiUtil.CreatePerspectiveCamera(scene);
|
||||
perspectiveCameraObject.Transform.Translation.Z = 2;
|
||||
var playerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
new ControllerComponent(),
|
||||
|
||||
new MovementController { Speed = 10f },
|
||||
new RigidbodyComponent(),
|
||||
new DragComponent { Drag = 10f, Coefficient = new Vector3(1, 1, 0) },
|
||||
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = new Vector3(1, 1, 2) },
|
||||
Offset = new Vector3(0, 0f, 1f),
|
||||
ColliderGroups = { "player" },
|
||||
ExcludeColliderCollideGroups = { "player" }
|
||||
},
|
||||
|
||||
new PlayerController(perspectiveCamera),
|
||||
|
||||
new WeaponController(),
|
||||
playWeaponView,
|
||||
|
||||
new HealthController(),
|
||||
playHealthView,
|
||||
]);
|
||||
playerObject.Transform.Translation.X = -3;
|
||||
var gameControllerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
new GameController(menuController),
|
||||
|
||||
new TimerController(),
|
||||
plaTimerView,
|
||||
|
||||
new ScoreController(),
|
||||
playScoreView,
|
||||
|
||||
new CollisionManager(),
|
||||
]);
|
||||
|
||||
scene.AddChild(playerObject, perspectiveCameraObject);
|
||||
|
||||
var mapObject = LoadMap(parEngine, scene, "default");
|
||||
var impObject = EnemyObject.CreateEnemy(parEngine, scene, EnemyData.Imp);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
private static GameObject LoadMap(Engine.Engine parEngine, Engine.Scene.Scene parScene, string parMapName)
|
||||
{
|
||||
var mapObject = GameObjectUtil.CreateGameObject(parScene, [
|
||||
new MeshRenderer
|
||||
{
|
||||
Mesh = parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/mesh.obj"),
|
||||
Albedo = parEngine.AssetResourceManager.Load<Texture>($"map/{parMapName}/texture.png")
|
||||
}
|
||||
]);
|
||||
|
||||
var colliders = parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/colliders.obj");
|
||||
var collidersObject = MeshToColliders(parScene, colliders);
|
||||
|
||||
var monsterSpawnPoints = MeshToSpawnPoints(
|
||||
parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/monster_spawners.obj"));
|
||||
|
||||
var valuableSpawnPoints = MeshToSpawnPoints(
|
||||
parEngine.AssetResourceManager.Load<Mesh>($"map/{parMapName}/valuable_spawners.obj"));
|
||||
|
||||
parScene.AddChild(mapObject, collidersObject);
|
||||
|
||||
return mapObject;
|
||||
}
|
||||
|
||||
private static List<Vector3> MeshToSpawnPoints(Mesh parMesh)
|
||||
{
|
||||
return parMesh.Indices.Select(parIndex => parMesh.Vertices[(int)parIndex]._position).ToList();
|
||||
}
|
||||
|
||||
private static GameObject MeshToColliders(Engine.Scene.Scene parScene, Mesh parMesh)
|
||||
{
|
||||
var allCollidersObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
if (parMesh.Indices.Count % (4 * 6) != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Mesh is not an AABB collider");
|
||||
}
|
||||
|
||||
for (var i = 0; i < parMesh.Indices.Count; i += 24)
|
||||
{
|
||||
var max = new Vector3(float.MinValue);
|
||||
var min = new Vector3(float.MaxValue);
|
||||
for (var j = 0; j < 24; j++)
|
||||
{
|
||||
var position = parMesh.Vertices[(int)parMesh.Indices[i + j]]._position;
|
||||
max = Vector3.ComponentMax(max, position);
|
||||
min = Vector3.ComponentMin(min, position);
|
||||
}
|
||||
|
||||
var size = max - min;
|
||||
var offset = min + size / 2;
|
||||
|
||||
var colliderObject = GameObjectUtil.CreateColliderForceField(
|
||||
parScene,
|
||||
new AABBColliderComponent
|
||||
{
|
||||
Collider = new AABBCollider { Size = size }, Offset = offset, ColliderGroups = { "wall" }
|
||||
}
|
||||
);
|
||||
|
||||
parScene.AddChild(allCollidersObject, colliderObject);
|
||||
}
|
||||
|
||||
return allCollidersObject;
|
||||
}
|
||||
|
||||
private static (GameObject, (WeaponView, HealthView, ScoreView, TimerView)) CreateGameUi(Engine.Engine parEngine,
|
||||
Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
var (bottomContainerObject, bottomContainer) =
|
||||
UiUtil.CreateContainerUi(scene, new UiContainerComponent { Container = uiContainer });
|
||||
UiUtil.CreateContainerUi(parScene, new UiContainerComponent { Container = parUiContainer });
|
||||
bottomContainer.Anchor = Anchor.BottomCenter;
|
||||
bottomContainer.Center = Anchor.BottomCenter;
|
||||
bottomContainerObject.AddComponent(new Box2DRenderer
|
||||
@@ -33,104 +166,121 @@ public static class PlayScene
|
||||
});
|
||||
bottomContainerObject.AddComponent(new CopySizeComponent
|
||||
{
|
||||
Target = uiContainerObject.Transform, Coefficient = new Vector3(1, 0, 1)
|
||||
Target = parUiContainer.GameObject.Transform, Coefficient = new Vector3(1, 0, 1)
|
||||
});
|
||||
bottomContainerObject.Transform.Size.Y = 1.5f;
|
||||
scene.AddChild(uiContainerObject, bottomContainerObject);
|
||||
parScene.AddChild(parentObject, bottomContainerObject);
|
||||
|
||||
var (gunObject, (gunUi, gunSprite)) = UiUtil.CreateSpriteUi(scene, bottomContainer,
|
||||
parEngine.AssetResourceManager.Load<Texture>("texture/pistol.png"), RenderLayer.HUD);
|
||||
var (gunObject, (gunUi, gunSprite)) = UiUtil.CreateSpriteUi(parScene, bottomContainer,
|
||||
null, RenderLayer.HUD);
|
||||
gunObject.Transform.Scale = new Vector3(5);
|
||||
gunUi.Anchor = Anchor.TopCenter;
|
||||
gunUi.Center = Anchor.BottomCenter;
|
||||
scene.AddChild(bottomContainerObject, gunObject);
|
||||
gunUi.Offset = new Vector2(0, -0.05f);
|
||||
parScene.AddChild(bottomContainerObject, gunObject);
|
||||
|
||||
var (healthObject, healthUi, (_, healthTextRenderer)) =
|
||||
UiUtil.CreateTextUi(scene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Здоровье: 000",
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Здоровье: 000",
|
||||
TextAlignComponent.Align.Center,
|
||||
RenderLayer.HUD);
|
||||
healthObject.Transform.Scale = new Vector3(0.75f);
|
||||
healthUi.Anchor = Anchor.CenterLeft;
|
||||
healthUi.Center = Anchor.CenterLeft;
|
||||
scene.AddChild(bottomContainerObject, healthObject);
|
||||
parScene.AddChild(bottomContainerObject, healthObject);
|
||||
|
||||
var (ammoObject, ammoUi, (_, ammoTextRenderer)) =
|
||||
UiUtil.CreateTextUi(scene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Патроны: 00/00",
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Патроны: 00/00",
|
||||
TextAlignComponent.Align.Center,
|
||||
RenderLayer.HUD);
|
||||
ammoObject.Transform.Scale = new Vector3(0.75f);
|
||||
ammoUi.Anchor = Anchor.TopRight;
|
||||
ammoUi.Center = Anchor.TopRight;
|
||||
scene.AddChild(bottomContainerObject, ammoObject);
|
||||
parScene.AddChild(bottomContainerObject, ammoObject);
|
||||
|
||||
var (weaponObject, weaponUi, (_, weaponTextRenderer)) =
|
||||
UiUtil.CreateTextUi(scene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Оружие: ОРУЖИЕОР",
|
||||
UiUtil.CreateTextUi(parScene, bottomContainer, UiUtil.GetDoomFont(parEngine), "Оружие: ОРУЖИЕОР",
|
||||
TextAlignComponent.Align.Center,
|
||||
RenderLayer.HUD);
|
||||
weaponObject.Transform.Scale = new Vector3(0.75f);
|
||||
weaponUi.Anchor = Anchor.BottomRight;
|
||||
weaponUi.Center = Anchor.BottomRight;
|
||||
scene.AddChild(bottomContainerObject, weaponObject);
|
||||
parScene.AddChild(bottomContainerObject, weaponObject);
|
||||
|
||||
var (topContainerObject, topContainer) =
|
||||
UiUtil.CreateContainerUi(scene, new UiContainerComponent { Container = uiContainer });
|
||||
UiUtil.CreateContainerUi(parScene, new UiContainerComponent { Container = parUiContainer });
|
||||
topContainer.Anchor = Anchor.TopCenter;
|
||||
topContainer.Center = Anchor.TopCenter;
|
||||
topContainerObject.AddComponent(
|
||||
new Box2DRenderer { Color = new Vector4(1, 0, 0, 1), RenderLayer = RenderLayer.HUD });
|
||||
topContainerObject.AddComponent(new CopySizeComponent
|
||||
{
|
||||
Target = uiContainerObject.Transform, Coefficient = new Vector3(1, 0, 1)
|
||||
Target = parUiContainer.GameObject.Transform, Coefficient = new Vector3(1, 0, 1)
|
||||
});
|
||||
topContainerObject.Transform.Size.Y = 1f;
|
||||
scene.AddChild(uiContainerObject, topContainerObject);
|
||||
parScene.AddChild(parentObject, topContainerObject);
|
||||
|
||||
var (timerObject, timerUi, _) =
|
||||
UiUtil.CreateTextUi(scene, topContainer, UiUtil.GetDoomFont(parEngine), "Время: 00:00",
|
||||
var (timerObject, timerUi, (_, timerTextRenderer)) =
|
||||
UiUtil.CreateTextUi(parScene, topContainer, UiUtil.GetDoomFont(parEngine), "Время: 00:00",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
timerUi.Anchor = Anchor.CenterLeft;
|
||||
timerUi.Center = Anchor.CenterLeft;
|
||||
scene.AddChild(topContainerObject, timerObject);
|
||||
parScene.AddChild(topContainerObject, timerObject);
|
||||
|
||||
var (scoreObject, scoreUi, (_, scoreTextRenderer)) =
|
||||
UiUtil.CreateTextUi(scene, topContainer, UiUtil.GetDoomFont(parEngine), "Счет: 00000",
|
||||
UiUtil.CreateTextUi(parScene, topContainer, UiUtil.GetDoomFont(parEngine), "Счет: 00000",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
scoreUi.Anchor = Anchor.CenterRight;
|
||||
scoreUi.Center = Anchor.CenterRight;
|
||||
scene.AddChild(topContainerObject, scoreObject);
|
||||
parScene.AddChild(topContainerObject, scoreObject);
|
||||
|
||||
var playerObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
new RigidbodyComponent(),
|
||||
new ControllerComponent { Speed = 5f },
|
||||
new DragComponent { Drag = 5f, Coefficient = new Vector3(1, 1, 0) },
|
||||
var weaponView = new WeaponView(weaponTextRenderer, ammoTextRenderer, gunSprite);
|
||||
var healthView = new HealthView(healthTextRenderer);
|
||||
var scoreView = new ScoreView(scoreTextRenderer);
|
||||
var timerView = new TimerView(timerTextRenderer);
|
||||
|
||||
new PlayerController(),
|
||||
return (parentObject, (weaponView, healthView, scoreView, timerView));
|
||||
}
|
||||
|
||||
new WeaponController(),
|
||||
new WeaponView(weaponTextRenderer, ammoTextRenderer, gunSprite),
|
||||
private static GameObject CreateEscapeMenu(Engine.Engine parEngine, Engine.Scene.Scene parScene,
|
||||
UiContainerComponent parUiContainer, MenuControllerComponent parMenuController)
|
||||
{
|
||||
var parentObject = GameObjectUtil.CreateGameObject(parScene);
|
||||
|
||||
new HealthController(),
|
||||
new HealthView(healthTextRenderer),
|
||||
var (backgroundObject, backgroundUiContainer) = UiUtil.CreateBackgroundUi(parScene,
|
||||
new UiContainerComponent { Container = parUiContainer }, new Vector4(0, 0, 0, 0.9f), RenderLayer.HUD);
|
||||
backgroundObject.AddComponent(new CopySizeComponent { Target = parUiContainer.GameObject.Transform });
|
||||
parScene.AddChild(parentObject, backgroundObject);
|
||||
|
||||
new ScoreController(),
|
||||
new ScoreView(scoreTextRenderer),
|
||||
]);
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, parScene, backgroundUiContainer, RenderLayer.HUD);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
parScene.AddChild(parentObject, logoObject);
|
||||
|
||||
var (perspectiveCameraObject, perspectiveCamera) = UiUtil.CreatePerspectiveCamera(scene);
|
||||
perspectiveCameraObject.Transform.Translation.Z = 2;
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(parScene, backgroundUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
backUi.OnClick += parUiComponent =>
|
||||
parUiComponent.GameObject.Scene!.FindFirstComponent<GameController>()!.Unpause();
|
||||
|
||||
var mapObject = GameObjectUtil.CreateGameObject(scene, [
|
||||
new MeshRenderer { Mesh = parEngine.AssetResourceManager.Load<Mesh>("model/map.obj") },
|
||||
]);
|
||||
var (exitUiObject, exitUi, _) = UiUtil.CreateTextUi(parScene, backgroundUiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Выход",
|
||||
parRenderLayer: RenderLayer.HUD);
|
||||
exitUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
|
||||
|
||||
var impObject = GameObjectUtil.CreateGameObject(scene,
|
||||
new Transform { Translation = new Vector3(0, 0, 1), Scale = new Vector3(1, 2, 1), }, [
|
||||
new Box2DRenderer { Texture = parEngine.AssetResourceManager.Load<Texture>("texture/imp.png") },
|
||||
new BillboardComponent { Target = perspectiveCameraObject.Transform }
|
||||
]);
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(parScene,
|
||||
new StackComponent
|
||||
{
|
||||
Offset = new Vector2(0, -1f), Container = backgroundUiContainer, Children = { backUi, exitUi }
|
||||
});
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
scene.AddChild(playerObject, perspectiveCameraObject);
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(parScene,
|
||||
new SelectorComponent { Children = { backUi, exitUi } }, RenderLayer.HUD);
|
||||
|
||||
return scene;
|
||||
parScene.AddChild(parentObject, selectorObject);
|
||||
parScene.AddChild(parentObject, stackObject);
|
||||
parScene.AddChild(stackObject, backUiObject);
|
||||
parScene.AddChild(stackObject, exitUiObject);
|
||||
|
||||
return parentObject;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using DoomDeathmatch.Component.UI;
|
||||
using DoomDeathmatch.Scene.Main;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace DoomDeathmatch.Scene.Rules;
|
||||
|
||||
public static class RulesScene
|
||||
{
|
||||
public static Engine.Scene.Scene Create(Engine.Engine parEngine)
|
||||
{
|
||||
var scene = new Engine.Scene.Scene();
|
||||
|
||||
var (cameraObject, camera) = UiUtil.CreateOrthographicCamera(scene);
|
||||
|
||||
var (uiContainerObject, uiContainer) = UiUtil.CreateBackgroundUi(scene, new UiContainerComponent { Camera = camera });
|
||||
|
||||
var (logoObject, logoUi) = UiUtil.CreateLogoUi(parEngine, scene, uiContainer);
|
||||
logoUi.Offset = new Vector2(0, 3f);
|
||||
|
||||
var (backUiObject, backUi, _) = UiUtil.CreateTextUi(scene, uiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Назад");
|
||||
backUi.OnClick += _ => parEngine.SceneManager.TransitionTo(() => MainScene.Create(parEngine));
|
||||
|
||||
var (rulesObject, rulesUi, _) = UiUtil.CreateTextUi(scene, uiContainer,
|
||||
UiUtil.GetDoomFont(parEngine), "Правила");
|
||||
|
||||
var (stackObject, stack) = UiUtil.CreateStackUi(scene,
|
||||
new StackComponent { Offset = new Vector2(0, -1f), Container = uiContainer, Children = { rulesUi, backUi } });
|
||||
stackObject.Transform.Size.Xy = new Vector2(1f, 6f);
|
||||
|
||||
var (selectorObject, selector) = UiUtil.CreateSelectorUi(scene, new SelectorComponent { Children = { backUi } });
|
||||
|
||||
scene.AddChild(uiContainerObject, selectorObject);
|
||||
|
||||
scene.AddChild(uiContainerObject, logoObject);
|
||||
scene.AddChild(uiContainerObject, stackObject);
|
||||
|
||||
scene.AddChild(stackObject, rulesObject);
|
||||
scene.AddChild(stackObject, backUiObject);
|
||||
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public static class UiUtil
|
||||
selectorObject.AddComponent(parSelectorComponent);
|
||||
var innerSelectorObject = new GameObject
|
||||
{
|
||||
Transform = { Translation = new Vector3(-0.5f, 0, 0), Size = new Vector3(0.25f, 0.5f, 1f) }
|
||||
Transform = { Translation = new Vector3(-0.5f, 0, -1), Size = new Vector3(0.25f, 0.5f, 1f) }
|
||||
};
|
||||
innerSelectorObject.AddComponent(new Box2DRenderer
|
||||
{
|
||||
|
||||
@@ -7,12 +7,12 @@ uniform mat4 uViewMatrix;
|
||||
layout (location = 0) in vec3 aPosition;
|
||||
layout (location = 1) in vec2 aUV;
|
||||
layout (location = 2) in vec4 aColor;
|
||||
layout (location = 3) in int aTextureId;
|
||||
layout (location = 3) in float aTextureId;
|
||||
layout (location = 4) in mat4 aModel;
|
||||
|
||||
layout (location = 0) out vec4 oColor;
|
||||
layout (location = 1) out vec2 oUV;
|
||||
layout (location = 2) flat out int oTextureId;
|
||||
layout (location = 2) flat out float oTextureId;
|
||||
|
||||
void main()
|
||||
{
|
||||
@@ -30,7 +30,7 @@ uniform sampler2D uTexture[16];
|
||||
|
||||
layout (location = 0) in vec4 iColor;
|
||||
layout (location = 1) in vec2 iUV;
|
||||
layout (location = 2) flat in int iTextureId;
|
||||
layout (location = 2) flat in float iTextureId;
|
||||
|
||||
layout (location = 0) out vec4 FragColor;
|
||||
|
||||
@@ -39,7 +39,7 @@ void main()
|
||||
FragColor = iColor;
|
||||
|
||||
if (iTextureId >= 0)
|
||||
FragColor *= texture(uTexture[iTextureId], iUV);
|
||||
FragColor *= texture(uTexture[int(iTextureId)], iUV);
|
||||
|
||||
if (FragColor.a == 0.0)
|
||||
discard;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace Engine.Asset.Font.Metadata;
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace Engine.Asset.Mesh.Loader;
|
||||
|
||||
public class ObjMeshLoader : IMeshLoader
|
||||
{
|
||||
private static readonly ObjMeshLoader _instance = new();
|
||||
private static readonly ObjMeshLoader INSTANCE = new();
|
||||
|
||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||
{
|
||||
return _instance.LoadMesh(parReader, parParameters);
|
||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||
}
|
||||
|
||||
private ObjMeshLoader()
|
||||
@@ -62,7 +62,7 @@ public class ObjMeshLoader : IMeshLoader
|
||||
break;
|
||||
|
||||
case "f":
|
||||
for (var i = 1; i <= 3; i++)
|
||||
for (var i = 1; i < parts.Length; i++)
|
||||
{
|
||||
var faceComponents = parts[i].Split('/');
|
||||
var meshVertex = new Mesh.Vertex { _position = tempVertices[int.Parse(faceComponents[0]) - 1] };
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace Engine.Asset.Mesh.Loader;
|
||||
|
||||
public class StlMeshLoader : IMeshLoader
|
||||
{
|
||||
private static readonly StlMeshLoader _instance = new();
|
||||
private static readonly StlMeshLoader INSTANCE = new();
|
||||
|
||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||
{
|
||||
return _instance.LoadMesh(parReader, parParameters);
|
||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||
}
|
||||
|
||||
private StlMeshLoader()
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Engine.Graphics.Render.Quad;
|
||||
public struct QuadInstanceVertex : IVertex
|
||||
{
|
||||
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
||||
[Vertex(VertexAttribType.Int)] public int _textureId;
|
||||
[Vertex(VertexAttribType.Float)] public float _textureId;
|
||||
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
||||
|
||||
public override int GetHashCode()
|
||||
|
||||
@@ -40,7 +40,7 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
||||
|
||||
_instanceVertices[_queuedInstanceCount]._modelMatrix = parModelMatrix;
|
||||
_instanceVertices[_queuedInstanceCount]._color = parColor;
|
||||
_instanceVertices[_queuedInstanceCount]._textureId = textureId;
|
||||
_instanceVertices[_queuedInstanceCount]._textureId = textureId + 0.01f;
|
||||
|
||||
_frameHash = HashCode.Combine(_frameHash, _instanceVertices[_queuedInstanceCount]);
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using Engine.Graphics.Pixel;
|
||||
using Engine.Graphics.Render.Quad;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using Serilog;
|
||||
|
||||
namespace Engine.Graphics;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Engine.Graphics.Texture;
|
||||
namespace Engine.Graphics.Texture;
|
||||
|
||||
public class TextureUnitMap(int parCapacity)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Engine.Graphics.Pixel;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
|
||||
5
Engine/src/Resource/ShaderResource.Designer.cs
generated
5
Engine/src/Resource/ShaderResource.Designer.cs
generated
@@ -7,10 +7,7 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Engine {
|
||||
using System;
|
||||
|
||||
|
||||
namespace Engine.Resource {
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Engine.Graphics.Camera;
|
||||
using Engine.Graphics.Pipeline;
|
||||
using OpenTK.Mathematics;
|
||||
using Serilog;
|
||||
|
||||
namespace Engine.Scene.Component.BuiltIn;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ public class PerspectiveCamera(
|
||||
{
|
||||
public float FieldOfView { get; set; } = parFieldOfView;
|
||||
|
||||
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
||||
public override Matrix4 View
|
||||
{
|
||||
get
|
||||
|
||||
@@ -31,6 +31,11 @@ public class Transform : Component
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetFullTranslation()
|
||||
{
|
||||
return FullTransformMatrix.ExtractTranslation();
|
||||
}
|
||||
|
||||
public Transform Clone()
|
||||
{
|
||||
var clone =
|
||||
@@ -38,4 +43,15 @@ public class Transform : Component
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public float SquaredDistanceTo(Transform parTransform)
|
||||
{
|
||||
var translation = GetFullTranslation();
|
||||
var otherTranslation = parTransform.GetFullTranslation();
|
||||
|
||||
var difference = translation - otherTranslation;
|
||||
var squaredDistance = difference.LengthSquared;
|
||||
|
||||
return squaredDistance;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,10 @@ public abstract class Component : IUpdate, IRender
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void PreUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Update(double parDeltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -11,22 +11,26 @@ public sealed class GameObject : IUpdate, IRender
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => IsSelfEnabled && IsParentEnabled;
|
||||
set => IsSelfEnabled = value;
|
||||
set => _nextIsSelfEnabled = value;
|
||||
}
|
||||
|
||||
private bool IsSelfEnabled { get; set; } = true;
|
||||
private bool _prevIsSelfEnabled = true;
|
||||
private bool _nextIsSelfEnabled = true;
|
||||
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
||||
|
||||
public Transform Transform { get; }
|
||||
|
||||
internal Scene? Scene { get; set; }
|
||||
public Scene? Scene { get; set; }
|
||||
|
||||
private readonly Queue<Action> _componentActions = new();
|
||||
|
||||
private readonly List<Component.Component> _components = [];
|
||||
private readonly HashSet<Type> _addedComponentTypes = [];
|
||||
|
||||
private readonly HashSet<Component.Component> _addedComponents = [];
|
||||
private readonly HashSet<Component.Component> _removedComponents = [];
|
||||
|
||||
public GameObject()
|
||||
{
|
||||
AddComponent<Transform>();
|
||||
@@ -43,20 +47,37 @@ public sealed class GameObject : IUpdate, IRender
|
||||
Transform = GetComponent<Transform>()!;
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
public void PreUpdate()
|
||||
{
|
||||
ProcessAddedComponents();
|
||||
ProcessRemovedComponents();
|
||||
|
||||
foreach (var component in _components)
|
||||
{
|
||||
component.Awake();
|
||||
component.PreUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
private void ProcessAddedComponents()
|
||||
{
|
||||
foreach (var component in _components)
|
||||
foreach (var component in _addedComponents)
|
||||
{
|
||||
component.Awake();
|
||||
component.Start();
|
||||
}
|
||||
|
||||
_addedComponents.Clear();
|
||||
}
|
||||
|
||||
private void ProcessRemovedComponents()
|
||||
{
|
||||
foreach (var component in _removedComponents)
|
||||
{
|
||||
component.Destroy();
|
||||
component.GameObject = null!;
|
||||
}
|
||||
|
||||
_removedComponents.Clear();
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
@@ -112,7 +133,44 @@ public sealed class GameObject : IUpdate, IRender
|
||||
|
||||
public T? GetComponent<T>() where T : Component.Component
|
||||
{
|
||||
return !HasComponent<T>() ? null : _components.OfType<T>().First();
|
||||
if (!HasComponent<T>())
|
||||
return null;
|
||||
|
||||
foreach (var component in _components)
|
||||
{
|
||||
if (component is T result)
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public T? GetComponentAny<T>() where T : Component.Component
|
||||
{
|
||||
var component = GetComponent<T>();
|
||||
if (component != null)
|
||||
return component;
|
||||
|
||||
component = GetComponentInChildren<T>();
|
||||
return component;
|
||||
}
|
||||
|
||||
public T? GetComponentInChildren<T>() where T : Component.Component
|
||||
{
|
||||
var children = Scene!.Hierarchy.GetChildren(this);
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
var component = child.GetComponent<T>();
|
||||
if (component != null)
|
||||
return component;
|
||||
|
||||
var childComponent = child.GetComponentInChildren<T>();
|
||||
if (childComponent != null)
|
||||
return childComponent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void AddComponent<T>() where T : Component.Component, new()
|
||||
@@ -142,6 +200,8 @@ public sealed class GameObject : IUpdate, IRender
|
||||
|
||||
public void AddComponent<T>(T parComponent) where T : Component.Component
|
||||
{
|
||||
parComponent.GameObject = this;
|
||||
|
||||
_componentActions.Enqueue(() =>
|
||||
{
|
||||
if (HasComponent<T>())
|
||||
@@ -149,9 +209,9 @@ public sealed class GameObject : IUpdate, IRender
|
||||
throw new ArgumentException($"GameObject already has component of type {typeof(T)}");
|
||||
}
|
||||
|
||||
parComponent.GameObject = this;
|
||||
_components.Add(parComponent);
|
||||
_addedComponentTypes.Add(parComponent.GetType().GetComponentBaseType());
|
||||
_addedComponents.Add(parComponent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -177,6 +237,7 @@ public sealed class GameObject : IUpdate, IRender
|
||||
|
||||
_components.Remove(component);
|
||||
_addedComponentTypes.Remove(typeof(T));
|
||||
_removedComponents.Add(component);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -188,9 +249,26 @@ public sealed class GameObject : IUpdate, IRender
|
||||
|
||||
internal void ProcessChanges()
|
||||
{
|
||||
IsSelfEnabled = _nextIsSelfEnabled;
|
||||
|
||||
while (_componentActions.TryDequeue(out var action))
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString();
|
||||
}
|
||||
|
||||
public override bool Equals(object? parObj)
|
||||
{
|
||||
return parObj is GameObject gameObject && Id == gameObject.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Id);
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,9 @@ public class Hierarchy<T>
|
||||
|
||||
_hierarchyActions.Enqueue(() =>
|
||||
{
|
||||
if (!Contains(parObj))
|
||||
return;
|
||||
|
||||
var parent = GetParent(parObj);
|
||||
_childrenLookup[parent].Remove(parObj);
|
||||
|
||||
@@ -97,9 +100,24 @@ public class Hierarchy<T>
|
||||
: throw new InvalidOperationException($"Child {parChild} is not in hierarchy");
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetChildren(T? parObj = null)
|
||||
public IEnumerable<T> GetChildren(T? parParent = null)
|
||||
{
|
||||
return _childrenLookup.TryGetValue(parObj, out IList<T>? children) ? children : Enumerable.Empty<T>();
|
||||
return _childrenLookup.TryGetValue(parParent, out var children) ? children : Enumerable.Empty<T>();
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAllChildren(T? parParent = null)
|
||||
{
|
||||
var children = GetChildren(parParent);
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
foreach (var descendant in GetAllChildren(child))
|
||||
{
|
||||
yield return descendant;
|
||||
}
|
||||
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInHierarchy(T? parAncestor, T? parChild)
|
||||
@@ -133,19 +151,4 @@ public class Hierarchy<T>
|
||||
|
||||
return IsInHierarchy(parAncestor, parent);
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAllChildren(T? parObj = null)
|
||||
{
|
||||
IEnumerable<T>? children = GetChildren(parObj);
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
yield return child;
|
||||
|
||||
foreach (var descendant in GetAllChildren(child))
|
||||
{
|
||||
yield return descendant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ public class Scene : IUpdate, IRender
|
||||
{
|
||||
public bool IsPlaying { get; private set; }
|
||||
public IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
||||
public float TimeScale { get; set; } = 1.0f;
|
||||
|
||||
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
||||
|
||||
internal Hierarchy<GameObject> Hierarchy { get; } = new();
|
||||
@@ -32,9 +34,11 @@ public class Scene : IUpdate, IRender
|
||||
IsPlaying = true;
|
||||
}
|
||||
|
||||
public List<T> FindAllComponents<T>() where T : Component.Component
|
||||
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
||||
{
|
||||
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
return Hierarchy.Objects
|
||||
.Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled)
|
||||
.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
.Where(parComponent => parComponent != null).ToList()!;
|
||||
}
|
||||
|
||||
@@ -53,9 +57,16 @@ public class Scene : IUpdate, IRender
|
||||
|
||||
ProcessChanges();
|
||||
|
||||
foreach (var gameObject in Hierarchy.Objects)
|
||||
var hierarchyObjects = Hierarchy.Objects;
|
||||
|
||||
foreach (var gameObject in hierarchyObjects)
|
||||
{
|
||||
gameObject.Update(parDeltaTime);
|
||||
gameObject.PreUpdate();
|
||||
}
|
||||
|
||||
foreach (var gameObject in hierarchyObjects)
|
||||
{
|
||||
gameObject.Update(parDeltaTime * TimeScale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,16 +99,9 @@ public class Scene : IUpdate, IRender
|
||||
}
|
||||
|
||||
public void Add(GameObject parGameObject)
|
||||
{
|
||||
Hierarchy.Add(parGameObject);
|
||||
|
||||
_sceneActions.Enqueue(() =>
|
||||
{
|
||||
parGameObject.Scene = this;
|
||||
|
||||
parGameObject.Awake();
|
||||
parGameObject.Start();
|
||||
});
|
||||
Hierarchy.Add(parGameObject);
|
||||
}
|
||||
|
||||
public void AddChild(GameObject parParent, GameObject parChild)
|
||||
@@ -108,11 +112,12 @@ public class Scene : IUpdate, IRender
|
||||
|
||||
public void Remove(GameObject parGameObject)
|
||||
{
|
||||
var children = Hierarchy.GetAllChildren(parGameObject).ToList();
|
||||
Hierarchy.Remove(parGameObject);
|
||||
|
||||
_sceneActions.Enqueue(() =>
|
||||
{
|
||||
foreach (var child in Hierarchy.GetAllChildren(parGameObject))
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.Destroy();
|
||||
child.Scene = null;
|
||||
@@ -124,6 +129,11 @@ public class Scene : IUpdate, IRender
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<GameObject> GetChildren(GameObject parParent, bool parRecursive = false)
|
||||
{
|
||||
return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent);
|
||||
}
|
||||
|
||||
private void ProcessChanges()
|
||||
{
|
||||
Hierarchy.ProcessChanges();
|
||||
|
||||
@@ -5,7 +5,6 @@ public class SceneManager : IUpdate, IRender
|
||||
public Scene? CurrentScene => _currentScene;
|
||||
|
||||
private Scene? _currentScene;
|
||||
// private Scene? _nextScene;
|
||||
private Func<Scene>? _nextScene;
|
||||
|
||||
public void TransitionTo(Func<Scene>? parScene)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Engine.Util;
|
||||
|
||||
public class Timer
|
||||
public class TickableTimer
|
||||
{
|
||||
public event Action? OnFinished;
|
||||
public event Action<double>? OnUpdate;
|
||||
@@ -41,7 +41,7 @@ public class Timer
|
||||
private double _totalTime;
|
||||
private double _currentTime;
|
||||
|
||||
public Timer(double parTotalTime)
|
||||
public TickableTimer(double parTotalTime)
|
||||
{
|
||||
if (parTotalTime <= 0)
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parTotalTime);
|
||||
@@ -1,7 +1,6 @@
|
||||
using Engine.Graphics;
|
||||
using Engine.Graphics.Texture;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
|
||||
@@ -8,7 +8,7 @@ using Engine.Resource;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
using OpenTK.Windowing.Common;
|
||||
using PresenterConsole.Resource;
|
||||
using ShaderResource = PresenterConsole.Resource.ShaderResource;
|
||||
|
||||
namespace PresenterConsole;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Engine;
|
||||
using Engine.Graphics;
|
||||
|
||||
Reference in New Issue
Block a user