Vastly less error-prone animation lifecycle
This commit is contained in:
1 parent
9a836effda
commit
4b9d90efc8
1 file changed
+66
-43
+66
-43
@@ -72,7 +72,7 @@ static uint8_t s_selected_indices[3] = {9, 9, 9};
|
||||
static uint8_t s_secs_since_last_input;
|
||||
static uint8_t s_shake_cooldown;
|
||||
static uint8_t s_zap_cooldown;
|
||||
static uint8_t s_countdown_int = 0;
|
||||
static uint8_t s_countdown_int;
|
||||
static bool s_continuous_zap_on_first_triangle;
|
||||
static char s_countdown_buf[4];
|
||||
static char s_progress_buf[8];
|
||||
@@ -107,8 +107,11 @@ static const VibePattern s_first_triangle_vibe_pattern = {
|
||||
};
|
||||
|
||||
// animation stuff
|
||||
#define ANIMATION_DURATION 350
|
||||
static GRect s_left_door_starting_grect = GRect(-(PBL_DISPLAY_WIDTH / 2), s_small_top_bar_height, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_HEIGHT - s_small_top_bar_height - s_bottom_bar_grect.size.h);
|
||||
static GRect s_right_door_starting_grect = GRect(PBL_DISPLAY_WIDTH, s_small_top_bar_height, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_HEIGHT - s_small_top_bar_height - s_bottom_bar_grect.size.h);
|
||||
static bool s_door_correct;
|
||||
static Animation *s_door_anim;
|
||||
|
||||
// alarm drop menu options
|
||||
static const char *s_options_function[3];
|
||||
@@ -515,54 +518,69 @@ static void door_layer_green_update_proc(Layer *layer, GContext *ctx) {
|
||||
graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone);
|
||||
}
|
||||
|
||||
static void door_closed_fail_animation_handler(Animation *animation, bool finished, void *context) {
|
||||
if (s_current_triangle_num > 1) {
|
||||
static void door_closed_animation_stopped_handler(Animation *a, bool finished, void *ctx) {
|
||||
if (!finished)
|
||||
return;
|
||||
if (s_door_correct)
|
||||
s_current_triangle_num++;
|
||||
else if (s_current_triangle_num > 1)
|
||||
s_current_triangle_num--;
|
||||
}
|
||||
right_triangle_layer_regen(s_triangle_layer);
|
||||
update_for_new_triangle();
|
||||
}
|
||||
|
||||
static void door_closed_success_animation_handler(Animation *animation, bool finished, void *context) {
|
||||
s_current_triangle_num++;
|
||||
right_triangle_layer_regen(s_triangle_layer);
|
||||
update_for_new_triangle();
|
||||
static void door_animation_stopped_handler(Animation *a, bool finished, void *ctx) {
|
||||
animation_destroy(a);
|
||||
s_door_anim = NULL;
|
||||
}
|
||||
|
||||
static void destroy_animation_handler(Animation *animation, bool finished, void *context) {
|
||||
animation_destroy(animation);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// reuse some of these animations, if possible
|
||||
// add iconography to door halves
|
||||
static void play_door_animation(bool correct) {
|
||||
PropertyAnimation *left_door_prop_anim = property_animation_create_layer_frame(s_left_door_layer, &s_left_door_starting_grect, &GRect(0, s_left_door_starting_grect.origin.y, s_left_door_starting_grect.size.w, s_left_door_starting_grect.size.h));
|
||||
PropertyAnimation *right_door_prop_anim = property_animation_create_layer_frame(s_right_door_layer, &s_right_door_starting_grect, &GRect(PBL_DISPLAY_WIDTH / 2, s_left_door_starting_grect.origin.y, s_left_door_starting_grect.size.w, s_left_door_starting_grect.size.h));
|
||||
Animation *left_door_close_anim = property_animation_get_animation(left_door_prop_anim);
|
||||
Animation *right_door_close_anim = property_animation_get_animation(right_door_prop_anim);
|
||||
animation_set_duration(left_door_close_anim, 500);
|
||||
animation_set_duration(right_door_close_anim, 500);
|
||||
Animation *left_door_open_anim = animation_clone(left_door_close_anim);
|
||||
Animation *right_door_open_anim = animation_clone(right_door_close_anim);
|
||||
animation_set_reverse(left_door_open_anim, true);
|
||||
animation_set_reverse(right_door_open_anim, true);
|
||||
Animation *door_close_spawn_anim = animation_spawn_create(left_door_close_anim, right_door_close_anim, NULL);
|
||||
Animation *door_open_spawn_anim = animation_spawn_create(left_door_open_anim, right_door_open_anim, NULL);
|
||||
animation_set_delay(door_open_spawn_anim, 100);
|
||||
if (correct) {
|
||||
layer_set_update_proc(s_left_door_layer, door_layer_green_update_proc);
|
||||
layer_set_update_proc(s_right_door_layer, door_layer_green_update_proc);
|
||||
animation_set_handlers(door_close_spawn_anim, (AnimationHandlers){.stopped = door_closed_success_animation_handler}, NULL);
|
||||
} else {
|
||||
layer_set_update_proc(s_left_door_layer, door_layer_red_update_proc);
|
||||
layer_set_update_proc(s_right_door_layer, door_layer_red_update_proc);
|
||||
animation_set_handlers(door_close_spawn_anim, (AnimationHandlers){.stopped = door_closed_fail_animation_handler}, NULL);
|
||||
// prevent from playing if an instance is already running and not yet finished;
|
||||
// prevents instances where the user is punished if the timer expires AFTER the submission
|
||||
if (s_door_anim) {
|
||||
return;
|
||||
}
|
||||
Animation *seq = animation_sequence_create(door_close_spawn_anim, door_open_spawn_anim, NULL);
|
||||
animation_set_handlers(seq, (AnimationHandlers){.stopped = destroy_animation_handler}, NULL);
|
||||
animation_schedule(seq);
|
||||
};
|
||||
|
||||
// reset shake cooldown here so it is guaranteed
|
||||
// to be in effect while an animation is playing
|
||||
s_shake_cooldown = SHAKE_COOLDOWN_SECS;
|
||||
|
||||
// ensure door layers are correct color
|
||||
s_door_correct = correct;
|
||||
layer_set_update_proc(s_left_door_layer, correct ? door_layer_green_update_proc : door_layer_red_update_proc);
|
||||
layer_set_update_proc(s_right_door_layer, correct ? door_layer_green_update_proc : door_layer_red_update_proc);
|
||||
|
||||
// create base close animations
|
||||
PropertyAnimation *l = property_animation_create_layer_frame(s_left_door_layer, &s_left_door_starting_grect, &GRect(0, s_left_door_starting_grect.origin.y, s_left_door_starting_grect.size.w, s_left_door_starting_grect.size.h));
|
||||
PropertyAnimation *r = property_animation_create_layer_frame(s_right_door_layer, &s_right_door_starting_grect, &GRect(PBL_DISPLAY_WIDTH / 2, s_left_door_starting_grect.origin.y, s_left_door_starting_grect.size.w, s_left_door_starting_grect.size.h));
|
||||
Animation *lc = property_animation_get_animation(l), *rc = property_animation_get_animation(r);
|
||||
animation_set_duration(lc, ANIMATION_DURATION);
|
||||
animation_set_duration(rc, ANIMATION_DURATION);
|
||||
|
||||
// duplicate and reverse for open animations
|
||||
Animation *lo = animation_clone(lc), *ro = animation_clone(rc);
|
||||
animation_set_reverse(lo, true);
|
||||
animation_set_reverse(ro, true);
|
||||
|
||||
// create spawn animations
|
||||
Animation *close = animation_spawn_create(lc, rc, NULL);
|
||||
Animation *open = animation_spawn_create(lo, ro, NULL);
|
||||
animation_set_delay(open, 100);
|
||||
|
||||
// set door close handler
|
||||
animation_set_handlers(close, (AnimationHandlers){.stopped = door_closed_animation_stopped_handler}, NULL);
|
||||
|
||||
// create sequence
|
||||
s_door_anim = animation_sequence_create(close, open, NULL);
|
||||
|
||||
// set stopped handler;
|
||||
// even though the SDK auto-destroys after finished, we destroy manually
|
||||
// since we already need a stopped handler to NULL it for the check in alarm_window_unload()
|
||||
animation_set_handlers(s_door_anim, (AnimationHandlers){.stopped = door_animation_stopped_handler}, NULL);
|
||||
|
||||
// play animation sequence;
|
||||
animation_schedule(s_door_anim);
|
||||
}
|
||||
|
||||
static void increment_secs_since_last_input() {
|
||||
if (s_secs_since_last_input < MAX_SECS_WITHOUT_INPUT) {
|
||||
@@ -674,7 +692,8 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) {
|
||||
|
||||
if (s_countdown_int > 0) {
|
||||
if (s_current_triangle_num == settings.TriangleCount) {
|
||||
tick_timer_service_unsubscribe(); // alarm is fully disarmed - prevent stimulus in confirmation window
|
||||
tick_timer_service_unsubscribe(); // prevent stimulus in confirmation window
|
||||
accel_tap_service_unsubscribe(); // don't listen for shakes in confirmation window
|
||||
wakeup_service_subscribe(NULL); // ignore wakeup events to avoid overwriting new alarm
|
||||
if (schedule_alarm(WAKE_ALARM)) {
|
||||
push_status_window(STATUS_CONFIRMATION);
|
||||
@@ -683,8 +702,6 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) {
|
||||
play_door_animation(true);
|
||||
}
|
||||
}
|
||||
|
||||
s_shake_cooldown = SHAKE_COOLDOWN_SECS;
|
||||
}
|
||||
|
||||
static void alarm_click_config_provider(void *ctx) {
|
||||
@@ -913,6 +930,12 @@ static void alarm_window_load() {
|
||||
static void alarm_window_unload() {
|
||||
tick_timer_service_unsubscribe();
|
||||
accel_tap_service_unsubscribe();
|
||||
if (s_door_anim) {
|
||||
// unschedule and destroy if unload occurs while an animation is running;
|
||||
// e.g. if the alarm reset button is used from the phone-side
|
||||
animation_destroy(s_door_anim);
|
||||
s_door_anim = NULL;
|
||||
}
|
||||
layer_destroy(s_right_door_layer);
|
||||
layer_destroy(s_left_door_layer);
|
||||
text_layer_destroy(s_progress_txt_layer);
|
||||
|
||||
Reference in new issue
Block a user