somehow fixed but now jumping while walking against wall changes jump behav

This commit is contained in:
minjaesong
2023-08-11 01:04:34 +09:00
parent cef58f6a73
commit 579b6b5b29

View File

@@ -802,20 +802,13 @@ open class ActorWithBody : Actor {
val stepBox = sixteenStep[step] val stepBox = sixteenStep[step]
debug2("stepbox[$step]=$stepBox; feet?=${vectorSum.y > PHYS_EPSILON_DIST}") val downDown = if (this is ActorHumanoid) this.isDownDown else false
val goingDown = (vectorSum.y > PHYS_EPSILON_DIST) || (vectorSum.y >= 0.0 && !isWalled(stepBox, if (gravitation.y >= 0.0) COLLIDING_BOTTOM else COLLIDING_TOP))
/*forEachOccupyingTilePos(stepBox) { debug2("stepbox[$step]=$stepBox; goingDown=$goingDown, downDown=$downDown")
val tileCoord = LandUtil.resolveBlockAddr(world!!, it)
val tile = world!!.getTileFromTerrain(tileCoord.first, tileCoord.second) ?: Block.STONE
if (shouldICollideWithThis(tile) || (it.isFeetTile(stepBox) && shouldICollideWithThisFeet(tile))) { // careful when using feet mode, as it will only look for collision on feet tiles!
collidingStep = step if (isColliding(stepBox, goingDown && !downDown)) {
}
}*/
// trying to use same function as the others, in an effort to eliminate the "contradiction" mentioned below
if (isColliding(stepBox, vectorSum.y > PHYS_EPSILON_DIST)) {
// not registering collision sometimes?
collidingStep = step collidingStep = step
} }
@@ -826,24 +819,13 @@ open class ActorWithBody : Actor {
var bounceY = false var bounceY = false
var zeroX = false var zeroX = false
var zeroY = false var zeroY = false
// collision NOT detected
if (collidingStep == null) {
debug1("== Collision step: no collision")
hitbox.translate(vectorSum)
// grounded = false
}
// collision detected
else {
debug1("== Collision step: $collidingStep / $ccdSteps")
debug1("CCD hitbox: ${sixteenStep[collidingStep]}") val newHitbox = if (collidingStep == null) null else hitbox.clone().reassign(sixteenStep[collidingStep])
var staircaseStatus = 0
val newHitbox = hitbox.reassign(sixteenStep[collidingStep]) var stairHeightLeft = 0.0
var stairHeightRight = 0.0
var staircaseStatus = 0 val selfCollisionStatus = if (newHitbox == null) 0 else
var stairHeightLeft = 0.0 intArrayOf(1,2,4,8).fold(0) { acc, state ->
var stairHeightRight = 0.0
val selfCollisionStatus = intArrayOf(1,2,4,8).fold(0) { acc, state ->
// also update staircaseStatus while we're iterating // also update staircaseStatus while we're iterating
if (state and 5 != 0) { if (state and 5 != 0) {
isWalledStairs(newHitbox, state).let { isWalledStairs(newHitbox, state).let {
@@ -857,6 +839,19 @@ open class ActorWithBody : Actor {
acc or (state * isWalledStairs(newHitbox, state).first.coerceAtMost(1)) acc or (state * isWalledStairs(newHitbox, state).first.coerceAtMost(1))
} }
// collision NOT detected
if (collidingStep == null) {
debug1("== Collision step: no collision")
hitbox.translate(vectorSum)
// grounded = false
}
// collision detected
else {
debug1("== Collision step: $collidingStep / $ccdSteps")
debug1("CCD hitbox: ${sixteenStep[collidingStep]}")
val newHitbox = newHitbox!!
// superseded by isWalledStairs-related codes // superseded by isWalledStairs-related codes
//if (isWalled(newHitbox, COLLIDING_LEFT)) selfCollisionStatus += COLL_LEFTSIDE // 1 //if (isWalled(newHitbox, COLLIDING_LEFT)) selfCollisionStatus += COLL_LEFTSIDE // 1
//if (isWalled(newHitbox, COLLIDING_BOTTOM)) selfCollisionStatus += COLL_BOTTOMSIDE // 2 //if (isWalled(newHitbox, COLLIDING_BOTTOM)) selfCollisionStatus += COLL_BOTTOMSIDE // 2
@@ -868,9 +863,9 @@ open class ActorWithBody : Actor {
debug1("collision: $selfCollisionStatus\tstaircasing: $staircaseStatus") debug1("collision: $selfCollisionStatus\tstaircasing: $staircaseStatus")
when (selfCollisionStatus) { when (selfCollisionStatus) {
0 -> { /*0 -> {
debug1("Contradiction -- collision detected by CCD, but isWalled() says otherwise") debug1("Contradiction -- collision detected by CCD, but isWalled() says otherwise")
} }*/
5 -> { 5 -> {
zeroX = true zeroX = true
} }
@@ -896,177 +891,207 @@ open class ActorWithBody : Actor {
} }
// fire Collision Event with one/two/three-side collision if (selfCollisionStatus == 0) {
// for the ease of writing, this jumptable is separated from above. debug1("== selfCollisionStatus was zero, behaving as if (collidingStep = null)")
when (selfCollisionStatus) { hitbox.translate(vectorSum)
// TODO compose CollisionInfo and fire collided()
} }
else {
// fire Collision Event with one/two/three-side collision
// for the ease of writing, this jumptable is separated from above.
when (selfCollisionStatus) {
// TODO compose CollisionInfo and fire collided()
}
// two-side collision // two-side collision
if (selfCollisionStatus in listOf(3, 6, 9, 12)) { if (selfCollisionStatus in listOf(3, 6, 9, 12)) {
debug1("twoside collision $selfCollisionStatus") debug1("twoside collision $selfCollisionStatus")
// !! this code is based on Dyn4j Vector's coord system; V(1,0) -> 0, V(0,1) -> pi, V(0,-1) -> -pi !! // // !! this code is based on Dyn4j Vector's coord system; V(1,0) -> 0, V(0,1) -> pi, V(0,-1) -> -pi !! //
// we can use selfCollisionStatus to tell which of those four side we care // we can use selfCollisionStatus to tell which of those four side we care
// points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!) // points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!)
val offendingTileWorldX = if (selfCollisionStatus in listOf(6, 12)) val offendingTileWorldX = if (selfCollisionStatus in listOf(6, 12))
newHitbox.endX.div(TILE_SIZE).floorToDouble() * TILE_SIZE - PHYS_EPSILON_DIST newHitbox.endX.div(TILE_SIZE).floorToDouble() * TILE_SIZE - PHYS_EPSILON_DIST
else else
newHitbox.startX.div(TILE_SIZE).ceilToDouble() * TILE_SIZE newHitbox.startX.div(TILE_SIZE).ceilToDouble() * TILE_SIZE
// points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!) // points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!)
val offendingTileWorldY = if (selfCollisionStatus in listOf(3, 6)) val offendingTileWorldY = if (selfCollisionStatus in listOf(3, 6))
newHitbox.endY.div(TILE_SIZE).floorToDouble() * TILE_SIZE - PHYS_EPSILON_DIST newHitbox.endY.div(TILE_SIZE).floorToDouble() * TILE_SIZE - PHYS_EPSILON_DIST
else else
newHitbox.startY.div(TILE_SIZE).ceilToDouble() * TILE_SIZE newHitbox.startY.div(TILE_SIZE).ceilToDouble() * TILE_SIZE
val offendingHitboxPointX = if (selfCollisionStatus in listOf(6, 12)) val offendingHitboxPointX = if (selfCollisionStatus in listOf(6, 12))
newHitbox.endX newHitbox.endX
else else
newHitbox.startX newHitbox.startX
val offendingHitboxPointY = if (selfCollisionStatus in listOf(3, 6)) val offendingHitboxPointY = if (selfCollisionStatus in listOf(3, 6))
newHitbox.endY newHitbox.endY
else else
newHitbox.startY newHitbox.startY
val angleOfIncidence =
val angleOfIncidence =
if (selfCollisionStatus in listOf(3, 9)) if (selfCollisionStatus in listOf(3, 9))
vectorSum.direction.toPositiveRad() vectorSum.direction.toPositiveRad()
else else
vectorSum.direction vectorSum.direction
val angleThreshold = val angleThreshold =
if (selfCollisionStatus in listOf(3, 9)) if (selfCollisionStatus in listOf(3, 9))
(Vector2(offendingHitboxPointX, offendingHitboxPointY) - (Vector2(offendingHitboxPointX, offendingHitboxPointY) -
Vector2(offendingTileWorldX, offendingTileWorldY)).direction.toPositiveRad() Vector2(offendingTileWorldX, offendingTileWorldY)).direction.toPositiveRad()
else else
(Vector2(offendingHitboxPointX, offendingHitboxPointY) - (Vector2(offendingHitboxPointX, offendingHitboxPointY) -
Vector2(offendingTileWorldX, offendingTileWorldY)).direction Vector2(offendingTileWorldX, offendingTileWorldY)).direction
debug1("incidentAngle: ${Math.toDegrees(angleOfIncidence)}°, threshold: ${Math.toDegrees(angleThreshold)}°") debug1(
"incidentAngle: ${Math.toDegrees(angleOfIncidence)}°, threshold: ${
Math.toDegrees(
angleThreshold
)
}°"
)
debug1("offendingTileWorldY=$offendingTileWorldY, offendingHitboxPointY=$offendingHitboxPointY") debug1("offendingTileWorldY=$offendingTileWorldY, offendingHitboxPointY=$offendingHitboxPointY")
val displacementAbs = Vector2( val displacementAbs = Vector2(
(offendingTileWorldX - offendingHitboxPointX).abs(), (offendingTileWorldX - offendingHitboxPointX).abs(),
(offendingTileWorldY - offendingHitboxPointY).abs() (offendingTileWorldY - offendingHitboxPointY).abs()
) )
// FIXME jump-thru-ceil bug on 1px-wide (the edge), case-9 collision (does not occur on case-12 coll.) // FIXME jump-thru-ceil bug on 1px-wide (the edge), case-9 collision (does not occur on case-12 coll.)
val displacementUnitVector = val displacementUnitVector =
if (angleOfIncidence == angleThreshold) if (angleOfIncidence == angleThreshold)
-vectorSum.signum -vectorSum.signum
else { else {
when (selfCollisionStatus) { when (selfCollisionStatus) {
3 -> if (angleOfIncidence > angleThreshold) Vector2(1.0, 0.0) else Vector2(0.0, -1.0) 3 -> if (angleOfIncidence > angleThreshold) Vector2(1.0, 0.0)
6 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, -1.0) else Vector2(-1.0, 0.0) else Vector2(
9 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, 1.0) else Vector2(1.0, 0.0) 0.0,
12 -> if (angleOfIncidence > angleThreshold) Vector2(-1.0, 0.0) else Vector2(0.0, 1.0) -1.0
)
6 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, -1.0)
else Vector2(
-1.0,
0.0
)
9 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, 1.0) else Vector2(1.0, 0.0)
12 -> if (angleOfIncidence > angleThreshold) Vector2(-1.0, 0.0)
else Vector2(
0.0,
1.0
)
else -> throw InternalError("Blame hardware or universe") else -> throw InternalError("Blame hardware or universe")
} }
} }
val finalDisplacement = val finalDisplacement =
// if (angleOfIncidence == angleThreshold) // if (angleOfIncidence == angleThreshold)
// displacementUnitVector // displacementUnitVector
// else // else
Vector2( Vector2(
displacementAbs.x * displacementUnitVector.x, displacementAbs.x * displacementUnitVector.x,
displacementAbs.y * displacementUnitVector.y displacementAbs.y * displacementUnitVector.y
) )
debug1("displacementAbs=$displacementAbs") debug1("displacementAbs=$displacementAbs")
debug1("displacementUnitVector=$displacementUnitVector") debug1("displacementUnitVector=$displacementUnitVector")
debug1("finalDisplacement=$finalDisplacement") debug1("finalDisplacement=$finalDisplacement")
// adjust finalDisplacement for honest-to-god staircasing // adjust finalDisplacement for honest-to-god staircasing
if (physProp.useStairs && vectorSum.y <= 0.0 && staircaseStatus in listOf(1, 4) && selfCollisionStatus in (if (gravitation.y >= 0.0) listOf(3,6) else listOf(9, 12))) { if (physProp.useStairs && vectorSum.y <= 0.0 && staircaseStatus in listOf(1, 4) &&
// remove Y displacement selfCollisionStatus in (if (gravitation.y >= 0.0) listOf(3, 6) else listOf(9, 12))
// let original X velocity to pass-thru instead of snapping to tiles coded above ) {
// pass-thru values are held by the vectorSum // remove Y displacement
// let original X velocity to pass-thru instead of snapping to tiles coded above
// pass-thru values are held by the vectorSum
debug1("staircasing: $staircaseStatus for $selfCollisionStatus") debug1("staircasing: $staircaseStatus for $selfCollisionStatus")
val stairHeight = if (staircaseStatus == COLLIDING_LEFT) stairHeightLeft else stairHeightRight val stairHeight =
finalDisplacement.y = -stairHeight if (staircaseStatus == COLLIDING_LEFT) stairHeightLeft else stairHeightRight
finalDisplacement.x = vectorSum.x finalDisplacement.y = -stairHeight
finalDisplacement.x = vectorSum.x
bounceX = false bounceX = false
bounceY = false bounceY = false
// this will slow down the player, but its main purpose is to hide a bug // this will slow down the player, but its main purpose is to hide a bug
// that when player happens to be "walled" (which zeroes the x velo) they can keep // that when player happens to be "walled" (which zeroes the x velo) they can keep
// move left/right as long as "buried depth" <= stairheight // move left/right as long as "buried depth" <= stairheight
// so we also zero the same exact value here for perfect hiding // so we also zero the same exact value here for perfect hiding
if (controllerV != null) { if (controllerV != null) {
val stairRatio = stairHeight / hitbox.height val stairRatio = stairHeight / hitbox.height
stairPenaltyVector = Math.pow(1.0 - (stairRatio), 90 * stairRatio).times(10).coerceIn(0.00005, 1.0) stairPenaltyVector =
controllerV!!.x = 0.0 Math.pow(1.0 - (stairRatio), 90 * stairRatio).times(10).coerceIn(0.00005, 1.0)
stairPenaltyCounter = 0 controllerV!!.x = 0.0
stairPenaltyMax = Math.pow(stairRatio, 2.4).times(166).roundToInt().coerceAtMost(64) stairPenaltyCounter = 0
stairPenaltyMax = Math.pow(stairRatio, 2.4).times(166).roundToInt().coerceAtMost(64)
}
} }
else {
bounceX = angleOfIncidence == angleThreshold || displacementUnitVector.x != 0.0
bounceY = angleOfIncidence == angleThreshold || displacementUnitVector.y != 0.0
}
newHitbox.translate(finalDisplacement)
debug1("displacement: $finalDisplacement")
// TODO: translate other axis proportionally to the incident vector
} }
else {
bounceX = angleOfIncidence == angleThreshold || displacementUnitVector.x != 0.0 // bounce X/Y
bounceY = angleOfIncidence == angleThreshold || displacementUnitVector.y != 0.0 if (bounceX) {
externalV.x *= elasticity
controllerV?.let { controllerV!!.x *= elasticity }
}
if (bounceY) {
externalV.y *= elasticity
controllerV?.let { controllerV!!.y *= elasticity }
}
if (zeroX) {
externalV.x = 0.0
controllerV?.let { controllerV!!.x = 0.0 }
}
if (zeroY) {
externalV.y = 0.0
controllerV?.let { controllerV!!.y = 0.0 }
} }
newHitbox.translate(finalDisplacement) hitbox.reassign(newHitbox)
debug1("resulting hitbox: $newHitbox")
debug1("displacement: $finalDisplacement") // slam-into-whatever damage (such dirty; much hack; wow)
// vvvv hack (supposed to be 1.0) vvv 50% hack
val collisionDamage = mass * (vectorSum.magnitude / (10.0 / Terrarum.PHYS_TIME_FRAME).sqr()) / fallDamageDampening.sqr() * GAME_TO_SI_ACC
// kg * m / s^2 (mass * acceleration), acceleration -> (vectorMagn / (0.01)^2).gameToSI()
if (collisionDamage != 0.0) debug1("Collision damage: $collisionDamage N")
// FIXME instead of 0.5mv^2, we can model after "change of velocity (aka accel)", just as in real-life; big change of accel on given unit time is what kills
// TODO: translate other axis proportionally to the incident vector // grounded = true
// another platform-related hacks
if (this is ActorHumanoid) downDownVirtually = false
} }
// bounce X/Y
if (bounceX) {
externalV.x *= elasticity
controllerV?.let { controllerV!!.x *= elasticity }
}
if (bounceY) {
externalV.y *= elasticity
controllerV?.let { controllerV!!.y *= elasticity }
}
if (zeroX) {
externalV.x = 0.0
controllerV?.let { controllerV!!.x = 0.0 }
}
if (zeroY) {
externalV.y = 0.0
controllerV?.let { controllerV!!.y = 0.0 }
}
hitbox.reassign(newHitbox)
debug1("resulting hitbox: $newHitbox")
// slam-into-whatever damage (such dirty; much hack; wow)
// vvvv hack (supposed to be 1.0) vvv 50% hack
val collisionDamage = mass * (vectorSum.magnitude / (10.0 / Terrarum.PHYS_TIME_FRAME).sqr()) / fallDamageDampening.sqr() * GAME_TO_SI_ACC
// kg * m / s^2 (mass * acceleration), acceleration -> (vectorMagn / (0.01)^2).gameToSI()
if (collisionDamage != 0.0) debug1("Collision damage: $collisionDamage N")
// FIXME instead of 0.5mv^2, we can model after "change of velocity (aka accel)", just as in real-life; big change of accel on given unit time is what kills
// grounded = true
// another platform-related hacks
if (this is ActorHumanoid) downDownVirtually = false
}// end of collision not detected }// end of collision not detected
@@ -1129,7 +1154,7 @@ open class ActorWithBody : Actor {
/** /**
* @see /work_files/hitbox_collision_detection_compensation.jpg * @see /work_files/hitbox_collision_detection_compensation.jpg
*/ */
private fun isColliding(hitbox: Hitbox, feet: Boolean = false): Boolean { private fun isColliding(hitbox: Hitbox, usePlatformDetection: Boolean = false): Boolean {
if (isNoCollideWorld) return false if (isNoCollideWorld) return false
// detectors are inside of the bounding box // detectors are inside of the bounding box
@@ -1145,7 +1170,8 @@ open class ActorWithBody : Actor {
val tyStart = y1/*.plus(HALF_PIXEL)*/.floorToInt() val tyStart = y1/*.plus(HALF_PIXEL)*/.floorToInt()
val tyEnd = y2/*.plus(HALF_PIXEL)*/.floorToInt() val tyEnd = y2/*.plus(HALF_PIXEL)*/.floorToInt()
return isCollidingInternalStairs(txStart, if (feet) tyEnd else tyStart, txEnd, tyEnd, feet).first > 0 // return isCollidingInternalStairs(txStart, if (feet) tyEnd else tyStart, txEnd, tyEnd, feet).first > 0
return isCollidingInternalStairs(txStart, tyStart, txEnd, tyEnd, usePlatformDetection).first > 0
} }
/** /**
@@ -1327,7 +1353,7 @@ open class ActorWithBody : Actor {
var stairHeight = 0 var stairHeight = 0
var hitFloor = false var hitFloor = false
if (ys.last != ys.first && feet) throw InternalError("Feet mode collision but pyStart != pyEnd ($pyStart .. $pyEnd)") // if (ys.last != ys.first && feet) throw InternalError("Feet mode collision but pyStart != pyEnd ($pyStart .. $pyEnd)")
for (y in ys) { for (y in ys) {