In this chapter, we’ll turn your WebXR experience into a simple game by adding a proximity-based hit test for the targets and implementing a scoreboard to track the player’s score. These additions will make your scene more interactive and engaging, providing immediate feedback when a target is hit.
To display the score, we’ll use the troika-three-text
library, which allows us to render high-quality text in Three.js. The score will be displayed on a monitor in the space station scene, giving the player a clear view of their progress.
First, we define the score text and an updateScoreDisplay
function to manage the scoreboard:
import { Text } from 'troika-three-text';
let score = 0;
const scoreText = new Text();
scoreText.fontSize = 0.52;
scoreText.font = 'assets/SpaceMono-Bold.ttf';
scoreText.position.z = -2;
scoreText.color = 0xffa276;
scoreText.anchorX = 'center';
scoreText.anchorY = 'middle';
function updateScoreDisplay() {
const clampedScore = Math.max(0, Math.min(9999, score));
const displayScore = clampedScore.toString().padStart(4, '0');
scoreText.text = displayScore;
scoreText.sync();
}
- Score Text Setup: The
Text
object is used to display the score. We configure its font size, color, and position, ensuring it fits well within the scene. updateScoreDisplay
Function: This function updates the score text whenever the score changes. It clamps the score between 0 and 9999 and formats it to be a four-digit number, which is then displayed on the monitor.
Next, we add the score text to the scene in setupScene
:
function setupScene({ scene, camera, renderer, player, controllers }) {
// Other setup code...
// Add the score text to the scene
scene.add(scoreText);
scoreText.position.set(0, 0.67, -1.44);
scoreText.rotateX(-Math.PI / 3.3);
updateScoreDisplay();
}
- Score Text Positioning: The score text is positioned and rotated to align with a monitor in the space station scene, making it look like the monitor is displaying the player's score. You will need to adjust it if you are using a different environment.
To determine whether a bullet hits a target, we use a proximity-based hit test. This method checks the distance between the bullet and each target, and if they’re close enough, the target is considered “hit”:
function onFrame() {
// ... unchanged params
Object.values(bullets).forEach((bullet) => {
// ... unchanged logic
targets
.filter((target) => target.visible)
.forEach((target) => {
const distance = target.position.distanceTo(bullet.position);
if (distance < 1) {
delete bullets[bullet.uuid];
scene.remove(bullet);
// make target disappear, and then reappear at a different place after 2 seconds
target.visible = false;
setTimeout(() => {
target.visible = true;
target.position.x = Math.random() * 10 - 5;
target.position.z = -Math.random() * 5 - 5;
}, 2000);
score += 10; // Update the score when a target is hit
updateScoreDisplay(); // Update the rendered troika-three-text object
}
});
});
}
-
Proximity-Based Hit Test: This method checks if the distance between the bullet and a target is less than 1 unit. If it is, the bullet is removed from the scene, and the target is temporarily hidden.
-
Updating the Score: Each time a target is hit, the score is increased by 10 points, and the
updateScoreDisplay
function is called to refresh the displayed score. -
Respawning Targets: After 2 seconds, the target becomes visible again and is repositioned randomly within the scene.
-
Why Proximity-Based?: This approach is simple and effective for our scenario because the targets are round and facing the player. However, for more advanced and reliable hit detection, especially with irregularly shaped objects or targets that aren’t facing the player, you would need to look into using raycasting.
In this chapter, you’ve transformed your WebXR scene into a simple game by adding hit detection and a scoreboard. The proximity-based hit test provides a straightforward way to register hits, while the scoreboard keeps track of the player’s score, making the game more engaging and interactive.
Here’s what your game looks like now: