diff --git a/data/sprites/breadball.png b/data/sprites/breadball.png new file mode 100644 index 0000000..da1cba7 Binary files /dev/null and b/data/sprites/breadball.png differ diff --git a/data/sprites/bricks.png b/data/sprites/bricks.png new file mode 100644 index 0000000..fe10e29 Binary files /dev/null and b/data/sprites/bricks.png differ diff --git a/helianthus/animation.c b/helianthus/animation.c index acccf36..068d7c6 100644 --- a/helianthus/animation.c +++ b/helianthus/animation.c @@ -20,10 +20,10 @@ static cairo_surface_t* loadFrame(const char *path) { cairo_surface_t *frame = cairo_image_surface_create_from_png_stream(&read, f); fclose(f); if (!frame) - fprintf(stderr, "helianthus: cannot load PNG content form file: %s", path); + fprintf(stderr, "helianthus: cannot load PNG content form file: %s\n", path); return frame; } - fprintf(stderr, "helianthus: cannot open image file: %s", path); + fprintf(stderr, "helianthus: cannot open image file: %s\n", path); return NULL; } @@ -46,7 +46,7 @@ static HeliAnimation* load(const char *path) { if (frame) { heliArrayInsert(&a->frames, -1, frame, (HeliFreeCallback)&cairo_surface_destroy); } else { - fprintf(stderr, "helianthus: cannot load animation by path: %s", path); + fprintf(stderr, "helianthus: cannot load animation by path: %s\n", path); } } diff --git a/helianthus/array.c b/helianthus/array.c index 707e313..6e87b14 100644 --- a/helianthus/array.c +++ b/helianthus/array.c @@ -60,7 +60,7 @@ HeliPair* heliArrayInsertPair(HeliArray *a, int i, void *k, void *v, HeliFreeCal } HeliPair* heliArrayInsert(HeliArray *a, int i, void *v, HeliFreeCallback fv) - { return heliArrayInsertPair(a, i, NULL, NULL, v, fv); } + { return heliArrayInsertPair(a, i, NULL, v, NULL, fv); } void heliArrayRemove(HeliArray *a, int i) { if (i < 0 || i >= a->count) return; diff --git a/helianthus/common.c b/helianthus/common.c index 7020b40..c183e83 100644 --- a/helianthus/common.c +++ b/helianthus/common.c @@ -42,7 +42,7 @@ int heliStringEndsWithLowcase(const char *s, const char *tail) { } void heliLowercase(char *x) - { while(*x) *x = tolower(*x); } + { while(*x) { *x = tolower(*x); ++x; } } void heliParseColor(const char *x, double *color) { #warning "TODO: heliParseColor: not implemented yet" diff --git a/helianthus/main.c b/helianthus/main.c index dc75f8a..b0c0a49 100644 --- a/helianthus/main.c +++ b/helianthus/main.c @@ -1,10 +1,38 @@ +#include + #include "helianthus.h" +Sprite ball, brick1, brick2; + void init() { + ball = createSpriteEx(200, 200, 64, 64); + spriteSetAnimation(ball, "data/sprites/breadball.png"); + spriteSetDebug(ball, TRUE); + + brick1 = createSpriteEx(200-32, 200+64, 64, 64); + spriteSetAnimation(brick1, "data/sprites/bricks.png"); + spriteSetDebug(brick1, TRUE); + + brick2 = createSpriteEx(200+32, 200+64, 64, 64); + spriteSetAnimation(brick2, "data/sprites/bricks.png"); + spriteSetDebug(brick2, TRUE); } void draw() { + double dt = worldGetTimeStep(); + double speed = 100; + + spritePointTo(ball, mouseX(), mouseY()); + + double x = spriteGetX(ball); + double y = spriteGetY(ball); + if (keyDown("left")) spriteSetX(ball, x - speed*dt); + if (keyDown("right")) spriteSetX(ball, x + speed*dt); + if (keyDown("up")) spriteSetY(ball, y - speed*dt); + if (keyDown("down")) spriteSetY(ball, y + speed*dt); + + drawSprites(); } int main() { diff --git a/helianthus/sprite.c b/helianthus/sprite.c index 69c579a..57fb407 100644 --- a/helianthus/sprite.c +++ b/helianthus/sprite.c @@ -204,7 +204,7 @@ void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOff void spriteSetAnimation(Sprite sprite, const char *path) { HeliAnimation *prev = sprite->animation; sprite->animation = heliCreateAnimation(path); - heliAnimationUnref(prev); + if (prev) heliAnimationUnref(prev); spriteSetFrame(sprite, sprite->frame); } @@ -241,7 +241,7 @@ double spriteGetDirection(Sprite sprite) { ? atan2(sprite->vy, sprite->vx)*(180/PI) : 0; } void spritePointTo(Sprite sprite, double x, double y) - { spriteSetRotation( sprite, atan2(y - sprite->y, x - sprite->x)*(PI/180) ); } + { spriteSetRotation( sprite, atan2(y - sprite->y, x - sprite->x)*(180/PI) ); } double spriteGetScaledWidth(Sprite sprite) { return sprite->width * sprite->scale; } @@ -259,7 +259,9 @@ Sprite worldGetSprite(int i) static void drawSpriteDebug(cairo_t *cr, Sprite s) { cairo_save(cr); + cairo_set_line_width(cr, 0.5); cairo_translate(cr, s->x, s->y); + cairo_rotate(cr, s->rotation*(PI/180)); cairo_scale(cr, s->scale*s->mirrorX, s->scale*s->mirrorY); double hw = s->width*0.5; @@ -282,10 +284,11 @@ static void drawSpriteDebug(cairo_t *cr, Sprite s) { char buf[1024]; snprintf(buf, sizeof(buf)-1, "%f", s->depth); - double s1 = hw*0.5; + double s1 = hw*0.25; double s2 = hh*0.5; cairo_set_font_size(cr, s1 < s2 ? s1 : s2); - cairo_move_to(cr, -hw*0.9, -hh*0.9); + + cairo_move_to(cr, -hw*0.9, -hh*0.5); cairo_show_text(cr, buf); cairo_restore(cr); @@ -294,6 +297,7 @@ static void drawSpriteDebug(cairo_t *cr, Sprite s) { static void drawSprite(cairo_t *cr, Sprite s) { cairo_save(cr); cairo_translate(cr, s->x, s->y); + cairo_rotate(cr, s->rotation*(PI/180)); cairo_scale(cr, s->scale*s->mirrorX, s->scale*s->mirrorY); double hw = s->width*0.5; @@ -312,6 +316,7 @@ static void drawSprite(cairo_t *cr, Sprite s) { cairo_save(cr); cairo_scale(cr, hw/ihw, hh/ihh); cairo_set_source_surface(cr, frame, -ihw, -ihh); + cairo_paint(cr); cairo_restore(cr); } } else { @@ -374,12 +379,13 @@ void drawSprites() { } } - // draw if (heliCairo) { + // draw for(int i = 0; i < spritesSorted.count; ++i) { Sprite s = (Sprite)(spritesSorted.items[i].value); if (s->visible) drawSprite(heliCairo, s); } + // draw debug marks for(int i = 0; i < spritesSorted.count; ++i) { Sprite s = (Sprite)(spritesSorted.items[i].value); if (s->debug) drawSpriteDebug(heliCairo, s); diff --git a/helianthus/world.c b/helianthus/world.c index 2bbc96a..6a79902 100644 --- a/helianthus/world.c +++ b/helianthus/world.c @@ -20,8 +20,8 @@ static guint timerInterval; static Callback initCallback; static Callback drawCallback; -static int width; -static int height; +static int width = 400; +static int height = 400; static double frameRate = 30; static int cameraEnabled; @@ -79,19 +79,24 @@ double mouseY() int mousePressedOver(Sprite sprite) { return buttonsPressed.count && mouseIsOver(sprite); } +static void resize(int w, int h) { + width = w > 200 ? w : 100; + height = h > 200 ? h : 200; + if (started) { + gtk_window_set_default_size(GTK_WINDOW(window), width, height); + gtk_widget_set_size_request(window, width, height); + } +} + int worldGetWidth() { return width; } -void worldSetWidth(int w) { - width = w; - if (started) gtk_widget_set_size_request(window, width, height); -} +void worldSetWidth(int w) + { resize(w, height); } int worldGetHeight() { return height; } -void worldSetHeight(int h) { - height = h; - if (started) gtk_widget_set_size_request(window, width, height); -} +void worldSetHeight(int h) + { resize(width, h); } double worldGetFrameRate() { return frameRate; } @@ -100,6 +105,9 @@ void worldSetFrameRate(double frameRate) { if (!(frameRate < 100)) frameRate = 100; } +double worldGetTimeStep() + { return 1/frameRate; } + int worldGetFrameCount() { return (int)frameCount; } double worldGetSeconds() @@ -150,7 +158,7 @@ static gboolean timeout(gpointer data G_GNUC_UNUSED) { guint interval = (guint)round(1000/frameRate); if (interval != timerInterval) { timerInterval = interval; - g_timeout_add(timerInterval, timeout, NULL); + g_timeout_add_full(G_PRIORITY_LOW, timerInterval, timeout, NULL, NULL); return FALSE; } return TRUE; @@ -160,30 +168,22 @@ static gboolean draw(GtkWidget *widget G_GNUC_UNUSED, cairo_t *cr, gpointer data guint64 newTime = g_get_monotonic_time(); heliCairo = cr; - if (!frameCount) { - startTime = currentTime = newTime; - if (initCallback) initCallback(); + double dt = 1/frameRate; + guint64 timeStep = (guint64)round(dt*1e6); + while(currentTime < newTime) { + cairo_save(cr); heliDrawingPrepareFrame(); if (drawCallback) drawCallback(); + cairo_restore(cr); + + currentTime += timeStep; + ++frameCount; + heliSpriteUpdate(dt); + heliArrayClear(&keysPressedInFrame); heliArrayClear(&keysReleasedInFrame); heliArrayClear(&buttonsPressedInFrame); heliArrayClear(&buttonsReleasedInFrame); - ++frameCount; - } else { - double dt = 1/frameRate; - guint64 timeStep = (guint64)round(dt*1e6); - while(currentTime < newTime) { - currentTime += timeStep; - ++frameCount; - heliSpriteUpdate(dt); - heliDrawingPrepareFrame(); - if (drawCallback) drawCallback(); - heliArrayClear(&keysPressedInFrame); - heliArrayClear(&keysReleasedInFrame); - heliArrayClear(&buttonsPressedInFrame); - heliArrayClear(&buttonsReleasedInFrame); - } } heliCairo = NULL; @@ -200,6 +200,7 @@ static gboolean handleEvent(GtkWidget *widget G_GNUC_UNUSED, GdkEvent *event, gp heliLowercase(keyname); heliStringmapAdd(&keysPressed, keyname, NULL, NULL); heliStringmapAdd(&keysPressedInFrame, keyname, NULL, NULL); + //printf("key pressed: %s\n", keyname); free(keyname); return TRUE; case GDK_KEY_RELEASE: @@ -241,6 +242,8 @@ static void activate(GtkApplication* app, gpointer data G_GNUC_UNUSED) { window = gtk_application_window_new(app); g_signal_connect(window, "draw", G_CALLBACK(draw), NULL); g_signal_connect(window, "event", G_CALLBACK(handleEvent), NULL); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_default_size(GTK_WINDOW(window), width, height); gtk_widget_add_events( window, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK @@ -249,9 +252,12 @@ static void activate(GtkApplication* app, gpointer data G_GNUC_UNUSED) { | GDK_KEY_RELEASE_MASK ); gtk_widget_set_size_request(window, width, height); gtk_widget_show(window); - timerInterval = (guint)round(1000/frameRate); - g_timeout_add(timerInterval, timeout, NULL); + if (initCallback) initCallback(); + + currentTime = g_get_monotonic_time(); + timerInterval = (guint)round(1000/frameRate); + g_timeout_add_full(G_PRIORITY_LOW, timerInterval, timeout, NULL, NULL); } void worldRun() { diff --git a/helianthus/world.h b/helianthus/world.h index 8faff8b..2b8b377 100644 --- a/helianthus/world.h +++ b/helianthus/world.h @@ -41,6 +41,8 @@ void worldSetHeight(int height); double worldGetFrameRate(); void worldSetFrameRate(double frameRate); +double worldGetTimeStep(); + int worldGetFrameCount(); double worldGetSeconds();