Przeglądaj źródła

Basketball game: make the ball shine as a visual cue to indicate long

distances. Make other changes.
customisations
alemart 8 miesięcy temu
rodzic
commit
6645bd2d46
1 zmienionych plików z 86 dodań i 12 usunięć
  1. 86
    12
      demos/basketball/src/entities/ball.js

+ 86
- 12
demos/basketball/src/entities/ball.js Wyświetl plik

14
 const BALL_RADIUS = 0.275;
14
 const BALL_RADIUS = 0.275;
15
 
15
 
16
 /** Minimum distance for scoring 3 points */
16
 /** Minimum distance for scoring 3 points */
17
-const THREE_POINT_THRESHOLD = 6.0;
17
+const THREE_POINT_THRESHOLD = 5.0;
18
 
18
 
19
 /** Shoot angle */
19
 /** Shoot angle */
20
 const SHOOT_ANGLE = Math.PI / 4;
20
 const SHOOT_ANGLE = Math.PI / 4;
37
 /** Maximum distance in the y-axis from the camera to the ball, so that the ball is considered "lost" in the thrown state */
37
 /** Maximum distance in the y-axis from the camera to the ball, so that the ball is considered "lost" in the thrown state */
38
 const MAX_Y_DISTANCE = 5;
38
 const MAX_Y_DISTANCE = 5;
39
 
39
 
40
+/** Frequency of the shining effect, in Hz */
41
+const SHINE_FREQUENCY = 2.0;
42
+
43
+/** Maximum brightness of the ball when the shining effect is applied (a value in [0,1]) */
44
+const SHINE_MAX_BRIGHTNESS = 0.25;
45
+
46
+/** Duration of fading of the shining effect, in seconds */
47
+const SHINE_FADE_DURATION = 0.5;
48
+
40
 /** Collision flag for the backboard */
49
 /** Collision flag for the backboard */
41
 const FLAG_BACKBOARD = 1;
50
 const FLAG_BACKBOARD = 1;
42
 
51
 
66
         this._plane = new BABYLON.Plane(0, 0, 1, 0);
75
         this._plane = new BABYLON.Plane(0, 0, 1, 0);
67
         this._planeOrigin = new BABYLON.Vector3();
76
         this._planeOrigin = new BABYLON.Vector3();
68
         this._positionWhenThrown = new BABYLON.Vector3();
77
         this._positionWhenThrown = new BABYLON.Vector3();
78
+        this._hoopPosition = new BABYLON.Vector3();
69
         this._mesh = null;
79
         this._mesh = null;
70
         this._lastTrigger = '';
80
         this._lastTrigger = '';
71
         this._collisionFlags = 0;
81
         this._collisionFlags = 0;
72
         this._locked = false;
82
         this._locked = false;
83
+        this._shineFactor = 0;
84
+        this._shineTimer = 0;
73
     }
85
     }
74
 
86
 
75
     /**
87
     /**
103
         }
115
         }
104
 
116
 
105
         this._updatePlane();
117
         this._updatePlane();
118
+        this._shine();
106
 
119
 
107
         const fn = this._runState[this._state];
120
         const fn = this._runState[this._state];
108
         fn.call(this);
121
         fn.call(this);
211
         if(pointer.movementDuration == 0)
224
         if(pointer.movementDuration == 0)
212
             return pointer.velocity.times(0);
225
             return pointer.velocity.times(0);
213
 
226
 
214
-        const averageSpeed = pointer.movementLength / pointer.movementDuration;
227
+        const averageSpeed = pointer.movementLength / pointer.duration;
215
         const direction = pointer.initialPosition.directionTo(pointer.position);
228
         const direction = pointer.initialPosition.directionTo(pointer.position);
216
 
229
 
217
         return direction.times(averageSpeed);
230
         return direction.times(averageSpeed);
274
      */
287
      */
275
     _score()
288
     _score()
276
     {
289
     {
277
-        const pointA = this._mesh.absolutePosition;
278
-        const pointB = this._positionWhenThrown;
279
-
280
-        const plane = BABYLON.Plane.FromPositionAndNormal(this.ar.root.absolutePosition, this.ar.root.up);
281
-        const projectedPointA = this._orthogonalProjection(plane, pointA);
282
-        const projectedPointB = this._orthogonalProjection(plane, pointB);
283
-
284
-        const distance = BABYLON.Vector3.Distance(projectedPointA, projectedPointB);
290
+        const distance = this._calculateDistanceToHoop(this._positionWhenThrown);
285
         const score = this._calculateScore(distance);
291
         const score = this._calculateScore(distance);
286
-        const position = pointA;
292
+        const position = this._mesh.absolutePosition;
287
 
293
 
288
         this._broadcast(new GameEvent('scored', { score, position }));
294
         this._broadcast(new GameEvent('scored', { score, position }));
289
     }
295
     }
299
     }
305
     }
300
 
306
 
301
     /**
307
     /**
308
+     * Calculate the distance between a point and the hoop
309
+     * @param {BABYLON.Vector3} absolutePosition
310
+     * @returns {number}
311
+     */
312
+    _calculateDistanceToHoop(absolutePosition)
313
+    {
314
+        const ar = this.ar;
315
+        const pointA = absolutePosition;
316
+        const pointB = ar.root.absolutePosition.add(this._hoopPosition);
317
+
318
+        const groundLike = BABYLON.Plane.FromPositionAndNormal(ar.root.absolutePosition, ar.root.up);
319
+        const projectedPointA = this._orthogonalProjection(groundLike, pointA);
320
+        const projectedPointB = this._orthogonalProjection(groundLike, pointB);
321
+
322
+        return BABYLON.Vector3.Distance(projectedPointA, projectedPointB);
323
+    }
324
+
325
+    /**
302
      * Put this._plane in front of the camera
326
      * Put this._plane in front of the camera
303
      * @returns {void}
327
      * @returns {void}
304
      */
328
      */
366
     }
390
     }
367
 
391
 
368
     /**
392
     /**
393
+     * Make the ball shine as a visual cue to indicate long distances
394
+     * @returns {void}
395
+     */
396
+    _shine()
397
+    {
398
+        const b = this._shineBrightness();
399
+
400
+        this._mesh.getChildMeshes().forEach(child => {
401
+            if(child.material)
402
+                child.material.emissiveColor.set(b, b, b);
403
+        });
404
+    }
405
+
406
+    /**
407
+     * Compute the brightness of the shining ball
408
+     * @returns {number} in [0,1]
409
+     */
410
+    _shineBrightness()
411
+    {
412
+        if(this._state == 'thrown' || !this._mesh.isEnabled) {
413
+            this._shineFactor = this._shineTimer = 0;
414
+            return 0;
415
+        }
416
+
417
+        const time = this.ar.session.time;
418
+        const dt = time.delta;
419
+        const rate = 1.0 / SHINE_FADE_DURATION;
420
+        const distance = this._calculateDistanceToHoop(this._mesh.absolutePosition);
421
+
422
+        if(distance >= THREE_POINT_THRESHOLD) {
423
+            this._shineFactor = 1;
424
+            this._shineTimer += dt;
425
+        }
426
+        else {
427
+            this._shineFactor = Math.max(0, this._shineFactor - rate * dt);
428
+            if(this._shineFactor == 0)
429
+                this._shineTimer = 0;
430
+        }
431
+
432
+        const t = this._shineTimer;
433
+        const s = 0.5 - 0.5 * Math.cos(2 * Math.PI * SHINE_FREQUENCY * t);
434
+        return this._shineFactor * SHINE_MAX_BRIGHTNESS * s;
435
+    }
436
+
437
+    /**
369
      * Create a root node with a physics impostor
438
      * Create a root node with a physics impostor
370
      * @param {BABYLON.Mesh} mesh from gltf
439
      * @param {BABYLON.Mesh} mesh from gltf
371
      * @param {number} radius radius of the ball
440
      * @param {number} radius radius of the ball
374
     _createPhysicsRoot(mesh)
443
     _createPhysicsRoot(mesh)
375
     {
444
     {
376
         const r = BALL_RADIUS;
445
         const r = BALL_RADIUS;
446
+        const r_ = r - 0.003;
377
 
447
 
378
         // prepare the mesh
448
         // prepare the mesh
379
-        mesh.scaling.set(r, r, r); // original radius = 1
449
+        mesh.scaling.set(r_, r_, r_); // original radius = 1
380
         mesh.getChildMeshes().forEach(child => {
450
         mesh.getChildMeshes().forEach(child => {
381
             if(child.material)
451
             if(child.material)
382
                 child.material.specularIntensity = 0;
452
                 child.material.specularIntensity = 0;
430
                 );
500
                 );
431
                 break;
501
                 break;
432
 
502
 
503
+            case 'hoopready':
504
+                this._hoopPosition.copyFrom(event.detail.position);
505
+                break;
506
+
433
             case 'netready':
507
             case 'netready':
434
                 event.detail.entity.setBall(this._mesh);
508
                 event.detail.entity.setBall(this._mesh);
435
                 break;
509
                 break;

Ładowanie…
Anuluj
Zapisz