diff --git a/resources/examples/raylib/raylib_2d_camera_platformer.c3 b/resources/examples/raylib/raylib_2d_camera_platformer.c3 new file mode 100644 index 000000000..7fb5676bb --- /dev/null +++ b/resources/examples/raylib/raylib_2d_camera_platformer.c3 @@ -0,0 +1,292 @@ +/******************************************************************************************* +* +* raylib [core] example - 2d camera platformer +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 3.0 +* +* Example contributed by arvyy (@arvyy) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2019-2025 arvyy (@arvyy) +* converted to C3 by Christoffer Lerno +* +********************************************************************************************/ + +module raylib_camera_platformer; +import raylib55; + +const G = 400; +const float PLAYER_JUMP_SPD = 350; +const float PLAYER_HOR_SPD = 200; + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +struct Player +{ + RLVector2 position; + float speed; + bool can_jump; +} + +struct EnvItem +{ + RLRectangle rect; + int blocking; + RLColor color; +} + + +alias CameraUpdateFn = fn void(RLCamera2D* camera, Player* player, EnvItem[] env_items, float delta, int width, int height); + +enum CameraUpdateType : (ZString text, CameraUpdateFn func) +{ + CENTER = { "Follow player center", &update_camera_center }, + CENTER_INSIDE_MAP = { "Follow player center, but clamp to map edges", &update_camera_center_inside_map }, + CENTER_SMOOTH_FOLLOW = { "Follow player center; smoothed", &update_camera_center_smooth_follow }, + EVEN_OUT_ON_LANDING = { "Follow player center horizontally; update player center vertically after landing", &update_camera_even_out_on_landing }, + PLAYER_BOUNDS_PUSH = { "Player push camera on getting too close to screen edge", &update_camera_player_bounds_push } +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +fn int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int SCREEN_WIDTH = 800; + const int SCREEN_HEIGHT = 450; + + rl::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 2d camera platformer"); + + Player player = { + .position = { 400, 280 }, + .speed = 0, + .can_jump = false + }; + EnvItem[*] env_items = { + {{ 0, 0, 1000, 400 }, 0, rl::LIGHTGRAY }, + {{ 0, 400, 1000, 200 }, 1, rl::GRAY }, + {{ 300, 200, 400, 10 }, 1, rl::GRAY }, + {{ 250, 300, 100, 10 }, 1, rl::GRAY }, + {{ 650, 300, 100, 10 }, 1, rl::GRAY } + }; + + RLCamera2D camera = { + .target = player.position, + .offset = { SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f }, + .rotation = 0.0f, + .zoom = 1.0f + }; + + + CameraUpdateType camera_option = CENTER; + + rl::set_target_fps(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!rl::window_should_close()) + { + // Update + //---------------------------------------------------------------------------------- + float delta_time = rl::get_frame_time(); + + update_player(&player, &env_items, delta_time); + camera.zoom += ((float)rl::get_mouse_wheel_move() * 0.05f); + + switch + { + case camera.zoom > 3.0f: camera.zoom = 3.0f; + case camera.zoom < 0.25f: camera.zoom = 0.25f; + } + + if (rl::is_key_pressed(R)) + { + camera.zoom = 1.0f; + player.position = { 400, 280 }; + } + + if (rl::is_key_pressed(C)) camera_option = CameraUpdateType.from_ordinal(((int)camera_option + 1) % (int)CameraUpdateType.len); + + // Call update camera function by its pointer + camera_option.func(&camera, &player, &env_items, delta_time, SCREEN_WIDTH, SCREEN_HEIGHT); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + rl::@drawing() + { + rl::clear_background(rl::LIGHTGRAY); + rl::@mode2d(camera) + { + foreach (item : env_items) rl::draw_rectangle_rec(item.rect, item.color); + RLRectangle player_rect = { player.position.x - 20, player.position.y - 40, 40.0f, 40.0f }; + rl::draw_rectangle_rec(player_rect, rl::RED); + rl::draw_circle_v(player.position, 5.0f, rl::GOLD); + }; + rl::draw_text("Controls:", 20, 20, 10, rl::BLACK); + rl::draw_text("- Right/Left to move", 40, 40, 10, rl::DARKGRAY); + rl::draw_text("- Space to jump", 40, 60, 10, rl::DARKGRAY); + rl::draw_text("- Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, rl::DARKGRAY); + rl::draw_text("- C to change camera mode", 40, 100, 10, rl::DARKGRAY); + rl::draw_text("Current camera mode:", 20, 120, 10, rl::BLACK); + rl::draw_text(camera_option.text, 40, 140, 10, rl::DARKGRAY); + }; + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + rl::close_window(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} +fn void update_player(Player* player, EnvItem[] env_items, float delta) +{ + if (rl::is_key_down(LEFT)) player.position.x -= PLAYER_HOR_SPD * delta; + if (rl::is_key_down(RIGHT)) player.position.x += PLAYER_HOR_SPD * delta; + if (rl::is_key_down(SPACE) && player.can_jump) + { + player.speed = -PLAYER_JUMP_SPD; + player.can_jump = false; + } + + bool hit_obstacle = false; + foreach (item : env_items) + { + RLVector2* p = &player.position; + if (item.blocking + && item.rect.x <= p.x + && item.rect.x + item.rect.width >= p.x + && item.rect.y >= p.y + && item.rect.y <= p.y + player.speed * delta) + { + hit_obstacle = true; + player.speed = 0.0f; + p.y = item.rect.y; + break; + } + } + + if (hit_obstacle) + { + player.can_jump = true; + } + else + { + player.position.y += player.speed * delta; + player.speed += G * delta; + player.can_jump = false; + } +} + +fn void update_camera_center(RLCamera2D* camera, Player* player, EnvItem[] env_items, float delta, int width, int height) +{ + camera.offset = { width / 2.0f, height / 2.0f }; + camera.target = player.position; +} + +fn void update_camera_center_inside_map(RLCamera2D* camera, Player* player, EnvItem[] env_items, float delta, int width, int height) +{ + camera.target = player.position; + camera.offset = { width / 2.0f, height / 2.0f }; + float min_x = 1000; + float min_y = 1000; + float max_x = -1000; + float max_y = -1000; + + foreach (item : env_items) + { + min_x = min(item.rect.x, min_x); + max_x = max(item.rect.x + item.rect.width, max_x); + min_y = min(item.rect.y, min_y); + max_y = max(item.rect.y + item.rect.height, max_y); + } + + RLVector2 max = rl::get_world_to_screen2d({ max_x, max_y }, *camera); + RLVector2 min = rl::get_world_to_screen2d({ min_x, min_y }, *camera); + + if (max.x < width) camera.offset.x = width - (max.x - width / 2.0f); + if (max.y < height) camera.offset.y = height - (max.y - height / 2.0f); + if (min.x > 0) camera.offset.x = width / 2.0f - min.x; + if (min.y > 0) camera.offset.y = height / 2.0f - min.y; +} + +fn void update_camera_center_smooth_follow(RLCamera2D* camera, Player* player, EnvItem[] env_items, float delta, int width, int height) +{ + const float MIN_SPEED = 30; + const float MIN_EFFECT_LENGTH = 10; + const float FRACTION_SPEED = 0.8f; + + camera.offset = { width / 2.0f, height / 2.0f }; + float length = player.position.distance(camera.target); + + if (length > MIN_EFFECT_LENGTH) + { + float speed = max(FRACTION_SPEED * length, MIN_SPEED); + camera.target += (player.position - camera.target) * speed * delta / length; + } +} + +fn void update_camera_even_out_on_landing(RLCamera2D* camera, Player* player, EnvItem[] env_items, float delta, int width, int height) +{ + const float EVEN_OUT_SPEED = 700; + static bool evening_out = false; + static float even_out_target; + + camera.offset = { width / 2.0f, height / 2.0f }; + camera.target.x = player.position.x; + + if (evening_out) + { + if (even_out_target > camera.target.y) + { + camera.target.y += EVEN_OUT_SPEED * delta; + + if (camera.target.y > even_out_target) + { + camera.target.y = even_out_target; + evening_out = false; + } + } + else + { + camera.target.y -= EVEN_OUT_SPEED * delta; + + if (camera.target.y < even_out_target) + { + camera.target.y = even_out_target; + evening_out = false; + } + } + } + else + { + if (player.can_jump && player.speed == 0 && player.position.y != camera.target.y) + { + evening_out = true; + even_out_target = player.position.y; + } + } +} + +fn void update_camera_player_bounds_push(RLCamera2D* camera, Player* player, EnvItem[] env_items, float delta, int width, int height) +{ + const RLVector2 BBOX = { 0.2f, 0.2f }; + + RLVector2 bboxWorldMin = rl::get_screen_to_world2d({ (1 - BBOX.x) * 0.5f * width, (1 - BBOX.y) * 0.5f*height }, *camera); + RLVector2 bboxWorldMax = rl::get_screen_to_world2d({ (1 + BBOX.x) * 0.5f * width, (1 + BBOX.y) * 0.5f*height }, *camera); + camera.offset = { (1 - BBOX.x) * 0.5f * width, (1 - BBOX.y) * 0.5f * height }; + + if (player.position.x < bboxWorldMin.x) camera.target.x = player.position.x; + if (player.position.y < bboxWorldMin.y) camera.target.y = player.position.y; + if (player.position.x > bboxWorldMax.x) camera.target.x = bboxWorldMin.x + (player.position.x - bboxWorldMax.x); + if (player.position.y > bboxWorldMax.y) camera.target.y = bboxWorldMin.y + (player.position.y - bboxWorldMax.y); +} \ No newline at end of file diff --git a/resources/examples/raylib/raylib_delta_time.c3 b/resources/examples/raylib/raylib_delta_time.c3 new file mode 100644 index 000000000..387439fc1 --- /dev/null +++ b/resources/examples/raylib/raylib_delta_time.c3 @@ -0,0 +1,121 @@ +/******************************************************************************************* +* +* raylib [core] example - delta time +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev +* +* Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Robin (@RobinsAviary) +* converted to C3 by Christoffer Lerno +* +********************************************************************************************/ + +module raylib_delta_time; +import raylib55; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +fn int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int SCREEN_WIDTH = 800; + const int SCREEN_HEIGHT = 450; + + rl::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - delta time"); + + int current_fps = 60; + + // Store the position for the both of the circles + RLVector2 delta_circle = { 0, SCREEN_HEIGHT / 3.0f }; + RLVector2 frame_circle = { 0, SCREEN_HEIGHT * (2.0f / 3.0f) }; + + // The speed applied to both circles + const float SPEED = 10.0f; + const float CIRCLE_RADIUS = 32.0f; + + rl::set_target_fps(current_fps); + + //-------------------------------------------------------------------------------------- + // Main game loop + while (!rl::window_should_close()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // Adjust the FPS target based on the mouse wheel + float mouse_wheel = rl::get_mouse_wheel_move(); + if (mouse_wheel) + { + current_fps += (int)mouse_wheel; + if (current_fps < 0) current_fps = 0; + rl::set_target_fps(current_fps); + } + + // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time) + // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS + + // Multiply by 6.0 (an arbitrary value) in order to make the speed + // visually closer to the other circle (at 60 fps), for comparison + delta_circle.x += rl::get_frame_time() * 6 * SPEED; + // This circle can move faster or slower visually depending on the FPS + frame_circle.x += 0.1f * SPEED; + + // If either circle is off the screen, reset it back to the start + if (delta_circle.x > SCREEN_WIDTH) delta_circle.x = 0; + if (frame_circle.x > SCREEN_WIDTH) frame_circle.x = 0; + + // Reset both circles positions + if (rl::is_key_pressed(R)) + { + delta_circle.x = 0; + frame_circle.x = 0; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + rl::@drawing() + { + rl::clear_background(rl::RAYWHITE); + + // Draw both circles to the screen + rl::draw_circle_v(delta_circle, CIRCLE_RADIUS, rl::RED); + rl::draw_circle_v(frame_circle, CIRCLE_RADIUS, rl::BLUE); + + // Draw the help text + // Determine what help text to show depending on the current FPS target + ZString fps_text = null; + if (current_fps <= 0) + { + fps_text = rl::text_format("FPS: unlimited (%i)", rl::get_fps()); + } + else + { + fps_text = rl::text_format("FPS: %i (target: %i)", rl::get_fps(), current_fps); + } + rl::draw_text(fps_text, 10, 10, 20, rl::DARKGRAY); + rl::draw_text(rl::text_format("Frame time: %02.02f ms", rl::get_frame_time()), 10, 30, 20, rl::DARKGRAY); + rl::draw_text("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, rl::DARKGRAY); + + // Draw the text above the circles + rl::draw_text("FUNC: x += GetFrameTime()*speed", 10, 90, 20, rl::RED); + rl::draw_text("FUNC: x += speed", 10, 240, 20, rl::BLUE); + + }; + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + rl::close_window(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/resources/examples/raylib/raylib_input_gestures.c3 b/resources/examples/raylib/raylib_input_gestures.c3 new file mode 100644 index 000000000..0e7db1eb4 --- /dev/null +++ b/resources/examples/raylib/raylib_input_gestures.c3 @@ -0,0 +1,120 @@ + +/******************************************************************************************* +* +* raylib [core] example - input gestures +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 1.4, last time updated with raylib 4.2 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2016-2025 Ramon Santamaria (@raysan5) +* converted to C3 by Christoffer Lerno +* +********************************************************************************************/ + +module raylib_input_gestures; +import raylib55; + + +const MAX_GESTURE_STRINGS = 20; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +fn int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int SCREEN_WIDTH = 800; + const int SCREEN_HEIGHT = 450; + + rl::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - input gestures"); + + RLRectangle touch_area = { 220, 10, SCREEN_WIDTH - 230.0f, SCREEN_HEIGHT - 20.0f }; + + int gestures_count = 0; + ZString[MAX_GESTURE_STRINGS] gesture_strings; + + RLGesture current_gesture = NONE; + RLGesture last_gesture = NONE; + + //SetGesturesEnabled(0b0000000000001001); // Enable only some gestures to be detected + + rl::set_target_fps(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!rl::window_should_close()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + last_gesture = current_gesture; + current_gesture = rl::get_gesture_detected(); + RLVector2 touch_position = rl::get_touch_position(0); + + if (rl::check_collision_point_rec(touch_position, touch_area) && (current_gesture != NONE)) + { + if (current_gesture != last_gesture) + { + // Reset gestures strings + if (gestures_count >= MAX_GESTURE_STRINGS) + { + foreach (&s : gesture_strings) *s = ""; + gestures_count = 0; + } + // Store gesture string + switch (current_gesture) + { + case TAP: gesture_strings[gestures_count++] = "GESTURE TAP"; + case DOUBLETAP: gesture_strings[gestures_count++] = "GESTURE DOUBLETAP"; + case HOLD: gesture_strings[gestures_count++] = "GESTURE HOLD"; + case DRAG: gesture_strings[gestures_count++] = "GESTURE DRAG"; + case SWIPE_RIGHT: gesture_strings[gestures_count++] = "GESTURE SWIPE RIGHT"; + case SWIPE_LEFT: gesture_strings[gestures_count++] = "GESTURE SWIPE LEFT"; + case SWIPE_UP: gesture_strings[gestures_count++] = "GESTURE SWIPE UP"; + case SWIPE_DOWN: gesture_strings[gestures_count++] = "GESTURE SWIPE DOWN"; + case PINCH_IN: gesture_strings[gestures_count++] = "GESTURE PINCH IN"; + case PINCH_OUT: gesture_strings[gestures_count++] = "GESTURE PINCH OUT"; + } + + + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + rl::@drawing() + { + rl::clear_background(rl::RAYWHITE); + + rl::draw_rectangle_rec(touch_area, rl::GRAY); + rl::draw_rectangle(225, 15, SCREEN_WIDTH - 240, SCREEN_HEIGHT - 30, rl::RAYWHITE); + + rl::draw_text("GESTURES TEST AREA", SCREEN_WIDTH - 270, SCREEN_HEIGHT - 40, 20, rl::GRAY.fade(0.5f)); + + for (int i = 0; i < gestures_count; i++) + { + rl::draw_rectangle(10, 30 + 20 * i, 200, 20, rl::LIGHTGRAY.fade(i % 2 ? 0.3f : 0.5f)); + rl::draw_text(gesture_strings[i], 35, 36 + 20 * i, 10, i < gestures_count - 1 ? rl::DARKGRAY : rl::MAROON); + } + + rl::draw_rectangle_lines(10, 29, 200, SCREEN_HEIGHT - 50, rl::GRAY); + rl::draw_text("DETECTED GESTURES", 50, 15, 10, rl::GRAY); + + if (current_gesture != NONE) rl::draw_circle_v(touch_position, 30, rl::MAROON); + + }; + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + rl::close_window(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/resources/examples/raylib/raylib_models_animation_playing.c3 b/resources/examples/raylib/raylib_models_animation_playing.c3 index 25e0fbb47..db684a20f 100644 --- a/resources/examples/raylib/raylib_models_animation_playing.c3 +++ b/resources/examples/raylib/raylib_models_animation_playing.c3 @@ -1,5 +1,3 @@ -module raylib_models; -import raylib55; /******************************************************************************************* * * raylib [models] example - animation playing @@ -24,6 +22,8 @@ import raylib55; * ********************************************************************************************/ +module raylib_models; +import raylib55; //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------