3D魔方游戏

# 3D魔方游戏

这是一个基于Three.js的3D魔方游戏,支持2到6阶魔方的模拟操作。

## 功能特点

- 支持2到6阶魔方

- 真实的3D渲染效果

- 鼠标操作控制

- 随机打乱功能

- 提示功能

- 重置功能

### 安装依赖

```bash

npm install

```

### 启动游戏

```bash

npm start

```

然后在浏览器中访问 `http://localhost:8080` 即可开始游戏。

## 操作说明

- 使用鼠标拖拽可以旋转整个魔方

- 按住Shift键并点击魔方的某一面可以旋转该面

- 使用界面上的下拉菜单可以选择魔方的阶数(2到6阶)

- 点击"随机打乱"按钮可以随机打乱魔方

- 点击"提示"按钮可以获取下一步的提示

- 点击"重置"按钮可以重置魔方到初始状态

## 技术栈

- HTML5

- CSS3

- JavaScript (ES6+)

- Three.js (3D渲染库)

## 浏览器兼容性

支持所有现代浏览器,包括:

- Chrome

- Firefox

- Safari

- Edge

## 许可证

ISC

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import TWEEN from 'three/examples/jsm/libs/tween.module.js';
import { Cube } from './cube.js';// 全局变量
let scene, camera, renderer, controls;
let cube;
let currentOrder = 3; // 默认3阶魔方
let isCtrlPressed = false; // 跟踪Ctrl键是否按下
let isDragging = false; // 跟踪是否正在拖拽// 初始化场景
function init() {// 创建场景scene = new THREE.Scene();scene.background = new THREE.Color(0xf0f0f0);// 获取容器const container = document.getElementById('cube-container');const containerWidth = container.clientWidth;const containerHeight = container.clientHeight || window.innerHeight * 0.6;// 强制设置容器高度container.style.height = `${containerHeight}px`;// 创建相机camera = new THREE.PerspectiveCamera(50, // 视角更广containerWidth / containerHeight,0.1,1000);// 根据魔方阶数调整相机位置const cameraDistance = 8 + currentOrder * 1.0; // 增加距离camera.position.set(cameraDistance, cameraDistance, cameraDistance);camera.lookAt(0, 0, 0);// 创建渲染器renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(containerWidth, containerHeight);renderer.setPixelRatio(window.devicePixelRatio);container.appendChild(renderer.domElement);// 添加轨道控制controls = new OrbitControls(camera, renderer.domElement);controls.enableDamping = true;controls.dampingFactor = 0.1;controls.minDistance = cameraDistance * 0.5;controls.maxDistance = cameraDistance * 2;controls.enableRotate = true;controls.rotateSpeed = 1.0;// 允许完全旋转controls.minPolarAngle = 0;controls.maxPolarAngle = Math.PI;controls.minAzimuthAngle = -Infinity;controls.maxAzimuthAngle = Infinity;controls.target.set(0, 0, 0);// 默认禁用轨道控制controls.enabled = false;// 添加光源const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);directionalLight.position.set(10, 20, 15);scene.add(directionalLight);// 添加第二个方向光源,照亮底部const bottomLight = new THREE.DirectionalLight(0xffffff, 0.6);bottomLight.position.set(-5, -10, -7);scene.add(bottomLight);// 添加第三个方向光源,照亮侧面const sideLight = new THREE.DirectionalLight(0xffffff, 0.6);sideLight.position.set(-10, 5, -10);scene.add(sideLight);// 创建魔方createCube(currentOrder);// 添加窗口大小调整监听window.addEventListener('resize', onWindowResize);// 添加交互控制setupInteraction();
}// 窗口大小调整
function onWindowResize() {const container = document.getElementById('cube-container');const containerWidth = container.clientWidth;const containerHeight = container.clientHeight || window.innerHeight * 0.6;camera.aspect = containerWidth / containerHeight;camera.updateProjectionMatrix();renderer.setSize(containerWidth, containerHeight);
}// 动画循环
function animate() {requestAnimationFrame(animate);TWEEN.update(); // 更新动画controls.update();renderer.render(scene, camera);
}// 创建魔方
function createCube(order) {if (cube) {scene.remove(cube.group);}cube = new Cube(order);scene.add(cube.group);
}// 设置交互控制
function setupInteraction() {const raycaster = new THREE.Raycaster();const mouse = new THREE.Vector2();let selectedFace = null;let startPoint = new THREE.Vector2();let endPoint = new THREE.Vector2();// 监听Ctrl键window.addEventListener('keydown', function(event) {if (event.key === 'Control') {isCtrlPressed = true;controls.enabled = true;renderer.domElement.style.cursor = 'move';console.log('Ctrl键按下,启用轨道控制');}});window.addEventListener('keyup', function(event) {if (event.key === 'Control') {isCtrlPressed = false;controls.enabled = false;renderer.domElement.style.cursor = 'default';console.log('Ctrl键释放,禁用轨道控制');}});// 添加初始提示const infoDiv = document.createElement('div');infoDiv.style.position = 'absolute';infoDiv.style.bottom = '10px';infoDiv.style.left = '10px';infoDiv.style.backgroundColor = 'rgba(0,0,0,0.7)';infoDiv.style.color = 'white';infoDiv.style.padding = '5px 10px';infoDiv.style.borderRadius = '5px';infoDiv.style.fontSize = '14px';infoDiv.innerHTML = '按住Ctrl键可自由旋转整个魔方<br>点击或拖动魔方面可旋转该面';document.getElementById('cube-container').appendChild(infoDiv);// 10秒后隐藏提示setTimeout(() => {infoDiv.style.opacity = '0';infoDiv.style.transition = 'opacity 1s';setTimeout(() => {infoDiv.remove();}, 1000);}, 10000);// 初始自动旋转魔方,让用户看到所有面setTimeout(() => {// 先旋转到一个角度,让用户看到更多面const startRotation = { x: 0, y: 0 };const endRotation = { x: Math.PI / 3, y: Math.PI / 4 };new TWEEN.Tween(startRotation).to(endRotation, 1500).easing(TWEEN.Easing.Quadratic.Out).onUpdate(() => {cube.group.rotation.x = startRotation.x;cube.group.rotation.y = startRotation.y;}).onComplete(() => {// 旋转完成后,重置魔方位置setTimeout(() => {new TWEEN.Tween(cube.group.rotation).to({ x: 0, y: 0, z: 0 }, 1000).easing(TWEEN.Easing.Quadratic.Out).start();}, 1000);}).start();}, 500);// 鼠标按下事件renderer.domElement.addEventListener('mousedown', function(event) {// 如果按下Ctrl键,启用轨道控制并跳过魔方面旋转if (event.ctrlKey || isCtrlPressed) {controls.enabled = true;return;}// 禁用轨道控制controls.enabled = false;// 如果魔方正在动画中,则不处理if (cube && cube.isAnimating) return;isDragging = false;const rect = renderer.domElement.getBoundingClientRect();mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;// 保存起始点startPoint.set(event.clientX, event.clientY);endPoint.copy(startPoint); // 初始化终点与起点相同raycaster.setFromCamera(mouse, camera);try {const allCubies = cube.getAllCubies();if (!allCubies || allCubies.length === 0) {console.warn('没有找到魔方小块');return;}// 递归设置为true,以检测子对象const intersects = raycaster.intersectObjects(allCubies, true);if (intersects.length > 0) {// 确保我们有正确的对象和面let targetObject = intersects[0].object;// 如果点击的是边缘线段,获取其父对象(实际的方块)while (targetObject.parent && !(targetObject instanceof THREE.Mesh)) {targetObject = targetObject.parent;}// 创建一个新的交点对象,确保有正确的目标对象和面信息const correctedIntersect = {...intersects[0],object: targetObject};// 尝试获取面信息selectedFace = cube.getFaceFromIntersect(correctedIntersect);if (selectedFace) {console.log('选中面:', selectedFace);renderer.domElement.style.cursor = 'pointer';} else {console.log('未能确定选中的面');// 尝试直接从物体位置确定面const position = targetObject.position.clone();const x = Math.round((position.x + cube.offset) / (cube.cubeSize + cube.gap));const y = Math.round((position.y + cube.offset) / (cube.cubeSize + cube.gap));const z = Math.round((position.z + cube.offset) / (cube.cubeSize + cube.gap));// 确定是哪个面if (x === 0) {selectedFace = { axis: 'x', value: -1, layer: 0 };} else if (x === cube.order - 1) {selectedFace = { axis: 'x', value: 1, layer: cube.order - 1 };} else if (y === 0) {selectedFace = { axis: 'y', value: -1, layer: 0 };} else if (y === cube.order - 1) {selectedFace = { axis: 'y', value: 1, layer: cube.order - 1 };} else if (z === 0) {selectedFace = { axis: 'z', value: -1, layer: 0 };} else if (z === cube.order - 1) {selectedFace = { axis: 'z', value: 1, layer: cube.order - 1 };}if (selectedFace) {console.log('从位置推断的面:', selectedFace);renderer.domElement.style.cursor = 'pointer';}}} else {console.log('未选中任何面');selectedFace = null;}} catch (error) {console.error('射线检测错误:', error);}});// 鼠标移动事件window.addEventListener('mousemove', function(event) {// 如果按下Ctrl键,让轨道控制处理移动if (event.ctrlKey || isCtrlPressed) {controls.enabled = true;return;}// 如果没有选中面,则不处理if (!selectedFace) return;// 更新终点位置endPoint.set(event.clientX, event.clientY);// 计算移动距离const moveDistance = Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2));// 如果移动距离超过阈值,标记为拖拽if (moveDistance > 8) { // 增加阈值,减少误触isDragging = true;renderer.domElement.style.cursor = 'grabbing';// 计算拖拽方向向量const dragVector = {x: endPoint.x - startPoint.x,y: endPoint.y - startPoint.y};// 显示拖拽方向指示const direction = determineRotationDirection(selectedFace, dragVector);console.log('当前拖拽方向:', direction > 0 ? '顺时针' : '逆时针');}});// 鼠标释放事件window.addEventListener('mouseup', function(event) {renderer.domElement.style.cursor = 'default';// 如果按下Ctrl键,让轨道控制处理释放if (event.ctrlKey || isCtrlPressed) {return;}// 如果没有选中面,则不处理if (!selectedFace) return;// 更新终点位置endPoint.set(event.clientX, event.clientY);// 如果是拖拽操作,根据拖拽方向确定旋转方向if (isDragging) {// 计算拖拽方向向量const dragVector = {x: endPoint.x - startPoint.x,y: endPoint.y - startPoint.y};// 计算移动距离const moveDistance = Math.sqrt(Math.pow(dragVector.x, 2) + Math.pow(dragVector.y, 2));// 只有当移动距离足够大时才执行旋转,防止误触if (moveDistance > 15) {// 根据拖拽方向和选中的面确定旋转方向const direction = determineRotationDirection(selectedFace, dragVector);console.log('旋转方向:', direction);// 执行旋转if (cube && typeof cube.rotateFace === 'function') {cube.rotateFace({axis: selectedFace.axis,layer: selectedFace.layer,direction: direction});} else {console.error('cube.rotateFace 不是一个函数');}} else {console.log('移动距离不足,取消旋转');}} // 如果是点击操作,使用默认方向旋转else {console.log('点击操作');if (cube && typeof cube.rotateFace === 'function') {cube.rotateFace(selectedFace);} else {console.error('cube.rotateFace 不是一个函数');}}selectedFace = null;isDragging = false;});// 添加触摸支持renderer.domElement.addEventListener('touchstart', function(event) {if (cube && cube.isAnimating) return;event.preventDefault();isDragging = false;const rect = renderer.domElement.getBoundingClientRect();const touch = event.touches[0];mouse.x = ((touch.clientX - rect.left) / rect.width) * 2 - 1;mouse.y = -((touch.clientY - rect.top) / rect.height) * 2 + 1;// 保存起始点startPoint.set(touch.clientX, touch.clientY);endPoint.copy(startPoint);raycaster.setFromCamera(mouse, camera);try {const allCubies = cube.getAllCubies();if (!allCubies || allCubies.length === 0) return;const intersects = raycaster.intersectObjects(allCubies, true);if (intersects.length > 0) {// 确保我们有正确的对象和面let targetObject = intersects[0].object;// 如果点击的是边缘线段,获取其父对象(实际的方块)while (targetObject.parent && !(targetObject instanceof THREE.Mesh)) {targetObject = targetObject.parent;}// 创建一个新的交点对象const correctedIntersect = {...intersects[0],object: targetObject};selectedFace = cube.getFaceFromIntersect(correctedIntersect);console.log('触摸选中面:', selectedFace);}} catch (error) {console.error('触摸检测错误:', error);}});renderer.domElement.addEventListener('touchmove', function(event) {if (!selectedFace) return;event.preventDefault();const touch = event.touches[0];// 更新终点位置endPoint.set(touch.clientX, touch.clientY);// 计算移动距离const moveDistance = Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2));// 如果移动距离超过阈值,标记为拖拽if (moveDistance > 10) {isDragging = true;}});renderer.domElement.addEventListener('touchend', function(event) {if (!selectedFace) return;event.preventDefault();if (isDragging) {// 计算拖拽方向向量const dragVector = {x: endPoint.x - startPoint.x,y: endPoint.y - startPoint.y};// 根据拖拽方向和选中的面确定旋转方向const direction = determineRotationDirection(selectedFace, dragVector);// 执行旋转if (cube && typeof cube.rotateFace === 'function') {cube.rotateFace({axis: selectedFace.axis,layer: selectedFace.layer,direction: direction});}} else {if (cube && typeof cube.rotateFace === 'function') {cube.rotateFace(selectedFace);}}selectedFace = null;isDragging = false;});// 阻止右键菜单renderer.domElement.addEventListener('contextmenu', function(event) {event.preventDefault();});
}// 根据拖拽方向和选中的面确定旋转方向
function determineRotationDirection(face, dragVector) {const { axis, value } = face;// 计算拖拽的主要方向和角度const dragAngle = Math.atan2(dragVector.y, dragVector.x) * 180 / Math.PI;console.log('拖拽角度:', dragAngle);// 根据角度确定拖拽的主要方向let dragDirection;if (dragAngle > -45 && dragAngle <= 45) {dragDirection = 'right';} else if (dragAngle > 45 && dragAngle <= 135) {dragDirection = 'down';} else if (dragAngle > 135 || dragAngle <= -135) {dragDirection = 'left';} else {dragDirection = 'up';}console.log('拖拽方向:', dragDirection);// 根据面的轴和拖拽方向确定旋转方向// 1表示顺时针,-1表示逆时针let direction = 1;switch (axis) {case 'x': // 左右面if (value > 0) { // 右面direction = (dragDirection === 'up' || dragDirection === 'right') ? 1 : -1;} else { // 左面direction = (dragDirection === 'up' || dragDirection === 'left') ? 1 : -1;}break;case 'y': // 上下面if (value > 0) { // 上面direction = (dragDirection === 'right' || dragDirection === 'down') ? 1 : -1;} else { // 下面direction = (dragDirection === 'right' || dragDirection === 'up') ? 1 : -1;}break;case 'z': // 前后面if (value > 0) { // 前面direction = (dragDirection === 'right' || dragDirection === 'up') ? 1 : -1;} else { // 后面direction = (dragDirection === 'left' || dragDirection === 'up') ? 1 : -1;}break;}console.log('旋转方向:', direction > 0 ? '顺时针' : '逆时针');return direction;
}// 初始化事件监听
function initEventListeners() {// 魔方阶数选择document.getElementById('cube-order').addEventListener('change', (event) => {currentOrder = parseInt(event.target.value);createCube(currentOrder);updateLayerButtons(); // 更新层按钮});// 随机打乱按钮document.getElementById('scramble-btn').addEventListener('click', () => {cube.scramble();});// 提示按钮document.getElementById('hint-btn').addEventListener('click', () => {cube.showHint();});// 重置按钮document.getElementById('reset-btn').addEventListener('click', () => {createCube(currentOrder);updateLayerButtons(); // 更新层按钮});// 旋转控制按钮document.getElementById('rotate-left').addEventListener('click', () => {rotateCubeWithAnimation({ axis: 'y', angle: Math.PI / 4 });});document.getElementById('rotate-right').addEventListener('click', () => {rotateCubeWithAnimation({ axis: 'y', angle: -Math.PI / 4 });});document.getElementById('rotate-up').addEventListener('click', () => {rotateCubeWithAnimation({ axis: 'x', angle: Math.PI / 4 });});document.getElementById('rotate-down').addEventListener('click', () => {rotateCubeWithAnimation({ axis: 'x', angle: -Math.PI / 4 });});document.getElementById('rotate-clockwise').addEventListener('click', () => {rotateCubeWithAnimation({ axis: 'z', angle: -Math.PI / 4 });});document.getElementById('rotate-counter-clockwise').addEventListener('click', () => {rotateCubeWithAnimation({ axis: 'z', angle: Math.PI / 4 });});// 层选择控制// 初始化层按钮updateLayerButtons();// 轴选择变化时更新层按钮document.getElementById('axis-select').addEventListener('change', updateLayerButtons);// 层旋转方向按钮document.getElementById('rotate-clockwise-layer').addEventListener('click', () => {rotateSelectedLayer(1); // 顺时针});document.getElementById('rotate-counter-clockwise-layer').addEventListener('click', () => {rotateSelectedLayer(-1); // 逆时针});// 添加键盘控制window.addEventListener('keydown', function(event) {// 如果魔方正在动画中,则不处理if (cube && cube.isAnimating) return;// 如果按下Ctrl键,启用轨道控制if (event.key === 'Control') {isCtrlPressed = true;controls.enabled = true;renderer.domElement.style.cursor = 'move';console.log('Ctrl键按下,启用轨道控制');return;}// 键盘控制魔方旋转switch (event.key) {// 旋转整个魔方case 'ArrowLeft':if (event.shiftKey) {rotateCubeWithAnimation({ axis: 'y', angle: Math.PI / 4 });}break;case 'ArrowRight':if (event.shiftKey) {rotateCubeWithAnimation({ axis: 'y', angle: -Math.PI / 4 });}break;case 'ArrowUp':if (event.shiftKey) {rotateCubeWithAnimation({ axis: 'x', angle: Math.PI / 4 });}break;case 'ArrowDown':if (event.shiftKey) {rotateCubeWithAnimation({ axis: 'x', angle: -Math.PI / 4 });}break;// 旋转魔方的面 (按键1-9对应九宫格位置)case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':const keyNum = parseInt(event.key);let layer = 0;let axis = 'z';let direction = 1;// 根据按键确定旋转的面和方向if (keyNum <= 3) { // 上层layer = cube.order - 1;axis = 'y';direction = keyNum === 1 || keyNum === 3 ? -1 : 1;} else if (keyNum <= 6) { // 中层layer = Math.floor(cube.order / 2);axis = 'y';direction = keyNum === 4 || keyNum === 6 ? -1 : 1;} else { // 下层layer = 0;axis = 'y';direction = keyNum === 7 || keyNum === 9 ? -1 : 1;}// 执行旋转if (event.altKey) { // Alt键按下时旋转X轴axis = 'x';} else if (event.ctrlKey) { // Ctrl键按下时旋转Z轴axis = 'z';}cube.rotateFace({axis: axis,layer: layer,direction: direction});break;}});window.addEventListener('keyup', function(event) {if (event.key === 'Control') {isCtrlPressed = false;controls.enabled = false;renderer.domElement.style.cursor = 'default';console.log('Ctrl键释放,禁用轨道控制');}});// 添加操作说明const keyboardInfo = document.createElement('div');keyboardInfo.className = 'keyboard-info';keyboardInfo.innerHTML = `<h3>键盘控制说明:</h3><p>- Shift + 方向键: 旋转整个魔方</p><p>- 数字键1-9: 旋转对应位置的面</p><p>- Alt + 数字键: 沿X轴旋转</p><p>- Ctrl + 数字键: 沿Z轴旋转</p><p>- 默认沿Y轴旋转</p>`;document.querySelector('.instructions').appendChild(keyboardInfo);
}// 更新层按钮
function updateLayerButtons() {if (!cube) return;const layerButtonsContainer = document.getElementById('layer-buttons');layerButtonsContainer.innerHTML = ''; // 清空现有按钮const axis = document.getElementById('axis-select').value;// 为每一层创建按钮for (let i = 0; i < cube.order; i++) {const button = document.createElement('button');button.className = 'layer-button';button.textContent = i + 1;button.dataset.layer = i;button.dataset.axis = axis;// 点击选择层button.addEventListener('click', function() {// 移除其他按钮的选中状态document.querySelectorAll('.layer-button').forEach(btn => {btn.classList.remove('selected');});// 添加当前按钮的选中状态this.classList.add('selected');});layerButtonsContainer.appendChild(button);}// 默认选中第一个按钮if (layerButtonsContainer.firstChild) {layerButtonsContainer.firstChild.classList.add('selected');}
}// 旋转选中的层
function rotateSelectedLayer(direction) {const selectedButton = document.querySelector('.layer-button.selected');if (!selectedButton || !cube) return;const layer = parseInt(selectedButton.dataset.layer);const axis = selectedButton.dataset.axis;cube.rotateFace({axis: axis,layer: layer,direction: direction});
}// 旋转整个魔方的动画函数
function rotateCubeWithAnimation({ axis, angle }) {if (!cube || !cube.group) return;const startRotation = { value: 0 };const endRotation = { value: angle };new TWEEN.Tween(startRotation).to(endRotation, 300).easing(TWEEN.Easing.Quadratic.Out).onUpdate(() => {if (axis === 'x') {cube.group.rotation.x += (endRotation.value - startRotation.value) / 10;} else if (axis === 'y') {cube.group.rotation.y += (endRotation.value - startRotation.value) / 10;} else if (axis === 'z') {cube.group.rotation.z += (endRotation.value - startRotation.value) / 10;}}).start();
}// 页面加载完成后初始化
window.addEventListener('DOMContentLoaded', () => {init();initEventListeners();animate();
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87773.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

下载安装 com0com

下载 在 sourceforge 网站下载安装器&#xff1a;下载链接 安装完成后可以在设备管理器中看到默认创建的一对虚拟串口 使用串口调试助手收发 使用串口调试助手分别打开。如下图所示&#xff0c;在端口选择的下拉列表中可以看到刚才在设备管理器中看到的 COM3 和 COM5 分…

C++ 应用软件开发从入门到实战详解

目录 1、引言 2、IDE 开发环境介绍 2.1、Visual Studio 2.2、Qt Creator 3、 C语言特性 3.1、熟悉泛型编程 3.2、了解C/C异常处理 3.3、熟练使用STL容器 3.4、熟悉C11新特性 4、Windows 平台的编程技术与调试技能 4.1、需要掌握的若干编程技术和基础知识 4.2、需…

Python爬虫实战:研究slug相关技术

1. 引言 1.1 研究背景与意义 随着互联网技术的快速发展,网络上的信息量呈爆炸式增长。如何从海量的非结构化数据中提取有价值的信息,成为当前数据科学领域的重要研究方向。网络爬虫作为一种自动化数据采集工具,可以高效地获取网页内容,为数据分析提供丰富的数据来源。 Sl…

人工智能-基础篇-18-什么是RAG(检索增强生成:知识库+向量化技术+大语言模型LLM整合的技术框架)

RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;是一种结合外部知识检索与大语言模型&#xff08;LLM&#xff09;生成能力的技术框架&#xff0c;旨在提升生成式AI在问答、内容创作等任务中的准确性、实时性和领域适应性。 1、核心概念 …

CppCon 2018 学习:What do you mean “thread-safe“

什么是“线程安全”&#xff1f; “线程安全”指的是一个函数、方法或代码块能够在多个线程同时执行时&#xff0c;不会出现意外的交互或破坏共享数据&#xff0c;能够安全地运行。 POSIX 对线程安全的定义很清楚&#xff1a; “一个线程安全的函数可以在多个线程中被安全地并…

热方程初边值问题解法

已知公式&#xff1a; u ( x , t ) ∫ − ∞ ∞ G ( x , y , t ) g ( y ) d y . u(x,t)\int_{-\infty}^{\infty}G(x,y,t)g(y)dy. u(x,t)∫−∞∞​G(x,y,t)g(y)dy. &#xff08;1&#xff09; 其中 G ( x , y , t ) 1 2 k π t e − ( x − y ) 2 4 k t G(x,y,t)\frac{1}{2…

怎样理解:source ~/.bash_profile

场景复现 $ source ~/.bash_profileAnalysis 分析 一句话概括 source ~/.bash_profile “在 当前 终端会话里&#xff0c;立刻执行并加载 ~/.bash_profile 中的所有命令&#xff0c;让其中定义的环境变量、函数、alias 等即时生效&#xff0c;而无需重新登录或开新 Shell。…

搜索问答技术概述:基于知识图谱与MRC的创新应用

目录 一、问答系统应用分析 二、搜索问答技术与系统 &#xff08;一&#xff09;需求和信息分析 问答需求类型 多样的数据源 文本组织形态 &#xff08;二&#xff09;主要问答技术介绍 发展和成熟度分析 重点问答技术基础&#xff1a;KBQA和DeepQA KBQA&#xff08;…

TCP数据的发送和接收

本篇文章结合实验对 TCP 数据传输中的重传机制、滑动窗口以及拥塞控制做简要的分析学习。 重传 实验环境 这里使用两台腾讯云服务器&#xff1a;vm-1&#xff08;172.19.0.3&#xff09;和vm-2&#xff08;172.19.0.6&#xff09;。 超时重传 首先 vm-1 作为服务端启动 nc…

python 保存二维数组到本地

Python中保存二维数组有多种方法&#xff0c;以下是常用的几种方式&#xff1a;1. 使用NumPy&#xff08;推荐&#xff09;import numpy as np# 创建二维数组 arr np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])# 保存为.npy文件&#xff08;NumPy专用格式&#xff09; np.save…

LIN总线通讯中从节点波特率同步原理

波特率同步原理&#xff1a;从节点如何通过0x55校准时钟&#xff1f; 一、同步场的核心作用&#xff1a;统一“时间标尺” 在LIN总线中&#xff0c;主节点与从节点各自拥有独立的时钟源&#xff08;如MCU内部RC振荡器&#xff09;&#xff0c;但由于制造工艺差异&#xff0c;…

【Unity笔记02】订阅事件-自动开门

流程 当玩家移动到触发区域的时候&#xff0c;门自动打开 事件系统 using System; using System.Collections; using System.Collections.Generic; using UnityEngine;public class EventSystem : MonoBehaviour {public static EventSystem Instance { get; private set; }…

控制台字符动画

旋转的立方体 #include <cstdint> #include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <cstdlib> #include <ctime> #include <thread> using namespace std;float angleX .0f; float a…

基于 PyTorch 的猫狗图像分类实战

基于 PyTorch 的猫狗图像分类实战 项目背景简介 深度学习框架 PyTorch 因其动态计算图和灵活易用性&#xff0c;被广泛应用于图像分类等计算机视觉任务。在入门计算机视觉领域时&#xff0c;常常以手写数字识别&#xff08;MNIST&#xff09;作为 “Hello World”&#xff0c…

SwiftUI 7(iOS 26 / iPadOS 26)中玻璃化标签页的全新玩法

&#x1f378; Liquid Glass 登场&#xff1a;界面设计焕然一新 WWDC25 可谓惊喜连连&#xff0c;其中最引人瞩目的变革之一&#xff0c;莫过于苹果推出的全新跨平台设计语言 —— Liquid Glass&#xff08;液态玻璃&#xff09;。这一设计风格涵盖了从按钮到导航栏&#xff0…

PDF处理控件Spire.PDF教程:在Java中读取PDF,提取文本、图片和表格

在数据驱动的现代开发中&#xff0c;高效处理 PDF 文档已成为 Java 开发者不可或缺的核心能力。无论是处理各类发票扫描件、业务分析报告&#xff0c;还是包含丰富图表的技术文档&#xff0c;掌握 Java 版的 PDF 解析技术都将大幅提升数据处理效率&#xff0c;充分释放文档中的…

跨平台游戏引擎 Axmol-2.7.0 发布

Axmol 2.7.0 版本是一个以错误修复和功能改进为主的次要LTS长期支持版本 &#x1f64f;感谢所有贡献者及财务赞助者&#xff1a;scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox 重大变更 Android Studio 最低版本要求升级至 2025.1.1…

XML 笔记

<image src"hue.gif" width"100" height"auto" align"left"/> <br/> 换行 在 XML 中&#xff0c;<![CDATA[ 和 ]]> 用于定义一个 CDATA 节&#xff08;Character Data Section&#xff09;。CDATA 节是用于将一段…

Python实现优雅的目录结构打印工具

Python实现优雅的目录结构打印工具 在软件开发、系统管理和日常工作中&#xff0c;我们经常需要查看和分析目录结构。 工具功能概述 这个DirectoryPrinter类提供了以下功能&#xff1a; 递归打印目录结构可配置是否显示隐藏文件可设置最大递归深度自定义缩进和文件/文件夹符…

【Python】文件打开:with open具体解析

示例 # 使用 with 语句打开文件并读取内容 with open(pi.txt, r) as file_object:contents file_object.read()print(contents) # 文件在代码块结束后自动关闭with 解析 with 是 Python 中的上下文管理器语法&#xff0c;用于确保某个操作完成后自动执行清理操作。它常用于文…