<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>VirtualShop-test</title>
<link rel="stylesheet" href="button.css"> <!-- CSSファイルをリンク -->
<link rel="icon" href="favicon.ico">
</head>
<body>
<div id="WebGL-output"></div>
<div id="overlay"><!-- 透明なオーバーレイ -->
<button id="smile-button">⸜(*ˊᗜˋ*)⸝ smile</button>
<button id="sad-button">( ̄口 ̄∥) sad</button>
<button id="surprised-button">…!?∑(OωO; ) surprised</button>
</div>
<script type="module">
import * as THREE from "https://unpkg.com/three@0.126.1/build/three.module.js";
import { PointerLockControls } from "https://unpkg.com/three@0.126.1/examples/jsm/controls/PointerLockControls.js";
import { GLTFLoader } from "https://unpkg.com/three@0.126.1/examples/jsm/loaders/GLTFLoader.js";
let camera;
let scene;
let renderer;
let controls;
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let bChanLoaded = false; // B-chan.glbがロードされたかどうかを示すフラグ
let VirtualShop, WorldStick, bchanModel, mixer;
function init() {
// シーンの作成
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff); // 背景色
// カメラの作成
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(1, 5, 5);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// ライトの作成
//const light = new THREE.SpotLight(0xffffff, 1.0); デフォルト
//const light = new THREE.AmbientLight(0xffffff, 1.0); 白飛びしやすい
const light = new THREE.DirectionalLight(0xffffff, 1.0);
light.position.set(100, 500, 500);
scene.add(light);
light.castShadow = true;
// モデルの読み込み
const loader = new GLTFLoader();
loader.load(
'VirtualShop-final.glb',
function (gltf) {
VirtualShop = gltf.scene;
scene.add(VirtualShop);
}
);
loader.load(
'worldstick.glb',
function (gltf) {
WorldStick = gltf.scene;
scene.add(WorldStick);
}
);
// B-chanモデルの読み込み
loader.load(
'B-chan_plus_feel.glb',
//'B-chan_plus.glb',
function (gltf) {
bchanModel = gltf.scene;
scene.add(bchanModel);
bChanLoaded = true;
if (gltf.animations && gltf.animations.length) {
mixer = new THREE.AnimationMixer(bchanModel);
gltf.animations.forEach((clip) => mixer.clipAction(clip).play());
} else {
console.log("アニメーションがありません");
}
},
function (xhr) {
const progress = (xhr.loaded / xhr.total) * 100;
console.log(`読み込み進捗: ${Math.round(progress)}%`);
},
function (error) {
console.error('読み込みエラー', error);
console.log('読み込みエラー');
if (error && error.target) {
console.error('エラーが発生したURL:', error.target.responseURL);
}
}
);
// カメラコントロールの作成
controls = new PointerLockControls(camera, document.body);
const outputDiv = document.getElementById("WebGL-output");
outputDiv.addEventListener("click", function () {
controls.lock();
});
// 透明オーバーレイのクリックイベントを無効化
const overlay = document.getElementById("overlay");
overlay.addEventListener("click", function (event) {
event.preventDefault();
event.stopPropagation(); // イベントの伝播を止める
});
// キーボードのキー操作
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("keyup", handleKeyUp);
// レンダラーの作成
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8);
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// リサイズイベントの追加
window.addEventListener("resize", function () {
renderer.setSize(window.innerWidth * 0.8, window.innerHeight * 0.8);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});
}
function handleKeyDown(event) {
switch (event.key) {
case "w":
case "ArrowUp":
moveForward = true;
break;
case "s":
case "ArrowDown":
moveBackward = true;
break;
case "a":
case "ArrowLeft":
moveLeft = true;
break;
case "d":
case "ArrowRight":
moveRight = true;
break;
case "g": // 前へ進む
if (bChanLoaded) {
bchanModel.position.z -= 0.5; // 動かす方向を調整
}
break;
case "b": // 後ろへ進む
if (bChanLoaded) {
bchanModel.position.z += 0.5; // 動かす方向を調整
}
break;
case "f": // 左へ進む
if (bChanLoaded) {
bchanModel.position.x -= 0.5; // 動かす方向を調整
}
break;
case "h": // 右へ進む
if (bChanLoaded) {
bchanModel.position.x += 0.5; // 動かす方向を調整
}
break;
case "r": // Y軸固定で回転
if (bChanLoaded) {
bchanModel.rotation.y += Math.PI / 16; // 回転角度を指定(ここでは 22.5度)
}
break;
}
}
function handleKeyUp(event) {
switch (event.key) {
case "w":
case "ArrowUp":
moveForward = false;
break;
case "s":
case "ArrowDown":
moveBackward = false;
break;
case "a":
case "ArrowLeft":
moveLeft = false;
break;
case "d":
case "ArrowRight":
moveRight = false;
break;
}
}
function updateCameraPosition() {
const speed = 10;
const direction = new THREE.Vector3();
const { x, y, z } = controls.getObject().position;
direction.x = Number(moveRight) - Number(moveLeft);
direction.z = Number(moveBackward) - Number(moveForward);
direction.normalize();
const delta = speed * 0.1;
controls.getObject().translateX(direction.x * delta);
controls.getObject().translateZ(direction.z * delta);
}
// クリック時の処理
document.addEventListener("dblclick", function (event) {
event.preventDefault();
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
if (intersects[0].object.name == "ICO球") {
window.open("buy.html");
}
console.log(intersects[0]);
}
});
// 円ボタンのクリックイベント
document.getElementById("smile-button").addEventListener("click", function() {
console.log("smile ⸜(*ˊᗜˋ*)⸝ボタンがクリックされました!");
// "smile"のアニメーションを再生する
playAnimation("smile");
});
// 2番目のボタンのクリックイベント
document.getElementById("sad-button").addEventListener("click", function() {
console.log("sad ( ̄口 ̄∥)ボタンがクリックされました!");
// "sad"のアニメーションを再生する
playAnimation("sad");
});
// 3番目のボタンのクリックイベント
document.getElementById("surprised-button").addEventListener("click", function() {
console.log("surprised …!?∑(OωO; )ボタンがクリックされました!");
// "surprised"のアニメーションを再生する
playAnimation("surprised");
});
// アニメーションを再生する関数
function playAnimation(type) {
if (!bChanLoaded || !mixer) return;
const animation = bchanModel.animations.find(anim => anim.name === type);
if (animation) {
const action = mixer.clipAction(animation);
action.reset().play();
} else {
console.log(`${type}のアニメーションが見つかりません`);
}
}
// アニメーションを更新
function animate() {
requestAnimationFrame(animate);
if (mixer) mixer.update(0.01);
updateCameraPosition(); // カメラの移動更新
renderer.render(scene, camera);
}
init();
animate();
</script>
</body>
</html>