Going up the stairs again
This commit is contained in:
parent
05e21deb57
commit
35e5b64606
src
kbox2d/kotlin/ru/dbotthepony/kbox2d
main/kotlin/ru/dbotthepony/kstarbound
@ -6,6 +6,9 @@ import ru.dbotthepony.kbox2d.collision.DynamicTree
|
|||||||
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
import ru.dbotthepony.kbox2d.collision.BroadPhase
|
||||||
|
|
||||||
fun interface ProxyQueryCallback {
|
fun interface ProxyQueryCallback {
|
||||||
|
/**
|
||||||
|
* Return false to terminate query.
|
||||||
|
*/
|
||||||
fun invoke(nodeId: Int, userData: Any?): Boolean
|
fun invoke(nodeId: Int, userData: Any?): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +64,7 @@ interface IProxieable {
|
|||||||
* Query an AABB for overlapping proxies. The callback class
|
* Query an AABB for overlapping proxies. The callback class
|
||||||
* is called for each proxy that overlaps the supplied AABB.
|
* is called for each proxy that overlaps the supplied AABB.
|
||||||
*
|
*
|
||||||
* @return Whenever callback returned false
|
* @return Whenever callback terminated query early
|
||||||
*/
|
*/
|
||||||
fun query(aabb: AABB, callback: ProxyQueryCallback): Boolean
|
fun query(aabb: AABB, callback: ProxyQueryCallback): Boolean
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package ru.dbotthepony.kbox2d.api
|
package ru.dbotthepony.kbox2d.api
|
||||||
|
|
||||||
import ru.dbotthepony.kvector.api.concrete.IMatrix2d
|
import ru.dbotthepony.kvector.api.concrete.IMatrix2d
|
||||||
@ -130,8 +133,10 @@ class Rotation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A transform contains translation and rotation. It is used to represent
|
/**
|
||||||
/// the position and orientation of rigid frames.
|
* A transform contains translation and rotation. It is used to represent
|
||||||
|
* the position and orientation of rigid frames.
|
||||||
|
*/
|
||||||
class Transform(
|
class Transform(
|
||||||
position: Vector2d,
|
position: Vector2d,
|
||||||
val rotation: Rotation,
|
val rotation: Rotation,
|
||||||
@ -200,13 +205,17 @@ class Transform(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This describes the motion of a body/shape for TOI computation.
|
/**
|
||||||
|
* This describes the motion of a body/shape for TOI computation.
|
||||||
* Shapes are defined with respect to the body origin, which may
|
* Shapes are defined with respect to the body origin, which may
|
||||||
* no coincide with the center of mass. However, to support dynamics
|
* no coincide with the center of mass. However, to support dynamics
|
||||||
* we must interpolate the center of mass position.
|
* we must interpolate the center of mass position.
|
||||||
*/
|
*/
|
||||||
class Sweep {
|
class Sweep {
|
||||||
var localCenter: Vector2d = Vector2d.ZERO ///< local center of mass position
|
/**
|
||||||
|
* local center of mass position
|
||||||
|
*/
|
||||||
|
var localCenter: Vector2d = Vector2d.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
if (!value.isFinite) {
|
if (!value.isFinite) {
|
||||||
throw IllegalArgumentException("Tried to set illegal local center $value")
|
throw IllegalArgumentException("Tried to set illegal local center $value")
|
||||||
@ -215,7 +224,10 @@ class Sweep {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var c0: Vector2d = Vector2d.ZERO ///< center world positions
|
/**
|
||||||
|
* center world positions
|
||||||
|
*/
|
||||||
|
var c0: Vector2d = Vector2d.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
if (!value.isFinite) {
|
if (!value.isFinite) {
|
||||||
throw IllegalArgumentException("Tried to set illegal center world position $value")
|
throw IllegalArgumentException("Tried to set illegal center world position $value")
|
||||||
@ -224,7 +236,10 @@ class Sweep {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var c: Vector2d = Vector2d.ZERO ///< center world positions
|
/**
|
||||||
|
* center world positions
|
||||||
|
*/
|
||||||
|
var c: Vector2d = Vector2d.ZERO
|
||||||
set(value) {
|
set(value) {
|
||||||
if (!value.isFinite) {
|
if (!value.isFinite) {
|
||||||
throw IllegalArgumentException("Tried to set illegal center world position $value")
|
throw IllegalArgumentException("Tried to set illegal center world position $value")
|
||||||
@ -233,7 +248,13 @@ class Sweep {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var a0: Double = 0.0 ///< world angles
|
var oldCenter by this::c0
|
||||||
|
var newCenter by this::c
|
||||||
|
|
||||||
|
/**
|
||||||
|
* world angles
|
||||||
|
*/
|
||||||
|
var a0: Double = 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
if (!value.isFinite()) {
|
if (!value.isFinite()) {
|
||||||
throw IllegalArgumentException("Tried to set illegal non finite $value")
|
throw IllegalArgumentException("Tried to set illegal non finite $value")
|
||||||
@ -246,7 +267,10 @@ class Sweep {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var a: Double = 0.0 ///< world angles
|
/**
|
||||||
|
* world angles
|
||||||
|
*/
|
||||||
|
var a: Double = 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
if (!value.isFinite()) {
|
if (!value.isFinite()) {
|
||||||
throw IllegalArgumentException("Tried to set illegal non finite $value")
|
throw IllegalArgumentException("Tried to set illegal non finite $value")
|
||||||
@ -259,8 +283,13 @@ class Sweep {
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fraction of the current time step in the range [0,1]
|
var oldAngles by this::a0
|
||||||
/// c0 and a0 are the positions at alpha0.
|
var newAngles by this::a
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fraction of the current time step in the range [0,1]
|
||||||
|
* [c0] and [a0] are the positions at alpha0.
|
||||||
|
*/
|
||||||
var alpha0: Double = 0.0
|
var alpha0: Double = 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
if (!value.isFinite()) {
|
if (!value.isFinite()) {
|
||||||
@ -276,24 +305,30 @@ class Sweep {
|
|||||||
|
|
||||||
/** Get the interpolated transform at a specific time.
|
/** Get the interpolated transform at a specific time.
|
||||||
* @param transform the output transform
|
* @param transform the output transform
|
||||||
* @param beta is a factor in [0,1], where 0 indicates alpha0.
|
* @param beta is a factor in [0,1], where 0 indicates [alpha0].
|
||||||
* https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
|
* https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
|
||||||
*/
|
*/
|
||||||
fun getTransform(loadInto: Transform, beta: Double) {
|
fun getTransform(transform: Transform, beta: Double) {
|
||||||
loadInto.position = c0 * (1.0 - beta) + c * beta
|
transform.position = c0 * (1.0 - beta) + c * beta
|
||||||
val angle = (1.0 - beta) * a0 + beta * a
|
val angle = (1.0 - beta) * a0 + beta * a
|
||||||
loadInto.rotation.set(angle)
|
transform.rotation.set(angle)
|
||||||
loadInto.position -= loadInto.position * localCenter
|
transform.position -= transform.position * localCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the interpolated transform at a specific time.
|
||||||
|
* @param beta is a factor in [0,1], where 0 indicates [alpha0].
|
||||||
|
* https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
|
||||||
|
*/
|
||||||
fun getTransform(beta: Double): Transform {
|
fun getTransform(beta: Double): Transform {
|
||||||
val v = Transform()
|
val v = Transform()
|
||||||
getTransform(v, beta)
|
getTransform(v, beta)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance the sweep forward, yielding a new initial state.
|
/**
|
||||||
/// @param alpha the new initial time.
|
* Advance the sweep forward, yielding a new initial state.
|
||||||
|
* @param alpha the new initial time.
|
||||||
|
*/
|
||||||
fun advance(alpha: Double) {
|
fun advance(alpha: Double) {
|
||||||
require(alpha < 1.0) { "Bad advance value $alpha" }
|
require(alpha < 1.0) { "Bad advance value $alpha" }
|
||||||
|
|
||||||
@ -303,7 +338,9 @@ class Sweep {
|
|||||||
alpha0 = alpha
|
alpha0 = alpha
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize the angles.
|
/**
|
||||||
|
* Normalize the angles.
|
||||||
|
*/
|
||||||
fun normalize() {
|
fun normalize() {
|
||||||
val d = floor(a0 / (PI * 2.0)) * 2.0 * PI
|
val d = floor(a0 / (PI * 2.0)) * 2.0 * PI
|
||||||
a0 -= d
|
a0 -= d
|
||||||
|
@ -7,6 +7,7 @@ import ru.dbotthepony.kbox2d.collision.shapes.EdgeShape
|
|||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.ndouble.times
|
import ru.dbotthepony.kvector.vector.ndouble.times
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
var b2_gjkCalls = 0
|
var b2_gjkCalls = 0
|
||||||
private set
|
private set
|
||||||
@ -21,11 +22,11 @@ var b2_gjkMaxIters = 0
|
|||||||
* A distance proxy is used by the GJK algorithm.
|
* A distance proxy is used by the GJK algorithm.
|
||||||
* It encapsulates any shape.
|
* It encapsulates any shape.
|
||||||
*/
|
*/
|
||||||
class DistanceProxy {
|
class DistanceProxy(shape: IShape<*>, index: Int) {
|
||||||
val vertices: List<Vector2d>
|
val vertices: List<Vector2d>
|
||||||
val radius: Double
|
val radius: Double
|
||||||
|
|
||||||
constructor(shape: IShape<*>, index: Int) {
|
init {
|
||||||
when (shape.type) {
|
when (shape.type) {
|
||||||
IShape.Type.CIRCLE -> {
|
IShape.Type.CIRCLE -> {
|
||||||
val circle = shape as CircleShape
|
val circle = shape as CircleShape
|
||||||
@ -41,7 +42,7 @@ class DistanceProxy {
|
|||||||
|
|
||||||
IShape.Type.POLYGON -> {
|
IShape.Type.POLYGON -> {
|
||||||
val polygon = shape as PolygonShape
|
val polygon = shape as PolygonShape
|
||||||
vertices = polygon.vertices
|
vertices = Collections.unmodifiableList(polygon.vertices)
|
||||||
radius = polygon.radius
|
radius = polygon.radius
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,11 +63,6 @@ class DistanceProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(vertices: List<Vector2d>, radius: Double) {
|
|
||||||
this.vertices = vertices
|
|
||||||
this.radius = radius
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the supporting vertex index in the given direction.
|
* Get the supporting vertex index in the given direction.
|
||||||
*/
|
*/
|
||||||
|
@ -230,20 +230,22 @@ const val k_maxIterations = 20
|
|||||||
fun b2TimeOfImpact(
|
fun b2TimeOfImpact(
|
||||||
proxyA: DistanceProxy,
|
proxyA: DistanceProxy,
|
||||||
proxyB: DistanceProxy,
|
proxyB: DistanceProxy,
|
||||||
_sweepA: Sweep,
|
sweepA: Sweep,
|
||||||
_sweepB: Sweep,
|
sweepB: Sweep,
|
||||||
tMax: Double, // defines sweep interval [0, tMax]
|
tMax: Double, // defines sweep interval [0, tMax]
|
||||||
): TOIOutput {
|
): TOIOutput {
|
||||||
var timer = System.nanoTime()
|
val timer = System.nanoTime()
|
||||||
|
|
||||||
b2_toiCalls++
|
b2_toiCalls++
|
||||||
|
|
||||||
var state = TOIOutput.State.UNKNOWN
|
var state = TOIOutput.State.UNKNOWN
|
||||||
var t = tMax
|
var t = tMax
|
||||||
|
|
||||||
// TODO
|
@Suppress("name_shadowing")
|
||||||
val sweepA = _sweepA.copy()
|
val sweepA = sweepA.copy()
|
||||||
val sweepB = _sweepB.copy()
|
|
||||||
|
@Suppress("name_shadowing")
|
||||||
|
val sweepB = sweepB.copy()
|
||||||
|
|
||||||
sweepA.normalize()
|
sweepA.normalize()
|
||||||
sweepB.normalize()
|
sweepB.normalize()
|
||||||
|
@ -599,8 +599,8 @@ class B2World(
|
|||||||
val output = b2TimeOfImpact(
|
val output = b2TimeOfImpact(
|
||||||
proxyA = DistanceProxy(fA.shape, indexA),
|
proxyA = DistanceProxy(fA.shape, indexA),
|
||||||
proxyB = DistanceProxy(fB.shape, indexB),
|
proxyB = DistanceProxy(fB.shape, indexB),
|
||||||
_sweepA = bA.sweep,
|
sweepA = bA.sweep,
|
||||||
_sweepB = bB.sweep,
|
sweepB = bB.sweep,
|
||||||
tMax = 1.0
|
tMax = 1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ class ContactManager {
|
|||||||
|
|
||||||
// Remove from the world.
|
// Remove from the world.
|
||||||
run {
|
run {
|
||||||
val prev = contact.prev as AbstractContact?
|
val prev = contact.prev
|
||||||
val next = contact.next as AbstractContact?
|
val next = contact.next
|
||||||
|
|
||||||
prev?.next = next
|
prev?.next = next
|
||||||
next?.prev = prev
|
next?.prev = prev
|
||||||
@ -93,7 +93,6 @@ class ContactManager {
|
|||||||
// Update awake contacts.
|
// Update awake contacts.
|
||||||
|
|
||||||
for (c in contactListIterator) {
|
for (c in contactListIterator) {
|
||||||
c as AbstractContact
|
|
||||||
val fixtureA = c.fixtureA
|
val fixtureA = c.fixtureA
|
||||||
val fixtureB = c.fixtureB
|
val fixtureB = c.fixtureB
|
||||||
val indexA = c.childIndexA
|
val indexA = c.childIndexA
|
||||||
@ -119,8 +118,8 @@ class ContactManager {
|
|||||||
c.isFlaggedForFiltering = false
|
c.isFlaggedForFiltering = false
|
||||||
}
|
}
|
||||||
|
|
||||||
val activeA = bodyA.isAwake && bodyA.type != ru.dbotthepony.kbox2d.api.BodyType.STATIC
|
val activeA = bodyA.isAwake && bodyA.type != BodyType.STATIC
|
||||||
val activeB = bodyB.isAwake && bodyB.type != ru.dbotthepony.kbox2d.api.BodyType.STATIC
|
val activeB = bodyB.isAwake && bodyB.type != BodyType.STATIC
|
||||||
|
|
||||||
// At least one body must be awake and it must be dynamic or kinematic.
|
// At least one body must be awake and it must be dynamic or kinematic.
|
||||||
if (!activeA && !activeB) {
|
if (!activeA && !activeB) {
|
||||||
@ -144,7 +143,7 @@ class ContactManager {
|
|||||||
broadPhase.updatePairs(this::addPair)
|
broadPhase.updatePairs(this::addPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?) {
|
private fun addPair(proxyUserDataA: Any?, proxyUserDataB: Any?) {
|
||||||
val proxyA = proxyUserDataA as FixtureProxy
|
val proxyA = proxyUserDataA as FixtureProxy
|
||||||
val proxyB = proxyUserDataB as FixtureProxy
|
val proxyB = proxyUserDataB as FixtureProxy
|
||||||
|
|
||||||
@ -200,7 +199,7 @@ class ContactManager {
|
|||||||
// Contact creation may swap fixtures.
|
// Contact creation may swap fixtures.
|
||||||
// Insert into the world.
|
// Insert into the world.
|
||||||
c.next = contactList
|
c.next = contactList
|
||||||
(contactList as AbstractContact?)?.prev = c
|
contactList?.prev = c
|
||||||
contactList = c
|
contactList = c
|
||||||
|
|
||||||
// Connect to island graph.
|
// Connect to island graph.
|
||||||
|
@ -4,6 +4,8 @@ import org.apache.logging.log4j.LogManager
|
|||||||
import org.lwjgl.Version
|
import org.lwjgl.Version
|
||||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||||
import ru.dbotthepony.kbox2d.api.*
|
import ru.dbotthepony.kbox2d.api.*
|
||||||
|
import ru.dbotthepony.kbox2d.collision.DistanceProxy
|
||||||
|
import ru.dbotthepony.kbox2d.collision.b2TimeOfImpact
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
import ru.dbotthepony.kbox2d.collision.shapes.CircleShape
|
||||||
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
@ -130,7 +132,7 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run {
|
run {
|
||||||
val stripes = 4
|
val stripes = 0
|
||||||
|
|
||||||
for (stripe in 0 until stripes) {
|
for (stripe in 0 until stripes) {
|
||||||
for (x in 0 .. (stripes - stripe)) {
|
for (x in 0 .. (stripes - stripe)) {
|
||||||
@ -178,10 +180,6 @@ fun main() {
|
|||||||
client.gl.box2dRenderer.drawAABB = false
|
client.gl.box2dRenderer.drawAABB = false
|
||||||
client.gl.box2dRenderer.drawJoints = false
|
client.gl.box2dRenderer.drawJoints = false
|
||||||
|
|
||||||
client.onPostDrawWorld {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ent.spawn()
|
ent.spawn()
|
||||||
|
|
||||||
while (client.renderFrame()) {
|
while (client.renderFrame()) {
|
||||||
|
@ -8,5 +8,5 @@ data class ClientSettings(
|
|||||||
*/
|
*/
|
||||||
var scale: Float = 2f,
|
var scale: Float = 2f,
|
||||||
|
|
||||||
var debugCollisions: Boolean = true,
|
var debugCollisions: Boolean = false,
|
||||||
)
|
)
|
||||||
|
@ -260,7 +260,7 @@ class StarboundClient : AutoCloseable {
|
|||||||
val runtime = Runtime.getRuntime()
|
val runtime = Runtime.getRuntime()
|
||||||
|
|
||||||
gl.font.render("FPS: ${(averageFramesPerSecond * 100f).toInt() / 100f}", scale = 0.4f)
|
gl.font.render("FPS: ${(averageFramesPerSecond * 100f).toInt() / 100f}", scale = 0.4f)
|
||||||
gl.font.render("Mem: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", x = viewportWidth.toFloat(), scale = 0.4f, alignX = TextAlignX.RIGHT)
|
gl.font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = gl.font.lineHeight * 0.5f, scale = 0.4f)
|
||||||
|
|
||||||
GLFW.glfwSwapBuffers(window)
|
GLFW.glfwSwapBuffers(window)
|
||||||
GLFW.glfwPollEvents()
|
GLFW.glfwPollEvents()
|
||||||
|
@ -11,6 +11,7 @@ fun formatBytesShort(input: Long): String {
|
|||||||
in 0 until KIBIBYTE -> "${input}b"
|
in 0 until KIBIBYTE -> "${input}b"
|
||||||
in KIBIBYTE until MEBIBYTE -> "${(((input / KIBIBYTE).toDouble() + (input % KIBIBYTE).toDouble() / KIBIBYTE) * 100.0).toLong().toDouble() / 100.0}KiB"
|
in KIBIBYTE until MEBIBYTE -> "${(((input / KIBIBYTE).toDouble() + (input % KIBIBYTE).toDouble() / KIBIBYTE) * 100.0).toLong().toDouble() / 100.0}KiB"
|
||||||
in MEBIBYTE until GIBIBYTE -> "${(((input / MEBIBYTE).toDouble() + (input % MEBIBYTE).toDouble() / MEBIBYTE) * 100.0).toLong().toDouble() / 100.0}MiB"
|
in MEBIBYTE until GIBIBYTE -> "${(((input / MEBIBYTE).toDouble() + (input % MEBIBYTE).toDouble() / MEBIBYTE) * 100.0).toLong().toDouble() / 100.0}MiB"
|
||||||
|
in GIBIBYTE until TEBIBYTE -> "${(((input / GIBIBYTE).toDouble() + (input % GIBIBYTE).toDouble() / GIBIBYTE) * 100.0).toLong().toDouble() / 100.0}GiB"
|
||||||
else -> "${input}b"
|
else -> "${input}b"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.entities
|
package ru.dbotthepony.kstarbound.world.entities
|
||||||
|
|
||||||
|
import ru.dbotthepony.kbox2d.api.ContactEdge
|
||||||
|
import ru.dbotthepony.kbox2d.api.FixtureDef
|
||||||
|
import ru.dbotthepony.kbox2d.api.b2_linearSlop
|
||||||
|
import ru.dbotthepony.kbox2d.api.b2_polygonRadius
|
||||||
|
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
|
||||||
|
import ru.dbotthepony.kbox2d.dynamics.B2Fixture
|
||||||
import ru.dbotthepony.kstarbound.client.ClientWorld
|
import ru.dbotthepony.kstarbound.client.ClientWorld
|
||||||
import ru.dbotthepony.kstarbound.world.World
|
import ru.dbotthepony.kstarbound.world.World
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
@ -72,6 +78,32 @@ abstract class WalkableMovementController<T : IWalkableEntity>(entity: T) : Move
|
|||||||
|
|
||||||
protected abstract val moveDirection: Move
|
protected abstract val moveDirection: Move
|
||||||
|
|
||||||
|
protected var sensorA: B2Fixture? = null
|
||||||
|
protected var sensorB: B2Fixture? = null
|
||||||
|
protected var bodyFixture: B2Fixture? = null
|
||||||
|
|
||||||
|
protected abstract fun recreateBodyFixture()
|
||||||
|
|
||||||
|
protected open fun recreateSensors() {
|
||||||
|
sensorA?.destroy()
|
||||||
|
sensorB?.destroy()
|
||||||
|
|
||||||
|
val bodyFixture = bodyFixture ?: return
|
||||||
|
val aabb = bodyFixture.shape.computeAABB(0)
|
||||||
|
|
||||||
|
val sensorheight = (aabb.height - stepSize) / 2.0
|
||||||
|
|
||||||
|
sensorA = body.createFixture(FixtureDef(
|
||||||
|
shape = PolygonShape().also { it.setAsBox(0.2, sensorheight, Vector2d(-aabb.width / 2.0 - 0.2, aabb.height / 2.0 - sensorheight), 0.0) },
|
||||||
|
isSensor = true,
|
||||||
|
))
|
||||||
|
|
||||||
|
sensorB = body.createFixture(FixtureDef(
|
||||||
|
shape = PolygonShape().also { it.setAsBox(0.2, sensorheight, Vector2d(aabb.width / 2.0 + 0.2, aabb.height / 2.0 - sensorheight), 0.0) },
|
||||||
|
isSensor = true,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
var wantsToDuck = false
|
var wantsToDuck = false
|
||||||
open var isDucked = false
|
open var isDucked = false
|
||||||
protected set
|
protected set
|
||||||
@ -131,6 +163,8 @@ abstract class WalkableMovementController<T : IWalkableEntity>(entity: T) : Move
|
|||||||
|
|
||||||
protected abstract fun canUnDuck(): Boolean
|
protected abstract fun canUnDuck(): Boolean
|
||||||
|
|
||||||
|
protected var previousVelocity = Vector2d.ZERO
|
||||||
|
|
||||||
protected open fun thinkMovement(delta: Double) {
|
protected open fun thinkMovement(delta: Double) {
|
||||||
if (onGround && !isDucked) {
|
if (onGround && !isDucked) {
|
||||||
when (moveDirection) {
|
when (moveDirection) {
|
||||||
@ -151,6 +185,54 @@ abstract class WalkableMovementController<T : IWalkableEntity>(entity: T) : Move
|
|||||||
body.linearVelocity += -delta * world.gravity * 2.0
|
body.linearVelocity += -delta * world.gravity * 2.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wantToStepUp = false
|
||||||
|
var foundContact: ContactEdge? = null
|
||||||
|
|
||||||
|
for (contact in body.contactEdgeIterator) {
|
||||||
|
if (contact.contact.manifold.localNormal.dot(Vector2d.NEGATIVE_X) >= 0.95) {
|
||||||
|
// we hit something to our left
|
||||||
|
wantToStepUp = true
|
||||||
|
foundContact = contact
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantToStepUp) {
|
||||||
|
// make sure something we hit is actually a staircase of sort, and not geometry edges
|
||||||
|
val aabbFound: AABB
|
||||||
|
|
||||||
|
if (foundContact!!.contact.fixtureB == sensorA) {
|
||||||
|
aabbFound = foundContact.contact.fixtureA.getAABB(foundContact.contact.childIndexA)
|
||||||
|
} else {
|
||||||
|
aabbFound = foundContact.contact.fixtureB.getAABB(foundContact.contact.childIndexB)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aabbFound.maxs.y - b2_polygonRadius * 2.0 <= body.worldSpaceAABB.mins.y) {
|
||||||
|
// just a crack on sidewalk
|
||||||
|
wantToStepUp = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantToStepUp) {
|
||||||
|
var stepHeightClear = true
|
||||||
|
|
||||||
|
for (contact in body.contactEdgeIterator) {
|
||||||
|
if (contact.contact.fixtureA == sensorA || contact.contact.fixtureB == sensorA) {
|
||||||
|
if (contact.contact.isTouching) {
|
||||||
|
stepHeightClear = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stepHeightClear) {
|
||||||
|
val velocity = if (previousVelocity.length > body.linearVelocity.length) previousVelocity else body.linearVelocity
|
||||||
|
body.setTransform(body.position + Vector2d(x = -0.05, y = stepSize), body.angle)
|
||||||
|
body.linearVelocity = velocity
|
||||||
|
//body.linearVelocity += Vector2d(y = -delta * 18.0 * stepSize * world.gravity.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Move.MOVE_RIGHT -> {
|
Move.MOVE_RIGHT -> {
|
||||||
@ -166,9 +248,59 @@ abstract class WalkableMovementController<T : IWalkableEntity>(entity: T) : Move
|
|||||||
body.linearVelocity += -delta * world.gravity * 2.0
|
body.linearVelocity += -delta * world.gravity * 2.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wantToStepUp = false
|
||||||
|
var foundContact: ContactEdge? = null
|
||||||
|
|
||||||
|
for (contact in body.contactEdgeIterator) {
|
||||||
|
if (contact.contact.manifold.localNormal.dot(Vector2d.POSITIVE_X) >= 0.95) {
|
||||||
|
// we hit something to our right
|
||||||
|
wantToStepUp = true
|
||||||
|
foundContact = contact
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantToStepUp) {
|
||||||
|
// make sure something we hit is actually a staircase of sort, and not geometry edges
|
||||||
|
val aabbFound: AABB
|
||||||
|
|
||||||
|
if (foundContact!!.contact.fixtureB == sensorB) {
|
||||||
|
aabbFound = foundContact.contact.fixtureA.getAABB(foundContact.contact.childIndexA)
|
||||||
|
} else {
|
||||||
|
aabbFound = foundContact.contact.fixtureB.getAABB(foundContact.contact.childIndexB)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aabbFound.maxs.y - b2_polygonRadius * 2.0 <= body.worldSpaceAABB.mins.y) {
|
||||||
|
// just a crack on sidewalk
|
||||||
|
wantToStepUp = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantToStepUp) {
|
||||||
|
var stepHeightClear = true
|
||||||
|
|
||||||
|
for (contact in body.contactEdgeIterator) {
|
||||||
|
if (contact.contact.fixtureA == sensorB || contact.contact.fixtureB == sensorB) {
|
||||||
|
if (contact.contact.isTouching) {
|
||||||
|
stepHeightClear = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stepHeightClear) {
|
||||||
|
val velocity = if (previousVelocity.length > body.linearVelocity.length) previousVelocity else body.linearVelocity
|
||||||
|
body.setTransform(body.position + Vector2d(x = 0.05, y = stepSize), body.angle)
|
||||||
|
body.linearVelocity = velocity
|
||||||
|
//body.linearVelocity += Vector2d(y = -delta * 18.0 * stepSize * world.gravity.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previousVelocity = body.linearVelocity
|
||||||
|
|
||||||
if (jumpRequested) {
|
if (jumpRequested) {
|
||||||
jumpRequested = false
|
jumpRequested = false
|
||||||
nextJump = world.timer + 0.1
|
nextJump = world.timer + 0.1
|
||||||
@ -195,9 +327,15 @@ abstract class WalkableMovementController<T : IWalkableEntity>(entity: T) : Move
|
|||||||
|
|
||||||
if (wantsToDuck && onGround) {
|
if (wantsToDuck && onGround) {
|
||||||
isDucked = true
|
isDucked = true
|
||||||
|
recreateBodyFixture()
|
||||||
|
recreateSensors()
|
||||||
|
body.isAwake = true
|
||||||
} else if (isDucked) {
|
} else if (isDucked) {
|
||||||
if (canUnDuck()) {
|
if (canUnDuck()) {
|
||||||
isDucked = false
|
isDucked = false
|
||||||
|
recreateBodyFixture()
|
||||||
|
recreateSensors()
|
||||||
|
body.isAwake = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,44 +9,32 @@ import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
|||||||
|
|
||||||
class PlayerMovementController(entity: PlayerEntity) : WalkableMovementController<PlayerEntity>(entity) {
|
class PlayerMovementController(entity: PlayerEntity) : WalkableMovementController<PlayerEntity>(entity) {
|
||||||
public override var moveDirection = Move.STAND_STILL
|
public override var moveDirection = Move.STAND_STILL
|
||||||
private var bodyFixture: B2Fixture
|
|
||||||
|
|
||||||
override var isDucked: Boolean = false
|
override fun recreateBodyFixture() {
|
||||||
set(value) {
|
bodyFixture?.destroy()
|
||||||
if (value == field)
|
|
||||||
return
|
|
||||||
|
|
||||||
field = value
|
if (isDucked) {
|
||||||
|
bodyFixture = body.createFixture(FixtureDef(
|
||||||
bodyFixture.destroy()
|
shape = DUCKING,
|
||||||
|
friction = 0.4,
|
||||||
if (value) {
|
density = 1.9,
|
||||||
bodyFixture = body.createFixture(FixtureDef(
|
))
|
||||||
shape = DUCKING,
|
} else {
|
||||||
friction = 0.4,
|
bodyFixture = body.createFixture(FixtureDef(
|
||||||
density = 1.9,
|
shape = STANDING,
|
||||||
))
|
friction = 0.4,
|
||||||
} else {
|
density = 1.9,
|
||||||
bodyFixture = body.createFixture(FixtureDef(
|
))
|
||||||
shape = STANDING,
|
|
||||||
friction = 0.4,
|
|
||||||
density = 1.9,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
body.isAwake = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canUnDuck(): Boolean {
|
|
||||||
return world.isSpaceEmptyFromTiles(STANDING_AABB + position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
bodyFixture = body.createFixture(FixtureDef(
|
recreateBodyFixture()
|
||||||
shape = STANDING,
|
recreateSensors()
|
||||||
friction = 0.4,
|
}
|
||||||
density = 1.9,
|
|
||||||
))
|
override fun canUnDuck(): Boolean {
|
||||||
|
return world.isSpaceEmptyFromTiles(STANDING_AABB + position)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
Loading…
Reference in New Issue
Block a user