parent
8cb4e675b5
commit
3374a6924c
@ -0,0 +1,245 @@ |
|||||||
|
/*
|
||||||
|
Easy Circle Vs Rectangle Collision Resolution |
||||||
|
"Everything's just damp, the walls, the roof, everything!" - javidx9 |
||||||
|
|
||||||
|
Video: https://youtu.be/D2a5fHX-Qrs
|
||||||
|
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2018 - 2021 OneLoneCoder.com |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above |
||||||
|
copyright notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce |
||||||
|
the above copyright notice. This list of conditions and the following |
||||||
|
disclaimer must be reproduced in the documentation and/or other |
||||||
|
materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived |
||||||
|
from this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
Links |
||||||
|
~~~~~ |
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
https://www.youtube.com/javidx9extra
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author |
||||||
|
~~~~~~ |
||||||
|
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define OLC_PGE_APPLICATION |
||||||
|
#include "olcPixelGameEngine.h" |
||||||
|
|
||||||
|
#define OLC_PGEX_TRANSFORMEDVIEW |
||||||
|
#include "olcPGEX_TransformedView.h" |
||||||
|
|
||||||
|
|
||||||
|
class CircleVsRect : public olc::PixelGameEngine |
||||||
|
{ |
||||||
|
public: |
||||||
|
CircleVsRect() |
||||||
|
{ |
||||||
|
sAppName = "Circle Vs Rectangle"; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
olc::TileTransformedView tv; |
||||||
|
|
||||||
|
struct sWorldObject |
||||||
|
{ |
||||||
|
olc::vf2d vPos; |
||||||
|
olc::vf2d vVel; |
||||||
|
float fRadius = 0.5f; |
||||||
|
}; |
||||||
|
|
||||||
|
sWorldObject object; |
||||||
|
|
||||||
|
std::string sWorldMap = |
||||||
|
"################################" |
||||||
|
"#..............................#" |
||||||
|
"#.......#####.#.....#####......#" |
||||||
|
"#.......#...#.#.....#..........#" |
||||||
|
"#.......#...#.#.....#..........#" |
||||||
|
"#.......#####.#####.#####......#" |
||||||
|
"#..............................#" |
||||||
|
"#.....#####.#####.#####..##....#" |
||||||
|
"#.........#.#...#.....#.#.#....#" |
||||||
|
"#.....#####.#...#.#####...#....#" |
||||||
|
"#.....#.....#...#.#.......#....#" |
||||||
|
"#.....#####.#####.#####.#####..#" |
||||||
|
"#..............................#" |
||||||
|
"#..............................#" |
||||||
|
"#..#.#..........#....#.........#" |
||||||
|
"#..#.#..........#....#.........#" |
||||||
|
"#..#.#.......#####.#######.....#" |
||||||
|
"#..#.#..........#....#.........#" |
||||||
|
"#..#.#.............###.#.#.....#" |
||||||
|
"#..#.##########................#" |
||||||
|
"#..#..........#....#.#.#.#.....#" |
||||||
|
"#..#.####.###.#................#" |
||||||
|
"#..#.#......#.#................#" |
||||||
|
"#..#.#.####.#.#....###..###....#" |
||||||
|
"#..#.#......#.#....#......#....#" |
||||||
|
"#..#.########.#....#......#....#" |
||||||
|
"#..#..........#....#......#....#" |
||||||
|
"#..############....#......#....#" |
||||||
|
"#..................########....#" |
||||||
|
"#..............................#" |
||||||
|
"#..............................#" |
||||||
|
"################################"; |
||||||
|
|
||||||
|
olc::vi2d vWorldSize = { 32, 32 }; |
||||||
|
|
||||||
|
bool bFollowObject = false; |
||||||
|
|
||||||
|
public: |
||||||
|
bool OnUserCreate() override |
||||||
|
{ |
||||||
|
// Create "Tiled World", where each tile is 32x32 screen pixels. Coordinates
|
||||||
|
// for drawing will exist in unit-tile space from now on...
|
||||||
|
tv = olc::TileTransformedView({ ScreenWidth(), ScreenHeight() }, { 32, 32 });
|
||||||
|
object.vPos = { 3.0f, 3.0f }; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool OnUserUpdate(float fElapsedTime) override |
||||||
|
{ |
||||||
|
// Control of Player Object
|
||||||
|
object.vVel = { 0.0f, 0.0f }; |
||||||
|
if (GetKey(olc::Key::W).bHeld) object.vVel += { 0.0f, -1.0f }; |
||||||
|
if (GetKey(olc::Key::S).bHeld) object.vVel += { 0.0f, +1.0f }; |
||||||
|
if (GetKey(olc::Key::A).bHeld) object.vVel += { -1.0f, 0.0f }; |
||||||
|
if (GetKey(olc::Key::D).bHeld) object.vVel += { +1.0f, 0.0f }; |
||||||
|
|
||||||
|
if (object.vVel.mag2() > 0) |
||||||
|
object.vVel = object.vVel.norm() * (GetKey(olc::Key::SHIFT).bHeld ? 5.0f : 2.0f); |
||||||
|
|
||||||
|
if (GetKey(olc::Key::SPACE).bReleased) bFollowObject = !bFollowObject; |
||||||
|
|
||||||
|
|
||||||
|
// Where will object be worst case?
|
||||||
|
olc::vf2d vPotentialPosition = object.vPos + object.vVel * fElapsedTime; |
||||||
|
|
||||||
|
// Extract region of world cells that could have collision this frame
|
||||||
|
olc::vi2d vCurrentCell = object.vPos.floor(); |
||||||
|
olc::vi2d vTargetCell = vPotentialPosition; |
||||||
|
olc::vi2d vAreaTL = (vCurrentCell.min(vTargetCell) - olc::vi2d(1, 1)).max({ 0,0 }); |
||||||
|
olc::vi2d vAreaBR = (vCurrentCell.max(vTargetCell) + olc::vi2d(1, 1)).min(vWorldSize); |
||||||
|
|
||||||
|
olc::vf2d vRayToNearest; |
||||||
|
|
||||||
|
// Iterate through each cell in test area
|
||||||
|
olc::vi2d vCell; |
||||||
|
for (vCell.y = vAreaTL.y; vCell.y <= vAreaBR.y; vCell.y++) |
||||||
|
{ |
||||||
|
for (vCell.x = vAreaTL.x; vCell.x <= vAreaBR.x; vCell.x++) |
||||||
|
{ |
||||||
|
// Check if the cell is actually solid...
|
||||||
|
if (sWorldMap[vCell.y * vWorldSize.x + vCell.x] == '#') |
||||||
|
{ |
||||||
|
// ...it is! So work out nearest point to future player position, around perimeter
|
||||||
|
// of cell rectangle. We can test the distance to this point to see if we have
|
||||||
|
// collided.
|
||||||
|
|
||||||
|
olc::vf2d vNearestPoint; |
||||||
|
vNearestPoint.x = std::max(float(vCell.x), std::min(vPotentialPosition.x, float(vCell.x + 1))); |
||||||
|
vNearestPoint.y = std::max(float(vCell.y), std::min(vPotentialPosition.y, float(vCell.y + 1))); |
||||||
|
|
||||||
|
olc::vf2d vRayToNearest = vNearestPoint - vPotentialPosition; |
||||||
|
float fOverlap = object.fRadius - vRayToNearest.mag(); |
||||||
|
if (std::isnan(fOverlap)) fOverlap = 0; |
||||||
|
|
||||||
|
// If overlap is positive, then a collision has occurred, so we displace backwards by the
|
||||||
|
// overlap amount. The potential position is then tested against other tiles in the area
|
||||||
|
// therefore "statically" resolving the collision
|
||||||
|
if (fOverlap > 0) |
||||||
|
{ |
||||||
|
// Statically resolve the collision
|
||||||
|
vPotentialPosition = vPotentialPosition - vRayToNearest.norm() * fOverlap; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Set the objects new position to the allowed potential position
|
||||||
|
object.vPos = vPotentialPosition; |
||||||
|
|
||||||
|
|
||||||
|
// Clear World
|
||||||
|
Clear(olc::VERY_DARK_BLUE); |
||||||
|
|
||||||
|
if (bFollowObject) |
||||||
|
{ |
||||||
|
tv.SetWorldOffset(object.vPos - tv.ScaleToWorld(olc::vf2d(ScreenWidth()/2.0f, ScreenHeight()/2.0f))); |
||||||
|
DrawString({ 10,10 }, "Following Object"); |
||||||
|
} |
||||||
|
|
||||||
|
// Handle Pan & Zoom
|
||||||
|
if (GetMouse(2).bPressed) tv.StartPan(GetMousePos()); |
||||||
|
if (GetMouse(2).bHeld) tv.UpdatePan(GetMousePos()); |
||||||
|
if (GetMouse(2).bReleased) tv.EndPan(GetMousePos()); |
||||||
|
if (GetMouseWheel() > 0) tv.ZoomAtScreenPos(2.0f, GetMousePos()); |
||||||
|
if (GetMouseWheel() < 0) tv.ZoomAtScreenPos(0.5f, GetMousePos()); |
||||||
|
|
||||||
|
// Draw World
|
||||||
|
olc::vi2d vTL = tv.GetTopLeftTile().max({ 0,0 }); |
||||||
|
olc::vi2d vBR = tv.GetBottomRightTile().min(vWorldSize); |
||||||
|
olc::vi2d vTile; |
||||||
|
for (vTile.y = vTL.y; vTile.y < vBR.y; vTile.y++) |
||||||
|
for (vTile.x = vTL.x; vTile.x < vBR.x; vTile.x++) |
||||||
|
{ |
||||||
|
if (sWorldMap[vTile.y * vWorldSize.x + vTile.x] == '#') |
||||||
|
{ |
||||||
|
tv.DrawRect(vTile, { 1.0f, 1.0f }, olc::WHITE); |
||||||
|
tv.DrawLine(vTile, vTile + olc::vf2d(1.0f, 1.0f), olc::WHITE); |
||||||
|
tv.DrawLine(vTile + olc::vf2d(0.0f, 1.0f), vTile + olc::vf2d(1.0f, 0.0f), olc::WHITE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tv.FillRectDecal(vAreaTL, vAreaBR - vAreaTL + olc::vi2d(1,1), olc::Pixel(0,255,255,32)); |
||||||
|
|
||||||
|
// Draw Boundary
|
||||||
|
tv.DrawCircle(object.vPos, object.fRadius, olc::WHITE); |
||||||
|
|
||||||
|
// Draw Velocity
|
||||||
|
if (object.vVel.mag2() > 0) |
||||||
|
{ |
||||||
|
tv.DrawLine(object.vPos, object.vPos + object.vVel.norm() * object.fRadius, olc::MAGENTA); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
int main() |
||||||
|
{ |
||||||
|
CircleVsRect demo; |
||||||
|
if (demo.Construct(640, 480, 2, 2)) |
||||||
|
demo.Start(); |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue