tentatively refactor quad tree to make it more succinct + eliminate a class of potential bugs (passing wrong quad values through recursion)

This commit is contained in:
2025-01-27 23:27:52 +00:00
parent 42b42220d0
commit 6369be21fa
14 changed files with 393 additions and 1051 deletions

View File

@@ -4,18 +4,15 @@ struct
fun canWalkAhead (x, y, wallTree, platformTree) = fun canWalkAhead (x, y, wallTree, platformTree) =
let let
val ww = Constants.worldWidth
val wh = Constants.worldHeight
val y = y + Constants.enemySize - 5 val y = y + Constants.enemySize - 5
val searchHeight = 10 val searchHeight = 10
val searchWidth = Constants.moveEnemyBy val searchWidth = Constants.moveEnemyBy
in in
QuadTree.hasCollisionAt QuadHelp.hasCollisionAt
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, wallTree) (x, y, searchWidth, searchHeight, wallTree)
orelse orelse
QuadTree.hasCollisionAt QuadHelp.hasCollisionAt
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, platformTree) (x, y, searchWidth, searchHeight, platformTree)
end end
(* same function takes either wallTree or platformTree and returns true (* same function takes either wallTree or platformTree and returns true
@@ -31,11 +28,8 @@ struct
val width = Constants.enemySize val width = Constants.enemySize
val height = Platform.platHeight val height = Platform.platHeight
val ww = Constants.worldWidth
val wh = Constants.worldHeight
in in
QuadTree.hasCollisionAt (ex, ey, width, height, 0, 0, ww, wh, ~1, tree) QuadHelp.hasCollisionAt (ex, ey, width, height, tree)
end end
fun getPatrollPatches (enemy: enemy, wallTree, platformTree, acc) = fun getPatrollPatches (enemy: enemy, wallTree, platformTree, acc) =
@@ -68,19 +62,11 @@ struct
val searchWidth = Constants.moveEnemyBy val searchWidth = Constants.moveEnemyBy
val searchHeight = Constants.enemySize - 5 val searchHeight = Constants.enemySize - 5
val ww = Constants.worldWidth val hasWallAhead = QuadHelp.hasCollisionAt
val wh = Constants.worldHeight
val hasWallAhead = QuadTree.hasCollisionAt
( searchStartX ( searchStartX
, y , y
, searchWidth , searchWidth
, searchHeight , searchHeight
, 0
, 0
, ww
, wh
, ~1
, wallTree , wallTree
) )
in in
@@ -102,19 +88,11 @@ struct
val searchWidth = Constants.moveEnemyBy val searchWidth = Constants.moveEnemyBy
val searchHeight = Constants.enemySize - 5 val searchHeight = Constants.enemySize - 5
val ww = Constants.worldWidth val hasWallAhead = QuadHelp.hasCollisionAt
val wh = Constants.worldHeight
val hasWallAhead = QuadTree.hasCollisionAt
( searchStartX ( searchStartX
, y , y
, searchWidth , searchWidth
, searchHeight , searchHeight
, 0
, 0
, ww
, wh
, ~1
, wallTree , wallTree
) )
in in
@@ -155,12 +133,11 @@ struct
val searchWidth = Constants.playerSize val searchWidth = Constants.playerSize
val searchHeight = Constants.worldHeight - y val searchHeight = Constants.worldHeight - y
val ww = Constants.worldWidth val collisions = QuadHelp.getCollisions
val wh = Constants.worldHeight (x, y, searchWidth, searchHeight, platformTree)
val collisions = QuadTree.getCollisions
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, platformTree)
val checkY = y + Constants.playerSize val checkY = y + Constants.playerSize
val wh = Constants.worldHeight
in in
getHighestPlatform (collisions, platforms, wh, ~1, checkY) getHighestPlatform (collisions, platforms, wh, ~1, checkY)
end end
@@ -174,11 +151,9 @@ struct
val y = y + Constants.enemySize val y = y + Constants.enemySize
val ww = Constants.worldWidth val collisions = QuadHelp.getCollisions
(x, y, searchWidth, searchHeight, platformTree)
val wh = Constants.worldHeight val wh = Constants.worldHeight
val collisions = QuadTree.getCollisions
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, platformTree)
in in
getHighestPlatform (collisions, platforms, wh, ~1, y) getHighestPlatform (collisions, platforms, wh, ~1, y)
end end

View File

@@ -34,11 +34,9 @@ struct
val {x, y, health, ...} = enemy val {x, y, health, ...} = enemy
val size = Constants.enemySize val size = Constants.enemySize
val ww = Constants.worldWidth
val wh = Constants.worldHeight
val hasCollision = QuadTree.hasCollisionAt val hasCollision = QuadHelp.hasCollisionAt
(x, y, size, size, 0, 0, ww, wh, ~1, projectileTree) (x, y, size, size, projectileTree)
in in
if hasCollision then if hasCollision then
if health = 1 then if health = 1 then
@@ -182,10 +180,8 @@ struct
val {id, x, y, ...} = Vector.sub (enemyVec, pos) val {id, x, y, ...} = Vector.sub (enemyVec, pos)
val size = Constants.enemySize val size = Constants.enemySize
val ww = Constants.worldWidth
val wh = Constants.worldHeight
val acc = QuadTree.insert (x, y, size, size, 0, 0, ww, wh, id, acc) val acc = QuadHelp.insert (x, y, size, size, id, acc)
in in
helpGenerateTree (pos + 1, enemyVec, acc) helpGenerateTree (pos + 1, enemyVec, acc)
end end

View File

@@ -178,8 +178,20 @@ struct
val plat8 = {id = 8, x = 970, y = 815, width = 303} val plat8 = {id = 8, x = 970, y = 815, width = 303}
val plat9 = {id = 9, x = 959, y = 705, width = 303} val plat9 = {id = 9, x = 959, y = 705, width = 303}
val plat10 = {id = 10, x = 970, y = 759, width = 303} val plat10 = {id = 10, x = 970, y = 759, width = 303}
val plat11 = {id = 11, x = 970, y = 595, width = 303}
val plat12 = {id = 12, x = 959, y = 535, width = 303}
val plat13 = {id = 13, x = 970, y = 495, width = 303}
val plat14 = {id = 14, x = 1000, y = 415, width = 303}
val plat15 = {id = 15, x = 1000, y = 335, width = 303}
val plat16 = {id = 16, x = 1000, y = 295, width = 303}
val plat17 = {id = 17, x = 155, y = 599, width = 199}
val plat18 = {id = 18, x = 155, y = 499, width = 199}
val plat19 = {id = 19, x = 155, y = 399, width = 199}
val platforms = Vector.fromList val platforms = Vector.fromList
[plat1, plat2, plat3, plat4, plat5, plat6, plat7, plat8, plat9, plat10] [plat1, plat2, plat3, plat4, plat5, plat6, plat7, plat8, plat9, plat10,
plat11, plat12, plat13
, plat14, plat15, plat16, plat17, plat18,
plat19]
val platformTree = Platform.generateTree platforms val platformTree = Platform.generateTree platforms
val enemy1 = val enemy1 =
@@ -189,7 +201,7 @@ struct
, health = 1 , health = 1
, xAxis = STAY_STILL , xAxis = STAY_STILL
, yAxis = FALLING , yAxis = FALLING
, variant = EnemyVariants.FOLLOW_SLIME , variant = EnemyVariants.PATROL_SLIME
, platID = ~1 , platID = ~1
, nextPlatID = ~1 , nextPlatID = ~1
} }

View File

@@ -84,7 +84,7 @@ struct
val ww = Constants.worldWidth val ww = Constants.worldWidth
val wh = Constants.worldHeight val wh = Constants.worldHeight
in in
QuadTree.hasCollisionAt (x, y, width, height, 0, 0, ww, wh, ~1, tree) QuadHelp.hasCollisionAt (x, y, width, height, tree)
end end
fun standingOnAreaID (x, y, tree) = fun standingOnAreaID (x, y, tree) =
@@ -92,12 +92,27 @@ struct
val y = y + Fn.entitySize - 1 val y = y + Fn.entitySize - 1
val width = Fn.entitySize val width = Fn.entitySize
val height = Platform.platHeight val height = Platform.platHeight + 2
val plat1 = {id = 1, x = 155, y = 911, width = 199}
val plat2 = {id = 2, x = 355, y = 759, width = 555}
val plat3 = {id = 3, x = 355, y = 659, width = 111}
val plat4 = {id = 4, x = 155, y = 855, width = 99}
val plat5 = {id = 5, x = 155, y = 811, width = 199}
val plat6 = {id = 6, x = 155, y = 710, width = 199}
val plat7 = {id = 7, x = 301, y = 855, width = 99}
val plat8 = {id = 8, x = 970, y = 815, width = 303}
val plat9 = {id = 9, x = 959, y = 705, width = 303}
val plat10 = {id = 10, x = 970, y = 759, width = 303}
val ww = Constants.worldWidth val ww = Constants.worldWidth
val wh = Constants.worldHeight val wh = Constants.worldHeight
in
QuadTree.getItemID (x, y, width, height, 0, 0, ww, wh, tree) val _ = print "START getItemID\n"
val r =
QuadHelp.getItemID (x, y, width, height, tree)
val _ = print "FINISH getItemID\n"
in r
end end
fun getWallPatches (x, y, walls, wallTree, acc) = fun getWallPatches (x, y, walls, wallTree, acc) =
@@ -110,8 +125,8 @@ struct
(* check collision with wall to the left *) (* check collision with wall to the left *)
val acc = val acc =
let let
val leftWallID = QuadTree.getItemID val leftWallID = QuadHelp.getItemID
(x - 1, y, 1, 1, 0, 0, ww, wh, wallTree) (x - 1, y, 1, 1, wallTree)
in in
if leftWallID <> ~1 then if leftWallID <> ~1 then
let let
@@ -129,8 +144,8 @@ struct
(* check collision with wall to the right *) (* check collision with wall to the right *)
val acc = val acc =
let let
val rightWallID = QuadTree.getItemID val rightWallID = QuadHelp.getItemID
(x + size - 1, y, 1, 1, 0, 0, ww, wh, wallTree) (x + size - 1, y, 1, 1, wallTree)
in in
if rightWallID <> ~1 then if rightWallID <> ~1 then
let let
@@ -145,8 +160,8 @@ struct
end end
(* check collision with wall below *) (* check collision with wall below *)
val downWallID = QuadTree.getItemID val downWallID = QuadHelp.getItemID
(x + moveBy + 1, y + size, 1, 1, 0, 0, ww, wh, wallTree) (x + moveBy + 1, y + size, 1, 1, wallTree)
in in
if downWallID <> ~1 then if downWallID <> ~1 then
let let
@@ -172,23 +187,14 @@ struct
val ww = Constants.worldWidth val ww = Constants.worldWidth
val wh = Constants.worldHeight val wh = Constants.worldHeight
val platCollisions = QuadTree.getCollisionsBelow
(x, y, size, size, 0, 0, ww, wh, 0, platformTree)
val platID = standingOnAreaID (x, y, platformTree) val platID = standingOnAreaID (x, y, platformTree)
val acc = [] val acc = []
val acc = val acc =
if platID <> ~1 then if platID <> ~1 then
(case yAxis of (print ("platID: " ^ Int.toString platID ^ "\n"); case yAxis of
DROP_BELOW_PLATFORM => JUMPING _ =>
(* pass through, allowing player to drop below the platform *)
acc
| JUMPING _ =>
(* pass through, allowing player to jump above the platform *)
acc
| FLOATING _ =>
(* pass through, allowing player to jump above the platform *) (* pass through, allowing player to jump above the platform *)
acc acc
| _ => | _ =>
@@ -216,8 +222,8 @@ struct
* so we do not drop below any platforms again * so we do not drop below any platforms again
* *) * *)
if if
QuadTree.hasCollisionAt QuadHelp.hasCollisionAt
(x, y, size, size, 0, 0, ww, wh, ~1, platformTree) (x, y, size, size, platformTree)
then acc then acc
else Fn.W_Y_AXIS FALLING :: acc else Fn.W_Y_AXIS FALLING :: acc
| _ => acc | _ => acc

View File

@@ -11,8 +11,8 @@ struct
else else
let let
val {id, x, y, width} = Vector.sub (platVec, pos) val {id, x, y, width} = Vector.sub (platVec, pos)
val acc = QuadTree.insert val acc = QuadHelp.insert
(x, y, width, platHeight, 0, 0, 1920, 1080, id, acc) (x, y, width, platHeight, id, acc)
in in
helpGenerateTree (pos + 1, platVec, acc) helpGenerateTree (pos + 1, platVec, acc)
end end

View File

@@ -47,8 +47,8 @@ struct
(case attacked of (case attacked of
NOT_ATTACKED => NOT_ATTACKED =>
let let
val enemyCollisions = QuadTree.getCollisions val enemyCollisions = QuadHelp.getCollisions
(x, y, size, size, 0, 0, 1920, 1080, 0, enemyTree) (x, y, size, size, enemyTree)
val player = val player =
Player.enemyCollisionReaction Player.enemyCollisionReaction
@@ -72,8 +72,8 @@ struct
if amt = Constants.attackedLimit then if amt = Constants.attackedLimit then
(* if reached limit, detect enemies again *) (* if reached limit, detect enemies again *)
let let
val enemyCollisions = QuadTree.getCollisions val enemyCollisions = QuadHelp.getCollisions
(x, y, size, size, 0, 0, 1920, 1080, 0, enemyTree) (x, y, size, size, enemyTree)
val player = val player =
Player.exitAttackedAndCheckEnemies Player.exitAttackedAndCheckEnemies

View File

@@ -320,6 +320,9 @@ struct
let let
val player = #player game val player = #player game
val _ = print ("(playerX, playerY) = (" ^Int.toString (#x player) ^ ", " ^
Int.toString (#y player) ^ ")\n")
val patches = getProjectilePatches player val patches = getProjectilePatches player
val player = PlayerPatch.withPatches (player, patches) val player = PlayerPatch.withPatches (player, patches)
@@ -427,8 +430,8 @@ struct
val ww = Constants.worldWidth val ww = Constants.worldWidth
val wh = Constants.worldHeight val wh = Constants.worldHeight
val enemyCollisions = QuadTree.getCollisions val enemyCollisions = QuadHelp.getCollisions
(x, y, size, size, 0, 0, ww, wh, 0, enemyTree) (x, y, size, size, enemyTree)
in in
Vector.fromList enemyCollisions Vector.fromList enemyCollisions
end end

View File

@@ -11,7 +11,7 @@ struct
val size = projectileSizeInt val size = projectileSizeInt
val {x, y, facing = _} = Vector.sub (projectiles, pos) val {x, y, facing = _} = Vector.sub (projectiles, pos)
val acc = QuadTree.insert (x, y, size, size, 0, 0, 1920, 1080, pos, acc) val acc = QuadHelp.insert (x, y, size, size, pos, acc)
in in
helpGenerateTree (pos + 1, projectiles, acc) helpGenerateTree (pos + 1, projectiles, acc)
end end

View File

@@ -1,160 +0,0 @@
signature QUAD_FOLDER =
sig
type env
type state
val fState: state * env * int -> state
end
functor MakeQuadFolder(Fn: QUAD_FOLDER) =
struct
open QuadTreeType
fun visitTopLeft (iX, iY, iW, iH, qX, qY, qW, qH) =
let
val midX = qW div 2 + qX
val midY = qH div 2 + qY
in
iX <= midX andalso iY <= midY
end
fun visitTopRight (iX, iY, iW, iH, qX, qY, qW, qH) =
let
val midX = qW div 2 + qX
val midY = qH div 2 + qY
in
iX >= midX andalso iY <= midY
end
fun visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH) =
let
val midX = qW div 2 + qX
val midY = qH div 2 + qY
val iFinishY = iY + iH
in
iX <= midX andalso iFinishY >= midY
end
fun visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH) =
let
val midX = qW div 2 + qX
val midY = qH div 2 + qY
val iFinishX = iX + iH
val iFinishY = iY + iH
in
iFinishX >= midX andalso iFinishY >= midY
end
fun foldVec (iX, iY, iW, iH, pos, elements, state, env) =
if pos = Vector.length elements then
state
else
let
val {itemID, ...} = Vector.sub (elements, pos)
val state = Fn.fState (state, env, itemID)
in
foldVec (iX, iY, iW, iH, pos + 1, elements, state, env)
end
fun foldRegion
( itemX
, itemY
, itemW
, itemH
, quadX
, quadY
, quadW
, quadH
, env: Fn.env
, state: Fn.state
, tree: QuadTreeType.t
) =
case tree of
NODE {topLeft, topRight, bottomLeft, bottomRight} =>
let
(* fold over intersecting elements in this vector first *)
val halfW = quadW div 2
val halfH = quadH div 2
val midX = halfW + quadX
val midY = halfH + quadY
val iX = itemX
val iY = itemY
val iW = itemW
val iH = itemH
val qX = quadX
val qY = quadY
val qW = quadW
val qH = quadH
val vtl = visitTopLeft (iX, iY, iW, iH, qX, qY, qW, qH)
val vtr = visitTopRight (iX, iY, iW, iH, qX, qY, qW, qH)
val vbl = visitBottomLeft (iX, iY, iW, iH, qX, qY, qW, qH)
val vbr = visitBottomRight (iX, iY, iW, iH, qX, qY, qW, qH)
val state =
if vtl then
foldRegion
(iX, iY, iW, iH, qX, qY, halfW, halfH, env, state, topLeft)
else
state
val state =
if vtr then
foldRegion
( itemX
, itemY
, itemW
, itemH
, midX
, quadY
, halfW
, halfH
, env
, state
, topRight
)
else
state
val state =
if vbl then
foldRegion
( itemX
, itemY
, itemW
, itemH
, quadX
, midY
, halfW
, halfH
, env
, state
, bottomLeft
)
else
state
in
if vbr then
foldRegion
( itemX
, itemY
, itemW
, itemH
, midX
, midY
, halfW
, halfH
, env
, state
, bottomRight
)
else
state
end
| LEAF elements =>
foldVec (itemX, itemY, itemW, itemH, 0, elements, state, env)
end

View File

@@ -3,8 +3,17 @@ sig
type item = {itemID: int, startX: int, startY: int, width: int, height: int} type item = {itemID: int, startX: int, startY: int, width: int, height: int}
datatype t = datatype t =
NODE of {topLeft: t, topRight: t, bottomLeft: t, bottomRight: t} NODE of
| LEAF of item vector { topLeft: t
, topRight: t
, bottomLeft: t
, bottomRight: t
, x: int
, y: int
, w: int
, h: int
}
| LEAF of {items: item vector, x: int, y: int, w: int, h: int}
datatype quadrant = datatype quadrant =
TOP_LEFT TOP_LEFT
@@ -19,8 +28,17 @@ struct
type item = {itemID: int, startX: int, startY: int, width: int, height: int} type item = {itemID: int, startX: int, startY: int, width: int, height: int}
datatype t = datatype t =
NODE of {topLeft: t, topRight: t, bottomLeft: t, bottomRight: t} NODE of
| LEAF of item vector { topLeft: t
, topRight: t
, bottomLeft: t
, bottomRight: t
, x: int
, y: int
, w: int
, h: int
}
| LEAF of {items: item vector, x: int, y: int, w: int, h: int}
datatype quadrant = datatype quadrant =
TOP_LEFT TOP_LEFT

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@ struct
else else
let let
val {id, x, y, width, height} = Vector.sub (wallVec, pos) val {id, x, y, width, height} = Vector.sub (wallVec, pos)
val acc = QuadTree.insert val acc = QuadHelp.insert
(x, y, width, height, 0, 0, 1920, 1080, id, acc) (x, y, width, height, id, acc)
in in
helpGenerateTree (pos + 1, wallVec, acc) helpGenerateTree (pos + 1, wallVec, acc)
end end

View File

@@ -157,8 +157,6 @@ typedef Pointer Objptr;
extern "C" { extern "C" {
#endif #endif
MLLIB_PUBLIC(void mltonKeyCallback (Int32 x0, Int32 x1, Int32 x2, Int32 x3);)
MLLIB_PUBLIC(void mltonFramebufferSizeCallback (Real32 x0, Real32 x1);)
#undef MLLIB_PRIVATE #undef MLLIB_PRIVATE
#undef MLLIB_PUBLIC #undef MLLIB_PUBLIC

View File

@@ -5,7 +5,6 @@ fcore/constants.sml
fcore/quad-tree-type.sml fcore/quad-tree-type.sml
fcore/quad-tree.sml fcore/quad-tree.sml
fcore/quad-tree-fold.sml
fcore/bin-search.sml fcore/bin-search.sml
fcore/bin-vec.sml fcore/bin-vec.sml