実現したいこと
週刊アスキーに載っていたJavascriptのコードで実行すると3Dのサイコロを転がすようです。
しかし以下のようなエラーが出て動きません。
どのようにコードを修正すれば良いでしょうか?
発生している問題・分からないこと
エラーが発生してコードが動かない
エラーメッセージ
error
1Uncaught TypeError: dice.mesh.material.map is null
該当のソースコード
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>ころがせサイコロ</title> <script src="https://unpkg.com/three@0.137.4/build/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.js"></script> <script> let scene, camera, renderer; // シーン、カメラ、レンダー let world, dice; // 物理ワールド、サイコロ const canvas = new Array(); // キャンバス const context = new Array(); const c_t = [0, 5, 2, 3, 4, 1]; // テクスチャの順序 let sx, sy, mouseDown = false; // 始点、マウスが押されたか const init = () => { // シーン、カメラ、レンダー、ライト(平行光源、環境光) scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(50, 1, 1, 100); camera.position.set(5, 10, 5); camera.lookAt(scene.position); renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(600, 600); renderer.shadowMap.enabled = true; document.getElementById("renderArea").appendChild(renderer.domElement); const light = new THREE.DirectionalLight("#FFFFFF"); light.position.set(20, 10, 10); light.castShadow = true; scene.add(light); const ambientLight = new THREE.AmbientLight("#666666"); scene.add(ambientLight); // 物理ワールドの設定 world = new CANNON.World(); world.gravity.set(0, -9.8, 0); world.broadphase = new CANNON.NaiveBroadphase(); world.allowSleep = true; // 床の作成 const green = new THREE.MeshLambertMaterial({color: "#006600"}); createBox(0, 0, 0, 10, 1, 10, green); // テクスチャ用キャンバスをセット for (let i = 0; i < 6; i++) { canvas[i] = document.getElementById(`canvas_${i}`); context[i] = canvas[i].getContext("2d"); context[i].lineWidth = 4; context[i].lineCap = "round"; canvas[i].addEventListener("mousedown", startDraw); canvas[i].addEventListener("mousemove", draw); canvas[i].addEventListener("mouseup", endDraw); canvas[i].addEventListener("mouseleave", endDraw); } // サイコロの作成 const materials = new Array(); for (let i = 0; i < 6; i++) { const texture = new THREE.Texture(canvas[c_t[i]]); materials[i] = new THREE.MeshLambertMaterial({map: texture}); } dice = createBox(0, 5, 0.1, 1.1, materials, 1); dice.body.allowSleep = true; clearDice(); // 物理ワールドの更新+描画 update(); } const createBox = (x, y, z, w, h, d, material, mass = 0) => { // 表示用オブジェクトの作成 const geometry = new THREE.BoxGeometry(w, h, d); const mesh = new THREE.Mesh(geometry, material); mesh.position.set(x, y, z); mesh.castShadow = true; mesh.receiveShadow = true; scene.add(mesh); // 物理用オブジェクトの作成 const body = new CANNON.Body({mass: mass}); body.addShape(new CANNON.Box(new CANNON.Vec3(w / 2, h / 2, d / 2))); body.position.copy(mesh.position); world.addBody(body); return {"mesh": mesh, "body": body}; } const update = () => { // 物理ワールドの更新 world.step(1 / 60); dice.mesh.position.copy(dice.body.position); dice.mesh.quaternion.copy(dice.body.quaternion); // サイコロの目の検出 if (dice.body.sleepState > 0) { // 角度を変換 let x = dice.mesh.rotation.x / Math.PI * 180 + 180; let z = dice.mesh.rotation.z / Math.PI * 180 + 180; x = Math.round(x / 10) * 10; z = Math.round(z / 10) * 10; if (x === 360) x = 0; if (z === 360) z = 0; // 上の面を取得 let index = -1; if (x === 270) index = 1; if (x === 90) index = 4; if ((x === 180) && (z === 270) || ((x === 0) && (z === 90))) index = 0; if ((x === 180) && (z === 90) || ((x === 0) && (z === 270))) index = 5; if ((x === 90) && (z === 0) || ((x === 270) && (z === 180))) index = 2; if ((x === 90) && (z === 180) || ((x === 270) && (z === 0))) index = 3; // サイコロの目のキャンバスにスタイルをセット if (index > -1) canvas[index].classList.add("select"); } // テクスチャの更新許可、描画 if (Array.isArray(dice.mesh.material)) { dice.mesh.material.forEach(elm => elm.map.needsUpdate = true); } else { dice.mesh.material.map.needsUpdate = true; } renderer.render(scene, camera); window.requestAnimationFrame(update); } const cast = () => { // サイコロを投げる dice.body.wakeUp(); dice.body.position.set(0, 5, 0); const ax = Math.random() * 20 - 10; const ay = Math.random() * 20 - 10; const az = Math.random() * 20 - 10; dice.body.angularVelocity.set(ax, ay, az); // キャンバスからスタイルを削除 canvas.forEach(elm => elm.classList.remove("select")); } const startDraw = event => { // 描画開始 [sx, sy] = getPoint(event); mouseDown = true; } const draw = event => { // 描画 if (mouseDown) { const [x, y] = getPoint(event); const index = event.target.id.split("_")[1]; context[index].beginPath(); context[index].moveTo(sx, sy); context[index].lineTo(x, y); context[index].stroke(); [sx, sy] = [x, y]; } } const endDraw = event => { // 描画終了 mouseDown = false; } const getPoint = event => { // マウスカーソルの位置を取得 const rect = event.target.getBoundingClientRect(); return [event.clientX - rect.left, event.clientY - rect.top]; } const clearDice = () => { // サイコロの目をクリア for (let i = 0; i < 6; i++) { context[i].fillStyle = `hsl(${i * 60}, 100%, 80%)`; context[i].fillRect(0, 0, 128, 128); } } </script> <style> canvas {border: medium solid #CCCCCC;} .select {border: medium solid #FF0000;} </style> <body onload="init()"> <p>ころがせサイコロ</p> <input type="button" value="サイコロを投げる" onclick="cast()"> <input type="button" value="クリア" onclick="clearDice()"> <hr> <canvas id="canvas_0" width="128" height="128"></canvas> <canvas id="canvas_1" width="128" height="128"></canvas> <canvas id="canvas_2" width="128" height="128"></canvas> <canvas id="canvas_3" width="128" height="128"></canvas> <canvas id="canvas_4" width="128" height="128"></canvas> <canvas id="canvas_5" width="128" height="128"></canvas> <div id="renderArea"></div> </body> </html>
試したこと・調べたこと
上記の詳細・結果
ChatGPTに聞いてもコード修正しても変わらずエラーが出ます
補足
特になし
0 コメント