Unity3D仿星露谷物语开发39之非基于网格的光标
1、目标
当鼠标移动到reapable scenary(可收割庄稼)上方时,光标会变成十字架。
之前章节中,Grid有Dug/Watered属性,光标移动上方时会显示方框。
而这次的功能并非基于Grid的属性,而是基于scenary(庄稼)的属性。
2、概念
(1)非基于网格的光标的原因
我们的一些游戏逻辑是基于网格方格和网格属性的:
-- 例如地面网格方格,以及它们是否被挖掘和浇水
-- 种植的作物-------每个网格方格只能种植一种作物
但其他游戏逻辑和物体并不局限于网格:
-- 例如可以丢弃和捡起的物品。如果每个网格方格只能放置一个物品,游戏就会显得非常受限。
-- 还有像草这种“可收割”的场景元素 ----- 草可以紧密放置。
所以对于草,我们要用镰刀来收割。这意味着我们需要一个非基于网格的光标,以指示我们是否在有效范围内,以及是否可以对草执行有效的“收割”动作。
(2)计算光标影响的区域
由于现在光标不基于网格工作,需要计算光标影响的区域。
-- 当我们实现基于网格的光标时,我们可以轻松获取网格方格的位置,然后确定哪些游戏对象会受到影响,以及基于该网格方格或相邻网格方格会发生哪些碰撞。
-- 对于非基于网格的光标,我们需要通过考虑以下因素来计算玩家动作将影响的区域:
- 玩家自然的(x,y)中心位置
- 玩家使用工具的方向
- 任何效果的半径大小
(3)光标影响范围
对于Item,我们会定义使用的半径为r。
玩家默认的中心是pivot Point,当我们计算光标的使用半径时,我们将调整中心的位置,将Player的中心移到Player的自然中心位置。 即从绿点 移到 黄点。
(4)各方向工具的光标影响范围
1)右方
2)上方
3)左方
4)下方
所以总的光标的范围如下:
计算方法分两步:
第1步:如果光标在以下红色区域中,就设置它无效。
计算红色区域的条件如下:
第2步:如果光标在以下红色区域中,就设置它无效。
(5)使用Physics2D获取2D碰撞器
我们可以使用 Physics2D 函数来获取光标范围内任何物体的 2D 碰撞器
3、修改Settings.cs脚本
添加下面一行代码:
// Player
public static float playerCentreYOffset = 0.875f;
4、修改HelperMethods.cs脚本
添加第一个功能函数:
/// <summary>/// Gets Components of type T at positionToCheck. Returns truue if at least one found and the found components are/// returned in componentAtPositionList/// </summary>/// <param name="componentsAtPositionList"></param>/// <param name="positionToCheck"></param>/// <returns></returns>public static bool GetComponentsAtCursorLocation<T>(out List<T> componentsAtPositionList, Vector3 positionToCheck){bool found = false;List<T> componentList = new List<T>();Collider2D[] collider2DArray = Physics2D.OverlapPointAll(positionToCheck);// Loop through all colliders to get an object of type TT tComponent = default(T);for(int i = 0; i < collider2DArray.Length; i++){tComponent = collider2DArray[i].gameObject.GetComponentInParent<T>();if(tComponent != null){found = true;componentList.Add(tComponent);}else{tComponent = collider2DArray[i].gameObject.GetComponentInChildren<T>();if(tComponent != null ){found = true;componentList.Add(tComponent);}}}componentsAtPositionList = componentList;return found;}
为什么使用GetComponentInParent<T>()和GetComponentInChildren<T>()而不使用GetComponent<T>()?
GetComponent<T>()仅会在当前游戏对象上查找指定类型的组件。也就是说,它只检查该游戏对象自身是否挂载了类型为T的组件,不会去查找其父对象或者子对象。如果要查找的组件不在当前游戏对象上,而是在其父对象或者子对象上,那么GetComponent<T>()就无法找到该组件。
GetComponentInParent<T>():该方法会在当前游戏对象及其所有父对象中查找指定类型的组件。这意味着如果要查找的组件不在当前游戏对象上,但在其父对象的层级结构中,使用GetComponentInParent<T>()就能够找到它。 GetComponentInChildren<T>():此方法会在当前游戏对象及其所有子对象中查找指定类型的组件。也就是说,如果要查找的组件不在当前游戏对象上,但在其子对象的层级结构中,使用GetComponentInChildren<T>()就可以找到它。
添加第二个功能函数:
/// <summary>/// Returns array of components of type T at box with centre point and size and angle./// The numberOfCollidersToTest for is passed as a parameter./// Found components are returned in the array/// </summary>/// <typeparam name="T"></typeparam>/// <param name="numberOfCollidersToTest"></param>/// <param name="point"></param>/// <param name="size"></param>/// <param name="angle"></param>/// <returns></returns>public static T[] GetComponentsAtBoxLocationNonAlloc<T>(int numberOfCollidersToTest, Vector2 point, Vector2 size, float angle){Collider2D[] collider2DArray = new Collider2D[numberOfCollidersToTest];Physics2D.OverlapBoxNonAlloc(point, size, angle, collider2DArray);T tComponent = default(T);T[] componentArray = new T[collider2DArray.Length];for(int i = collider2DArray.Length - 1; i >= 0; i--){if (collider2DArray[i] != null){tComponent = collider2DArray[i].gameObject.GetComponent<T>();if( tComponent != null){componentArray[i] = tComponent;}}}return componentArray;}
Physics2D.OverlapBoxNonAlloc也是矩形框内的碰撞体检测,与Physics2D.OverlapBoxAll的区别是不会动态分配内存,而是存放在实现定义好的数组中,即collider2DArray变量。
5、修改Player.cs脚本
添加如下方法:
public Vector3 GetPlayerCentrePosition(){return new Vector3(transform.position.x, transform.position.y + Settings.playerCentreYOffset, transform.position.z);}
6、创建Cursor.cs脚本
位于Assets -> Scripts -> UI下。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class Cursor : MonoBehaviour
{private Canvas canvas;private Camera mainCamera; // 作用:通过camera内置函数将screenpoint转为worldposition[SerializeField] private Image cursorImage = null;[SerializeField] private RectTransform cursorRectTransform = null;[SerializeField] private Sprite greenCursorSprite = null; // 绿色光标[SerializeField] private Sprite transparentCursorSprite = null; // 透明光标[SerializeField] private GridCursor gridCursor = null; // 合适的时间调起另一个cursorprivate bool _cursorIsEnable = false;public bool CursorIsEnable { get => _cursorIsEnable; set => _cursorIsEnable = value; }private bool _cursorPositionIsValid = false;public bool CursorPositionIsValid { get => _cursorPositionIsValid; set => _cursorPositionIsValid = value; }private ItemType _selectedItemType;public ItemType SelectedItemType { get => _selectedItemType; set => _selectedItemType = value; }private float _itemUseRadius = 0f; // 非网格使用半径public float ItemUseRadius { get => _itemUseRadius; set => _itemUseRadius = value; }private void Start(){mainCamera = Camera.main;canvas = GetComponentInParent<Canvas>();}private void Update(){if (CursorIsEnable){DisplayCursor();}}private void DisplayCursor(){// Get position for cursorVector3 cursorWorldPosition = GetWorldPositionForCursor();// Set cursor spriteSetCursorValidity(cursorWorldPosition, Player.Instance.GetPlayerCentrePosition());// Get rect transform position for cursorcursorRectTransform.position = GetRectTransformPositionForCursor();}public Vector3 GetWorldPositionForCursor(){Vector3 screenPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0f);Vector3 worldPosition = mainCamera.ScreenToWorldPoint(screenPosition);return worldPosition; }public Vector2 GetRectTransformPositionForCursor(){Vector2 screenPosition = new Vector2(Input.mousePosition.x, Input.mousePosition.y);// 获取鼠标在canvas的rectTransform中的位置return RectTransformUtility.PixelAdjustPoint(screenPosition, cursorRectTransform, canvas);}private void SetCursorValidity(Vector3 cursorPosition, Vector3 playerPosition){SetCursorToValid();// Check use radius cornersif (cursorPosition.x > (playerPosition.x + ItemUseRadius / 2f) && cursorPosition.y > (playerPosition.y + ItemUseRadius / 2f)||cursorPosition.x < (playerPosition.x - ItemUseRadius / 2f) && cursorPosition.y > (playerPosition.y + ItemUseRadius / 2f)||cursorPosition.x < (playerPosition.x - ItemUseRadius / 2f) && cursorPosition.y < (playerPosition.y - ItemUseRadius / 2f)||cursorPosition.x > (playerPosition.x + ItemUseRadius / 2f) && cursorPosition.y < (playerPosition.y - ItemUseRadius / 2f)) {SetCursorToInvalid();return;}// Check item use radius is validif(Mathf.Abs(cursorPosition.x - playerPosition.x) > ItemUseRadius|| Mathf.Abs(cursorPosition.y - playerPosition.y) > ItemUseRadius){SetCursorToInvalid();return;}// Get selected item detailsItemDetails itemDetails = InventoryManager.Instance.GetSelectedInventoryItemDetails(InventoryLocation.player);if(itemDetails == null){SetCursorToInvalid();return;}// Determine cursor validity based on inventory item selected and what object the cursor is overswitch (itemDetails.itemType){case ItemType.Watering_tool:case ItemType.Breaking_tool:case ItemType.Chopping_tool:case ItemType.Hoeing_tool:case ItemType.Reaping_tool:case ItemType.Collecting_tool:if(!SetCursorValidityTool(cursorPosition, playerPosition, itemDetails)){SetCursorToInvalid();return;}break;case ItemType.none:break;case ItemType.count:break;default:break;}}/// <summary>/// Set the cursor to be valid/// </summary>private void SetCursorToValid(){cursorImage.sprite = greenCursorSprite;CursorPositionIsValid = true;gridCursor.DisableCursor(); // 另外一个cursor不生效,两个不要同时生效}/// <summary>/// Set the cursor to be invalid/// </summary>private void SetCursorToInvalid(){cursorImage.sprite = transparentCursorSprite;CursorPositionIsValid = false;gridCursor.EnableCursor(); // 另外一个cursor生效}/// <summary>/// Sets the cursor as either valid or invalid for the tool for the target./// Returns true if valid or false if invalid/// </summary>/// <param name="cursorPosition"></param>/// <param name="playerPosition"></param>/// <param name="itemDetails"></param>/// <returns></returns>private bool SetCursorValidityTool(Vector3 cursorPosition, Vector3 playerPosition, ItemDetails itemDetails){// Switch on toolswitch(itemDetails.itemType){case ItemType.Reaping_tool:return SetCursorValidityReapingTool(cursorPosition, playerPosition, itemDetails);default:return false;}}private bool SetCursorValidityReapingTool(Vector3 cursorPosition, Vector3 playerPosition, ItemDetails equippedItemDetails){List<Item> itemList = new List<Item>();if(HelperMethods.GetComponentsAtCursorLocation<Item>(out itemList, cursorPosition)){if(itemList.Count != 0){foreach(Item item in itemList){if(InventoryManager.Instance.GetItemDetails(item.ItemCode).itemType == ItemType.Reapable_scenary){return true;}}}}return false;}public void DisableCursor(){cursorImage.color = new Color(1f, 1f, 1f, 0f);CursorIsEnable = false;}public void EnableCursor(){cursorImage.color = new Color(1f, 1f, 1f, 1f);CursorIsEnable = true;}}
7、优化UIInventorySlot.cs脚本
添加一行代码如下:
添加一行代码如下:
添加2行代码如下:
添加多行代码如下:
完整的代码如下:
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;public class UIInventorySlot : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{private Camera mainCamera;private Transform parentItem; // 场景中的物体父类private GameObject draggedItem; // 被拖动的物体private Canvas parentCanvas;private GridCursor gridCursor;private Cursor cursor;public Image inventorySlotHighlight;public Image inventorySlotImage;public TextMeshProUGUI textMeshProUGUI;[SerializeField] private UIInventoryBar inventoryBar = null;[SerializeField] private GameObject itemPrefab = null;[SerializeField] private int slotNumber = 0; // 插槽的序列号[SerializeField] private GameObject inventoryTextBoxPrefab = null;[HideInInspector] public ItemDetails itemDetails;[HideInInspector] public int itemQuantity;[HideInInspector] public bool isSelected = false;private void Awake(){parentCanvas = GetComponentInParent<Canvas>();}private void OnDisable(){EventHandler.AfterSceneLoadEvent -= SceneLoaded;EventHandler.DropSelectedItemEvent -= DropSelectedItemAtMousePosition;}private void OnEnable(){EventHandler.AfterSceneLoadEvent += SceneLoaded;EventHandler.DropSelectedItemEvent += DropSelectedItemAtMousePosition;}public void SceneLoaded(){parentItem = GameObject.FindGameObjectWithTag(Tags.ItemsParentTransform).transform;}private void Start(){mainCamera = Camera.main;gridCursor = FindObjectOfType<GridCursor>();cursor = FindObjectOfType<Cursor>();}private void ClearCursors(){// Disable cursorgridCursor.DisableCursor();cursor.DisableCursor();// Set item type to nonegridCursor.SelectedItemType = ItemType.none;cursor.SelectedItemType = ItemType.none;}public void OnBeginDrag(PointerEventData eventData){if(itemDetails != null) {// Disable keyboard inputPlayer.Instance.DisablePlayerInputAndResetMovement();// Instatiate gameobject as dragged itemdraggedItem = Instantiate(inventoryBar.inventoryBarDraggedItem, inventoryBar.transform);// Get image for dragged itemImage draggedItemImage = draggedItem.GetComponentInChildren<Image>();draggedItemImage.sprite = inventorySlotImage.sprite;SetSelectedItem();}}public void OnDrag(PointerEventData eventData){// move game object as dragged itemif(!draggedItem != null){draggedItem.transform.position = Input.mousePosition;}}public void OnEndDrag(PointerEventData eventData){// Destroy game object as dragged itemif (draggedItem != null) {Destroy(draggedItem);// if drag ends over inventory bar, get item drag is over and swap thenif (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>() != null) {// get the slot number where the drag endedint toSlotNumber = eventData.pointerCurrentRaycast.gameObject.GetComponent<UIInventorySlot>().slotNumber;// Swap inventory items in inventory listInventoryManager.Instance.SwapInventoryItems(InventoryLocation.player, slotNumber, toSlotNumber);// Destroy inventory text boxDestroyInventoryTextBox();// Clear selected itemClearSelectedItem();}else{// else attemp to drop the item if it can be droppedif (itemDetails.canBeDropped){DropSelectedItemAtMousePosition();}}// Enable player inputPlayer.Instance.EnablePlayerInput();}}/// <summary>/// Drops the item(if selected) at the current mouse position. called by the DropItem event/// </summary>private void DropSelectedItemAtMousePosition(){if(itemDetails != null && isSelected){// If can drop item hereif (gridCursor.CursorPositionIsValid) {Vector3 worldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, -mainCamera.transform.position.z));// Create item from prefab at mouse positionGameObject itemGameObject = Instantiate(itemPrefab, new Vector3(worldPosition.x, worldPosition.y - Settings.gridCellSize/2f, worldPosition.z), Quaternion.identity, parentItem);Item item = itemGameObject.GetComponent<Item>();item.ItemCode = itemDetails.itemCode;// Remove item from player's inventoryInventoryManager.Instance.RemoveItem(InventoryLocation.player, item.ItemCode);// If no more of item then clear selectedif (InventoryManager.Instance.FindItemInInventory(InventoryLocation.player, item.ItemCode) == -1){ClearSelectedItem();}}}}public void OnPointerEnter(PointerEventData eventData){// Populate text box with item detailsif(itemQuantity != 0){// Instantiate inventory text boxinventoryBar.inventoryTextBoxGameobject = Instantiate(inventoryTextBoxPrefab, transform.position, Quaternion.identity);inventoryBar.inventoryTextBoxGameobject.transform.SetParent(parentCanvas.transform, false);UIInventoryTextBox inventoryTextBox = inventoryBar.inventoryTextBoxGameobject.GetComponent<UIInventoryTextBox>();// Set item type descriptionstring itemTypeDescription = InventoryManager.Instance.GetItemTypeDescription(itemDetails.itemType);// Populate text boxinventoryTextBox.SetTextboxText(itemDetails.itemDescription, itemTypeDescription, "", itemDetails.itemLongDescription, "", "");// Set text box position according to inventory bar positionif (inventoryBar.IsInventoryBarPositionBottom){inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 0f);inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y + 50f, transform.position.z);}else{inventoryBar.inventoryTextBoxGameobject.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 1f);inventoryBar.inventoryTextBoxGameobject.transform.position = new Vector3(transform.position.x, transform.position.y - 50f, transform.position.z);}}}public void OnPointerExit(PointerEventData eventData){DestroyInventoryTextBox();}private void DestroyInventoryTextBox(){if (inventoryBar.inventoryTextBoxGameobject != null) {Destroy(inventoryBar.inventoryTextBoxGameobject);}}public void OnPointerClick(PointerEventData eventData){// if left clickif (eventData.button == PointerEventData.InputButton.Left){// if inventory slot currently selected then deselectif (isSelected == true){ClearSelectedItem();}else // 未被选中且有东西则显示选中的效果{if(itemQuantity > 0){SetSelectedItem();}}}}/// <summary>/// Set this inventory slot item to be selected/// </summary>private void SetSelectedItem(){// Clear currently highlighted itemsinventoryBar.ClearHighlightOnInventorySlots();// Highlight item on inventory barisSelected = true;// Set highlighted inventory slotsinventoryBar.SetHighlightedInventorySlots();// Set use radius for cursors gridCursor.ItemUseGridRadius = itemDetails.itemUseGridRadius;cursor.ItemUseRadius = itemDetails.itemUseRadius;// If item requires a grid cursor then enable cursorif(itemDetails.itemUseGridRadius > 0){gridCursor.EnableCursor();}else{gridCursor.DisableCursor();}// If item requires a cursor then enable cursorif(itemDetails.itemUseRadius > 0f){cursor.EnableCursor();}else{cursor.DisableCursor();}// Set item typegridCursor.SelectedItemType = itemDetails.itemType;cursor.SelectedItemType = itemDetails.itemType;// Set item selected in inventoryInventoryManager.Instance.SetSelectedInventoryItem(InventoryLocation.player, itemDetails.itemCode);if (itemDetails.canBeCarried == true){// Show player carrying itemPlayer.Instance.ShowCarriedItem(itemDetails.itemCode);}else {Player.Instance.ClearCarriedItem();}}private void ClearSelectedItem(){ClearCursors();// Clear currently highlighted iteminventoryBar.ClearHighlightOnInventorySlots();isSelected = false;// set no item selected in inventoryInventoryManager.Instance.ClearSelectedInventoryItem(InventoryLocation.player);// Clear player carrying itemPlayer.Instance.ClearCarriedItem();}
}
8、优化GridCursor.cs脚本
在SetCursorValidity方法中,修改case条件,添加所有的tool信息。
9、设置UI组件
1)给UIPanel对象添加Cursor组件。
2)设置Grid Cursor属性
3)在UIPanel下创建新的空对象命名为Cursor。
4)给Cursor对象添加Image组件
设置Source Image为GreenCursor
设置Color为(255, 255, 255,0)
点击Set Native Size
5)设置UIPanel中Cursor组件的其他属性如下:
10、运行游戏
点击Scythe(镰刀)工具,放在草上会显示GreenCursor的图标。
相关文章:
Unity3D仿星露谷物语开发39之非基于网格的光标
1、目标 当鼠标移动到reapable scenary(可收割庄稼)上方时,光标会变成十字架。 之前章节中,Grid有Dug/Watered属性,光标移动上方时会显示方框。 而这次的功能并非基于Grid的属性,而是基于scenary&#x…...
什么是 MCP?AI 应用的“USB-C”标准接口详解
目录 🧩 什么是 MCP?AI 应用的“USB-C”标准接口详解 📌 背景与动机 🧠 核心概念 🏗️ 技术架构 🚀 应用场景 🧩 什么是 MCP?AI 应用的“USB-C”标准接口详解 📌 背…...
狼人杀中的智能策略:解析AI如何理解复杂社交游戏
想要掌握如何将大模型的力量发挥到极致吗?叶梓老师带您深入了解 Llama Factory —— 一款革命性的大模型微调工具(限时免费)。 1小时实战课程,您将学习到如何轻松上手并有效利用 Llama Factory 来微调您的模型,以发挥其…...
10 基于Gazebo和Rviz实现导航仿真,包括SLAM建图,地图服务,机器人定位,路径规划
在9中我们已经实现了机器人的模块仿真,现在要在这个基础上实现SLAM建图,地图服务,机器人定位,路径规划 1. 还是在上述机器人的工作空间下,新建功能包(nav),导入依赖 gmapping ma…...
jmeter-Beashell获取请求body data
在使用JMeter的BeanShell处理器或BeanShell断言中获取HTTP请求的body数据,可以通过几种方式实现。下面是一些常用的方法: 方法1:使用prev变量 在BeanShell处理器或断言中,prev变量可以用来访问最近的sampler(采样器&…...
区块链密码学核心
文章目录 概要1. 基础密码学哈希函数(Hash Function)对称加密与非对称加密数字签名(Digital Signature)密钥管理 2. 区块链专用密码学技术零知识证明(Zero-Knowledge Proof, ZKP)同态加密(Homom…...
Git 多账号切换及全局用户名设置不生效问,GIT进行上传无权限问题
解决 Git 多账号切换及全局用户名设置不生效问题 在软件开发过程中,我们经常会使用 Git 进行版本控制。有时,我们需要在同一台机器上管理多个 Git 账号,最近我在进行使用git的时候因为项目要进行上传的不同的git账号,但是通过本地…...
阿里云服务迁移实战: 04-IP 迁移
普通过户 如资料过户按量付费EIP所述,如果原账号是个人账号,则目标账号无限制,如果原账号是企业账号,则目标账号必须为相同认证主体的企业账号。 其主要操作就是,在原账号发起过户,在新账号接收过户。具体…...
探索PyTorch中的空间与通道双重注意力机制:实现concise的scSE模块
探索PyTorch中的空间与通道双重注意力机制:实现concise的scSE模块 在深度学习领域,尤其是在计算机视觉任务中,特征图的注意力机制变得越来越重要。近期,我在研究一种结合了通道和空间两种注意力机制的模块——Concise Spatial an…...
关闭正点原子atk-qtapp-start.service
# 查找相关服务 systemctl list-units --typeservice --staterunning # 查看详细信息 systemctl status atk-qtapp-start.service >> ● atk-qtapp-start.service - Qt App Start …...
[按键安卓ios脚本辅助插件开发]数组排序函数例子
按键安卓ios工具辅助脚本插件开发教程,教程目的是让大家掌握Lua基本语法与按键精灵手机版的插件开发制作。 在按键精灵中排序需要我们自己写算法实现,例如快速排序,冒泡排序等,而在Lua中有内置的table.sort()排序命令。 这个命令…...
【BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling】
BotSharp框架示例 ——实现聊天机器人,并通过 DeepSeek V3实现 function calling 一、一点点感悟二、创建项目1、创建项目2、添加引用3、MyWeatherPlugin项目代码编写4、WeatherApiDefaultService项目代码编写5、WebAPI MyWeatherAPI 的项目代码编写6、data文件夹中…...
记录一个单独读取evt.bdf的方法
问题描述 之前只能使用eeglab的工具,读取博瑞康达的data.bdf和evt.bdf时,使用的是eeglab的下面这个读取文件的插件。 evt.bdf使用记事本文件查看是乱码的形式。 实现方法 事实上,我们可以直接对这个文件的16进制进行解析。 对文件的位和…...
是否想要一个桌面哆啦A梦的宠物
是否想拥有一个在指定时间喊你的桌面宠物呢(手动狗头) 如果你有更好的想法,欢迎提出你的想法。 是否考虑过跟开发者一对一,提出你的建议(狗头)。 https://wwxc.lanzouo.com/idKnJ2uvq11c 密码:bbkm...
防爆风扇储能轴流风机风量风压如何保障通风安全?
在化工车间、煤矿巷道等高危环境中,通风安全是保障生产与人员生命安全的关键防线。防爆风扇储能轴流风机凭借独特的风量风压设计与性能优势,成为守护通风安全的可靠屏障。那么,它究竟是如何发挥作用的呢? 从风量设计来看,防爆风…...
Centos 7系统 宝塔部署Tomcat项目(保姆级教程)
再看文章之前默认已经安装好系统,可能是云系统,或者是虚拟机。 宝塔安装 这个比较简单,参考这个老哥的即可: https://blog.csdn.net/weixin_42753193/article/details/125959289 环境配置 进入宝塔面板之后会出现环境安装&…...
Electron读取本地文件
在 Electron 应用中,可以使用 Node.js 的 fs 模块来读取本地文件。以下是如何实现这一功能的详细步骤。 1. 设置项目结构 假设你的项目目录如下: my-electron-app/ ├── main.js ├── index.html └── renderer.js2. 使用 fs 模块读取文件 在主…...
Plesk 下的 IP 地址管理
Plesk是一个方便管理的控制面板,可以简化网站主机和服务器数据中心的自动化管理。它专为提供Windows和Linux服务器的供应商设计。Plesk面板适用于虚拟主机和独立服务器 服务器管理员可以使用Plesk来配置新网站、电子邮件系统和转售商账户,也可以通过Ple…...
基于STM32、HAL库的DS28E15P安全验证及加密芯片驱动程序设计
一、简介: DS28E15P是Maxim Integrated (现为Analog Devices)生产的一款1-Wire EEPROM芯片,具有以下特点: 1-Wire接口通信,仅需单根数据线加地线 1024位(128字节)EEPROM存储器 每个器件具有唯一的64位ROM ID 工作电压范围:2.8V至5.25V 内置CRC16校验功能 可编程写保护功能…...
浅析localhost、127.0.0.1 和 0.0.0.0的区别
文章目录 三者的解释三者的核心区别总结使用场景示例什么是回环地址常见问题开发工具中的地址使用为什么开发工具同时支持localhost和127.0.0.1?实际应用示例VSCode中的Live Server插件VSCode中的VUE项目IDEA中的Spring Boot应用 最佳实践建议 localhost、 127.0.0…...
antd+react实现html图片预览效果
import { Image } from ‘antd’; import { useEffect, useRef, useState } from ‘react’; import styles from ‘./index.module.less’; interface PreviewHtmlWithImagesProps { htmlContent: string; } const PreviewHtmlWithImages: React.FC ({ htmlContent }) >…...
【React】轻松掌握 React 中 useEffect的使用
你有没有想过,为什么你的 React 组件能够轻松应对周围发生的变化,比如每当有新数据到来时自动更新,或者处理可以动态响应实时事件的组件?这就是 useEffect 的用武之地!这个强大的钩子(Hook)就像…...
请简述一下什么是 Kotlin?它有哪些特性?
1 JVM 语言的共性:编译成字节码文件 Kotlin 和 Java 同属于 JVM(Java Virtual Machine)语言,它们的代码最终都会被编译成 JVM 字节码(.class)文件。 编译流程: Kotlin 编译:Kotli…...
Post与Get以及@Requestbody和@Pathvariable标签的应用
Post的使用场景:简单来讲适用于有安全性限制的,因为post请求的内容会被存在某个封装内容中(比如表单、jason格式等),这部分内容是不会被浏览器的cache所捕捉,安全性较强。 Get的使用场景:与pos…...
基于tabula对pdf中的excel进行识别并转换成word(三)
上一节中是基于PaddleOCR对图片中的excel进行识别并转换成word优化,本节改变思路,直接从pdf中读取表格的信息,具体思路如下所述。 PDF中的表格数据如下截图所示: 一、基于tabula从PDF中提取表格 df_list tabula.read_pdf("…...
k8s集群环境部署业务系统
k8s集群环境部署业务系统,通过shell脚本整合部署过程,简化部署流程。操作流程如下: A,B为业务系统服务名。 一.部署前准备。在k8s集群各节点执行该脚本,完成业务系统镜像加载。 #!/bin/bash # 1.删除deployment ech…...
MySQL 8.4.4 安全升级指南:从漏洞修复到版本升级全流程解析
目录 二、升级前关键注意事项 1. 数据安全与备份 2. 版本兼容性与路径规划 三、分步升级操作流程 1. 环境预检与准备 2. 安装包部署 3. 强制升级组件 4. 验证与启动 一、背景与必要性 近期安全扫描发现生产环境的 MySQL 数据库存在多个高危漏洞(CVE 详情参见Oracle 官…...
“假读“操作在I2C接收流程中的原因
在I2C接收流程中,"假读"操作是NXP I2C控制器工作特性要求的必要操作,具体原因如下: // 接收函数关键代码 void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size) {// ...dummy base->I2DR; /* 假读 *…...
TA学习之路——2.3图形的HLSL常用函数详解
1.基本数学运算 函数作用max(a,b)返回a,b值中较大的那个min(a,b)返回a,b值中较小的那个mul(a,b两变量相乘,常用于矩阵abs(a)返回a的绝对值sqrt(x)返回x的平方根rsqrt(x)返回x的平方根的倒数degrees(x)将弧度转成角度radians(x)将角度转成弧度noise(x)噪声函数1.1 创建一个测试…...
Python数据容器:数据容器的分类、数据容器特点总结、各数据容器遍历的特点、数据容器通用操作(统计,转换,排序)
目录 数据容器的分类 数据容器特点总结 数据容器遍历的特点 通用操作 通用统计len()、max()、min() 通用转换list()、tuple()、str()、set() 通用排序sorted 数据容器的分类 分类: 是否支持下标索引 支持:列表、元组、字符串-序列类型不支持&…...
FastAPI的发展历史
参考:https://zhuanlan.zhihu.com/p/710831974 FastAPI 于 2019 年发布,由 Sebastian Ramirez 创建。他是 Pydantic 框架的创建者,也是多个开源项目的贡献者。 FastAPI 的设计初衷是为了解决 Python Web 框架在数据类型验证和文档生成方面的问…...
本地大模型编程实战(28)查询图数据库NEO4J(1)
本文将基于langchain 框架,用LLM(大语言模型)查询图数据库NEO4J。 使用 qwen2.5 做实验,用 llama3.1 查不出内容。 文章目录 安装 NEO4J准备图数据查询图数据总结代码 安装 NEO4J 参见:在windows系统中安装图数据库NEO4J 。 准备图数据 我…...
从厨房到云端:从预制菜到云原生
小美:小猿,你最近在忙什么呢?看你总是加班。 小猿:唉,公司在搞什么“云原生”改造,说是要把我们的应用搬到云上,搞得我头都大了。 小美:云原生?听起来很高大上啊&#…...
单片机-89C51部分:9、串行口通讯
飞书文档https://x509p6c8to.feishu.cn/wiki/WSh3wnADkixHspk7kc8c5esRnad 一、什么是串口?它的作用? 串行口,简称为串口,什么是串口?它的作用是什么? 两个人交流,一般通过在说话在空气中产生…...
C++程序退出时的对象析构陷阱:深度解析与避坑指南
C++程序退出时的对象析构陷阱:深度解析与避坑指南 一、从诡异案例说起:局部对象为何"神秘消失"?二、全局对象 vs 局部对象1. 全局对象生命周期2. 局部对象生命周期三、程序终止的两种姿势:exit() vs return四、atexit():最后的救命稻草1.基础用法2. 核心特性3…...
为什么 Vite 速度比 Webpack 快?
一、webpack会先进行编译,再运行,vite会直接启动,再按需编译文件。 首先看两张图,可以清晰的看到,上面的图是webpack编译过的,而下面的图是vite直接使用工程内文件。 二、区别于Webpack先打包的方式&am…...
指针变量存放在哪?
指针的存储位置取决于其声明方式和作用域,以下是详细分析: 1. 栈(Stack) 局部指针变量:在函数内部声明的指针(非静态),作为局部变量存储在栈中。void func() {int *p; // p本身存储…...
Kafka 消息可靠性深度解析:大流量与小流量场景下的设计哲学
在分布式消息系统的设计中,消息可靠性保障本质上是系统在一致性、可用性、吞吐量三者之间动态博弈的结果。Kafka作为现代流式架构的核心组件,其消息可靠性机制在不同流量场景下呈现出截然不同的设计哲学。本文将从系统设计原理层面,解构大流量…...
python数据分析(六):Pandas 多数据操作全面指南
Pandas 多数据操作全面指南:Merge, Join, Concatenate 与 Compare 1. 引言 在数据分析工作中,我们经常需要处理多个数据集并将它们以各种方式组合起来。Pandas 提供了多种强大的多数据操作方法,包括合并(merge)、连接(join)、连接(concaten…...
在K8S迁移节点kubelet数据存储目录
默认k8s节点kubelet数据目录在 /var/lib/kubelet,如果在部署前没有做好规划,其实默认就存储在系统盘/分区下了,这样会导致一个问题,如果数据量过大会导致kubelet服务异常,其次,系统盘下有一些系统服务引用&…...
k8s 学习记录 (六)_Pod 污点和容忍性详解
一、前言 在 Kubernetes 集群中,我们已经了解了节点亲和性和 Pod 亲和性,它们在 Pod 调度方面提供了很大的灵活性。今天,我们来探讨另外两个重要的概念 ——Pod 污点(Taints)和容忍性(Tolerations…...
hutools工具类中isNotEmpty与isNotBlank区分
基于以下两种情况。在判断的变量是String类型时, 判断是否为空,推荐使用isNotBlank(). 1. isNotEmpty 不会验证str中是否含有空字符串,而 isNotBlank方法会验证 public static boolean isNotEmpty(CharSequence str) {return false isEmpty…...
C#解析USB - HID手柄上摇杆按键数据
1. 了解相关知识 HID设备通信原理:HID设备通过端点报告其状态和数据,设备通过报告描述符来描述数据用途,操作系统通过这个描述符了解设备发送数据的结构。通常一个完整的报告以特定的格式从设备传输至主机。Windows API函数…...
k8s术语pod
Pod概览 理解Pod Pod是kubernetes中你可以创建和部署的最小也是最简的单位,pod代表着集群中运行的进程。 Pod中封装着应用的容器(有的情况下是好几个容器),存储、独立的网络IP,管理容器如何运行的策略选项。Pod代表着部署的一个单位:kubemetes中应用的一个实例,可能由一个…...
使用高德MCP+AI编程工具打造一个旅游小助手
开始之前,我们先来回顾一下,什么是mcp MCP(Model Context Protocol) 模型上下文协议是 2024 年 11 月由 Anthropic 公司推出并开源的一个新标准 核心内容 架构2:MCP 采用客户端 - 服务器架构。AI 应用程序(如聊天工具或 IDE&a…...
AgeTravel | 银发文娱旅游一周新鲜事
银发文娱旅游一周新鲜事 整理 | AgeClub吕娆炜 一周银发文娱旅游产业资讯速览 星期一 4月28日 1 企业动态 携程老友会联合喜悦盛年,首发“旅游兴趣课堂”模式 华寿中心“这YOUNG的老年派”合作聚焦老龄文化需求 通用技术康养Q1营收同比增长129% 2 行业风向…...
【记】Laya2.x数字末尾导致换行异常问题
文章目录 图示 图示...
jspm老年体检信息管理系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 信息化时代,各行各业都以网络为基础飞速发展,而医疗服务行业的发展却进展缓慢,传统的医疗服务行业已经逐渐不满足民众的需求,有些还在以线下预约的方式接待病人,特别是针对于老年体检的服务,是少之又…...
point-cloud-annotation-tool工具windows复现,史诗巨制(windows 11)
point-cloud-annotation-tool 前期准备1.安装Qt52.安装Cmake3.安装VS 20194.编译构建 VTK 8.15.安装 PCL 1.86.编译Boost 软件环境适配准备1 Boost2 VTK 开始编译开始生成 前期准备 point-cloud-annotation-tool是一款三维标注软件,本次介绍的是再windows11环境下安…...
欧拉计划 Project Euler60(素数对集合)题解
欧拉计划 Project Euler 60 题解 题干思路code 题干 思路 先欧拉筛预处理出素数,然后dfs,注意剪枝,不然太容易炸了 code //13 5197 5701 6733 8389 //26033 #include <bits/stdc.h>using namespace std;using ll long long;bool i…...