diff --git a/src/components/map/MapDice.js b/src/components/map/MapDice.js index 2798c35..fc64c04 100644 --- a/src/components/map/MapDice.js +++ b/src/components/map/MapDice.js @@ -5,7 +5,7 @@ import ExpandMoreDiceIcon from "../../icons/ExpandMoreDiceIcon"; import DiceTray from "./dice/DiceTray"; function MapDice() { - const [isExpanded, setIsExpanded] = useState(true); + const [isExpanded, setIsExpanded] = useState(false); return ( + onDiceAdd(SunsetDice, "d20")} + > + + + onDiceAdd(SunsetDice, "d12")} + > + + + onDiceAdd(SunsetDice, "d10")} + > + + + onDiceAdd(SunsetDice, "d8")} + > + + + onDiceAdd(SunsetDice, "d6")} + > + + + onDiceAdd(SunsetDice, "d4")} + > + + + onDiceAdd(SunsetDice, "d100")} + > + + + + ); +} + +export default DiceButtons; diff --git a/src/components/map/dice/DiceControls.js b/src/components/map/dice/DiceControls.js index e1cc873..4e4dc58 100644 --- a/src/components/map/dice/DiceControls.js +++ b/src/components/map/dice/DiceControls.js @@ -1,80 +1,123 @@ -import React from "react"; -import { Flex } from "theme-ui"; +import React, { useEffect, useState } from "react"; +import * as BABYLON from "babylonjs"; -import SunsetDice from "../../../dice/galaxy/GalaxyDice"; +import DiceButtons from "./DiceButtons"; +import DiceResults from "./DiceResults"; -import D20Icon from "../../../icons/D20Icon"; -import D12Icon from "../../../icons/D12Icon"; -import D10Icon from "../../../icons/D10Icon"; -import D8Icon from "../../../icons/D8Icon"; -import D6Icon from "../../../icons/D6Icon"; -import D4Icon from "../../../icons/D4Icon"; -import D100Icon from "../../../icons/D100Icon"; +function DiceControls({ + diceRefs, + sceneVisibleRef, + onDiceAdd, + onDiceClear, + onDiceReroll, +}) { + const [diceRolls, setDiceRolls] = useState([]); -import DiceButton from "./DiceButton"; - -function DiceControls({ diceRolls, onDiceAdd }) { - const diceCounts = {}; - for (let dice of diceRolls) { - if (dice.type in diceCounts) { - diceCounts[dice.type] += 1; - } else { - diceCounts[dice.type] = 1; + // Update dice rolls + useEffect(() => { + // Find the number facing up on a dice object + function getDiceRoll(dice) { + let number = getDiceInstanceRoll(dice.instance); + // If the dice is a d100 add the d10 + if (dice.type === "d100") { + const d10Number = getDiceInstanceRoll(dice.d10Instance); + // Both zero set to 100 + if (d10Number === 0 && number === 0) { + number = 100; + } else { + number += d10Number; + } + } else if (dice.type === "d10" && number === 0) { + number = 10; + } + return { type: dice.type, roll: number }; } - } + + // Find the number facing up on a mesh instance of a dice + function getDiceInstanceRoll(instance) { + let highestDot = -1; + let highestLocator; + for (let locator of instance.getChildTransformNodes()) { + let dif = locator + .getAbsolutePosition() + .subtract(instance.getAbsolutePosition()); + let direction = dif.normalize(); + const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up()); + if (dot > highestDot) { + highestDot = dot; + highestLocator = locator; + } + } + return parseInt(highestLocator.name.slice(12)); + } + + function updateDiceRolls() { + const die = diceRefs.current; + const sceneVisible = sceneVisibleRef.current; + if (!sceneVisible) { + return; + } + const diceAwake = die.map((dice) => dice.asleep).includes(false); + if (!diceAwake) { + return; + } + + let newRolls = []; + for (let i = 0; i < die.length; i++) { + const dice = die[i]; + let roll = getDiceRoll(dice); + newRolls[i] = roll; + } + setDiceRolls(newRolls); + } + + const updateInterval = setInterval(updateDiceRolls, 100); + return () => { + clearInterval(updateInterval); + }; + }, [diceRefs, sceneVisibleRef]); return ( - - onDiceAdd(SunsetDice, "d20")} + <> +
- - - onDiceAdd(SunsetDice, "d12")} + { + onDiceClear(); + setDiceRolls([]); + }} + onDiceReroll={onDiceReroll} + /> +
+
- - - onDiceAdd(SunsetDice, "d10")} - > - - - onDiceAdd(SunsetDice, "d8")} - > - - - onDiceAdd(SunsetDice, "d6")} - > - - - onDiceAdd(SunsetDice, "d4")} - > - - - onDiceAdd(SunsetDice, "d100")} - > - - - + { + onDiceAdd(style, type); + setDiceRolls((prevRolls) => [ + ...prevRolls, + { type, roll: "unknown" }, + ]); + }} + /> +
+ ); } diff --git a/src/components/map/dice/DiceScene.js b/src/components/map/dice/DiceScene.js index d11494c..c93ab85 100644 --- a/src/components/map/dice/DiceScene.js +++ b/src/components/map/dice/DiceScene.js @@ -3,7 +3,7 @@ import * as BABYLON from "babylonjs"; import * as AMMO from "ammo.js"; import "babylonjs-loaders"; -function DiceScene({ onSceneMount }) { +function DiceScene({ onSceneMount, onPointerDown, onPointerUp }) { const sceneRef = useRef(); const engineRef = useRef(); const canvasRef = useRef(); @@ -86,6 +86,7 @@ function DiceScene({ onSceneMount }) { selectedMeshRef.current = pickInfo.pickedMesh; } } + onPointerDown(); } function handlePointerUp() { @@ -105,6 +106,8 @@ function DiceScene({ onSceneMount }) { } selectedMeshRef.current = null; selectedMeshDeltaPositionRef.current = null; + + onPointerUp(); } return ( @@ -121,4 +124,9 @@ function DiceScene({ onSceneMount }) { ); } +DiceScene.defaultProps = { + onPointerDown() {}, + onPointerUp() {}, +}; + export default DiceScene; diff --git a/src/components/map/dice/DiceTray.js b/src/components/map/dice/DiceTray.js index 5350e77..2a6327a 100644 --- a/src/components/map/dice/DiceTray.js +++ b/src/components/map/dice/DiceTray.js @@ -1,4 +1,4 @@ -import React, { useRef, useState, useCallback, useEffect } from "react"; +import React, { useRef, useCallback, useEffect } from "react"; import * as BABYLON from "babylonjs"; import { Box } from "theme-ui"; @@ -6,7 +6,6 @@ import environment from "../../../dice/environment.dds"; import Scene from "./DiceScene"; import DiceControls from "./DiceControls"; -import DiceResults from "./DiceResults"; import Dice from "../../../dice/Dice"; import createDiceTray, { @@ -16,18 +15,29 @@ import createDiceTray, { function DiceTray({ isOpen }) { const sceneRef = useRef(); const shadowGeneratorRef = useRef(); - const dieRef = useRef([]); - const dieSleepRef = useRef([]); - const [diceRolls, setDiceRolls] = useState([]); - - const sceneSleepRef = useRef(true); + const diceRefs = useRef([]); + const sceneVisibleRef = useRef(false); + const sceneInteractionRef = useRef(false); + // Set to true to ignore scene sleep and visible values + const forceSceneRenderRef = useRef(false); useEffect(() => { - if (!isOpen) { - sceneSleepRef.current = true; + let openTimeout; + if (isOpen) { + sceneVisibleRef.current = true; + // Force scene rendering on open for 1s to ensure dice tray is rendered + forceSceneRenderRef.current = true; + openTimeout = setTimeout(() => { + forceSceneRenderRef.current = false; + }, 1000); } else { - sceneSleepRef.current = false; + sceneVisibleRef.current = false; } + return () => { + if (openTimeout) { + clearTimeout(openTimeout); + } + }; }, [isOpen]); const handleSceneMount = useCallback(({ scene, engine }) => { @@ -124,69 +134,34 @@ function DiceTray({ isOpen }) { } } - // Find the number facing up on a dice object - function getDiceRoll(dice) { - let number = getDiceInstanceRoll(dice.instance); - // If the dice is a d100 add the d10 - if (dice.type === "d100") { - const d10Number = getDiceInstanceRoll(dice.d10Instance); - // Both zero set to 100 - if (d10Number === 0 && number === 0) { - number = 100; - } else { - number += d10Number; - } - } else if (dice.type === "d10" && number === 0) { - number = 10; - } - return { type: dice.type, roll: number }; + const die = diceRefs.current; + const sceneVisible = sceneVisibleRef.current; + if (!sceneVisible) { + return; } - - // Find the number facing up on a mesh instance of a dice - function getDiceInstanceRoll(instance) { - let highestDot = -1; - let highestLocator; - for (let locator of instance.getChildTransformNodes()) { - let dif = locator - .getAbsolutePosition() - .subtract(instance.getAbsolutePosition()); - let direction = dif.normalize(); - const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up()); - if (dot > highestDot) { - highestDot = dot; - highestLocator = locator; - } - } - return parseInt(highestLocator.name.slice(12)); - } - - const die = dieRef.current; - const shouldSleep = sceneSleepRef.current; - if (shouldSleep) { + const sceneInteraction = sceneInteractionRef.current; + const forceSceneRender = forceSceneRenderRef.current; + const diceAwake = die.map((dice) => dice.asleep).includes(false); + // Return early if scene doesn't need to be re-rendered + if (!forceSceneRender && !sceneInteraction && !diceAwake) { return; } for (let i = 0; i < die.length; i++) { const dice = die[i]; - const diceIsAsleep = dieSleepRef.current[i]; const speed = getDiceSpeed(dice); - if (speed < 0.01 && !diceIsAsleep) { - dieSleepRef.current[i] = true; - let roll = getDiceRoll(dice); - setDiceRolls((prevRolls) => { - let newRolls = [...prevRolls]; - newRolls[i] = roll; - return newRolls; - }); - } else if (speed > 0.5 && diceIsAsleep) { - dieSleepRef.current[i] = false; - setDiceRolls((prevRolls) => { - let newRolls = [...prevRolls]; - newRolls[i].roll = "unknown"; - return newRolls; - }); + // If the speed has been below 0.01 for 1s set dice to sleep + if (speed < 0.01 && !dice.sleepTimout) { + dice.sleepTimout = setTimeout(() => { + dice.asleep = true; + }, 1000); + } else if (speed > 0.5 && (dice.asleep || dice.sleepTimout)) { + dice.asleep = false; + clearTimeout(dice.sleepTimout); + dice.sleepTimout = null; } } + if (scene) { scene.render(); } @@ -199,7 +174,7 @@ function DiceTray({ isOpen }) { const instance = await style.createInstance(type, scene); shadowGenerator.addShadowCaster(instance); Dice.roll(instance); - let dice = { type, instance }; + let dice = { type, instance, asleep: false }; // If we have a d100 add a d10 as well if (type === "d100") { const d10Instance = await style.createInstance("d10", scene); @@ -207,32 +182,36 @@ function DiceTray({ isOpen }) { Dice.roll(d10Instance); dice.d10Instance = d10Instance; } - dieRef.current.push(dice); - dieSleepRef.current.push(false); - setDiceRolls((prevRolls) => [...prevRolls, { type, roll: "unknown" }]); + diceRefs.current.push(dice); } } function handleDiceClear() { - const die = dieRef.current; + const die = diceRefs.current; for (let dice of die) { dice.instance.dispose(); if (dice.type === "d100") { dice.d10Instance.dispose(); } } - dieRef.current = []; - dieSleepRef.current = []; - setDiceRolls([]); + diceRefs.current = []; + // Force scene rendering to show cleared dice + forceSceneRenderRef.current = true; + setTimeout(() => { + if (forceSceneRenderRef) { + forceSceneRenderRef.current = false; + } + }, 100); } function handleDiceReroll() { - const die = dieRef.current; + const die = diceRefs.current; for (let dice of die) { Dice.roll(dice.instance); if (dice.type === "d100") { Dice.roll(dice.d10Instance); } + dice.asleep = false; } } @@ -248,33 +227,22 @@ function DiceTray({ isOpen }) { }} bg="background" > - -
{ + sceneInteractionRef.current = true; }} - > - -
-
{ + sceneInteractionRef.current = false; }} - > - -
+ /> + ); }