Agnis Aldiņš "NeZvērs 1 день тому
committed by GitHub
джерело
коміт
ca9ba37f4f
Не вдалося знайти GPG ключ що відповідає даному підпису Ідентифікатор GPG ключа: B5690EEEBB952194
5 змінених файлів з 365 додано та 0 видалено
  1. +1
    -0
      examples/Makefile
  2. +4
    -0
      examples/Makefile.Web
  3. +360
    -0
      examples/core/core_3d_fps_controller.c
  4. BIN
      examples/core/core_3d_fps_controller.png
  5. BIN
      examples/core/resources/huh_jump.wav

+ 1
- 0
examples/Makefile Переглянути файл

@ -500,6 +500,7 @@ CORE = \
core/core_3d_camera_free \
core/core_3d_camera_mode \
core/core_3d_camera_split_screen \
core/core_3d_fps_controller \
core/core_3d_picking \
core/core_automation_events \
core/core_basic_screen_manager \

+ 4
- 0
examples/Makefile.Web Переглянути файл

@ -382,6 +382,7 @@ CORE = \
core/core_3d_camera_free \
core/core_3d_camera_mode \
core/core_3d_camera_split_screen \
core/core_3d_fps_controller \
core/core_3d_picking \
core/core_automation_events \
core/core_basic_screen_manager \
@ -587,6 +588,9 @@ core/core_3d_camera_mode: core/core_3d_camera_mode.c
core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
core/core_3d_fps_controller: core/core_3d_fps_controller.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
core/core_3d_picking: core/core_3d_picking.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)

+ 360
- 0
examples/core/core_3d_fps_controller.c Переглянути файл

@ -0,0 +1,360 @@
/*******************************************************************************************
*
* raylib [core] example - Input Gestures for Web
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 5.5
*
* Example contributed by Agnis Aldins (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025-2025 Agnis Aldins (@nezvers)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "rcamera.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 100
#endif
/* Movement constants */
#define GRAVITY 32.f
#define MAX_SPEED 20.f
#define CROUCH_SPEED 5.f
#define JUMP_FORCE 12.f
#define MAX_ACCEL 150.f
/* Grounded drag */
#define FRICTION 0.86f
/* Increasing air drag, increases strafing speed */
#define AIR_DRAG 0.98f
/* Responsiveness for turning movement direction to looked direction */
#define CONTROL 15.f
#define CROUCH_HEIGHT 0.f
#define STAND_HEIGHT 1.f
#define BOTTOM_HEIGHT 0.5f
#define NORMALIZE_INPUT 0
typedef struct {
Vector3 position;
Vector3 velocity;
Vector3 dir;
bool isGrounded;
Sound soundJump;
}Body;
const int screenWidth = 800;
const int screenHeight = 450;
Vector2 sensitivity = { 0.001f, 0.001f };
Body player;
Camera camera;
Vector2 lookRotation = { 0 };
float headTimer;
float walkLerp;
float headLerp;
Vector2 lean;
void UpdateDrawFrame(void); // Update and Draw one frame
void DrawLevel();
void UpdateCameraAngle();
void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "Raylib Quake-like controller");
InitAudioDevice();
player = (Body){ Vector3Zero(), Vector3Zero(), Vector3Zero(), false, LoadSound("resources/huh_jump.wav")};
camera = (Camera){ 0 };
camera.fovy = 60.f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
lookRotation = Vector2Zero();
headTimer = 0.f;
walkLerp = 0.f;
headLerp = STAND_HEIGHT;
lean = Vector2Zero();
camera.position = (Vector3){
player.position.x,
player.position.y + (BOTTOM_HEIGHT + headLerp),
player.position.z,
};
UpdateCameraAngle();
DisableCursor(); // Limit cursor to relative movement inside the window
#if defined(PLATFORM_WEB)
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
#else
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
UpdateDrawFrame();
}
#endif
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadSound(player.soundJump);
CloseAudioDevice();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
void UpdateDrawFrame(void)
{
// Update
//----------------------------------------------------------------------------------
Vector2 mouse_delta = GetMouseDelta();
lookRotation.x -= mouse_delta.x * sensitivity.x;
lookRotation.y += mouse_delta.y * sensitivity.y;
char sideway = (IsKeyDown(KEY_D) - IsKeyDown(KEY_A));
char forward = (IsKeyDown(KEY_W) - IsKeyDown(KEY_S));
bool crouching = IsKeyDown(KEY_LEFT_CONTROL);
UpdateBody(&player, lookRotation.x, sideway, forward, IsKeyPressed(KEY_SPACE), crouching);
float delta = GetFrameTime();
headLerp = Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.f * delta);
camera.position = (Vector3){
player.position.x,
player.position.y + (BOTTOM_HEIGHT + headLerp),
player.position.z,
};
if (player.isGrounded && (forward != 0 || sideway != 0)) {
headTimer += delta * 3.f;
walkLerp = Lerp(walkLerp, 1.f, 10.f * delta);
camera.fovy = Lerp(camera.fovy, 55.f, 5.f * delta);
}
else {
walkLerp = Lerp(walkLerp, 0.f, 10.f * delta);
camera.fovy = Lerp(camera.fovy, 60.f, 5.f * delta);
}
lean.x = Lerp(lean.x, sideway * 0.02f, 10.f * delta);
lean.y = Lerp(lean.y, forward * 0.015f, 10.f * delta);
UpdateCameraAngle();
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawLevel();
EndMode3D();
// Draw info box
DrawRectangle(5, 5, 330, 100, Fade(SKYBLUE, 0.5f));
DrawRectangleLines(5, 5, 330, 100, BLUE);
DrawText("Camera controls:", 15, 15, 10, BLACK);
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK);
DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK);
DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2) { player.velocity.x, player.velocity.z })), 15, 60, 10, BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
}
void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold)
{
Vector2 input = (Vector2){ (float)side, (float)-forward };
#if defined(NORMALIZE_INPUT)
// Slow down diagonal movement
if (side != 0 & forward != 0)
{
input = Vector2Normalize(input);
}
#endif
float delta = GetFrameTime();
if (!body->isGrounded)
{
body->velocity.y -= GRAVITY * delta;
}
if (body->isGrounded && jumpPressed)
{
body->velocity.y = JUMP_FORCE;
body->isGrounded = false;
SetSoundPitch(body->soundJump, 1.f + (GetRandomValue(-100, 100) * 0.001));
PlaySound(body->soundJump);
}
Vector3 front_vec = (Vector3){ sin(rot), 0.f, cos(rot) };
Vector3 right_vec = (Vector3){ cos(-rot), 0.f, sin(-rot) };
Vector3 desired_dir = (Vector3){
input.x * right_vec.x + input.y * front_vec.x,
0.f,
input.x * right_vec.z + input.y * front_vec.z,
};
body->dir = Vector3Lerp(body->dir, desired_dir, CONTROL * delta);
float decel = body->isGrounded ? FRICTION : AIR_DRAG;
Vector3 hvel = (Vector3){
body->velocity.x * decel,
0.f,
body->velocity.z * decel
};
float hvel_length = Vector3Length(hvel); // a.k.a. magnitude
if (hvel_length < MAX_SPEED * 0.01f) {
hvel = (Vector3){ 0 };
}
/* This is what creates strafing */
float speed = Vector3DotProduct(hvel, body->dir);
/*
Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
More info here: https://youtu.be/v3zT3Z5apaM?t=165
*/
float max_speed = crouchHold ? CROUCH_SPEED : MAX_SPEED;
float accel = Clamp(max_speed - speed, 0.f, MAX_ACCEL * delta);
hvel.x += body->dir.x * accel;
hvel.z += body->dir.z * accel;
body->velocity.x = hvel.x;
body->velocity.z = hvel.z;
body->position.x += body->velocity.x * delta;
body->position.y += body->velocity.y * delta;
body->position.z += body->velocity.z * delta;
/* Fancy collision system against "THE FLOOR" */
if (body->position.y <= 0.f)
{
body->position.y = 0.f;
body->velocity.y = 0.f;
body->isGrounded = true; // <= enables jumping
}
}
void UpdateCameraAngle()
{
const Vector3 up = (Vector3){ 0.f, 1.f, 0.f };
const Vector3 targetOffset = (Vector3){ 0.f, 0.f, -1.f };
/* Left & Right */
Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x);
// Clamp view up
float maxAngleUp = Vector3Angle(up, yaw);
maxAngleUp -= 0.001f; // avoid numerical errors
if ( -(lookRotation.y) > maxAngleUp) { lookRotation.y = -maxAngleUp; }
// Clamp view down
float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw);
maxAngleDown *= -1.0f; // downwards angle is negative
maxAngleDown += 0.001f; // avoid numerical errors
if ( -(lookRotation.y) < maxAngleDown) { lookRotation.y = -maxAngleDown; }
/* Up & Down */
Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up));
// Rotate view vector around right axis
Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, -lookRotation.y - lean.y);
// Head animation
// Rotate up direction around forward axis
float _sin = sin(headTimer * PI);
float _cos = cos(headTimer * PI);
const float stepRotation = 0.01f;
camera.up = Vector3RotateByAxisAngle(up, pitch, _sin * stepRotation + lean.x);
/* BOB */
const float bobSide = 0.1f;
const float bobUp = 0.15f;
Vector3 bobbing = Vector3Scale(right, _sin * bobSide);
bobbing.y = fabsf(_cos * bobUp);
camera.position = Vector3Add(camera.position, Vector3Scale(bobbing, walkLerp));
camera.target = Vector3Add(camera.position, pitch);
}
void DrawLevel()
{
const int floorExtent = 25;
const float tileSize = 5.f;
const Color tileColor1 = (Color){ 150, 200, 200, 255 };
// Floor tiles
for (int y = -floorExtent; y < floorExtent; y++)
{
for (int x = -floorExtent; x < floorExtent; x++)
{
if ((y & 1) && (x & 1))
{
DrawPlane((Vector3) { x * tileSize, 0.f, y * tileSize},
(Vector2) {tileSize, tileSize}, tileColor1);
}
else if(!(y & 1) && !(x & 1))
{
DrawPlane((Vector3) { x * tileSize, 0.f, y * tileSize},
(Vector2) {tileSize, tileSize}, LIGHTGRAY);
}
}
}
const Vector3 towerSize = (Vector3){ 16.f, 32.f, 16.f };
const Color towerColor = (Color){ 150, 200, 200, 255 };
Vector3 towerPos = (Vector3){ 16.f, 16.f, 16.f };
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
towerPos.x *= -1;
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
towerPos.z *= -1;
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
towerPos.x *= -1;
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
// Red sun
DrawSphere((Vector3) { 300.f, 300.f, 0.f }, 100.f, (Color) { 255, 0, 0, 255 });
}

BIN
examples/core/core_3d_fps_controller.png Переглянути файл

Перед Після
Ширина: 800  |  Висота: 450  |  Розмір: 8.9 KiB

BIN
examples/core/resources/huh_jump.wav Переглянути файл


Завантаження…
Відмінити
Зберегти