enemy is following player, hurrah

This commit is contained in:
Magnus Küderli
2025-10-28 12:12:26 +01:00
parent ceed441b69
commit 4f1385fac2
31 changed files with 882 additions and 198 deletions
-1
View File
@@ -10,6 +10,5 @@ clean:
rm -f $(TARGET)
run: $(TARGET)
rm -f $(TARGET)
gcc $(SRC) -o $(TARGET) $(LIB)
./$(TARGET)
+2 -2
View File
@@ -20,8 +20,8 @@
-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,6,8,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,6,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,6,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,6,1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,6,1,1,1,1,1,1,-1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
20 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
21 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
22 -1 6 8 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
23 -1 -1 -1 -1 -1 -1 6 1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
24 -1 -1 -1 -1 -1 -1 -1 6 1 1 1 1 1 1 1 -1 1 1 1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
25 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
26 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
27 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

+2 -2
View File
@@ -161,8 +161,8 @@
0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,4103,4105,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,4103,4098,0,0,0,0,0,0,0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,4103,4098,4098,4098,4098,4098,4098,4098,4098,4098,4098,4098,4098,0,0,0,0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,4103,4098,0,0,0,0,0,0,4098,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,4103,4098,4098,4098,4098,4098,4098,0,4098,4098,4098,4098,4098,0,0,0,0,0,0,0,0,0,4098,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,4098,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+6 -6
View File
@@ -5,8 +5,8 @@
},
"activeFile": "sunny_map.tmx",
"expandedProjectPaths": [
"tilesets",
"."
".",
"tilesets"
],
"fileStates": {
"main.tsx": {
@@ -23,11 +23,11 @@
}
},
"sunny_map.tmx": {
"scale": 3.5939,
"selectedLayer": 0,
"scale": 4.158,
"selectedLayer": 3,
"viewCenter": {
"x": 165.69743175937003,
"y": 86.11814463396311
"x": 247.11399711399713,
"y": 374.8196248196248
}
},
"sunnyside_world.tsx": {
+28 -4
View File
@@ -11,6 +11,18 @@
"file": "/home/magnus/code/c/projects/raylib_topdown/src/anim.c",
"output": "/home/magnus/code/c/projects/raylib_topdown/target"
},
{
"arguments": [
"/usr/bin/gcc",
"-c",
"-o",
"target",
"src/astar.c"
],
"directory": "/home/magnus/code/c/projects/raylib_topdown",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/astar.c",
"output": "/home/magnus/code/c/projects/raylib_topdown/target"
},
{
"arguments": [
"/usr/bin/gcc",
@@ -113,10 +125,10 @@
"-c",
"-o",
"target",
"src/player.c"
"src/movement.c"
],
"directory": "/home/magnus/code/c/projects/raylib_topdown",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/player.c",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/movement.c",
"output": "/home/magnus/code/c/projects/raylib_topdown/target"
},
{
@@ -125,10 +137,10 @@
"-c",
"-o",
"target",
"src/test_enemy.c"
"src/player.c"
],
"directory": "/home/magnus/code/c/projects/raylib_topdown",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/test_enemy.c",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/player.c",
"output": "/home/magnus/code/c/projects/raylib_topdown/target"
},
{
@@ -166,5 +178,17 @@
"directory": "/home/magnus/code/c/projects/raylib_topdown",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/world.c",
"output": "/home/magnus/code/c/projects/raylib_topdown/target"
},
{
"arguments": [
"/usr/bin/gcc",
"-c",
"-o",
"target",
"src/zombie_enemy.c"
],
"directory": "/home/magnus/code/c/projects/raylib_topdown",
"file": "/home/magnus/code/c/projects/raylib_topdown/src/zombie_enemy.c",
"output": "/home/magnus/code/c/projects/raylib_topdown/target"
}
]
+7 -3
View File
@@ -6,6 +6,7 @@
typedef struct timeval timeval_t;
typedef struct Entity entity_t; // forward declare
// All the sprite visuals should be handeled here
// Basic animation system.
// All tracks must have the same number of frames
// and the same animation speed
@@ -19,16 +20,19 @@ typedef struct Animator {
float anim_speed;
int flipped;
int frame_count;
// never manually set, use play_animation
int state;
timeval_t anim_start;
} animator_t;
void render_entity(entity_t* entity);
void entity_face_moving_dir(entity_t* entity);
void play_animation(animator_t* anim, int state);
unsigned long get_time_since_ms(timeval_t begin);
Rectangle get_animation_frame(entity_t* entity);
void render_animtor(entity_t* entity);
+43
View File
@@ -0,0 +1,43 @@
# pragma once
# include <stdbool.h>
# include <raylib.h>
# include <raymath.h>
typedef struct ANode {
Vector2 parent;
int h;
int g;
int f;
bool is_collapsed;
bool is_obstacle;
} anode_t;
typedef struct AField {
int rows;
int columns;
anode_t** map;
} afield_t;
void reset_field(afield_t *field, int** map);
afield_t generate_astar_field(int rows, int columns, int** col_map);
Vector2* get_a_star_path(afield_t *field, Vector2 start, Vector2 goal, int max_searches, int* path_length);
Vector2* do_full_a_star_search(int width, int height, int** collisions, Vector2 start, Vector2 target, int max_searches, int* path_len);
void setup_start_cell(afield_t* field, Vector2 start, Vector2 target);
Vector2 get_best_node(afield_t *field);
void collapse_node_at(afield_t *field, Vector2 node_pos);
int assign_node_as_parent(afield_t *field, Vector2 target, Vector2 node_pos);
void debug_print_field_collisions(afield_t* field);
void debug_print_field(afield_t* field);
int adjusted_euclidean(Vector2 a, Vector2 b);
+6
View File
@@ -1,7 +1,13 @@
# pragma once
# include <stdbool.h>
# define TILE_SIZE 16
typedef struct Config {
bool render_colliders;
bool render_paths;
bool player_collision;
// layers
+7 -4
View File
@@ -1,7 +1,5 @@
#pragma once
# include "../include/entity.h"
# include "../include/player.h"
# include "../include/world.h"
@@ -9,11 +7,16 @@
typedef struct Enemy enemy_t;
typedef struct Enemy {
entity_t entity;
path_t path;
void (*set_vel_func)(enemy_t*, player_t*);
int (*move_func)(entity_t*, world_t*);
int vision_radius;
void (*set_vel_func)(world_t*, enemy_t*, player_t*);
int (*move_func)(enemy_t*, player_t*, world_t*);
void (*update_anim_func)(enemy_t*);
} enemy_t;
void update_enemy(enemy_t* enemy, player_t* player, world_t* world);
void render_enemy(enemy_t* enemy);
+2 -6
View File
@@ -3,7 +3,6 @@
# include <raylib.h>
# include <raylib.h>
# include "../include/world.h"
# include "../include/tilemap.h"
# include "../include/anim.h"
@@ -24,11 +23,8 @@ typedef struct Entity {
float speed_cap;
} entity_t;
int entity_move_and_collide(entity_t* entity, world_t* world);
void entity_move_by_velocity(entity_t* entity);
void entity_apply_friction(entity_t* entity);
void entity_face_moving_dir(entity_t* entity);
void set_entity_center(entity_t* entity, Vector2 center_pos);
Vector2 get_entity_center(entity_t* entity);
Rectangle get_entity_rect(entity_t* entity);
Rectangle** get_col_tiles_around_entity(entity_t* entity, tilemap_t* col_map);
+11
View File
@@ -0,0 +1,11 @@
typedef struct Enemy enemy_t;
typedef struct Entity entity_t;
typedef struct Player player_t;
typedef struct World world_t;
void entity_move_by_velocity(entity_t* entity);
void entity_apply_friction(entity_t* entity);
int entity_move_and_collide(entity_t* entity, world_t* world);
int enemy_follow_path(enemy_t* enemy, player_t* player, world_t* world);
+1 -2
View File
@@ -3,7 +3,6 @@
#include <raylib.h>
#include "../include/world.h"
#include "../include/tilemap.h"
#include "../include/entity.h"
typedef enum State {
@@ -21,4 +20,4 @@ typedef struct Player {
void init_player(player_t* player);
void update_player(player_t* player, world_t* world);
void render_player(player_t* player, unsigned long total_ms);
void render_player(player_t* player);
-8
View File
@@ -1,8 +0,0 @@
# pragma once
#include "../include/enemy.h"
#include "../include/player.h"
void target_player(enemy_t* enemy, player_t* player);
void init_test_enemy(enemy_t* enemy);
void target_player(enemy_t* enemy, player_t* player);
+15 -1
View File
@@ -1,11 +1,25 @@
# pragma once
#include "../include/tilemap.h"
#include "../include/astar.h"
typedef struct World {
tilemap_t* col_map;
afield_t afield;
} world_t;
typedef struct Path {
int len;
Vector2* nodes;
Vector2 target_pos;
int index;
bool finished;
} path_t;
void init_world(world_t* world, char* collision_map);
Vector2 world_to_tilemap_coord(Vector2 world_pos);
Vector2 tilemap_to_world_coord(Vector2 tile_pos);
path_t world_get_path(world_t* world, Vector2 start, Vector2 target, int max_searches);
+10
View File
@@ -0,0 +1,10 @@
# pragma once
#include "../include/enemy.h"
#include "../include/player.h"
#include "../include/world.h"
void target_player(world_t* world, enemy_t* enemy, player_t* player);
void init_zombie_enemy(enemy_t* enemy);
void update_animation_path(enemy_t* enemy);
void update_animation_velocity(enemy_t* enemy);
+32
View File
@@ -4,6 +4,7 @@
# include "../include/anim.h"
# include "../include/entity.h"
# include "../include/config.h"
Rectangle get_animation_frame(entity_t* entity) {
unsigned long time_delta = get_time_since_ms(entity->anim.anim_start);
@@ -44,3 +45,34 @@ unsigned long get_time_since_ms(timeval_t begin) {
unsigned long delta_ms = sec_diff * 1000 + usec_diff / 1000;
return delta_ms;
}
void entity_face_moving_dir(entity_t* entity) {
if (!entity->velocity.x) { return; }
entity->anim.flipped = entity->velocity.x < 0;
return;
}
void render_entity(entity_t* entity) {
Vector2 offset = {
.x = entity->sprite_offset.x * (entity->anim.flipped?-1:1),
.y = entity->sprite_offset.y,
};
Rectangle dest_rect = {
entity->position.x + offset.x,
entity->position.y + offset.y,
entity->bb_size,
entity->bb_size,
};
Rectangle source_rect = get_animation_frame(entity);
DrawTexturePro(entity->anim.texture, source_rect, dest_rect, (Vector2) { 0.0, 0.0 }, 0, WHITE);
if (get_config_ptr()->render_colliders) {
Vector2 center = get_entity_center(entity);
DrawCircleV(center, 2.0, BLUE);
}
}
+332
View File
@@ -0,0 +1,332 @@
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "../include/astar.h"
void reset_field(afield_t *field, int** map) {
for (int r = 0; r < field->rows; r++) {
for (int c = 0; c < field->columns; c++) {
bool is_obstacle = map[r][c] != -1 ? true : false;
field->map[r][c] = (anode_t){
.parent = {-1, -1},
.h = -1,
.g = -1,
.f = -1,
.is_collapsed = false,
.is_obstacle = is_obstacle,
};
}
}
}
afield_t generate_astar_field(int rows, int columns, int** col_map) {
anode_t **map = calloc(sizeof(anode_t *), rows);
assert(map);
for (int r = 0; r < rows; r++) {
map[r] = (anode_t *)calloc(sizeof(anode_t), columns);
assert(map[r]);
}
afield_t field = {
rows = rows,
columns = columns,
map = map,
};
reset_field(&field, col_map);
return field;
}
void free_field(afield_t* field) {
if (!field || !field->map)
return;
for (int i = 0; i < field->rows; i++) {
free(field->map[i]);
}
free(field->map);
field->map = NULL;
}
int adjusted_euclidean(Vector2 a, Vector2 b) {
int dx = b.x - a.x;
int dy = b.y - a.y;
return (int)round(sqrt(dx * dx + dy * dy) * 10.0);
}
int octile_heuristic(Vector2 a, Vector2 b) {
int dx = abs((int)b.x - (int)a.x);
int dy = abs((int)b.y - (int)a.y);
int min_d = dx < dy ? dx : dy;
int max_d = dx > dy ? dx : dy;
return 14 * min_d + 10 * (max_d - min_d);
}
void update_node_values(afield_t *field, Vector2 target, Vector2 node_pos, Vector2 parent_pos) {
anode_t *node = &field->map[(int)node_pos.y][(int)node_pos.x];
int traversal_cost = (node_pos.x == parent_pos.x) || (node_pos.y == parent_pos.y) ? 10 : 14;
int new_g = field->map[(int)parent_pos.y][(int)parent_pos.x].g + traversal_cost;
if (node->parent.y != -1) {
// new potential cost
if (node->g <= new_g) {
return;
}
}
// set parent
node->parent = parent_pos;
// set values
node->g = new_g;
node->h = octile_heuristic(node_pos, target);
node->f = node->g + node->h;
}
bool is_valid_child(afield_t* field, Vector2 parent, Vector2 child) {
int child_x = child.x;
int child_y = child.y;
// if in bounds set note as parent
if (child_x < 0 || child_y < 0 || child_x >= field->columns || child_y >= field->rows) {
// printf("out of bounds node at: %d %d\n", child_x, child_y);
return false;
}
if (field->map[child_y][child_x].is_collapsed) { return false; }
if (field->map[child_y][child_x].is_obstacle) { return false; }
// dont allow diagonal passing trough obstacles
if (child.x == parent.x || child.y == parent.y) {
// no diagonal movement
return true;
}
Vector2 dir = Vector2Subtract(child, parent);
// printf("Direction: %i %i\n", (int)dir.x, (int)dir.y);
bool is_x_obstacle = field->map[(int)(parent.y+dir.y)][(int)parent.x].is_obstacle;
bool is_y_obstacle = field->map[(int)parent.y][(int)(parent.x+dir.x)].is_obstacle;
// printf("obstacles: %i %i\n", is_x_obstacle, is_y_obstacle);
if (is_x_obstacle || is_y_obstacle) {
return false;
}
return true;
}
int assign_node_as_parent(afield_t* field, Vector2 target, Vector2 node_pos) {
Vector2 offsets[3][3] = {
{(Vector2){-1.0f, -1.0f}, (Vector2){0.0f, -1.0f}, (Vector2){1.0f, -1.0f}},
{(Vector2){-1.0f, 0.0f}, (Vector2){0.0f, 0.0f}, (Vector2){1.0f, 0.0f}},
{(Vector2){-1.0f, 1.0f}, (Vector2){0.0f, 1.0f}, (Vector2){1.0f, 1.0f}},
};
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (x == 1 && y == 1) { continue; } // skip parent
int child_x = node_pos.x + offsets[y][x].x;
int child_y = node_pos.y + offsets[y][x].y;
if (!is_valid_child(field, node_pos, (Vector2) { child_x, child_y } )) {
continue;
}
if (child_x == (int)target.x && child_y == (int)target.y) {
// target found!!
field->map[(int)target.y][(int)target.x].is_collapsed = true;
field->map[(int)target.y][(int)target.x].parent = node_pos;
return 1;
}
update_node_values(field, target, (Vector2) {child_x, child_y}, node_pos);
}
}
return 0;
}
void collapse_node_at(afield_t *field, Vector2 node_pos) {
field->map[(int)node_pos.y][(int)node_pos.x].is_collapsed = true;
}
Vector2 get_best_node(afield_t *field) {
Vector2 best_pos = {-1, -1};
int best_f = __INT_MAX__;
int best_h = __INT_MAX__;
for (int y = 0; y < field->rows; y++) {
for (int x = 0; x < field->columns; x++) {
anode_t *test_node = &field->map[y][x];
if (test_node->f == -1) { continue; }
// TODO what if two nodes have same f and same heuristic? (symetric
// problem)
if ((
(test_node->f < best_f) ||
(test_node->f == best_f && test_node->h < best_h)
) &&
!test_node->is_collapsed
&&
!test_node->is_obstacle
) {
// new best
best_pos = (Vector2){x, y};
best_f = test_node->f;
best_h = test_node->h;
}
}
}
// will return -1 -1 when no uncollapsed node was found
return best_pos;
}
Vector2* reconstruct_path(afield_t* field, Vector2 start, Vector2 end, int *out_length) {
// entirely chatgpt i had no motivation left
int rows = field->rows;
int columns = field->columns;
anode_t** map = field->map;
int max_len = rows * columns; // safe upper bound
Vector2 *path = malloc(max_len * sizeof(Vector2));
if (!path) return NULL;
int count = 0;
Vector2 current = end;
while (!(current.x == start.x && current.y == start.y)) {
path[count++] = current;
int px = (int)current.x;
int py = (int)current.y;
// bounds check
if (px < 0 || px >= columns || py < 0 || py >= rows) break;
current = map[py][px].parent;
if (count >= max_len) break; // prevent infinite loops
}
path[count++] = start;
// reverse path so it goes start -> end
for (int i = 0; i < count / 2; i++) {
Vector2 tmp = path[i];
path[i] = path[count - 1 - i];
path[count - 1 - i] = tmp;
}
*out_length = count;
return path;
}
Vector2* get_a_star_path(afield_t *field, Vector2 start, Vector2 goal, int max_searches, int* path_length) {
setup_start_cell(field, start, goal);
collapse_node_at(field, start);
assign_node_as_parent(field, goal, start);
// debug_print_field(field);
for (int x = 0; x < max_searches; x++) {
Vector2 best = get_best_node(field);
if (best.x == -1 || best.y == -1) {
// all search paths exausted
// debug_print_field(field);
// printf("TARGET NOT REACHABLE!\n");
*path_length = 0;
return NULL;
}
// printf("Best node: %d %d\n", (int)best.x, (int)best.y);
int found = assign_node_as_parent(field, goal, best);
collapse_node_at(field, best);
// printf("collapsed node at: %d %d", (int)best.x, (int)best.y);
if (found) {
// debug_print_field(field);
// printf("FOUND TARGET!\n");
return reconstruct_path(field, start, goal, path_length);
}
}
// printf("All searches wasted, target not found, quitting!\n");
*path_length = 0;
return NULL;
}
Vector2* do_full_a_star_search(int width, int height, int** collisions, Vector2 start, Vector2 target, int max_searches, int* path_len) {
afield_t field = generate_astar_field(height, width, collisions);
Vector2* path = get_a_star_path(&field, start, target, max_searches, path_len);
free_field(&field);
return path;
}
void setup_start_cell(afield_t* field, Vector2 start, Vector2 target) {
int heuristic = octile_heuristic(start, target);
field->map[(int)start.y][(int)start.x].g = 0;
field->map[(int)start.y][(int)start.x].h = heuristic;
field->map[(int)start.y][(int)start.x].f = heuristic;
}
void debug_print_field_collisions(afield_t* field) {
printf("\nAstar Collision:\n");
for (int r = 0; r < field->rows; r++) {
for (int c = 0; c < field->columns; c++) {
printf("%i", field->map[r][c].is_obstacle);
}
printf("\n");
}
printf("\n");
}
void debug_print_field(afield_t* field) {
char* arrows[3][3] = {
{"", "", ""},
{"", "", ""},
{"", "", ""}
};
printf("\nAstar Field:\n");
for (int r = 0; r < field->rows; r++) {
for (int c = 0; c < field->columns; c++) {
printf(" |");
if (field->map[r][c].is_obstacle) {
printf("#");
}
// else if (field->map[r][c].is_collapsed) {
// printf("[]");
// }
else {
Vector2 parent = field->map[r][c].parent;
if (parent.x != -1 && field->map[r][c].is_collapsed) {
Vector2 par_offs = Vector2Subtract(parent, (Vector2) {c, r});
printf("%s", arrows[(int)par_offs.y+1][(int)par_offs.x+1]);
}
else {
printf(" ");
}
}
printf("| ");
}
printf("\n");
}
printf("\n");
}
+1
View File
@@ -4,6 +4,7 @@
config_t global_config = {
// DEBUG
.render_colliders = false,
.render_paths = true,
.player_collision = true,
// layers
+2 -2
View File
@@ -72,12 +72,12 @@ void read_csv_into_array(char* csv, int** array, int columns) {
int** allocate_2d_array(int max_row, int max_column) {
// ALLOCATE BASE ARRAY
int** array_2d = calloc((size_t)max_row, sizeof(int*));
if (!array_2d) { printf("[!] malloc failed"); exit(1); }
if (!array_2d) { printf("[!] malloc failed\n"); exit(1); }
// ALLOCATE EACH ROW ARRAY
for (int r = 0; r < max_row; r++) {
array_2d[r] = (int*)calloc(max_column, sizeof(int));
if (!array_2d[r]) { printf("[!] malloc failed"); exit(1); }
if (!array_2d[r]) { printf("[!] malloc failed\n"); exit(1); }
}
return array_2d;
+2
View File
@@ -18,6 +18,8 @@ void render_colliders(player_t* player, world_t* world) {
void read_debug_input() {
if (IsKeyPressed(KEY_TAB)) { get_config_ptr()->render_colliders ^= 1; }
if (IsKeyPressed(KEY_P)) { get_config_ptr()->render_paths ^= 1; }
if (IsKeyPressed(KEY_F)) { get_config_ptr()->player_collision ^= 1; }
if (IsKeyPressed(KEY_ONE)) { get_config_ptr()->render_ground ^= 1; }
+15 -2
View File
@@ -4,12 +4,25 @@
# include "../include/enemy.h"
# include "../include/player.h"
# include "../include/world.h"
# include "../include/config.h"
void update_enemy(enemy_t* enemy, player_t* player, world_t* world) {
assert(enemy->set_vel_func != NULL);
assert(enemy->move_func != NULL);
assert(enemy->update_anim_func != NULL);
enemy->set_vel_func(enemy, player);
enemy->move_func(&(enemy->entity), world);
enemy->set_vel_func(world, enemy, player);
enemy->move_func(enemy, player, world);
enemy->update_anim_func(enemy);
entity_face_moving_dir(&(enemy->entity));
}
void render_enemy(enemy_t* enemy) {
render_entity(&enemy->entity);
if (get_config_ptr()->render_paths && enemy->path.nodes) {
DrawLineStrip(enemy->path.nodes, enemy->path.len, WHITE);
DrawCircleV(tilemap_to_world_coord(enemy->path.target_pos), 2.0f, BLUE);
}
}
+16 -94
View File
@@ -2,21 +2,20 @@
#include <raymath.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "../include/tilemap.h"
#include "../include/entity.h"
#include "../include/world.h"
#include "../include/config.h"
Rectangle** get_col_tiles_around_entity(entity_t* entity, tilemap_t* col_map) {
const int ts = 16; // tile size
const int ts = TILE_SIZE; // tile size
// tilemap at position (0,0)
Rectangle** close_walls = malloc(sizeof(Rectangle*) * 3);
if (!close_walls) { printf("[!] malloc failed"); exit(1); }
if (!close_walls) { printf("[!] malloc failed\n"); exit(1); }
for (int i = 0; i < 3; i++) {
close_walls[i] = calloc((size_t)3, sizeof(Rectangle));
if (!close_walls[i]) { printf("[!] calloc failed"); exit(1); }
if (!close_walls[i]) { printf("[!] calloc failed\n"); exit(1); }
}
Vector2 offsets[3][3] = {
@@ -57,95 +56,6 @@ Rectangle** get_col_tiles_around_entity(entity_t* entity, tilemap_t* col_map) {
return close_walls;
}
void entity_move_by_velocity(entity_t* entity) {
entity->position = Vector2Add(entity->position, Vector2Scale(entity->velocity, GetFrameTime()));
}
int entity_move_and_collide(entity_t* entity, world_t* world) {
Rectangle** rects = get_col_tiles_around_entity(entity, world->col_map);
int col = 0; // 0 = no col
// 1 = horizontal
// 2 = vertical
// 3 = both
int ts = 16; // tile size
Vector2 offset = Vector2Scale(entity->velocity, GetFrameTime());
Vector2 p_ws = {
// entity sprite whitespace
0.5f*(entity->bb_size - entity->sprite_size.x),
0.5f*(entity->bb_size - entity->sprite_size.y),
};
// HORIZONTAL COMPONENT ----------
entity->position.x += offset.x;
// check collision
float xpos = entity->position.x;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (CheckCollisionRecs(rects[y][x], get_entity_rect(entity))) {
// printf("Horizontal Collision \n");
col += 1;
// collision
if (offset.x > 0) {
// collision on the right -> snap to left edge
entity->velocity.x = 0;
entity->position.x = rects[y][x].x - entity->bb_size + p_ws.x;
}
else if (offset.x < 0) {
// collision on the left -> snap to right edge
entity->velocity.x = 0;
entity->position.x = rects[y][x].x + rects[y][x].width - p_ws.x ;
}
}
}
}
// VERTICAL COMPONENT --------
entity->position.y += offset.y;
// check collision
float ypos = entity->position.y;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (CheckCollisionRecs(rects[y][x], get_entity_rect(entity))) {
// printf("Vertical Collision \n");
col += 2;
// collision
if (offset.y > 0) {
// collision on the bottom -> snap to top edge
entity->velocity.y = 0;
entity->position.y = rects[y][x].y - entity->bb_size + p_ws.y;
} else if (offset.y <0) {
// collision on the top -> snap to bottom edge
entity->velocity.y = 0;
entity->position.y = rects[y][x].y + rects[y][x].height - p_ws.y;
}
}
}
}
// Finish up
free(rects);
return col;
}
void entity_apply_friction(entity_t* entity) {
float frameTime = GetFrameTime();
entity->velocity = Vector2Scale(entity->velocity, pow(entity->friction, frameTime));
}
void entity_face_moving_dir(entity_t* entity) {
if (!entity->velocity.x) { return; }
entity->anim.flipped = entity->velocity.x < 0;
return;
}
Rectangle get_entity_rect(entity_t* entity) {
int v_offset = 0.5f*(entity->bb_size - entity->sprite_size.y);
int h_offset = 0.5f*(entity->bb_size - entity->sprite_size.x);
@@ -157,3 +67,15 @@ Rectangle get_entity_rect(entity_t* entity) {
.height = entity->sprite_size.y,
};
}
Vector2 get_entity_center(entity_t* entity) {
Vector2 pc = entity->position;
Vector2 offset = entity->sprite_size;
pc = Vector2Add(pc, offset);
return pc;
}
void set_entity_center(entity_t* entity, Vector2 center_pos) {
Vector2 offset = entity->sprite_size;
entity->position = Vector2Subtract(center_pos, offset);
}
+6 -10
View File
@@ -9,22 +9,20 @@
#include "../include/config.h"
#include "../include/debug.h"
#include "../include/enemy.h"
#include "../include/test_enemy.h"
#include "../include/zombie_enemy.h"
int start_game() {
unsigned long time_us = 0; // time in µs
player_t player;
camera_t camera;
world_t world;
enemy_t test_enemy;
enemy_t zombie_enemy;
// Initialize components
init_window("Game", /*RESOLUTION:*/1920/2, 1200/2, /*FPS:*/600);
init_player(&player);
init_camera(&camera, /*ZOOM*/ 5.0);
init_world(&world, "assets/levels/test_collision.csv");
init_test_enemy(&test_enemy);
init_zombie_enemy(&zombie_enemy);
printf("[!] Initialized successfully!\n\n");
tileset_t* tileset_sunnyside = load_tileset(16, "assets/tilesheets/sunnyside.png");
@@ -40,7 +38,7 @@ int start_game() {
// UPDATE
update_player(&player, &world);
update_camera(&camera, &(player.entity));
update_enemy(&test_enemy, &player, &world);
update_enemy(&zombie_enemy, &player, &world);
read_debug_input();
@@ -49,8 +47,8 @@ int start_game() {
if (get_config_ptr()->render_ground) { render_tilemap(tilemap_ground, tileset_sunnyside); }
if (get_config_ptr()->render_objects) { render_tilemap(tilemap_objects, tileset_sunnyside); }
render_player(&player, (int)time_us/1000);
DrawCircleV(test_enemy.entity.position, 2.0f, RED);
render_enemy(&zombie_enemy);
render_player(&player);
if (get_config_ptr()->render_tips) { render_tilemap(tilemap_tips, tileset_sunnyside); }
if (get_config_ptr()->render_colliders) { render_tilemap(world.col_map, tileset_collision); }
@@ -58,8 +56,6 @@ int start_game() {
render_colliders(&player, &world);
render_to_window(&camera);
time_us += GetFrameTime() * 1000000;
}
// cleanup_window(&window);
+141
View File
@@ -0,0 +1,141 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "../include/world.h"
#include "../include/config.h"
#include "../include/movement.h"
#include "../include/entity.h"
#include "../include/player.h"
#include "../include/enemy.h"
int enemy_follow_path(enemy_t* enemy, player_t* player, world_t* world) {
if (!enemy->path.nodes || enemy->path.len < 1 || enemy->path.index == -1 || enemy->path.finished) {
// no path calcutated
return 1;
}
if (enemy->path.finished) {
printf("TARGET REACHED!\n");
// GOAL WAS ALREADY REACHED
// chase player
return 0;
}
Vector2 target_node = enemy->path.nodes[enemy->path.index];
Vector2 dir = Vector2Subtract(target_node, get_entity_center(&enemy->entity));
float dir_len = Vector2Length(dir);
Vector2 norm_dir = Vector2Normalize(dir);
printf("Next node posi [%d|%d]\n", (int)dir.x, (int)dir.y);
printf("Next direction [%f|%f]\n", norm_dir.x, norm_dir.y);
float speed = enemy->entity.speed_cap * 0.7;
Vector2 step = Vector2Scale(norm_dir, GetFrameTime() * speed);
float step_len = Vector2Length(step);
printf("Next step into [%f|%f]\n", step.x, step.y);
if (step_len < dir_len) {
Vector2 new_pos = Vector2Add(get_entity_center(&enemy->entity), step);
set_entity_center(&enemy->entity, new_pos);
// DONE
return 0;
}
// ELSE: NODE REACHED
set_entity_center(&enemy->entity, target_node);
if (enemy->path.index == enemy->path.len -1) {
enemy->path.finished = true;
// GOAL REACHED
return 0;
}
enemy->path.index++;
return 0;
}
void entity_move_by_velocity(entity_t* entity) {
entity->position = Vector2Add(entity->position, Vector2Scale(entity->velocity, GetFrameTime()));
}
void entity_apply_friction(entity_t* entity) {
float frameTime = GetFrameTime();
entity->velocity = Vector2Scale(entity->velocity, pow(entity->friction, frameTime));
}
int entity_move_and_collide(entity_t* entity, world_t* world) {
Rectangle** rects = get_col_tiles_around_entity(entity, world->col_map);
int col = 0; // 0 = no col
// 1 = horizontal
// 2 = vertical
// 3 = both
int ts = TILE_SIZE; // tile size
Vector2 offset = Vector2Scale(entity->velocity, GetFrameTime());
Vector2 p_ws = {
// entity sprite whitespace
0.5f*(entity->bb_size - entity->sprite_size.x),
0.5f*(entity->bb_size - entity->sprite_size.y),
};
// HORIZONTAL COMPONENT ----------
entity->position.x += offset.x;
// check collision
float xpos = entity->position.x;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (CheckCollisionRecs(rects[y][x], get_entity_rect(entity))) {
// printf("Horizontal Collision \n");
col += 1;
// collision
if (offset.x > 0) {
// collision on the right -> snap to left edge
entity->velocity.x = 0;
entity->position.x = rects[y][x].x - entity->bb_size + p_ws.x;
}
else if (offset.x < 0) {
// collision on the left -> snap to right edge
entity->velocity.x = 0;
entity->position.x = rects[y][x].x + rects[y][x].width - p_ws.x ;
}
}
}
}
// VERTICAL COMPONENT --------
entity->position.y += offset.y;
// check collision
float ypos = entity->position.y;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
if (CheckCollisionRecs(rects[y][x], get_entity_rect(entity))) {
// printf("Vertical Collision \n");
col += 2;
// collision
if (offset.y > 0) {
// collision on the bottom -> snap to top edge
entity->velocity.y = 0;
entity->position.y = rects[y][x].y - entity->bb_size + p_ws.y;
} else if (offset.y <0) {
// collision on the top -> snap to bottom edge
entity->velocity.y = 0;
entity->position.y = rects[y][x].y + rects[y][x].height - p_ws.y;
}
}
}
}
// Finish up
free(rects);
return col;
}
+9 -27
View File
@@ -1,12 +1,14 @@
#include <raylib.h>
#include <raymath.h>
#include <stdio.h>
#include <stdlib.h>
#include "../include/player.h"
#include "../include/world.h"
#include "../include/config.h"
#include "../include/entity.h"
#include "../include/anim.h"
#include "../include/movement.h"
void init_player(player_t* player) {
// visual
@@ -18,7 +20,7 @@ void init_player(player_t* player) {
.state = (int)IDLE,
.frame_count = 4,
};
if (!IsTextureValid(animt.texture)) { printf("WARINING: Player texture loading failed"); return; }
if (!IsTextureValid(animt.texture)) { printf("WARINING: Player texture loading failed\n"); exit(1); }
player->entity = (entity_t){
.anim = animt,
@@ -27,11 +29,8 @@ void init_player(player_t* player) {
.sprite_offset = (Vector2) { -1, -5 }, // X axis relative to player movement direction
// Physics
.position.x = 0.0f,
.position.y = 0.0f,
.velocity.x = 0.0f,
.velocity.y = 0.0f,
.position = Vector2Zero(),
.velocity = Vector2Zero(),
.acceleration = 1000.0f,
.friction = 0.001f,
@@ -55,26 +54,6 @@ Rectangle get_player_sprite(player_t* player, unsigned long total_ms) {
return rect;
}
void render_player(player_t* player, unsigned long total_ms) {
entity_t* ply_ent = &(player->entity);
Vector2 offset = {
.x = ply_ent->sprite_offset.x * (ply_ent->anim.flipped?-1:1),
.y = ply_ent->sprite_offset.y,
};
Rectangle dest_rect = {
ply_ent->position.x + offset.x,
ply_ent->position.y + offset.y,
ply_ent->bb_size,
ply_ent->bb_size,
};
Rectangle source_rect = get_animation_frame(&(player->entity));
DrawTexturePro(player->entity.anim.texture, source_rect, dest_rect, (Vector2) { 0.0, 0.0 }, 0, WHITE);
}
void player_update_velocity_by_input(player_t* player) {
entity_t* ply_ent = &(player->entity);
float frameTime = GetFrameTime();
@@ -111,7 +90,6 @@ void player_update_state(player_t* player) {
}
void update_player(player_t* player, world_t* world) {
player_update_velocity_by_input(player);
if (get_config_ptr()->player_collision) {
entity_move_and_collide(&(player->entity), world);
@@ -123,3 +101,7 @@ void update_player(player_t* player, world_t* world) {
entity_apply_friction(&(player->entity));
}
void render_player(player_t* player) {
render_entity(&player->entity);
}
-20
View File
@@ -1,20 +0,0 @@
#include "../include/test_enemy.h"
#include "../include/enemy.h"
#include "../include/entity.h"
#include "../include/player.h"
# include <raymath.h>
typedef enum Test_Enemy_States {
TEST_ENEMY_IDLE = 0,
} test_enemy_states;
void init_test_enemy(enemy_t* enemy) {
enemy->set_vel_func = &target_player;
enemy->move_func = &entity_move_and_collide;
}
void target_player(enemy_t* enemy, player_t* player) {
enemy->entity.velocity = Vector2Scale(Vector2Normalize(Vector2Subtract(player->entity.position, enemy->entity.position)), 25.0f);
}
+3 -4
View File
@@ -1,6 +1,5 @@
#include <raylib.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
@@ -11,7 +10,7 @@
tile_t* create_tile(int tile_size, int x, int y) {
// allocate memory
tile_t* tile_p = (tile_t*)malloc(sizeof(tile_t));
if (!tile_p) { printf("[!] malloc failed"); exit(1); }
if (!tile_p) { printf("[!] malloc failed\n"); exit(1); }
// write properties
tile_p->rect = (Rectangle) {
@@ -31,7 +30,7 @@ tileset_t* load_tileset(int tile_size, char* tile_sheet_path) {
assert(tile_size > 0);
tileset_t* tileset_p = malloc(sizeof(tileset_t));
if (!tileset_p) { printf("[!] malloc failed"); exit(1); }
if (!tileset_p) { printf("[!] malloc failed\n"); exit(1); }
Texture2D texture = LoadTexture(tile_sheet_path);
assert(IsTextureValid(texture));
@@ -45,7 +44,7 @@ tileset_t* load_tileset(int tile_size, char* tile_sheet_path) {
tileset_p->tile_size = tile_size;
tileset_p->tiles = malloc(sizeof(tile_t*) * total_tiles);
if (!tileset_p->tiles) { printf("[!] malloc failed"); exit(1); }
if (!tileset_p->tiles) { printf("[!] malloc failed\n"); exit(1); }
// populate tile list
int index = 0;
+98
View File
@@ -1,10 +1,108 @@
#include <raylib.h>
#include <stdio.h>
#include "../include/tilemap.h"
#include "../include/world.h"
#include "../include/config.h"
void init_world(world_t* world, char* collision_map) {
world->col_map = load_tilemap(collision_map);
world->afield = (afield_t)generate_astar_field(
world->col_map->rows,
world->col_map->columns,
world->col_map->tmap
);
}
Vector2 world_to_tilemap_coord(Vector2 world_pos) {
Vector2 tile_coord;
tile_coord.x = (int)(floor(world_pos.x / TILE_SIZE));
tile_coord.y = (int)(floor(world_pos.y / TILE_SIZE));
return tile_coord;
}
Vector2 tilemap_to_world_coord(Vector2 tile_pos) {
// scales to top left corner
Vector2 world_coord;
world_coord = Vector2Scale(tile_pos, TILE_SIZE);
return world_coord;
}
path_t world_get_path(world_t* world, Vector2 start, Vector2 target, int max_searches) {
// reset all field values for a clean new search
reset_field(&world->afield, world->col_map->tmap);
// if target is in a collision tile try neighboring cells
Vector2 goal = target;
if (world->col_map->tmap[(int)target.y][(int)target.x] != -1) {
Vector2 offsets[8] = {
// axis aligned checks first
(Vector2){ 0.0f, 1.0f},
(Vector2){ 1.0f, 0.0f},
(Vector2){-1.0f, 0.0f},
(Vector2){ 0.0f, -1.0f},
// diagonals next
(Vector2){-1.0f, -1.0f},
(Vector2){-1.0f, 1.0f},
(Vector2){ 1.0f, -1.0f},
(Vector2){ 1.0f, 1.0f},
};
bool better_pos_found = false;
for (int i = 0; i < 8; i++) {
Vector2 test_pos = (Vector2) {
.x = target.x + offsets[i].x,
.y = target.y + offsets[i].y,
};
// bounds check
if (test_pos.x < 0 || test_pos.y < 0 ||
test_pos.x >= world->col_map->columns ||
test_pos.y >= world->col_map->rows
) {
// out of bounds
continue;
}
// printf("checking out %d %d\n", (int)test_pos.x, (int)test_pos.y);
if (world->col_map->tmap[(int)test_pos.y][(int)test_pos.x] == -1) {
// valid goal found
goal = (Vector2) { (int)test_pos.x, (int)test_pos.y };
// printf("goal: %d %d\n\n", (int)goal.x, (int)goal.y);
better_pos_found = true;
break;
}
}
if (!better_pos_found) {
return (path_t) {
.len = -1,
.nodes = NULL,
.target_pos = (Vector2) { -1, -1 },
.index = -1,
.finished = true,
};
}
}
int path_len = 0;
Vector2* path = get_a_star_path(&world->afield, start, goal, max_searches, &path_len);
// scale path from tile to world positions
for (int i = 0; i < path_len; i++) {
path[i] = tilemap_to_world_coord(path[i]);
path[i].x += TILE_SIZE * 0.5;
path[i].y += TILE_SIZE * 0.5;
}
return (path_t) {
.len = path_len,
.nodes = path,
.target_pos = goal,
.index = 1, // skip start node, we should already be there
.finished = false,
};
}
+85
View File
@@ -0,0 +1,85 @@
#include "../include/zombie_enemy.h"
#include "../include/movement.h"
#include "../include/enemy.h"
#include "../include/entity.h"
#include "../include/player.h"
#include "../include/anim.h"
# include <raymath.h>
# include <assert.h>
# include <stdio.h>
#define MAX_TARGET_POS_CHANGE 40.0f /*In pixels*/
typedef enum Zombie_Enemy_States {
ZOMBIE_ENEMY_IDLE = 0,
} zombie_enemy_states;
void init_zombie_enemy(enemy_t* enemy) {
animator_t anim = {
.texture = LoadTexture("assets/sprites/goblin.png"),
.anim_speed = 0.008f,
.flipped = false,
.state = (int)IDLE,
.frame_count = 4,
};
assert(IsTextureValid(anim.texture));
enemy->vision_radius = 150,
enemy->entity = (entity_t) {
.anim = anim,
.bb_size = 16,
.sprite_size = (Vector2) { 8.0f, 8.0f },
.sprite_offset = (Vector2) { -1.0f, -5.0f },
.position = (Vector2) {0.0f, 0.0f},
.velocity = Vector2Zero(),
.acceleration = 1000.0f,
.friction = 0.001f,
.speed_cap = 60.0f,
};
enemy->set_vel_func = &target_player;
enemy->move_func = &enemy_follow_path;
enemy->update_anim_func = &update_animation_path;
}
void new_path(world_t* world, enemy_t* enemy, player_t* player) {
Vector2 enemy_tile_pos = world_to_tilemap_coord(get_entity_center(&enemy->entity));
Vector2 player_tile_pos = world_to_tilemap_coord(get_entity_center(&player->entity));
enemy->path = world_get_path(world, enemy_tile_pos, player_tile_pos, 200);
}
void target_player(world_t* world, enemy_t* enemy, player_t* player) {
int dist = Vector2Length(Vector2Subtract(player->entity.position, enemy->entity.position));
if (enemy->path.nodes) {
float target_pos_change = Vector2Length(Vector2Subtract(get_entity_center(&player->entity), tilemap_to_world_coord(enemy->path.target_pos)));
printf("target moved %f\n", target_pos_change);
if (target_pos_change > MAX_TARGET_POS_CHANGE) {
new_path(world, enemy, player);
return;
}
}
if ((dist <= enemy->vision_radius && !enemy->path.nodes) || IsKeyDown(KEY_T) || enemy->path.finished) {
new_path(world, enemy, player);
return;
}
}
void update_animation_path(enemy_t* enemy) {
}
void update_animation_velocity(enemy_t* enemy) {
if (Vector2Length(enemy->entity.velocity) > 2.0) {
play_animation(&(enemy->entity.anim), (int)RUNNING);
}
else {
play_animation(&(enemy->entity.anim), (int)IDLE);
}
}
BIN
View File
Binary file not shown.