From 676221c3a4aca75cc39ac8196a0e1e5524587613 Mon Sep 17 00:00:00 2001 From: jdeokkim Date: Mon, 27 May 2024 16:50:55 +0900 Subject: [PATCH] Rewrite examples/src/melon.c --- examples/Makefile | 1 + examples/Makefile.emcc | 1 + examples/Makefile.mingw | 1 + examples/src/melon.c | 308 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 examples/src/melon.c diff --git a/examples/Makefile b/examples/Makefile index 43297f1..8d0475c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -42,6 +42,7 @@ SOURCE_PATH = src TARGETS ?= \ ${SOURCE_PATH}/basic.out \ ${SOURCE_PATH}/cows.out \ + ${SOURCE_PATH}/melon.out \ ${SOURCE_PATH}/query.out \ ${SOURCE_PATH}/raycast.out \ ${SOURCE_PATH}/raylib.out diff --git a/examples/Makefile.emcc b/examples/Makefile.emcc index 4be2ed2..c1c5d89 100644 --- a/examples/Makefile.emcc +++ b/examples/Makefile.emcc @@ -34,6 +34,7 @@ SOURCE_PATH = src TARGETS = \ ${SOURCE_PATH}/basic.html \ ${SOURCE_PATH}/cows.html \ + ${SOURCE_PATH}/melon.html \ ${SOURCE_PATH}/query.html \ ${SOURCE_PATH}/raycast.html \ ${SOURCE_PATH}/raylib.html diff --git a/examples/Makefile.mingw b/examples/Makefile.mingw index 3e0e95e..cecb8f1 100644 --- a/examples/Makefile.mingw +++ b/examples/Makefile.mingw @@ -33,6 +33,7 @@ SOURCE_PATH = src TARGETS = \ ${SOURCE_PATH}/basic.exe \ ${SOURCE_PATH}/cows.exe \ + ${SOURCE_PATH}/melon.exe \ ${SOURCE_PATH}/query.exe \ ${SOURCE_PATH}/raycast.exe \ ${SOURCE_PATH}/raylib.exe diff --git a/examples/src/melon.c b/examples/src/melon.c new file mode 100644 index 0000000..069131a --- /dev/null +++ b/examples/src/melon.c @@ -0,0 +1,308 @@ +/* + Copyright (c) 2021-2023 Jaedeok Kim + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* Includes ================================================================ */ + +#include "ferox.h" +#include "raylib.h" + +#define FEROX_RAYLIB_IMPLEMENTATION +#include "ferox-raylib.h" + +#ifdef PLATFORM_WEB + #include +#endif + +/* Macros ================================================================== */ + +// clang-format off + +#define TARGET_FPS 60 + +#define SCREEN_WIDTH 600 +#define SCREEN_HEIGHT 800 + +#define CURSOR_COOLDOWN 0.65f + +#define MAX_WALL_COUNT 4 +#define MELON_KIND_COUNT 4 + +// clang-format on + +/* Typedefs ================================================================ */ + +typedef struct _MelonKind { + int index; + Color color; +} MelonKind; + +/* Constants =============================================================== */ + +static const Rectangle SCREEN_BOUNDS = { .width = SCREEN_WIDTH, + .height = SCREEN_HEIGHT }; + +static const MelonKind MELON_KINDS[MELON_KIND_COUNT] = { + { .index = 0, .color = RED }, + { .index = 1, .color = ORANGE }, + { .index = 2, .color = YELLOW }, + { .index = 3, .color = GREEN } +}; + +static const float CELL_SIZE = 2.0f, DELTA_TIME = 1.0f / TARGET_FPS; + +/* Private Variables ======================================================= */ + +static frWorld *world; + +static frShape *melonShapes[MELON_KIND_COUNT]; + +static frBody *cursor, *walls[MAX_WALL_COUNT]; + +static float cursorCounter = CURSOR_COOLDOWN; + +/* Private Function Prototypes ============================================= */ + +static void InitExample(void); +static void UpdateExample(void); +static void DeinitExample(void); + +static void OnPostStep(frBodyPair key, frCollision *value); + +/* Public Functions ======================================================== */ + +int main(void) { + SetConfigFlags(FLAG_MSAA_4X_HINT); + + InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "c-krit/ferox | melon.c"); + + InitExample(); + +#ifdef PLATFORM_WEB + emscripten_set_main_loop(UpdateExample, 0, 1); +#else + SetTargetFPS(TARGET_FPS); + + while (!WindowShouldClose()) + UpdateExample(); +#endif + + DeinitExample(); + + CloseWindow(); + + return 0; +} + +/* Private Functions ======================================================= */ + +static void InitExample(void) { + world = frCreateWorld(frVector2ScalarMultiply(FR_WORLD_DEFAULT_GRAVITY, + 1.5f), + CELL_SIZE); + + frSetWorldCollisionHandler(world, + (frCollisionHandler) { + .postStep = OnPostStep, + }); + + walls[0] = frCreateBodyFromShape( + FR_BODY_STATIC, + frVector2PixelsToUnits((frVector2) { .x = 0.5f * SCREEN_WIDTH, + .y = 1.05f * SCREEN_HEIGHT }), + frCreateRectangle((frMaterial) { .density = 1.25f, + .friction = 0.5f, + .restitution = 0.05f }, + frPixelsToUnits(1.0f * SCREEN_WIDTH), + frPixelsToUnits(0.1f * SCREEN_HEIGHT))); + + walls[1] = frCreateBodyFromShape( + FR_BODY_STATIC, + frVector2PixelsToUnits((frVector2) { .x = 1.05f * SCREEN_WIDTH, + .y = 0.5f * SCREEN_HEIGHT }), + frCreateRectangle((frMaterial) { .density = 1.25f, + .friction = 0.5f, + .restitution = 0.05f }, + frPixelsToUnits(0.1f * SCREEN_WIDTH), + frPixelsToUnits(1.0f * SCREEN_HEIGHT))); + + walls[2] = frCreateBodyFromShape( + FR_BODY_STATIC, + frVector2PixelsToUnits((frVector2) { .x = 0.5f * SCREEN_WIDTH, + .y = -0.05f * SCREEN_HEIGHT }), + frCreateRectangle((frMaterial) { .density = 1.25f, + .friction = 0.5f, + .restitution = 0.05f }, + frPixelsToUnits(1.0f * SCREEN_WIDTH), + frPixelsToUnits(0.1f * SCREEN_HEIGHT))); + + walls[3] = frCreateBodyFromShape( + FR_BODY_STATIC, + frVector2PixelsToUnits((frVector2) { .x = -0.05f * SCREEN_WIDTH, + .y = 0.5f * SCREEN_HEIGHT }), + frCreateRectangle((frMaterial) { .density = 1.25f, + .friction = 0.5f, + .restitution = 0.05f }, + frPixelsToUnits(0.1f * SCREEN_WIDTH), + frPixelsToUnits(1.0f * SCREEN_HEIGHT))); + + for (int i = 0; i < MAX_WALL_COUNT; i++) + frAddBodyToWorld(world, walls[i]); + + for (int i = 0; i < MELON_KIND_COUNT; i++) + melonShapes[i] = frCreateCircle((frMaterial) { .density = 0.35f + / (i + 1), + .friction = 0.35f, + .restitution = 0.05f }, + 0.18f * (i + 3)); + + cursor = frCreateBodyFromShape(FR_BODY_KINEMATIC, + frVector2PixelsToUnits((frVector2) { + .x = 0.5f * SCREEN_WIDTH, + .y = 0.1f * SCREEN_HEIGHT }), + melonShapes[0]); + + frSetBodyUserData(cursor, (void *) &MELON_KINDS[0]); +} + +static void UpdateExample(void) { + const MelonKind *cursorKind = frGetBodyUserData(cursor); + + { + frVector2 cursorPosition = frGetBodyPosition(cursor); + + cursorPosition.x = frPixelsToUnits((GetMousePosition()).x); + + float cursorRadius = 0.18f * (cursorKind->index + 3); + + if (cursorPosition.x < cursorRadius) cursorPosition.x = cursorRadius; + + if (cursorPosition.x > (frPixelsToUnits(SCREEN_WIDTH) - cursorRadius)) + cursorPosition.x = (frPixelsToUnits(SCREEN_WIDTH) - cursorRadius); + + frSetBodyPosition(cursor, cursorPosition); + + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) + && cursorCounter >= CURSOR_COOLDOWN) { + frBody *melon = + frCreateBodyFromShape(FR_BODY_DYNAMIC, + cursorPosition, + melonShapes[cursorKind->index]); + + frSetBodyAngle(melon, DEG2RAD * GetRandomValue(0, 360)); + frSetBodyUserData(melon, (void *) cursorKind); + + frAddBodyToWorld(world, melon); + + const int cursorIndex = GetRandomValue(0, MELON_KIND_COUNT - 1); + + frSetBodyShape(cursor, melonShapes[cursorIndex]); + frSetBodyUserData(cursor, (void *) &MELON_KINDS[cursorIndex]); + + cursorCounter = 0.0f; + } + + cursorCounter += GetFrameTime(); + } + + frUpdateWorld(world, DELTA_TIME); + + { + BeginDrawing(); + + ClearBackground(FR_DRAW_COLOR_MATTEBLACK); + + frDrawGrid(SCREEN_BOUNDS, + CELL_SIZE, + 0.25f, + ColorAlpha(DARKGRAY, 0.75f)); + + frDrawBodyLines(cursor, 2.0f, ColorAlpha(cursorKind->color, 0.5f)); + + const int bodyCount = frGetBodyCountForWorld(world); + + for (int i = 0; i < bodyCount; i++) { + frBody *body = frGetBodyFromWorld(world, i); + + const MelonKind *melonKind = frGetBodyUserData(body); + + if (melonKind != NULL) + frDrawBodyLines(body, 2.0f, melonKind->color); + else + frDrawBodyLines(body, 1.0f, DARKGRAY); + } + + const Font font = GetFontDefault(); + + DrawTextEx(GetFontDefault(), + TextFormat("%d/%d bodies", + frGetBodyCountForWorld(world), + FR_WORLD_MAX_OBJECT_COUNT), + (Vector2) { .x = 8.0f, .y = 32.0f }, + font.baseSize, + 2.0f, + WHITE); + + DrawFPS(8, 8); + + EndDrawing(); + } +} + +static void DeinitExample(void) { + for (int i = 0; i < MELON_KIND_COUNT; i++) + frReleaseShape(melonShapes[i]); + + frReleaseBody(cursor); + + frReleaseWorld(world); +} + +static void OnPostStep(frBodyPair key, frCollision *value) { + const MelonKind *bodyData1 = frGetBodyUserData(key.first); + const MelonKind *bodyData2 = frGetBodyUserData(key.second); + + if (key.first == cursor || key.second == cursor || bodyData1 == NULL + || bodyData2 == NULL || bodyData1->index != bodyData2->index + || bodyData1->index >= MELON_KIND_COUNT - 1) + return; + + frVector2 bodyPosition1 = frGetBodyPosition(key.first); + frVector2 bodyPosition2 = frGetBodyPosition(key.second); + + frVector2 newPosition = (bodyPosition1.y < bodyPosition2.y) ? bodyPosition1 + : bodyPosition2; + + int newIndex = bodyData1->index + 1; + + frRemoveBodyFromWorld(world, key.first); + frRemoveBodyFromWorld(world, key.second); + + frBody *newMelon = frCreateBodyFromShape(FR_BODY_DYNAMIC, + newPosition, + melonShapes[newIndex]); + + frSetBodyUserData(newMelon, (void *) &MELON_KINDS[newIndex]); + + frAddBodyToWorld(world, newMelon); + + value->count = 0; +} \ No newline at end of file