From 305efcf5ad715305ccb33b0e1d2d9baf4a34976a Mon Sep 17 00:00:00 2001 From: victorfisac Date: Sat, 5 Mar 2016 17:05:02 +0100 Subject: [PATCH] Redesigned physics module (IN PROGRESS) physac modules is being redesigned. Physics base behaviour is done and it is composed by three steps: apply physics, resolve collisions and fix overlapping. A basic example is currently in progress. The next steps are try to add torque and unoriented physic collisions and implement physics basic functions to add forces. Rigidbody grounding state is automatically calculated and has a perfect result. Rigidbodies interacts well with each others. To achieve physics accuracy, UpdatePhysics() is called a number of times per frame. In a future, it should be changed to another thread and call it without any target frame restriction. Basic physics example has been redone (not finished) using the new module functions. Forces examples will be redone so I removed it from branch. --- examples/physics_basic_rigidbody.c | 131 +++---- examples/physics_rigidbody_force.c | 135 ------- examples/physics_rigidbody_force.png | Bin 18510 -> 0 bytes src/physac.c | 532 ++++++++++++++------------- src/physac.h | 58 +-- src/raylib.h | 49 ++- 6 files changed, 392 insertions(+), 513 deletions(-) delete mode 100644 examples/physics_rigidbody_force.c delete mode 100644 examples/physics_rigidbody_force.png diff --git a/examples/physics_basic_rigidbody.c b/examples/physics_basic_rigidbody.c index 6c354eb73..c604dd14b 100644 --- a/examples/physics_basic_rigidbody.c +++ b/examples/physics_basic_rigidbody.c @@ -1,8 +1,8 @@ /******************************************************************************************* * -* raylib [physac] physics example - Basic rigidbody +* raylib [physac] example - Basic rigidbody * -* This example has been created using raylib 1.4 (www.raylib.com) +* This example has been created using raylib 1.5 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * * Copyright (c) 2016 Victor Fisac and Ramon Santamaria (@raysan5) @@ -11,8 +11,8 @@ #include "raylib.h" -#define OBJECT_SIZE 50 -#define PLAYER_INDEX 0 +#define MOVE_VELOCITY 5 +#define JUMP_VELOCITY 35 int main() { @@ -20,28 +20,45 @@ int main() //-------------------------------------------------------------------------------------- int screenWidth = 800; int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [physics] example - basic rigidbody"); - InitPhysics(3); // Initialize physics system with maximum physic objects + InitWindow(screenWidth, screenHeight, "raylib [physac] example - basic rigidbody"); + InitPhysics(); // Initialize physics module - // Object initialization - Transform player = (Transform){(Vector2){(screenWidth - OBJECT_SIZE) / 2, (screenHeight - OBJECT_SIZE) / 2}, 0.0f, (Vector2){OBJECT_SIZE, OBJECT_SIZE}}; - AddCollider(PLAYER_INDEX, (Collider){true, COLLIDER_RECTANGLE, (Rectangle){player.position.x, player.position.y, player.scale.x, player.scale.y}, 0}); - AddRigidbody(PLAYER_INDEX, (Rigidbody){true, 1.0f, (Vector2){0, 0}, (Vector2){0, 0}, false, false, true, 0.5f, 1.0f}); + SetTargetFPS(60); - // Floor initialization - // NOTE: floor doesn't need a rigidbody because it's a static physic object, just a collider to collide with other dynamic colliders (with rigidbody) - Transform floor = (Transform){(Vector2){0, screenHeight * 0.8f}, 0.0f, (Vector2){screenWidth, screenHeight * 0.2f}}; - AddCollider(PLAYER_INDEX + 1, (Collider){true, COLLIDER_RECTANGLE, (Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, 0}); + // Debug variables + bool isDebug = false; - // Object properties initialization - float moveSpeed = 6.0f; - float jumpForce = 5.0f; + // Player physic object + PhysicObject *player = CreatePhysicObject((Vector2){ screenWidth*0.25f, screenHeight/2 }, 0.0f, (Vector2){ 50, 50 }); + player->rigidbody.enabled = true; // Enable physic object rigidbody behaviour + player->rigidbody.applyGravity = true; + player->rigidbody.friction = 0.3f; + player->collider.enabled = true; // Enable physic object collisions detection - bool physicsDebug = false; + // Player physic object + PhysicObject *player2 = CreatePhysicObject((Vector2){ screenWidth*0.75f, screenHeight/2 }, 0.0f, (Vector2){ 50, 50 }); + player2->rigidbody.enabled = true; + player2->rigidbody.applyGravity = true; + player2->rigidbody.friction = 0.1f; + player2->collider.enabled = true; + + // Floor physic object + PhysicObject *floor = CreatePhysicObject((Vector2){ screenWidth/2, screenHeight*0.95f }, 0.0f, (Vector2){ screenWidth*0.9f, 100 }); + floor->collider.enabled = true; // Enable just physic object collisions detection + + // Left wall physic object + PhysicObject *leftWall = CreatePhysicObject((Vector2){ 0.0f, screenHeight/2 }, 0.0f, (Vector2){ screenWidth*0.1f, screenHeight }); + leftWall->collider.enabled = true; + + // Right wall physic object + PhysicObject *rightWall = CreatePhysicObject((Vector2){ screenWidth, screenHeight/2 }, 0.0f, (Vector2){ screenWidth*0.1f, screenHeight }); + rightWall->collider.enabled = true; + + // Platform physic objectdd + PhysicObject *platform = CreatePhysicObject((Vector2){ screenWidth/2, screenHeight*0.7f }, 0.0f, (Vector2){ screenWidth*0.25f, 20 }); + platform->collider.enabled = true; - SetTargetFPS(60); //-------------------------------------------------------------------------------------- // Main game loop @@ -49,35 +66,22 @@ int main() { // Update //---------------------------------------------------------------------------------- + UpdatePhysics(); // Update all created physic objects - // Update object physics - // NOTE: all physics detections and reactions are calculated in ApplyPhysics() function (You will live happier :D) - ApplyPhysics(PLAYER_INDEX, &player.position); + // Check debug switch input + if (IsKeyPressed('P')) isDebug = !isDebug; - // Check jump button input - if (IsKeyDown(KEY_SPACE) && GetRigidbody(PLAYER_INDEX).isGrounded) - { - // Reset object Y velocity to avoid double jumping cases but keep the same X velocity that it already has - SetRigidbodyVelocity(PLAYER_INDEX, (Vector2){GetRigidbody(PLAYER_INDEX).velocity.x, 0}); - - // Add jumping force in Y axis - AddRigidbodyForce(PLAYER_INDEX, (Vector2){0, jumpForce}); - } + // Check player movement inputs + if (IsKeyDown('W') && player->rigidbody.isGrounded) player->rigidbody.velocity.y = JUMP_VELOCITY; + + if (IsKeyDown('A')) player->rigidbody.velocity.x = -MOVE_VELOCITY; + else if (IsKeyDown('D')) player->rigidbody.velocity.x = MOVE_VELOCITY; - // Check movement buttons input - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) - { - // Set rigidbody velocity in X based on moveSpeed value and apply the same Y velocity that it already has - SetRigidbodyVelocity(PLAYER_INDEX, (Vector2){moveSpeed, GetRigidbody(PLAYER_INDEX).velocity.y}); - } - else if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) - { - // Set rigidbody velocity in X based on moveSpeed negative value and apply the same Y velocity that it already has - SetRigidbodyVelocity(PLAYER_INDEX, (Vector2){-moveSpeed, GetRigidbody(PLAYER_INDEX).velocity.y}); - } + // Check player 2 movement inputs + if (IsKeyDown(KEY_UP) && player2->rigidbody.isGrounded) player2->rigidbody.velocity.y = JUMP_VELOCITY; - // Check debug mode toggle button input - if (IsKeyPressed(KEY_P)) physicsDebug = !physicsDebug; + if (IsKeyDown(KEY_LEFT)) player2->rigidbody.velocity.x = -MOVE_VELOCITY; + else if (IsKeyDown(KEY_RIGHT)) player2->rigidbody.velocity.x = MOVE_VELOCITY; //---------------------------------------------------------------------------------- // Draw @@ -86,28 +90,28 @@ int main() ClearBackground(RAYWHITE); - // Draw information - DrawText("Use LEFT / RIGHT to MOVE and SPACE to JUMP", (screenWidth - MeasureText("Use LEFT / RIGHT to MOVE and SPACE to JUMP", 20)) / 2, screenHeight * 0.20f, 20, LIGHTGRAY); - DrawText("Use P to switch DEBUG MODE", (screenWidth - MeasureText("Use P to switch DEBUG MODE", 20)) / 2, screenHeight * 0.3f, 20, LIGHTGRAY); - - // Check if debug mode is enabled - if (physicsDebug) + if (isDebug) { - // Draw every internal physics stored collider if it is active - for (int i = 0; i < 2; i++) - { - if (GetCollider(i).enabled) - { - DrawRectangleLines(GetCollider(i).bounds.x, GetCollider(i).bounds.y, GetCollider(i).bounds.width, GetCollider(i).bounds.height, GREEN); - } - } + DrawRectangleLines(floor->collider.bounds.x, floor->collider.bounds.y, floor->collider.bounds.width, floor->collider.bounds.height, GREEN); + DrawRectangleLines(leftWall->collider.bounds.x, leftWall->collider.bounds.y, leftWall->collider.bounds.width, leftWall->collider.bounds.height, GREEN); + DrawRectangleLines(rightWall->collider.bounds.x, rightWall->collider.bounds.y, rightWall->collider.bounds.width, rightWall->collider.bounds.height, GREEN); + DrawRectangleLines(platform->collider.bounds.x, platform->collider.bounds.y, platform->collider.bounds.width, platform->collider.bounds.height, GREEN); + DrawRectangleLines(player->collider.bounds.x, player->collider.bounds.y, player->collider.bounds.width, player->collider.bounds.height, GREEN); + DrawRectangleLines(player2->collider.bounds.x, player2->collider.bounds.y, player2->collider.bounds.width, player2->collider.bounds.height, GREEN); } else { - // Draw player and floor - DrawRectangleRec((Rectangle){player.position.x, player.position.y, player.scale.x, player.scale.y}, GRAY); - DrawRectangleRec((Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, BLACK); + // Convert transform values to rectangle data type variable + DrawRectangleRec(TransformToRectangle(floor->transform), DARKGRAY); + DrawRectangleRec(TransformToRectangle(leftWall->transform), DARKGRAY); + DrawRectangleRec(TransformToRectangle(rightWall->transform), DARKGRAY); + DrawRectangleRec(TransformToRectangle(platform->transform), DARKGRAY); + DrawRectangleRec(TransformToRectangle(player->transform), RED); + DrawRectangleRec(TransformToRectangle(player2->transform), BLUE); } + + // Draw all physic object information in specific screen position and font size + // DrawPhysicObjectInfo(player, (Vector2){ 10.0f, 10.0f }, 10); EndDrawing(); //---------------------------------------------------------------------------------- @@ -115,8 +119,7 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - UnloadPhysics(); // Unload physic objects - + ClosePhysics(); // Unitialize physics module CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- diff --git a/examples/physics_rigidbody_force.c b/examples/physics_rigidbody_force.c deleted file mode 100644 index 74a88a970..000000000 --- a/examples/physics_rigidbody_force.c +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************************* -* -* raylib [physac] physics example - Rigidbody forces -* -* This example has been created using raylib 1.4 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2016 Victor Fisac and Ramon Santamaria (@raysan5) -* -********************************************************************************************/ - -#include "raylib.h" - -#define MAX_OBJECTS 5 -#define OBJECTS_OFFSET 150 - -#define FORCE_INTENSITY 250.0f // Customize by user -#define FORCE_RADIUS 100 // Customize by user - -int main() -{ - // Initialization - //-------------------------------------------------------------------------------------- - int screenWidth = 800; - int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [physics] example - rigidbodies forces"); - - InitPhysics(MAX_OBJECTS + 1); // Initialize physics system with maximum physic objects - - // Physic Objects initialization - Transform objects[MAX_OBJECTS]; - - for (int i = 0; i < MAX_OBJECTS; i++) - { - objects[i] = (Transform){(Vector2){75 + OBJECTS_OFFSET * i, (screenHeight - 50) / 2}, 0.0f, (Vector2){50, 50}}; - AddCollider(i, (Collider){true, COLLIDER_RECTANGLE, (Rectangle){objects[i].position.x, objects[i].position.y, objects[i].scale.x, objects[i].scale.y}, 0}); - AddRigidbody(i, (Rigidbody){true, 1.0f, (Vector2){0, 0}, (Vector2){0, 0}, false, false, true, 0.5f, 0.5f}); - } - - // Floor initialization - // NOTE: floor doesn't need a rigidbody because it's a static physic object, just a collider to collide with other dynamic colliders (with rigidbody) - Transform floor = (Transform){(Vector2){0, screenHeight * 0.8f}, 0.0f, (Vector2){screenWidth, screenHeight * 0.2f}}; - AddCollider(MAX_OBJECTS, (Collider){true, COLLIDER_RECTANGLE, (Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, 0}); - - bool physicsDebug = false; - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - - // Update object physics - // NOTE: all physics detections and reactions are calculated in ApplyPhysics() function (You will live happier :D) - for (int i = 0; i < MAX_OBJECTS; i++) - { - ApplyPhysics(i, &objects[i].position); - } - - // Check foce button input - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) - { - AddForceAtPosition(GetMousePosition(), FORCE_INTENSITY, FORCE_RADIUS); - } - - // Check debug mode toggle button input - if (IsKeyPressed(KEY_P)) physicsDebug = !physicsDebug; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - // Check if debug mode is enabled - if (physicsDebug) - { - // Draw every internal physics stored collider if it is active (floor included) - for (int i = 0; i < MAX_OBJECTS; i++) - { - if (GetCollider(i).enabled) - { - // Draw collider bounds - DrawRectangleLines(GetCollider(i).bounds.x, GetCollider(i).bounds.y, GetCollider(i).bounds.width, GetCollider(i).bounds.height, GREEN); - - // Check if current collider is not floor - if (i < MAX_OBJECTS) - { - // Draw lines between mouse position and objects if they are in force range - if (CheckCollisionPointCircle(GetMousePosition(), (Vector2){GetCollider(i).bounds.x + GetCollider(i).bounds.width / 2, GetCollider(i).bounds.y + GetCollider(i).bounds.height / 2}, FORCE_RADIUS)) - { - DrawLineV(GetMousePosition(), (Vector2){GetCollider(i).bounds.x + GetCollider(i).bounds.width / 2, GetCollider(i).bounds.y + GetCollider(i).bounds.height / 2}, RED); - } - } - } - } - - // Draw radius circle - DrawCircleLines(GetMousePosition().x, GetMousePosition().y, FORCE_RADIUS, RED); - } - else - { - // Draw objects - for (int i = 0; i < MAX_OBJECTS; i++) - { - DrawRectangleRec((Rectangle){objects[i].position.x, objects[i].position.y, objects[i].scale.x, objects[i].scale.y}, GRAY); - } - - // Draw floor - DrawRectangleRec((Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, BLACK); - } - - - // Draw help messages - DrawText("Use LEFT MOUSE BUTTON to create a force in mouse position", (screenWidth - MeasureText("Use LEFT MOUSE BUTTON to create a force in mouse position", 20)) / 2, screenHeight * 0.20f, 20, LIGHTGRAY); - DrawText("Use P to switch DEBUG MODE", (screenWidth - MeasureText("Use P to switch DEBUG MODE", 20)) / 2, screenHeight * 0.3f, 20, LIGHTGRAY); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadPhysics(); // Unload physic objects - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/examples/physics_rigidbody_force.png b/examples/physics_rigidbody_force.png deleted file mode 100644 index 48afa91b05fe843aea605d30e3af39996dddca1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18510 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 diff --git a/src/physac.c b/src/physac.c index 4c50dd41b..13247117c 100644 --- a/src/physac.c +++ b/src/physac.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* [physac] raylib physics engine module - Basic functions to apply physics to 2D objects +* [physac] raylib physics module - Basic functions to apply physics to 2D objects * * Copyright (c) 2015 Victor Fisac and Ramon Santamaria * @@ -29,329 +29,343 @@ #include "raylib.h" #endif -#include -#include // Required for: malloc(), free() +#include // Declares malloc() and free() for memory management +#include // abs() and fminf() //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define DECIMAL_FIX 0.26f // Decimal margin for collision checks (avoid rigidbodies shake) +#define MAX_PHYSIC_OBJECTS 256 +#define PHYSICS_GRAVITY -9.81f/2 +#define PHYSICS_STEPS 450 +#define PHYSICS_ACCURACY 0.0001f // Velocity subtract operations round filter (friction) +#define PHYSICS_ERRORPERCENT 0.001f // Collision resolve position fix //---------------------------------------------------------------------------------- // Types and Structures Definition +// NOTE: Below types are required for PHYSAC_STANDALONE usage //---------------------------------------------------------------------------------- // ... //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static Collider *colliders; // Colliders array, dynamically allocated at runtime -static Rigidbody *rigidbodies; // Rigitbody array, dynamically allocated at runtime -static bool collisionChecker; - -static int maxElements; // Max physic elements to compute -static bool enabled; // Physics enabled? (true by default) -static Vector2 gravity; // Gravity value used for physic calculations +static PhysicObject *physicObjects[MAX_PHYSIC_OBJECTS]; // Physic objects pool +static int physicObjectsCount; // Counts current enabled physic objects //---------------------------------------------------------------------------------- -// Module specific Functions Declarations +// Module specific Functions Declaration //---------------------------------------------------------------------------------- -static float Vector2Length(Vector2 vector); -static float Vector2Distance(Vector2 a, Vector2 b); -static void Vector2Normalize(Vector2 *vector); +static float Vector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two Vector2 //---------------------------------------------------------------------------------- -// Module Functions Definitions +// Module Functions Definition //---------------------------------------------------------------------------------- -void InitPhysics(int maxPhysicElements) -{ - maxElements = maxPhysicElements; - - colliders = (Collider *)malloc(maxElements*sizeof(Collider)); - rigidbodies = (Rigidbody *)malloc(maxElements*sizeof(Rigidbody)); - - for (int i = 0; i < maxElements; i++) - { - colliders[i].enabled = false; - colliders[i].bounds = (Rectangle){ 0, 0, 0, 0 }; - colliders[i].radius = 0; - - rigidbodies[i].enabled = false; - rigidbodies[i].mass = 0.0f; - rigidbodies[i].velocity = (Vector2){ 0.0f, 0.0f }; - rigidbodies[i].acceleration = (Vector2){ 0.0f, 0.0f }; - rigidbodies[i].isGrounded = false; - rigidbodies[i].isContact = false; - rigidbodies[i].friction = 0.0f; - } - - collisionChecker = false; - enabled = true; - - // NOTE: To get better results, gravity needs to be 1:10 from original parameter - gravity = (Vector2){ 0.0f, -9.81f/10.0f }; // By default, standard gravity -} - -void UnloadPhysics() -{ - free(colliders); - free(rigidbodies); -} - -void AddCollider(int index, Collider collider) -{ - colliders[index] = collider; -} -void AddRigidbody(int index, Rigidbody rigidbody) +// Initializes pointers array (just pointers, fixed size) +void InitPhysics() { - rigidbodies[index] = rigidbody; + // Initialize physics variables + physicObjectsCount = 0; } -void ApplyPhysics(int index, Vector2 *position) +// Update physic objects, calculating physic behaviours and collisions detection +void UpdatePhysics() { - if (rigidbodies[index].enabled) + // Reset all physic objects is grounded state + for(int i = 0; i < physicObjectsCount; i++) { - // Apply friction to acceleration - if (rigidbodies[index].acceleration.x > DECIMAL_FIX) - { - rigidbodies[index].acceleration.x -= rigidbodies[index].friction; - } - else if (rigidbodies[index].acceleration.x < -DECIMAL_FIX) - { - rigidbodies[index].acceleration.x += rigidbodies[index].friction; - } - else - { - rigidbodies[index].acceleration.x = 0; - } - - if (rigidbodies[index].acceleration.y > DECIMAL_FIX / 2) - { - rigidbodies[index].acceleration.y -= rigidbodies[index].friction; - } - else if (rigidbodies[index].acceleration.y < -DECIMAL_FIX / 2) - { - rigidbodies[index].acceleration.y += rigidbodies[index].friction; - } - else - { - rigidbodies[index].acceleration.y = 0; - } - - // Apply friction to velocity - if (rigidbodies[index].isGrounded) - { - if (rigidbodies[index].velocity.x > DECIMAL_FIX) - { - rigidbodies[index].velocity.x -= rigidbodies[index].friction; - } - else if (rigidbodies[index].velocity.x < -DECIMAL_FIX) - { - rigidbodies[index].velocity.x += rigidbodies[index].friction; - } - else - { - rigidbodies[index].velocity.x = 0; - } - } - - if (rigidbodies[index].velocity.y > DECIMAL_FIX / 2) - { - rigidbodies[index].velocity.y -= rigidbodies[index].friction; - } - else if (rigidbodies[index].velocity.y < -DECIMAL_FIX / 2) - { - rigidbodies[index].velocity.y += rigidbodies[index].friction; - } - else - { - rigidbodies[index].velocity.y = 0; - } - - // Apply gravity - rigidbodies[index].velocity.y += gravity.y; - rigidbodies[index].velocity.x += gravity.x; - - // Apply acceleration - rigidbodies[index].velocity.y += rigidbodies[index].acceleration.y; - rigidbodies[index].velocity.x += rigidbodies[index].acceleration.x; - - // Update position vector - position->x += rigidbodies[index].velocity.x; - position->y -= rigidbodies[index].velocity.y; - - // Update collider bounds - colliders[index].bounds.x = position->x; - colliders[index].bounds.y = position->y; - - // Check collision with other colliders - collisionChecker = false; - rigidbodies[index].isContact = false; - for (int j = 0; j < maxElements; j++) + if(physicObjects[i]->rigidbody.enabled) physicObjects[i]->rigidbody.isGrounded = false; + } + + for(int steps = 0; steps < PHYSICS_STEPS; steps++) + { + for(int i = 0; i < physicObjectsCount; i++) { - if (index != j) + if(physicObjects[i]->enabled) { - if (colliders[index].enabled && colliders[j].enabled) + // Update physic behaviour + if(physicObjects[i]->rigidbody.enabled) { - if (colliders[index].type == COLLIDER_RECTANGLE) + // Apply friction to acceleration in X axis + if (physicObjects[i]->rigidbody.acceleration.x > PHYSICS_ACCURACY) physicObjects[i]->rigidbody.acceleration.x -= physicObjects[i]->rigidbody.friction/PHYSICS_STEPS; + else if (physicObjects[i]->rigidbody.acceleration.x < PHYSICS_ACCURACY) physicObjects[i]->rigidbody.acceleration.x += physicObjects[i]->rigidbody.friction/PHYSICS_STEPS; + else physicObjects[i]->rigidbody.acceleration.x = 0.0f; + + // Apply friction to velocity in X axis + if (physicObjects[i]->rigidbody.velocity.x > PHYSICS_ACCURACY) physicObjects[i]->rigidbody.velocity.x -= physicObjects[i]->rigidbody.friction/PHYSICS_STEPS; + else if (physicObjects[i]->rigidbody.velocity.x < PHYSICS_ACCURACY) physicObjects[i]->rigidbody.velocity.x += physicObjects[i]->rigidbody.friction/PHYSICS_STEPS; + else physicObjects[i]->rigidbody.velocity.x = 0.0f; + + // Apply gravity to velocity + if (physicObjects[i]->rigidbody.applyGravity) physicObjects[i]->rigidbody.velocity.y += PHYSICS_GRAVITY/PHYSICS_STEPS; + + // Apply acceleration to velocity + physicObjects[i]->rigidbody.velocity.x += physicObjects[i]->rigidbody.acceleration.x/PHYSICS_STEPS; + physicObjects[i]->rigidbody.velocity.y += physicObjects[i]->rigidbody.acceleration.y/PHYSICS_STEPS; + + // Apply velocity to position + physicObjects[i]->transform.position.x += physicObjects[i]->rigidbody.velocity.x/PHYSICS_STEPS; + physicObjects[i]->transform.position.y -= physicObjects[i]->rigidbody.velocity.y/PHYSICS_STEPS; + } + + // Update collision detection + if (physicObjects[i]->collider.enabled) + { + // Update collider bounds + physicObjects[i]->collider.bounds = TransformToRectangle(physicObjects[i]->transform); + + // Check collision with other colliders + for (int k = 0; k < physicObjectsCount; k++) { - if (colliders[j].type == COLLIDER_RECTANGLE) + if (physicObjects[k]->collider.enabled && i != k) { - if (CheckCollisionRecs(colliders[index].bounds, colliders[j].bounds)) + // Check if colliders are overlapped + if (CheckCollisionRecs(physicObjects[i]->collider.bounds, physicObjects[k]->collider.bounds)) { - collisionChecker = true; + // Resolve physic collision + // NOTE: collision resolve is generic for all directions and conditions (no axis separated cases behaviours) + // and it is separated in rigidbody attributes resolve (velocity changes by impulse) and position correction (position overlap) + + // 1. Calculate collision normal + // ------------------------------------------------------------------------------------------------------------------------------------- + + // Define collision ontact normal + Vector2 contactNormal = { 0.0f, 0.0f }; + + // Calculate direction vector from i to k + Vector2 direction; + direction.x = (physicObjects[k]->transform.position.x + physicObjects[k]->transform.scale.x/2) - (physicObjects[i]->transform.position.x + physicObjects[i]->transform.scale.x/2); + direction.y = (physicObjects[k]->transform.position.y + physicObjects[k]->transform.scale.y/2) - (physicObjects[i]->transform.position.y + physicObjects[i]->transform.scale.y/2); + + // Define overlapping and penetration attributes + Vector2 overlap; + float penetrationDepth = 0.0f; - if ((colliders[index].bounds.y + colliders[index].bounds.height <= colliders[j].bounds.y) == false) + // Calculate overlap on X axis + overlap.x = (physicObjects[i]->transform.scale.x + physicObjects[k]->transform.scale.x)/2 - abs(direction.x); + + // SAT test on X axis + if (overlap.x > 0.0f) { - rigidbodies[index].isContact = true; + // Calculate overlap on Y axis + overlap.y = (physicObjects[i]->transform.scale.y + physicObjects[k]->transform.scale.y)/2 - abs(direction.y); + + // SAT test on Y axis + if (overlap.y > 0.0f) + { + // Find out which axis is axis of least penetration + if (overlap.y > overlap.x) + { + // Point towards k knowing that direction points from i to k + if (direction.x < 0.0f) contactNormal = (Vector2){ -1.0f, 0.0f }; + else contactNormal = (Vector2){ 1.0f, 0.0f }; + + // Update penetration depth for position correction + penetrationDepth = overlap.x; + } + else + { + // Point towards k knowing that direction points from i to k + if (direction.y < 0.0f) contactNormal = (Vector2){ 0.0f, 1.0f }; + else contactNormal = (Vector2){ 0.0f, -1.0f }; + + // Update penetration depth for position correction + penetrationDepth = overlap.y; + } + } + } + + // Update rigidbody grounded state + if (physicObjects[i]->rigidbody.enabled) + { + if (contactNormal.y < 0.0f) physicObjects[i]->rigidbody.isGrounded = true; + } + + // 2. Calculate collision impulse + // ------------------------------------------------------------------------------------------------------------------------------------- + + // Calculate relative velocity + Vector2 relVelocity = { physicObjects[k]->rigidbody.velocity.x - physicObjects[i]->rigidbody.velocity.x, physicObjects[k]->rigidbody.velocity.y - physicObjects[i]->rigidbody.velocity.y }; + + // Calculate relative velocity in terms of the normal direction + float velAlongNormal = Vector2DotProduct(relVelocity, contactNormal); + + // Dot not resolve if velocities are separating + if (velAlongNormal <= 0.0f) + { + // Calculate minimum bounciness value from both objects + float e = fminf(physicObjects[i]->rigidbody.bounciness, physicObjects[k]->rigidbody.bounciness); + + // Calculate impulse scalar value + float j = -(1.0f + e) * velAlongNormal; + j /= 1.0f/physicObjects[i]->rigidbody.mass + 1.0f/physicObjects[k]->rigidbody.mass; + + // Calculate final impulse vector + Vector2 impulse = { j*contactNormal.x, j*contactNormal.y }; + + // Calculate collision mass ration + float massSum = physicObjects[i]->rigidbody.mass + physicObjects[k]->rigidbody.mass; + float ratio = 0.0f; + + // Apply impulse to current rigidbodies velocities if they are enabled + if (physicObjects[i]->rigidbody.enabled) + { + // Calculate inverted mass ration + ratio = physicObjects[i]->rigidbody.mass/massSum; + + // Apply impulse direction to velocity + physicObjects[i]->rigidbody.velocity.x -= impulse.x*ratio; + physicObjects[i]->rigidbody.velocity.y -= impulse.y*ratio; + } + + if (physicObjects[k]->rigidbody.enabled) + { + // Calculate inverted mass ration + ratio = physicObjects[k]->rigidbody.mass/massSum; + + // Apply impulse direction to velocity + physicObjects[k]->rigidbody.velocity.x += impulse.x*ratio; + physicObjects[k]->rigidbody.velocity.y += impulse.y*ratio; + } + + // 3. Correct colliders overlaping (transform position) + // --------------------------------------------------------------------------------------------------------------------------------- + + // Calculate transform position penetration correction + Vector2 posCorrection; + posCorrection.x = penetrationDepth/((1.0f/physicObjects[i]->rigidbody.mass) + (1.0f/physicObjects[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.x; + posCorrection.y = penetrationDepth/((1.0f/physicObjects[i]->rigidbody.mass) + (1.0f/physicObjects[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.y; + + // Fix transform positions + if (physicObjects[i]->rigidbody.enabled) + { + // Fix physic objects transform position + physicObjects[i]->transform.position.x -= 1.0f/physicObjects[i]->rigidbody.mass*posCorrection.x; + physicObjects[i]->transform.position.y += 1.0f/physicObjects[i]->rigidbody.mass*posCorrection.y; + + // Update collider bounds + physicObjects[i]->collider.bounds = TransformToRectangle(physicObjects[i]->transform); + + if (physicObjects[k]->rigidbody.enabled) + { + // Fix physic objects transform position + physicObjects[k]->transform.position.x += 1.0f/physicObjects[k]->rigidbody.mass*posCorrection.x; + physicObjects[k]->transform.position.y -= 1.0f/physicObjects[k]->rigidbody.mass*posCorrection.y; + + // Update collider bounds + physicObjects[k]->collider.bounds = TransformToRectangle(physicObjects[k]->transform); + } + } } - } - } - else - { - if (CheckCollisionCircleRec((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, colliders[index].bounds)) - { - collisionChecker = true; - } - } - } - else - { - if (colliders[j].type == COLLIDER_RECTANGLE) - { - if (CheckCollisionCircleRec((Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius, colliders[j].bounds)) - { - collisionChecker = true; - } - } - else - { - if (CheckCollisionCircles((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, (Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius)) - { - collisionChecker = true; } } } } } } - - // Update grounded rigidbody state - rigidbodies[index].isGrounded = collisionChecker; - - // Set grounded state if needed (fix overlap and set y velocity) - if (collisionChecker && rigidbodies[index].velocity.y != 0) - { - position->y += rigidbodies[index].velocity.y; - rigidbodies[index].velocity.y = -rigidbodies[index].velocity.y * rigidbodies[index].bounciness; - } - - if (rigidbodies[index].isContact) - { - position->x -= rigidbodies[index].velocity.x; - rigidbodies[index].velocity.x = rigidbodies[index].velocity.x; - } } } -void SetRigidbodyEnabled(int index, bool state) -{ - rigidbodies[index].enabled = state; -} - -void SetRigidbodyVelocity(int index, Vector2 velocity) -{ - rigidbodies[index].velocity.x = velocity.x; - rigidbodies[index].velocity.y = velocity.y; -} - -void SetRigidbodyAcceleration(int index, Vector2 acceleration) +// Unitialize all physic objects and empty the objects pool +void ClosePhysics() { - rigidbodies[index].acceleration.x = acceleration.x; - rigidbodies[index].acceleration.y = acceleration.y; + // Free all dynamic memory allocations + for (int i = 0; i < physicObjectsCount; i++) free(physicObjects[i]); + + // Reset enabled physic objects count + physicObjectsCount = 0; } -void AddRigidbodyForce(int index, Vector2 force) +// Create a new physic object dinamically, initialize it and add to pool +PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale) { - rigidbodies[index].acceleration.x = force.x / rigidbodies[index].mass; - rigidbodies[index].acceleration.y = force.y / rigidbodies[index].mass; + // Allocate dynamic memory + PhysicObject *obj = (PhysicObject *)malloc(sizeof(PhysicObject)); + + // Initialize physic object values with generic values + obj->id = physicObjectsCount; + obj->enabled = true; + + obj->transform = (Transform){ (Vector2){ position.x - scale.x/2, position.y - scale.y/2 }, rotation, scale }; + + obj->rigidbody.enabled = false; + obj->rigidbody.mass = 1.0f; + obj->rigidbody.acceleration = (Vector2){ 0.0f, 0.0f }; + obj->rigidbody.velocity = (Vector2){ 0.0f, 0.0f }; + obj->rigidbody.applyGravity = false; + obj->rigidbody.isGrounded = false; + obj->rigidbody.friction = 0.0f; + obj->rigidbody.bounciness = 0.0f; + + obj->collider.enabled = false; + obj->collider.type = COLLIDER_RECTANGLE; + obj->collider.bounds = TransformToRectangle(obj->transform); + obj->collider.radius = 0.0f; + + // Add new physic object to the pointers array + physicObjects[physicObjectsCount] = obj; + + // Increase enabled physic objects count + physicObjectsCount++; + + return obj; } -void AddForceAtPosition(Vector2 position, float intensity, float radius) +// Destroy a specific physic object and take it out of the list +void DestroyPhysicObject(PhysicObject *pObj) { - for(int i = 0; i < maxElements; i++) + // Free dynamic memory allocation + free(physicObjects[pObj->id]); + + // Remove *obj from the pointers array + for (int i = pObj->id; i < physicObjectsCount; i++) { - if(rigidbodies[i].enabled) + // Resort all the following pointers of the array + if ((i + 1) < physicObjectsCount) { - // Get position from its collider - Vector2 pos = {colliders[i].bounds.x, colliders[i].bounds.y}; - - // Get distance between rigidbody position and target position - float distance = Vector2Distance(position, pos); - - if(distance <= radius) - { - // Calculate force based on direction - Vector2 force = {colliders[i].bounds.x - position.x, colliders[i].bounds.y - position.y}; - - // Normalize the direction vector - Vector2Normalize(&force); - - // Invert y value - force.y *= -1; - - // Apply intensity and distance - force = (Vector2){force.x * intensity / distance, force.y * intensity / distance}; - - // Add calculated force to the rigidbodies - AddRigidbodyForce(i, force); - } + physicObjects[i] = physicObjects[i + 1]; + physicObjects[i]->id = physicObjects[i + 1]->id; } + else free(physicObjects[i]); } + + // Decrease enabled physic objects count + physicObjectsCount--; } -void SetColliderEnabled(int index, bool state) -{ - colliders[index].enabled = state; -} - -Collider GetCollider(int index) +// Convert Transform data type to Rectangle (position and scale) +Rectangle TransformToRectangle(Transform transform) { - return colliders[index]; + return (Rectangle){transform.position.x, transform.position.y, transform.scale.x, transform.scale.y}; } -Rigidbody GetRigidbody(int index) +// Draw physic object information at screen position +void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize) { - return rigidbodies[index]; + // Draw physic object ID + DrawText(FormatText("PhysicObject ID: %i - Enabled: %i", pObj->id, pObj->enabled), position.x, position.y, fontSize, BLACK); + + // Draw physic object transform values + DrawText(FormatText("\nTRANSFORM\nPosition: %f, %f\nRotation: %f\nScale: %f, %f", pObj->transform.position.x, pObj->transform.position.y, pObj->transform.rotation, pObj->transform.scale.x, pObj->transform.scale.y), position.x, position.y, fontSize, BLACK); + + // Draw physic object rigidbody values + DrawText(FormatText("\n\n\n\n\n\nRIGIDBODY\nEnabled: %i\nMass: %f\nAcceleration: %f, %f\nVelocity: %f, %f\nApplyGravity: %i\nIsGrounded: %i\nFriction: %f\nBounciness: %f", pObj->rigidbody.enabled, pObj->rigidbody.mass, pObj->rigidbody.acceleration.x, pObj->rigidbody.acceleration.y, + pObj->rigidbody.velocity.x, pObj->rigidbody.velocity.y, pObj->rigidbody.applyGravity, pObj->rigidbody.isGrounded, pObj->rigidbody.friction, pObj->rigidbody.bounciness), position.x, position.y, fontSize, BLACK); + + DrawText(FormatText("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nCOLLIDER\nEnabled: %i\nBounds: %i, %i, %i, %i\nRadius: %i", pObj->collider.enabled, pObj->collider.bounds.x, pObj->collider.bounds.y, pObj->collider.bounds.width, pObj->collider.bounds.height, pObj->collider.radius), position.x, position.y, fontSize, BLACK); } //---------------------------------------------------------------------------------- -// Module specific Functions Definitions +// Module specific Functions Definition //---------------------------------------------------------------------------------- -static float Vector2Length(Vector2 vector) -{ - return sqrt((vector.x * vector.x) + (vector.y * vector.y)); -} -static float Vector2Distance(Vector2 a, Vector2 b) +// Returns the dot product of two Vector2 +static float Vector2DotProduct(Vector2 v1, Vector2 v2) { - Vector2 vector = {b.x - a.x, b.y - a.y}; - return sqrt((vector.x * vector.x) + (vector.y * vector.y)); -} + float result; -static void Vector2Normalize(Vector2 *vector) -{ - float length = Vector2Length(*vector); - - if (length != 0.0f) - { - vector->x /= length; - vector->y /= length; - } - else - { - vector->x = 0.0f; - vector->y = 0.0f; - } + result = v1.x*v2.x + v1.y*v2.y; + + return result; } diff --git a/src/physac.h b/src/physac.h index 9e1b0b88b..b948e4ce0 100644 --- a/src/physac.h +++ b/src/physac.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* [physac] raylib physics engine module - Basic functions to apply physics to 2D objects +* [physac] raylib physics module - Basic functions to apply physics to 2D objects * * Copyright (c) 2015 Victor Fisac and Ramon Santamaria * @@ -31,62 +31,64 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition +// NOTE: Below types are required for PHYSAC_STANDALONE usage //---------------------------------------------------------------------------------- -// Collider types + +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; + typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType; -// Transform struct typedef struct Transform { Vector2 position; float rotation; Vector2 scale; } Transform; -// Rigidbody struct typedef struct Rigidbody { - bool enabled; + bool enabled; // Acts as kinematic state (collisions are calculated anyway) float mass; Vector2 acceleration; Vector2 velocity; - bool isGrounded; - bool isContact; // Avoid freeze player when touching floor bool applyGravity; - float friction; // 0.0f to 1.0f - float bounciness; // 0.0f to 1.0f + bool isGrounded; + float friction; // Normalized value + float bounciness; // Normalized value } Rigidbody; -// Collider struct typedef struct Collider { bool enabled; ColliderType type; - Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE - int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE + Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE + int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE } Collider; +typedef struct PhysicObject { + unsigned int id; + Transform transform; + Rigidbody rigidbody; + Collider collider; + bool enabled; +} PhysicObject; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif //---------------------------------------------------------------------------------- -// Module Functions Declarations +// Module Functions Declaration //---------------------------------------------------------------------------------- -void InitPhysics(int maxPhysicElements); // Initialize all internal physics values -void UnloadPhysics(); // Unload physic elements arrays - -void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot -void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot - -void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter -void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody -void SetRigidbodyVelocity(int index, Vector2 velocity); // Set velocity of rigidbody (without considering of mass value) -void SetRigidbodyAcceleration(int index, Vector2 acceleration); // Set acceleration of rigidbody (without considering of mass value) -void AddRigidbodyForce(int index, Vector2 force); // Set rigidbody force (considering mass value) -void AddForceAtPosition(Vector2 position, float intensity, float radius); // Add a force to all enabled rigidbodies at a position +void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection +void ClosePhysics(); // Unitialize all physic objects and empty the objects pool -void SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider +PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool +void DestroyPhysicObject(PhysicObject *pObj); // Destroy a specific physic object and take it out of the list -Rigidbody GetRigidbody(int index); // Returns the internal rigidbody data defined by index parameter -Collider GetCollider(int index); // Returns the internal collider data defined by index parameter +Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale) +void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize); // Draw physic object information at screen position #ifdef __cplusplus } diff --git a/src/raylib.h b/src/raylib.h index c598ec303..b9cf05a4c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -465,37 +465,40 @@ typedef struct { // Camera system modes typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode; -// Collider types typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType; -// Transform struct typedef struct Transform { Vector2 position; float rotation; Vector2 scale; } Transform; -// Rigidbody struct typedef struct Rigidbody { - bool enabled; + bool enabled; // Acts as kinematic state (collisions are calculated anyway) float mass; Vector2 acceleration; Vector2 velocity; - bool isGrounded; - bool isContact; // Avoid freeze player when touching floor bool applyGravity; - float friction; // 0.0f to 1.0f - float bounciness; // 0.0f to 1.0f + bool isGrounded; + float friction; // Normalized value + float bounciness; // Normalized value } Rigidbody; -// Collider struct typedef struct Collider { bool enabled; ColliderType type; - Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE - int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE + Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE + int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE } Collider; +typedef struct PhysicObject { + unsigned int id; + Transform transform; + Rigidbody rigidbody; + Collider collider; + bool enabled; +} PhysicObject; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -800,25 +803,17 @@ void SetShaderMap(Shader *shader, int mapLocation, Texture2D texture, int textur void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied) //---------------------------------------------------------------------------------- -// Physics System Functions (engine-module: physac) +// Physics System Functions (Module: physac) //---------------------------------------------------------------------------------- -void InitPhysics(int maxPhysicElements); // Initialize all internal physics values -void UnloadPhysics(); // Unload physic elements arrays - -void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot -void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot - -void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter -void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody -void SetRigidbodyVelocity(int index, Vector2 velocity); // Set velocity of rigidbody (without considering of mass value) -void SetRigidbodyAcceleration(int index, Vector2 acceleration); // Set acceleration of rigidbody (without considering of mass value) -void AddRigidbodyForce(int index, Vector2 force); // Set rigidbody force (considering mass value) -void AddForceAtPosition(Vector2 position, float intensity, float radius); // Add a force to all enabled rigidbodies at a position +void InitPhysics(); // Initializes pointers array (just pointers, fixed size) +void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection +void ClosePhysics(); // Unitialize all physic objects and empty the objects pool -void SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider +PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool +void DestroyPhysicObject(PhysicObject *pObj); // Destroy a specific physic object and take it out of the list -Rigidbody GetRigidbody(int index); // Returns the internal rigidbody data defined by index parameter -Collider GetCollider(int index); // Returns the internal collider data defined by index parameter +Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale) +void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize); // Draw physic object information at screen position //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio)