SG 1 год назад
Родитель
Сommit
eee5d3ae1a
4 измененных файлов с 173 добавлено и 21 удалено
  1. 120 13
      entity.c
  2. 13 4
      entity.h
  3. 6 0
      entity_i.h
  4. 34 4
      level.c

+ 120 - 13
entity.c

@@ -11,34 +11,76 @@ int32_t entities_get_count(void) {
     return entities_count;
     return entities_count;
 }
 }
 
 
+typedef enum {
+    ColliderTypeCircle,
+    ColliderTypeRect,
+} ColliderType;
+
+typedef struct {
+    ColliderType type;
+    union {
+        struct {
+            float radius;
+        } circle;
+        struct {
+            float width;
+            float height;
+        } rect;
+    };
+} ColliderDescription;
+
 struct Entity {
 struct Entity {
     Vector position;
     Vector position;
-    const EntityDescription* behaviour;
+    const EntityDescription* description;
     void* context;
     void* context;
+    ColliderDescription* collider;
 };
 };
 
 
-Entity* entity_alloc(const EntityDescription* behaviour) {
+Entity* entity_alloc(const EntityDescription* description) {
     entities_count++;
     entities_count++;
     Entity* entity = malloc(sizeof(Entity));
     Entity* entity = malloc(sizeof(Entity));
     entity->position = VECTOR_ZERO;
     entity->position = VECTOR_ZERO;
-    entity->behaviour = behaviour;
+    entity->description = description;
     entity->context = NULL;
     entity->context = NULL;
-    if(behaviour && behaviour->context_size > 0) {
-        entity->context = malloc(behaviour->context_size);
+    if(description && description->context_size > 0) {
+        entity->context = malloc(description->context_size);
     }
     }
+    entity->collider = NULL;
     ENTITY_DEBUG("Allocated at %p", entity);
     ENTITY_DEBUG("Allocated at %p", entity);
     return entity;
     return entity;
 }
 }
 
 
+void entity_collider_add_circle(Entity* entity, float radius) {
+    furi_check(entity->collider == NULL, "Collider already added");
+    entity->collider = malloc(sizeof(ColliderDescription));
+    entity->collider->type = ColliderTypeCircle;
+    entity->collider->circle.radius = radius;
+}
+
+void entity_collider_add_rect(Entity* entity, float width, float height) {
+    furi_check(entity->collider == NULL, "Collider already added");
+    entity->collider = malloc(sizeof(ColliderDescription));
+    entity->collider->type = ColliderTypeRect;
+    entity->collider->rect.width = width;
+    entity->collider->rect.height = height;
+}
+
 void entity_free(Entity* entity) {
 void entity_free(Entity* entity) {
     entities_count--;
     entities_count--;
     ENTITY_DEBUG("Freeing at %p", entity);
     ENTITY_DEBUG("Freeing at %p", entity);
     if(entity->context) {
     if(entity->context) {
         free(entity->context);
         free(entity->context);
     }
     }
+    if(entity->collider) {
+        free(entity->collider);
+    }
     free(entity);
     free(entity);
 }
 }
 
 
+const EntityDescription* entity_description_get(Entity* entity) {
+    return entity->description;
+}
+
 Vector entity_pos_get(Entity* entity) {
 Vector entity_pos_get(Entity* entity) {
     return entity->position;
     return entity->position;
 }
 }
@@ -52,25 +94,90 @@ void* entity_context_get(Entity* entity) {
 }
 }
 
 
 void entity_call_start(Level* level, Entity* entity) {
 void entity_call_start(Level* level, Entity* entity) {
-    if(entity->behaviour && entity->behaviour->start) {
-        entity->behaviour->start(entity, level, entity->context);
+    if(entity->description && entity->description->start) {
+        entity->description->start(entity, level, entity->context);
     }
     }
 }
 }
 
 
 void entity_call_stop(Level* level, Entity* entity) {
 void entity_call_stop(Level* level, Entity* entity) {
-    if(entity->behaviour && entity->behaviour->stop) {
-        entity->behaviour->stop(entity, level, entity->context);
+    if(entity->description && entity->description->stop) {
+        entity->description->stop(entity, level, entity->context);
     }
     }
 }
 }
 
 
 void entity_call_update(Entity* entity, Director* director) {
 void entity_call_update(Entity* entity, Director* director) {
-    if(entity->behaviour && entity->behaviour->update) {
-        entity->behaviour->update(entity, director, entity->context);
+    if(entity->description && entity->description->update) {
+        entity->description->update(entity, director, entity->context);
     }
     }
 }
 }
 
 
 void entity_call_render(Entity* entity, Director* director, Canvas* canvas) {
 void entity_call_render(Entity* entity, Director* director, Canvas* canvas) {
-    if(entity->behaviour && entity->behaviour->render) {
-        entity->behaviour->render(entity, director, canvas, entity->context);
+    if(entity->description && entity->description->render) {
+        entity->description->render(entity, director, canvas, entity->context);
+    }
+}
+
+void entity_call_collision(Entity* entity, Entity* other, Director* director) {
+    if(entity->description && entity->description->collision) {
+        entity->description->collision(entity, other, director, entity->context);
+    }
+}
+
+bool entity_collider_circle_circle(Entity* entity, Entity* other) {
+    float dx = entity->position.x - other->position.x;
+    float dy = entity->position.y - other->position.y;
+    float distance = sqrtf(dx * dx + dy * dy);
+    return distance < entity->collider->circle.radius + other->collider->circle.radius;
+}
+
+bool entity_collider_rect_rect(Entity* entity, Entity* other) {
+    float left1 = entity->position.x - entity->collider->rect.width / 2;
+    float right1 = entity->position.x + entity->collider->rect.width / 2;
+    float top1 = entity->position.y - entity->collider->rect.height / 2;
+    float bottom1 = entity->position.y + entity->collider->rect.height / 2;
+
+    float left2 = other->position.x - other->collider->rect.width / 2;
+    float right2 = other->position.x + other->collider->rect.width / 2;
+    float top2 = other->position.y - other->collider->rect.height / 2;
+    float bottom2 = other->position.y + other->collider->rect.height / 2;
+
+    return left1 < right2 && right1 > left2 && top1 < bottom2 && bottom1 > top2;
+}
+
+bool entity_collider_circle_rect(Entity* entity, Entity* other) {
+    float left = other->position.x - other->collider->rect.width / 2;
+    float right = other->position.x + other->collider->rect.width / 2;
+    float top = other->position.y - other->collider->rect.height / 2;
+    float bottom = other->position.y + other->collider->rect.height / 2;
+
+    float closestX = fmaxf(left, fminf(entity->position.x, right));
+    float closestY = fmaxf(top, fminf(entity->position.y, bottom));
+
+    float dx = entity->position.x - closestX;
+    float dy = entity->position.y - closestY;
+    float distance = sqrtf(dx * dx + dy * dy);
+    return distance < entity->collider->circle.radius;
+}
+
+bool entity_collider_check_collision(Entity* entity, Entity* other) {
+    furi_check(entity->collider);
+    furi_check(other->collider);
+
+    if(entity->collider->type == ColliderTypeCircle) {
+        if(other->collider->type == ColliderTypeCircle) {
+            return entity_collider_circle_circle(entity, other);
+        } else {
+            return entity_collider_circle_rect(entity, other);
+        }
+    } else {
+        if(other->collider->type == ColliderTypeCircle) {
+            return entity_collider_circle_rect(other, entity);
+        } else {
+            return entity_collider_rect_rect(entity, other);
+        }
     }
     }
+}
+
+bool entity_collider_exists(Entity* entity) {
+    return entity->collider != NULL;
 }
 }

+ 13 - 4
entity.h

@@ -17,19 +17,28 @@ typedef struct {
 #define VECTOR_ZERO ((Vector){0, 0})
 #define VECTOR_ZERO ((Vector){0, 0})
 
 
 typedef struct {
 typedef struct {
-    void (*start)(Entity* entity, Level* level, void* context);
-    void (*stop)(Entity* entity, Level* level, void* context);
-    void (*update)(Entity* entity, Director* director, void* context);
-    void (*render)(Entity* entity, Director* director, Canvas* canvas, void* context);
+    void (*start)(Entity* self, Level* level, void* context);
+    void (*stop)(Entity* self, Level* level, void* context);
+    void (*update)(Entity* self, Director* director, void* context);
+    void (*render)(Entity* self, Director* director, Canvas* canvas, void* context);
+    void (*collision)(Entity* self, Entity* other, Director* director, void* context);
     size_t context_size;
     size_t context_size;
 } EntityDescription;
 } EntityDescription;
 
 
+const EntityDescription* entity_description_get(Entity* entity);
+
 Vector entity_pos_get(Entity* entity);
 Vector entity_pos_get(Entity* entity);
 
 
 void entity_pos_set(Entity* entity, Vector position);
 void entity_pos_set(Entity* entity, Vector position);
 
 
 void* entity_context_get(Entity* entity);
 void* entity_context_get(Entity* entity);
 
 
+typedef struct Collider Collider;
+
+void entity_collider_add_circle(Entity* entity, float radius);
+
+void entity_collider_add_rect(Entity* entity, float width, float height);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 6 - 0
entity_i.h

@@ -18,6 +18,12 @@ void entity_call_update(Entity* entity, Director* director);
 
 
 void entity_call_render(Entity* entity, Director* director, Canvas* canvas);
 void entity_call_render(Entity* entity, Director* director, Canvas* canvas);
 
 
+void entity_call_collision(Entity* entity, Entity* other, Director* director);
+
+bool entity_collider_check_collision(Entity* entity, Entity* other);
+
+bool entity_collider_exists(Entity* entity);
+
 int32_t entities_get_count(void);
 int32_t entities_get_count(void);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus

+ 34 - 4
level.c

@@ -115,15 +115,45 @@ void level_remove_entity(Level* level, Entity* entity) {
     entity_call_stop(level, entity);
     entity_call_stop(level, entity);
 }
 }
 
 
-void level_update(Level* level, Director* director) {
-    level_process_add(level);
-    level_process_remove(level);
-
+static void level_process_update(Level* level, Director* director) {
     FOREACH(item, level->entities) {
     FOREACH(item, level->entities) {
         entity_call_update(*item, director);
         entity_call_update(*item, director);
     }
     }
 }
 }
 
 
+static void level_process_collision(Level* level, Director* director) {
+    EntityList_it_t it_first;
+    EntityList_it_t it_second;
+
+    EntityList_it(it_first, level->entities);
+    while(!EntityList_end_p(it_first)) {
+        if(entity_collider_exists(*EntityList_cref(it_first))) {
+            // start second iterator at the next entity,
+            // so we don't check the same pair twice
+            EntityList_it_set(it_second, it_first);
+            EntityList_next(it_second);
+            while(!EntityList_end_p(it_second)) {
+                if(entity_collider_exists(*EntityList_cref(it_second))) {
+                    Entity* first = *EntityList_ref(it_first);
+                    Entity* second = *EntityList_ref(it_second);
+                    if(entity_collider_check_collision(first, second)) {
+                        entity_call_collision(first, second, director);
+                        entity_call_collision(second, first, director);
+                    }
+                }
+                EntityList_next(it_second);
+            }
+        }
+        EntityList_next(it_first);
+    }
+}
+void level_update(Level* level, Director* director) {
+    level_process_add(level);
+    level_process_remove(level);
+    level_process_update(level, director);
+    level_process_collision(level, director);
+}
+
 void level_render(Level* level, Director* director, Canvas* canvas) {
 void level_render(Level* level, Director* director, Canvas* canvas) {
     FOREACH(item, level->entities) {
     FOREACH(item, level->entities) {
         entity_call_render(*item, director, canvas);
         entity_call_render(*item, director, canvas);