1
+ import * as THREE from 'three'
2
+ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
3
+ import { A , D , DIRECTIONS , S , W } from './utils'
4
+
5
+
6
+ export class CharacterControls {
7
+
8
+ model : THREE . Group
9
+ mixer : THREE . AnimationMixer
10
+ animationsMap : Map < string , THREE . AnimationAction > = new Map ( ) // Walk, Run, Idle
11
+ orbitControl : OrbitControls
12
+ camera : THREE . Camera
13
+
14
+ // state
15
+ toggleRun : boolean = true
16
+ currentAction : string
17
+
18
+ // temporary data
19
+ walkDirection = new THREE . Vector3 ( )
20
+ rotateAngle = new THREE . Vector3 ( 0 , 1 , 0 )
21
+ rotateQuarternion : THREE . Quaternion = new THREE . Quaternion ( )
22
+ cameraTarget = new THREE . Vector3 ( )
23
+
24
+ // constants
25
+ fadeDuration : number = 0.2
26
+ runVelocity = 5
27
+ walkVelocity = 2
28
+
29
+ constructor ( model : THREE . Group ,
30
+ mixer : THREE . AnimationMixer , animationsMap : Map < string , THREE . AnimationAction > ,
31
+ orbitControl : OrbitControls , camera : THREE . Camera ,
32
+ currentAction : string ) {
33
+ this . model = model
34
+ this . mixer = mixer
35
+ this . animationsMap = animationsMap
36
+ this . currentAction = currentAction
37
+ this . animationsMap . forEach ( ( value , key ) => {
38
+ if ( key == currentAction ) {
39
+ value . play ( )
40
+ }
41
+ } )
42
+ this . orbitControl = orbitControl
43
+ this . camera = camera
44
+ this . updateCameraTarget ( 0 , 0 )
45
+ }
46
+
47
+ public switchRunToggle ( ) {
48
+ this . toggleRun = ! this . toggleRun
49
+ }
50
+
51
+ public update ( delta : number , keysPressed : any ) {
52
+ const directionPressed = DIRECTIONS . some ( key => keysPressed [ key ] == true )
53
+
54
+ var play = '' ;
55
+ if ( directionPressed && this . toggleRun ) {
56
+ play = 'Run'
57
+ } else if ( directionPressed ) {
58
+ play = 'Walk'
59
+ } else {
60
+ play = 'Idle'
61
+ }
62
+
63
+ if ( this . currentAction != play ) {
64
+ const toPlay = this . animationsMap . get ( play )
65
+ const current = this . animationsMap . get ( this . currentAction )
66
+
67
+ current . fadeOut ( this . fadeDuration )
68
+ toPlay . reset ( ) . fadeIn ( this . fadeDuration ) . play ( ) ;
69
+
70
+ this . currentAction = play
71
+ }
72
+
73
+ this . mixer . update ( delta )
74
+
75
+ if ( this . currentAction == 'Run' || this . currentAction == 'Walk' ) {
76
+ // calculate towards camera direction
77
+ var angleYCameraDirection = Math . atan2 (
78
+ ( this . camera . position . x - this . model . position . x ) ,
79
+ ( this . camera . position . z - this . model . position . z ) )
80
+ // diagonal movement angle offset
81
+ var directionOffset = this . directionOffset ( keysPressed )
82
+
83
+ // rotate model
84
+ this . rotateQuarternion . setFromAxisAngle ( this . rotateAngle , angleYCameraDirection + directionOffset )
85
+ this . model . quaternion . rotateTowards ( this . rotateQuarternion , 0.2 )
86
+
87
+ // calculate direction
88
+ this . camera . getWorldDirection ( this . walkDirection )
89
+ this . walkDirection . y = 0
90
+ this . walkDirection . normalize ( )
91
+ this . walkDirection . applyAxisAngle ( this . rotateAngle , directionOffset )
92
+
93
+ // run/walk velocity
94
+ const velocity = this . currentAction == 'Run' ? this . runVelocity : this . walkVelocity
95
+
96
+ // move model & camera
97
+ const moveX = this . walkDirection . x * velocity * delta
98
+ const moveZ = this . walkDirection . z * velocity * delta
99
+ this . model . position . x += moveX
100
+ this . model . position . z += moveZ
101
+ this . updateCameraTarget ( moveX , moveZ )
102
+ }
103
+ }
104
+
105
+ private updateCameraTarget ( moveX : number , moveZ : number ) {
106
+ // move camera
107
+ this . camera . position . x += moveX
108
+ this . camera . position . z += moveZ
109
+
110
+ // update camera target
111
+ this . cameraTarget . x = this . model . position . x
112
+ this . cameraTarget . y = this . model . position . y + 1
113
+ this . cameraTarget . z = this . model . position . z
114
+ this . orbitControl . target = this . cameraTarget
115
+ }
116
+
117
+ private directionOffset ( keysPressed : any ) {
118
+ var directionOffset = 0 // w
119
+
120
+ if ( keysPressed [ W ] ) {
121
+ if ( keysPressed [ A ] ) {
122
+ directionOffset = Math . PI / 4 // w+a
123
+ } else if ( keysPressed [ D ] ) {
124
+ directionOffset = - Math . PI / 4 // w+d
125
+ }
126
+ } else if ( keysPressed [ S ] ) {
127
+ if ( keysPressed [ A ] ) {
128
+ directionOffset = Math . PI / 4 + Math . PI / 2 // s+a
129
+ } else if ( keysPressed [ D ] ) {
130
+ directionOffset = - Math . PI / 4 - Math . PI / 2 // s+d
131
+ } else {
132
+ directionOffset = Math . PI // s
133
+ }
134
+ } else if ( keysPressed [ A ] ) {
135
+ directionOffset = Math . PI / 2 // a
136
+ } else if ( keysPressed [ D ] ) {
137
+ directionOffset = - Math . PI / 2 // d
138
+ }
139
+
140
+ return directionOffset
141
+ }
142
+ }
0 commit comments