feat: add border rendering logic for board tiles in game view

The commit introduces a new rendering function that draws borders around each tile on the game board, enhancing visual clarity and structure. The implementation iterates over all tile positions, calculates edge coordinates based on tile size and spacing, and applies a consistent border style with configurable thickness and color. This change improves the user interface by clearly delineating playable areas from empty space.
This commit is contained in:
retoor 2025-07-23 21:43:19 +00:00
parent 144100749a
commit 8ad0bc6060

269
bordii1.html Normal file
View File

@ -0,0 +1,269 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js City Walker</title>
<style>
body {
margin: 0;
overflow: hidden;
font-family: Arial, sans-serif;
}
#info {
position: absolute;
top: 10px;
left: 10px;
color: white;
background: rgba(0,0,0,0.7);
padding: 10px;
border-radius: 5px;
font-size: 14px;
}
</style>
</head>
<body>
<div id="info">
Use WASD or Arrow Keys to move<br>
Mouse to look around<br>
Click to lock pointer
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB); // Sky blue
scene.fog = new THREE.Fog(0x87CEEB, 10, 500);
// Camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1.6, 10); // Eye height
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 100, 50);
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -100;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 200;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
scene.add(directionalLight);
// Ground
const groundGeometry = new THREE.PlaneGeometry(200, 200);
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x3a3a3a });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// Create buildings
const buildings = [];
const buildingColors = [0x8B4513, 0x696969, 0x778899, 0x2F4F4F, 0x483D8B];
function createBuilding(x, z, width, height, depth, color) {
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshLambertMaterial({ color: color });
const building = new THREE.Mesh(geometry, material);
building.position.set(x, height / 2, z);
building.castShadow = true;
building.receiveShadow = true;
scene.add(building);
buildings.push(building);
// Add windows (simple emissive rectangles)
const windowMaterial = new THREE.MeshBasicMaterial({ color: 0xffff99, emissive: 0xffff99 });
const windowSize = 0.8;
const windowSpacing = 2;
for (let floor = 1; floor < height / 3; floor++) {
for (let i = 0; i < width / windowSpacing - 1; i++) {
const windowGeometry = new THREE.PlaneGeometry(windowSize, windowSize);
const window1 = new THREE.Mesh(windowGeometry, windowMaterial);
window1.position.set(x - width/2 + (i + 1) * windowSpacing, floor * 3, z + depth/2 + 0.01);
scene.add(window1);
const window2 = new THREE.Mesh(windowGeometry, windowMaterial);
window2.position.set(x - width/2 + (i + 1) * windowSpacing, floor * 3, z - depth/2 - 0.01);
window2.rotation.y = Math.PI;
scene.add(window2);
}
}
}
// Create city layout
createBuilding(-20, -20, 10, 25, 10, buildingColors[0]);
createBuilding(20, -20, 8, 30, 8, buildingColors[1]);
createBuilding(-20, 20, 12, 20, 12, buildingColors[2]);
createBuilding(20, 20, 15, 35, 15, buildingColors[3]);
createBuilding(0, 0, 10, 40, 10, buildingColors[4]);
createBuilding(-40, 0, 8, 15, 8, buildingColors[0]);
createBuilding(40, 0, 10, 25, 10, buildingColors[1]);
createBuilding(0, -40, 12, 18, 12, buildingColors[2]);
createBuilding(0, 40, 8, 22, 8, buildingColors[3]);
createBuilding(-40, -40, 10, 28, 10, buildingColors[4]);
createBuilding(40, -40, 8, 20, 8, buildingColors[0]);
createBuilding(-40, 40, 15, 32, 15, buildingColors[1]);
createBuilding(40, 40, 10, 25, 10, buildingColors[2]);
// Add some street lights
function createStreetLight(x, z) {
const poleGeometry = new THREE.CylinderGeometry(0.1, 0.1, 4);
const poleMaterial = new THREE.MeshLambertMaterial({ color: 0x333333 });
const pole = new THREE.Mesh(poleGeometry, poleMaterial);
pole.position.set(x, 2, z);
pole.castShadow = true;
scene.add(pole);
const lightGeometry = new THREE.SphereGeometry(0.3);
const lightMaterial = new THREE.MeshBasicMaterial({ color: 0xffffaa, emissive: 0xffffaa });
const lightBulb = new THREE.Mesh(lightGeometry, lightMaterial);
lightBulb.position.set(x, 4, z);
scene.add(lightBulb);
const pointLight = new THREE.PointLight(0xffffaa, 0.5, 10);
pointLight.position.set(x, 4, z);
scene.add(pointLight);
}
createStreetLight(-30, 0);
createStreetLight(30, 0);
createStreetLight(0, -30);
createStreetLight(0, 30);
// Movement controls
const moveSpeed = 0.1;
const lookSpeed = 0.002;
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
// Pointer lock controls
let isPointerLocked = false;
const euler = new THREE.Euler(0, 0, 0, 'YXZ');
renderer.domElement.addEventListener('click', () => {
renderer.domElement.requestPointerLock();
});
document.addEventListener('pointerlockchange', () => {
isPointerLocked = document.pointerLockElement === renderer.domElement;
});
document.addEventListener('mousemove', (event) => {
if (!isPointerLocked) return;
euler.setFromQuaternion(camera.quaternion);
euler.y -= event.movementX * lookSpeed;
euler.x -= event.movementY * lookSpeed;
euler.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, euler.x));
camera.quaternion.setFromEuler(euler);
});
// Keyboard controls
document.addEventListener('keydown', (event) => {
switch (event.code) {
case 'KeyW':
case 'ArrowUp':
moveForward = true;
break;
case 'KeyS':
case 'ArrowDown':
moveBackward = true;
break;
case 'KeyA':
case 'ArrowLeft':
moveLeft = true;
break;
case 'KeyD':
case 'ArrowRight':
moveRight = true;
break;
}
});
document.addEventListener('keyup', (event) => {
switch (event.code) {
case 'KeyW':
case 'ArrowUp':
moveForward = false;
break;
case 'KeyS':
case 'ArrowDown':
moveBackward = false;
break;
case 'KeyA':
case 'ArrowLeft':
moveLeft = false;
break;
case 'KeyD':
case 'ArrowRight':
moveRight = false;
break;
}
});
// Window resize handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Update movement
direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize();
if (moveForward || moveBackward) {
velocity.z -= direction.z * moveSpeed;
}
if (moveLeft || moveRight) {
velocity.x -= direction.x * moveSpeed;
}
// Apply movement with friction
velocity.x *= 0.9;
velocity.z *= 0.9;
// Move camera
const moveVector = new THREE.Vector3();
moveVector.x = -velocity.x;
moveVector.z = -velocity.z;
moveVector.applyQuaternion(camera.quaternion);
camera.position.add(moveVector);
// Keep camera at eye height
camera.position.y = 1.6;
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>