Bläddra i källkod

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

distances. Make other changes.
customisations
alemart 8 månader sedan
förälder
incheckning
6645bd2d46
1 ändrade filer med 86 tillägg och 12 borttagningar
  1. 86
    12
      demos/basketball/src/entities/ball.js

+ 86
- 12
demos/basketball/src/entities/ball.js Visa fil

@@ -14,7 +14,7 @@ import { GameEvent } from '../core/events.js';
14 14
 const BALL_RADIUS = 0.275;
15 15
 
16 16
 /** Minimum distance for scoring 3 points */
17
-const THREE_POINT_THRESHOLD = 6.0;
17
+const THREE_POINT_THRESHOLD = 5.0;
18 18
 
19 19
 /** Shoot angle */
20 20
 const SHOOT_ANGLE = Math.PI / 4;
@@ -37,6 +37,15 @@ const MAX_DISTANCE = 15;
37 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 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 49
 /** Collision flag for the backboard */
41 50
 const FLAG_BACKBOARD = 1;
42 51
 
@@ -66,10 +75,13 @@ export class Ball extends Entity
66 75
         this._plane = new BABYLON.Plane(0, 0, 1, 0);
67 76
         this._planeOrigin = new BABYLON.Vector3();
68 77
         this._positionWhenThrown = new BABYLON.Vector3();
78
+        this._hoopPosition = new BABYLON.Vector3();
69 79
         this._mesh = null;
70 80
         this._lastTrigger = '';
71 81
         this._collisionFlags = 0;
72 82
         this._locked = false;
83
+        this._shineFactor = 0;
84
+        this._shineTimer = 0;
73 85
     }
74 86
 
75 87
     /**
@@ -103,6 +115,7 @@ export class Ball extends Entity
103 115
         }
104 116
 
105 117
         this._updatePlane();
118
+        this._shine();
106 119
 
107 120
         const fn = this._runState[this._state];
108 121
         fn.call(this);
@@ -211,7 +224,7 @@ export class Ball extends Entity
211 224
         if(pointer.movementDuration == 0)
212 225
             return pointer.velocity.times(0);
213 226
 
214
-        const averageSpeed = pointer.movementLength / pointer.movementDuration;
227
+        const averageSpeed = pointer.movementLength / pointer.duration;
215 228
         const direction = pointer.initialPosition.directionTo(pointer.position);
216 229
 
217 230
         return direction.times(averageSpeed);
@@ -274,16 +287,9 @@ export class Ball extends Entity
274 287
      */
275 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 291
         const score = this._calculateScore(distance);
286
-        const position = pointA;
292
+        const position = this._mesh.absolutePosition;
287 293
 
288 294
         this._broadcast(new GameEvent('scored', { score, position }));
289 295
     }
@@ -299,6 +305,24 @@ export class Ball extends Entity
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 326
      * Put this._plane in front of the camera
303 327
      * @returns {void}
304 328
      */
@@ -366,6 +390,51 @@ export class Ball extends Entity
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 438
      * Create a root node with a physics impostor
370 439
      * @param {BABYLON.Mesh} mesh from gltf
371 440
      * @param {number} radius radius of the ball
@@ -374,9 +443,10 @@ export class Ball extends Entity
374 443
     _createPhysicsRoot(mesh)
375 444
     {
376 445
         const r = BALL_RADIUS;
446
+        const r_ = r - 0.003;
377 447
 
378 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 450
         mesh.getChildMeshes().forEach(child => {
381 451
             if(child.material)
382 452
                 child.material.specularIntensity = 0;
@@ -430,6 +500,10 @@ export class Ball extends Entity
430 500
                 );
431 501
                 break;
432 502
 
503
+            case 'hoopready':
504
+                this._hoopPosition.copyFrom(event.detail.position);
505
+                break;
506
+
433 507
             case 'netready':
434 508
                 event.detail.entity.setBall(this._mesh);
435 509
                 break;

Laddar…
Avbryt
Spara