1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < title > three.js - platformer demo</ title >
5+ < meta charset ="utf-8 ">
6+ < meta name ="viewport " content ="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0 ">
7+ < style >
8+ body {
9+ background-color : rgb (200 , 200 , 200 );
10+ margin : 0px ;
11+ overflow : hidden;
12+ }
13+
14+ # info {
15+ position : absolute;
16+ top : 0px ; width : 100% ;
17+ color : # ffffff ;
18+ padding : 5px ;
19+ font-family : Monospace;
20+ font-size : 13px ;
21+ font-weight : bold;
22+ text-align : center;
23+ }
24+
25+ a {
26+ color : # ffffff ;
27+ }
28+ </ style >
29+ </ head >
30+ < body >
31+
32+ < div id ="container "> </ div >
33+ < div id ="info "> < a href ="http://threejs.org " target ="_blank "> three.js</ a > - platformer demo. cubemap by < a href ="http://www.zfight.com/ " target ="_blank "> Jochum Skoglund</ a > .< br /> Use arrow keys to look around and WASD to move.</ div >
34+
35+ < script src ="../build/three.min.js "> </ script >
36+
37+ < script >
38+
39+ // player motion parameters
40+
41+ var motion = {
42+ airborne : false ,
43+ position : new THREE . Vector3 ( ) , velocity : new THREE . Vector3 ( ) ,
44+ rotation : new THREE . Vector2 ( ) , spinning : new THREE . Vector2 ( )
45+ } ;
46+
47+ motion . position . y = - 150 ;
48+
49+
50+ // game systems code
51+
52+ var resetPlayer = function ( ) {
53+ if ( motion . position . y < - 123 ) {
54+ motion . position . set ( - 2 , 7.7 , 25 ) ;
55+ motion . velocity . multiplyScalar ( 0 ) ;
56+ }
57+ } ;
58+
59+ var keyboardControls = ( function ( ) {
60+
61+ var keys = { SP : 32 , W : 87 , A : 65 , S : 83 , D : 68 , UP : 38 , LT : 37 , DN : 40 , RT : 39 } ;
62+
63+ var keysPressed = { } ;
64+
65+ ( function ( watchedKeyCodes ) {
66+ var handler = function ( down ) {
67+ return function ( e ) {
68+ var index = watchedKeyCodes . indexOf ( e . keyCode ) ;
69+ if ( index >= 0 ) {
70+ keysPressed [ watchedKeyCodes [ index ] ] = down ; e . preventDefault ( ) ;
71+ }
72+ } ;
73+ }
74+ window . addEventListener ( "keydown" , handler ( true ) , false ) ;
75+ window . addEventListener ( "keyup" , handler ( false ) , false ) ;
76+ } ) ( [
77+ keys . SP , keys . W , keys . A , keys . S , keys . D , keys . UP , keys . LT , keys . DN , keys . RT
78+ ] ) ;
79+
80+ var forward = new THREE . Vector3 ( ) ;
81+ var sideways = new THREE . Vector3 ( ) ;
82+
83+ return function ( ) {
84+ if ( ! motion . airborne ) {
85+
86+ // look around
87+ var sx = keysPressed [ keys . UP ] ? 0.04 : ( keysPressed [ keys . DN ] ? - 0.04 : 0 ) ;
88+ var sy = keysPressed [ keys . LT ] ? 0.04 : ( keysPressed [ keys . RT ] ? - 0.04 : 0 ) ;
89+
90+ if ( Math . abs ( sx ) >= Math . abs ( motion . spinning . x ) ) motion . spinning . x = sx ;
91+ if ( Math . abs ( sy ) >= Math . abs ( motion . spinning . y ) ) motion . spinning . y = sy ;
92+
93+ // move around
94+ forward . set ( Math . sin ( motion . rotation . y ) , 0 , Math . cos ( motion . rotation . y ) ) ;
95+ sideways . set ( forward . z , 0 , - forward . x ) ;
96+
97+ forward . multiplyScalar ( keysPressed [ keys . W ] ? - 0.1 : ( keysPressed [ keys . S ] ? 0.1 : 0 ) ) ;
98+ sideways . multiplyScalar ( keysPressed [ keys . A ] ? - 0.1 : ( keysPressed [ keys . D ] ? 0.1 : 0 ) ) ;
99+
100+ var combined = forward . add ( sideways ) ;
101+ if ( Math . abs ( combined . x ) >= Math . abs ( motion . velocity . x ) ) motion . velocity . x = combined . x ;
102+ if ( Math . abs ( combined . y ) >= Math . abs ( motion . velocity . y ) ) motion . velocity . y = combined . y ;
103+ if ( Math . abs ( combined . z ) >= Math . abs ( motion . velocity . z ) ) motion . velocity . z = combined . z ;
104+ }
105+ } ;
106+ } ) ( ) ;
107+
108+ var jumpPads = ( function ( ) {
109+ var pads = [ new THREE . Vector3 ( - 17.5 , 8 , - 10 ) , new THREE . Vector3 ( 17.5 , 8 , - 10 ) , new THREE . Vector3 ( 0 , 8 , 21 ) ] ;
110+ var temp = new THREE . Vector3 ( ) ;
111+
112+ return function ( ) {
113+ if ( ! motion . airborne ) {
114+ for ( var j = 0 , n = pads . length ; j < n ; j ++ ) {
115+ if ( pads [ j ] . distanceToSquared ( motion . position ) < 2.3 ) {
116+
117+ // calculate velocity towards another side of platform from jump pad position
118+ temp . copy ( pads [ j ] ) ; temp . y = 0 ; temp . setLength ( - 0.8 ) ; temp . y = 0.7 ;
119+
120+ motion . airborne = true ; motion . velocity . copy ( temp ) ; break ;
121+ }
122+ }
123+ }
124+ } ;
125+ } ) ( ) ;
126+
127+ var applyPhysics = ( function ( ) {
128+ var timeStep = 5 ;
129+ var timeLeft = timeStep + 1 ;
130+
131+ var birdsEye = 100 ;
132+ var kneeDeep = 0.4 ;
133+
134+ var raycaster = new THREE . Raycaster ( ) ;
135+ raycaster . ray . direction . set ( 0 , - 1 , 0 ) ;
136+
137+ var angles = new THREE . Vector2 ( ) ;
138+ var displacement = new THREE . Vector3 ( ) ;
139+
140+ return function ( dt ) {
141+ var platform = scene . getObjectByName ( "platform" , true ) ;
142+ if ( platform ) {
143+
144+ timeLeft += dt ;
145+
146+ // run several fixed-step iterations to approximate varying-step
147+
148+ dt = 5 ;
149+ while ( timeLeft >= dt ) {
150+
151+ var time = 0.3 , damping = 0.93 , gravity = 0.01 , tau = 2 * Math . PI ;
152+
153+ raycaster . ray . origin . copy ( motion . position ) ;
154+ raycaster . ray . origin . y += birdsEye ;
155+
156+ var hits = raycaster . intersectObject ( platform ) ;
157+
158+ motion . airborne = true ;
159+
160+ // are we above, or at most knee deep in, the platform?
161+
162+ if ( ( hits . length > 0 ) && ( hits [ 0 ] . face . normal . y > 0 ) ) {
163+ var actualHeight = hits [ 0 ] . distance - birdsEye ;
164+
165+ // collision: stick to the surface if landing on it
166+
167+ if ( ( motion . velocity . y <= 0 ) && ( Math . abs ( actualHeight ) < kneeDeep ) ) {
168+ motion . position . y -= actualHeight ;
169+ motion . velocity . y = 0 ;
170+ motion . airborne = false ;
171+ }
172+ }
173+
174+ if ( motion . airborne ) motion . velocity . y -= gravity ;
175+
176+ angles . copy ( motion . spinning ) . multiplyScalar ( time ) ;
177+ if ( ! motion . airborne ) motion . spinning . multiplyScalar ( damping ) ;
178+
179+ displacement . copy ( motion . velocity ) . multiplyScalar ( time ) ;
180+ if ( ! motion . airborne ) motion . velocity . multiplyScalar ( damping ) ;
181+
182+ motion . rotation . add ( angles ) ;
183+ motion . position . add ( displacement ) ;
184+
185+ // limit the tilt at ±0.4 radians
186+
187+ motion . rotation . x = Math . max ( - 0.4 , Math . min ( + 0.4 , motion . rotation . x ) ) ;
188+
189+ // wrap horizontal rotation to 0...2π
190+
191+ motion . rotation . y += tau ; motion . rotation . y %= tau ;
192+
193+ timeLeft -= dt ;
194+ }
195+ }
196+ } ;
197+ } ) ( ) ;
198+
199+ var updateCamera = ( function ( ) {
200+ var euler = new THREE . Euler ( 0 , 0 , 0 , 'YXZ' ) ;
201+
202+ return function ( ) {
203+ euler . x = motion . rotation . x ;
204+ euler . y = motion . rotation . y ;
205+ camera . quaternion . setFromEuler ( euler ) ;
206+
207+ camera . position . copy ( motion . position ) ;
208+
209+ camera . position . y += 3.0 ;
210+ } ;
211+ } ) ( ) ;
212+
213+
214+ // init 3D stuff
215+
216+ function makeSkybox ( urls , size ) {
217+ var skyboxCubemap = THREE . ImageUtils . loadTextureCube ( urls ) ;
218+ skyboxCubemap . format = THREE . RGBFormat ;
219+
220+ var skyboxShader = THREE . ShaderLib [ 'cube' ] ;
221+ skyboxShader . uniforms [ 'tCube' ] . value = skyboxCubemap ;
222+
223+ return new THREE . Mesh (
224+ new THREE . BoxGeometry ( size , size , size ) ,
225+ new THREE . ShaderMaterial ( {
226+ fragmentShader : skyboxShader . fragmentShader , vertexShader : skyboxShader . vertexShader ,
227+ uniforms : skyboxShader . uniforms , depthWrite : false , side : THREE . BackSide
228+ } )
229+ ) ;
230+ } ;
231+
232+ function makePlatform ( jsonUrl , textureUrl , textureQuality ) {
233+ var placeholder = new THREE . Object3D ( ) ;
234+
235+ var texture = THREE . ImageUtils . loadTexture ( textureUrl ) ;
236+ texture . anisotropy = textureQuality ;
237+
238+ var loader = new THREE . JSONLoader ( ) ;
239+ loader . load ( jsonUrl , function ( geometry ) {
240+
241+ geometry . computeFaceNormals ( ) ;
242+
243+ var platform = new THREE . Mesh ( geometry , new THREE . MeshBasicMaterial ( { map : texture } ) ) ;
244+
245+ platform . name = "platform" ;
246+
247+ placeholder . add ( platform ) ;
248+ } ) ;
249+
250+ return placeholder ;
251+ } ;
252+
253+ var renderer = new THREE . WebGLRenderer ( { antialias : true } ) ;
254+
255+ var camera = new THREE . PerspectiveCamera ( 90 , 1 , 0.1 , 9000 ) ;
256+
257+ var scene = new THREE . Scene ( ) ;
258+
259+ scene . add ( camera ) ;
260+
261+ scene . add ( makeSkybox ( [
262+ 'textures/cube/skybox/px.jpg' , // right
263+ 'textures/cube/skybox/nx.jpg' , // left
264+ 'textures/cube/skybox/py.jpg' , // top
265+ 'textures/cube/skybox/ny.jpg' , // bottom
266+ 'textures/cube/skybox/pz.jpg' , // back
267+ 'textures/cube/skybox/nz.jpg' // front
268+ ] , 8000 ) ) ;
269+
270+ scene . add ( makePlatform (
271+ 'models/platform/platform.json' ,
272+ 'models/platform/platform.jpg' ,
273+ renderer . getMaxAnisotropy ( )
274+ ) ) ;
275+
276+
277+ // start the game
278+
279+ var start = function ( gameLoop , gameViewportSize ) {
280+ var resize = function ( ) {
281+ var viewport = gameViewportSize ( ) ;
282+ renderer . setSize ( viewport . width , viewport . height ) ;
283+ camera . aspect = viewport . width / viewport . height ;
284+ camera . updateProjectionMatrix ( ) ;
285+ } ;
286+
287+ window . addEventListener ( 'resize' , resize , false ) ;
288+ resize ( ) ;
289+
290+ var lastTimeStamp ;
291+ var render = function ( timeStamp ) {
292+ var timeElapsed = lastTimeStamp ? timeStamp - lastTimeStamp : 0 ; lastTimeStamp = timeStamp ;
293+
294+ // call our game loop with the time elapsed since last rendering, in ms
295+ gameLoop ( timeElapsed ) ;
296+
297+ renderer . render ( scene , camera ) ;
298+ requestAnimationFrame ( render ) ;
299+ } ;
300+
301+ requestAnimationFrame ( render ) ;
302+ } ;
303+
304+
305+ var gameLoop = function ( dt ) {
306+ resetPlayer ( ) ;
307+ keyboardControls ( ) ;
308+ jumpPads ( ) ;
309+ applyPhysics ( dt ) ;
310+ updateCamera ( ) ;
311+ } ;
312+
313+ var gameViewportSize = function ( ) { return {
314+ width : window . innerWidth , height : window . innerHeight
315+ } } ;
316+
317+ document . getElementById ( 'container' ) . appendChild ( renderer . domElement ) ;
318+
319+ start ( gameLoop , gameViewportSize ) ;
320+ </ script >
321+ </ body >
322+ </ html >
0 commit comments