From 292f0058d5c55108dcc95fbf3537c8fd5ebe3e6d Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Sat, 27 Jan 2024 04:05:00 +0200 Subject: [PATCH] zephr/game: fix few memory leaks (and introduced a big one). spawn particles when tiles merge --- assets/particles/merge.png | Bin 0 -> 6106 bytes game.odin | 80 ++++++++++++++++++++------ particle.odin | 112 +++++++++++++++++++++++++++++++++++++ shaders/font.vert | 2 +- zephr/os_linux.odin | 1 + zephr/texture.odin | 7 +++ zephr/ui.odin | 14 +++-- zephr/zephr.odin | 2 + 8 files changed, 194 insertions(+), 24 deletions(-) create mode 100644 assets/particles/merge.png create mode 100644 particle.odin diff --git a/assets/particles/merge.png b/assets/particles/merge.png new file mode 100644 index 0000000000000000000000000000000000000000..3db26748ccb5c7bed3dd82fdee92a26518938ee0 GIT binary patch literal 6106 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 zephr.Vec2 { @@ -558,6 +561,7 @@ game_loop :: proc() { last_frame = now update_positions(delta_t) + update_emitters(delta_t) draw_bg() draw_board() @@ -567,6 +571,14 @@ game_loop :: proc() { } } +update_emitters :: proc(delta_t: time.Duration) { + for row in &game.board { + for tile in &row { + update_emitter(delta_t, &tile.emitter) + } + } +} + update_positions :: proc(delta_t: time.Duration) { if (game.animating) { switch game.last_move_dir { @@ -578,13 +590,14 @@ update_positions :: proc(delta_t: time.Duration) { continue } if src.anim_timer < TILE_ANIM_DURATION { - src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) - src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) src.anim_timer += delta_t } else { dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y] if dest.new_value == src.value * 2 { + emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200) dest.merged = false } @@ -596,6 +609,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.new_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = dest.emitter, } src^ = Tile{ value = src.new_value, @@ -605,6 +619,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.start_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = src.emitter } game.spawning_new_tile = true @@ -619,13 +634,14 @@ update_positions :: proc(delta_t: time.Duration) { continue } if src.anim_timer < TILE_ANIM_DURATION { - src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) - src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) src.anim_timer += delta_t } else { dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y] if dest.new_value == src.value * 2 { + emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200) dest.merged = false } @@ -637,6 +653,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.new_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = dest.emitter, } src^ = Tile{ value = src.new_value, @@ -646,6 +663,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.start_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = src.emitter, } game.spawning_new_tile = true @@ -660,13 +678,14 @@ update_positions :: proc(delta_t: time.Duration) { continue } if src.anim_timer < TILE_ANIM_DURATION { - src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) - src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) src.anim_timer += delta_t } else { dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y] if dest.new_value == src.value * 2 { + emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200) dest.merged = false } @@ -678,6 +697,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.new_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = dest.emitter, } src^ = Tile{ value = src.new_value, @@ -687,6 +707,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.start_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = src.emitter, } game.spawning_new_tile = true @@ -701,13 +722,14 @@ update_positions :: proc(delta_t: time.Duration) { continue } if src.anim_timer < TILE_ANIM_DURATION { - src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) - src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_in_out_back(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.x = math.lerp(src.start_pos.x, src.new_pos.x, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) + src.position.y = math.lerp(src.start_pos.y, src.new_pos.y, cast(f32)zephr.ease_out_cubic(cast(f64)src.anim_timer / cast(f64)TILE_ANIM_DURATION)) src.anim_timer += delta_t } else { dest := &game.board[cast(int)src.new_pos.x][cast(int)src.new_pos.y] if dest.new_value == src.value * 2 { + emitter_start(&dest.emitter, 500, dest.new_pos, time.Millisecond * 200) dest.merged = false } @@ -719,6 +741,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.new_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = dest.emitter, } src^ = Tile{ value = src.new_value, @@ -728,6 +751,7 @@ update_positions :: proc(delta_t: time.Duration) { new_pos = src.start_pos, anim_timer = 0, scale = zephr.Vec2{1, 1}, + emitter = src.emitter } game.spawning_new_tile = true @@ -883,6 +907,26 @@ draw_board :: proc() { zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED) zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED) + + // Draw particles + particle_con := zephr.DEFAULT_UI_CONSTRAINTS + for particle in tile.emitter.particles { + if (particle.life <= 0) { + continue + } + zephr.set_parent_constraint(&particle_con, &tile_con) + zephr.set_x_constraint(&particle_con, particle.pos.x, .FIXED) + zephr.set_y_constraint(&particle_con, particle.pos.y, .FIXED) + particle_con.scale = particle.size + zephr.set_width_constraint(&particle_con, 30, .RELATIVE_PIXELS) + zephr.set_height_constraint(&particle_con, 1, .ASPECT_RATIO) + zephr.draw_texture(&particle_con, game.merge_particle_texture, zephr.UiStyle{ + bg_color = particle.color, + fg_color = particle.color, + align = .CENTER, + }) + } + zephr.draw_quad(&tile_con, style) // value text diff --git a/particle.odin b/particle.odin new file mode 100644 index 0000000..8c25322 --- /dev/null +++ b/particle.odin @@ -0,0 +1,112 @@ +package main + +import "core:math/rand" +import "core:time" + +import "zephr" + +Particle :: struct { + pos: zephr.Vec2, + vel: zephr.Vec2, + color: zephr.Color, + size: f32, + life: time.Duration, +} + +ParticleEmitter :: struct { + pos: zephr.Vec2, + particles: [dynamic]Particle, + duration: time.Duration, + spawn_rate: u32, +} + +@private +last_emitted_particle_idx: int = 0 +// restricts emission rate to 60fps +@private +particle_time: time.Duration = 0 + +@private +update_particles :: proc(delta_t: time.Duration, particles: ^[dynamic]Particle, start_pos: zephr.Vec2) { + delta_t_f := cast(f32)time.duration_seconds(delta_t) + + for p in particles { + p.life -= delta_t + if p.life <= 0 { + continue + } + + // NOTE: fades faster on higher framerates. don't feel like fixing it + p.color.a -= 1 + p.size -= delta_t_f + p.vel.x += (p.pos.x + p.vel.x / 7) * delta_t_f + p.vel.y -= (p.pos.y + p.vel.y / 7) * delta_t_f + p.pos += p.vel * -delta_t_f + } +} + +@private +find_first_dead_particle :: proc(particles: [dynamic]Particle) -> int { + // search from last emitted particle, should return almost immediately + for i in last_emitted_particle_idx.. 0 { + emitter.duration -= delta_t + if emitter.duration <= 0 { + return + } + + particle_time += delta_t + + for particle_time >= time.Millisecond * 16 { + for i in 0.. TextureId { + context.logger = logger + texture_id: TextureId width, height, channels: i32 data := stb.load(path, &width, &height, &channels, 4) // RGBA + if data == nil { + log.errorf("Failed to load texture: \"%s\"", path) + } defer stb.image_free(data) format := gl.RGBA diff --git a/zephr/ui.odin b/zephr/ui.odin index c5fe41d..147d778 100644 --- a/zephr/ui.odin +++ b/zephr/ui.odin @@ -381,13 +381,13 @@ draw_triangle :: proc(constraints: ^UiConstraints, style: UiStyle) { gl.BindVertexArray(0) } -draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color: Color, radius: f32, align: Alignment) { +draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, style: UiStyle) { use_shader(ui_shader) - set_vec4f(ui_shader, "aColor", cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255) + set_vec4f(ui_shader, "aColor", cast(f32)style.bg_color.r / 255, cast(f32)style.bg_color.g / 255, cast(f32)style.bg_color.b / 255, cast(f32)style.bg_color.a / 255) set_float(ui_shader, "uiWidth", constraints.width) set_float(ui_shader, "uiHeight", constraints.height) - set_float(ui_shader, "borderRadius", radius) + set_float(ui_shader, "borderRadius", style.border_radius) set_bool(ui_shader, "hasTexture", true) set_mat4f(ui_shader, "projection", zephr_ctx.projection) @@ -395,7 +395,7 @@ draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color: rect: Rect = --- apply_constraints(constraints, &rect.pos, &rect.size) - apply_alignment(align, constraints, rect.size, &rect.pos) + apply_alignment(style.align, constraints, rect.size, &rect.pos) // set the positions after applying alignment so children can use them constraints.x = rect.pos.x @@ -586,7 +586,11 @@ draw_icon_button :: proc(constraints: ^UiConstraints, icon_tex_id: TextureId, st set_y_constraint(&icon_constraints, 0, .RELATIVE_PIXELS) set_width_constraint(&icon_constraints, constraints.width * 0.8, .FIXED) set_height_constraint(&icon_constraints, constraints.height * 0.8, .FIXED) - draw_texture(&icon_constraints, icon_tex_id, style.fg_color, 0, .CENTER) + tex_style := UiStyle { + bg_color = style.fg_color, + align = .CENTER, + } + draw_texture(&icon_constraints, icon_tex_id, tex_style) if (clicked && state == .ACTIVE) { return true diff --git a/zephr/zephr.odin b/zephr/zephr.odin index 32b4e2f..9077b84 100644 --- a/zephr/zephr.odin +++ b/zephr/zephr.odin @@ -671,6 +671,8 @@ init :: proc(font_path: cstring, icon_path: cstring, window_title: cstring, wind deinit :: proc() { backend_shutdown() + queue.destroy(&zephr_ctx.event_queue) + delete(zephr_ctx.ui.elements) //audio_close() }