06-12-2023, 03:16 PM
Over the past few months I've made a few attempts at creating a 2D physics library for QB64 but have failed miserably. My first few attempts were writing something from scratch. I quickly realized that while I have a fair grasp on basic trig and vector math I have nowhere near the knowledge to implement such things as angular momentum, raytracing, and 2D collision physics. Even after trying to tutor myself on the subject I seem to still be just as confused (if not more).
I then decided to follow a few video tutorials I found on Youtube related to creating a 2D physics engine. Of course these are all either meant for C++, Java, or JavaScript using OOP. I figured the process of converting the Java OOP code to functions and procedures would be fairly straight forward ... not so much. (Below I provide a link to the video series I am following along with the code I created so far).
My next thought was to incorporate the Box2D physics engine (the engine that Rovio used to create Angry Birds) into QB64. The Library is written in C++ and I figured by using DECLARE LIBRARY I could get this done. However, my C++ knowledge is lacking as well. Trying to figure out where pointers are used versus variables, their types, and when I need _OFFSETs is just confusing this old thick headed brain of mine. Here is a link to the Box2D physics engine:
https://box2d.org/about/
I feel QB64 needs a 2D physics engine to help attract more users. I know my games would be vastly improved if I had access to something that could create Angry Birds style game physics.
Is anyone with more knowledge on either subject, importing Box2D into QB64, or tutoring and helping me build an engine, willing to help? Box2D also has a light version, Box2D_Lite, that may be a good start if porting the engine is an option.
Below is a link to the video series I was following. I got to the end of Lesson 9 and am totally confused on what the presenter did when introducing the FOR loop.
The video series:
https://www.youtube.com/watch?v=vcgtwY39...iO&index=2
And the code I hacked together so far trying to follow along and convert OOP to QB64 on the fly.
I then decided to follow a few video tutorials I found on Youtube related to creating a 2D physics engine. Of course these are all either meant for C++, Java, or JavaScript using OOP. I figured the process of converting the Java OOP code to functions and procedures would be fairly straight forward ... not so much. (Below I provide a link to the video series I am following along with the code I created so far).
My next thought was to incorporate the Box2D physics engine (the engine that Rovio used to create Angry Birds) into QB64. The Library is written in C++ and I figured by using DECLARE LIBRARY I could get this done. However, my C++ knowledge is lacking as well. Trying to figure out where pointers are used versus variables, their types, and when I need _OFFSETs is just confusing this old thick headed brain of mine. Here is a link to the Box2D physics engine:
https://box2d.org/about/
I feel QB64 needs a 2D physics engine to help attract more users. I know my games would be vastly improved if I had access to something that could create Angry Birds style game physics.
Is anyone with more knowledge on either subject, importing Box2D into QB64, or tutoring and helping me build an engine, willing to help? Box2D also has a light version, Box2D_Lite, that may be a good start if porting the engine is an option.
Below is a link to the video series I was following. I got to the end of Lesson 9 and am totally confused on what the presenter did when introducing the FOR loop.
The video series:
https://www.youtube.com/watch?v=vcgtwY39...iO&index=2
And the code I hacked together so far trying to follow along and convert OOP to QB64 on the fly.
Code: (Select All)
'https://www.youtube.com/watch?v=XG6yOtEpRSw&list=PLtrSb4XxIVbpZpV65kk73OoUcIrBzoSiO&index=2 OPTION _EXPLICIT CONST MIN_VALUE = -2.802597E-45 TYPE Type_Vector2 x AS SINGLE y AS SINGLE END TYPE TYPE Type_Ray2D origin AS Type_Vector2 direction AS Type_Vector2 END TYPE TYPE Type_RaycastResult ppoint AS Type_Vector2 normal AS Type_Vector2 t AS SINGLE hit AS INTEGER END TYPE TYPE Type_Line2D from AS Type_Vector2 too AS Type_Vector2 colour AS _UNSIGNED LONG lifetime AS INTEGER END TYPE TYPE Type_Rigidbody2D position AS Type_Vector2 rotation AS SINGLE END TYPE TYPE Type_Box2D ' rotated bounding box size AS Type_Vector2 halfSize AS Type_Vector2 rigidbody AS Type_Rigidbody2D END TYPE TYPE Type_AABB ' axis aligned bounding box (not rotated) size AS Type_Vector2 halfSize AS Type_Vector2 rigidbody AS Type_Rigidbody2D END TYPE TYPE Type_Circle Radius AS SINGLE rigidbody AS Type_Rigidbody2D END TYPE DIM __AABB AS Type_AABB DIM __Circle AS Type_Circle DIM __rigidbody2D AS Type_Rigidbody2D DIM Vertices(4) AS Type_Vector2 SUB Line2D.setFromToo (__line2D AS Type_Line2D, from AS Type_Vector2, too AS Type_Vector2) __line2D.from = from __line2D.too = too END SUB SUB Line2D (__line2D AS Type_Line2D, from AS Type_Vector2, too AS Type_Vector2, colour AS _UNSIGNED LONG, lifetime AS INTEGER) __line2D.from = from __line2D.too = too __line2D.colour = colour __line2D.lifetime = lifetime END SUB FUNCTION Line2D.beginFrame (__line2d AS Type_Line2D) __line2d.lifetime = __line2d.lifetime - 1 Line2D.beginFrame = __line2d.lifetime END FUNCTION SUB Line2D.getFrom (__line2d AS Type_Line2D, from AS Type_Vector2) from = __line2d.from END SUB SUB Line2D.getToo (__line2d AS Type_Line2D, too AS Type_Vector2) too = __line2d.too END SUB SUB Line2D.getStart (__line2d AS Type_Line2D, start AS Type_Vector2) start = __line2d.from END SUB SUB Line2D.getEnd (__line2d AS Type_Line2D, endd AS Type_Vector2) endd = __line2d.too END SUB FUNCTION Line2D.getColour (__line2d AS Type_Line2D) Line2D.getColour = __line2d.colour END FUNCTION FUNCTION Line2D.lengthSquared (__line2d AS Type_Line2D) DIM from AS Type_Vector2 DIM too AS Type_Vector2 DIM length AS Type_Vector2 Line2D.getFrom __line2d, from Line2D.getToo __line2d, too length.x = too.x - from.x length.y = too.y - from.y Line2D.lengthSquared = lengthSquared(length) END FUNCTION '******************************** '* RIGIDBODY * <----------------------------------------------------------------------- '******************************** '-------------------------------- '---- IntersectionDetector2D ---- '-------------------------------- FUNCTION PointOnLine (TestPoint AS Type_Vector2, __line2D AS Type_Line2D) ' based on the Slope Intercept Form of the equation of a straight line ' ' ' | S = Line Start = (0,1) ' | (8,5) E = Line End = (8,5) ' 5+ __ù Need to get values of this formula: y = m * x + b ' | _- Solve for m: ' | _-- - dy = Ey - Sy = 5 - 1 = 4 ' 4+ __- - dx = Ex - Sx = 8 - 0 = 8 ' | _- dy 4 1 ' | P _-- - m = ---- = --- = --- (m solved) ' 3+ ù __- dx 8 2 ' | (2,3) _- Solve for b: ' | _-- - b = y - mx (plug in x (0) and y (1) from line start) ' 2+ __- 1 ' | _- - b = 1 - --- * 0 = 1 - 0 = 1 (b solved) ' |_-- 2 ' 1ù Plug in values from Px along with solved m and b to compare y result with Py '(0,1) 1 ' | - y = --- * x + 1 = y = .5 * 2 + 1 = y = 2 (FALSE) 2 is not equal to Py (3) ' +---+---+---+---+---+---+---+---+--- 2 ' 0 1 2 3 4 5 6 7 8 ' DIM lineStart AS Type_Vector2 ' start vector of line (x,y) DIM lineEnd AS Type_Vector2 ' end vector of line (x,y) DIM dx AS SINGLE ' run (delta in the x direction) DIM dy AS SINGLE ' rise (delta in the y direction) DIM m AS SINGLE ' slope (rise over run) DIM b AS SINGLE ' the y intercept PointOnLine = 0 ' assume point not on line '---------- lineStart = __line2D.from lineEnd = __line2D.too 'Line2D.getStart __line2D, lineStart ' get line start vector (x,y) 'Line2D.getEnd __line2D, lineEnd ' get line end vector (x,y) '---------- dy = lineEnd.y - lineStart.y ' calculate rise dx = lineEnd.x - lineStart.x ' calculate run IF dx = 0 THEN ' vertical line? (avoid divide by 0) IF TestPoint.x = lineStart.x THEN ' yes, do x values match? PointOnLine = -1 ' yes, must be on the line EXIT FUNCTION ' leave END IF END IF m = dy / dx ' calculate slope b = lineStart.y - (m * lineStart.x) ' calculate y intercept IF TestPoint.y = m * TestPoint.x + b THEN PointOnLine = -1 ' point on line if y = mx + b END FUNCTION FUNCTION PointInCircle (TestPoint AS Type_Vector2, __circle AS Type_Circle) ' Check for point within circle. ' ' ' ********* - Simply use Pythagoras to solve. ' **** **** ' ***\ *** - Calculate x and y sides from point to center ' ** \ ** - if x side * x side + y side * y side <= radius * radius then point within circle ' * \ x2 * - (this method negates having to use square root) ' * Radius\ +--------------*---__ù p2 ' * \ y2| _*-- p1: x1 = p1.x - center.x ' * \ | __-- * y1 = p1.y - center.y ' * \ | __-- * x1 * x1 + y1 * y1 <= radius * radius (TRUE - point within circle) ' * \| __-- L2 * ' * Center x,y ù-- * p2: x2 = p2.x - center.x ' * |\ * y2 = p2.y - center.y ' * | \ L1 * x2 * x2 + y2 * y2 <= radius * radius (FALSE - point NOT within circle) ' * y1| \ * ' * | \ * ' * +----ù * ' * x1 p1 * ' ** ** ' *** *** ' **** **** ' ********* DIM circleCenter AS Type_Vector2 ' center coordinate of circle (x,y) DIM centerToPoint AS Type_Vector2 ' x,y lengths DIM radius AS SINGLE ' radius of circle PointInCircle = 0 ' assume point not within circle '---------- circleCenter = __circle.rigidbody.position 'Circle.getCenter __circle, circleCenter ' get center coordinate of circle (x,y) '---------- '---------- radius = __circle.Radius 'radius = Circle.getRadius(__circle) ' get radius of circle '---------- centerToPoint.x = TestPoint.x - circleCenter.x ' calculate x distance from point to center of circle centerToPoint.y = TestPoint.y - circleCenter.y ' calculate y distance from point to center of circle IF lengthSquared(centerToPoint) <= radius * radius THEN PointInCircle = -1 ' return true if length <= radus of circle END FUNCTION FUNCTION PointInAABB (TestPoint AS Type_Vector2, box AS Type_AABB) ' Check for a point inside standard rectangle (AABB axis aligned bounding box) ' ' . . Four simple checks needed to see if point is within a non rotated rectangle ' . . ' . . p1: p1.x <= max.x (TRUE) AND ' ..... +-------------------------------+ ..... min.x <= p1.x (TRUE) AND ' |min(x,y) | p1.y <= max.y (TRUE) AND ' | | min.y <= p1.y (TRUE) = All TRUE means within rectangle ' | | ' | p1 | p2: p2.x <= max.x (FALSE) AND ' | . | min.x <= p2.x (TRUE) AND ' | | p2 p2.y <= max.y (TRUE) AND ' | | . min.y <= p2.y (TRUE) = Any FALSE means NOT within rectangle ' | | ' | | ' | | ' | max(x,y)| ' ..... +-------------------------------+ ..... ' . . ' . . ' . . DIM min AS Type_Vector2 ' upper left rectangular coordinate (x,y) DIM max AS Type_Vector2 ' lower right rectangular coordinate (x,y) AABB.getMin box, min ' get upper left coordinate AABB.getMax box, max ' get lower right coordinate PointInAABB = 0 ' assume point not within AABB IF TestPoint.x <= max.x THEN ' perform the four checks IF min.x <= TestPoint.x THEN IF TestPoint.y <= max.y THEN IF min.y <= TestPoint.y THEN PointInAABB = -1 ' if all true report point within END IF END IF END IF END IF END FUNCTION FUNCTION PointInBox2D (TestPoint AS Type_Vector2, box AS Type_Box2D) ' ' Test for a point in a rotated 2D box by rotating the point into the box's local space ' ' _-\ ' _- \ P ' _- \ _ù rotated position ' _- \ _- ' _- \_- ' _- _-\ - Rotate point P around origin C the same degree as rotated box ' - Rotated _- \ - Point P is now in the local boxe's space ' \ AABB _- \ - From here it's just a simple AABB min/max check ' \ Cù \ ' \ \ _- ' \ \ _- ' \ \ _- | ' \ _\ / ' \ _- \ / ' \ _- \ _- ' \- \ _- ' ù original position ' P DIM pointLocalBoxSpace AS Type_Vector2 DIM min AS Type_Vector2 DIM max AS Type_Vector2 PointInBox2D = 0 ' assume point not within Box2D.getMin box, min ' get upper left coordinate Box2D.getMax box, max ' get lower right coordinate pointLocalBoxSpace = TestPoint ' copy test point '+------------------------------------------------+ '| Translate the point into the box's local space | '+------------------------------------------------+ Rotate pointLocalBoxSpace, box.rigidbody.rotation, box.rigidbody.position ' rotate point into box's local space '+-----------------------------------------+ '| Perform standard point within AABB test | '+-----------------------------------------+ IF pointLocalBoxSpace.x <= max.x THEN ' perform the four AABB checks IF min.x <= pointLocalBoxSpace.x THEN IF pointLocalBoxSpace.y <= max.y THEN IF min.y <= pointLocalBoxSpace.y THEN PointInBox2D = -1 ' if all true report point within END IF END IF END IF END IF END FUNCTION FUNCTION lineAndCircle (__line2D AS Type_Line2D, __circle AS Type_Circle) ' ' Use projection to determine if a line is intersecting a circle ' ' ********* Determine if line B is intersecting circle: ' **** **** - Line B end points are outside circle (check points within circle) ' *** *** - If either end point within circle then line B is intersecting (return TRUE) ' ** ** - Get line segment x and y lengths and store in ab.x and ab.y ' * * - Get vector x and y lengths from center of circle to start of line segment ' * * - Store in centerToLineStart.x and centerToLineStart.y ' * * - Perform dot product of vectors to get a percentage of line segment ' * * - centerToLineStart ù ab ' * Center * t = ------------------------ t = 0 to 1 (percentage of A to B) ' * x,y * ab ù ab ' * __+ * - Add (ab * t) to Start to get point C (closest point to center) ' * __--- | * - check for point C within circle ' * ___--- | * - (this method negates the need to use square root) ' A ___-- | * ' __--- * | * ' ___--- * * ' Start ù------------------------------ù-----------------------------ù End ' ** C ** ' B *** closest point *** ' **** **** ' | ********* | ' |---------------------------- ab --------------------------| ' | | DIM LineStart AS Type_Vector2 ' start line vector (x,y) DIM LineEnd AS Type_Vector2 ' end line vector (x,y) DIM ab AS Type_Vector2 ' line segment (ex-sx,ey-sy) DIM circleCenter AS Type_Vector2 ' center of circle (x,y) DIM centerToLineStart AS Type_Vector2 ' start of line to center (cx-sx,cy-sy) DIM t AS SINGLE ' percentage of the line (0 to 1) DIM closestPoint AS Type_Vector2 ' closest point on line to center lineAndCircle = 0 ' assume no intersection '---------- LineStart = __line2D.from LineEnd = __line2D.too 'Line2D.getStart __line2D, LineStart ' get start vector of line (x,y) 'Line2D.getEnd __line2D, LineEnd ' get end vector of line (x,y) '---------- IF PointInCircle(LineStart, __circle) OR PointInCircle(LineEnd, __circle) THEN ' is either line end point within circle? lineAndCircle = -1 ' yes, then line must be intersecting circle EXIT FUNCTION ' leave END IF ab.x = LineEnd.x - LineStart.x ' calculate line segment length ab.y = LineEnd.y - LineStart.y '+--------------------------------------------------------+ '| Project point (circle position) onto ab (line segment) | '| result = parameterized position t | '+--------------------------------------------------------+ '---------- circleCenter = __circle.rigidbody.position 'Circle.getCenter __circle, circleCenter ' get center of circle '---------- centerToLineStart.x = circleCenter.x - LineStart.x ' calculate length from center to start of line segment centerToLineStart.y = circleCenter.y - LineStart.y t = Dot(centerToLineStart, ab) / Dot(ab, ab) ' perform dot product on vectors to get percentage IF t < 0 OR t > 1 THEN EXIT FUNCTION ' leave if not between the line segment, no intersection '+--------------------------------------------+ '| Find the closest point to the line segment | '+--------------------------------------------+ closestPoint.x = LineStart.x + ab.x * t ' calculate closest line point to center of circle closestPoint.y = LineStart.y + ab.y * t lineAndCircle = PointInCircle(closestPoint, __circle) ' return result of closest point within circle END FUNCTION FUNCTION lineAndAABB (__line2D AS Type_Line2D, box AS Type_AABB) 'Raycasting DIM lineStart AS Type_Vector2 DIM lineEnd AS Type_Vector2 DIM unitVector AS Type_Vector2 DIM min AS Type_Vector2 DIM max AS Type_Vector2 DIM tmin AS SINGLE DIM tmax AS SINGLE DIM t AS SINGLE lineAndAABB = 0 Line2D.getStart __line2D, lineStart Line2D.getEnd __line2D, lineEnd IF PointInAABB(lineStart, box) OR PointInAABB(lineEnd, box) THEN lineAndAABB = -1 EXIT FUNCTION END IF unitVector.x = lineEnd.x - lineStart.x unitVector.y = lineEnd.y - lineEnd.y Normalize unitVector IF unitVector.x <> 0 THEN unitVector.x = 1 / unitVector.x IF unitVector.y <> 0 THEN unitVector.y = 1 / unitVector.y AABB.getMin box, min min.x = min.x - lineStart.x * unitVector.x min.y = min.y - lineStart.y * unitVector.y AABB.getMax box, max max.x = max.x - lineStart.x * unitVector.x max.y = max.y - lineStart.y * unitVector.y tmin = MathMax(MathMin(min.x, max.x), MathMin(min.y, max.y)) tmax = MathMin(MathMax(min.x, max.x), MathMax(min.y, max.y)) IF tmax < 0 OR tmin > tmax THEN EXIT FUNCTION IF tmin < 0 THEN t = tmax ELSE t = tmin IF t > 0 AND t * t < Line2D.lengthSquared(__line2D) THEN lineAndAABB = -1 END FUNCTION FUNCTION lineAndBox2D (__line2d AS Type_Line2D, box AS Type_Box2D) 'Rotate the line into the box's local space DIM theta AS SINGLE DIM center AS Type_Vector2 DIM localStart AS Type_Vector2 DIM localEnd AS Type_Vector2 DIM localLine AS Type_Line2D DIM min AS Type_Vector2 DIM max AS Type_Vector2 DIM __aabb AS Type_AABB theta = -box.rigidbody.rotation center = box.rigidbody.position Line2D.getStart __line2d, localStart Line2D.getEnd __line2d, localEnd Rotate localStart, theta, center Rotate localEnd, theta, center 'Line2D localLine, localStart, localEnd, _RGB32(255, 255, 255), 1 (instead of 2 lines below) localLine.from = localStart localLine.too = localEnd Box2D.getMin box, min Box2D.getMax box, max AABB __aabb, min, max lineAndBox2D = lineAndAABB(localLine, __aabb) END FUNCTION ' +----------+ ' | Raycasts | ' +----------+ FUNCTION RaycastCircle (__circle AS Type_Circle, ray AS Type_Ray2D, result AS Type_RaycastResult) DIM originToCircle AS Type_Vector2 DIM center AS Type_Vector2 DIM origin AS Type_Vector2 DIM radius AS SINGLE DIM radiusSquared AS SINGLE DIM originToCircleLengthSquared AS SINGLE DIM direction AS Type_Vector2 DIM a AS SINGLE DIM bSq AS SINGLE DIM f AS SINGLE DIM t AS SINGLE DIM ppoint AS Type_Vector2 DIM normal AS Type_Vector2 RaycastCircle = 0 RaycastResult.reset result Circle.getCenter __circle, center Ray2D.getOrigin ray, origin originToCircle.x = center.x - origin.x originToCircle.y = center.y - origin.y radius = Circle.getRadius(__circle) radiusSquared = radius * radius originToCircleLengthSquared = lengthSquared(originToCircle) ' Project the vector from the ray origin onto the direction of the ray Ray2D.getDirection ray, direction a = Dot(originToCircle, direction) bSq = originToCircleLengthSquared - (a * a) IF radiusSquared - bSq < 0 THEN EXIT FUNCTION f = SQR(radiusSquared - bSq) t = 0 IF originToCircleLengthSquared < radiusSquared THEN t = a + f ' ray starts inside the circle ELSE t = a - f ' ray starts outside the circle END IF IF result.ppoint.x + result.ppoint.y = 0 THEN ppoint.x = origin.x + direction.x * t ppoint.y = origin.y + direction.y * t normal.x = ppoint.x - center.x normal.y = ppoint.y - center.y Normalize normal result.ppoint = ppoint result.normal = normal result.t = t result.hit = -1 END IF RaycastCircle = -1 END FUNCTION FUNCTION RaycastAABB (box AS Type_AABB, __Ray2D AS Type_Ray2D, result AS Type_RaycastResult) 'DIM lineStart AS Type_Vector2 'DIM lineEnd AS Type_Vector2 DIM unitVector AS Type_Vector2 DIM min AS Type_Vector2 DIM max AS Type_Vector2 DIM tmin AS SINGLE DIM tmax AS SINGLE DIM t AS SINGLE DIM hit AS INTEGER DIM ppoint AS Type_Vector2 DIM normal AS Type_Vector2 RaycastAABB = 0 RaycastResult.reset result unitVector.x = __Ray2D.direction.x ' lineEnd.x - lineStart.x unitVector.y = __Ray2D.direction.y 'lineEnd.y - lineEnd.y Normalize unitVector IF unitVector.x <> 0 THEN unitVector.x = 1 / unitVector.x IF unitVector.y <> 0 THEN unitVector.y = 1 / unitVector.y AABB.getMin box, min min.x = min.x - __Ray2D.origin.x 'lineStart.x * unitVector.x min.y = min.y - __Ray2D.origin.y 'lineStart.y * unitVector.y AABB.getMax box, max max.x = max.x - __Ray2D.origin.x 'lineStart.x * unitVector.x max.y = max.y - __Ray2D.origin.y 'lineStart.y * unitVector.y tmin = MathMax(MathMin(min.x, max.x), MathMin(min.y, max.y)) tmax = MathMin(MathMax(min.x, max.x), MathMax(min.y, max.y)) IF tmax < 0 OR tmin > tmax THEN EXIT FUNCTION IF tmin < 0 THEN t = tmax ELSE t = tmin IF t > 0 THEN hit = -1 IF NOT hit THEN EXIT FUNCTION IF result.ppoint.x = 0 AND result.ppoint.y = 0 THEN ppoint.x = __Ray2D.origin.x + __Ray2D.direction.x * t ppoint.y = __Ray2D.origin.y + __Ray2D.direction.y * t normal.x = __Ray2D.origin.x - ppoint.x normal.y = __Ray2D.origin.y - ppoint.y Normalize normal result.ppoint = ppoint result.normal = normal result.t = t result.hit = -1 END IF RaycastAABB = -1 END FUNCTION FUNCTION RaycastBox2D (box AS Type_Box2D, __Ray2D AS Type_Ray2D, result AS Type_RaycastResult) DIM xAxis AS Type_Vector2 DIM yAxis AS Type_Vector2 DIM zerozero AS Type_Vector2 DIM p AS Type_Vector2 DIM f AS Type_Vector2 DIM e AS Type_Vector2 DIM size AS Type_Vector2 RaycastBox2D = 0 RaycastResult.reset result Box2D.halfSize box, size xAxis.x = 1 xAxis.y = 0 yAxis.x = 0 yAxis.y = 1 Rotate xAxis, -box.rigidbody.rotation, zerozero Rotate yAxis, -box.rigidbody.rotation, zerozero p.x = box.rigidbody.position.x - __Ray2D.origin.x p.y = box.rigidbody.position.y - __Ray2D.origin.y ' Project the direction of the ray onto each axis of the box f.x = Dot(xAxis, __Ray2D.direction) f.y = Dot(yAxis, __Ray2D.direction) ' Next, project p onto every axis of the box e.x = Dot(xAxis, p) e.y = Dot(yAxis, p) RaycastBox2D = -1 END FUNCTION '-------------------------------- '--------- RIGIDBODY2D ---------- '-------------------------------- SUB RigidBody2D.getPosition (__rigidbody2D AS Type_Rigidbody2D, position AS Type_Vector2) position.x = __rigidbody2D.position.x position.y = __rigidbody2D.position.y END SUB SUB RigidBody2D.setPosition (__rigidbody2D AS Type_Rigidbody2D, position AS Type_Vector2) __rigidbody2D.position.x = position.x __rigidbody2D.position.y = position.y END SUB FUNCTION RigidBody2D.getRotation (__rigidbody2D AS Type_Rigidbody2D) RigidBody2D.getRotation = __rigidbody2D.rotation END FUNCTION SUB RigidBody2D.setRotation (__rigidbody2D AS Type_Rigidbody2D, rotation AS SINGLE) __rigidbody2D.rotation = rotation END SUB '******************************** '* PHYSICS2D PRIMATIVES * <----------------------------------------------------------------------- '******************************** '-------------------------------- '------------ AABB -------------- '-------------------------------- SUB AABB (__AABB AS Type_AABB, min AS Type_Vector2, max AS Type_Vector2) __AABB.size.x = max.x - min.x ' set size of object __AABB.size.y = max.y - min.y __AABB.halfSize.x = __AABB.size.x * .5 __AABB.halfSize.y = __AABB.size.y * .5 END SUB SUB AABB.halfSize (__AABB AS Type_AABB, halfSize AS Type_Vector2) halfSize.x = __AABB.size.x * .5 halfSize.y = __AABB.size.y * .5 END SUB SUB AABB.getMin (__AABB AS Type_AABB, min AS Type_Vector2) DIM halfSize AS Type_Vector2 AABB.halfSize __AABB, halfSize min.x = __AABB.rigidbody.position.x - halfSize.x min.y = __AABB.rigidbody.position.y - halfSize.y END SUB SUB AABB.getMax (__AABB AS Type_AABB, max AS Type_Vector2) DIM halfSize AS Type_Vector2 AABB.halfSize __AABB, halfSize max.x = __AABB.rigidbody.position.x + halfSize.x max.y = __AABB.rigidbody.position.y + halfSize.y END SUB '-------------------------------- '----------- Box2D -------------- '-------------------------------- SUB Box2D (__box2D AS Type_Box2D, min AS Type_Vector2, max AS Type_Vector2) __box2D.size.x = max.x - min.x ' set size of object __box2D.size.y = max.y - min.y __box2D.halfSize.x = __box2D.size.x * .5 __box2D.halfSize.y = __box2D.size.y * .5 END SUB SUB Box2D.halfSize (__box2D AS Type_Box2D, halfSize AS Type_Vector2) halfSize.x = __box2D.size.x * .5 halfSize.y = __box2D.size.y * .5 END SUB SUB Box2D.getMin (__box2D AS Type_Box2D, min AS Type_Vector2) DIM halfSize AS Type_Vector2 Box2D.halfSize __box2D, halfSize min.x = __box2D.rigidbody.position.x - halfSize.x min.y = __box2D.rigidbody.position.y - halfSize.y END SUB SUB Box2D.getMax (__box2D AS Type_Box2D, max AS Type_Vector2) DIM halfSize AS Type_Vector2 Box2D.halfSize __box2D, halfSize max.x = __box2D.rigidbody.position.x + halfSize.x max.y = __box2D.rigidbody.position.y + halfSize.y END SUB SUB Box2D.getVertices (__box2d AS Type_Box2D, Vertices() AS Type_Vector2) DIM min AS Type_Vector2 DIM max AS Type_Vector2 DIM vert AS Type_Vector2 DIM vCount AS INTEGER Box2D.getMin __box2d, min Box2D.getMax __box2d, max Vertices(1).x = min.x Vertices(1).y = min.y Vertices(2).x = min.x Vertices(2).y = max.y Vertices(3).x = max.x Vertices(3).y = min.y Vertices(4).x = max.x Vertices(4).y = max.y IF __box2d.rigidbody.rotation <> 0 THEN vCount = 0 DO vert = Vertices(vCount) Rotate vert, __box2d.rigidbody.rotation, __box2d.rigidbody.position LOOP UNTIL vCount = 4 END IF END SUB '-------------------------------- '----------- Circle ------------- '-------------------------------- FUNCTION Circle.getRadius (__circle AS Type_Circle) Circle.getRadius = __circle.Radius END FUNCTION SUB Circle.setRadius (__circle AS Type_Circle, radius AS SINGLE) __circle.Radius = radius END SUB SUB Circle.getCenter (__circle AS Type_Circle, center AS Type_Vector2) center = __circle.rigidbody.position END SUB '-------------------------------- '--------- Collider2D ----------- '-------------------------------- '-------------------------------- '------------ Ray2D ------------- '-------------------------------- SUB Ray2D (__Ray2D AS Type_Ray2D, origin AS Type_Vector2, direction AS Type_Vector2) __Ray2D.origin = origin __Ray2D.direction = direction Normalize __Ray2D.direction END SUB SUB Ray2D.getOrigin (__ray2D AS Type_Ray2D, origin AS Type_Vector2) origin = __ray2D.origin END SUB SUB Ray2D.getDirection (__ray2D AS Type_Ray2D, direction AS Type_Vector2) direction = __ray2D.direction END SUB '-------------------------------- '--------- RaycastResult -------- '-------------------------------- SUB RaycastResult (__RaycastResult AS Type_RaycastResult) __RaycastResult.ppoint.x = 0 __RaycastResult.ppoint.y = 0 __RaycastResult.normal.x = 0 __RaycastResult.normal.y = 0 __RaycastResult.t = -1 __RaycastResult.hit = 0 END SUB SUB RaycastResult.init (__RaycastResult AS Type_RaycastResult, ppoint AS Type_Vector2, normal AS Type_Vector2, t AS SINGLE, hit AS INTEGER) __RaycastResult.ppoint = ppoint __RaycastResult.normal = normal __RaycastResult.t = t __RaycastResult.hit = hit END SUB SUB RaycastResult.reset (result AS Type_RaycastResult) IF result.ppoint.x OR result.ppoint.y THEN result.ppoint.x = 0 result.ppoint.y = 0 result.normal.x = 0 result.normal.y = 0 result.t = -1 result.hit = 0 END IF END SUB SUB AddVectors (V1 AS Type_Vector2, V2 AS Type_Vector2, Vout AS Type_Vector2) ' - - ' Formula: V1 + V2 = (V1.x, v1.y) + (V2.x, V2.y) = (V1.x + V2.x, V1.y + V2.y) ' V1 - input : Vector 1 ' V2 - input : Vector 2 ' Vout - output: the new vector Vout.x = V1.x + V2.x ' x value of vector 2 gets added to x value of vector 1 Vout.y = V1.y + V2.y ' y value of vector 2 gets added to y value of vector 1 END SUB SUB SubtractVectors (V1 AS Type_Vector2, V2 AS Type_Vector2, Vout AS Type_Vector2) ' - - ' Formula: V1 + V2 = (V1.x, v1.y) - (V2.x, V2.y) = (V1.x - V2.x, V1.y - V2.y) ' V1 - input : Vector 1 ' V2 - input : Vector 2 ' Vout - output: the new vector Vout.x = V1.x - V2.x ' x value of vector 2 gets subtracted from x value of vector 1 Vout.y = V1.y - V2.y ' y value of vector 2 gets subtracted from y value of vector 1 END SUB SUB ScalarMultiplyVector (V AS Type_Vector2, Scalar AS SINGLE, Vout AS Type_Vector2) ' "Scaling the vector" ' ' Formula: V * Scalar = (Vx, Vy) * Scalar = (Vx * Scalar, Vy * Scalar) ' V - input : Vector ' Scalar - input : scalar multiplication value ' Vout - output: the new vector Vout.x = V.x * Scalar ' x value of vector gets multiplied by scalar Vout.y = V.y * Scalar ' y value of vector gets multiplied by scalar END SUB FUNCTION Dot (V1 AS Type_Vector2, V2 AS Type_Vector2) ' Dot product of vectors ' - - ' Formula: V1 ù V2 = (V1.x, V1.y) ù (V2.x, V2.y) = (V1.x * v2.x) + (V1.y * V2.y) Dot = V1.x * V2.x + V1.y * V2.y ' multiply vector x values then add multiplied vector y values END FUNCTION FUNCTION CrossProductVectors (V1 AS Type_Vector2, V2 AS Type_Vector2) ' Also known as a "Wedge Product" or "Perp Product" for 2D vectors ' - - ³ x y ³ ' Formula: V1 * V2 = (V1.x, V1.y) * (V2.x, V2.y) = ³V1.x V1.y³ = (V1.x * V2.y) - (V1.y * V2.x) ' ³V2.x V2.y³ CrossProductVectors = V1.x * V2.y - V1.y * V2.x END FUNCTION FUNCTION VectorLength (V AS Type_Vector2) ' _______________________ ' Formula: º V º = û V.x * V.x + V.y * V.y VectorLength = _HYPOT(V.x, V.y) END FUNCTION SUB Normalize (v AS Type_Vector2) ' Also known as a unit vector ' _______________________ ' Formula: V / º V º = (V1.x, V1.y) / û V.x * V.x + V.y * v.y DIM VecLength AS SINGLE VecLength = _HYPOT(v.x, v.y) ' length of vector v.x = v.x / VecLength ' normalized x length v.y = v.y / VecLength ' normalized y length END SUB SUB Rotate (vec AS Type_Vector2, angleDeg AS SINGLE, origin AS Type_Vector2) ' Rotate a point around an origin using linear transformations. ' ' Rotating from (x,y) to (x',y') | ' | (x',y') : L = R cosé | All of this shows how to get to this ' | ù : A = x' | ----------- ' | /.\ : B = L cosè = R cosè cosé = x cosé | | ' | / .è\ : (note - * opposite angles are equal) | | ' | / . \ : C = R siné | +----+ ' | / . \ : D = C sinè = R sinè siné = y siné | | ' | / . \ : Y = R sinè | | ' | / . \C : X = r cosè | ' | / . \ __ | ----------------- ' | / . \ L stops : x' = B - |AB| = X cosé - Y siné | ' | / . \ here ' | / . \ | All of this just to show how to get from x to x' using (X cosé - Y siné) ' | / . \ | Use the same linear transformation methods to get y' using (X siné + Y cosé) ' | R/ . \ | ' | / .¿ D \ Change the origin point of all rotations to (0,0) by subtracting the current ' | / .------------âù_--ù (x,y) origin point from the current vector length. Add it back when rotation is ' | / . __--. . completed. ' | / . * __-- . . ' | / . __-- . . ' | / L __-- . . ' | / __-- . . .Y ' | / __-- * . . . ' | / __-- . . . ' | / é __-- . . . ' |/__-- è â. . . ' ù-----------------------ù-------------ù---ù------------ ' A B ' |------------------- X -------------------| DIM x AS SINGLE DIM y AS SINGLE DIM __cos AS SINGLE DIM __sin AS SINGLE DIM xPrime AS SINGLE DIM yPrime AS SINGLE x = vec.x - origin.x ' move rotation vector origin to 0 y = vec.y - origin.y __cos = COS(_D2R(angleDeg)) ' get cosine and sine of angle __sin = SIN(_D2R(angleDeg)) xPrime = (x * __cos) - (y * __sin) ' calculate rotated location of vector yPrime = (x * __sin) + (y * __cos) xPrime = xPrime + origin.x ' move back to original origin yPrime = yPrime + origin.y vec.x = xPrime ' pass back rotated vector vec.y = yPrime END SUB FUNCTION compareXYEpsilon (x AS SINGLE, y AS SINGLE, epsilon AS SINGLE) compareXYEpsilon = 0 IF ABS(x - y) <= epsilon * MathMax(1, MathMax(ABS(x), ABS(y))) THEN compareXYEpsilon = -1 END FUNCTION FUNCTION compareVecEpsilon (vec1 AS Type_Vector2, vec2 AS Type_Vector2, epsilon AS SINGLE) compareVecEpsilon = 0 IF compareXYEpsilon(vec1.x, vec2.x, epsilon) AND compareXYEpsilon(vec1.y, vec2.y, epsilon) THEN compareVecEpsilon = -1 END FUNCTION FUNCTION compareXY (x AS SINGLE, y AS SINGLE) compareXY = 0 IF ABS(x - y) <= MIN_VALUE * MathMax(1, MathMax(ABS(x), ABS(y))) THEN compareXY = -1 END FUNCTION FUNCTION compareVec (vec1 AS Type_Vector2, vec2 AS Type_Vector2) compareVec = 0 IF compareXY(vec1.x, vec2.x) AND compareXY(vec1.y, vec2.y) THEN compareVec = -1 END FUNCTION FUNCTION lengthSquared (length AS Type_Vector2) lengthSquared = length.x * length.x + length.y + length.y END FUNCTION FUNCTION MathMax (num1 AS SINGLE, num2 AS SINGLE) IF num1 >= num2 THEN MathMax = num1 ELSE MathMax = num2 END FUNCTION FUNCTION MathMin (num1 AS SINGLE, num2 AS SINGLE) IF num1 <= num2 THEN MathMin = num1 ELSE MathMin = num2 END FUNCTION