麦田物语第二十天

news2024/9/25 19:22:26

系列文章目录

麦田物语第二十天


文章目录

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


一、构建地图信息系统

我们上一节课已经做好了鼠标的显示,这节课需要构建地图的一些信息,例如:可挖坑,可丢弃物品等地区。我们点击地图时,只有鼠标位于规定的地区时才会出现相应的鼠标图片。
怎么我们怎么编辑才能让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;

    //鼠标检测
    //屏幕坐标切换为世界坐标就是需要调用mainCamera
    private Camera mainCamera;

    //将屏幕坐标转化为网格坐标需要拿到Grid,切换场景时要切换成当前场景的Grid
    private 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;
        }
        else
            return 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.coloradmin.cn/o/1989042.html

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

相关文章

Altium designer学习笔记02 - 原理图库的创建与绘制

原理图库的创建与绘制 1. 元件符号的概述2. 单部件元件符号的绘制-实例电容3.子件原件符号的创建绘制&#xff08;示例 放大器&#xff09;4.已存在原理图直接生成元件库5.元件库的拷贝6.元件的检查与报告 1. 元件符号的概述 元件符号是元件在原理图上的表现形式&#xff0c;主…

Java语言程序设计——篇十二

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

winform chart标记

下图为某个点的值大于25即标记为白色。 代码&#xff1a; // 设置曲线的样式Series series chart1.Series[1];// 画样条曲线&#xff08;Spline&#xff09; 散点图Pointseries.ChartType SeriesChartType.Spline;// 线宽2个像素series.BorderWidth 2;// 线的颜色&…

K8S Helm

简述 Helm 是 Kubernetes 的开源包管理器。它提供了提供、共享和使用为 Kubernetes 构建的软件的能力。它允许开发者定义、‌打包、‌发布和管理Kubernetes应用资源&#xff0c;‌类似于Linux下的apt或yum包管理器。‌Helm3的架构主要包括Helm客户端、‌Chart仓库以及Kubernet…

程序员窃喜!卡了大模型脖子的Json输出,OpenAI终于做到了100%正确

OpenAI上次提到JSON模式的概念&#xff0c;还是在去年的DevDay上。那是ChatGPT第一次拥抱JSON模式。 但这个功能可以说是饱受诟病。 经常遇到模型不遵循指令&#xff0c;不按照你想要的格式输出&#xff0c;即使在 prompt 中明确说了要按照指定格式&#xff08;比如Json、XML&…

【Java】字符/字符串转整数 常用的三个方法

前言&#xff1a; 做Oj题时&#xff0c;偶尔需要用到&#xff0c;久了不用&#xff0c;用到就得查一下&#xff0c;遂总结一篇用法&#xff0c;加深记忆。 目录 方法1&#xff1a;使用字符的ASCII值&#xff08;字符&#xff09; 方法2&#xff1a;使用Character.getNumeri…

【GCC】结合GPT4 延迟梯度学习1:公式推导及理论分析

大神的分析 本文主要借鉴。【TWCC 】基于gpt和python简化分析webrtc拥塞控制论文: Analysis and Design of the Google Congestion Contro for Web Real-time Communication (WebRTC)感觉应该学习好理论后再进行python 分析:【gcc】基于gpt和python的流程和延迟梯度分析另外:…

html+css+js前端作业qq音乐官网5个页面 带js

htmlcssjs前端作业qq音乐官网5个页面 带js 有轮播图&#xff0c;tab切换等多种效果 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编…

Flink 实时数仓(九)【DWS 层搭建(三)交易域汇总表创建】

前言 今天立秋&#xff0c;任务是完成 DWS 剩余的表&#xff0c;不知道今天能不能做完&#xff0c;欲速则不达&#xff0c;学不完就明天继续&#xff0c;尽量搞懂每一个需求&#xff1b; 1、交易域下单各窗口汇总表 任务&#xff1a;从 Kafka 订单明细主题读取数据&#xff0…

【Linux-WMware Tools安装失败“segmentation fault”解决方法】

VMware版本&#xff1a;17 Ubuntu版本: 22.04 安装常规办法&#xff0c;通过vmware安装Tool&#xff0c;安装显示报错&#xff1a;“segmentation fault”&#xff0c;查了下可能是tool和ubuntu版本不兼容导致的。解决办法&#xff1a;通过命令行逐次安装。 1、sudo apt insta…

PostgreSQL(二十五)PG_FDW的使用

目录 一、FDW的简介与特性 二、pg_fdw的部署与使用 1、编译postgres_fdw 2、添加postgres_fdw 3、创建FDW服务器 ​4、授权并创建用户映射 5、客户端创建FDW测试表 6、访问外部表 ​7、可能出现的问题 三、FDW的执行原理 1、PG-PG访问过程描述 2、PG-PG访问过程查看…

一部分优化算法

一、优化问题 1、优化目标 &#xff08;1&#xff09;优化和深度学习的目标是根本不同的。前者主要关注的是最小化目标&#xff0c;后者则关注在给定有限数据量的情况下寻找合适的模型。 &#xff08;2&#xff09;优化算法的目标函数通常是基于训练数据集的损失函数&#x…

springboot 定义类导入爆红,@Autowried自动注入失败

springboot 定义类导入爆红&#xff0c;Autowried自动注入失败 根据提供的异常信息&#xff0c;分析如下&#xff1a; 异常起因&#xff1a;UnsatisfiedDependencyException 表示在创建名为 ‘a1001Service’ 的 bean 时存在依赖问题&#xff0c;具体是在字段 ‘a1001Mapper’ …

算法日记day 32(贪心之划分字母区间|合并区间|单调递增的数字|监控二叉树)

一、划分字母区间 题目&#xff1a; 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每…

nginx 405错误是什么意思

405错误&#xff1a;方法不被允许 当Web服务器收到一个它不支持的HTTP请求方法时&#xff0c;就会返回405错误。 原因 405错误通常是由于客户端发出了不兼容或不支持的HTTP请求方法。例如&#xff0c;客户端可能请求一个只能通过GET方法访问的资源&#xff0c;但使用了POST方…

C代码做底层及Matlab_SimuLink做应用层设计单片机程序

前言:SimuLink工具极其强大,但是能直接支持单片机自主开发的很少,造成这个问题的原因主要是我们使用的芯片底层多是C代码工程,芯片厂家也只提供C代码库,很少能提供SimuLink的支持库,即使提供也不是很不完善,如NXP的一些芯片提供的SimuLink库不含盖高级应用,再比如意法半…

视创云展:轻松构建出独一无二的元宇宙空间

视创云展作为一款前沿的元宇宙数字营销平台&#xff0c;集成了多项核心技术&#xff0c;旨在为用户提供低门槛、高效能的元宇宙体验与创作工具。其核心技术主要包括&#xff1a; 1、低门槛、模块化&#xff0c;3D场景创作工具 视创云展集成了海量的元宇宙场景模板&#xff0c;…

SpringBoot中如何自定义自己的过滤器Filter(简易版)

本文不再说SpringMVC中的写法&#xff0c;毕竟现在项目都是SpringBoot&#xff0c;我们还是尽量使用SpringBoot的写法&#xff0c;首先了解一下Filter。 说白了&#xff0c;就是在请求到达服务器之前进行拦截&#xff0c;一般使用场景是拦截登录进行权限校验&#xff0c;当然一…

跟李沐学AI:GoogLeNet含并行连结的网络

Inception块 GoogleNet中的基本卷积块&#xff0c;从4个路径从不同层面抽取信息&#xff0c;然后再输出通道维合并。 数据输入后共有四条路径&#xff1a;第一个路径为1x1的卷积层&#xff1b;第二个路径先用1x1的卷积层修改通道数&#xff0c;在输入到3x3的卷积层&#xff0c…

腾讯云AI代码助手助力软件开发体验分享

引言 现在&#xff0c;AI工具在软件开发中变得越来越重要&#xff0c;它们能显著提升效率和代码质量。本文就来分享一下我用腾讯云AI代码助手的经历&#xff0c;看看它是怎么在开发中帮了大忙的。 开发环境介绍 这次的项目用的是JavaScript&#xff0c;开发环境是Windows 10…