B3DV is a simple, FOSS, 3D game, or prototype of a game, made by
me with the sole purpose of practicing.
I originally started it out of boredom and texturawasd's suggestion, and have continued it since. It is heavily inspired in
early iterations of Minecraft.
It is free & open source (see github repo)
It's written in pure C + Raylib, with few dependencies [see dependencies]; and (for now) features just a basic world the player can walk on, break and place blocks, and save (different worlds)
It is written in C because it's the only language I mostly know how to program in.
Also, since the codebase is not big and is rather simple, it's straightforward to keep it portable across operating systems.[see codebase explanation]
Features:
- Simple world with terrain
- Block placing/destroying
- Saving different worlds
- Simple commands (/tp, etc)
- Simple UI, with translated text to different languages
- Fully free and open source: BSD-3-Clause license
- Simplistic design throughout
Raylib does all the boilerplate work for me, (initialize a window, OpenGL) so I
only implemented the game's logic:
The render loop, the world data handling, the rendering, the optimizations, the ui (menus)
I intend to KISS, hackable and extensible:
You can use any font you want if you put a .ttf file in ./assets/fonts/[name]/ttf/[yourfile.ttf];
Settings are simply stored in options.txt;
The build system is Just A Makefile™;
You can implement your own translation for the UI in any language you like (./assets/text/[lang]/[chat|credits|menu].txt),
as well as textures (./assets/textures/blocks);
Really, you can implement any changes you want to it. (hackable as I said)
Get/update:
You can always `git clone` the repo and build it, but if you want an "actual" release, head over to https://texturawasd.ddns.net/files/b3dv/releases/
To be honest, I may forget to build the windows executable sometimes, but it's guaranteed to be there in the release .zip.
Commands that are usable via chat:
/select [stone|grass|dirt|wood|sand] - Select which kind of block to place
/tp [x y z] - Teleport player to coords X Y Z
/setblock [x y z [block]] - set a block at coords
/fly [enable|disable] - Toggle flight
/noclip [enable|disable] - Toggle noclip
Each world is saved as a folder with a `chunks` folder, the actual chunk data; and a world.txt file, containing data like last played timestamp and player position
The world isn't just an array/grid of blocks, but it is divided into manageable chunks.
It is an infinite 3D grid, but split into small boxes, that are loaded and unloaded dynamically.
Only the chunk that the player is in, and the adjacent ones are 'loaded'
(meaning loaded into memory, so effectively, actually relevant)
and are unloaded once the player is far enough from them.
This is crucial and is what allows infinite worlds in finite systems, just like in Minecraft for instance.
The player has several properties:
- Physics:
Position relative to player's head's X Y Z coords;
Velocity how fast the player is moving;
Gravity: 35 m/s^2;
Jumping: apply upward force
Collision: The player is actually a cilinder, (radius 0.35, height 1.9mts) so they interact (collide) with blocks
- Features:
Flight mode: can fly (no gravity) (enabled with `/fly enable`)
No-clip (no collision) mode: stops interacting with blocks, enabled with `/noclip enable` (especially useful when combined with flight)
Block selection: The player struct has a selected block field, as of now it can only be changed with /select
Sneaking: the player can "sneak" or move slowly and not go down blocks
Sprinting: the player can sprint or move faster than normal walking
The whole shtick:
I've use many optimization techniques to bring the FPS above 30:
Distance culling, a.k.a. a limited render distance
Frustum culling: Only render blocks that are in front of the player, based on FOV; at the block level and chunk level (entire chunks can get skipped before individual blocks)
Back-pane culling: Don't draw back faces of blocks (back face meaning faces that the player can't see anyways)
Occlusion culling, a.k.a. don't draw blocks that are obscured from view anyways
and raycasing is used to see which block the player is going to break/place
Think of the menu system as a state machine with different screens:
- Main menu: the start screen, buttons for entering a world, creating a new one, quit, and credits. Displays a backround picture (ranomized from .png files inside ./assets/mainmenubackground/*.png) and a randomized splash text
- World select screen
- World create screen
- Settings: set FPS limit and render distance, modifying options.txt
And the gameplay, (when there's no menu) and credits screen which is merely text display
The 'menu' is localized (translated to 11*) and the settings are persistent.
The game uses direct input for gameplay, Raylib takes care of that. Yeah that's it.
The main loop which runs every frame, goes from line 200 to 1480, contains the game logic, as the following sequence, roughly:
- Handle input:
└─ Read keyboard & mouse -> update camera angle and process block placement/destruction
- Update player physics
└─ Apply gravity to player, handle jumping/flying, check collisions with blocks, update the player's position
- Update world
└─ Load chunks near the player, unload chunks far from the player, generate terrain for new chunks if needed
- Render
└─ Calculate which chunks to render,
└─ Calculate which blocks to render
└─ Calculate which faces to render
└─ render those, with their respective texture; if the player is pointing at it, highlight it with a wireframe
& draw hud and crosshair on top
It's just a makefile. run `make` and that's it. (linux)
You need raylib. And clang.
If you're on windows: why? go on a linux machine (or use WSL2) and use the "windows" make target. (Just run `make windows`)
clang, Raylib, and C standard library of course.
How to install:
Arch:
sudo pacman -S --needed clang base-devel git raylib
Debian:
sudo apt update && sudo apt install clang build-essential git libraylib-dev
Fedora:
sudo dnf install clang @development-tools git raylib-devel
RHEL:
sudo dnf groupinstall "Development Tools"
sudo dnf install clang git raylib-devel
sudo dnf install epel-release
Alpine:
sudo apk add clang build-base git raylib-dev
Void:
sudo xbps-install -S clang base-devel git raylib-devel
FreeBSD:
sudo pkg install llvm git raylib gmake
Windows:
Just use WSL.
Idea: Leandro Testa ("texturawasd")
update-version.py, organize-screeshots.py: Alina Kravetska
Raylib: Ramon Santamaria (raysan5)
Everything else: me
(BSD-3-Clause lic.)