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.
This commit is contained in:
victorfisac 2016-03-05 17:05:02 +01:00
parent 68088bc5be
commit 305efcf5ad
6 changed files with 418 additions and 539 deletions

View file

@ -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) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* *
* Copyright (c) 2016 Victor Fisac and Ramon Santamaria (@raysan5) * Copyright (c) 2016 Victor Fisac and Ramon Santamaria (@raysan5)
@ -11,8 +11,8 @@
#include "raylib.h" #include "raylib.h"
#define OBJECT_SIZE 50 #define MOVE_VELOCITY 5
#define PLAYER_INDEX 0 #define JUMP_VELOCITY 35
int main() int main()
{ {
@ -21,27 +21,44 @@ int main()
int screenWidth = 800; int screenWidth = 800;
int screenHeight = 450; int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [physics] example - basic rigidbody"); InitWindow(screenWidth, screenHeight, "raylib [physac] example - basic rigidbody");
InitPhysics(); // Initialize physics module
InitPhysics(3); // Initialize physics system with maximum physic objects
// 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});
// 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});
// Object properties initialization
float moveSpeed = 6.0f;
float jumpForce = 5.0f;
bool physicsDebug = false;
SetTargetFPS(60); SetTargetFPS(60);
// Debug variables
bool isDebug = false;
// 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
// 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;
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
// Main game loop // Main game loop
@ -49,35 +66,22 @@ int main()
{ {
// Update // Update
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
UpdatePhysics(); // Update all created physic objects
// Update object physics // Check debug switch input
// NOTE: all physics detections and reactions are calculated in ApplyPhysics() function (You will live happier :D) if (IsKeyPressed('P')) isDebug = !isDebug;
ApplyPhysics(PLAYER_INDEX, &player.position);
// Check jump button input // Check player movement inputs
if (IsKeyDown(KEY_SPACE) && GetRigidbody(PLAYER_INDEX).isGrounded) if (IsKeyDown('W') && player->rigidbody.isGrounded) player->rigidbody.velocity.y = JUMP_VELOCITY;
{
// 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 if (IsKeyDown('A')) player->rigidbody.velocity.x = -MOVE_VELOCITY;
AddRigidbodyForce(PLAYER_INDEX, (Vector2){0, jumpForce}); else if (IsKeyDown('D')) player->rigidbody.velocity.x = MOVE_VELOCITY;
}
// Check movement buttons input // Check player 2 movement inputs
if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) if (IsKeyDown(KEY_UP) && player2->rigidbody.isGrounded) player2->rigidbody.velocity.y = JUMP_VELOCITY;
{
// 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 debug mode toggle button input if (IsKeyDown(KEY_LEFT)) player2->rigidbody.velocity.x = -MOVE_VELOCITY;
if (IsKeyPressed(KEY_P)) physicsDebug = !physicsDebug; else if (IsKeyDown(KEY_RIGHT)) player2->rigidbody.velocity.x = MOVE_VELOCITY;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Draw // Draw
@ -86,37 +90,36 @@ int main()
ClearBackground(RAYWHITE); ClearBackground(RAYWHITE);
// Draw information if (isDebug)
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)
{ {
// Draw every internal physics stored collider if it is active DrawRectangleLines(floor->collider.bounds.x, floor->collider.bounds.y, floor->collider.bounds.width, floor->collider.bounds.height, GREEN);
for (int i = 0; i < 2; i++) 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);
if (GetCollider(i).enabled) 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(GetCollider(i).bounds.x, GetCollider(i).bounds.y, GetCollider(i).bounds.width, GetCollider(i).bounds.height, GREEN); DrawRectangleLines(player2->collider.bounds.x, player2->collider.bounds.y, player2->collider.bounds.width, player2->collider.bounds.height, GREEN);
}
}
} }
else else
{ {
// Draw player and floor // Convert transform values to rectangle data type variable
DrawRectangleRec((Rectangle){player.position.x, player.position.y, player.scale.x, player.scale.y}, GRAY); DrawRectangleRec(TransformToRectangle(floor->transform), DARKGRAY);
DrawRectangleRec((Rectangle){floor.position.x, floor.position.y, floor.scale.x, floor.scale.y}, BLACK); 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(); EndDrawing();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
} }
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
UnloadPhysics(); // Unload physic objects ClosePhysics(); // Unitialize physics module
CloseWindow(); // Close window and OpenGL context CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------

View file

@ -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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View file

@ -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 * Copyright (c) 2015 Victor Fisac and Ramon Santamaria
* *
@ -29,329 +29,343 @@
#include "raylib.h" #include "raylib.h"
#endif #endif
#include <math.h> #include <stdlib.h> // Declares malloc() and free() for memory management
#include <stdlib.h> // Required for: malloc(), free() #include <math.h> // abs() and fminf()
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // 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 // Types and Structures Definition
// NOTE: Below types are required for PHYSAC_STANDALONE usage
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// ... // ...
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static Collider *colliders; // Colliders array, dynamically allocated at runtime static PhysicObject *physicObjects[MAX_PHYSIC_OBJECTS]; // Physic objects pool
static Rigidbody *rigidbodies; // Rigitbody array, dynamically allocated at runtime static int physicObjectsCount; // Counts current enabled physic objects
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
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declarations // Module specific Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static float Vector2Length(Vector2 vector); static float Vector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two Vector2
static float Vector2Distance(Vector2 a, Vector2 b);
static void Vector2Normalize(Vector2 *vector);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definitions // Module Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
void InitPhysics(int maxPhysicElements)
// Initializes pointers array (just pointers, fixed size)
void InitPhysics()
{ {
maxElements = maxPhysicElements; // Initialize physics variables
physicObjectsCount = 0;
}
colliders = (Collider *)malloc(maxElements*sizeof(Collider)); // Update physic objects, calculating physic behaviours and collisions detection
rigidbodies = (Rigidbody *)malloc(maxElements*sizeof(Rigidbody)); void UpdatePhysics()
{
for (int i = 0; i < maxElements; i++) // Reset all physic objects is grounded state
for(int i = 0; i < physicObjectsCount; i++)
{ {
colliders[i].enabled = false; if(physicObjects[i]->rigidbody.enabled) physicObjects[i]->rigidbody.isGrounded = 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; for(int steps = 0; steps < PHYSICS_STEPS; steps++)
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)
{
rigidbodies[index] = rigidbody;
}
void ApplyPhysics(int index, Vector2 *position)
{
if (rigidbodies[index].enabled)
{ {
// Apply friction to acceleration for(int i = 0; i < physicObjectsCount; i++)
if (rigidbodies[index].acceleration.x > DECIMAL_FIX)
{ {
rigidbodies[index].acceleration.x -= rigidbodies[index].friction; if(physicObjects[i]->enabled)
}
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; // Update physic behaviour
} if(physicObjects[i]->rigidbody.enabled)
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 (index != j)
{
if (colliders[index].enabled && colliders[j].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;
if (colliders[j].type == COLLIDER_RECTANGLE) 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;
if (CheckCollisionRecs(colliders[index].bounds, colliders[j].bounds))
{
collisionChecker = true;
if ((colliders[index].bounds.y + colliders[index].bounds.height <= colliders[j].bounds.y) == false) // 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;
rigidbodies[index].isContact = true; 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
else if (physicObjects[i]->rigidbody.applyGravity) physicObjects[i]->rigidbody.velocity.y += PHYSICS_GRAVITY/PHYSICS_STEPS;
{
if (CheckCollisionCircleRec((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, colliders[index].bounds)) // Apply acceleration to velocity
{ physicObjects[i]->rigidbody.velocity.x += physicObjects[i]->rigidbody.acceleration.x/PHYSICS_STEPS;
collisionChecker = true; 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;
else 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 (CheckCollisionCircleRec((Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius, 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)
else
{ // 1. Calculate collision normal
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; // 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;
// 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)
{
// 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);
}
}
}
} }
} }
} }
} }
} }
} }
// 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) // Unitialize all physic objects and empty the objects pool
void ClosePhysics()
{ {
rigidbodies[index].enabled = state; // Free all dynamic memory allocations
for (int i = 0; i < physicObjectsCount; i++) free(physicObjects[i]);
// Reset enabled physic objects count
physicObjectsCount = 0;
} }
void SetRigidbodyVelocity(int index, Vector2 velocity) // Create a new physic object dinamically, initialize it and add to pool
PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale)
{ {
rigidbodies[index].velocity.x = velocity.x; // Allocate dynamic memory
rigidbodies[index].velocity.y = velocity.y; 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 SetRigidbodyAcceleration(int index, Vector2 acceleration) // Destroy a specific physic object and take it out of the list
void DestroyPhysicObject(PhysicObject *pObj)
{ {
rigidbodies[index].acceleration.x = acceleration.x; // Free dynamic memory allocation
rigidbodies[index].acceleration.y = acceleration.y; free(physicObjects[pObj->id]);
}
void AddRigidbodyForce(int index, Vector2 force) // Remove *obj from the pointers array
{ for (int i = pObj->id; i < physicObjectsCount; i++)
rigidbodies[index].acceleration.x = force.x / rigidbodies[index].mass;
rigidbodies[index].acceleration.y = force.y / rigidbodies[index].mass;
}
void AddForceAtPosition(Vector2 position, float intensity, float radius)
{
for(int i = 0; i < maxElements; i++)
{ {
if(rigidbodies[i].enabled) // Resort all the following pointers of the array
if ((i + 1) < physicObjectsCount)
{ {
// Get position from its collider physicObjects[i] = physicObjects[i + 1];
Vector2 pos = {colliders[i].bounds.x, colliders[i].bounds.y}; physicObjects[i]->id = physicObjects[i + 1]->id;
// 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);
}
} }
else free(physicObjects[i]);
} }
// Decrease enabled physic objects count
physicObjectsCount--;
} }
void SetColliderEnabled(int index, bool state) // Convert Transform data type to Rectangle (position and scale)
Rectangle TransformToRectangle(Transform transform)
{ {
colliders[index].enabled = state; return (Rectangle){transform.position.x, transform.position.y, transform.scale.x, transform.scale.y};
} }
Collider GetCollider(int index) // Draw physic object information at screen position
void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize)
{ {
return colliders[index]; // Draw physic object ID
} DrawText(FormatText("PhysicObject ID: %i - Enabled: %i", pObj->id, pObj->enabled), position.x, position.y, fontSize, BLACK);
Rigidbody GetRigidbody(int index) // 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);
return rigidbodies[index];
// 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}; float result;
return sqrt((vector.x * vector.x) + (vector.y * vector.y));
}
static void Vector2Normalize(Vector2 *vector) result = v1.x*v2.x + v1.y*v2.y;
{
float length = Vector2Length(*vector);
if (length != 0.0f) return result;
{
vector->x /= length;
vector->y /= length;
}
else
{
vector->x = 0.0f;
vector->y = 0.0f;
}
} }

View file

@ -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 * Copyright (c) 2015 Victor Fisac and Ramon Santamaria
* *
@ -31,62 +31,64 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // 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; typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType;
// Transform struct
typedef struct Transform { typedef struct Transform {
Vector2 position; Vector2 position;
float rotation; float rotation;
Vector2 scale; Vector2 scale;
} Transform; } Transform;
// Rigidbody struct
typedef struct Rigidbody { typedef struct Rigidbody {
bool enabled; bool enabled; // Acts as kinematic state (collisions are calculated anyway)
float mass; float mass;
Vector2 acceleration; Vector2 acceleration;
Vector2 velocity; Vector2 velocity;
bool isGrounded;
bool isContact; // Avoid freeze player when touching floor
bool applyGravity; bool applyGravity;
float friction; // 0.0f to 1.0f bool isGrounded;
float bounciness; // 0.0f to 1.0f float friction; // Normalized value
float bounciness; // Normalized value
} Rigidbody; } Rigidbody;
// Collider struct
typedef struct Collider { typedef struct Collider {
bool enabled; bool enabled;
ColliderType type; ColliderType type;
Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE
int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE
} Collider; } Collider;
typedef struct PhysicObject {
unsigned int id;
Transform transform;
Rigidbody rigidbody;
Collider collider;
bool enabled;
} PhysicObject;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Declarations // Module Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
void InitPhysics(int maxPhysicElements); // Initialize all internal physics values void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection
void UnloadPhysics(); // Unload physic elements arrays void ClosePhysics(); // Unitialize all physic objects and empty the objects pool
void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool
void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot void DestroyPhysicObject(PhysicObject *pObj); // Destroy a specific physic object and take it out of the list
void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale)
void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize); // Draw physic object information at screen position
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 SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider
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
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -465,37 +465,40 @@ typedef struct {
// Camera system modes // Camera system modes
typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode; 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; typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType;
// Transform struct
typedef struct Transform { typedef struct Transform {
Vector2 position; Vector2 position;
float rotation; float rotation;
Vector2 scale; Vector2 scale;
} Transform; } Transform;
// Rigidbody struct
typedef struct Rigidbody { typedef struct Rigidbody {
bool enabled; bool enabled; // Acts as kinematic state (collisions are calculated anyway)
float mass; float mass;
Vector2 acceleration; Vector2 acceleration;
Vector2 velocity; Vector2 velocity;
bool isGrounded;
bool isContact; // Avoid freeze player when touching floor
bool applyGravity; bool applyGravity;
float friction; // 0.0f to 1.0f bool isGrounded;
float bounciness; // 0.0f to 1.0f float friction; // Normalized value
float bounciness; // Normalized value
} Rigidbody; } Rigidbody;
// Collider struct
typedef struct Collider { typedef struct Collider {
bool enabled; bool enabled;
ColliderType type; ColliderType type;
Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE
int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE
} Collider; } Collider;
typedef struct PhysicObject {
unsigned int id;
Transform transform;
Rigidbody rigidbody;
Collider collider;
bool enabled;
} PhysicObject;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #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) 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 InitPhysics(); // Initializes pointers array (just pointers, fixed size)
void UnloadPhysics(); // Unload physic elements arrays void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection
void ClosePhysics(); // Unitialize all physic objects and empty the objects pool
void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot PhysicObject *CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool
void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot void DestroyPhysicObject(PhysicObject *pObj); // Destroy a specific physic object and take it out of the list
void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale)
void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody void DrawPhysicObjectInfo(PhysicObject *pObj, Vector2 position, int fontSize); // Draw physic object information at screen position
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 SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider
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
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Audio Loading and Playing Functions (Module: audio) // Audio Loading and Playing Functions (Module: audio)