Happy-Valley-Game / index.html
awacke1's picture
Update index.html
1d3b6f2 verified
raw
history blame
12.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shared 3D World Builder</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
#info { position: absolute; top: 10px; left: 10px; color: white; background: rgba(0,0,0,0.7); padding: 10px; }
</style>
</head>
<body>
<div id="info">Click to place selected object, right-click to delete.</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script>
let scene, camera, renderer, websocket, selectedObjectType = 'None', worldObjects = {};
const username = window.USERNAME || 'Anonymous';
const websocketUrl = window.WEBSOCKET_URL || 'ws://localhost:8765';
const plotWidth = window.PLOT_WIDTH || 50.0;
const plotDepth = window.PLOT_DEPTH || 50.0;
function init() {
scene = new THREE.Scene();
// Orthographic camera for top-down view
const aspect = window.innerWidth / window.innerHeight;
const viewSize = plotWidth / 2; // Half of plotWidth for centered view
camera = new THREE.OrthographicCamera(
-viewSize * aspect, viewSize * aspect,
viewSize, -viewSize,
0.1, 1000
);
camera.position.set(0, 20, 0);
camera.lookAt(0, 0, 0);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Add lighting
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(0, 10, 0);
scene.add(directionalLight);
const groundGeometry = new THREE.PlaneGeometry(plotWidth, plotDepth);
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x888888 });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.name = 'ground';
scene.add(ground);
const gridHelper = new THREE.GridHelper(plotWidth, plotWidth / 2);
scene.add(gridHelper);
document.addEventListener('mousedown', onMouseDown);
window.addEventListener('resize', () => {
const aspect = window.innerWidth / window.innerHeight;
camera.left = -viewSize * aspect;
camera.right = viewSize * aspect;
camera.top = viewSize;
camera.bottom = -viewSize;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
initWebSocket();
animate();
}
function initWebSocket() {
websocket = new WebSocket(websocketUrl);
websocket.onopen = () => console.log('WebSocket connected');
websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received message:', data);
if (data.type === 'initial_state' || data.type === 'object_placed') {
const objects = data.type === 'initial_state' ? data.payload : { [data.payload.object_data.obj_id]: data.payload.object_data };
for (let obj_id in objects) {
if (!worldObjects[obj_id]) {
worldObjects[obj_id] = objects[obj_id];
addObjectToScene(objects[obj_id]);
}
}
} else if (data.type === 'object_deleted') {
if (worldObjects[data.payload.obj_id]) {
removeObjectFromScene(data.payload.obj_id);
delete worldObjects[data.payload.obj_id];
}
} else if (data.type === 'user_join' || data.type === 'user_leave' || data.type === 'user_rename') {
console.log(`${data.payload.username} ${data.type === 'user_join' ? 'joined' : data.type === 'user_leave' ? 'left' : 'renamed to ' + data.payload.new_username}`);
}
};
websocket.onclose = () => console.log('WebSocket closed');
websocket.onerror = (error) => console.error('WebSocket error:', error);
}
function updateSelectedObjectType(newType) {
selectedObjectType = newType;
console.log(`Tool changed to: ${newType}`);
}
function addObjectToScene(objData) {
console.log('Adding object:', objData);
let geometry, material, mesh;
switch (objData.type) {
case 'Cube':
geometry = new THREE.BoxGeometry(1, 1, 1);
material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
break;
case 'Sphere':
geometry = new THREE.SphereGeometry(0.5, 32, 32);
material = new THREE.MeshLambertMaterial({ color: 0xff0000 });
break;
case 'Cylinder':
geometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);
material = new THREE.MeshLambertMaterial({ color: 0x0000ff });
break;
case 'Cone':
geometry = new THREE.ConeGeometry(0.5, 1, 32);
material = new THREE.MeshLambertMaterial({ color: 0xffff00 });
break;
case 'Torus':
geometry = new THREE.TorusGeometry(0.4, 0.1, 16, 100);
material = new THREE.MeshLambertMaterial({ color: 0xff00ff });
break;
case 'Tree':
geometry = new THREE.ConeGeometry(0.5, 2, 8);
material = new THREE.MeshLambertMaterial({ color: 0x228B22 });
break;
case 'Rock':
geometry = new THREE.DodecahedronGeometry(0.5);
material = new THREE.MeshLambertMaterial({ color: 0x808080 });
break;
case 'Simple House':
geometry = new THREE.BoxGeometry(2, 1.5, 2);
material = new THREE.MeshLambertMaterial({ color: 0xA52A2A });
break;
case 'Pine Tree':
geometry = new THREE.ConeGeometry(0.3, 1.5, 8);
material = new THREE.MeshLambertMaterial({ color: 0x006400 });
break;
case 'Brick Wall':
geometry = new THREE.BoxGeometry(3, 1, 0.2);
material = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
break;
case 'Mushroom':
geometry = new THREE.SphereGeometry(0.5, 32, 16, 0, Math.PI);
material = new THREE.MeshLambertMaterial({ color: 0xF5F5DC });
break;
case 'Cactus':
geometry = new THREE.CylinderGeometry(0.2, 0.2, 1.5, 8);
material = new THREE.MeshLambertMaterial({ color: 0x2E8B57 });
break;
case 'Campfire':
geometry = new THREE.DodecahedronGeometry(0.3);
material = new THREE.MeshLambertMaterial({ color: 0xFF4500 });
break;
case 'Star':
geometry = new THREE.SphereGeometry(0.3, 4, 2);
material = new THREE.MeshLambertMaterial({ color: 0xFFFF00 });
break;
case 'Gem':
geometry = new THREE.OctahedronGeometry(0.4);
material = new THREE.MeshLambertMaterial({ color: 0x00CED1 });
break;
case 'Tower':
geometry = new THREE.CylinderGeometry(0.3, 0.5, 2, 8);
material = new THREE.MeshLambertMaterial({ color: 0x708090 });
break;
case 'Barrier':
geometry = new THREE.BoxGeometry(2, 0.5, 0.2);
material = new THREE.MeshLambertMaterial({ color: 0x696969 });
break;
case 'Fountain':
geometry = new THREE.CylinderGeometry(0.5, 0.3, 1, 32);
material = new THREE.MeshLambertMaterial({ color: 0x4682B4 });
break;
case 'Lantern':
geometry = new THREE.BoxGeometry(0.3, 0.5, 0.3);
material = new THREE.MeshLambertMaterial({ color: 0xFFD700 });
break;
case 'Sign Post':
geometry = new THREE.BoxGeometry(0.2, 1, 0.2);
material = new THREE.MeshLambertMaterial({ color: 0x8B4513 });
break;
default:
console.warn('Unknown object type:', objData.type);
return;
}
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(objData.position.x, objData.position.y, objData.position.z);
mesh.rotation.set(objData.rotation.x, objData.rotation.y, objData.rotation.z);
mesh.name = objData.obj_id;
scene.add(mesh);
console.log('Object added to scene:', objData.obj_id);
}
function removeObjectFromScene(obj_id) {
const object = scene.getObjectByName(obj_id);
if (object) {
scene.remove(object);
console.log('Object removed:', obj_id);
}
}
function onMouseDown(event) {
event.preventDefault();
const mouse = new THREE.Vector2(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (event.button === 0 && selectedObjectType !== 'None') {
for (let intersect of intersects) {
if (intersect.object.name === 'ground') {
const position = intersect.point;
position.y = 0.5; // Place above ground
console.log('Placing object at:', position);
placeObject(position);
break;
}
}
} else if (event.button === 2) {
for (let intersect of intersects) {
if (intersect.object.name && intersect.object.name !== 'ground') {
console.log('Deleting object:', intersect.object.name);
websocket.send(JSON.stringify({
type: 'delete_object',
payload: { obj_id: intersect.object.name, username }
}));
break;
}
}
}
}
function placeObject(position) {
if (selectedObjectType === 'None') return;
const obj_id = `obj_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
const objectData = {
obj_id: obj_id,
type: selectedObjectType,
position: { x: position.x, y: position.y, z: position.z },
rotation: { x: 0, y: 0, z: 0 }
};
console.log('Sending place_object:', objectData);
websocket.send(JSON.stringify({
type: 'place_object',
payload: { username, object_data: objectData }
}));
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
init();
</script>
</body>
</html>