麦田物语第二十天

系列文章目录

麦田物语第二十天


文章目录

  • 系列文章目录
  • 一、构建地图信息系统
  • 二、生成地图数据


一、构建地图信息系统

我们上一节课已经做好了鼠标的显示,这节课需要构建地图的一些信息,例如:可挖坑,可丢弃物品等地区。我们点击地图时,只有鼠标位于规定的地区时才会出现相应的鼠标图片。
怎么我们怎么编辑才能让Unity知道这块是允许挖坑的呢?

简单实现方法:
1.我们可以创建自定义的瓦片地图,也就是Tile,如果你创建一个新的代码并继承自Tile,那么我们就可以创建一个自定义的Tile。例如我们使用的Rule Tile,他挂载了也是继承自Tile的脚本,所以理论上我们希望某一个格子上面有着一些代码的逻辑或者属性的话,我们就可以创建一个代码,挂载在一个空的物体上,然后将这个物体放入Default Sprite的Default GameObject中作为它的索引,如下图,那么系统就会识别到每一个格子对应的空物体身上的代码以及他的属性了。(这是一个最基本的解决方案,但是只适用于小地图!!!)
在这里插入图片描述
2.Unity的2D Tilemap Extras拓展包提供了Grid Information脚本,这个脚本需要挂载在Grid(网格)上,这里面有一些方法,可以设置每一个确定位置坐标的属性,并且可以通过Get的方式去拿到它的属性;在本项目中,一个格子可能包含多个bool属性,可以丢弃物品,可以种东西等,所以也没有办法通过Grid Information来拿到这个属性。

上面介绍的这两种方法在小地图上都是可以使用的,下面我们需要介绍一下本项目中用到的方法。
我们想要手动编写一套系统,让这套系统可以挂载在咱们当时设置的Grid Property下面的四个Tilemap上,我们在每一个Tilemap的瓦片地图上绘制信息,在通过该代码拿到瓦片地图上绘制的信息,传递给一个ScriptObject,用来存储当前坐标的对应属性,例如canDig,canDropItem等,然后将这些与当前的地图连接上(因为存在多个地图),保证每一张地图有着各自的属性。
我们首先需要创建的是单个地图的信息,因此我们需要编写储存信息类型TileProperty,返回DataCollection脚本;这个类型中需要包含网格的位置信息tileCoordinate,接着因为我们需要一个枚举变量了解这个瓦片地图属于什么类型,所以还要回到Enums脚本编写一个枚举类型GridType,最后我们还需要一个bool类型的变量boolTypeValue(我也不知道具体是干嘛的)

Enums脚本的枚举变量GridType代码如下:

public enum GridType
{Diggable, DropItem, PlaceFurniture, NPCObstacle
}

DataCollection脚本的网格信息类型tileProperty代码如下:

[System.Serializable]
public class TileProperty
{public Vector2Int tileCoordinate;public GridType gridType;public bool boolTypeValue;
}

我们会在Unity将这个数据类型读取到瓦片上,然后按照实际的范围去访问所有的网格坐标,将坐标拿到的值对应存储到SO文件中。
现在我们返回Unity,单独创建Scripts-> Map->Data文件夹来构建地图,接着在Data文件夹中添加脚本MapData_SO,使得每一个场景都可以有一个SO文件用来存储网格的相关属性。
接着编写MapData_SO脚本(和之前的SO文件编写类似),需要注意的就是里面属性的定义,我们需要定义的是场景的名字sceneName,并将其使用咱们当时编写的Attribute,最后我们需要创建的是一个TileProperty的列表,返回Unity我们就可以创建这个SO文件了,并将文件改名为MapData_Field(位置为GameData->Map Data)。
我们可以通过拖拽框选择场景的名字,这些属性我么你也要通过代码将其添加进去,通过这个代码,在我们将所需的范围绘制好了之后,我们将绘制的网格对应的坐标等信息保存到SO文件中。
接着创建脚本Scripts->Map->Logic->GridMap脚本,我们编写的这个脚本是挂载在瓦片地图上的,因此我们添加usingUnityEngine.Tilemap;接着我们需要编写一个变量,首先就是SO变量,接着我们需要创建GridType的枚举变量,用来定义瓦片地图的属性是什么,最后就是我们的瓦片地图了;将Field场景中的Grid Properties设置为可视状态,然后给其下面的四个Tilemap添加Gridmap这个脚本,之后返回Unity进行赋值(记得选择Grid Type);
我们怎么将我们绘制的地图信息存储到我们的SO文件当中呢?
首先这个脚本我们需要在EditMode下去运行,同时希望地图被关闭时去读所有的数据,每次启动地图时会刷新这些网格数据,然后存储到瓦片地图的MapData_SO中,因为我们使用OnEnable和OnDisable方法,我们希望所有的函数都在编辑模式下,而非运行当中,所以我们在OnEnable和OnDisable中进行判断,如果没有处于运行状态,我们就获取这个物体的Tilemap;接着我需要获取得到的mapData是否为空,如果不为空的话,我们将mapData中的信息全部清空。在OnDisable方法中也是如此,只不过不清空属性,而是将网格属性更新到SO文件中。
接着我们查看Unity官方文档,我们查看Tilemap的代码,里面的CompressBounds方法可以获取真实存在的所有网格信息,通过这个方法可以压缩拿到最小的可视性的范围(我其实也不是很懂)。
我们编写UpdateTileProperties方法,在这个方法中我们先调用CompressBounds方法。接下来我们还是要进行一些判断,如上面相同,首先该代码必须不在游戏运行模式下,并且mapData不为空才能接着执行下面的代码,我们要获得坐标的范围,即左下角和右上角网格的坐标(Vector3Int类型),然后循环这里面的所有网格,拿到每一个格子tile,如果tile不为空的话,我们新建一个TileProperty属性,并对其赋值,最后将这个TileProperty添加到mapData中。
我们接下来就可以将UpdateTileProperties方法在OnDisable方法中调用了,但是ScriptObject类型有一个特性,就是你需要保存他,如果你临时修改之后退出Unity再回来这个属性就丢失了。所以我们要将其标记为Dirty,只有这样才能实时的进行保存和修改。并且为了确保该部分代码是在我们的Editor编辑器里面去执行的,所以可以加上#if UNITY_EDITOR和#endif(这个文件是会打包到游戏里面的(不懂,呜呜呜,之后自己去查一下))。
我们已经编写好了代码,接着返回Unity,绘制相应的层(用Collision那一个Tilemap就可),绘制完成后关闭Grid Properties这个物体,我们查看SO 文件就会发现已经存储好了我们需要的网格的数据。

二、生成地图数据

在本节课中我们创建地图数据的管理类。我们可以发现我们上节课保存的格子信息里面同一个格子可能会有好几个属性,但是由于不同的属性就会多存储好几个数据,因为我们可以简化这个格子的数据。
那么怎么去简化这个信息呢,我们可以创建一个类似于ItemDetails类型的类TileDetails,我们在DataCollection中来编写TileDetails类,瓦片信息包括瓦片坐标,是否可挖坑,是否可丢弃,是否可以放置家具,是否为NPC障碍,还有其他的int类型的变量,包括已经被挖了多少天了,已经浇了多少天水了,当前土地的种子信息,当前种子成长了多长时间,距离上次收割的时间。

DataCollection脚本的TileDetails代码如下:

[System.Serializable]
public class TileDetails
{public int gridX, gridY;public bool canDig;public bool canDropItem;public bool canPlaceFurniture;public bool isNPCObstacle;public int daySceneDug = -1;public int daysSinceWatered = -1;public int seedItemID = -1;public int growthDays = -1;public int daySinceLastHarvest = -1;
}

接着在PresistentScene场景中添加空物体GridMap Manager,然后创建Scripts->Map -> Logic -> GridMapManager脚本,接着编写该脚本,我们首先为其添加命名空间MFarm.Map,我们的游戏会有很多场景,所以会有很多SO文件用来保存每个场景中格子的信息,因为我们首先定义MapData_SO类型的数组,并返回Unity为其赋值(目前只有场景1Filed的SO文件);接着我们定义字典来保存场景名和场景中格子信息,但是该字典的键值对的键采用瓦片的x和y坐标+场景名称,这样可以减少重复。
现在我们要进行初始化字典了InitTileDetailsDict,我们循环mapData的tileProperties数组,每一个tilePropertie都要新建一个TileDetails类型的数据,并对其进行赋值,最后按照我们生成键的方法将该TileDetails添加进去即可。(该字典也会因为地图格子数据的改变而再次改变,所以当我们改变了地图数据后,我们在字典中通过key值查找是否有对应的TileDetails,有的话返回它,没有的话返回null,接着在上方调用这个方法GetTileDetails,如果存在相应的值,那么我们进行更新,但是没有的话我们直接插入键值对就可以了),但是TileDetails除了x,y之外还有其他的变量,那些bool值也需要在该方法中被赋值,我们利用Switch来对这些bool值进行赋值,最后再进行我们之前说的更新即可。

GridMapManager脚本的InitTimeDetailsDict方法和GetTileDetails方法代码如下:

private void InitTileDetailsDict(MapData_SO mapData){foreach (TileProperty tileProperty in mapData.tileProperties){TileDetails tileDetails = new TileDetails{gridX = tileProperty.tileCoordinate.x,gridY = tileProperty.tileCoordinate.y};string key = tileDetails.gridX + "x" + tileDetails.gridY + "y" + mapData.sceneName;if (GetTileDetails(key) != null){tileDetails = GetTileDetails(key);}switch(tileProperty.gridType){case GridType.Diggable:tileDetails.canDig = tileProperty.boolTypeValue;break;case GridType.DropItem:tileDetails.canDropItem = tileProperty.boolTypeValue;break;case GridType.PlaceFurniture:tileDetails.canPlaceFurniture = tileProperty.boolTypeValue;break;case GridType.NPCObstacle:tileDetails.isNPCObstacle = tileProperty.boolTypeValue;break;}if (GetTileDetails(key) != null){tileDetailsDict[key] = tileDetails;}else{tileDetailsDict.Add(key, tileDetails);}}}/// <summary>/// 根据字典的key返回瓦片信息/// </summary>/// <param name="key">x+y+地图名字</param>/// <returns></returns>private TileDetails GetTileDetails(string key){if (tileDetailsDict.ContainsKey(key)){return tileDetailsDict[key];}else{return null;}}

通过这个方法我们就可以生成对应SO的字典数据了,现在我们在Start方法中循环mapDataList对每一个SO文件进行字典数据的初始化。
我们构建好了字典之后,我们来到CursorManager中,当我们选择锤子后,鼠标放在特地位置需要产生相应的效果,就是我们鼠标放在可以建造的位置判断是否可以建造。
接下来我们创建一系列变量来实现这个功能,此时就需要补充两个知识,我们鼠标移动时是屏幕坐标,我们要切换成世界坐标,因此就要拿到主相机(MainCamera),屏幕坐标转换为世界坐标之后,还要转换成网格坐标,因为我们需要判断的都是整数型的网格坐标,因此我们需要拿到Grid(当前场景的网格)。
然后是这两个变量的初始化,我们在Start方法中拿到主相机,但是Grid是当前场景的网格,因此我们需要在切换场景后拿到,所以我们添加EventHandler的事件并添加OnAfterSceneLoadedEvent方法就行(但是这块有一个问题,等会我们来揭晓)。
我们继续编写CheckCursorValid方法来检测鼠标指针是否可用,这个方法最后在Update中去调用,在这个方法中我们要获得鼠标的世界坐标和网格坐标,因此我们需要先去上面定义这两个变量,然后再进行获取(这个获取方法一定要记住),最后为了验证我们使用Debug.Log去输出网格信息。
但是当我们返回Unity运行发现出现了报空错误,因为我们找不到了Grid(当前地图的网格),这就是我刚才说的那个问题,因为我们是在跳转场景之后找到的Grid,但是我们第一次运行游戏时没有得到Grid,因为我们回到TransitionManager脚本中,在Start方法中我们在加载场景后呼叫一下CallAfterSceneLoadedEvent方法就行了(麦扣老师这里吧Start方法改为协程方法,并调用(yield return )LoadSceneSetActive方法,而不是之前通过StartCoroutine,最后呼叫就好了,这个改动我其实有点不太懂),返回Unity,发现还是有报空存在,这次又是因为啥嘞?
因为我们是在TransitionManager脚本中的Start方法是在加载场景后呼叫的,因为在场景加载好之前鼠标是不可用的,所以我们首先声明一个bool类型的值cursorEnable,在添加BeforeSceneUnloadEvent事件后,同时也添加OnBeforeSceneUnloadEvent方法,在OnBeforeSceneUnloadEvent将cursorEnable设置为false,在OnAfterSceneLoadedEvent将cursorEnable设置为true,只有cursorEnable为true时并且不和UI进行互动时,才能执行SetCursorImage(currentSprite);和CheckCursorValid();方法

GridMapManager脚本的代码如下:

namespace MFarm.Map
{public class GridMapManager : MonoBehaviour{[Header("地图信息")]public List<MapData_SO> mapDataList;//场景名字+坐标和对应的瓦片信息private Dictionary<string, TileDetails> tileDetailsDict = new Dictionary<string, TileDetails>();private void Start(){foreach (MapData_SO mapData in mapDataList){InitTileDetailsDict(mapData);}}private void InitTileDetailsDict(MapData_SO mapData){foreach (TileProperty tileProperty in mapData.tileProperties){TileDetails tileDetails = new TileDetails{gridX = tileProperty.tileCoordinate.x,gridY = tileProperty.tileCoordinate.y};string key = tileDetails.gridX + "x" + tileDetails.gridY + "y" + mapData.sceneName;if (GetTileDetails(key) != null){tileDetails = GetTileDetails(key);}switch(tileProperty.gridType){case GridType.Diggable:tileDetails.canDig = tileProperty.boolTypeValue;break;case GridType.DropItem:tileDetails.canDropItem = tileProperty.boolTypeValue;break;case GridType.PlaceFurniture:tileDetails.canPlaceFurniture = tileProperty.boolTypeValue;break;case GridType.NPCObstacle:tileDetails.isNPCObstacle = tileProperty.boolTypeValue;break;}if (GetTileDetails(key) != null){tileDetailsDict[key] = tileDetails;}else{tileDetailsDict.Add(key, tileDetails);}}}/// <summary>/// 根据字典的key返回瓦片信息/// </summary>/// <param name="key">x+y+地图名字</param>/// <returns></returns>private TileDetails GetTileDetails(string key){if (tileDetailsDict.ContainsKey(key)){return tileDetailsDict[key];}else{return null;}}}
}

CursorManager脚本的代码如下:

public class CursorManager : MonoBehaviour
{public Sprite normal, tool, seed, item;//存储当前图片private Sprite currentSprite;private Image cursorImage;private RectTransform cursorCanvas;//鼠标检测//屏幕坐标切换为世界坐标就是需要调用mainCameraprivate Camera mainCamera;//将屏幕坐标转化为网格坐标需要拿到Grid,切换场景时要切换成当前场景的Gridprivate Grid currentGrid;private Vector3 mouseWorldPos;private Vector3Int mouseGridPos;private bool cursorEnable;private void Start(){cursorCanvas = GameObject.FindGameObjectWithTag("CursorCanvas").GetComponent<RectTransform>();cursorImage = cursorCanvas.GetChild(0).GetComponent<Image>();currentSprite = normal;SetCursorImage(normal);//MainCamera一定是被标记为MainCamera的相机mainCamera = Camera.main;}private void Update(){if (cursorImage == null) return;cursorImage.transform.position = Input.mousePosition;if (!InteractWithUI() && cursorEnable){SetCursorImage(currentSprite);CheckCursorValid();}else{SetCursorImage(normal);}}private void SetCursorImage(Sprite sprite){cursorImage.sprite = sprite;cursorImage.color = new Color(1, 1, 1, 1);}private void OnEnable(){EventHandler.ItemSelectedEvent += OnItemSelectedEvent;EventHandler.BeforeSceneUnloadEvent += OnBeforeSceneUnloadEvent;EventHandler.AfterSceneLoadedEvent += OnAfterSceneLoadedEvent;}private void OnDisable(){EventHandler.ItemSelectedEvent -= OnItemSelectedEvent;EventHandler.BeforeSceneUnloadEvent -= OnBeforeSceneUnloadEvent;EventHandler.AfterSceneLoadedEvent -= OnAfterSceneLoadedEvent;}private void OnItemSelectedEvent(ItemDetails itemDetails, bool isSelected){if (!isSelected){currentSprite = normal;}else{//添加所有类型对应图片currentSprite = itemDetails.itemType switch{ItemType.Seed => seed,ItemType.Commodity => item,ItemType.ChopTool => tool,ItemType.HoeTool => tool,ItemType.WaterTool => tool,ItemType.BreakTool => tool,ItemType.ReapTool => tool,ItemType.Furniture => tool,_ => normal,};}}/// <summary>/// 判断是否跟UI互动/// </summary>/// <returns></returns>private bool InteractWithUI(){if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject()){return true;}elsereturn false;}private void OnBeforeSceneUnloadEvent(){cursorEnable = false;}private void OnAfterSceneLoadedEvent(){currentGrid = FindObjectOfType<Grid>();cursorEnable = true;}private void CheckCursorValid(){mouseWorldPos = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y,-mainCamera.transform.position.z));mouseGridPos = currentGrid.WorldToCell(mouseWorldPos);Debug.Log(mouseGridPos);}
}

TransitionManager脚本的Start方法如下:

private IEnumerator Start(){fadeCanvasGroup = FindObjectOfType<CanvasGroup>();yield return LoadSceneSetActive(startSceneName);EventHandler.CallAfterSceneLoadedEvent();}

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

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

相关文章

c语言学习,isdigit()函数分析

1&#xff1a;isdigit() 函数说明&#xff1a; 检查参数c&#xff0c;是否为0到9阿拉伯数字 2&#xff1a;函数原型&#xff1a; int isdigit(int c) 3&#xff1a;函数参数&#xff1a; 参数c&#xff0c;为检测整数 4&#xff1a;返回值&#xff1a; 参数c是阿拉伯码字符&…

【机器学习第8章——集成学习】

机器学习第8章——集成学习 8.集成学习8.1个体与集成弱分类器之间的关系组合时&#xff0c;如何选择学习器怎么组合弱分类器boosting和Bagging 8.2 BoostingAdaBoost算法步骤训练过程 8.3 Bagging与随机森林随机采样(bootstrap)弱学习器结合策略方差与偏差算法流程随机森林 8.4…

中职云计算实训室

一、实训室建设背景 随着信息技术的飞速发展&#xff0c;云计算已成为推动数字化转型、促进经济社会发展的重要力量。《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》明确提出&#xff0c;要加快数字化发展&#xff0c;建设数字中国。云计算作为数…

NLP——Transfromer 架构详解

Transformer总体架构图 输入部分&#xff1a;源文本嵌入层及其位置编码器、目标文本嵌入层及其位置编码器 编码器部分 由N个编码器层堆叠而成 每个编码器层由两个子层连接结构组成 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接 第二个子层连接结构包…

基于Java中的SSM框架实现远程同步课堂系统项目【项目源码+论文说明】计算机毕业设计

基于Java中的SSM框架实现远程同步课堂系统演示 远程同步课堂系统设计与实现 摘要&#xff1a;在这样一个网络数据大爆炸的时代&#xff0c;人们获取知识、获取信息的通道非常的多元化&#xff0c;通过网络来实现数据信息的获取成为了现在非常常见的一种方式&#xff0c;而通过…

MindSearch:用于增强网络搜索效率的开源人工智能

Web 信息查找与集成是搜索、检索、提取或集成 Web 资源以满足特定需求的活动&#xff0c;是实际生活中几乎所有领域中每个决策和解决问题的实体都必须执行的操作。 大型语言模型 (LLM) 与搜索引擎的集成重新定义了我们在网络上查找和使用信息的方式。因此&#xff0c;LLM 能够…

leetcode递归(203. 移除链表元素)

前言 经过前期的基础训练以及部分实战练习&#xff0c;粗略掌握了各种题型的解题思路。现阶段开始专项练习。 描述 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a;…

将元组类型的日期时间转换为字符串格式time.asctime([t])

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 将元组类型的日期时间转换为 字符串格式 time.asctime([t]) [太阳]选择题 根据给定的Python代码&#xff0c;哪个选项是错误的&#xff1f; import time time_tuple (1993, 6, 30, 21, 49…

pytorch下载慢,如何下载到本地再去安装,本地安装pytorch

有时候按部就班的用指令去安装pytorch&#xff0c;网上很慢&#xff0c;并且往往最后可能还没有安装成功。 本次&#xff0c;介绍一下如何将这个文件先下载到本地&#xff0c;然后在去安装。 至于如何安装pytorch&#xff0c;先看一下我之前写的 深度学习环境-------pytorch…

什么是多模态大模型?为什么需要多模态大模型?

“ 多模态大模型&#xff0c;就是支持多种数据格式的模型**”** 很多人都听说过多模态&#xff0c;也知道多模态大模型&#xff0c;但如果让你介绍一下什么是多模态大模型&#xff0c;它有什么优点和缺点&#xff0c;以及为什么需要多模态&#xff0c;这时可能就有点傻眼了。‍…

jupyter项目使用Anaconda环境内核

1、创建虚拟环境 conda create --name myjupyter python3.7 2、进入虚拟环境 conda activate myjupyter 3、切换到自己jupyter notebook项目想在的目录 E: cd E:\first\project\jupyter\jupyter01 4、安装IPython内核包&#xff0c;这是Jupyter Notebook使用Python内核所必需的…

vlunstack-2(复现红日安全-ATT CK实战)

环境搭建 配置信息 DC IP&#xff1a;10.10.10.10 OS&#xff1a;Windows 2012(64) 应用&#xff1a;AD域 WEB IP1&#xff1a;10.10.10.80 IP2&#xff1a;192.168.47.131 OS&#xff1a;Windows 2008(64) 应用&#xff1a;Weblogic 10.3.6MSSQL 2008 PC IP1&#xff1a;10.10…

Chapter 30 多态

欢迎大家订阅【Python从入门到精通】专栏&#xff0c;一起探索Python的无限可能&#xff01; 文章目录 前言一、基本概念二、抽象类 前言 多态&#xff08;Polymorphism&#xff09;是面向对象编程中的核心概念&#xff0c;本章将详细讲解 Python 中多态的实现方式以及如何应用…

科技驱动健康,景联文科技提供高质量高血压数据采集

当前&#xff0c;穿戴手表市场呈现出快速发展趋势&#xff0c;已成为可穿戴设备领域的一个重要组成部分。市场上智能手表的厂商包括小米、华为、苹果、步步高、vivo、努比亚、三六零、科大讯飞、等。 高血压数据采集可为高血压的预防提供支持&#xff0c;持续监测可以帮助用户及…

RabbitMQ高级特性 - 生产者消息确认机制

文章目录 生产者消息确认机制概述confirm 代码实现return 代码实现 生产者消息确认机制 概述 为了保证信息 从生产者 发送到 队列&#xff0c;因此引入了生产者的消息确认机制. RabbitMQ 提供了两种解决方案&#xff1a; 通过事务机制实现.通过发送确认机制&#xff08;confi…

【第九节】python中xml解析和json编解码

目录 一、Python XML 解析 1.1 什么是XML 1.2 Python 对 XML 的解析方法 1.3 SAX解析xml 1.4 xml.dom解析xml 1.6 ElementTree解析XML 二、Python编解码json 2.1 什么是json 2.2 使用json 库 2.3 使用第三方库Demjson 一、Python XML 解析 1.1 什么是XML XML&#x…

Mouser中元件特性对比功能

搜索所需的元件&#xff0c;并点击比对 在比对界面里搜索所需比对的另外元器件&#xff0c;并比对3.得到的结果

从Vue到Postman全面验证API接口跨域问题

文章目录 1、前言2、跨域问题3、后端服务接口4、接口跨域测试4.1 Vue调用测试4.2 Postman测试 5、服务接口增加注解CrossOrigin解决跨域 1、前言 最近刚接手了一个新项目&#xff0c;业务还没了解全&#xff0c;让开发功能。做了俩接口&#xff0c;postman自测完能拿到数据就给…

影视解说中视频素材哪里找?

想做电影解说类视频&#xff0c;不知道哪里下载高清解说素材&#xff1f; 今天小编就带大家揭秘5大影视解说素材网站&#xff0c;赶紧进来看看吧&#xff01;&#xff5e; 1. 稻虎网 作为国内顶尖视频素材网站&#xff0c;稻虎素材网提供了丰富的电影解说视频素材&#xff0c;…

配置Mysql的慢查询日志

一、什么是Mysql慢查询日志 MySQL慢查询日志是MySQL数据库自带的一个功能&#xff0c;用于记录执行时间超过指定阈值的SQL语句&#xff0c;以便于后续的性能优化工作 帮助开发和DBA发现哪些SQL语句需要优化&#xff0c;在哪些地方需要修改&#xff0c;以提高数据库的性能 默认…