Skip to content

Commit f873ab1

Browse files
authored
feat: implement enemy spawning and basic brute/drone AI (#29)
* feat: make textured material with alpha threshold and UV scaling * feat: set sky material alpha threshold to 0 for full opacity * feat: improve enemy component with additional properties for attack and hover mechanics * feat: implement enemy spawner with configuration for spawning and managing enemies * feat: implement enemy AI system for dynamic movement and targeting behavior * feat: integrate enemy AI and spawner systems into play state * feat: update config file to have enemies and spawn points * refactor: align enemy templates with component arrays * fix: use a capsule collider for monsters * fix: keep non-aggro flyers stationary * refactor: remove app dependency from enemy AI * refactor: remove radius and height from player component * fix: calculate player direction using XZ plane for enemy AI * refactor: update enemy AI system to accept player entity directly * fix: update enemy movement logic for flyers to use aggro range * fix: correct casing of collider shape in enemy configuration * refactor: rename enemySpawner configuration method to improve clarity
1 parent 6ddb13e commit f873ab1

13 files changed

Lines changed: 482 additions & 40 deletions

File tree

assets/shaders/textured.frag

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ out vec4 frag_color;
99

1010
uniform vec4 tint;
1111
uniform sampler2D tex;
12+
uniform float alphaThreshold;
1213

1314
void main(){
1415
frag_color = tint * fs_in.color * texture(tex, fs_in.tex_coord);
16+
if (frag_color.a <= alphaThreshold) discard;
1517
}

assets/shaders/textured.vert

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ out Varyings {
1010
} vs_out;
1111

1212
uniform mat4 transform;
13+
uniform vec2 uvScale;
1314

1415
void main(){
1516
gl_Position = transform * vec4(position, 1.0);
1617
vs_out.color = color;
17-
vs_out.tex_coord = tex_coord;
18+
vs_out.tex_coord = tex_coord * uvScale;
1819
}

config/app.jsonc

Lines changed: 152 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,58 @@
4141
"default": {},
4242
"pixelated": {
4343
"MAG_FILTER": "GL_NEAREST"
44+
},
45+
"ground_tiled": {
46+
"MIN_FILTER": "GL_LINEAR_MIPMAP_LINEAR",
47+
"MAG_FILTER": "GL_LINEAR",
48+
"WRAP_S": "GL_REPEAT",
49+
"WRAP_T": "GL_REPEAT",
50+
"MAX_ANISOTROPY": 16.0
4451
}
4552
},
4653
"materials": {
54+
"ground": {
55+
"type": "textured",
56+
"shader": "textured",
57+
"pipelineState": {
58+
"faceCulling": {
59+
"enabled": false
60+
},
61+
"depthTesting": {
62+
"enabled": true
63+
}
64+
},
65+
"tint": [1.0, 1.0, 1.0, 1.0],
66+
"texture": "grass",
67+
"sampler": "ground_tiled",
68+
"uvScale": [96.0, 96.0]
69+
},
70+
"drone": {
71+
"type": "tinted",
72+
"shader": "tinted",
73+
"pipelineState": {
74+
"faceCulling": {
75+
"enabled": false
76+
},
77+
"depthTesting": {
78+
"enabled": true
79+
}
80+
},
81+
"tint": [0.85, 0.9, 1.0, 1.0]
82+
},
83+
"monster": {
84+
"type": "tinted",
85+
"shader": "tinted",
86+
"pipelineState": {
87+
"faceCulling": {
88+
"enabled": false
89+
},
90+
"depthTesting": {
91+
"enabled": true
92+
}
93+
},
94+
"tint": [1.0, 1.0, 1.0, 1.0]
95+
},
4796
"metal": {
4897
"type": "tinted",
4998
"shader": "tinted",
@@ -55,7 +104,7 @@
55104
"enabled": true
56105
}
57106
},
58-
"tint": [0.45, 0.4, 0.5, 1]
107+
"tint": [0.45, 0.4, 0.5, 1.0]
59108
},
60109
"glass": {
61110
"type": "textured",
@@ -75,7 +124,7 @@
75124
"depthMask": false
76125
},
77126
"transparent": true,
78-
"tint": [1, 1, 1, 1],
127+
"tint": [1.0, 1.0, 1.0, 1.0],
79128
"texture": "glass",
80129
"sampler": "pixelated"
81130
},
@@ -90,7 +139,7 @@
90139
"enabled": true
91140
}
92141
},
93-
"tint": [1, 1, 1, 1],
142+
"tint": [1.0, 1.0, 1.0, 1.0],
94143
"texture": "grass",
95144
"sampler": "default"
96145
},
@@ -105,7 +154,7 @@
105154
"enabled": true
106155
}
107156
},
108-
"tint": [1, 1, 1, 1],
157+
"tint": [1.0, 1.0, 1.0, 1.0],
109158
"texture": "monkey",
110159
"sampler": "default"
111160
},
@@ -120,59 +169,133 @@
120169
"enabled": true
121170
}
122171
},
123-
"tint": [1, 1, 1, 1],
172+
"tint": [1.0, 1.0, 1.0, 1.0],
124173
"texture": "moon",
125174
"sampler": "default"
126175
}
127176
}
128177
},
178+
"game": {
179+
"enemySpawner": {
180+
"spawnPoints": [
181+
[30.0, 0.0, 30.0], [-30.0, 0.0, 30.0], [30.0, 0.0, -30.0], [-30.0, 0.0, -30.0],
182+
[42.0, 0.0, 0.0], [-42.0, 0.0, 0.0], [0.0, 0.0, 42.0], [0.0, 0.0, -42.0]
183+
],
184+
"spawnInterval": 2.0,
185+
"initialSpawnCount": 4,
186+
"maxAliveEnemies": 12,
187+
"enemies": [
188+
{
189+
"name": "Monster",
190+
"mesh": "cube",
191+
"material": "monster",
192+
"rotation": [0.0, 0.0, 0.0],
193+
"scale": [0.65, 0.65, 0.65],
194+
"components": [
195+
{
196+
"type": "Enemy",
197+
"enemyType": "Brute",
198+
"moveSpeed": 3.8,
199+
"turnSpeed": 5.5,
200+
"aggroRange": 120.0,
201+
"attackRange": 2.0,
202+
"attackDamage": 10.0,
203+
"attackCooldown": 1.2,
204+
"scoreValue": 100
205+
},
206+
{
207+
"type": "Health",
208+
"maxHealth": 60.0
209+
},
210+
{
211+
"type": "Collider",
212+
"layer": "enemy",
213+
"shape": "Capsule",
214+
"radius": 0.5,
215+
"height": 1.5
216+
}
217+
]
218+
},
219+
{
220+
"name": "Drone",
221+
"mesh": "cube",
222+
"material": "drone",
223+
"rotation": [0.0, 0.0, 0.0],
224+
"scale": [1.2, 1.2, 1.2],
225+
"components": [
226+
{
227+
"type": "Enemy",
228+
"enemyType": "Flyer",
229+
"moveSpeed": 3.4,
230+
"turnSpeed": 6.0,
231+
"aggroRange": 120.0,
232+
"attackRange": 12.0,
233+
"preferredDistance": 9.0,
234+
"attackDamage": 8.0,
235+
"attackCooldown": 2.0,
236+
"hoverFrequency": 2.3,
237+
"hoverAmplitude": 0.4,
238+
"baseHeight": 5.0,
239+
"scoreValue": 150
240+
},
241+
{
242+
"type": "Health",
243+
"maxHealth": 35.0
244+
},
245+
{
246+
"type": "Collider",
247+
"layer": "enemy",
248+
"radius": 0.8
249+
}
250+
]
251+
}
252+
]
253+
}
254+
},
129255
"world": [
130256
{
131-
"position": [0, 1.7, 0],
257+
"name": "Player Camera",
258+
"position": [0.0, 1.8, 14.0],
132259
"components": [
133260
{
134261
"type": "Camera",
135262
"fovY": 60,
136263
"near": 0.1,
137-
"far": 500
264+
"far": 500.0
138265
},
139266
{
140267
"type": "Free Camera Controller",
141-
"positionSensitivity": [5.0, 5.0, 5.0],
142-
"speedupFactor": 3.0,
268+
"positionSensitivity": [8.0, 0.0, 8.0],
269+
"rotationSensitivity": 0.008,
270+
"speedupFactor": 2.5,
143271
"groundLevel": 0.0,
144-
"playerHeight": 1.7
272+
"playerHeight": 1.8
145273
},
146274
{
147275
"type": "Player"
148-
}
149-
],
150-
"children": [
276+
},
151277
{
152-
"position": [1, -1, -1],
153-
"rotation": [45, 45, 0],
154-
"scale": [0.1, 0.1, 1.0],
155-
"components": [
156-
{
157-
"type": "Mesh Renderer",
158-
"mesh": "cube",
159-
"material": "metal"
160-
}
161-
]
278+
"type": "Health",
279+
"maxHealth": 100.0
280+
},
281+
{
282+
"type": "Collider",
283+
"layer": "player",
284+
"radius": 0.45,
285+
"height": 1.8
162286
}
163287
]
164288
},
165-
166-
// Ground plane
167289
{
168-
"position": [0, 0, 0],
169-
"rotation": [-90, 0, 0],
170-
"scale": [100, 100, 1],
290+
"name": "Ground",
291+
"position": [0.0, 0.0, 0.0],
292+
"rotation": [-90.0, 0.0, 0.0],
293+
"scale": [120.0, 120.0, 1.0],
171294
"components": [
172295
{
173296
"type": "Mesh Renderer",
174297
"mesh": "plane",
175-
"material": "grass"
298+
"material": "ground"
176299
}
177300
]
178301
}

src/common/material/material.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace our {
4343
void TexturedMaterial::setup() const {
4444
TintedMaterial::setup();
4545
shader->set("alphaThreshold", alphaThreshold);
46+
shader->set("uvScale", uvScale);
4647
glActiveTexture(GL_TEXTURE0);
4748
if (texture) {
4849
texture->bind();
@@ -60,6 +61,7 @@ namespace our {
6061
alphaThreshold = data.value("alphaThreshold", 0.0f);
6162
texture = AssetLoader<Texture2D>::get(data.value("texture", ""));
6263
sampler = AssetLoader<Sampler>::get(data.value("sampler", ""));
64+
uvScale = data.value("uvScale", uvScale);
6365
}
6466

6567
} // namespace our

src/common/material/material.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <glm/vec2.hpp>
34
#include <glm/vec4.hpp>
45
#include <json/json.hpp>
56

@@ -50,6 +51,7 @@ namespace our {
5051
Texture2D* texture;
5152
Sampler* sampler;
5253
float alphaThreshold;
54+
glm::vec2 uvScale = glm::vec2(1.0f, 1.0f);
5355

5456
void setup() const override;
5557
void deserialize(const nlohmann::json& data) override;

src/common/systems/forward-renderer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace our {
4747
this->skyMaterial->sampler = skySampler;
4848
this->skyMaterial->pipelineState = skyPipelineState;
4949
this->skyMaterial->tint = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
50-
this->skyMaterial->alphaThreshold = 1.0f;
50+
this->skyMaterial->alphaThreshold = 0.0f;
5151
this->skyMaterial->transparent = false;
5252
}
5353

src/game/components/enemy.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ namespace gameplay {
1717
turnSpeed = data.value("turnSpeed", turnSpeed);
1818
aggroRange = data.value("aggroRange", aggroRange);
1919
attackRange = data.value("attackRange", attackRange);
20-
separationRadius = data.value("separationRadius", separationRadius);
20+
attackCooldown = data.value("attackCooldown", attackCooldown);
21+
attackDamage = data.value("attackDamage", attackDamage);
2122
preferredDistance = data.value("preferredDistance", preferredDistance);
23+
hoverFrequency = data.value("hoverFrequency", hoverFrequency);
24+
hoverAmplitude = data.value("hoverAmplitude", hoverAmplitude);
25+
baseHeight = data.value("baseHeight", baseHeight);
2226
scoreValue = data.value("scoreValue", scoreValue);
2327
}
2428

src/game/components/enemy.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@ namespace gameplay {
1414
float turnSpeed = 6.0f;
1515
float aggroRange = 15.0f;
1616
float attackRange = 1.5f;
17-
float separationRadius = 1.0f;
17+
float attackCooldown = 2.0f;
18+
float attackTimer = 0.0f;
19+
float attackDamage = 10.0f;
1820
float preferredDistance = 4.0f;
21+
float hoverFrequency = 2.0f;
22+
float hoverAmplitude = 0.5f;
23+
float baseHeight = 3.0f;
24+
bool hoverOriginSet = false;
25+
float hoverOriginY = 0.0f;
1926
int scoreValue = 100;
2027

2128
static std::string getID() {

src/game/components/player.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
namespace gameplay {
44

55
void PlayerComponent::deserialize(const nlohmann::json& data) {
6-
if (!data.is_object()) return;
7-
radius = data.value("radius", radius);
8-
height = data.value("height", height);
6+
//
97
}
108

119
} // namespace gameplay

src/game/components/player.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ namespace gameplay {
66

77
class PlayerComponent : public our::Component {
88
public:
9-
float radius = 0.5f;
10-
float height = 1.8f;
11-
129
static std::string getID() {
1310
return "Player";
1411
}

0 commit comments

Comments
 (0)