From 52767eb8ae9d18f4f5d2bf588bb1126075a69dac Mon Sep 17 00:00:00 2001 From: Timothy Fiss Date: Wed, 23 Jul 2025 17:41:10 -0600 Subject: [PATCH] example: add easing_ball_anim --- build.zig | 5 + examples/shapes/easings_ball_anim.zig | 86 ++++++++++++ examples/shapes/reasings.zig | 192 ++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 examples/shapes/easings_ball_anim.zig create mode 100644 examples/shapes/reasings.zig diff --git a/build.zig b/build.zig index 4ae5782..60a1e7d 100644 --- a/build.zig +++ b/build.zig @@ -234,6 +234,11 @@ pub fn build(b: *std.Build) !void { .path = "examples/shapes/draw_ring.zig", .desc = "Dynaically renders a ring using raygui", }, + .{ + .name = "easings_ball_anim", + .path = "examples/shapes/easings_ball_anim.zig", + .desc = "Renders a ball that demonstrates various easing functions", + }, .{ .name = "following_eyes", .path = "examples/shapes/following_eyes.zig", diff --git a/examples/shapes/easings_ball_anim.zig b/examples/shapes/easings_ball_anim.zig new file mode 100644 index 0000000..33f0f3a --- /dev/null +++ b/examples/shapes/easings_ball_anim.zig @@ -0,0 +1,86 @@ +const rl = @import("raylib"); +const reasings = @import("reasings.zig"); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +pub fn main() anyerror!void { + // Initialization + //-------------------------------------------------------------------------------------- + const screenWidth = 800; + const screenHeight = 450; + + rl.initWindow(screenWidth, screenHeight, "raylib [shapes] example - easings ball anim"); + defer rl.closeWindow(); // Defer closing window and OpenGL context + + // Ball variable value to be animated with easings + var ballPositionX: i32 = -100; + var ballRadius: f32 = 20; + var ballAlpha: f32 = 0; + + var state: i32 = 0; + var framesCounter: i32 = 0; + + rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!rl.windowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + switch (state) { + 0 => { // Move ball position X with easing + framesCounter += 1; + ballPositionX = @intFromFloat(reasings.elasticOut(@floatFromInt(framesCounter), -100, @as(f32, @floatFromInt(screenWidth)) / 2 + 100, 120)); + + if (framesCounter >= 120) { + framesCounter = 0; + state = 1; + } + }, + 1 => { // Increase ball radius with easing + framesCounter += 1; + ballRadius = reasings.elasticIn(@floatFromInt(framesCounter), 20, 500, 200); + + if (framesCounter >= 200) { + framesCounter = 0; + state = 2; + } + }, + 2 => { // Change ball alpha with easing (background color blending) + framesCounter += 1; + ballAlpha = reasings.cubicOut(@floatFromInt(framesCounter), 0.0, 1.0, 200); + if (framesCounter >= 200) { + framesCounter = 0; + state = 3; + } + }, + 3 => { // Reset state to play again + if (rl.isKeyPressed(.enter)) { + ballPositionX = -100; + ballRadius = 20; + ballAlpha = 0.0; + state = 0; + } + }, + else => unreachable, + } + + if (rl.isKeyPressed(.r)) framesCounter = 0; + //---------------------------------------------------------------------------------- + // Draw + //---------------------------------------------------------------------------------- + rl.beginDrawing(); + defer rl.endDrawing(); + + rl.clearBackground(.ray_white); + + if (state >= 2) rl.drawRectangle(0, 0, screenWidth, screenHeight, .green); + rl.drawCircle(ballPositionX, 200, ballRadius, .fade(.red, 1.0 - ballAlpha)); + + if (state == 3) rl.drawText("PRESS [ENTER] TO PLAY AGAIN!", 240, 200, 20, .black); + + //---------------------------------------------------------------------------------- + } +} diff --git a/examples/shapes/reasings.zig b/examples/shapes/reasings.zig new file mode 100644 index 0000000..664075d --- /dev/null +++ b/examples/shapes/reasings.zig @@ -0,0 +1,192 @@ +const math = @import("std").math; + +// Linear Easing functions +pub fn linearNone(t: f32, b: f32, c: f32, d: f32) f32 { + return (c * t / d + b); +} +pub fn linearIn(t: f32, b: f32, c: f32, d: f32) f32 { + return (c * t / d + b); +} +pub fn linearOut(t: f32, b: f32, c: f32, d: f32) f32 { + return (c * t / d + b); +} +pub fn linearInOut(t: f32, b: f32, c: f32, d: f32) f32 { + return (c * t / d + b); +} + +pub fn sineIn(t: f32, b: f32, c: f32, d: f32) f32 { + return (-c * math.cos(t / d * (math.pi / 2.0)) + c + b); +} +pub fn sineOut(t: f32, b: f32, c: f32, d: f32) f32 { + return (c * math.sin(t / d * (math.pi / 2.0)) + b); +} +pub fn sineInOut(t: f32, b: f32, c: f32, d: f32) f32 { + return (-c / 2.0 * (math.cos(math.pi * t / d) - 1.0) + b); +} + +// Circular Easing functions +pub fn circIn(t: f32, b: f32, c: f32, d: f32) f32 { + const postFix = t / d; + return (-c * (math.sqrt(1.0 - postFix * postFix) - 1.0) + b); +} +pub fn circOut(t: f32, b: f32, c: f32, d: f32) f32 { + const postFix = t / d - 1.0; + return (c * math.sqrt(1.0 - postFix * postFix) + b); +} +pub fn circInOut(t: f32, b: f32, c: f32, d: f32) f32 { + var postFix = t / d; + if ((postFix / 2.0) < 1.0) return (-c / 2.0 * (math.sqrt(1.0 - postFix * postFix) - 1.0) + b); + postFix -= 2.0; + return (c / 2.0 * (math.sqrt(1.0 - postFix * postFix) + 1.0) + b); +} + +// Cubic Easing functions +pub fn cubicIn(t: f32, b: f32, c: f32, d: f32) f32 { + const postFix = t / d; + return (c * postFix * postFix * postFix + b); +} +pub fn cubicOut(t: f32, b: f32, c: f32, d: f32) f32 { + const postFix = t / d - 1.0; + return (c * (postFix * postFix * postFix + 1.0) + b); +} +pub fn cubicInOut(t: f32, b: f32, c: f32, d: f32) f32 { + const postFix = t / d; + if (postFix / 2.0 < 1.0) return (c / 2.0 * postFix * postFix * postFix + b); + postFix -= 2.0; + return (c / 2.0 * (t * t * t + 2.0) + b); +} + +// Quadratic Easing functions +pub fn quadIn(t: f32, b: f32, c: f32, d: f32) f32 { + const postfix = t / d; + return (c * postfix * postfix + b); +} +pub fn quadOut(t: f32, b: f32, c: f32, d: f32) f32 { + const postFix = t / d; + return (-c * postFix * (postFix - 2.0) + b); +} +pub fn quadInOut(t: f32, b: f32, c: f32, d: f32) f32 // Ease: Quadratic In Out +{ + const postFix = t / d; + return if (postFix / 2 < 1) + (((c / 2) * (postFix * postFix)) + b) + else + (-c / 2.0 * (((postFix - 1.0) * (postFix - 3.0)) - 1.0) + b); +} + +// Exponential Easing functions +pub fn expoIn(t: f32, b: f32, c: f32, d: f32) f32 { + return if (t == 0.0) b else (c * math.pow(f32, 2.0, 10.0 * (t / d - 1.0)) + b); +} +pub fn expoOut(t: f32, b: f32, c: f32, d: f32) f32 { + return if (t == d) (b + c) else (c * (-math.pow(f32, 2.0, -10.0 * t / d) + 1.0) + b); +} +pub fn expoInOut(t: f32, b: f32, c: f32, d: f32) f32 { + if (t == 0.0) return b; + if (t == d) return (b + c); + const postFix = t / d; + if ((postFix / 2.0) < 1.0) return (c / 2.0 * math.pow(f32, 2.0, 10.0 * (postFix - 1.0)) + b); + + return (c / 2.0 * (-math.pow(f32, 2.0, -10.0 * (postFix - 1.0)) + 2.0) + b); +} + +// Back Easing functions +pub fn backIn(t: f32, b: f32, c: f32, d: f32) f32 { + const s = 1.70158; + const postFix = t / d; + return (c * (postFix) * postFix * ((s + 1.0) * postFix - s) + b); +} + +pub fn backOut(t: f32, b: f32, c: f32, d: f32) f32 { + const s = 1.70158; + const postFix = t / d - 1.0; + return (c * (postFix * postFix * ((s + 1.0) * postFix + s) + 1.0) + b); +} + +pub fn backInOut(t: f32, b: f32, c: f32, d: f32) f32 { + const s = 1.70158; + t /= d; + if (t / 2.0 < 1.0) { + s *= 1.525; + return (c / 2.0 * (t * t * ((s + 1.0) * t - s)) + b); + } + t -= 2; + const postFix = t; + s *= 1.525; + return (c / 2.0 * ((postFix) * t * ((s + 1.0) * t + s) + 2.0) + b); +} + +// Bounce Easing functions + +pub fn bounceOut(t: f32, b: f32, c: f32, d: f32) f32 { + var tDivD = t / d; + if (tDivD < (1.0 / 2.75)) { + return (c * (7.5625 * tDivD * tDivD) + b); + } else if (tDivD < (2.0 / 2.75)) { + tDivD -= (1.5 / 2.75); + return (c * (7.5625 * tDivD * tDivD + 0.75) + b); + } else if (tDivD < (2.5 / 2.75)) { + tDivD -= (2.25 / 2.75); + return (c * (7.5625 * tDivD * tDivD + 0.9375) + b); + } else { + tDivD -= (2.625 / 2.75); + return (c * (7.5625 * tDivD * tDivD + 0.984375) + b); + } +} + +pub fn bounceIn(t: f32, b: f32, c: f32, d: f32) f32 { + return (c - bounceOut(d - t, 0.0, c, d) + b); +} + +pub fn bounceInOut(t: f32, b: f32, c: f32, d: f32) f32 { + if (t < d / 2.0) return (bounceIn(t * 2.0, 0.0, c, d) * 0.5 + b); + return (bounceOut(t * 2.0 - d, 0.0, c, d) * 0.5 + c * 0.5 + b); +} + +// Elastic Easing functions +pub fn elasticIn(t: f32, b: f32, c: f32, d: f32) f32 { + if (t == 0.0) return b; + var tDivD = t / d; + if (tDivD == 1.0) return (b + c); + + const p = d * 0.3; + const a = c; + const s = p / 4.0; + tDivD -= 1; + const postFix = a * math.pow(f32, 2.0, 10.0 * tDivD); + + return (-(postFix * math.sin((tDivD * d - s) * (2.0 * math.pi) / p)) + b); +} + +pub fn elasticOut(t: f32, b: f32, c: f32, d: f32) f32 { + if (t == 0.0) return b; + const tDivD = t / d; + if (tDivD == 1.0) return (b + c); + + const p = d * 0.3; + const a = c; + const s = p / 4.0; + + return (a * math.pow(f32, 2.0, -10.0 * tDivD) * math.sin((tDivD * d - s) * (2.0 * math.pi) / p) + c + b); +} + +pub fn elasticInOut(t: f32, b: f32, c: f32, d: f32) f32 { + if (t == 0.0) return b; + const tDivD = t / d; + if (tDivD / 2.0 == 2.0) return (b + c); + + const p = d * (0.3 * 1.5); + const a = c; + const s = p / 4.0; + + if (tDivD < 1.0) { + tDivD -= 1; + const postFix = a * math.pow(f32, 2.0, 10.0 * tDivD); + return -0.5 * (postFix * math.sin((tDivD * d - s) * (2.0 * math.pi) / p)) + b; + } + + tDivD -= 1; + const postFix = a * math.pow(f32, 2.0, -10.0 * (tDivD)); + + return (postFix * math.sin((tDivD * d - s) * (2.0 * math.pi) / p) * 0.5 + c + b); +}