From a968e8ca749ecc1ae4d1325c26eb4c2751290c82 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sat, 14 Sep 2019 23:39:26 -0700 Subject: [PATCH] writing about transforms --- content/_index.md | 4 +++- content/broad_phase/_index.md | 6 +++--- content/narrow_phase/EPA2D.md | 24 ++++++++++++++++++++++++ content/narrow_phase/GJK2D.md | 20 ++++++++++++++++++++ content/narrow_phase/_index.md | 13 +++++++++++++ content/narrow_phase/overview.md | 7 +++++++ content/shapes/_index.md | 6 +++--- content/shapes/overview.md | 10 +++++----- content/transform/Position2D.md | 31 +++++++++++++++++++++++++++++++ content/transform/Transform2D.md | 13 +++++++++++++ content/transform/_index.md | 13 +++++++++++++ content/transform/overview.md | 14 ++++++++++++++ 12 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 content/narrow_phase/EPA2D.md create mode 100644 content/narrow_phase/GJK2D.md create mode 100644 content/narrow_phase/_index.md create mode 100644 content/narrow_phase/overview.md create mode 100644 content/transform/Position2D.md create mode 100644 content/transform/Transform2D.md create mode 100644 content/transform/_index.md create mode 100644 content/transform/overview.md diff --git a/content/_index.md b/content/_index.md index 83e5714..1adc916 100644 --- a/content/_index.md +++ b/content/_index.md @@ -10,6 +10,8 @@ Bonk **is** designed to help you figure out if two shapes are overlapping and by Bonk **is not** a physics simulator and it will not help you execute collision responses. +Bonk is designed for performance and memory efficiency. Defining shapes and performing collision tests require no heap allocations and thus put no pressure on the garbage collector. If you reuse spatial hashes, Bonk will never cause garbage collection. + Bonk is available as a [NuGet package](https://www.nuget.org/packages/MoonTools.Core.Bonk/). -Bonk is licensed under the [LGPL-3](https://www.gnu.org/licenses/lgpl-3.0.en.html). In summary: feel free to include it in your closed-source game and modify it internally at will, but if you make changes that you intend to redistribute, you **must** freely publish your changes. \ No newline at end of file +Bonk is licensed under the [LGPL-3](https://www.gnu.org/licenses/lgpl-3.0.en.html) license. In summary: feel free to include it in your closed-source game and modify it internally at will, but if you make changes that you intend to redistribute, you **must** freely publish your changes. \ No newline at end of file diff --git a/content/broad_phase/_index.md b/content/broad_phase/_index.md index e67589e..2040d8e 100644 --- a/content/broad_phase/_index.md +++ b/content/broad_phase/_index.md @@ -1,12 +1,12 @@ +++ title = "Broad Phase" date = 2019-09-14T18:33:15-07:00 -weight = 15 +weight = 20 chapter = true -pre = "3. " +pre = "4. " +++ -### Chapter 3 +### Chapter 4 # Broad Phase diff --git a/content/narrow_phase/EPA2D.md b/content/narrow_phase/EPA2D.md new file mode 100644 index 0000000..4aa2bdf --- /dev/null +++ b/content/narrow_phase/EPA2D.md @@ -0,0 +1,24 @@ +--- +title: "EPA2D" +date: 2019-09-14T22:54:35-07:00 +weight: 20 +--- + +If an overlap has occurred, often we wish to determine the minimum separating vector, which is the shortest possible vector by which one of the shapes could be moved in order for them to stop overlapping. For this purpose, Bonk implements the Expanding Polytope Algorithm. + +To use this feature, you must pass the shapes and transforms as well as the simplex that is returned from the *GJK2D.TestCollision* method. + +```cs +var squareA = new Rectangle(-1, -1, 1, 1); +var transformA = Transform2D.DefaultTransform; +var squareB = new Rectangle(-1, -1, 1, 1); +var transformB = new Transform2D(new Vector2(1.5f, 0)); + +var (result, simplex) = GJK2D.TestCollision(squareA, transformA, squareB, transformB); + +if (result) +{ + var intersection = EPA2D.Intersect(squareA, transformA, squareB, transformB, simplex); +} +``` + diff --git a/content/narrow_phase/GJK2D.md b/content/narrow_phase/GJK2D.md new file mode 100644 index 0000000..3df61b9 --- /dev/null +++ b/content/narrow_phase/GJK2D.md @@ -0,0 +1,20 @@ +--- +title: "GJK2D" +date: 2019-09-14T22:47:55-07:00 +weight: 10 +--- + +Bonk uses the Gilbert-Johnson-Keerthi, or GJK, algorithm to perform narrow phase collision detection. + +To accurately check a collision, you must pass two sets of shapes and transforms. Remember that a transform operates on the vertices of a shape: it moves, rotates, and scales them in 2D space. + +```cs +var circleA = new Circle(2); +var transformA = new Transform2D(new Vector2(-1, -1)); +var circleB = new Circle(2); +var transformB = new Transform2D(new Vector2(1, 1)); + +var (result, simplex) = GJK2D.TestCollision(circleA, transformA, circleB, transformB); +``` + +Note that this method returns two variables. The first is whether an overlap has occurred. The second is the simplex. You can think of the simplex as a key that helps you find the minimum separating vector. Note that if there was no collision, the value of the simplex is irrelevant. \ No newline at end of file diff --git a/content/narrow_phase/_index.md b/content/narrow_phase/_index.md new file mode 100644 index 0000000..b2e314e --- /dev/null +++ b/content/narrow_phase/_index.md @@ -0,0 +1,13 @@ ++++ +title = "Narrow Phase" +date = 2019-09-14T22:10:17-07:00 +weight = 40 +chapter = true +pre = "5. " ++++ + +### Chapter 5 + +# Narrow Phase + +Slow and precise. \ No newline at end of file diff --git a/content/narrow_phase/overview.md b/content/narrow_phase/overview.md new file mode 100644 index 0000000..fc5e5a3 --- /dev/null +++ b/content/narrow_phase/overview.md @@ -0,0 +1,7 @@ +--- +title: "Overview" +date: 2019-09-14T22:11:27-07:00 +weight: 5 +--- + +In the broad phase, we were looking for speed. Now, once we have obtained a list of potential collisions, we want to know if these shapes are actually colliding or not. We are looking for accuracy. \ No newline at end of file diff --git a/content/shapes/_index.md b/content/shapes/_index.md index 257a368..344b41b 100644 --- a/content/shapes/_index.md +++ b/content/shapes/_index.md @@ -1,12 +1,12 @@ +++ title = "Shapes" date = 2019-09-14T19:08:45-07:00 -weight = 10 +weight = 15 chapter = true -pre = "2. " +pre = "3. " +++ -### Chapter 2 +### Chapter 3 # Shapes diff --git a/content/shapes/overview.md b/content/shapes/overview.md index 2baaa17..915a2be 100644 --- a/content/shapes/overview.md +++ b/content/shapes/overview.md @@ -4,14 +4,14 @@ date: 2019-09-14T19:19:03-07:00 weight: 5 --- -In Bonk, an **IShape2D** is defined by two methods. +In Bonk, an *IShape2D* is defined by two methods. -1) A *support function*, which is a method that returns the farthest possible point in the shape in a given **Vector2** direction. The shape vertices are transformed by a **Transform2D**. +1) A *support function*, which is a method that returns the farthest possible point in the shape in a given *Vector2* direction. The shape vertices are transformed by a *Transform2D*. -2) A method for creating an axis-aligned bounding box from the shape that takes a **Transform2D** to transform the shape vertices. +2) A method for creating an axis-aligned bounding box from the shape that takes a *Transform2D* to transform the shape vertices. -Bonk provides you with tested implementations of four common 2D shapes, but you can implement these methods to create custom **IShape2D** structs. +Bonk provides you with tested implementations of four common 2D shapes, but you can implement these methods to create custom *IShape2D* structs. -Note that shapes themselves do not contain absolute position data. You will use a **Transform2D** to manipulate the shape's position, rotation, and scale in 2D space. +Note that shapes themselves do not contain absolute position data. You will use a *Transform2D* to manipulate the shape's position, rotation, and scale in 2D space. A final restriction to note: Defining concave shapes is an error. The collision solving algorithm that Bonk uses does not support concave shapes. If you wish to define concave shapes, define them as a composition of multiple convex shapes. \ No newline at end of file diff --git a/content/transform/Position2D.md b/content/transform/Position2D.md new file mode 100644 index 0000000..fb5dd76 --- /dev/null +++ b/content/transform/Position2D.md @@ -0,0 +1,31 @@ +--- +title: "Position2D" +date: 2019-09-14T23:17:46-07:00 +weight: 10 +--- + +In 2D games, when it comes to rendering we are generally dealing with pixels. This presents us with certain challenges that don't exist in 3D. + +For example - what is half of a pixel? The pixel is the smallest possible unit in 2D rendering, so fractions of pixels don't really exist. + +This can present us with problems when it comes to game physics. If your character is at coordinates (2.5, 3.5), where do you draw the character? Is their sprite rounded up, or down? It might look strange if the rounding does not line up with your physics system. + +To avoid these problems, I created the *Position2D* struct. *Position2D* maintains an integer X and Y position, as well as X and Y fractional carryover. Whenever numbers with fractional values are added to the position, the fractional values are added to the carryover. Once the carryover value reaches 1, it is added to the coordinate. This means that we can treat positions as integer values without sacrificing precision. + +This means that in our physics calculations, we will never have discrepancies between internal game position logic and rendering. + +```cs +var one = new Position2D(1.3f, 3.5f); +var two = new Position2D(4.8f, 0.9f); + +var result = one + two; // result.X is 6, result.Y is 4 +``` + +When we subtract *Position2D* vectors, this creates a *Vector2* representing motion, and the carryover values are used. + +```cs +var one = new Position2D(1.3f, 3.5f); +var two = new Position2D(4.8f, 0.9f); + +var result = one - two; // result.X is -3.5f, result.Y is 2.6f +``` \ No newline at end of file diff --git a/content/transform/Transform2D.md b/content/transform/Transform2D.md new file mode 100644 index 0000000..61e3da2 --- /dev/null +++ b/content/transform/Transform2D.md @@ -0,0 +1,13 @@ +--- +title: "Transform2D" +date: 2019-09-14T23:30:26-07:00 +weight: 20 +--- + +To create a *Transform2D*, you must provide a *Position2D* or a *Vector2* for translation (*Vector2* is converted to *Position2D* internally), a float representing rotation in degrees, and a *Vector2* representing scale. + +```cs +var transform = new Transform2D(new Position2D(4, 1), 5f, new Vector2(3, 1)); +``` + +The following section will describe how to use Shapes in conjunction with Transforms to perform collision detection. \ No newline at end of file diff --git a/content/transform/_index.md b/content/transform/_index.md new file mode 100644 index 0000000..25c8238 --- /dev/null +++ b/content/transform/_index.md @@ -0,0 +1,13 @@ ++++ +title = "Transform" +date = 2019-09-14T23:05:37-07:00 +weight = 10 +chapter = true +pre = "2. " ++++ + +### Chapter 2 + +# Transform + +Mathematical operations in disguise. \ No newline at end of file diff --git a/content/transform/overview.md b/content/transform/overview.md new file mode 100644 index 0000000..c1da1be --- /dev/null +++ b/content/transform/overview.md @@ -0,0 +1,14 @@ +--- +title: "Overview" +date: 2019-09-14T23:08:31-07:00 +weight: 5 +--- + +Bonk uses the *Transform2D* struct which is taken from the MoonTools.Core.Structs package. By extension it also uses the *Position2D* struct taken from the same package. + +A *Transform2D* is basically a way to store information about an object's location, rotation, and scale in 2-dimensional space. Transforms use matrix math to perform all of these operations at once, making them very fast. + +For memory performance purposes, shapes are implemented as structs, meaning they are value types. This means that it is much easier to deal with collision using a transform applied to a shape, rather than containing a transform within the shape. + +It is also often the case that you will have a shape attached to a game object that is offset from the object by a certain amount. A *Transform2D* can represent this information as well, since *Tranform2Ds* can be composed together. As you can see, a *Transform2D* provides a generic structure for dealing with many scenarios involving 2D space. +