Skip to content

About

Top down shooter made with SFML

Resources

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

Phantom Force

A game built with the SFML library

CI

Build

For Linux and Mac, the CMake config does not automatically install sfml and assumes the libraries already exist on the system. See below to install sfml:

Prerequisites

Linux

# May need to run to fetch latest packages
sudo apt-get update
sudo apt-get install libsfml-dev

MacOS

# May need to run to fetch latest packages
brew update
brew install sfml

Windows

Windows does not need to install sfml as a prerequisite like Linux and MacOS do. Instead, the CMake config will download sfml using the FetchContent module.

if(NOT SFML_FOUND)
message("SFML not found, downloading...")
include(FetchContent)
FetchContent_Declare(SFML
GIT_REPOSITORY https://github.com/SFML/SFML.git
GIT_TAG 2.6.x
)
FetchContent_MakeAvailable(SFML)
endif()

CMake

For a single-configuration generator (typically the case on Linux and macOS):

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

For a multi-configuration generator (typically the case on Windows):

cmake -S . -B build
cmake --build build --config Release

Run

cd build
./PhantomForce

Move with WASD, aim with the mouse, Esc pauses and Space toggles the debug overlay: collision shapes, contact points and normals, plus a performance read-out (FPS, frame time, physics time per frame and per step, body / pair / test / contact / resting counts and draw calls).

Test

The physics engine has a unit test suite that runs without a window:

cmake --build build
ctest --test-dir build --output-on-failure

Pass -DPHANTOMFORCE_BUILD_TESTS=OFF when configuring to skip building it.

physics_bench (also run by ctest) prints the average and worst step time of a few scenarios: hundreds to a thousand bodies bouncing around an arena, a crate pyramid settling under gravity, and a thousand crates at rest:

./build/physics_bench

Physics

Gameplay objects are simulated by a small 2D rigid-body engine in include/physics/:

  • physics::Body – a circle or (rotatable) box with mass (or density, so bigger objects of a material are heavier), velocity, restitution (bounciness), friction, linear damping and a force accumulator, plus angular velocity, moment of inertia, torque and angular damping. A mass of 0 makes a body static; a static body with a velocity acts as a kinematic mover. setFixedRotation(true) keeps collisions from spinning a body (the player uses this so it can keep facing the mouse).
  • physics::PhysicsWorld – holds bodies (not owned) and advances them with step(dt): a sort-and-sweep broad phase on bounding boxes, exact circle/box narrow-phase tests (separating-axis test with face clipping for rotated boxes) that produce contact points, iterative impulse resolution at those points with restitution and Coulomb friction, then positional correction so bodies do not sink into each other. Hits away from the centre of mass spin bodies, friction makes balls roll, and linear and angular momentum are conserved. Optional gravity and a per-contact callback are available for game logic. Without gravity, pairs of bodies that have been sitting still are skipped by the broad phase, so a scene full of resting objects costs almost nothing; getStats() reports the work done by the last step.
  • physics::Surface – the floor under a body in a top-down world: Coulomb friction (a constant deceleration, so ice gives long slides and concrete stops things fast), drag (extra decay for sand or mud) and grip (how much traction the player gets). Presets: none, metal, concrete, ice, sand. The world samples the surface under each body every step through setSurfaceSampler; the game reads the background tile under each body (ice and sand tiles live in assets/background.png). Bodies that are not on the floor (projectiles) opt out with setOnGround(false).
  • Object (and its Circle / Rectangle subclasses) is an sf::Sprite that is also a physics::Body; the sprite's position and rotation are the body's position and angle. Rectangle(texture, size) tiles the texture over the collider, so a wall of crates is one repeated crate texture. Player is a dynamic Circle that turns input into acceleration.

Positions are in pixels, angles in radians (clockwise on screen) and time in seconds. The game steps the world on a fixed 120 Hz timestep independent of the render frame rate.

physics::PhysicsWorld world;          // no gravity: top-down game
world.setDefaultSurface(physics::Surface::metal());
world.setSurfaceSampler([&](sf::Vector2f p) {
  return tile_map.getTile(p) == kIceTile ? physics::Surface::ice()
                                         : world.getDefaultSurface();
});
Rectangle* wall = new Rectangle(tex, {40.f, 400.f});   // mass 0 => static
Circle* ball = new Circle(tex, 20.f);
ball->setMass(3.f);
ball->setRestitution(0.6f);
world.addBody(wall);
world.addBody(ball);
world.step(1.f / 120.f);

License

This project is licensed under the MIT License - see the LICENSE.md

About

Top down shooter made with SFML

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages