Git Product home page Git Product logo

sharpmath2's Introduction

SharpMath2

banner

This is a C# math library. It is built as the bare minimum to get up and running with a 2D game in C#. It is compatible with or without monogame. To compile with monogame, use the compiler directive "NOT_MONOGAME". This will provide versions of Microsoft.XNA.Framework.Vector2 and Microsoft.XNA.Framework.Point that do not require any external libraries (with reduced functionality).

Examples

Import tags

using SharpMath2;
using Microsoft.XNA.Framework;

Polygon construction

var triangle1 = new Polygon2(new[] { new Vector2(0, 0), new Vector2(1, 1), new Vector2(2, 0) });
var triangle2 = ShapeUtils.CreateCircle(1, segments=3); // this is not the same triangle as triangle1, this will be equilateral
var octogon = ShapeUtils.CreateCircle(1, segments=8);

Polygon intersection

// Check intersection of two entities, both of which have the same triangle bounds but one is
// rotated at rotation1 and located at position1, where the other is rotated at rotation2 and 
// located at position2

var triangle = new Polygon2(new[] { new Vector2(0, 0), new Vector2(1, 1), new Vector2(2, 0) });

// Rotation2 caches Math.Sin and Math.Cos of the given angle, so if you know you are going to reuse
// rotations often (like 0) they should be cached (Rotation2.Zero is provided)
var rotation1 = Rotation2.Zero;
var rotation2 = Rotation2.Zero; // new Rotation2((float)(Math.PI / 6)) would be 30degrees
var position1 = new Vector2(5, 3);
var position2 = new Vector2(6, 3);

// Determine if the polygons overlap or touch: 
Polygon2.Intersects(triangle, triangle, position1, position2, rotation1, rotation2, false); // True

// Determine if the polygons overlap
Polygon2.Intersects(triangle, triangle, position1, position2, rotation1, rotation2, true); // False

// Note that in the special case of no rotation (rotation1 == rotation2 == Rotation2.Zero) we can
// use the shorter function definition by omitting the rotation parameters
Polygon2.Intersects(triangle, triangle, position1, position2, true); // False

Polygon collision detection + handling

// Suppose we have two entities, entity1 and entity2, both of which use the polygon "triangle" and are at position1, rotation1 and 
// position2, rotation2 respectively. If we are updating entity1 and we want to detect and handle collision with entity2 we would
// do:

// note we do not check for intersection first - while intersection is faster to check than intersection + MTV, it is not 
// faster to check intersection then intersection + MTV if you will need the MTV.

// Note strict is not an option for MTV - if two triangles are touching but not overlapping then
// it doesn't make sense to try and get an MTV
Tuple<Vector2, float> mtv = Polygon2.IntersectMTV(triangle, triangle, position1, position2, rotation1, rotation2);
if(mtv != null)
{
  // The two entites are colliding.
  position1 += mtv.Item1 * mtv.Item2;
  
  // Polygon2.Intersects(triangle, triangle, position1, position2, rotation1, rotation2, true); -> False
  // Polygon2.Intersects(triangle, triangle, position1, position2, rotation1, rotation2, false); -> True
}

Polygon -> AABB collision

It is very common to need to check polygons against unrotated rectangles in square-grid systems. In this case there are functions in Shape2 that provide these comparisons that is slightly faster than complete polygon to polygon collision that you would get from ShapeUtils.CreateRectangle(width, height) rather than new Rect(minx, miny, maxx, maxy)

var triangle = ShapeUtils.CreateCircle(1, segments=3);
var tile = new Rect2(0, 0, 1, 1); // minX, minY, maxX, maxY NOT x, y, w, h.

var triPos = new Vector2(3.3, 4.1);
var triRot = new Rotation2((float)(Math.PI / 6));

Vector2 tmp = Vector2.Zero; // Vector2 is a struct so this is safe
int xMin = (int)triPos.x;
int xMax = (int)Math.Ceiling(triPos.x + triangle.LongestAxisLength);
int yMin = (int)triPos.y;
int yMax = (int)Math.Ceiling(triPos.y + triangle.LongestAxisLength);
for(int y = yMin; y <= yMax; y++)
{
  tmp.Y = y;
  for(int x = xMin; x <= xMax; x++) 
  {
     tmp.X = x;
     var intersectsTileAtXY = Shape2.Intersects(triangle, tile, triPos, tmp, triRot, true);
     Console.Write($"({x},{y})={intersectsTileAtXY}")
     if(intersectsTileAtXY)
       Console.Write("  "); // true is 1 letter shorter than false
     else
       Console.Write(" ");
  }
  Console.WriteLine();
}

Polygon AABB checking

Note that this is only faster for fairly complicated polygons (theoretical breakeven at 6 unique normals each). Further note that it's almost never faster for rotated polygons - finding the AABB for rotated polygons is not supported (though not complicated).

The provided AABB is most often used in UI elements which do not anticipate rotation and can have somewhat complicated polygons but don't have rotation, which is where AABBs shine.

var complicatedShape = ShapeUtils.CreateCircle(5); // radius 5, 32 segments

// Note we are not providing rotation - rect2 does not support rotation 
// (use ShapeUtils.CreateRectangle for that, which returns a Polygon2)
Rect2.Intersects(complicatedShape.AABB, complicatedShape.AABB, Vector2.Zero, new Vector2(3, 0), true); // True

Circles

Circles have similiar functions to polygons. The only thing to note is that all API functions will use the top-left of the bounding box of the circle for the circles position, rather than the center of the circle. This makes switching things between circles and polygons easier in return for a very small performance cost.

var circle = new Circle2(3); // The only argument is the radius of the circle.
var anotherCircle = new Circle2(5); 
var triangle = ShapeUtils.CreateCircle(2, segments=3); 

// Circle -> Circle collision using the same underlying circle object
Circle2.Intersects(circle, circle, Vector2.Zero, new Vector(1, 0), true); // True

// Circle -> Circle collision can be done using just the radius
Circle2.Intersects(3.0f, 3.0f, Vector2.Zero, new Vector2(1, 0), true); // Identical to above

// Circle -> Polygon collision must pass in a circle, not the radius of the circle
Shape2.Intersects(circle, triangle, Vector2.Zero, new Vector2(1, 1), true); // True

// Circle -> AABB collision
Shape2.Intersects(circle, triangle.AABB, Vector2.Zero, new Vector2(10, 0), true); // False

Performance notes

This library is designed for when:

  1. You have only a few different polygon types
  2. You need to check collision on those polygon types when they are in rapidly changing positions and rotations.

For example in a 2D game where everything is either a triangle or hexagon, in this library you would only need to construct two polygons, then reuse those two polygons everywhere else. This allows the library to cache certain operations.

The library is designed such that changing rotations or position is fast, but the downside is when rotation or position does not change there is only a minor improvement in performance.

sharpmath2's People

Contributors

tjstretchalot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

sharpmath2's Issues

Request for a scale function on the shape

Hey there!

Is it possible we can have an argument for scale in the Intersects function? If not, what would be your recommendation about changing the scales of shapes? Just create a new shape for each scale or?

With friendly regards.

About rotation and a request.

Hey there,

This library is bloody amazing. Easily implemented it (I already had a polygon/shape class ready so it really was a surprise I didn't have to change much!)

I have a two requests - one of them is that if you could make it possible I could just reset the Rotation2 cos/sin instead of making a new object every time (and the same would go for a Polygon2.)

Also I'm wondering how I'd change the rotation based on the example collision between polygons (I probably missed something really obvious.)

`
Tuple<Vector2, float> mtv;
///


/// Upon collision with another quad object
///

/// The object we've colldied with.
public virtual void OnCollision(QuadObject @object)
{
mtv = Polygon2.IntersectMTV(
Shape.CollisionBox, @object.Shape.CollisionBox, Position, @object.Position, Shape.CollisionBoxRotation, @object.Shape.CollisionBoxRotation);

        if (mtv != null)
        {
            Position += mtv.Item1 * mtv.Item2;
            // Rotation += ?
        }

    }`

As for re-using the existing polygon, I mean something along the lines of:
/// <summary> /// The shape of the object. /// </summary> private Vector2[] Points { get { return _Points; } set { OrderClockwise(ref _Points); CollisionBox = new Polygon2(_Points); // CollisionBox.SetPoints(_Points); } }

Buggy collision upon going left, or up.

Hey there!

It's me again,

There seems to be an issue when colliding with numerous objects going left, and up. I've re-written my code a few times, even tried it without the quad tree but the problem persists. I'm unsure at this point where the problem is so I'm hoping you could shed some light on it, as I'm not very familiar with your code.

da6da46c462dd04fd8cddc4a2c13f678

I first thought it had to do with the quad tree (as the tree wasn't dynamically expanding), but it then turns out that by just pushing one block at a time there was no issue. This issue does not exist for moving to the right or the bottom so I'm assuming it's a form of rounding error.

I've included a copy of my source in case you need it.
Engine.zip

Update: I've adjusted the following zip to exclude quad trees, and strangely enough - now it does somewhat work better, but it seems to just pick and choose which ones to push away and which ones not to. Is the pushing away suppose to be happening? I know it happens because I also check collisions for the non player-boxes, but still. It seems to pick and choose.

80bec15aacfe79e95fa051a427cef348

I got a feeling it's me not understanding the library that is the issue.
Engine No Trees.zip

Polygon-polygon minimum translation vector appears invalid

Calling IntersectMTV with two rotated polygons leads to strange behaviour:

  • MTV is not normalized
  • MTV orientation may be incorrect depending on the orientation of the collision
  • Depending on orientation of intersection, the MTV only gets calculated if poly2 completely contains poly1, or at least it appears the center point of poly1 must be contained by poly2

https://imgur.com/a/CSnDjWi

Let me know what more information you need, if any. I'm using a modified version of 4ed1885 adapted to use Unity.Mathematics types and math functions. I'm also avoiding allocations by reusing cached objects rather than constructing new ones, but other than that, the code should be the same.

Circle2 IntersectMTV not working correctly

Hello guys,

I dont know whether someone will read this, but could you please fix the IntersectMTV-method of the Circle2 class? It does not give the right results if the circles overlap. The Intersects method is broken too, if there are two circles with different radii.

Thank you :)

---Edit: Circle and Polygon is broken too :(---
Edit2: I dont have see that there a two methods.. Circle-Polygon and Polygon-Circle :(

The line-class has a bug for calculating the Horizontal and Vertical

That fixed it... buts still broken on the resolving of the collisions
var k = Math.Abs(this.End.Y - this.Start.Y) / Math.Abs(this.End.X - this.Start.X);
this.Horizontal = k == 0;
this.Vertical = float.IsInfinity(k);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.