mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-10 18:44:05 +09:00
removing moveDelta to utilise externalForce and controllerMoveDelta separately, for great justice
This commit is contained in:
@@ -1,211 +0,0 @@
|
|||||||
import java.io.InputStream
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just to make things slow down
|
|
||||||
*
|
|
||||||
* This version of Brainfuck fills memory with sanitised input program, and initialises
|
|
||||||
* memory pointer to be just right after your input program. This brings three major improvements:
|
|
||||||
*
|
|
||||||
* 1. Possibility of Self-modifying code
|
|
||||||
* 2. Fucks your brain even more
|
|
||||||
* 3. Forces you to enhance your calm
|
|
||||||
*
|
|
||||||
* Also note that program counter and memory pointer will wrap around when commands are executed,
|
|
||||||
* but not when program is being loaded (will throw OutOfMemoryException).
|
|
||||||
*
|
|
||||||
* If memory at Program Counter is equal to 0xFF, it is interpreted as termination. (0xFF is NOT a
|
|
||||||
* valid opcode for input program, however)
|
|
||||||
*
|
|
||||||
* Created by minjaesong on 17-04-29.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class BFVM(
|
|
||||||
val memSize: Int = 65536,
|
|
||||||
val stdout: OutputStream = System.out,
|
|
||||||
val stdin: InputStream = System.`in`
|
|
||||||
) {
|
|
||||||
private val ZERO = 0.toByte()
|
|
||||||
|
|
||||||
private val INP = '>'.toByte()
|
|
||||||
private val DEP = '<'.toByte()
|
|
||||||
private val INC = '+'.toByte()
|
|
||||||
private val DEC = '-'.toByte()
|
|
||||||
private val PRN = '.'.toByte()
|
|
||||||
private val RDI = ','.toByte()
|
|
||||||
private val JPZ = '['.toByte()
|
|
||||||
private val JPN = ']'.toByte()
|
|
||||||
private val CYA = 0xFF.toByte()
|
|
||||||
|
|
||||||
private val bfOpcodes = hashSetOf<Byte>(43,44,45,46,60,62,91,93)
|
|
||||||
|
|
||||||
private val instSet = hashMapOf<Byte, () -> Unit>(
|
|
||||||
Pair(INP, { INP() }),
|
|
||||||
Pair(DEP, { DEP() }),
|
|
||||||
Pair(INC, { INC() }),
|
|
||||||
Pair(DEC, { DEC() }),
|
|
||||||
Pair(PRN, { PRN() }),
|
|
||||||
Pair(RDI, { RDI() }),
|
|
||||||
Pair(JPZ, { JPZ() }),
|
|
||||||
Pair(JPN, { JPN() })
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
private var r1: Byte = ZERO // Register One (Data register)
|
|
||||||
private var r2 = 0 // Register Two (Scratchpad); theoretically I can use R1 but it limits bracket depth to 254
|
|
||||||
private var mp = 0 // Memory Pointer
|
|
||||||
private var pc = 0 // Program Counter
|
|
||||||
private var ir = 0 // Instruction Register; does lookahead ahd lookbehind
|
|
||||||
|
|
||||||
private val mem = ByteArray(memSize)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Input program is loaded into the memory from index zero.
|
|
||||||
|
|
||||||
Interrupts are hard-coded, 'cause why not?
|
|
||||||
|
|
||||||
Code Mnemo. Desc.
|
|
||||||
----|------|-----
|
|
||||||
INP > Increment pointer
|
|
||||||
DEP < Decrement pointer
|
|
||||||
INC + Increment memory
|
|
||||||
DEC - Decrement memory
|
|
||||||
PRN . Print as text
|
|
||||||
RDI , Read from input
|
|
||||||
JPZ [ Jump past to matching ] when mem is zero
|
|
||||||
JPN ] Jump back to matching [ when mem is non-zero
|
|
||||||
|
|
||||||
[ Internal operations ]
|
|
||||||
CYA 0xFF Marks end of the input program
|
|
||||||
*/
|
|
||||||
|
|
||||||
// NOTE: INC_PC is implied
|
|
||||||
private fun INP() {
|
|
||||||
INC_MP()
|
|
||||||
}
|
|
||||||
private fun DEP() {
|
|
||||||
DEC_MP()
|
|
||||||
}
|
|
||||||
private fun INC() {
|
|
||||||
r1 = mem[mp]
|
|
||||||
r1++
|
|
||||||
mem[mp] = r1
|
|
||||||
}
|
|
||||||
private fun DEC() {
|
|
||||||
r1 = mem[mp]
|
|
||||||
r1--
|
|
||||||
mem[mp] = r1
|
|
||||||
}
|
|
||||||
private fun PRN() {
|
|
||||||
stdout.write(mem[mp].toInt())
|
|
||||||
}
|
|
||||||
private fun RDI() {
|
|
||||||
r1 = stdin.read().toByte()
|
|
||||||
mem[mp] = r1
|
|
||||||
}
|
|
||||||
private fun JPZ() {
|
|
||||||
if (mem[mp] == ZERO) {
|
|
||||||
// lookahead
|
|
||||||
ir = pc
|
|
||||||
r2 = 0
|
|
||||||
|
|
||||||
while (r2 != -1) {
|
|
||||||
INC_IR()
|
|
||||||
if (JPZ == mem[ir]) {
|
|
||||||
r2++
|
|
||||||
}
|
|
||||||
else if (JPN == mem[ir]) {
|
|
||||||
r2--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pc = ir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun JPN() {
|
|
||||||
if (mem[mp] != ZERO) {
|
|
||||||
// lookbehind
|
|
||||||
ir = pc
|
|
||||||
r2 = 0
|
|
||||||
|
|
||||||
while (r2 != -1) {
|
|
||||||
DEC_IR()
|
|
||||||
if (JPN == mem[ir]) {
|
|
||||||
r2++
|
|
||||||
}
|
|
||||||
else if (JPZ == mem[ir]) {
|
|
||||||
r2--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pc = ir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// END OF NOTE (INC_PC is implied)
|
|
||||||
|
|
||||||
|
|
||||||
fun execute() {
|
|
||||||
while (mem[pc] != CYA) {
|
|
||||||
//println("pc = $pc, mp = $mp, inst = ${mem[pc].toChar()}, mem = ${mem[mp]}")
|
|
||||||
instSet[mem[pc]]?.invoke() // fetch-decode-execute in one line
|
|
||||||
INC_PC()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadProgram(program: String) {
|
|
||||||
val program = program.toByteArray(charset = Charsets.US_ASCII)
|
|
||||||
|
|
||||||
pc = 0 // FOR NOW it's PC for input program
|
|
||||||
mp = 0 // where to dump input bytes
|
|
||||||
|
|
||||||
while (pc < program.size) {
|
|
||||||
if (pc >= memSize - 1) {
|
|
||||||
throw OutOfMemoryError("Virtual Machine Out of Memory")
|
|
||||||
}
|
|
||||||
|
|
||||||
r1 = program[pc]
|
|
||||||
|
|
||||||
if (r1 in bfOpcodes) {
|
|
||||||
mem[mp] = r1
|
|
||||||
INC_MP()
|
|
||||||
}
|
|
||||||
|
|
||||||
INC_PC()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mem[program.size] = CYA
|
|
||||||
mp = (program.size + 1) mod memSize
|
|
||||||
pc = 0
|
|
||||||
ir = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun INC_PC() { pc = (pc + 1) mod memSize }
|
|
||||||
private fun INC_IR() { ir = (ir + 1) mod memSize }
|
|
||||||
private fun DEC_IR() { ir = (ir - 1) mod memSize }
|
|
||||||
private fun INC_MP() { mp = (mp + 1) mod memSize }
|
|
||||||
private fun DEC_MP() { mp = (mp - 1) mod memSize }
|
|
||||||
private infix fun Int.mod(other: Int) = Math.floorMod(this, other)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val vm = BFVM()
|
|
||||||
|
|
||||||
val factorials = """
|
|
||||||
+++++++++++
|
|
||||||
>+>>>>++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
>++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>
|
|
||||||
+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-
|
|
||||||
<-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<
|
|
||||||
-]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]
|
|
||||||
>[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++
|
|
||||||
+++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++
|
|
||||||
++++++++++++++++++++++++++++++++++++++++++++.[-]<<
|
|
||||||
<<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<
|
|
||||||
[-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]
|
|
||||||
"""
|
|
||||||
|
|
||||||
vm.loadProgram(factorials)
|
|
||||||
vm.execute()
|
|
||||||
@@ -73,7 +73,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
*/
|
*/
|
||||||
internal val externalForce = Vector2(0.0, 0.0)
|
internal val externalForce = Vector2(0.0, 0.0)
|
||||||
|
|
||||||
val moveDelta = Vector2(0.0, 0.0) // moveDelta = velocity + controllerMoveDelta
|
//val moveDelta = Vector2(0.0, 0.0) // moveDelta = velocity + controllerMoveDelta
|
||||||
@Transient private val VELO_HARD_LIMIT = 100.0
|
@Transient private val VELO_HARD_LIMIT = 100.0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -359,11 +359,9 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
//applyBuoyancy()
|
//applyBuoyancy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// --> Combine all the force (velo) and walk <-- //
|
|
||||||
combineVeloToMoveDelta()
|
|
||||||
// hard limit velocity
|
// hard limit velocity
|
||||||
moveDelta.x = moveDelta.x.bipolarClamp(VELO_HARD_LIMIT) // displaceHitbox SHOULD use moveDelta
|
externalForce.x = externalForce.x.bipolarClamp(VELO_HARD_LIMIT) // displaceHitbox SHOULD use moveDelta
|
||||||
moveDelta.y = moveDelta.y.bipolarClamp(VELO_HARD_LIMIT)
|
externalForce.y = externalForce.y.bipolarClamp(VELO_HARD_LIMIT)
|
||||||
|
|
||||||
if (!isChronostasis) {
|
if (!isChronostasis) {
|
||||||
///////////////////////////////////////////////////
|
///////////////////////////////////////////////////
|
||||||
@@ -405,7 +403,8 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
applyNormalForce()
|
applyNormalForce()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hitbox.translate(moveDelta)
|
hitbox.translate(externalForce)
|
||||||
|
hitbox.translate(controllerMoveDelta)
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
@@ -450,37 +449,37 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
* F 1 velo + walk + velo + walk
|
* F 1 velo + walk + velo + walk
|
||||||
* as a result, the speed will keep increase without it
|
* as a result, the speed will keep increase without it
|
||||||
*/
|
*/
|
||||||
private fun combineVeloToMoveDelta() {
|
/*private fun combineVeloToMoveDelta() {
|
||||||
if (this is Controllable) {
|
if (this is Controllable) {
|
||||||
// decide whether to ignore walkX
|
// decide whether to ignore walkX
|
||||||
if (!(isCollidingSide(hitbox, COLLIDING_LEFT) && walkX < 0)
|
if (!(isTouchingSide(hitbox, COLLIDING_LEFT) && walkX < 0)
|
||||||
|| !(isCollidingSide(hitbox, COLLIDING_RIGHT) && walkX > 0)
|
|| !(isTouchingSide(hitbox, COLLIDING_RIGHT) && walkX > 0)
|
||||||
) {
|
) {
|
||||||
moveDelta.x = externalForce.x + walkX
|
moveDelta.x = externalForce.x + walkX
|
||||||
}
|
}
|
||||||
|
|
||||||
// decide whether to ignore walkY
|
// decide whether to ignore walkY
|
||||||
if (!(isCollidingSide(hitbox, COLLIDING_TOP) && walkY < 0)
|
if (!(isTouchingSide(hitbox, COLLIDING_TOP) && walkY < 0)
|
||||||
|| !(isCollidingSide(hitbox, COLLIDING_BOTTOM) && walkY > 0)
|
|| !(isTouchingSide(hitbox, COLLIDING_BOTTOM) && walkY > 0)
|
||||||
) {
|
) {
|
||||||
moveDelta.y = externalForce.y + walkY
|
moveDelta.y = externalForce.y + walkY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!isCollidingSide(hitbox, COLLIDING_LEFT)
|
if (!isTouchingSide(hitbox, COLLIDING_LEFT)
|
||||||
|| !isCollidingSide(hitbox, COLLIDING_RIGHT)
|
|| !isTouchingSide(hitbox, COLLIDING_RIGHT)
|
||||||
) {
|
) {
|
||||||
moveDelta.x = externalForce.x
|
moveDelta.x = externalForce.x
|
||||||
}
|
}
|
||||||
|
|
||||||
// decide whether to ignore walkY
|
// decide whether to ignore walkY
|
||||||
if (!isCollidingSide(hitbox, COLLIDING_TOP)
|
if (!isTouchingSide(hitbox, COLLIDING_TOP)
|
||||||
|| !isCollidingSide(hitbox, COLLIDING_BOTTOM)
|
|| !isTouchingSide(hitbox, COLLIDING_BOTTOM)
|
||||||
) {
|
) {
|
||||||
moveDelta.y = externalForce.y
|
moveDelta.y = externalForce.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply gravitation to the every falling body (unless not levitating)
|
* Apply gravitation to the every falling body (unless not levitating)
|
||||||
@@ -488,7 +487,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
* Apply only if not grounded; normal force is precessed separately.
|
* Apply only if not grounded; normal force is precessed separately.
|
||||||
*/
|
*/
|
||||||
private fun applyGravitation() {
|
private fun applyGravitation() {
|
||||||
if (!isNoSubjectToGrav && !isTouchingSide(hitbox, COLLIDING_BOTTOM)) {
|
if (!isNoSubjectToGrav) {
|
||||||
//if (!isTouchingSide(hitbox, COLLIDING_BOTTOM)) {
|
//if (!isTouchingSide(hitbox, COLLIDING_BOTTOM)) {
|
||||||
/**
|
/**
|
||||||
* weight; gravitational force in action
|
* weight; gravitational force in action
|
||||||
@@ -503,7 +502,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
* Drag of atmosphere
|
* Drag of atmosphere
|
||||||
* D = Cd (drag coefficient) * 0.5 * rho (density) * V^2 (velocity sqr) * A (area)
|
* D = Cd (drag coefficient) * 0.5 * rho (density) * V^2 (velocity sqr) * A (area)
|
||||||
*/
|
*/
|
||||||
val D: Vector2 = Vector2(moveDelta.x.magnSqr(), moveDelta.y.magnSqr()) * dragCoefficient * 0.5 * A// * tileDensityFluid.toDouble()
|
val D: Vector2 = Vector2(externalForce.x.magnSqr(), externalForce.y.magnSqr()) * dragCoefficient * 0.5 * A// * tileDensityFluid.toDouble()
|
||||||
|
|
||||||
val V: Vector2 = (W - D) / Terrarum.TARGET_FPS.toDouble() * SI_TO_GAME_ACC
|
val V: Vector2 = (W - D) / Terrarum.TARGET_FPS.toDouble() * SI_TO_GAME_ACC
|
||||||
|
|
||||||
@@ -514,6 +513,8 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
|
|
||||||
private fun applyNormalForce() {
|
private fun applyNormalForce() {
|
||||||
if (!isNoCollideWorld) {
|
if (!isNoCollideWorld) {
|
||||||
|
val moveDelta = externalForce + controllerMoveDelta
|
||||||
|
|
||||||
// axis Y. Using operand >= and hitting the ceiling will lock the player to the position
|
// axis Y. Using operand >= and hitting the ceiling will lock the player to the position
|
||||||
|
|
||||||
// was moving downward?
|
// was moving downward?
|
||||||
@@ -552,7 +553,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
/**
|
/**
|
||||||
* nextHitbox must NOT be altered before this method is called!
|
* nextHitbox must NOT be altered before this method is called!
|
||||||
*/
|
*/
|
||||||
@Deprecated("It's stupid anyway.") private fun displaceByCCD() {
|
/*@Deprecated("It's stupid anyway.") private fun displaceByCCD() {
|
||||||
if (!isNoCollideWorld) {
|
if (!isNoCollideWorld) {
|
||||||
if (!isColliding(hitbox))
|
if (!isColliding(hitbox))
|
||||||
return
|
return
|
||||||
@@ -574,7 +575,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
|
|
||||||
//println("ccdCollided: $ccdCollided")
|
//println("ccdCollided: $ccdCollided")
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nextHitbox must NOT be altered before this method is called!
|
* nextHitbox must NOT be altered before this method is called!
|
||||||
@@ -596,137 +597,132 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
// find "edge" point using binary search
|
// find "edge" point using binary search
|
||||||
// [END OF SUBROUTINE]
|
// [END OF SUBROUTINE]
|
||||||
|
|
||||||
if (moveDelta.isZero) {
|
|
||||||
println("0")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun getBacktrackDelta(percentage: Double): Vector2 {
|
fun getBacktrackDelta(percentage: Double): Vector2 {
|
||||||
if (percentage < 0.0 || percentage > 1.0)
|
if (percentage < 0.0 || percentage > 1.0)
|
||||||
throw IllegalArgumentException("$percentage")
|
throw IllegalArgumentException("$percentage")
|
||||||
|
|
||||||
return moveDelta * percentage
|
return externalForce * percentage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val simulationHitbox = hitbox.clone()
|
if (externalForce.isZero) {
|
||||||
var ccdTick: Int = ccdSteps // 0..15: collision detected, 16: not
|
println("externalForce is zero")
|
||||||
|
|
||||||
// do CCD first
|
|
||||||
for (i in 1..ccdSteps) { // start from 1: if you are grounded, CCD of 0 will report as COLLIDING and will not you jump
|
|
||||||
simulationHitbox.reassign(hitbox)
|
|
||||||
simulationHitbox.translate(getBacktrackDelta(i.toDouble() / ccdSteps))
|
|
||||||
|
|
||||||
println("ccd $i, endY = ${simulationHitbox.endPointY}")
|
|
||||||
|
|
||||||
if (isColliding(simulationHitbox)) { //COLLIDING_EXTRA_SIZE: doing trick so that final pos would be x.99800000 instead of y.0000000
|
|
||||||
ccdTick = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
val simulationHitbox = hitbox.clone()
|
||||||
|
var ccdTick: Int = ccdSteps // 0..15: collision detected, 16: not
|
||||||
|
|
||||||
// FIXME CCD-ing is not right (not-so-crucial for most cases anyway...)
|
// do CCD first
|
||||||
// DESCRIPTION: 0.999999999999 ~ 1.0 pixels off
|
for (i in 1..ccdSteps) { // start from 1: if you are grounded, CCD of 0 will report as COLLIDING and will not you jump
|
||||||
// I think collision detection is one pixel off -- very fucking likely
|
|
||||||
|
|
||||||
|
|
||||||
println("ccdTick = $ccdTick, endY = ${simulationHitbox.endPointY}")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
// FIXME THE EDGE CASE //
|
|
||||||
/////////////////////////
|
|
||||||
/*
|
|
||||||
no collision; endY = 7989.683178548076
|
|
||||||
no collision; endY = 7995.169755787131
|
|
||||||
no collision; endY = 8000.749058412345 <-- CCD did NOT caught collision (8000.0 be collision)
|
|
||||||
reflY
|
|
||||||
0
|
|
||||||
0
|
|
||||||
*/
|
|
||||||
// THIS is also the consequence of COLLISION DETECTION being 1 pixel off
|
|
||||||
//
|
|
||||||
// Fixed the issue by offsetting hitbox when doing collision detection,
|
|
||||||
// now it won't jump as if it's stuck in the ground (L/R stuck)
|
|
||||||
// "snap to closest tile" does not make any difference
|
|
||||||
|
|
||||||
|
|
||||||
// collision not found
|
|
||||||
if (ccdTick == ccdSteps) {
|
|
||||||
hitbox.translate(moveDelta)
|
|
||||||
println("no collision; endY = ${hitbox.endPointY}")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
println("embedding befure: ${simulationHitbox.endPointY}")
|
|
||||||
|
|
||||||
// find no-collision point using binary search
|
|
||||||
// trust me, X- and Y-axis must move simultaneously.
|
|
||||||
//// binary search ////
|
|
||||||
if (ccdTick >= 1) {
|
|
||||||
var low = (ccdTick - 1).toDouble() / ccdSteps
|
|
||||||
var high = (ccdTick).toDouble() / ccdSteps
|
|
||||||
var bmid: Double
|
|
||||||
|
|
||||||
(1..binaryBranchingMax).forEach { _ ->
|
|
||||||
|
|
||||||
bmid = (low + high) / 2.0
|
|
||||||
|
|
||||||
simulationHitbox.reassign(hitbox)
|
simulationHitbox.reassign(hitbox)
|
||||||
simulationHitbox.translate(getBacktrackDelta(bmid))
|
simulationHitbox.translate(getBacktrackDelta(i.toDouble() / ccdSteps))
|
||||||
|
|
||||||
print("bmid = $bmid, new endY: ${simulationHitbox.endPointY}")
|
println("ccd $i, endY = ${simulationHitbox.endPointY}")
|
||||||
|
|
||||||
// set new mid
|
|
||||||
if (isColliding(simulationHitbox)) { //COLLIDING_EXTRA_SIZE: doing trick so that final pos would be x.99800000 instead of y.0000000
|
if (isColliding(simulationHitbox)) { //COLLIDING_EXTRA_SIZE: doing trick so that final pos would be x.99800000 instead of y.0000000
|
||||||
print(", going back\n")
|
ccdTick = i
|
||||||
high = bmid
|
break
|
||||||
}
|
|
||||||
else {
|
|
||||||
print(", going forth\n")
|
|
||||||
low = bmid
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println("binarySearch embedding: ${simulationHitbox.endPointY}")
|
// FIXME CCD-ing is not right (not-so-crucial for most cases anyway...)
|
||||||
|
// DESCRIPTION: 0.999999999999 ~ 1.0 pixels off
|
||||||
|
// I think collision detection is one pixel off -- very fucking likely
|
||||||
|
|
||||||
|
|
||||||
|
println("ccdTick = $ccdTick, endY = ${simulationHitbox.endPointY}")
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// FIXME THE EDGE CASE //
|
||||||
|
/////////////////////////
|
||||||
|
/*
|
||||||
|
no collision; endY = 7989.683178548076
|
||||||
|
no collision; endY = 7995.169755787131
|
||||||
|
no collision; endY = 8000.749058412345 <-- CCD did NOT caught collision (8000.0 be collision)
|
||||||
|
reflY
|
||||||
|
0
|
||||||
|
0
|
||||||
|
*/
|
||||||
|
// THIS is also the consequence of COLLISION DETECTION being 1 pixel off
|
||||||
|
//
|
||||||
|
// Fixed the issue by offsetting hitbox when doing collision detection,
|
||||||
|
// now it won't jump as if it's stuck in the ground (L/R stuck)
|
||||||
|
// "snap to closest tile" does not make any difference
|
||||||
|
|
||||||
|
|
||||||
|
// collision not found
|
||||||
|
if (ccdTick == ccdSteps) {
|
||||||
|
hitbox.translate(externalForce)
|
||||||
|
println("no collision; endX = ${hitbox.endPointX}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
println("embedding befure: ${simulationHitbox.endPointX}")
|
||||||
|
|
||||||
|
// find no-collision point using binary search
|
||||||
|
// trust me, X- and Y-axis must move simultaneously.
|
||||||
|
//// binary search ////
|
||||||
|
if (ccdTick >= 1) {
|
||||||
|
var low = (ccdTick - 1).toDouble() / ccdSteps
|
||||||
|
var high = (ccdTick).toDouble() / ccdSteps
|
||||||
|
var bmid: Double
|
||||||
|
|
||||||
|
(1..binaryBranchingMax).forEach { _ ->
|
||||||
|
|
||||||
|
bmid = (low + high) / 2.0
|
||||||
|
|
||||||
|
simulationHitbox.reassign(hitbox)
|
||||||
|
simulationHitbox.translate(getBacktrackDelta(bmid))
|
||||||
|
|
||||||
|
print("bmid = $bmid, new endY: ${simulationHitbox.endPointY}")
|
||||||
|
|
||||||
|
// set new mid
|
||||||
|
if (isColliding(simulationHitbox)) { //COLLIDING_EXTRA_SIZE: doing trick so that final pos would be x.99800000 instead of y.0000000
|
||||||
|
print(", going back\n")
|
||||||
|
high = bmid
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print(", going forth\n")
|
||||||
|
low = bmid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println("binarySearch embedding: ${simulationHitbox.endPointY}")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// snap to closest tile
|
||||||
|
// naturally, binarySearch gives you a point like 7584.99999999 (barely not colliding) or
|
||||||
|
// 7585.000000000 (colliding as fuck), BUT what we want is 7584.00000000 .
|
||||||
|
// [Procedure]
|
||||||
|
// 1. get touching area of four sides incl. edge points
|
||||||
|
// 2. a side with most touching area is the "colliding side"
|
||||||
|
// 3. round the hitbox so that coord of "colliding" side be integer
|
||||||
|
// 3.1. there's two main cases: "main axis" being X; "main axis" being Y
|
||||||
|
// 3.2. edge cases: (TBA)
|
||||||
|
|
||||||
|
// test: assume hitting bottom
|
||||||
|
val roundedInteger = simulationHitbox.endPointY.div(TILE_SIZE).roundInt() * TILE_SIZE
|
||||||
|
val displacementMainAxis = roundedInteger - simulationHitbox.endPointY
|
||||||
|
val displacementSecondAxis = displacementMainAxis * externalForce.x / externalForce.y
|
||||||
|
|
||||||
|
simulationHitbox.translate(displacementSecondAxis, displacementMainAxis)
|
||||||
|
println("dx: $displacementSecondAxis, dy: $displacementMainAxis")
|
||||||
|
|
||||||
|
|
||||||
|
println("externalForce: $externalForce, displacement: ${simulationHitbox - hitbox}")
|
||||||
|
//hitbox.translate(getBacktrackDelta(bmid))
|
||||||
|
hitbox.reassign(simulationHitbox)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// snap to closest tile
|
// resolve controllerMoveDelta
|
||||||
// binarySearch gives embedding of ~3 pixels, which is safe to round up/down. // binarySearch gives embedding: it shouldn't but it does :\
|
|
||||||
// [Procedure]
|
|
||||||
// 1. get touching area of four sides incl. edge points
|
|
||||||
// 2. a side with most touching area is the "colliding side"
|
|
||||||
// 3. round the hitbox so that coord of "colliding" side be integer
|
|
||||||
// 3.1. there's two main cases: "main axis" being X; "main axis" being Y
|
|
||||||
// 3.2. edge cases: (TBA)
|
|
||||||
|
|
||||||
// test: assume hitting bottom
|
|
||||||
/*val roundedInteger = simulationHitbox.endPointY.div(TILE_SIZE).roundInt() * TILE_SIZE
|
|
||||||
val displacementMainAxis = roundedInteger - simulationHitbox.endPointY
|
|
||||||
val displacementSecondAxis = displacementMainAxis * moveDelta.x / moveDelta.y
|
|
||||||
|
|
||||||
simulationHitbox.translate(displacementSecondAxis, displacementMainAxis)
|
|
||||||
println("dx: $displacementSecondAxis, dy: $displacementMainAxis")*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// manual compensation
|
|
||||||
// standing on the floow
|
|
||||||
if (isTouchingSide(simulationHitbox, COLLIDING_BOTTOM)) {
|
|
||||||
simulationHitbox.translate(0.0, -1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//println("moveDelta: $moveDelta, displacement: ${simulationHitbox - hitbox})
|
|
||||||
println("moveDelta: $moveDelta, displacement: ${simulationHitbox - hitbox}")
|
|
||||||
//hitbox.translate(getBacktrackDelta(bmid))
|
|
||||||
hitbox.reassign(simulationHitbox)
|
|
||||||
|
|
||||||
println("# final hitbox.endY = ${hitbox.endPointY}")
|
println("# final hitbox.endY = ${hitbox.endPointY}")
|
||||||
|
|
||||||
|
|
||||||
@@ -775,6 +771,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
*/
|
*/
|
||||||
private fun hitAndForciblyReflectY() {
|
private fun hitAndForciblyReflectY() {
|
||||||
println("hitAndForciblyReflectY")
|
println("hitAndForciblyReflectY")
|
||||||
|
val moveDelta = externalForce + controllerMoveDelta
|
||||||
// TODO HARK! I have changed veloX/Y to moveDelta.x/y
|
// TODO HARK! I have changed veloX/Y to moveDelta.x/y
|
||||||
if (moveDelta.y < 0) {
|
if (moveDelta.y < 0) {
|
||||||
// kills movement if it is Controllable
|
// kills movement if it is Controllable
|
||||||
@@ -793,7 +790,8 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
externalForce.y = moveDelta.y * CEILING_HIT_ELASTICITY
|
externalForce.y = moveDelta.y * CEILING_HIT_ELASTICITY
|
||||||
//externalForce.y = 0.0
|
//externalForce.y = 0.0
|
||||||
|
|
||||||
hitbox.translatePosY(0.5)
|
|
||||||
|
//hitbox.translatePosY(0.5) // TODO why we need it?
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw Error("Check this out bitch (moveDelta.y = ${moveDelta.y})")
|
throw Error("Check this out bitch (moveDelta.y = ${moveDelta.y})")
|
||||||
@@ -809,10 +807,10 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
if (isNoCollideWorld) return false
|
if (isNoCollideWorld) return false
|
||||||
|
|
||||||
// offsets will stretch and shrink detection box according to the argument
|
// offsets will stretch and shrink detection box according to the argument
|
||||||
val x1 = hitbox.posX - A_PIXEL
|
val x1 = hitbox.posX
|
||||||
val x2 = hitbox.posX + hitbox.width
|
val x2 = hitbox.endPointX - A_PIXEL
|
||||||
val y1 = hitbox.posY - A_PIXEL
|
val y1 = hitbox.posY
|
||||||
val y2 = hitbox.posY + hitbox.height
|
val y2 = hitbox.endPointY - A_PIXEL
|
||||||
|
|
||||||
|
|
||||||
val txStart = x1.div(TILE_SIZE).floorInt() // plus(1.0) : adjusting for yet another anomaly
|
val txStart = x1.div(TILE_SIZE).floorInt() // plus(1.0) : adjusting for yet another anomaly
|
||||||
@@ -878,46 +876,6 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean
|
|||||||
return isCollidingInternal(txStart, tyStart, txEnd, tyEnd)
|
return isCollidingInternal(txStart, tyStart, txEnd, tyEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun isCollidingSide(hitbox: Hitbox, option: Int): Boolean {
|
|
||||||
val x1: Double
|
|
||||||
val x2: Double
|
|
||||||
val y1: Double
|
|
||||||
val y2: Double
|
|
||||||
if (option == COLLIDING_TOP) {
|
|
||||||
x1 = hitbox.posX
|
|
||||||
x2 = hitbox.endPointX
|
|
||||||
y1 = hitbox.posY
|
|
||||||
y2 = y1
|
|
||||||
}
|
|
||||||
else if (option == COLLIDING_BOTTOM) {
|
|
||||||
x1 = hitbox.posX
|
|
||||||
x2 = hitbox.endPointX
|
|
||||||
y1 = hitbox.endPointY
|
|
||||||
y2 = y1
|
|
||||||
}
|
|
||||||
else if (option == COLLIDING_LEFT) {
|
|
||||||
x1 = hitbox.posX
|
|
||||||
x2 = x1
|
|
||||||
y1 = hitbox.posY
|
|
||||||
y2 = hitbox.endPointY
|
|
||||||
}
|
|
||||||
else if (option == COLLIDING_RIGHT) {
|
|
||||||
x1 = hitbox.endPointX
|
|
||||||
x2 = x1
|
|
||||||
y1 = hitbox.posY
|
|
||||||
y2 = hitbox.endPointY
|
|
||||||
}
|
|
||||||
else throw IllegalArgumentException()
|
|
||||||
|
|
||||||
val txStart = x1.div(TILE_SIZE).floorInt()
|
|
||||||
val txEnd = x2.div(TILE_SIZE).floorInt()
|
|
||||||
val tyStart = y1.div(TILE_SIZE).floorInt()
|
|
||||||
val tyEnd = y2.div(TILE_SIZE).floorInt()
|
|
||||||
|
|
||||||
return isCollidingInternal(txStart, tyStart, txEnd, tyEnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isCollidingInternal(txStart: Int, tyStart: Int, txEnd: Int, tyEnd: Int): Boolean {
|
private fun isCollidingInternal(txStart: Int, tyStart: Int, txEnd: Int, tyEnd: Int): Boolean {
|
||||||
for (y in tyStart..tyEnd) {
|
for (y in tyStart..tyEnd) {
|
||||||
for (x in txStart..txEnd) {
|
for (x in txStart..txEnd) {
|
||||||
|
|||||||
Reference in New Issue
Block a user