Implemented simple turret state machine ai

dev
Shariq Shah 6 years ago
parent 9b73126b8c
commit 10727fbc33
  1. 2
      assets/entities/turret.symtres
  2. 2
      src/common/version.h
  3. 2
      src/game/editor.c
  4. 228
      src/game/enemy.c
  5. 8
      src/game/enemy.h
  6. 29
      src/game/entity.h
  7. 2
      src/game/event.c
  8. 8
      src/game/player.c
  9. 5
      todo.txt

@ -31,7 +31,7 @@ Entity
bouding_box_min : -0.500 -0.500 -0.500
source_filename : sounds/bullet_1.wav
sound_type : 1
sound_max_distance : 10.0000
sound_max_distance : 30.0000
name : Turret_Weapon_Sound
bouding_box_max : 0.500 0.500 0.500
sound_attenuation_type : 2

@ -4,7 +4,7 @@
/* Auto generated version file. DO NOT MODIFY */
#define SYMMETRY_VERSION_MAJOR 0
#define SYMMETRY_VERSION_MINOR 1
#define SYMMETRY_VERSION_REVISION 320
#define SYMMETRY_VERSION_REVISION 321
#define SYMMETRY_VERSION_BRANCH "dev"
#endif

@ -1682,6 +1682,8 @@ void editor_window_scene_hierarchy(struct nk_context* context, struct Editor* ed
nk_layout_row_dynamic(context, 380, 1);
if(nk_group_begin(context, "Entity Name", NK_WINDOW_SCROLL_AUTO_HIDE))
{
editor_show_entity_in_list(editor, context, scene, &scene->player);
if(nk_tree_push(context, NK_TREE_TAB, "Cameras", NK_MAXIMIZED))
{
for(int i = 0; i < MAX_SCENE_CAMERAS; i++)

@ -14,10 +14,12 @@
#include <string.h>
#include <math.h>
#include <assert.h>
static void enemy_on_scene_loaded(struct Event* event, void* enemy_ptr);
static void enemy_update_physics_turret(struct Enemy* enemy, struct Game_State* game_state, float fixed_dt);
static void enemy_update_ai_turret(struct Enemy* enemy, struct Game_State* game_state, float dt);
static void enemy_state_set_turret(struct Enemy* enemy, int state);
void enemy_init(struct Enemy* enemy, int type)
{
@ -32,13 +34,24 @@ void enemy_init(struct Enemy* enemy, int type)
{
case ENEMY_TURRET:
{
enemy->health = 100;
enemy->damage = 10;
enemy->Turret.turn_direction_positive = true;
enemy->Turret.turn_speed = 10.f;
enemy->Turret.max_turn_angle = 60.f;
break;
enemy->health = 100;
enemy->damage = 10;
enemy->Turret.turn_direction_positive = true;
enemy->Turret.scan = false;
enemy->Turret.turn_speed = 10.f;
enemy->Turret.max_turn_angle = 60.f;
enemy->Turret.pulsate_height = 1.5f;
enemy->Turret.pulsate_speed_scale = 0.1f;
enemy->Turret.attack_cooldown = 0.05f;
enemy->Turret.alert_cooldown = 1.f;
enemy->Turret.color_default = (vec4){ 0.f, 1.f, 1.f, 1.f };
enemy->Turret.color_alert = (vec4){ 1.f, 1.f, 0.f, 1.f };
enemy->Turret.color_attack = (vec4){ 1.f, 0.f, 0.f, 1.f };
enemy->Turret.time_elapsed_since_alert = 0.f;
enemy->Turret.time_elapsed_since_attack = 0.f;
enemy->Turret.vision_range = 15.f;
}
break;
default:
log_error("enemy:init", "Unsupported Enemy Type");
break;
@ -70,7 +83,7 @@ void enemy_static_mesh_set(struct Enemy* enemy, const char* geometry_filename, i
void enemy_update(struct Enemy* enemy, struct Scene* scene, float dt)
{
static float enemy_update_interval = 1.f / 60.f;
static float enemy_update_interval = 1.f / 120.f;
static float time_elapsed_since_last_update = 0.f;
time_elapsed_since_last_update += dt;
@ -169,60 +182,193 @@ void enemy_on_scene_loaded(struct Event* event, void* enemy_ptr)
enemy->weapon_sound = (struct Sound_Source*)child;
}
if(enemy->mesh) enemy->mesh->base.flags |= EF_TRANSIENT;
if(enemy->weapon_sound) enemy->weapon_sound->base.flags |= EF_TRANSIENT;
if(enemy->mesh)
enemy->mesh->base.flags |= EF_TRANSIENT;
else
log_error("enemy:on_scene_loaded", "Could not find mesh child entity for enemy %s", enemy->base.name);
if(enemy->weapon_sound)
enemy->weapon_sound->base.flags |= EF_TRANSIENT;
else
log_error("enemy:on_scene_loaded", "Could not find weapon_sound child entity for enemy %s", enemy->base.name);
// Do other post-scene-load initialization stuff per enemy type here
switch(enemy->type)
{
case ENEMY_TURRET:
enemy_state_set_turret(enemy, TURRET_DEFAULT);
break;
}
}
void enemy_update_physics_turret(struct Enemy* enemy, struct Game_State* game_state, float dt)
void enemy_update_physics_turret(struct Enemy* enemy, struct Game_State* game_state, float fixed_dt)
{
/* Turning/Rotation */
static vec3 turn_axis = { 0.f, 1.f, 0.f };
float current_yaw = quat_get_yaw(&enemy->base.transform.rotation);
static vec3 yaw_axis = { 0.f, 1.f, 0.f };
if(enemy->Turret.scan)
{
float current_yaw = quat_get_yaw(&enemy->base.transform.rotation);
float yaw = enemy->Turret.turn_speed * 1.f * dt;
if(!enemy->Turret.turn_direction_positive)
yaw *= -1.f;
float yaw = enemy->Turret.turn_speed * 1.f * fixed_dt;
if(!enemy->Turret.turn_direction_positive)
yaw *= -1.f;
current_yaw += yaw;
if(current_yaw >= enemy->Turret.max_turn_angle)
{
yaw = 0.f;
enemy->Turret.turn_direction_positive = false;
current_yaw += yaw;
if(current_yaw >= enemy->Turret.max_turn_angle)
{
yaw = 0.f;
enemy->Turret.turn_direction_positive = false;
}
else if(current_yaw <= -enemy->Turret.max_turn_angle)
{
yaw = 0.f;
enemy->Turret.turn_direction_positive = true;
}
if(yaw != 0.f)
transform_rotate(enemy, &yaw_axis, yaw, TS_LOCAL);
}
else if(current_yaw <= -enemy->Turret.max_turn_angle)
else if(!enemy->Turret.scan)
{
yaw = 0.f;
enemy->Turret.turn_direction_positive = true;
}
float current_yaw = quat_get_yaw(&enemy->base.transform.rotation);
if(fabsf(current_yaw) + EPSILON != 0.f)
{
float yaw = enemy->Turret.turn_speed * 1.f * fixed_dt;
if(current_yaw > 0.f)
yaw *= -1.f;
if(yaw != 0.f)
transform_rotate(enemy, &turn_axis, yaw, TS_LOCAL);
transform_rotate(enemy, &yaw_axis, yaw, TS_LOCAL);
}
}
/* Movement */
float ticks = (float)platform_ticks_get();
float pulsate_speed_scale = 0.1f;
float pulsate_height = 1.5f;
vec3 translation = { 0.f };
transform_get_absolute_position(enemy, &translation);
translation.y += sinf(TO_RADIANS(ticks * pulsate_speed_scale)) * dt * pulsate_height;
transform_set_position(enemy, &translation);
if(enemy->Turret.pulsate)
{
float ticks = (float)platform_ticks_get();
vec3 translation = { 0.f };
//transform_get_absolute_position(enemy->mesh, &translation);
vec3_assign(&translation, &enemy->mesh->base.transform.position);
translation.y += sinf(TO_RADIANS(ticks * enemy->Turret.pulsate_speed_scale)) * fixed_dt * enemy->Turret.pulsate_height;
transform_set_position(enemy->mesh, &translation);
}
}
void enemy_update_ai_turret(struct Enemy* enemy, struct Game_State* game_state, float dt)
{
struct Scene* scene = game_state->scene;
float turret_vision_range = 5.f;
struct Ray turret_ray;
transform_get_absolute_position(enemy, &turret_ray.origin);
transform_get_absolute_position(enemy->mesh, &turret_ray.origin);
transform_get_forward(enemy, &turret_ray.direction);
im_ray(&turret_ray, turret_vision_range, (vec4) { 0.f, 1.f, 1.f, 1.f }, 4);
struct Entity* player = scene_ray_intersect_closest(scene, &turret_ray, ERM_PLAYER);
if(player)
switch(enemy->current_state)
{
case TURRET_DEFAULT:
{
im_ray(&turret_ray, enemy->Turret.vision_range, enemy->Turret.color_default, 4);
struct Entity* player = scene_ray_intersect_closest(scene, &turret_ray, ERM_PLAYER);
if(player)
{
float distance = scene_entity_distance(scene, player, enemy);
if(distance <= enemy->Turret.vision_range)
{
enemy_state_set_turret(enemy, TURRET_ALERT);
log_message("Player spotted");
}
}
}
break;
case TURRET_ALERT:
{
float distance = scene_entity_distance(scene, player, enemy);
if(distance <= turret_vision_range)
log_message("Player Intersected, distance: %.3f", distance);
enemy->Turret.time_elapsed_since_alert += dt;
if(enemy->Turret.time_elapsed_since_alert >= enemy->Turret.alert_cooldown)
{
enemy_state_set_turret(enemy, TURRET_DEFAULT);
break;
}
im_ray(&turret_ray, enemy->Turret.vision_range, enemy->Turret.color_alert, 4);
struct Entity* player = scene_ray_intersect_closest(scene, &turret_ray, ERM_PLAYER);
if(player)
{
float distance = scene_entity_distance(scene, player, enemy);
if(distance <= enemy->Turret.vision_range)
{
enemy_state_set_turret(enemy, TURRET_ATTACK);
log_message("Player spotted");
}
}
}
break;
case TURRET_ATTACK:
{
enemy->Turret.time_elapsed_since_attack += dt;
if(enemy->Turret.time_elapsed_since_attack >= enemy->Turret.attack_cooldown)
{
im_ray(&turret_ray, enemy->Turret.vision_range, enemy->Turret.color_attack, 4);
struct Entity* player = scene_ray_intersect_closest(scene, &turret_ray, ERM_PLAYER);
bool target_lost = false;
if(player)
{
float distance = scene_entity_distance(scene, player, enemy);
if(distance <= enemy->Turret.vision_range)
{
enemy->Turret.time_elapsed_since_attack = 0.f;
sound_source_play(game_state->sound, enemy->weapon_sound);
log_message("Player spotted and attacked");
}
else
{
target_lost = true;
}
}
else
{
target_lost = true;
}
if(target_lost)
{
enemy_state_set_turret(enemy, TURRET_ALERT);
log_message("Target lost");
}
}
}
break;
}
}
void enemy_state_set_turret(struct Enemy* enemy, int state)
{
assert(state >= 0 && state < TURRET_STATE_MAX);
struct Model* model = &enemy->mesh->model;
enemy->current_state = state;
switch(enemy->current_state)
{
case TURRET_DEFAULT:
{
vec4_assign(&model->material_params[MMP_DIFFUSE_COL].val_vec4, &enemy->Turret.color_default);
enemy->Turret.time_elapsed_since_alert = 0.f;
enemy->Turret.time_elapsed_since_attack = 0.f;
enemy->Turret.pulsate = true;
enemy->Turret.scan = false;
vec3 default_position = { 0.f };
transform_set_position(enemy->mesh, &default_position);
}
break;
case TURRET_ALERT:
{
enemy->Turret.pulsate = false;
enemy->Turret.scan = true;
vec4_assign(&model->material_params[MMP_DIFFUSE_COL].val_vec4, &enemy->Turret.color_alert);
enemy->Turret.time_elapsed_since_alert = 0.f;
}
break;
case TURRET_ATTACK:
{
enemy->Turret.pulsate = false;
enemy->Turret.scan = false;
vec4_assign(&model->material_params[MMP_DIFFUSE_COL].val_vec4, &enemy->Turret.color_attack);
enemy->Turret.time_elapsed_since_attack = 0.f;
}
break;
}
}

@ -7,6 +7,14 @@ struct Parser_Object;
struct Entity;
struct Hashmap;
enum Turret_State
{
TURRET_DEFAULT = 0,
TURRET_ALERT,
TURRET_ATTACK,
TURRET_STATE_MAX
};
void enemy_init(struct Enemy* enemy, int type);
void enemy_update_physics(struct Enemy* enemy, struct Scene* scene, float dt);
void enemy_update(struct Enemy* enemy, struct Scene* scene, float dt);

@ -79,14 +79,14 @@ enum Entity_Ray_Mask
struct Transform
{
vec3 position;
vec3 scale;
quat rotation;
mat4 trans_mat;
bool is_modified;
bool sync_physics;
struct Entity* parent;
struct Entity** children;
vec3 position;
vec3 scale;
quat rotation;
mat4 trans_mat;
bool is_modified;
bool sync_physics;
struct Entity* parent;
struct Entity** children;
};
struct Entity
@ -197,6 +197,7 @@ struct Enemy
int type;
int health;
int damage;
int current_state;
struct Static_Mesh* mesh;
struct Sound_Source* weapon_sound;
union
@ -206,6 +207,18 @@ struct Enemy
float turn_speed;
float max_turn_angle;
bool turn_direction_positive;
bool pulsate;
bool scan;
float pulsate_speed_scale;
float pulsate_height;
float attack_cooldown;
float time_elapsed_since_attack;
float time_elapsed_since_alert;
float alert_cooldown;
float vision_range;
vec4 color_default;
vec4 color_alert;
vec4 color_attack;
}Turret;
};
};

@ -269,7 +269,7 @@ void event_manager_subscribe_with_object(struct Event_Manager* event_manager, in
for(int i = 0; i < MAX_EVENT_SUBSCRIPTIONS; i++)
{
struct Event_Subscription* subscription = &event_manager->event_subsciptions[i];
if(subscription->type == EST_WITH_OBJECT && subscription->event_type == event_type && subscription->handler_with_object == handler_func && subscription->subscriber != subscriber)
if(subscription->type == EST_WITH_OBJECT && subscription->event_type == event_type && subscription->handler_with_object == handler_func && subscription->subscriber == subscriber)
{
log_message("Already subscibed to %s event", event_name_get(event_type));
subscribed = true;

@ -25,9 +25,11 @@ void player_init(struct Player* player, struct Scene* scene)
{
struct Game_State* game_state = game_state_get();
entity_init(player, "Player", &scene->root_entity);
player->base.flags |= EF_ACTIVE;
player->base.id = 1;
player->base.type = ET_PLAYER;
player->base.flags |= EF_ACTIVE;
player->base.id = 1;
player->base.type = ET_PLAYER;
player->base.bounding_box.min = (vec3){ -1.5f, -1.5f, -1.0f };
player->base.bounding_box.max = (vec3){ 1.5f, 1.5f, 1.0f };
struct Hashmap* config = game_state->cvars;
player->move_speed = hashmap_float_get(config, "player_move_speed");

@ -1,6 +1,11 @@
Todo:
- Implement separate property window for player related variables that can be shown in the editor similar to renderer settings etc
- Implement turret state machine
- Add all sound source properties to propery inspector
- Add turret properties to property inspector
- Add another ambient sound_source entity as child to enemy entity
- Fix Turret losing target at diagonals
- Track current height of the turret and return it to default once pulsation resumes
- Fix rotate gizmo's origin not being set to the selected entity
- Player shooting
- Player jump cooldown, don't allow jump until a certian time interval has passed, even if we're grounded

Loading…
Cancel
Save