From cef58f6a7380d6473e99e2da40c2f262e22fc17d Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 10 Aug 2023 23:49:43 +0900 Subject: [PATCH] phys debugging; see L818@ActorWithBody.kt --- src/net/torvald/terrarum/clut/Skybox.kt | 5 + .../terrarum/gameactors/ActorWithBody.kt | 93 ++++++++++--------- src/org/dyn4j/geometry/Vector2.kt | 4 + 3 files changed, 57 insertions(+), 45 deletions(-) diff --git a/src/net/torvald/terrarum/clut/Skybox.kt b/src/net/torvald/terrarum/clut/Skybox.kt index 7efe058d3..899373962 100644 --- a/src/net/torvald/terrarum/clut/Skybox.kt +++ b/src/net/torvald/terrarum/clut/Skybox.kt @@ -214,6 +214,11 @@ object Skybox : Disposable { /** * To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl + * + * Sidenote: the original model involved two cosine curves, but since its Taylor series begins with x^2, I figured + * quadratic curve ought to be good enough, and the error against the original model was below 1/255 for + * reasonable range of p, and that's the reason I stopped at x^2 rather than also taking x^4 into the approximated + * model that is the code below. */ internal fun smoothLinear(p: Double, x0: Double): Double { val x = x0 - 0.5 diff --git a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt index 23110ee19..777408efa 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithBody.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithBody.kt @@ -698,6 +698,7 @@ open class ActorWithBody : Actor { } private fun displaceHitbox() { + val printdbg1 = true // // HOW IT SHOULD WORK // // // //////////////////////// // combineVeloToMoveDelta now @@ -724,25 +725,6 @@ open class ActorWithBody : Actor { if (world != null) { - fun debug1(wut: Any?) { - // vvvvv set it true to make debug print work - if (false) printdbg(this, wut) - } - - fun debug2(wut: Any?) { - // vvvvv set it true to make debug print work - if (false) printdbg(this, wut) - } - - fun debug3(wut: Any?) { - // vvvvv set it true to make debug print work - if (false) printdbg(this, wut) - } - - fun debug4(wut: Any?) { - // vvvvv set it true to make debug print work - if (false) printdbg(this, wut) - } fun BlockAddress.isFeetTile(hitbox: Hitbox): Boolean { val (x, y) = LandUtil.resolveBlockAddr(world!!, this) @@ -763,6 +745,8 @@ open class ActorWithBody : Actor { (x in newTilewiseHitbox.startX.toInt()..newTilewiseHitbox.endX.toInt()) // copied from forEachOccupyingTilePos } + + fun Double.modTile() = this.div(TILE_SIZE).floorToInt().times(TILE_SIZE) fun Double.modTileDelta() = this - this.modTile() @@ -777,10 +761,27 @@ open class ActorWithBody : Actor { // the job of the ccd is that the "next hitbox" would not dig into the terrain greater than the tile size, // in which the modTileDelta returns a wrong value val vectorSum = (externalV + controllerV) - val ccdSteps = (vectorSum.magnitude / TILE_SIZE).floorToInt().coerceIn(2, 16) // adaptive + val ccdSteps = (vectorSum.magnitude / TILE_SIZE).ceilToInt().plus(1).times(2).coerceIn(0, 64) // adaptive + fun debug1(wut: Any?) { + // vvvvv set it true to make debug print work + if (printdbg1 && vectorSum.magnitudeSquared != 0.0) printdbg(this, wut) + } + + fun debug2(wut: Any?) { + // vvvvv set it true to make debug print work + if (true && vectorSum.magnitudeSquared != 0.0) printdbg(this, wut) + } + + if (printdbg1 && vectorSum.magnitudeSquared != 0.0) println("") + debug1("Update Frame: ${INGAME.WORLD_UPDATE_TIMER}") + debug1("Hitbox: ${hitbox}") + + debug1("vec dir: ${Math.toDegrees(vectorSum.direction)}°, value: $vectorSum") + + // * NEW idea: wall pushes the actors (ref. SM64 explained by dutch pancake) * // direction to push is determined by the velocity // proc: @@ -793,14 +794,16 @@ open class ActorWithBody : Actor { // ignore MOST of the codes below (it might be possible to recycle the structure??) // and the idea above has not yet implemented, and may never will. --Torvald, 2018-12-30 - val sixteenStep = (0..ccdSteps).map { hitbox.clone().translate(vectorSum * (it / ccdSteps.toDouble())) } // zeroth step is for special condition + val sixteenStep = (0..ccdSteps).map { hitbox.clone().translate(vectorSum * (it / ccdSteps.toDouble())) } var collidingStep: Int? = null - for (step in 1..ccdSteps) { + for (step in 0..ccdSteps) { val stepBox = sixteenStep[step] + debug2("stepbox[$step]=$stepBox; feet?=${vectorSum.y > PHYS_EPSILON_DIST}") + /*forEachOccupyingTilePos(stepBox) { val tileCoord = LandUtil.resolveBlockAddr(world!!, it) val tile = world!!.getTileFromTerrain(tileCoord.first, tileCoord.second) ?: Block.STONE @@ -812,10 +815,11 @@ open class ActorWithBody : Actor { // 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 } - if (collidingStep != null) break +// if (collidingStep != null) break } var bounceX = false @@ -824,6 +828,7 @@ open class ActorWithBody : Actor { var zeroY = false // collision NOT detected if (collidingStep == null) { + debug1("== Collision step: no collision") hitbox.translate(vectorSum) // grounded = false } @@ -831,6 +836,7 @@ open class ActorWithBody : Actor { else { debug1("== Collision step: $collidingStep / $ccdSteps") + debug1("CCD hitbox: ${sixteenStep[collidingStep]}") val newHitbox = hitbox.reassign(sixteenStep[collidingStep]) @@ -859,11 +865,11 @@ open class ActorWithBody : Actor { // fixme UP and RIGHT && LEFT and DOWN bug - //println("collision: $selfCollisionStatus\tstaircasing: $staircaseStatus") + debug1("collision: $selfCollisionStatus\tstaircasing: $staircaseStatus") when (selfCollisionStatus) { 0 -> { - debug1("[ActorWBMovable] Contradiction -- collision detected by CCD, but isWalled() says otherwise") + debug1("Contradiction -- collision detected by CCD, but isWalled() says otherwise") } 5 -> { zeroX = true @@ -943,10 +949,9 @@ open class ActorWithBody : Actor { (Vector2(offendingHitboxPointX, offendingHitboxPointY) - Vector2(offendingTileWorldX, offendingTileWorldY)).direction + debug1("incidentAngle: ${Math.toDegrees(angleOfIncidence)}°, threshold: ${Math.toDegrees(angleThreshold)}°") - debug1("vectorSum: $vectorSum, vectorDirRaw: ${vectorSum.direction / Math.PI}pi") - debug1("incidentAngle: ${angleOfIncidence / Math.PI}pi, threshold: ${angleThreshold / Math.PI}pi") - + debug1("offendingTileWorldY=$offendingTileWorldY, offendingHitboxPointY=$offendingHitboxPointY") val displacementAbs = Vector2( (offendingTileWorldX - offendingHitboxPointX).abs(), @@ -958,7 +963,7 @@ open class ActorWithBody : Actor { val displacementUnitVector = if (angleOfIncidence == angleThreshold) - -vectorSum + -vectorSum.signum else { when (selfCollisionStatus) { 3 -> if (angleOfIncidence > angleThreshold) Vector2(1.0, 0.0) else Vector2(0.0, -1.0) @@ -970,21 +975,25 @@ open class ActorWithBody : Actor { } val finalDisplacement = - if (angleOfIncidence == angleThreshold) - displacementUnitVector - else +// if (angleOfIncidence == angleThreshold) +// displacementUnitVector +// else Vector2( displacementAbs.x * displacementUnitVector.x, displacementAbs.y * displacementUnitVector.y ) + debug1("displacementAbs=$displacementAbs") + debug1("displacementUnitVector=$displacementUnitVector") + debug1("finalDisplacement=$finalDisplacement") + // 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))) { // 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 - //println("staircasing: $staircaseStatus for $selfCollisionStatus") + debug1("staircasing: $staircaseStatus for $selfCollisionStatus") val stairHeight = if (staircaseStatus == COLLIDING_LEFT) stairHeightLeft else stairHeightRight finalDisplacement.y = -stairHeight @@ -1043,6 +1052,7 @@ open class ActorWithBody : Actor { hitbox.reassign(newHitbox) + debug1("resulting hitbox: $newHitbox") // slam-into-whatever damage (such dirty; much hack; wow) @@ -1069,7 +1079,7 @@ open class ActorWithBody : Actor { } } - fun collisionInterpolatorRun() { + /*fun collisionInterpolatorRun() { fun isWalled2(hitbox: Hitbox, option: Int): Boolean { val newHB = Hitbox.fromTwoPoints( @@ -1086,18 +1096,16 @@ open class ActorWithBody : Actor { val intpStep = 64.0 // make interpolation even if the "next" position is clear of collision - val testHitbox = hitbox.clone() + var testHitbox = hitbox.clone() - // divide the vectors by the constant - externalV /= intpStep - controllerV?.let { controllerV!! /= intpStep } + val vecSum = (externalV + controllerV) repeat(intpStep.toInt()) { // basically we don't care if we're already been encased (e.g. entrapped by falling sand) // change the order and the player gets stuck in the vertical wall // so leave as-is - testHitbox.translate(externalV + controllerV) + testHitbox = hitbox.clone().translate(vecSum * ((it + 1.0) / intpStep)) // <- this would not accumulate errors // vertical collision if (isWalled2(testHitbox, COLLIDING_UD)) { @@ -1114,14 +1122,9 @@ open class ActorWithBody : Actor { hitbox.reassign(testHitbox) - // revert the division because the Acceleration depends on being able to integrate onto the velo values - // don't want to mess up with the value they're expecting - externalV *= intpStep - controllerV?.let { controllerV!! *= intpStep } - // TODO collision damage } - } + }*/ /** * @see /work_files/hitbox_collision_detection_compensation.jpg diff --git a/src/org/dyn4j/geometry/Vector2.kt b/src/org/dyn4j/geometry/Vector2.kt index 3a6665663..c827bbc66 100644 --- a/src/org/dyn4j/geometry/Vector2.kt +++ b/src/org/dyn4j/geometry/Vector2.kt @@ -28,6 +28,7 @@ package org.dyn4j.geometry import org.dyn4j.Epsilon +import kotlin.math.sign /** * This class represents a vector or point in 2D space. @@ -699,6 +700,9 @@ class Vector2 { return a } + val signum: Vector2 + get() = Vector2(this.x.sign, this.y.sign) + companion object { /** A vector representing the x-axis; this vector should not be changed at runtime; used internally */ internal val X_AXIS = Vector2(1.0, 0.0)