Skip to content

Commit

Permalink
Add Box2D (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
jthomperoo committed Dec 1, 2021
1 parent bf7ed84 commit aad6463
Show file tree
Hide file tree
Showing 82 changed files with 1,770 additions and 204 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Steps to reproduce the behavior:
A clear and concise description of what you expected to happen.

**Version:**
JamJarNative version.
JamJar version.

**Additional context**
Add any other context about the problem here.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ Versioning](https://semver.org/spec/v2.0.0.html).
- Modular design, choose to remove parts of the engine and replace parts.
- Customisable render pipeline.
- Rendering to a WebGL2 canvas.
- Simple motion based physics.
- Interpolation between frames.
- Orthographic camera projection.
- Sprite rendering.
- Image loading from filesystem (virtual or otherwise).
- Input using SDL2 events (keyboard and mouse).
- Box2D based Physics system.
- Collision detection using Box2D.

[Unreleased]: https://github.com/jamjarlabs/JamJarNative
[Unreleased]: https://github.com/jamjarlabs/JamJar
42 changes: 30 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.14)

set (CMAKE_CXX_STANDARD 17)

project(JamJarNative VERSION 0.0.0)
project(JamJar VERSION 0.0.0)

option(JAMJAR_NATIVE_COMPILE_UNIT_TESTS "Compile JamJarNative unit tests" OFF)
option(JAMJAR_NATIVE_COMPILE_UNIT_TESTS "Compile JamJar unit tests" OFF)

### ===== dependencies ===== ###

Expand All @@ -21,6 +21,18 @@ FetchContent_MakeAvailable(stb_image)
add_library(stb_image INTERFACE)
target_include_directories(stb_image INTERFACE ${stb_image_SOURCE_DIR})

set(BOX2D_BUILD_DOCS OFF CACHE BOOL "Build Box2D docs")
set(BOX2D_BUILD_UNIT_TESTS OFF CACHE BOOL "Build Box2D unit tests")
set(BOX2D_BUILD_TESTBED OFF CACHE BOOL "Build Box2D testbed")

FetchContent_Declare(
box2d
GIT_REPOSITORY https://github.com/erincatto/Box2D.git
GIT_TAG v2.4.1
)

FetchContent_MakeAvailable(box2d)

if(JAMJAR_NATIVE_COMPILE_UNIT_TESTS)
FetchContent_Declare(
googletest
Expand All @@ -39,7 +51,9 @@ endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

set(CMAKE_CXX_FLAGS "-s USE_SDL=2 -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2")
if (EMSCRIPTEN)
set(CMAKE_CXX_FLAGS "-s USE_SDL=2 -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2")
endif()

set(LIB_SOURCES
src/stb_image.cpp
Expand All @@ -62,10 +76,10 @@ set(LIB_SOURCES
src/system/stateful_system.cpp
src/system/system_entity.cpp
src/system/system.cpp
src/standard/2d/box2d/box2d_body.cpp
src/standard/2d/box2d/box2d_physics_system.cpp
src/standard/2d/camera/camera.cpp
src/standard/2d/interpolation/interpolation_system.cpp
src/standard/2d/motion/motion_system.cpp
src/standard/2d/motion/motion.cpp
src/standard/2d/primitive/primitive_system.cpp
src/standard/2d/primitive/primitive.cpp
src/standard/2d/primitive/webgl2_default_primitive_shaders.cpp
Expand All @@ -84,13 +98,14 @@ set(TEST_SOURCES
src/game_test.cpp
)

add_library(JamJarNative
add_library(JamJar
${LIB_SOURCES}
)

target_link_libraries(JamJarNative PRIVATE stb_image)
target_link_libraries(JamJar PRIVATE stb_image)
target_link_libraries(JamJar PRIVATE box2d)

target_include_directories(JamJarNative
target_include_directories(JamJar
PUBLIC
include
PRIVATE
Expand All @@ -100,22 +115,25 @@ target_include_directories(JamJarNative
if(JAMJAR_NATIVE_COMPILE_UNIT_TESTS)
# Unit tests
add_executable(
JamJarNativeTests
JamJarTests
${TEST_SOURCES}
)

target_link_libraries(
JamJarNativeTests
JamJarNative
JamJarTests
JamJar
gtest_main
)

include(GoogleTest)
gtest_discover_tests(JamJarNativeTests)
gtest_discover_tests(JamJarTests)
endif()

add_subdirectory (examples/Collision)
add_subdirectory (examples/Sprites)
add_subdirectory (examples/Primitives)
add_subdirectory (examples/Physics)
add_subdirectory (examples/PhysicsResizing)

### ===== clang-format ===== ###

Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing

Any contributions to the JamJarNative game engine are welcome, please review our [Code of Conduct](./CODE_OF_CONDUCT.md)
Any contributions to the JamJar game engine are welcome, please review our [Code of Conduct](./CODE_OF_CONDUCT.md)
before making a contribution.

## Raising an Issue
Expand All @@ -17,7 +17,7 @@ Please use the feature request template for requesting a new feature.

### Requesting Help

Please use the [discussions](https://github.com/jamjarlabs/JamJarNative/discussions) to raise any requests for help or
Please use the [discussions](https://github.com/jamjarlabs/JamJar/discussions) to raise any requests for help or
general queries.

## Developing
Expand Down Expand Up @@ -67,7 +67,7 @@ This command will generate some build files in the `build/` directory, inside wh
emmake make
```

This will generate the `build/libJamJarNative.a` library archive which can be used when building game executables.
This will generate the `build/libJamJar.a` library archive which can be used when building game executables.

##### Running unit tests

Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# JamJarNative
# JamJar

JamJarNative is a 2D, C++ game engine that primarily targets running in the browser through web assembly.
JamJar is a 2D, C++ game engine that primarily targets running in the browser through web assembly.

## Features

Expand All @@ -9,12 +9,13 @@ JamJarNative is a 2D, C++ game engine that primarily targets running in the brow
- Modular design, choose to remove parts of the engine and replace parts.
- Customisable render pipeline.
- Rendering to a WebGL2 canvas.
- Simple motion based physics.
- Interpolation between frames.
- Orthographic camera projection.
- Sprites.
- Image loading from filesystem (virtual or otherwise).
- Input using SDL2 events (keyboard and mouse).
- Box2D based Physics system.
- Collision detection using Box2D.

## Getting Started

Expand All @@ -27,7 +28,8 @@ See the [examples directory for working code examples](./examples).
## Dependencies

- [STB](https://github.com/nothings/stb) - MIT/Public domain
- [SDL2](https://github.com/libsdl-org/SDL) (v2.0.9) - ZLib
- [SDL2](https://github.com/libsdl-org/SDL) - ZLib
- [Box2D](https://github.com/erincatto/box2d) - MIT

**Dev Dependencies**

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ECS

JamJarNative uses an *Entity Component System* (ECS) design at its core, this pattern drives much of the
JamJar uses an *Entity Component System* (ECS) design at its core, this pattern drives much of the
architecture of the engine.

## Overview
Expand All @@ -10,13 +10,12 @@ three components should be able to cover any functionality that a game needs.

An entity is a single *thing* in the game, for example a player, a bullet, or a camera.

A component is a piece of data that is attached to an entity, for example position (Transform), velocity and
acceleration (Motion), or the player's number of lives.
A component is a piece of data that is attached to an entity, for example position (Transform), an image (Sprite), or
the player's number of lives.

A system is some logic that can operate on entities and components to interpret them and modify them, for example
the MotionSystem manages entities that have a Transform and a Motion, adjusting the values of the data stored in
them based on physics equations, or the SpriteSystem which takes Sprite descriptions and converts them into a format
that the render pipeline can use to draw shapes.
the SpriteSystem which takes Sprite descriptions and converts them into a format that the render pipeline can use to
draw shapes.

## Benefits

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,26 @@ must return `true` if it should be tracked as a system entity, or `false` if it
An example of an evaluator looks like this:

```c++
bool JamJar::Standard::_2D::MotionSystem::evaluator(Entity *entity, std::vector<JamJar::Component *> components) {
bool hasMotion = false;
bool JamJar::Standard::_2D::SpriteSystem::evaluator(Entity *entity,
const std::vector<JamJar::Component *> &components) {
bool hasSprite = false;
bool hasTransform = false;
for (const auto &component : components) {
if (component->m_key == JamJar::Standard::_2D::Motion::KEY) {
hasMotion = true;
} else if (component->m_key == JamJar::Standard::_2D::Transform::KEY) {
if (component->key == JamJar::Standard::_2D::Sprite::KEY) {
hasSprite = true;
}
if (component->key == JamJar::Standard::_2D::Transform::KEY) {
hasTransform = true;
}

if (hasMotion && hasTransform) {
if (hasSprite && hasTransform) {
return true;
}
}
return false;
}
```
This evaluator is used in the MotionSystem to make sure that only components that have a Motion component and a
This evaluator is used in the SpriteSystem to make sure that only components that have a Sprite component and a
Transform component are tracked.
## System Entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ A system is simply a piece of logic that runs.
## Logic

The description above is pretty vague, but this is because a system is not constrained in what it can and can't do,
it can operate on entities and components (for example the MotionSystem, which updates position and motion data every
update), or it can operate without any entities and components, sending and receiving messages (for example a system
it can operate on entities and components (for example the SpriteSystem, which generates renderables from Sprites and
Transforms), or it can operate without any entities and components, sending and receiving messages (for example a system
that stops the game when the player is destroyed). These systems are Stateless Systems (does not track entities or
components) and Stateful Systems (tracks entities and components).

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Messages

JamJarNative extensively uses *messages* to send information between different parts of the engine. This allows for
JamJar extensively uses *messages* to send information between different parts of the engine. This allows for
different parts of the system to be decoupled and not directly depend on eachother. The pattern that messages use
is broadcast and listen - a message is broadcast, and parts of the engine can *subscribe* to message types to listen
for. This means a single message may be sent to multiple listeners, or *subscribers*.
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Game Loop

The game loop in the JamJarNative engine is built following the article ['Fix Your Timestep!' by Glenn
The game loop in the JamJar engine is built following the article ['Fix Your Timestep!' by Glenn
Fiedler](https://gafferongames.com/post/fix_your_timestep/). The article sets out nicely a progression in game loops
before concluding with a recommended game loop. To understand the game loop better, read the article, but the key
points of the approach JamJarNative uses are laid out here:
points of the approach JamJar uses are laid out here:

- Rendering can occur as fast as possible.
- Physics/Game calculations are uncoupled from this render/loop rate.
- Physics/Game updates can catch up if running behind.
- Physics/Game updates will slow down if ahead.

The basic algorithm that JamJarNative uses is:
The basic algorithm that JamJar uses is:

```
timestep = 1/60
Expand Down Expand Up @@ -39,12 +39,12 @@ while(running)

## Timestep

The timestep of JamJarNative is for an update to occur every 1/60th of a second (60 FPS). When running with emscripten
The timestep of JamJar is for an update to occur every 1/60th of a second (60 FPS). When running with emscripten
it will use the [requestAnimationFrame] browser function, which is limited to 60 FPS.

### Interpolation

Because of the game loop used JamJarNative must interpolate as part of the rendering process, this means taking the last
Because of the game loop used JamJar must interpolate as part of the rendering process, this means taking the last
rendered position of an object and the current position of the object and rendering at the halfway between these
two positions.

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ You can see this structure in the headers for the library, it is structured like
- `game.hpp`
- `message/message.hpp`
- `geometry/vector_2d.hpp`
- `standard/motion/motion.hpp`
- `standard/2d/sprite/sprite.hpp`

As you can see anything that is in the `standard/` directory/path is in the standard library.

Expand Down
66 changes: 66 additions & 0 deletions docs/Documentation/Box2D/bodies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Bodies

A Box2D body is represented in JamJar by a `Box2DBody`.

## Using a Box2D Body

A Box2D body can be set up by attaching `Box2DBody` and `Transform` components to an entity.

```c++
#include "entity/entity.hpp"
#include "geometry/vector_2d.hpp"
#include "standard/2d/transform/transform.hpp"
#include "standard/2d/box2d/box2d_body.hpp"
#include "geometry/polygon.hpp"
...
auto body = new JamJar::Entity(messageBus);
body->Add(std::move(
std::make_unique<JamJar::Standard::_2D::Transform>(JamJar::Vector2D(0, 30), JamJar::Vector2D(10, 10))));
body->Add(std::move(std::make_unique<JamJar::Standard::_2D::Box2DBody>(
JamJar::Polygon({-0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, -0.5}),
JamJar::Standard::_2D::Box2DBodyProperties({.density = 1.0f, .angularVelocity = 1.0f}))));
```
This creates a body at position `x: 0, y: 30` with a scale of `x: 10, y: 10`, the shape of the body uses the coordinates
`-0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, -0.5` (this is a square) and some additional properties of the body are set using
the `Box2DBodyProperties` (density is `1.0f`, initial angular velocity is `1.0f`).
### Changing Box2D Body Position/Angle
Using a `Box2DBody` and a `Transform` the `Box2DBody` component takes precdence over the transform for determining
the position, the position in the `Transform` should be treated as read only in this scenario, if you need to update
the position of the entity use the `Box2DBody.SetPosition` function.
```c++
bodyComp->SetPosition(JamJar::Vector2D(3, 2));
```

This sets the entity's position to `x: 3, y:2`.

The same applies to changing the entities angle, use the `Box2DBody.SetAngle` function:

```c++
bodyComp->SetAngle(2);
```
This sets the entity's angle to `2`.
### Scaling a Box2D Body
Due to the way Box2D handles shapes and scaling, you must adjust the entity's scale using the `Box2DBody` component by
using the `Box2DBody.SetScale` function:
```c++
bodyComp->SetScale(JamJar::Vector2D(3, 2));
```

This will adjust the scale to `x: 3, y: 2`.

#### Scaling Behind the Scenes

Box2D does not support scaling an existing body, so when you call `SetScale` this adjusts an extra scaling value, and
then marks the body as ready for regeneration using the `regenerate` property. Then in the next update the
`Box2DPhysicsSystem` will create a copy of the body's fixture before deleting the old fixture and replacing it.

For more information around how Box2D works, check out the [Box2D
documentation](https://box2d.org/documentation/index.html).

0 comments on commit aad6463

Please sign in to comment.