mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-20 07:24:06 +09:00
CCD implementation for hitting ground
Former-commit-id: 786f0b4551b32be44767eac627dc247df3773968 Former-commit-id: 6ff2be7b31c91a581125c5682055776047660195
This commit is contained in:
@@ -8,6 +8,7 @@ import net.torvald.terrarum.tileproperties.TilePropCodex
|
||||
import net.torvald.spriteanimation.SpriteAnimation
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.tileproperties.TileNameCode
|
||||
import org.dyn4j.geometry.ChainedVector2
|
||||
import org.dyn4j.geometry.Vector2
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
@@ -26,19 +27,21 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
var baseHitboxW: Int = 0
|
||||
var baseHitboxH: Int = 0
|
||||
|
||||
@Transient private val map: GameMap = Terrarum.game.map
|
||||
|
||||
/**
|
||||
* Velocity vector (broken down by axes) for newtonian sim.
|
||||
* Acceleration: used in code like:
|
||||
* veloY += 3.0
|
||||
* +3.0 is acceleration. You __accumulate__ acceleration to the velocity.
|
||||
*/
|
||||
val velocity = Vector2(0.0, 0.0)
|
||||
val velocity = ChainedVector2(0.0, 0.0)
|
||||
var veloX: Double
|
||||
get() = velocity.x
|
||||
set(value) { velocity.set(value, veloY) }
|
||||
set(value) { velocity.x = value }
|
||||
var veloY: Double
|
||||
get() = velocity.y
|
||||
set(value) { velocity.set(veloX, value) }
|
||||
set(value) { velocity.y = value }
|
||||
@Transient private val VELO_HARD_LIMIT = 10000.0
|
||||
|
||||
var grounded = false
|
||||
@@ -106,7 +109,7 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
*/
|
||||
@Transient private val SI_TO_GAME_VEL = METER / Terrarum.TARGET_FPS
|
||||
|
||||
@Transient private var gravitation: Double = 0.toDouble()
|
||||
@Transient private var gravitation: Vector2 = map.gravitation
|
||||
@Transient private val DRAG_COEFF = 1.0
|
||||
|
||||
@Transient private val CONTACT_AREA_TOP = 0
|
||||
@@ -135,8 +138,6 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
*/
|
||||
@Transient val INVINCIBILITY_TIME: Int = 500
|
||||
|
||||
@Transient private val map: GameMap
|
||||
|
||||
@Transient private val MASS_DEFAULT: Double = 60.0
|
||||
|
||||
internal val physSleep: Boolean
|
||||
@@ -154,8 +155,10 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
@Transient val DYNAMIC = 2
|
||||
@Transient val STATIC = 3
|
||||
|
||||
private val SLEEP_THRE = 0.05
|
||||
|
||||
init {
|
||||
map = Terrarum.game.map
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,10 +246,13 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
//}
|
||||
//else {
|
||||
// compensate for colliding
|
||||
updateHorizontalCollision()
|
||||
updateVerticalCollision()
|
||||
//updateHorizontalCollision()
|
||||
//updateVerticalCollision()
|
||||
adjustHit()
|
||||
applyNormalForce()
|
||||
|
||||
setHorizontalFriction()
|
||||
if (isPlayerNoClip) setVerticalFriction()
|
||||
//}
|
||||
|
||||
// apply our compensation to actual hitbox
|
||||
@@ -273,16 +279,19 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
* weight; gravitational force in action
|
||||
* W = mass * G (9.8 [m/s^2])
|
||||
*/
|
||||
val W = gravitation * mass
|
||||
val W: Vector2 = gravitation * mass
|
||||
/**
|
||||
* Drag of atmosphere
|
||||
* D = Cd (drag coefficient) * 0.5 * rho (density) * V^2 (velocity) * A (area)
|
||||
*/
|
||||
val A = scale * scale
|
||||
val D = DRAG_COEFF * 0.5 * 1.292 * veloY * veloY * A
|
||||
// TODO replace 1.292 with fluid tile density
|
||||
val A: Double = scale * scale
|
||||
val D: Vector2 = velocity.copy().toVector() * DRAG_COEFF * 0.5 * 1.292 * A
|
||||
|
||||
veloY += clampCeil(
|
||||
(W - D) / mass * SI_TO_GAME_ACC * G_MUL_PLAYABLE_CONST, VELO_HARD_LIMIT)
|
||||
//veloY += (W - D) / mass * SI_TO_GAME_ACC
|
||||
val V: Vector2 = (W - D) / mass * SI_TO_GAME_ACC
|
||||
veloX += V.x
|
||||
veloY += V.y
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +307,18 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setVerticalFriction() {
|
||||
val friction = BASE_FRICTION * tileFriction.tileFrictionToMult()
|
||||
if (veloY < 0) {
|
||||
veloY += friction
|
||||
if (veloY > 0) veloY = 0.0 // compensate overshoot
|
||||
}
|
||||
else if (veloY > 0) {
|
||||
veloY -= friction
|
||||
if (veloY < 0) veloY = 0.0 // compensate overshoot
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVerticalCollision() {
|
||||
if (!isNoCollideWorld) {
|
||||
if (veloY >= 0) { // check downward
|
||||
@@ -451,6 +472,67 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
nextHitbox.setPosition(newX, newY)
|
||||
}
|
||||
|
||||
/**
|
||||
* nextHitbox must NOT altered before this method is called!
|
||||
*/
|
||||
private fun adjustHit() {
|
||||
val delta: Vector2 = Vector2(hitbox.toVector() - nextHitbox.toVector()) // we need to traverse back, so may as well negate at the first place
|
||||
delta *= SLEEP_THRE // CCD delta
|
||||
|
||||
while (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT)
|
||||
|| isColliding(CONTACT_AREA_TOP) || isColliding(CONTACT_AREA_BOTTOM)
|
||||
) {
|
||||
// CCD to the 'delta'
|
||||
nextHitbox.translate(delta)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyNormalForce() {
|
||||
if (!isNoCollideWorld) {
|
||||
if (gravitation.y != 0.0) {
|
||||
if (veloY > SLEEP_THRE && isColliding(CONTACT_AREA_BOTTOM, 0, 1)) {
|
||||
veloY = 0.0
|
||||
grounded = (gravitation.y > 0)
|
||||
}
|
||||
else if (veloY < -SLEEP_THRE && isColliding(CONTACT_AREA_TOP, 0, -1)) {
|
||||
veloY = 0.0
|
||||
grounded = (gravitation.y < 0)
|
||||
}
|
||||
else {
|
||||
grounded = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((veloY > SLEEP_THRE && isColliding(CONTACT_AREA_BOTTOM, 0, 1))
|
||||
|| (veloY < -SLEEP_THRE && isColliding(CONTACT_AREA_TOP, 0, -1))
|
||||
) {
|
||||
veloY = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
if (gravitation.x != 0.0) {
|
||||
if (veloX > SLEEP_THRE && isColliding(CONTACT_AREA_BOTTOM, 1, 0)) {
|
||||
veloX = 0.0
|
||||
grounded = (gravitation.x > 0)
|
||||
}
|
||||
else if (veloX < -SLEEP_THRE && isColliding(CONTACT_AREA_TOP, -1, 0)) {
|
||||
veloY = 0.0
|
||||
grounded = (gravitation.x < 0)
|
||||
}
|
||||
else {
|
||||
grounded = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((veloX > SLEEP_THRE && isColliding(CONTACT_AREA_RIGHT, 1, 0))
|
||||
|| (veloX < -SLEEP_THRE && isColliding(CONTACT_AREA_LEFT, -1, 0))
|
||||
) {
|
||||
veloX = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun elasticReflectX() {
|
||||
if (veloX != 0.0 && (veloX * elasticity).abs() > 0.5) {
|
||||
veloX = -veloX * elasticity
|
||||
@@ -619,35 +701,6 @@ open class ActorWithBody constructor() : Actor(), Visible {
|
||||
return density
|
||||
}
|
||||
|
||||
/**
|
||||
* Get highest fluid resistance value from tiles that the body occupies.
|
||||
* @return
|
||||
*/
|
||||
private val fluidResistance: Int
|
||||
get() {
|
||||
var resistance = 0
|
||||
|
||||
//get highest fluid density
|
||||
val tilePosXStart = (nextHitbox.posX / TSIZE).roundToInt()
|
||||
val tilePosYStart = (nextHitbox.posY / TSIZE).roundToInt()
|
||||
val tilePosXEnd = (nextHitbox.hitboxEnd.x / TSIZE).roundToInt()
|
||||
val tilePosYEnd = (nextHitbox.hitboxEnd.y / TSIZE).roundToInt()
|
||||
for (y in tilePosYStart..tilePosYEnd) {
|
||||
for (x in tilePosXStart..tilePosXEnd) {
|
||||
val tile = map.getTileFromTerrain(x, y)
|
||||
|
||||
if (TilePropCodex.getProp(tile).isFluid) {
|
||||
val thisResistance = TilePropCodex.getProp(tile).movementResistance
|
||||
|
||||
if (thisResistance > resistance) resistance = thisResistance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resistance
|
||||
}
|
||||
fun Int.resistanceToMult(): Double = 1.0 / (1 + this / 16.0)
|
||||
|
||||
private fun clampHitbox() {
|
||||
hitbox.setPositionFromPoint(
|
||||
clampW(hitbox.pointedX), clampH(hitbox.pointedY))
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.torvald.terrarum.gameactors
|
||||
|
||||
import net.torvald.point.Point2d
|
||||
import org.dyn4j.geometry.ChainedVector2
|
||||
import org.dyn4j.geometry.Vector2
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
@@ -54,6 +56,18 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) {
|
||||
this.height = height
|
||||
}
|
||||
|
||||
fun translate(x: Double, y: Double) {
|
||||
setPosition(posX + x, posY + y)
|
||||
}
|
||||
|
||||
fun translate(vec: ChainedVector2) {
|
||||
translate(vec.x, vec.y)
|
||||
}
|
||||
|
||||
fun translate(vec: Vector2) {
|
||||
translate(vec.x, vec.y)
|
||||
}
|
||||
|
||||
fun setPosition(x1: Double, y1: Double) {
|
||||
hitboxStart = Point2d(x1, y1)
|
||||
hitboxEnd = Point2d(x1 + width, y1 + height)
|
||||
@@ -112,4 +126,8 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double) {
|
||||
|
||||
val centeredY: Double
|
||||
get() = (hitboxStart.y + hitboxEnd.y) * 0.5f
|
||||
|
||||
fun toVector(): Vector2 {
|
||||
return Vector2(posX, posY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,10 +322,12 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
|
||||
if (isFuncDown(input, EnumKeyFunc.MOVE_RIGHT) && !isFuncDown(input, EnumKeyFunc.MOVE_LEFT)) {
|
||||
walkHorizontal(false, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
} // ↓F, ↑S
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && !isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
walkHorizontal(true, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
} // ↓F, ↓S
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_LEFT) && isFuncDown(input, EnumKeyFunc.MOVE_RIGHT)) {
|
||||
if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) {
|
||||
walkHorizontal(false, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_RIGHT)
|
||||
@@ -333,8 +335,7 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
|
||||
walkHorizontal(true, AXIS_POSMAX)
|
||||
prevHMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)
|
||||
}
|
||||
}// ↓F, ↓S
|
||||
// ↓F, ↑S
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,15 +347,16 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
|
||||
walkVertical(axisY > 0, AXIS_POSMAX)
|
||||
}
|
||||
} else {
|
||||
// ↑E
|
||||
// ↓D
|
||||
// ↑E, ↓D
|
||||
if (isFuncDown(input, EnumKeyFunc.MOVE_DOWN) && !isFuncDown(input, EnumKeyFunc.MOVE_UP)) {
|
||||
walkVertical(false, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
} // ↓E, ↑D
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && !isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
walkVertical(true, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)
|
||||
} else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
} // ↓E, ↓D
|
||||
else if (isFuncDown(input, EnumKeyFunc.MOVE_UP) && isFuncDown(input, EnumKeyFunc.MOVE_DOWN)) {
|
||||
if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) {
|
||||
walkVertical(false, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_DOWN)
|
||||
@@ -362,10 +364,7 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
|
||||
walkVertical(true, AXIS_POSMAX)
|
||||
prevVMoveKey = KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)
|
||||
}
|
||||
}// ↓E
|
||||
// ↓D
|
||||
// ↓E
|
||||
// ↑D
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user