-
Notifications
You must be signed in to change notification settings - Fork 86
/
movement.js
164 lines (125 loc) · 4.18 KB
/
movement.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import vec3 from 'gl-vec3'
/**
*
* State object of the `movement` component
*
*/
export function MovementState() {
this.heading = 0 // radians
this.running = false
this.jumping = false
// options
this.maxSpeed = 10
this.moveForce = 30
this.responsiveness = 15
this.runningFriction = 0
this.standingFriction = 2
// jumps
this.airMoveMult = 0.5
this.jumpImpulse = 10
this.jumpForce = 12
this.jumpTime = 500 // ms
this.airJumps = 1
// internal state
this._jumpCount = 0
this._currjumptime = 0
this._isJumping = false
}
/**
* Movement component. State stores settings like jump height, etc.,
* as well as current state (running, jumping, heading angle).
* Processor checks state and applies movement/friction/jump forces
* to the entity's physics body.
* @param {import('..').Engine} noa
*/
export default function (noa) {
return {
name: 'movement',
order: 30,
state: new MovementState(),
onAdd: null,
onRemove: null,
system: function movementProcessor(dt, states) {
var ents = noa.entities
for (var i = 0; i < states.length; i++) {
var state = states[i]
var phys = ents.getPhysics(state.__id)
if (phys) applyMovementPhysics(dt, state, phys.body)
}
}
}
}
var tempvec = vec3.create()
var tempvec2 = vec3.create()
var zeroVec = vec3.create()
/**
* @param {number} dt
* @param {MovementState} state
* @param {*} body
*/
function applyMovementPhysics(dt, state, body) {
// move implementation originally written as external module
// see https://github.com/fenomas/voxel-fps-controller
// for original code
// jumping
var onGround = (body.atRestY() < 0)
var canjump = (onGround || state._jumpCount < state.airJumps)
if (onGround) {
state._isJumping = false
state._jumpCount = 0
}
// process jump input
if (state.jumping) {
if (state._isJumping) { // continue previous jump
if (state._currjumptime > 0) {
var jf = state.jumpForce
if (state._currjumptime < dt) jf *= state._currjumptime / dt
body.applyForce([0, jf, 0])
state._currjumptime -= dt
}
} else if (canjump) { // start new jump
state._isJumping = true
if (!onGround) state._jumpCount++
state._currjumptime = state.jumpTime
body.applyImpulse([0, state.jumpImpulse, 0])
// clear downward velocity on airjump
if (!onGround && body.velocity[1] < 0) body.velocity[1] = 0
}
} else {
state._isJumping = false
}
// apply movement forces if entity is moving, otherwise just friction
var m = tempvec
var push = tempvec2
if (state.running) {
var speed = state.maxSpeed
// todo: add crouch/sprint modifiers if needed
// if (state.sprint) speed *= state.sprintMoveMult
// if (state.crouch) speed *= state.crouchMoveMult
vec3.set(m, 0, 0, speed)
// rotate move vector to entity's heading
vec3.rotateY(m, m, zeroVec, state.heading)
// push vector to achieve desired speed & dir
// following code to adjust 2D velocity to desired amount is patterned on Quake:
// https://github.com/id-Software/Quake-III-Arena/blob/master/code/game/bg_pmove.c#L275
vec3.subtract(push, m, body.velocity)
push[1] = 0
var pushLen = vec3.length(push)
vec3.normalize(push, push)
if (pushLen > 0) {
// pushing force vector
var canPush = state.moveForce
if (!onGround) canPush *= state.airMoveMult
// apply final force
var pushAmt = state.responsiveness * pushLen
if (canPush > pushAmt) canPush = pushAmt
vec3.scale(push, push, canPush)
body.applyForce(push)
}
// different friction when not moving
// idea from Sonic: http://info.sonicretro.org/SPG:Running
body.friction = state.runningFriction
} else {
body.friction = state.standingFriction
}
}