diff --git a/app.c b/app.c index 7f94aed..8e8ef1d 100644 --- a/app.c +++ b/app.c @@ -1,5 +1,6 @@ #include "app.h" +#include "camera.h" #include "entity.h" #include "input.h" #include "physics.h" @@ -16,6 +17,7 @@ App *app_NewApp() { app->physics = physics_NewSystem(app); app->player = player_NewSystem(app); app->entity = entity_NewSystem(app); + app->camera = camera_NewSystem(app); app->wantQuit = false; @@ -59,6 +61,7 @@ void app_DeleteApp(App *app) { entity_DeleteSystem(app->entity); physics_DeleteSystem(app->physics); player_DeleteSystem(app->player); + camera_DeleteSystem(app->camera); free(app); } @@ -69,4 +72,5 @@ void app_Advance(App *app, Duration deltaTime) { player_Advance(app->player, deltaTime); physics_Advance(app->physics, deltaTime); entity_Advance(app->entity, deltaTime); + camera_Advance(app->camera, deltaTime); } diff --git a/app.h b/app.h index 1d35789..f678ad4 100644 --- a/app.h +++ b/app.h @@ -4,6 +4,7 @@ #include "physics.h" #include "player.h" #include "input.h" +#include "camera.h" #include "types.h" #include "util/vector.h" @@ -17,6 +18,7 @@ typedef struct _App { System_Player *player; System_Input *input; System_Entity *entity; + System_Camera *camera; bool wantQuit; } App; diff --git a/app_debug.c b/app_debug.c index 6b307c2..7be3570 100644 --- a/app_debug.c +++ b/app_debug.c @@ -1,5 +1,6 @@ #include "app.h" +#include "types.h" #include "util/vector.h" #include @@ -19,7 +20,7 @@ void app_DebugText(App *app, vector_Vector *vec_string) { buf, sizeof(buf) - 1, "Player:\n" " Pos: [%.4lf, %.4lf]\n" - " Vec:[%.4lf, %.4lf]\n" + " Vec: [%.4lf, %.4lf]\n" " OnGround: %s\n" " JumpCount: %d\n", player->super->position->position.x, @@ -31,6 +32,17 @@ void app_DebugText(App *app, vector_Vector *vec_string) { PUSH_STRING(buf); } + Vec2 center = box2_Center(app->camera->cam), + size = app->camera->cam.size; + snprintf( + buf, sizeof(buf) - 1, + "Camera:\n" + " Center: [%.4lf, %.4lf]\n" + " Size: [%.4lf, %.4lf]\n", + center.x, center.y, + size.x, size.y); + PUSH_STRING(buf); + char zero = '\0'; vector_Push(vec_string, &zero); } diff --git a/app_render.cpp b/app_render.cpp index d7f8fc2..726650f 100644 --- a/app_render.cpp +++ b/app_render.cpp @@ -1,5 +1,6 @@ #include "app.h" +#include "camera.h" #include "physics.h" #include "easyx.h" #include "util/tree.h" @@ -21,7 +22,7 @@ void app_Render(App *app) { else setlinecolor(RGB(255, 255, 0)); - Box2 box = physics_HitboxAbsolute(e->hitbox); + Box2 box = camera_TransformBox2(app->camera, physics_HitboxAbsolute(e->hitbox)); rectangle( (int)round(box.lefttop.x), (int)round(box.lefttop.y), diff --git a/camera.c b/camera.c index bb3d365..265443a 100644 --- a/camera.c +++ b/camera.c @@ -1,12 +1,86 @@ #include "camera.h" #include "types.h" +#include "entity.h" +#include "util/assert.h" + + +static inline double dabs(double x) { return x < 0.0 ? -x : x; } +static inline double dmin(double x, double y) { return x < y ? x : y; } System_Camera *camera_NewSystem(App *super) { System_Camera *sys = zero_malloc(sizeof(System_Camera)); sys->super = super; -} -void camera_DeleteSystem(System_Camera *sys); -void camera_Advance(System_Camera *sys, Duration deltaTime); + Box2 default_screen = { + .lefttop = {.x = 0.0, .y = 0.0}, + .size = {.x = 1280, .y = 720}}; + sys->cam = sys->screen = default_screen; + return sys; +} +void camera_DeleteSystem(System_Camera *sys) { + free(sys); +} + + +void camera_AddEntity(System_Camera *sys, Entity *e) { + if (e->player) { + ASSERT(!sys->player && "Two player entities are added! Did you forget to DeleteEntity for player?"); + sys->player = e->player; + } +} +void camera_DeleteEntity(System_Camera *sys, uintptr_t id) { + if (sys->player && sys->player->super->id == id) + sys->player = NULL; +} + + +void camera_Advance(System_Camera *sys, Duration deltaTime) { + Vec2 tocenter; + if (sys->target.size.x > EPS) + tocenter = box2_Center(sys->target); + else if (sys->player) + tocenter = sys->player->super->position->position; + else + return; + + double speed; + if (dabs(sys->speed) < EPS) + speed = 4.0; + else + speed = sys->speed; + + Vec2 oldcenter = box2_Center(sys->cam), oldsize = sys->cam.size; + Vec2 newcenter, newsize; + Vec2 diff = vec2_Minus(tocenter, oldcenter); + if (dabs(diff.x) < EPS && dabs(diff.y) < EPS) + newcenter = tocenter; + else { + Vec2 xdiff = vec2_Scale(diff, dmin(speed * duration_Seconds(deltaTime), 1.0)); + newcenter = vec2_Add(oldcenter, xdiff); + } + + // TODO size scaling + newsize = oldsize; + sys->cam = box2_FromCenter(newcenter, newsize); +} + + +inline static Vec2 _camera_TransformSize(System_Camera *sys, Vec2 worldSize) { + Vec2 result = { + .x = worldSize.x / sys->cam.size.x * sys->screen.size.x, + .y = worldSize.y / sys->cam.size.y * sys->screen.size.y}; + return result; +} + +Vec2 camera_TransformVec2(System_Camera *sys, Vec2 world) { + Vec2 relative = vec2_Minus(world, sys->cam.lefttop); + return vec2_Add(sys->screen.lefttop, _camera_TransformSize(sys, relative)); +} +Box2 camera_TransformBox2(System_Camera *sys, Box2 world) { + Box2 result = { + .lefttop = camera_TransformVec2(sys, world.lefttop), + .size = _camera_TransformSize(sys, world.size)}; + return result; +} diff --git a/camera.h b/camera.h index 8ac8641..f504b1d 100644 --- a/camera.h +++ b/camera.h @@ -15,6 +15,7 @@ typedef struct { App *super; Component_Player *player; + Box2 screen; // Screen box, e.g. 1280x720 origin (0,0) Box2 cam, target; // Current & target camera double speed; // Fraction of distance between cam & target to be covered in 1 sec } System_Camera; @@ -22,8 +23,14 @@ typedef struct { System_Camera *camera_NewSystem(App *super); void camera_DeleteSystem(System_Camera *sys); +void camera_AddEntity(System_Camera *sys, Entity *e); +void camera_DeleteEntity(System_Camera *sys, uintptr_t id); + void camera_Advance(System_Camera *sys, Duration deltaTime); +Vec2 camera_TransformVec2(System_Camera *sys, Vec2 world); +Box2 camera_TransformBox2(System_Camera *sys, Box2 world); + #ifdef __cplusplus } diff --git a/entity.c b/entity.c index 69e5708..3bcfcba 100644 --- a/entity.c +++ b/entity.c @@ -1,6 +1,7 @@ #include "entity.h" #include "app.h" +#include "camera.h" #include "physics.h" #include "util/assert.h" #include "util/tree.h" @@ -29,6 +30,7 @@ void entity_DeleteSystem(System_Entity *sys) { physics_DeleteEntity(sys->super->physics, e->id); player_DeleteEntity(sys->super->player, e->id); + camera_DeleteEntity(sys->super->camera, e->id); if (e->position) free(e->position); @@ -67,6 +69,7 @@ Entity *entity_Create(System_Entity *sys, const char *name) { void entity_Commit(System_Entity *sys, Entity *e) { physics_AddEntity(sys->super->physics, e); player_AddEntity(sys->super->player, e); + camera_AddEntity(sys->super->camera, e); } @@ -82,6 +85,7 @@ static inline void _entity_Delete(System_Entity *sys, uintptr_t id) { physics_DeleteEntity(sys->super->physics, e->id); player_DeleteEntity(sys->super->player, e->id); + camera_DeleteEntity(sys->super->camera, e->id); if (e->position) free(e->position); diff --git a/types.c b/types.c index 8d4ede4..085c0ef 100644 --- a/types.c +++ b/types.c @@ -25,6 +25,13 @@ Vec2 vec2_Add(Vec2 x, Vec2 y) { return result; } +Vec2 vec2_Minus(Vec2 pos, Vec2 neg) { + Vec2 result = { + .x = pos.x - neg.x, + .y = pos.y - neg.y}; + return result; +} + Vec2 vec2_Scale(Vec2 v, double scale) { Vec2 result = { .x = v.x * scale, @@ -71,6 +78,15 @@ Vec2 box2_Center(Box2 box) { box.lefttop.y + box.size.y / 2.0); } +Box2 box2_FromCenter(Vec2 center, Vec2 size) { + Box2 box = { + .size = size, + .lefttop = { + .x = center.x - size.x / 2.0, + .y = center.y - size.y / 2.0}}; + return box; +} + Box2 box2_Offset(Box2 box, Vec2 offset) { box.lefttop = vec2_Add(box.lefttop, offset); return box; diff --git a/types.h b/types.h index 0425e87..a9ca92b 100644 --- a/types.h +++ b/types.h @@ -29,6 +29,7 @@ static inline Vec2 vec2(double x, double y) { } Vec2 vec2_Add(Vec2 x, Vec2 y); +Vec2 vec2_Minus(Vec2 pos, Vec2 neg); Vec2 vec2_Scale(Vec2 v, double scale); @@ -43,6 +44,7 @@ typedef struct { bool box2_Intersects(const Box2 x, const Box2 y, Box2 *out_intersection); Vec2 box2_Center(Box2 box); +Box2 box2_FromCenter(Vec2 center, Vec2 size); Box2 box2_Offset(Box2 box, Vec2 offset); Box2 box2_OffsetX(Box2 box, double offsetX);