【unity实战】在Unity中使用有限状态机制作一个敌人AI

news2024/10/7 8:26:40

最终效果

在这里插入图片描述

文章目录

  • 最终效果
  • 前言
  • 有限状态机的主要作用和意义
  • 素材下载
  • 逻辑图
  • 敌人动画配置
  • 优雅的代码文件目录
  • 状态机代码
  • 定义敌人不同状态切换
  • 创建敌人
  • 效果
  • 更多的敌人
  • 参考
  • 源码
  • 完结

前言

有限状态机以前的我嗤之以鼻,现在的我逐帧分析。其实之前我就了解过有限状态机,但是奈何那时能力不够,并不能理解其中的奥秘,只觉得麻烦。直到我项目需要越来越多的去编写敌人的AI,大量的if else让我头晕目眩,各种状态的切换和调试耗费我大量的时间。于是我又重新查找一些状态机的教程进行深入学习。以下我我的学习记录,希望对你有帮助。

如果后续项目使用时存在任何问题我还会回来补充和调整,文章的代码我也会尽量保持完整分享,以便大家可以复制粘贴盗自己的项目中即可使用。

有限状态机的主要作用和意义

有限状态机(Finite State Machine,FSM)是一种在计算机科学和工程中常用的模型,用于描述对象或系统在有限状态集合中的行为和状态转换。它的主要作用和意义包括:

  1. 行为管理与控制: FSM通过定义有限数量的状态和状态之间的转换规则,可以有效管理和控制对象或系统的行为。每个状态代表对象可能处于的一种特定状态,例如待机、行走、攻击、受伤等,而状态之间的转换则定义了这些行为如何响应外部事件或条件变化。

  2. 简化复杂性: 将复杂的行为分解为简单的状态和状态转换,使得程序员可以更容易地理解和管理系统的行为逻辑。这种分解也有助于减少错误和提高代码的可维护性。

  3. 灵活性和扩展性: FSM可以根据具体需求进行灵活的定制和扩展。通过修改状态和状态转换规则,可以快速调整和扩展系统的行为,而无需大规模重构代码。

  4. 行为预测和调试: FSM的结构使得系统的行为预测变得相对容易,因为每个状态和转换的行为是明确定义的。这种结构也有助于调试和排查问题,因为可以更容易地追踪和理解系统在特定状态下的行为。

  5. 应用领域广泛: FSM不仅在游戏开发中常见,还在自动控制、工作流程管理、编程语言解析、通信协议等许多领域有着广泛的应用。其简单而强大的结构使得它成为许多复杂系统中行为管理的首选模型之一。

总之,有限状态机通过状态和状态转换的定义,提供了一种清晰且有效的方法来管理和控制对象或系统的复杂行为,为程序员和系统设计师提供了强大的工具,用于实现各种复杂的行为逻辑和控制流程。

素材下载

https://rvros.itch.io/animated-pixel-hero
https://jesse-m.itch.io/skeleton-pack

逻辑图

在这里插入图片描述

敌人动画配置

在这里插入图片描述

优雅的代码文件目录

在这里插入图片描述

状态机代码

新增StateType定义状态类型枚举

// 定义状态类型枚举
public enum StateType
{
    Idle, //待机
    Patrol, //巡逻
    Chase, //追击
    React, //反应
    Attack, //攻击
    Hit, //受击
    Death //死亡
}

新增Parameter,可序列化的参数类,存储了角色的各种状态参数和配置

// 可序列化的参数类,存储了角色的各种状态参数和配置
using System;
using UnityEngine;

[Serializable]
public class Parameter
{
    public int health;              // 健康值
    public float moveSpeed;         // 移动速度
    public float chaseSpeed;        // 追击速度
    public float idleTime;          // 空闲时间
    public Transform[] patrolPoints;    // 巡逻点数组
    public Transform[] chasePoints;     // 追击点数组
    [HideInInspector] public Transform target; // 目标对象
    public LayerMask targetLayer;   // 目标层
    public Transform attackPoint;   // 攻击点的位置
    public float attackArea;        // 攻击范围
    [HideInInspector] public Animator animator;       // 角色动画控制器
    [HideInInspector] public bool getHit;             // 是否被击中
    [HideInInspector] public AnimatorStateInfo animatorStateInfo;    // 动画状态信息
}

新增FSM有限状态机类

using System.Collections.Generic;
using UnityEngine;

// 有限状态机类
public class FSM : MonoBehaviour
{
    private IState currentState;        // 当前状态接口
    protected Dictionary<StateType, IState> states = new Dictionary<StateType, IState>();  // 状态字典,存储各种状态

    public Parameter parameter;     // 状态机参数

    protected virtual void Awake() {
    
    }

    protected virtual void OnEnable()
    {
        parameter.animator = transform.GetComponent<Animator>();  // 获取角色上的动画控制器组件
        TransitionState(StateType.Idle);    // 初始状态为Idle
        currentState.OnEnter();
    }

    void Update()
    {
        parameter.animatorStateInfo = parameter.animator.GetCurrentAnimatorStateInfo(0);// 获取当前动画状态信息

        currentState.OnUpdate();    // 每帧更新当前状态

        //TODO:用于测试 如果按下回车键,设置被击中状态为true
        if (Input.GetKeyDown(KeyCode.Return))
        {
            parameter.getHit = true;
        }
    }


    void FixedUpdate()
    {
        currentState.OnFixedUpdate();
    }

    // 状态转换方法
    public void TransitionState(StateType type)
    {
        if (currentState != null)
            currentState.OnExit();  // 如果当前状态不为空,调用退出方法

        currentState = states[type];    // 更新当前状态为指定类型的状态
        currentState.OnEnter();         // 调用新状态的进入方法
    }

    // 翻转角色朝向方法,使其朝向目标
    public void FlipTo(Transform target)
    {
        if (target != null)
        {
            if (transform.position.x > target.position.x)
            {
                transform.localScale = new Vector3(-1, 1, 1);    // 如果角色在目标左侧,翻转角色朝向为左
            }
            else if (transform.position.x < target.position.x)
            {
                transform.localScale = new Vector3(1, 1, 1);     // 如果角色在目标右侧,翻转角色朝向为右
            }
        }
    }

    public void Destroy(){
        Destroy(gameObject);
    }

    // 触发器进入事件,检测到玩家时设置目标为玩家
    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            parameter.target = other.transform;
        }
    }

    // 触发器离开事件,玩家离开时清空目标
    private void OnTriggerExit2D(Collider2D other)
    {
        if (other.CompareTag("Player"))
        {
            parameter.target = null;
        }
    }

    // 在Scene视图中绘制攻击范围的辅助图形
    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(parameter.attackPoint.position, parameter.attackArea);
    }
}

新增IState抽象基类,定义了所有状态类的基本结构

//抽象基类,定义了所有状态类的基本结构
public abstract class IState
{
    protected FSM manager;// 当前状态机
    protected Parameter parameter;// 参数
    public abstract void OnEnter();// 进入状态时的方法
    public abstract void OnUpdate();// 更新方法
    public abstract void OnFixedUpdate();// 固定更新方法
    public abstract void OnExit();// 退出状态时的方法
}

定义敌人不同状态切换

待机状态

using UnityEngine;

public class IdleState : IState
{
    private float timer;        // 计时器

    public IdleState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }

    public override void OnEnter()
    {
        parameter.animator.Play("Idle");
    }

    public override void OnUpdate()
    {
        timer += Time.deltaTime;    // 计时器累加

        // 如果被击中了,转换到受击状态
        if (parameter.getHit)
        {
            manager.TransitionState(StateType.Hit);
        }

        // 如果有目标且目标在追逐范围内,则转换到反应状态
        if (parameter.target != null &&
            parameter.target.position.x >= parameter.chasePoints[0].position.x &&
            parameter.target.position.x <= parameter.chasePoints[1].position.x)
        {
            manager.TransitionState(StateType.React);
        }

        // 如果达到空闲时间上限,则转换到巡逻状态
        if (timer >= parameter.idleTime)
        {
            manager.TransitionState(StateType.Patrol);
        }
    }

    public override void OnFixedUpdate()
    {
        
    }

    public override void OnExit()
    {
        timer = 0;    // 重置计时器
    }
}

巡逻状态,每次敌人到达巡逻点都会在原地观察一段时间

using UnityEngine;

public class PatrolState : IState
{
    private int patrolPosition;    // 当前巡逻点索引

    public PatrolState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }

    public override void OnEnter()
    {
        parameter.animator.Play("Walk");
    }

    public override void OnUpdate()
    {
        // 如果被击中了,转换到受击状态
        if (parameter.getHit)
        {
            manager.TransitionState(StateType.Hit);
        }

        // 如果有目标且目标在追逐范围内,则转换到反应状态
        if (parameter.target != null &&
            parameter.target.position.x >= parameter.chasePoints[0].position.x &&
            parameter.target.position.x <= parameter.chasePoints[1].position.x)
        {
            manager.TransitionState(StateType.React);
        }

        // 如果已经接近当前巡逻点,则转换到空闲状态
        if (Vector2.Distance(manager.transform.position, parameter.patrolPoints[patrolPosition].position) < .1f)
        {
            manager.TransitionState(StateType.Idle);
        }
    }
    
    public override void OnFixedUpdate()
    {
        // 朝向当前巡逻点
        manager.FlipTo(parameter.patrolPoints[patrolPosition]);
        
        // 移动到当前巡逻点
        manager.transform.position = Vector2.MoveTowards(manager.transform.position,
            parameter.patrolPoints[patrolPosition].position, parameter.moveSpeed * Time.deltaTime);
    }

    public override void OnExit()
    {
        patrolPosition++;    // 切换到下一个巡逻点

        // 如果超过巡逻点数组长度,循环回到第一个巡逻点
        if (patrolPosition >= parameter.patrolPoints.Length)
        {
            patrolPosition = 0;
        }
    }
}

反应状态

using UnityEngine;

public class ReactState : IState
{
    public ReactState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }

    public override void OnEnter()
    {
        parameter.animator.Play("React");
    }

    public override void OnUpdate()
    {
        // 如果被击中标志为true,转换到受击状态
        if (parameter.getHit)
        {
            manager.TransitionState(StateType.Hit);
        }

        // 如果动画播放进度超过95%,转换到追逐状态
        if (parameter.animatorStateInfo.normalizedTime >= 0.95f)
        {
            manager.TransitionState(StateType.Chase);
        }
    }

    public override void OnFixedUpdate()
    {
        
    }

    public override void OnExit()
    {

    }
}

追击状态

using UnityEngine;

public class ChaseState : IState
{
    // 构造函数
    public ChaseState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }

    public override  void OnEnter()
    {
        //TODO:如果有奔跑动画,当然切换为奔跑动画最好
        parameter.animator.Play("Walk");
    }

    public override void OnUpdate()
    {
        // 如果被击中了,转换到受击状态
        if (parameter.getHit)
        {
            manager.TransitionState(StateType.Hit);
        }

        // 如果目标不存在或者超出追逐范围,则转换到空闲状态
        if (parameter.target == null ||
            manager.transform.position.x < parameter.chasePoints[0].position.x ||
            manager.transform.position.x > parameter.chasePoints[1].position.x)
        {
            manager.TransitionState(StateType.Idle);
        }

        // 如果检测到攻击范围内有目标,则转换到攻击状态
        if (Physics2D.OverlapCircle(parameter.attackPoint.position, parameter.attackArea, parameter.targetLayer))
        {
            manager.TransitionState(StateType.Attack);
        }
    }

    
    public override void OnFixedUpdate()
    {
        manager.FlipTo(parameter.target);    // 面向目标
        // 向目标位置移动
        if (parameter.target != null)
        {
            manager.transform.position = Vector2.MoveTowards(manager.transform.position,
                parameter.target.position, parameter.chaseSpeed * Time.deltaTime);
        }
    }

    public override void OnExit()
    {
        
    }

}

攻击状态


public class AttackState : IState
{
    public AttackState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }
    public override void OnEnter()
    {
        parameter.animator.Play("Attack");
    }

    public override void OnUpdate()
    {
        if (parameter.getHit)
        {
            manager.TransitionState(StateType.Hit);
        }
        if (parameter.animatorStateInfo.normalizedTime >= .95f)
        {
            manager.TransitionState(StateType.Chase);
        }
    }

    public override void OnFixedUpdate()
    {

    }

    public override void OnExit()
    {

    }
}

受击状态

using UnityEngine;

public class HitState : IState
{
    public HitState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }

    public override void OnEnter()
    {
        parameter.animator.Play("Hit");
        parameter.health--;    // 减少角色生命值
    }

    public override void OnUpdate()
    {
        // 如果角色生命值小于等于0,转换到死亡状态
        if (parameter.health <= 0)
        {
            manager.TransitionState(StateType.Death);
        }

        // 如果动画播放进度超过95%,重新寻找玩家目标并转换到追逐状态
        if (parameter.animatorStateInfo.normalizedTime >= 0.95f)
        {
            parameter.target = GameObject.FindWithTag("Player").transform;    // 寻找标签为Player的目标

            manager.TransitionState(StateType.Chase);    // 转换到追逐状态
        }
    }

    public override void OnFixedUpdate()
    {

    }

    public override void OnExit()
    {
        parameter.getHit = false;    // 离开状态时重置受击标志
    }
}

死亡状态


using UnityEngine;

public  class DeathState : IState
{
    public DeathState(FSM manager)
    {
        this.manager = manager;
        this.parameter = manager.parameter;
    }
    public override void OnEnter()
    {
        parameter.animator.Play("Dead");
    }

    public override void OnUpdate()
    {
        // 如果动画播放进度超过95%,销毁敌人
        if (parameter.animatorStateInfo.normalizedTime >= 0.95f)
        {
            manager.Destroy();
        }
    }

    public override void OnFixedUpdate()
    {
        
    }

    public override void OnExit()
    {

    }

}

创建敌人

比如新增Skull 骷髅怪,继承FSM

public class Skull : FSM {
    protected override void Awake() {
        // 初始化各个状态,并添加到状态字典中
        states.Add(StateType.Idle, new IdleState(this));
        states.Add(StateType.Patrol, new PatrolState(this));
        states.Add(StateType.Chase, new ChaseState(this));
        states.Add(StateType.React, new ReactState(this));
        states.Add(StateType.Attack, new AttackState(this));
        states.Add(StateType.Hit, new HitState(this));
        states.Add(StateType.Death, new DeathState(this));  
    }
}

配置
在这里插入图片描述

效果

现在运行游戏,可以看到敌人现在可以发现玩家并进行追击,在进入攻击范围后攻击玩家,玩家从视野中消失或者超出追击范围后恢复到巡逻状态
在这里插入图片描述

我们很快的就搭建好了一个简单的敌人逻辑,而且代码也不显得杂乱,这就是使用有限状态机编写代码的好处,而且添加新的状态也很方便,只要在有关的状态中设置好切换条件,注册好新建的状态,然后编写自身的状态代码即可。

更多的敌人

其他敌人只要都继承这个FSM状态机即可实现代码的复用,比如我再创建不同类型的敌人哥布林

public class Goblin : FSM {
    protected override void Awake() {
        // 初始化各个状态,并添加到状态字典中
        states.Add(StateType.Idle, new GoblinIdleState(this));
        states.Add(StateType.Patrol, new GoblinPatrolState(this));
        states.Add(StateType.Chase, new GoblinChaseState(this));
        states.Add(StateType.React, new GoblinReactState(this));
        states.Add(StateType.Attack, new GoblinAttackState(this));
        states.Add(StateType.Hit, new GoblinHitState(this));
        states.Add(StateType.Death, new GoblinDeathState(this));  
    }
}

参考

https://www.bilibili.com/video/BV1k1421Z7g8
https://www.bilibili.com/video/BV1zf4y1r7FJ
https://www.bilibili.com/video/BV1xp4y137Xr

源码

https://gitcode.net/unity1/fsm
在这里插入图片描述

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

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

相关文章

晚上睡觉要不要关路由器?一语中的

前言 前几天小白去了一个朋友家&#xff0c;有朋友说&#xff1a;路由器不关机的话会影响睡眠吗&#xff1f; 这个影响睡眠嘛&#xff0c;确实是会的。毕竟一时冲浪一时爽&#xff0c;一直冲浪一直爽……刷剧刷抖音刷到根本停不下来&#xff0c;肯定影响睡眠。 所以晚上睡觉要…

PCL 点云最小图割(前景、背景点云提取)

点云最小图割 一、概述1.1 概念1.2 算法原理二、代码示例三、运行结果🙋 结果预览 一、概述 1.1 概念 最小图割算法(pcl::MinCutSegmentation):是一种基于图论的对象分割方法,主要用于点云数据的处理和分析。该算法将点云数据表示为一个图结构,其中点云中的点作为图的节…

【SkiaSharp绘图14】SKCanvas方法详解(三)URL注释、按顶点绘制、 是否裁切区域之外、旋转、缩放、倾斜、平移、保存/恢复画布

文章目录 SKCanvas方法DrawUrlAnnotation 绘制URL注释DrawVertices 按顶点绘制Flush 立即绘制QuickReject 判断区域是否在裁切区域之外ResetMatrix重置矩阵Restore、RestoreToCountRotateDegrees按角度旋转画布RotateRadians按弧度旋转画布SaveLayer保存并新建图层Scale 缩放画…

二叉树(2)

二叉树的销毁 分为三个部分的销毁&#xff1a;根节点&#xff0c;左子树和右子树 void TreeDestory(BTNode* root) {if(rootNULL)return;TreeDestory(root->left);TreeDestory(root->right);free(root);rootNULL; }层序遍历&#xff08;上一层带下一层&#xff09; ty…

nginx如何解决惊群效应

什么是惊群效应 惊群效应&#xff08;thundering herd&#xff09;是指多进程&#xff08;多线程&#xff09;在同时阻塞等待同一个事件的时候&#xff08;休眠状态&#xff09;&#xff0c;如果等待的这个事件发生&#xff0c;那么他就会唤醒等待的所有进程&#xff08;或者线…

web权限到系统权限 内网学习第一天 权限提升 使用手工还是cs???msf可以不??

现在开始学习内网的相关的知识了&#xff0c;我们在拿下web权限过后&#xff0c;我们要看自己拿下的是什么权限&#xff0c;可能是普通的用户权限&#xff0c;这个连添加用户都不可以&#xff0c;这个时候我们就要进行权限提升操作了。 权限提升这点与我们后门进行内网渗透是乘…

用AI,每天创作200+优质内容,2分钟教会你操作!

前段时间发布了这篇“寻找爆款文案及标题的9大渠道&#xff0c;直接搬运都能搞流量&#xff01;”&#xff0c;里面我讲到如何寻找爆款标题。最近不少朋友问我&#xff0c;如何创作这个标题相关的内容。 多数平台都有风控规则&#xff0c;有些平台内容也会有字数要求。为了让大…

【D3.js in Action 3 精译】1.2.2 可缩放矢量图形(三)

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知 1.2.1 HTML 与 DOM1.2.2 SVG - 可缩放矢量图形 ✔️ 第一部分第二部分【第三部分】✔️ 1.2.3 Canvas 与 WebGL&#xff08;精译中 ⏳&#xff09;1.2.4 C…

10.8K star!史上最强Web应用防火墙雷池WAF

长亭雷池SafeLine是长亭科技耗时近 10 年倾情打造的WAF(Web Application Firewall)&#xff0c; 一款敢打出口号 “不让黑客越雷池一步” 的 WAF&#xff0c;愿称之为史上最强的一款Web应用防火墙&#xff0c;足够简单、足够好用、足够强的免费且开源的 WAF&#xff0c;基于业…

全球首款商用,AI为视频自动配音配乐产品上线

近日&#xff0c;海外推出了一款名为Resona V2A的产品&#xff0c;这是全球首款商用视频转音频 (V2A) 技术产品。这项突破性技术利用AI&#xff0c;仅凭视频数据即可自动生成高质量、与上下文相关的音频&#xff0c;包括声音设计、音效、拟音和环境音&#xff0c;为电影制作人、…

单向链表结构

链表结构简介 链表结构是一种用比较特殊的数据结构类型&#xff0c;它也是线性数据结构中的一种&#xff0c;但是与栈结构等线性数据结构不同&#xff0c;它的内部结构并不是一个简单的存储空间&#xff0c;而是一个带有指向性质的单元。要理解链表结构要弄清楚两个问题&#x…

react_后台管理_项目

目录 1.运行项目 2. 项目结构 ①项目顶部导航栏 ②项目左侧导航栏 ③主页面-路由切换区 本项目使用的是 reacttsscss 技术栈。 1.运行项目 在当前页面顶部下载本项目&#xff0c;解压后使用编辑器打开&#xff0c;然后再终端输入命令&#xff1a; npm i 下载依赖后&am…

使用Python绘制动态螺旋线:旋转动画效果

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame绘制螺旋线函数主循环 完整代码 引言 螺旋线是一个具有美学和数学魅力的图形。通过编程&#xff0c;我们可以轻松创建动态旋转的螺旋线动画。在这篇博客中&#xff0c;我们将使用Python和Pygame库来实现…

【python脚本】批量检测sql延时注入

文章目录 前言批量检测sql延时注入工作原理脚本演示 前言 SQL延时注入是一种在Web应用程序中利用SQL注入漏洞的技术&#xff0c;当传统的基于错误信息或数据回显的注入方法不可行时&#xff0c;例如当Web应用进行了安全配置&#xff0c;不显示任何错误信息或敏感数据时&#x…

解决卡顿发热,超帧技术焕发中重载游戏动力

近几年&#xff0c;中国手游市场规模不断扩大&#xff0c;开发者通过在画面、玩法等方面的持续创新和打磨&#xff0c;推出更加精品化的产品。然而愈发精美的画质和复杂的玩法&#xff0c;也给硬件带来超高的负载&#xff0c;导致玩家在游戏过程中&#xff0c;频繁出现掉帧卡顿…

动态规划算法,完全零基础小白教程!不是计算机的都能学会!万字吐血详解。

目录 一、动态规划算法概念 题一 1、算法解析 1&#xff09;确定状态&#xff1a; ​2&#xff09;状态转移方程&#xff1a; ​3&#xff09;初始化&#xff1a; 4&#xff09;填表顺序&#xff1a; 5&#xff09;返回值&#xff1a; 2、代码 题二 1、算法解析 1、确…

你喜欢波段交易吗?

波段交易的核心在于精准捕捉市场中的长期趋势波动&#xff0c;以实现更为稳健的收益。与剥头皮和日内交易不同&#xff0c;波段交易者更倾向于持有交易头寸数日乃至数周&#xff0c;以更宽广的视角把握市场动态。 这种交易方式的优势在于&#xff0c;它降低了对即时市场反应的…

C - Popcorn(abs358)

题意&#xff1a;有n个摊子&#xff0c;m个爆米花&#xff0c;想花费最少去的店铺买到所有的口味的爆米花&#xff0c;找到每一列都为‘o’的最少行数。 分析&#xff1a;用dfs寻找最少路径 #include<bits/stdc.h> using namespace std; typedef long long ll; char x;…

【面试系列】AI研究员高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…

内网渗透第四天!!!冲冲冲!!怎么绕过uac以及你会all劫持???不安全的服务路径以及服务权限,你会吗???

在第三天我们简单的说了一下绕过uac&#xff0c;但是我们使用的msf模块ask要对方管理员跟我们一起来进行操作&#xff0c;才可以进行提权的操作&#xff0c;这点就限制住了我们。我们今天来讲一下不用钓鱼的绕过的操作。 绕过uac&#xff1a; 使用uacme项目和msf联动来进行绕过…