Added expand controls to map dice
This commit is contained in:
parent
07e85f46a3
commit
fee7a270e4
@ -1,4 +1,4 @@
|
||||
import React, { useState, Fragment, useRef } from "react";
|
||||
import React, { useState, Fragment } from "react";
|
||||
import { IconButton, Flex, Box } from "theme-ui";
|
||||
|
||||
import RadioIconButton from "./controls/RadioIconButton";
|
||||
@ -144,9 +144,6 @@ function MapContols({
|
||||
);
|
||||
}
|
||||
|
||||
const controlsRef = useRef();
|
||||
const settingsRef = useRef();
|
||||
|
||||
function getToolSettings() {
|
||||
const Settings = toolsById[selectedToolId].SettingsComponent;
|
||||
if (Settings) {
|
||||
@ -161,7 +158,6 @@ function MapContols({
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
p={1}
|
||||
ref={settingsRef}
|
||||
>
|
||||
<Settings
|
||||
settings={toolSettings[selectedToolId]}
|
||||
@ -189,7 +185,6 @@ function MapContols({
|
||||
alignItems: "center",
|
||||
}}
|
||||
mx={1}
|
||||
ref={controlsRef}
|
||||
>
|
||||
{controls}
|
||||
</Flex>
|
||||
|
@ -1,200 +1,39 @@
|
||||
import React, { useRef, useState, useCallback } from "react";
|
||||
import * as BABYLON from "babylonjs";
|
||||
import { Box } from "theme-ui";
|
||||
import React, { useState } from "react";
|
||||
import { Flex, IconButton } from "theme-ui";
|
||||
|
||||
import environment from "../../dice/environment.dds";
|
||||
|
||||
import ColorDice from "../../dice/color/ColorDice";
|
||||
import GemStoneDice from "../../dice/gemStone/GemStoneDice";
|
||||
import GlassDice from "../../dice/glass/GlassDice";
|
||||
import MetalDice from "../../dice/metal/MetalDice";
|
||||
import MetalStoneDice from "../../dice/metalStone/MetalStoneDice";
|
||||
import WoodDice from "../../dice/wood/WoodDice";
|
||||
|
||||
import Scene from "./MapDiceScene";
|
||||
import ExpandMoreDiceIcon from "../../icons/ExpandMoreDiceIcon";
|
||||
import DiceTray from "./dice/DiceTray";
|
||||
|
||||
function MapDice() {
|
||||
const sceneRef = useRef();
|
||||
const dieRef = useRef([]);
|
||||
const dieSleepRef = useRef([]);
|
||||
const [dieNumbers, setDieNumbers] = useState([]);
|
||||
|
||||
const handleSceneMount = useCallback(({ scene, engine }) => {
|
||||
sceneRef.current = scene;
|
||||
initializeScene(scene);
|
||||
engine.runRenderLoop(() => update(scene));
|
||||
}, []);
|
||||
|
||||
async function initializeScene(scene) {
|
||||
var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 2, scene);
|
||||
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
ground,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 100.0 },
|
||||
scene
|
||||
);
|
||||
ground.isVisible = false;
|
||||
|
||||
function createWall(name, x, z, yaw) {
|
||||
let wall = BABYLON.Mesh.CreateBox(
|
||||
name,
|
||||
50,
|
||||
scene,
|
||||
true,
|
||||
BABYLON.Mesh.DOUBLESIDE
|
||||
);
|
||||
wall.rotation = new BABYLON.Vector3(0, yaw, 0);
|
||||
wall.position.z = z;
|
||||
wall.position.x = x;
|
||||
wall.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
wall,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 1.0 },
|
||||
scene
|
||||
);
|
||||
wall.isVisible = false;
|
||||
}
|
||||
|
||||
createWall("wallTop", 0, -35, 0);
|
||||
createWall("wallRight", -39, 0, Math.PI / 2);
|
||||
createWall("wallBottom", 0, 35, Math.PI);
|
||||
createWall("wallLeft", 39, 0, -Math.PI / 2);
|
||||
|
||||
var roof = BABYLON.Mesh.CreateGround("roof", 100, 100, 2, scene);
|
||||
roof.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
roof,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 1.0 },
|
||||
scene
|
||||
);
|
||||
roof.position.y = 10;
|
||||
roof.isVisible = false;
|
||||
|
||||
scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
|
||||
environment,
|
||||
scene
|
||||
);
|
||||
scene.environmentIntensity = 1.5;
|
||||
}
|
||||
|
||||
function update(scene) {
|
||||
const die = dieRef.current;
|
||||
if (die.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < die.length; i++) {
|
||||
const dice = die[i];
|
||||
const diceAsleep = dieSleepRef.current[i];
|
||||
const speed = dice.physicsImpostor.getLinearVelocity().length();
|
||||
if (speed < 0.01 && !diceAsleep) {
|
||||
let highestDot = -1;
|
||||
let highestLocator;
|
||||
for (let locator of dice.getChildTransformNodes()) {
|
||||
let dif = locator
|
||||
.getAbsolutePosition()
|
||||
.subtract(dice.getAbsolutePosition());
|
||||
let direction = dif.normalize();
|
||||
const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up());
|
||||
if (dot > highestDot) {
|
||||
highestDot = dot;
|
||||
highestLocator = locator;
|
||||
}
|
||||
}
|
||||
dieSleepRef.current[i] = true;
|
||||
const newNumber = parseInt(highestLocator.name.slice(8));
|
||||
setDieNumbers((prevNumbers) => {
|
||||
let newNumbers = [...prevNumbers];
|
||||
newNumbers[i] = newNumber;
|
||||
return newNumbers;
|
||||
});
|
||||
} else if (speed > 0.5 && diceAsleep) {
|
||||
dieSleepRef.current[i] = false;
|
||||
setDieNumbers((prevNumbers) => {
|
||||
let newNumbers = [...prevNumbers];
|
||||
newNumbers[i] = null;
|
||||
return newNumbers;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (scene) {
|
||||
scene.render();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddDice(style, type) {
|
||||
const scene = sceneRef.current;
|
||||
if (scene) {
|
||||
const instance = await style.createInstance(type, scene);
|
||||
dieRef.current.push(instance);
|
||||
dieSleepRef.current.push(false);
|
||||
setDieNumbers((prevNumbers) => [...prevNumbers, null]);
|
||||
}
|
||||
}
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
<Box
|
||||
<Flex
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "500px",
|
||||
height: "500px",
|
||||
borderRadius: "4px",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
bg="overlay"
|
||||
m={2}
|
||||
ml={1}
|
||||
>
|
||||
<Scene onSceneMount={handleSceneMount} />
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "8px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
display: "flex",
|
||||
color: "white",
|
||||
<IconButton
|
||||
aria-label={isExpanded ? "Hide Dice Tray" : "Show Dice Tray"}
|
||||
title={isExpanded ? "Hide Dice Tray" : "Show Dice Tray"}
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
sx={{
|
||||
transform: `rotate(${isExpanded ? "0" : "180deg"})`,
|
||||
display: "block",
|
||||
backgroundColor: "overlay",
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
m={2}
|
||||
>
|
||||
{dieNumbers.map((num, index) => (
|
||||
<h3 key={index}>
|
||||
{num || "?"}
|
||||
{index === dieNumbers.length - 1 ? "" : "+"}
|
||||
</h3>
|
||||
))}
|
||||
<h3>
|
||||
{dieNumbers.length > 0 &&
|
||||
`= ${dieNumbers.reduce((a, b) => (a || 0) + (b || 0))}`}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "24px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<button onClick={() => handleAddDice(ColorDice, "d20")}>
|
||||
Add color d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(GemStoneDice, "d20")}>
|
||||
Add gem d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(GlassDice, "d20")}>
|
||||
Add glass d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(MetalDice, "d20")}>
|
||||
Add metal d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(MetalStoneDice, "d20")}>
|
||||
Add metal stone d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(WoodDice, "d20")}>
|
||||
Add wood d20
|
||||
</button>
|
||||
</div>
|
||||
</Box>
|
||||
<ExpandMoreDiceIcon />
|
||||
</IconButton>
|
||||
<DiceTray isOpen={isExpanded} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import * as BABYLON from "babylonjs";
|
||||
import * as AMMO from "ammo.js";
|
||||
import "babylonjs-loaders";
|
||||
|
||||
function MapDiceScene({ onSceneMount }) {
|
||||
function DiceScene({ onSceneMount }) {
|
||||
const sceneRef = useRef();
|
||||
const engineRef = useRef();
|
||||
const canvasRef = useRef();
|
||||
@ -121,4 +121,4 @@ function MapDiceScene({ onSceneMount }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default MapDiceScene;
|
||||
export default DiceScene;
|
209
src/components/map/dice/DiceTray.js
Normal file
209
src/components/map/dice/DiceTray.js
Normal file
@ -0,0 +1,209 @@
|
||||
import React, { useRef, useState, useCallback, useEffect } from "react";
|
||||
import * as BABYLON from "babylonjs";
|
||||
import { Box } from "theme-ui";
|
||||
|
||||
import environment from "../../../dice/environment.dds";
|
||||
|
||||
import ColorDice from "../../../dice/color/ColorDice";
|
||||
import GemStoneDice from "../../../dice/gemStone/GemStoneDice";
|
||||
import GlassDice from "../../../dice/glass/GlassDice";
|
||||
import MetalDice from "../../../dice/metal/MetalDice";
|
||||
import MetalStoneDice from "../../../dice/metalStone/MetalStoneDice";
|
||||
import WoodDice from "../../../dice/wood/WoodDice";
|
||||
|
||||
import Scene from "./DiceScene";
|
||||
|
||||
function DiceTray({ isOpen }) {
|
||||
const sceneRef = useRef();
|
||||
const dieRef = useRef([]);
|
||||
const dieSleepRef = useRef([]);
|
||||
const [dieNumbers, setDieNumbers] = useState([]);
|
||||
|
||||
const sceneSleepRef = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
sceneSleepRef.current = true;
|
||||
} else {
|
||||
sceneSleepRef.current = false;
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleSceneMount = useCallback(({ scene, engine }) => {
|
||||
sceneRef.current = scene;
|
||||
initializeScene(scene);
|
||||
engine.runRenderLoop(() => update(scene));
|
||||
}, []);
|
||||
|
||||
async function initializeScene(scene) {
|
||||
var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 2, scene);
|
||||
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
ground,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 100.0 },
|
||||
scene
|
||||
);
|
||||
ground.isVisible = false;
|
||||
|
||||
function createWall(name, x, z, yaw) {
|
||||
let wall = BABYLON.Mesh.CreateBox(
|
||||
name,
|
||||
50,
|
||||
scene,
|
||||
true,
|
||||
BABYLON.Mesh.DOUBLESIDE
|
||||
);
|
||||
wall.rotation = new BABYLON.Vector3(0, yaw, 0);
|
||||
wall.position.z = z;
|
||||
wall.position.x = x;
|
||||
wall.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
wall,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 1.0 },
|
||||
scene
|
||||
);
|
||||
wall.isVisible = false;
|
||||
}
|
||||
|
||||
createWall("wallTop", 0, -35, 0);
|
||||
createWall("wallRight", -35, 0, Math.PI / 2);
|
||||
createWall("wallBottom", 0, 35, Math.PI);
|
||||
createWall("wallLeft", 35, 0, -Math.PI / 2);
|
||||
|
||||
var roof = BABYLON.Mesh.CreateGround("roof", 100, 100, 2, scene);
|
||||
roof.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
roof,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 1.0 },
|
||||
scene
|
||||
);
|
||||
roof.position.y = 10;
|
||||
roof.isVisible = false;
|
||||
|
||||
scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
|
||||
environment,
|
||||
scene
|
||||
);
|
||||
scene.environmentIntensity = 1.5;
|
||||
}
|
||||
|
||||
function update(scene) {
|
||||
const die = dieRef.current;
|
||||
const shouldSleep = sceneSleepRef.current;
|
||||
if (die.length === 0 || shouldSleep) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < die.length; i++) {
|
||||
const dice = die[i];
|
||||
const diceAsleep = dieSleepRef.current[i];
|
||||
const speed = dice.physicsImpostor.getLinearVelocity().length();
|
||||
if (speed < 0.01 && !diceAsleep) {
|
||||
let highestDot = -1;
|
||||
let highestLocator;
|
||||
for (let locator of dice.getChildTransformNodes()) {
|
||||
let dif = locator
|
||||
.getAbsolutePosition()
|
||||
.subtract(dice.getAbsolutePosition());
|
||||
let direction = dif.normalize();
|
||||
const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up());
|
||||
if (dot > highestDot) {
|
||||
highestDot = dot;
|
||||
highestLocator = locator;
|
||||
}
|
||||
}
|
||||
dieSleepRef.current[i] = true;
|
||||
const newNumber = parseInt(highestLocator.name.slice(8));
|
||||
setDieNumbers((prevNumbers) => {
|
||||
let newNumbers = [...prevNumbers];
|
||||
newNumbers[i] = newNumber;
|
||||
return newNumbers;
|
||||
});
|
||||
} else if (speed > 0.5 && diceAsleep) {
|
||||
dieSleepRef.current[i] = false;
|
||||
setDieNumbers((prevNumbers) => {
|
||||
let newNumbers = [...prevNumbers];
|
||||
newNumbers[i] = null;
|
||||
return newNumbers;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (scene) {
|
||||
scene.render();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddDice(style, type) {
|
||||
const scene = sceneRef.current;
|
||||
if (scene) {
|
||||
const instance = await style.createInstance(type, scene);
|
||||
dieRef.current.push(instance);
|
||||
dieSleepRef.current.push(false);
|
||||
setDieNumbers((prevNumbers) => [...prevNumbers, null]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "500px",
|
||||
height: "500px",
|
||||
borderRadius: "4px",
|
||||
display: isOpen ? "box" : "none",
|
||||
}}
|
||||
bg="overlay"
|
||||
>
|
||||
<Scene onSceneMount={handleSceneMount} />
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "8px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
display: "flex",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
{dieNumbers.map((num, index) => (
|
||||
<h3 key={index}>
|
||||
{num || "?"}
|
||||
{index === dieNumbers.length - 1 ? "" : "+"}
|
||||
</h3>
|
||||
))}
|
||||
<h3>
|
||||
{dieNumbers.length > 0 &&
|
||||
`= ${dieNumbers.reduce((a, b) => (a || 0) + (b || 0))}`}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "24px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<button onClick={() => handleAddDice(ColorDice, "d20")}>
|
||||
Add color d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(GemStoneDice, "d20")}>
|
||||
Add gem d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(GlassDice, "d20")}>
|
||||
Add glass d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(MetalDice, "d20")}>
|
||||
Add metal d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(MetalStoneDice, "d20")}>
|
||||
Add metal stone d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(WoodDice, "d20")}>
|
||||
Add wood d20
|
||||
</button>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DiceTray;
|
18
src/icons/ExpandMoreDiceIcon.js
Normal file
18
src/icons/ExpandMoreDiceIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function ExpandMoreDiceIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M13.006 3.564l5.986 3.42A2 2 0 0120 8.72v6.516a2 2 0 01-1 1.732l-5.983 3.456a2 2 0 01-1.997.002l-6.017-3.459A2 2 0 014 15.233v-6.53a2 2 0 011.015-1.74L11.03 3.56a2 2 0 011.977.004zm-.992 1.737L6 8.703v6.53l6.017 3.459L18 15.236V8.72L12.014 5.3zM13.92 10a1 1 0 01.781 1.625l-1.92 2.399a1 1 0 01-1.56 0l-1.92-2.4A1 1 0 0110.08 10z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ExpandMoreDiceIcon;
|
Loading…
Reference in New Issue
Block a user