enemy is following player, hurrah
This commit is contained in:
@@ -10,6 +10,5 @@ clean:
|
||||
rm -f $(TARGET)
|
||||
|
||||
run: $(TARGET)
|
||||
rm -f $(TARGET)
|
||||
gcc $(SRC) -o $(TARGET) $(LIB)
|
||||
./$(TARGET)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Binary file not shown.
|
After Width: | Height: | Size: 720 B |
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
config_t global_config = {
|
||||
// DEBUG
|
||||
.render_colliders = false,
|
||||
.render_paths = true,
|
||||
.player_collision = true,
|
||||
|
||||
// layers
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user