test4-meiro
<!DOCTYPE html>
<html>
<head>
<title>3D Maze in VR (A-Frame)</title>
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-environment-component@1.3.1/dist/aframe-environment-component.min.js"></script>
<script>
// カスタムコンポーネント: キー{ード入力での移動
AFRAME.registerComponent('movement-controls', {
schema: {
speed: { type: 'number', default: 0.2 }
},
init: function () {
this.direction = new THREE.Vector3();
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
window.addEventListener('keydown', this.onKeyDown);
window.addEventListener('keyup', this.onKeyUp);
this.forward = false;
this.backward = false;
this.left = false;
this.right = false;
},
tick: function (time, timeDelta) {
const speed = this.data.speed;
const rotation = this.el.getAttribute('rotation');
const direction = this.direction;
direction.set(0, 0, 0); // 毎回リセット
if (this.forward) direction.z -= 1;
if (this.backward) direction.z += 1;
if (this.left) direction.x -= 1;
if (this.right) direction.x += 1;
direction.normalize(); // ベクトルの長さを1に
if (direction.length() > 0) {
// カメラの向きを考慮して移動
const cameraRotation = this.el.getAttribute('rotation');
// Y軸周りの回転(Yaw)だけを適用するクォータニオンを作成
const yawQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, THREE.MathUtils.degToRad(cameraRotation.y), 0, 'YXZ'));
direction.applyQuaternion(yawQuaternion);
// 移動量を計算
const deltaPosition = direction.multiplyScalar(speed * timeDelta / 1000);
// 現在の位置に加算
const currentPosition = this.el.getAttribute('position');
const newPosition = {
x: currentPosition.x + deltaPosition.x,
y: currentPosition.y, // 高さは変えない
z: currentPosition.z + deltaPosition.z
};
this.el.setAttribute('position', newPosition);
}
},
onKeyDown: function (event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
this.forward = true;
break;
case 'ArrowDown':
case 'KeyS':
this.backward = true;
break;
case 'ArrowLeft':
case 'KeyA':
this.left = true;
break;
case 'ArrowRight':
case 'KeyD':
this.right = true;
break;
}
},
onKeyUp: function (event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
this.forward = false;
break;
case 'ArrowDown':
case 'KeyS':
this.backward = false;
break;
case 'ArrowLeft':
case 'KeyA':
this.left = false;
break;
case 'ArrowRight':
case 'KeyD':
this.right = false;
break;
}
},
remove: function () {
window.removeEventListener('keydown', this.onKeyDown);
window.removeEventListener('keyup', this.onKeyUp);
}
});
// 迷路生成 (簡単な例)
AFRAME.registerComponent('maze-generator', {
init: function () {
const mazeData = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 0, 1, 1, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 1, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
const wallSize = 2; // 壁のサイズ
const wallHeight = 3; //壁の高さ
for (let row = 0; row < mazeData.length; row++) {
for (let col = 0; col < mazeData[row].length; col++) {
if (mazeData[row][col] === 1) {
const wall = document.createElement('a-box');
wall.setAttribute('width', wallSize);
wall.setAttribute('height', wallHeight); // 高さを設定
wall.setAttribute('depth', wallSize);
wall.setAttribute('position', {
x: col * wallSize,
y: wallHeight / 2, // 高さを半分にして地面に合わせる
z: row * wallSize
});
wall.setAttribute('material', 'color', 'gray'); // 壁の色
this.el.appendChild(wall);
}
}
}
// 天井 (屋根)
const ceiling = document.createElement('a-plane');
ceiling.setAttribute('width', wallSize * mazeData[0].length);
ceiling.setAttribute('height', wallSize * mazeData.length);
ceiling.setAttribute('position', { x: (wallSize * mazeData[0].length) / 2 - wallSize/2, y: wallHeight, z: (wallSize * mazeData.length) / 2 - wallSize/2 }); //中央に
ceiling.setAttribute('rotation', '-90 0 0');
ceiling.setAttribute('material', 'color', '#87CEEB'); // 空のような色
ceiling.setAttribute('side', 'double'); //両面
this.el.appendChild(ceiling);
// 床
const floor = document.createElement('a-plane');
floor.setAttribute('width', wallSize * mazeData[0].length);
floor.setAttribute('height', wallSize * mazeData.length);
floor.setAttribute('position', {x: (wallSize * mazeData[0].length) / 2 - wallSize/2, y: 0, z: (wallSize * mazeData.length) / 2- wallSize/2 });
floor.setAttribute('rotation', '-90 0 0');
floor.setAttribute('material', 'color', '#228B22'); // 草のような色
floor.setAttribute('side', 'double');
this.el.appendChild(floor);
}
});
</script>
</head>
<body>
<a-scene>
<a-entity id="rig" movement-controls>
<a-entity camera look-controls wasd-controls="enabled:false" position="0 1.6 0"></a-entity>
</a-entity>
<a-entity maze-generator></a-entity>
<a-entity environment="preset: forest; groundColor: #445; grid: cross"></a-entity>
</a-scene>
</body>
</html>
使用変数
<!DOCTYPE html>
<html>
<head>
<title>3D Maze in VR (A-Frame)</title>
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-environment-component@1.3.1/dist/aframe-environment-component.min.js"></script>
<script>
// カスタムコンポーネント: キー{ード入力での移動
AFRAME.registerComponent('movement-controls', {
schema: {
speed: { type: 'number', default: 0.2 }
},
init: function () {
this.direction = new THREE.Vector3();
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
window.addEventListener('keydown', this.onKeyDown);
window.addEventListener('keyup', this.onKeyUp);
this.forward = false;
this.backward = false;
this.left = false;
this.right = false;
},
tick: function (time, timeDelta) {
const speed = this.data.speed;
const rotation = this.el.getAttribute('rotation');
const direction = this.direction;
direction.set(0, 0, 0); // 毎回リセット
if (this.forward) direction.z -= 1;
if (this.backward) direction.z += 1;
if (this.left) direction.x -= 1;
if (this.right) direction.x += 1;
direction.normalize(); // ベクトルの長さを1に
if (direction.length() > 0) {
// カメラの向きを考慮して移動
const cameraRotation = this.el.getAttribute('rotation');
// Y軸周りの回転(Yaw)だけを適用するクォータニオンを作成
const yawQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, THREE.MathUtils.degToRad(cameraRotation.y), 0, 'YXZ'));
direction.applyQuaternion(yawQuaternion);
// 移動量を計算
const deltaPosition = direction.multiplyScalar(speed * timeDelta / 1000);
// 現在の位置に加算
const currentPosition = this.el.getAttribute('position');
const newPosition = {
x: currentPosition.x + deltaPosition.x,
y: currentPosition.y, // 高さは変えない
z: currentPosition.z + deltaPosition.z
};
this.el.setAttribute('position', newPosition);
}
},
onKeyDown: function (event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
this.forward = true;
break;
case 'ArrowDown':
case 'KeyS':
this.backward = true;
break;
case 'ArrowLeft':
case 'KeyA':
this.left = true;
break;
case 'ArrowRight':
case 'KeyD':
this.right = true;
break;
}
},
onKeyUp: function (event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
this.forward = false;
break;
case 'ArrowDown':
case 'KeyS':
this.backward = false;
break;
case 'ArrowLeft':
case 'KeyA':
this.left = false;
break;
case 'ArrowRight':
case 'KeyD':
this.right = false;
break;
}
},
remove: function () {
window.removeEventListener('keydown', this.onKeyDown);
window.removeEventListener('keyup', this.onKeyUp);
}
});
// 迷路生成 (簡単な例)
AFRAME.registerComponent('maze-generator', {
init: function () {
const mazeData = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 0, 1, 1, 0, 1, 1, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 1, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
const wallSize = 2; // 壁のサイズ
const wallHeight = 3; //壁の高さ
for (let row = 0; row < mazeData.length; row++) {
for (let col = 0; col < mazeData[row].length; col++) {
if (mazeData[row][col] === 1) {
const wall = document.createElement('a-box');
wall.setAttribute('width', wallSize);
wall.setAttribute('height', wallHeight); // 高さを設定
wall.setAttribute('depth', wallSize);
wall.setAttribute('position', {
x: col * wallSize,
y: wallHeight / 2, // 高さを半分にして地面に合わせる
z: row * wallSize
});
wall.setAttribute('material', 'color', 'gray'); // 壁の色
this.el.appendChild(wall);
}
}
}
// 天井 (屋根)
const ceiling = document.createElement('a-plane');
ceiling.setAttribute('width', wallSize * mazeData[0].length);
ceiling.setAttribute('height', wallSize * mazeData.length);
ceiling.setAttribute('position', { x: (wallSize * mazeData[0].length) / 2 - wallSize/2, y: wallHeight, z: (wallSize * mazeData.length) / 2 - wallSize/2 }); //中央に
ceiling.setAttribute('rotation', '-90 0 0');
ceiling.setAttribute('material', 'color', '#87CEEB'); // 空のような色
ceiling.setAttribute('side', 'double'); //両面
this.el.appendChild(ceiling);
// 床
const floor = document.createElement('a-plane');
floor.setAttribute('width', wallSize * mazeData[0].length);
floor.setAttribute('height', wallSize * mazeData.length);
floor.setAttribute('position', {x: (wallSize * mazeData[0].length) / 2 - wallSize/2, y: 0, z: (wallSize * mazeData.length) / 2- wallSize/2 });
floor.setAttribute('rotation', '-90 0 0');
floor.setAttribute('material', 'color', '#228B22'); // 草のような色
floor.setAttribute('side', 'double');
this.el.appendChild(floor);
}
});
</script>
</head>
<body>
<a-scene>
<a-entity id="rig" movement-controls>
<a-entity camera look-controls wasd-controls="enabled:false" position="0 1.6 0"></a-entity>
</a-entity>
<a-entity maze-generator></a-entity>
<a-entity environment="preset: forest; groundColor: #445; grid: cross"></a-entity>
</a-scene>
</body>
</html>
使用変数
-------( Function ) | |
backward | |
cameraRotation | |
ceiling | |
col | |
controls | |
currentPosition | |
deltaPosition | |
direction | |
environment | |
floor | |
forward | |
id | |
left | |
mazeData | |
newPosition | |
onKeyDown | |
onKeyUp | |
position | |
right | |
rotation | |
row | |
speed | |
src | |
wall | |
wallHeight | |
wallSize | |
yawQuaternion |