Unity教程(二十二)技能系统 分身技能

news2025/3/19 14:43:10

Unity开发2D类银河恶魔城游戏学习笔记

Unity教程(零)Unity和VS的使用相关内容
Unity教程(一)开始学习状态机
Unity教程(二)角色移动的实现
Unity教程(三)角色跳跃的实现
Unity教程(四)碰撞检测
Unity教程(五)角色冲刺的实现
Unity教程(六)角色滑墙的实现
Unity教程(七)角色蹬墙跳的实现
Unity教程(八)角色攻击的基本实现
Unity教程(九)角色攻击的改进

Unity教程(十)Tile Palette搭建平台关卡
Unity教程(十一)相机
Unity教程(十二)视差背景

Unity教程(十三)敌人状态机
Unity教程(十四)敌人空闲和移动的实现
Unity教程(十五)敌人战斗状态的实现
Unity教程(十六)敌人攻击状态的实现
Unity教程(十七)敌人战斗状态的完善

Unity教程(十八)战斗系统 攻击逻辑
Unity教程(十九)战斗系统 受击反馈
Unity教程(二十)战斗系统 角色反击

Unity教程(二十一)技能系统 基础部分
Unity教程(二十二)技能系统 分身技能
Unity教程(二十三)技能系统 投掷技能


如果你更习惯用知乎
Unity开发2D类银河恶魔城游戏学习笔记目录


文章目录

  • Unity开发2D类银河恶魔城游戏学习笔记
  • 前言
  • 一、概述
  • 二、预制件(Prefab)
    • (1)预制件介绍
    • (2)创建分身预制件
  • 三、分身技能的实现
    • (1)分身技能的创建
    • (2)分身技能的实现
    • (3)分身的消失与销毁
    • (4)补充:将预制件Clone实例化到当前位置
  • 四、分身攻击的实现
    • (1)攻击动画
    • (2)分身攻击的实现
    • (3)分身攻击方向
  • 总结 完整代码
    • PlayerDashState.cs
    • Clone_Skill.cs
    • Clone_Skill_Controller.cs
    • SkillManager.cs
    • Player.cs


前言

本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记,如有错误,欢迎指正。

本节实现角色分身技能。

Udemy课程地址

对应视频:
Clone Creating Ability
Clone’s Attack


一、概述

本节实现分身技能。
实现解锁技能后,在角色冲刺时产生一个分身,分身会攻击距离最近的一个敌人。
在这里插入图片描述

二、预制件(Prefab)

(1)预制件介绍

Unity的预制件相当于创建一个模板,使得游戏对象作为可重用资源。可以用这个模板在场景中创建新的预制件实例。
详细内容可见Unity官方手册预制件

首先是预制件的创建。
预制件的创建很简单,将一个游戏对象从 Hierarchy 窗口拖入 Project 窗口就可以了。
在这里插入图片描述


预制件的实例化。
最简单的是将预制件资源从 Project 视图拖动到 Hierarchy 或 Scene 视图,创建实例。
在这里插入图片描述

也可通过脚本进行实例化。使用Object的Instantiate函数。


Object Instantiate (Object original);

Object Instantiate (Object original, Transform parent);

Object Instantiate (Object original, Transform parent, bool instantiateInWorldSpace);

Object Instantiate (Object original, Vector3 position, Quaternion rotation);

Object Instantiate (Object original, Vector3 position, Quaternion rotation, Transform parent);

参数含义如下表:

参数含义
original要复制的现有对象(如预制件)
position新对象的位置
rotation新对象的方向
parent将指定给新对象的父对象
instantiateInWorldSpace分配父对象时,传递 true 可直接在世界空间中定位新对象。
传递 false 可相对于其新父项来设置对象的位置。

预制件的编辑。 可以在预制件模式下编辑预制件。这种情况可以单独或在上下文中编辑预制件资源。

在这里插入图片描述

在这里插入图片描述
也可以在实例中编辑后覆盖预制体。使用Overrides应用和还原覆盖。

在这里插入图片描述

(2)创建分身预制件

将玩家精灵表第一帧拖入层次面板中并重命名为Clone。

在这里插入图片描述
在这里插入图片描述

创建动画控制器Clone_AC

在这里插入图片描述
将Clone_AC挂载到Clone下面

在这里插入图片描述
这时我们发现Clone的图像被遮挡了。

在这里插入图片描述
调整SpriteRenderer中的层次顺序,使Clone位于敌人之前,玩家之后。

在这里插入图片描述
给Clone添加动画,这里我们不需要重新建立动画,只需要把原来Player的动画复用。
打开Clone的Aniamtor面板,将PlayerIdle拖入作为默认状态,再拖入PlayerAttack1、PlayerAttack2、PlayerAttack3。

在这里插入图片描述
创建Prefabs文件夹存放预制件。

在这里插入图片描述
将层次面板中的Clone拖入文件夹,预制件创建完成。

在这里插入图片描述
把层次面板中的Clone删除,后续我们用到它时会在脚本中创建。

三、分身技能的实现

分身技能Clone_Skill继承自Skill基类。我们要实现按下冲刺键后,在玩家冲刺的位置创建一个分身,因此我们需要在PlayerDashState开始时传入Player的位置创建分身。

实现时还要创建一个分身技能的控制器Clone_Skill_Controller,将它挂载到Clone预制件下,控制分身的位置。这里让人容易疑惑,为什么要大费周章再创建一个控制器脚本。个人理解,更多是因为马上要实现的分身攻击,攻击时需要触发动画事件,因此必须有个脚本挂在Clone预制体下面。这里就把控制分身位置的功能也写进去了。

此外,还可在实例化时就指定分身位置,这种形式也会在下面写一下。

(1)分身技能的创建

在Scripts文件夹中创建Skills文件夹存放技能相关脚本。
在这里插入图片描述
创建分身技能脚本Clone_Skill,它继承自Skill基类,将它挂到技能管理器下。

在这里插入图片描述
在这里插入图片描述

在技能管理器脚本中创建分身技能并赋值。

    public Clone_Skill clone { get; private set; }
    
    private void Start()
    {
        dash = GetComponent<Dash_Skill>();
        clone = GetComponent<Clone_Skill>();
    }

(2)分身技能的实现

创建分身技能控制器Clone_Skill_Controller,创建函数SetupClone设置分身信息。
在这里插入图片描述
双击预制件Clone,在面板中点击Add Component添加组件。
注意:脚本是添加在预制件上的。

在这里插入图片描述

在Clone_Skill_Controller中添加设置分身信息的函数。

//Clone_Skill_Controller:分身技能控制器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Clone_Skill_Controller : MonoBehaviour
{
    //设置分身信息
    public void SetupClone(Transform _newTransform)
    {
        transform.position = _newTransform.position;
    }
}

在Clone_Skill脚本中添加实例化预制体的函数,并调用SetupClone设置分身信息。
//Clone_Skill:分身技能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Clone_Skill : Skill
{
    [SerializeField] private GameObject clonePerfab;

    public void CreateClone(Transform _clonePosition)
    {
        GameObject newClone = Instantiate(clonePerfab);

        newClone.GetComponent<Clone_Skill_Controller>().SetupClone(_clonePosition);
    }
}

为预制体赋值
在这里插入图片描述


在Player中创建SkillManager方便管理。
    public SkillManager skill;

    // 设置初始状态
    protected override void Start()
    {
        base.Start();

        skill = SkillManager.instance;
        
        StateMachine.Initialize(idleState);
    }

接着在PlayerDashState的中调用CreateClone,使得冲刺后在玩家位置创建一个分身。

    //进入状态
    public override void Enter()
    {
        base.Enter();

        player.skill.clone.CreateClone(player.transform);

        //设置冲刺持续时间
        stateTimer = player.dashDuration;
    }

效果如下:
在这里插入图片描述

基本功能已经实现,但有个很明显的问题,创建的分身没有回收。

(3)分身的消失与销毁

此技能需要让分身持续一段时间后逐渐消失,可以使clone预制件的图像随时间透明度逐渐降低实现,最后透明度降到0时销毁分身。这需要用到计时器,而且要设置分身持续的时长和消失的速度。

我们把经常需要变动修改的变量放在CloneSkill中方便管理,这里将分身持续时长cloneDuration放在里面。

public class Clone_Skill : Skill
{
    [Header("Clone Info")]
    [SerializeField] private GameObject clonePerfab;
    [SerializeField] private float cloneDuration;

    public void CreateClone(Transform _clonePosition)
    {
        GameObject newClone = Instantiate(clonePerfab);

        newClone.GetComponent<Clone_Skill_Controller>().SetupClone(_clonePosition, cloneDuration);
    }
}

在Clone_Skill_Controller中设置计时器,实现图像透明度递减,并在透明度为0时销毁对象。
public class Clone_Skill_Controller : MonoBehaviour
{
    private SpriteRenderer sr;
    [SerializeField] private float colorLosingSpeed;

    private float cloneTimer;

    private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
    }

    private void Update()
    {
        cloneTimer -= Time.deltaTime;

        if(cloneTimer < 0)
        {
            sr.color = new Color(1, 1, 1, sr.color.a - Time.deltaTime * colorLosingSpeed);

            if (sr.color.a <= 0)
                Destroy(gameObject);
        }
    }

    //设置分身信息
    public void SetupClone(Transform _newTransform, float _cloneDuration)
    {
        transform.position = _newTransform.position;
        cloneTimer = _cloneDuration;
    }
}

设置合适的技能持续时间和消失速度
在这里插入图片描述
在这里插入图片描述
效果如下:
在这里插入图片描述

(4)补充:将预制件Clone实例化到当前位置

只需在实例化时直接传入位置参数即可,其他函数要随着做一些更改。
在Clone_Skill中

    public void CreateClone(Transform _clonePosition)
    {
        GameObject newClone = Instantiate(clonePerfab,_clonePosition);

        newClone.GetComponent<Clone_Skill_Controller>().SetupClone(cloneDuration);
    }

在Clone_Skill_Controller中

    //设置分身信息
    public void SetupClone(float _cloneDuration)
    {
        cloneTimer = _cloneDuration;
    }

四、分身攻击的实现

(1)攻击动画

进入预制件Clone的编辑,在Animator中创建空状态用于空闲和攻击状态过渡。创建Int型变量AttackNumber用于确定攻击段数。
在这里插入图片描述
连接playerIdle与Empty状态,当AttackNumber>0时,进入Empty。
PlayerIdle->Empty, 加条件变量并更改设置

在这里插入图片描述
分别连接Empty和三个攻击状态,在AttackNumber等于1时进入playerAttack1,同理等于2、3时分别进入其他两个状态
在这里插入图片描述

(2)分身攻击的实现

在技能树中分身攻击技能解锁后才可使用,这里我们先实现功能部分,所以先在Clone_Skill中添加一个变量canAttack用于测试。

public class Clone_Skill : Skill
{
    [Header("Clone Info")]
    [SerializeField] private GameObject clonePerfab;
    [SerializeField] private float cloneDuration;
    [Space]
    [SerializeField] private bool canAttack;

    public void CreateClone(Transform _clonePosition)
    {
        GameObject newClone = Instantiate(clonePerfab);

        newClone.GetComponent<Clone_Skill_Controller>().SetupClone(_clonePosition, cloneDuration,canAttack);
    }
}

当解锁分身攻击技能时,在Clone_Skill_Controller中实现分身攻击的设置。

设置Animator中的条件变量AttackNumber为1-3中随机一个数,播放相应攻击动画。
注意:在Unity中Random.Range(a, b) 生成的是 [ a, b ) 区间内的整数。


    private Animator anim;
    
    private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }
    
    //设置分身信息
    public void SetupClone(Transform _newTransform, float _cloneDuration, bool _canAttack)
    {
        if (_canAttack)
            anim.SetInteger("AttackNumber", Random.Range(1, 4));
        transform.position = _newTransform.position;
        cloneTimer = _cloneDuration;
    }
    

效果如下:
在这里插入图片描述

现在分身攻击是连续不断地没有结束攻击的部分,我们可以复用攻击动画以前的事件实现。
在这里插入图片描述
参照PlayerAnimationTriggers里函数的写法,在Clone_Skill_Controller中添加AnimationTrigger和AttackTrigger两个函数,分别用于结束攻击和触发攻击效果。
AnimatonTrigger会将计时器设置为小于0的数,相当于分身技能持续时间直接结束,开始逐渐消失。
AttackTrigger与玩家攻击的实现逻辑一样,检查攻击范围内的敌人,造成伤害效果。
添加如下代码:

    [SerializeField] private Transform attackCheck;
    [SerializeField] private float attackCheckRadius = 0.8f;


    private void AnimationTrigger()
    {
        cloneTimer = -0.1f;
    }

    private void AttackTrigger()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(attackCheck.position, attackCheckRadius);

        foreach (var hit in colliders)
        {
            if (hit.GetComponent<Enemy>() != null)
                hit.GetComponent<Enemy>().Damage();
        }
    }

在Clone下创建一个空物体并重命名为attackCheck,将它移动到分身前方合适的位置。然后用它为变量attackCheck赋值。
进行修改时可以在预制体的编辑里,也可以将预制体拖到场景中修改完再覆盖原来的预制体。
在这里插入图片描述
在这里插入图片描述
做完这些你会发现分身可以攻击敌人了,但攻击还是接连不断。
在这里插入图片描述
因为这次在Animator中没有将PlayerAttack连到Exit状态,没有退出,所以动画会循环播放。
将三个攻击动画的lLoop Time勾掉就可以了。

在这里插入图片描述
现在每次冲刺产生分身就是正常攻击一次了。
在这里插入图片描述

(3)分身攻击方向

最后一个需要解决的问题是分身面向现在是固定朝右的,我们需要再添加一个让分身朝向最近敌人攻击的功能。
在分身一定范围内检测敌人,比较后选取离分身最近的一个。如果它在分身左侧,则翻转分身让它面向左侧。

    private Transform closestEnemy;
    
    //设置分身信息
    public void SetupClone(Transform _newTransform, float _cloneDuration, bool _canAttack)
    {
        if (_canAttack)
            anim.SetInteger("AttackNumber", Random.Range(1, 4));

        transform.position = _newTransform.position;
        cloneTimer = _cloneDuration;

        FaceClosestTarget();
    }

    private void FaceClosestTarget()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 25);

        float closestDistace = Mathf.Infinity;

        foreach(var hit in colliders)
        {
            if (hit.GetComponent<Enemy>() != null)
            {
                float distanceToEnemy = Vector2.Distance(transform.position, hit.transform.position);

                if (distanceToEnemy < closestDistace)
                {
                    closestDistace = distanceToEnemy;
                    closestEnemy = hit.transform;
                }
            }
        }

        if(closestEnemy != null)
        {
            if (closestEnemy.position.x < transform.position.x)
                transform.Rotate(0, 180, 0);
        }

    }

效果如下:

在这里插入图片描述

总结 完整代码

PlayerDashState.cs

添加分身的创建。

//PlayerDashState:冲刺状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerDashState : PlayerState
{
    //构造函数
    public PlayerDashState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName)
    {
    }

    //进入状态
    public override void Enter()
    {
        base.Enter();

        player.skill.clone.CreateClone(player.transform);

        //设置冲刺持续时间
        stateTimer = player.dashDuration;
    }

    //退出状态
    public override void Exit()
    {
        base.Exit();
    }

    //更新
    public override void Update()
    {
        base.Update();

        //切换滑墙状态
        if(!player.isGroundDetected() && player.isWallDetected()) 
            stateMachine.ChangeState(player.wallSlideState);

        //设置冲刺速度
        player.SetVelocity(player.dashDir * player.dashSpeed, 0);


        //切换到空闲状态
        if (stateTimer < 0)
            stateMachine.ChangeState(player.idleState);
    }
}

Clone_Skill.cs

创建分身,添加经常需要在面板上更改的相关变量。

//Clone_Skill:分身技能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Clone_Skill : Skill
{
    [Header("Clone Info")]
    [SerializeField] private GameObject clonePerfab;
    [SerializeField] private float cloneDuration;
    [Space]
    [SerializeField] private bool canAttack;

    public void CreateClone(Transform _clonePosition)
    {
        GameObject newClone = Instantiate(clonePerfab);

        newClone.GetComponent<Clone_Skill_Controller>().SetupClone(_clonePosition, cloneDuration,canAttack);
    }
}

Clone_Skill_Controller.cs

设置分身信息,实现分身逐渐消失,实现分身攻击,将分身改为面向最近的敌人。

//Clone_Skill_Controller:分身技能控制器
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

public class Clone_Skill_Controller : MonoBehaviour
{
    private SpriteRenderer sr;
    private Animator anim;
    [SerializeField] private float colorLosingSpeed;

    private float cloneTimer;
    [SerializeField] private Transform attackCheck;
    [SerializeField] private float attackCheckRadius = 0.8f;
    private Transform closestEnemy;

    private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }

    private void Update()
    {
        cloneTimer -= Time.deltaTime;

        if(cloneTimer < 0)
        {
            sr.color = new Color(1, 1, 1, sr.color.a - Time.deltaTime * colorLosingSpeed);

            if (sr.color.a <= 0)
                Destroy(gameObject);
        }
    }

    //设置分身信息
    public void SetupClone(Transform _newTransform, float _cloneDuration, bool _canAttack)
    {
        if (_canAttack)
            anim.SetInteger("AttackNumber", Random.Range(1, 4));

        transform.position = _newTransform.position;
        cloneTimer = _cloneDuration;

        FaceClosestTarget();
    }

    private void AnimationTrigger()
    {
        cloneTimer = -0.1f;
    }

    private void AttackTrigger()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(attackCheck.position, attackCheckRadius);

        foreach (var hit in colliders)
        {
            if (hit.GetComponent<Enemy>() != null)
                hit.GetComponent<Enemy>().Damage();
        }
    }

    private void FaceClosestTarget()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 25);

        float closestDistance = Mathf.Infinity;

        foreach(var hit in colliders)
        {
            if (hit.GetComponent<Enemy>() != null)
            {
                float distanceToEnemy = Vector2.Distance(transform.position, hit.transform.position);

                if (distanceToEnemy < closestDistance)
                {
                    closestDistance = distanceToEnemy;
                    closestEnemy = hit.transform;
                }
            }
        }

        if(closestEnemy != null)
        {
            if (closestEnemy.position.x < transform.position.x)
                transform.Rotate(0, 180, 0);
        }

    }
}

SkillManager.cs

创建分身技能。

//SkillManager:玩家管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SkillManager : MonoBehaviour
{
    public static SkillManager instance;

    public Dash_Skill dash { get; private set; }
    public Clone_Skill clone { get; private set; }

    private void Awake()
    {
        if (instance != null && instance != this)
        {
            Destroy(this.gameObject);
        }
        else
        {
            instance = this;
        }
    }

    private void Start()
    {
        dash = GetComponent<Dash_Skill>();
        clone = GetComponent<Clone_Skill>();
    }
}

Player.cs

创建并初始化技能管理器。

//Player:玩家
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : Entity
{
    [Header("Attack details")]
    public Vector2[] attackMovement;
    public float counterAttackDuration = 0.2f;

    public bool isBusy { get; private set; }

    [Header("Move Info")]
    public float moveSpeed = 8f;
    public float jumpForce = 12f;


    [Header("Dash Info")]
    public float dashSpeed=25f;
    public float dashDuration=0.2f;
    public float dashDir { get; private set; }


    public SkillManager skill;


    #region 状态
    public PlayerStateMachine StateMachine { get; private set; }
    public PlayerIdleState idleState { get; private set; }
    public PlayerMoveState moveState { get; private set; }
    public PlayerJumpState jumpState { get; private set; }
    public PlayerAirState airState { get; private set; }
    public PlayerDashState dashState { get; private set; }
    public PlayerWallSlideState wallSlideState { get; private set; }
    public PlayerWallJumpState wallJumpState { get; private set; }
    public PlayerPrimaryAttackState primaryAttack { get; private set; }
    public PlayerCounterAttackState counterAttack { get; private set; }

    #endregion

    //创建对象
    protected override void Awake()
    {
        base.Awake();

        StateMachine = new PlayerStateMachine();

        idleState = new PlayerIdleState(StateMachine, this, "Idle");
        moveState = new PlayerMoveState(StateMachine, this, "Move");
        jumpState = new PlayerJumpState(StateMachine, this, "Jump");
        airState = new PlayerAirState(StateMachine, this, "Jump");
        dashState = new PlayerDashState(StateMachine, this, "Dash");
        wallSlideState = new PlayerWallSlideState(StateMachine, this, "WallSlide");
        wallJumpState = new PlayerWallJumpState(StateMachine, this, "Jump");
        primaryAttack = new PlayerPrimaryAttackState(StateMachine, this, "Attack");
        counterAttack = new PlayerCounterAttackState(StateMachine, this, "CounterAttack");
    }

    // 设置初始状态
    protected override void Start()
    {
        base.Start();

        skill = SkillManager.instance;

        StateMachine.Initialize(idleState);
    }

    // 更新
    protected override void Update()
    {
        base.Update();

        StateMachine.currentState.Update();

        CheckForDashInput();
    }

    public IEnumerator BusyFor(float _seconds)
    {
        isBusy = true;

        yield return new WaitForSeconds(_seconds);

        isBusy = false;
    }

    //设置触发器
    public void AnimationTrigger() => StateMachine.currentState.AnimationFinishTrigger();

    //检查冲刺输入
    public void CheckForDashInput()
    {
        if (Input.GetKeyDown(KeyCode.LeftShift) && SkillManager.instance.dash.CanUseSkill())
        {
            dashDir = Input.GetAxisRaw("Horizontal");

            if (dashDir == 0)
                dashDir = facingDir;

            StateMachine.ChangeState(dashState);
        }
    }

}

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

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

相关文章

HTML5扫雷游戏开发实战

HTML5扫雷游戏开发实战 这里写目录标题 HTML5扫雷游戏开发实战项目介绍技术栈项目架构1. 游戏界面设计2. 核心类设计 核心功能实现1. 游戏初始化2. 地雷布置算法3. 数字计算逻辑4. 扫雷功能实现 性能优化1. DOM操作优化2. 算法优化 项目亮点技术难点突破1. 首次点击保护2. 连锁…

【Git学习笔记】Git分支管理策略及其结构原理分析

【Git学习笔记】Git分支管理策略及其结构原理分析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;Git学习笔记 文章目录 【Git学习笔记】Git分支管理策略及其结构原理分析前言一.合并冲突二. 分支管理策略2.1 分支策略2.2 bug分支2.3 删除临…

Spring Cloud Alibaba Nacos 2023.X 配置问题

文章目录 问题现象&#xff08;一&#xff09;解决方法&#xff08;一&#xff09;问题现象&#xff08;二&#xff09;解决方法&#xff08;二&#xff09;问题现象&#xff08;三&#xff09;解决方法&#xff08;三&#xff09; 问题现象&#xff08;一&#xff09; Spring…

厨卫行业供应链产销协同前中后大平台现状需求分析报告+P120(120页PPT)(文末有下载方式)

资料解读&#xff1a;厨卫行业供应链产销协同前中后大平台现状需求分析报告 详细资料请看本解读文章的最后内容。在当前厨卫行业竞争激烈的市场环境下&#xff0c;企业的发展战略和业务模式创新至关重要。本次解读的报告围绕某厨卫企业展开&#xff0c;深入探讨其供应链产销协同…

我在哪,要去哪

在直播间听到一首好听的歌《我在哪&#xff0c;要去哪》-汤倩。 遇见的事&#xff1a;21~24号抽调去招生。 感受到的情绪&#xff1a;公假吗&#xff1f;给工作量吗&#xff1f;月工作量不够扣钱吗&#xff1f;报销方便吗&#xff1f;有事情&#xff0c;从来不解决后顾&#x…

SpringBoot-2整合MyBatis以及基本的使用方法

目录 1.引入依赖 2.数据库表的创建 3.数据源的配置 4.编写pojo类 5.编写controller类 6.编写接口 7.编写接口的实现类 8.编写mapper 1.引入依赖 在pom.xml引入依赖 <!-- mysql--><dependency><groupId>com.mysql</groupId><artifac…

本周安全速报(2025.3.11~3.17)

合规速递 01 瑞士出台新规&#xff1a;关基设施遭遇网络攻击需在24小时内上报 原文: https://www.bleepingcomputer.com/news/security/swiss-critical-sector-faces-new-24-hour-cyberattack-reporting-rule/ 新规要求&#xff0c;关键基础设施组织发现网络攻击后&…

【css酷炫效果】纯CSS实现瀑布流加载动画

【css酷炫效果】纯CSS实现瀑布流加载动画 缘创作背景html结构css样式完整代码基础版进阶版(无限往复加载) 效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;https://download.csdn.net/download/u011561335/90492012 缘 创作随缘&#xff0c;不定时更新。 创作…

咖啡点单小程序毕业设计(JAVA+SpringBoot+微信小程序+完整源码+论文)

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着社会的快速发展和…

网络编程套接字【端口号/TCPUDP/网络字节序/socket编程接口/UDPTCP网络实验】

网络编程套接字 0. 前言1. 认识端口号2. 认识TCP和UDP协议3. 网络字节序4. socket编程接口5. 实现一个简单的UDP网络程序5.1 需求分析5.2 头文件准备5.3 服务器端设计5.4 客户端设计5.5 本地测试5.6 跨网络测试5.7 UDP小应用——客户端输入命令&#xff0c;服务器端执行 6. 地址…

DeepSeek 3FS 与 JuiceFS:架构与特性比较

近期&#xff0c;DeepSeek 开源了其文件系统 Fire-Flyer File System (3FS)&#xff0c;使得文件系统这一有着 70 多年历时的“古老”的技术&#xff0c;又获得了各方的关注。在 AI 业务中&#xff0c;企业需要处理大量的文本、图像、视频等非结构化数据&#xff0c;还需要应对…

Unity WebGL项目访问时自动全屏

Unity WebGL项目访问时自动全屏 打开TemplateData/style.css文件 在文件最下方添加红色框内的两行代码 使用vscode或者其他编辑器打开index.html 将按钮注释掉&#xff0c;并且更改为默认全屏

C# WPF编程-Menu

C# WPF编程-Menu 布局&#xff1a;代码&#xff1a;效果 在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Menu控件用于创建下拉菜单或上下文菜单&#xff0c;它提供了丰富的定制选项来满足不同的应用需求。下面将介绍如何在WPF应用程序中使用Menu…

Docker和containerd之概览(Overview of Docker and Containerd)

Docker和containerd之概览 容器本质上就是一个进程。 Namespace是一种逻辑分组机制&#xff0c;允许您将集群资源划分为独立的虚拟环境。每个 Namespace 为资源提供了一个范围&#xff0c;使得不同的团队、应用程序或环境可以在同一集群中共存&#xff0c;而不会相互干扰。 C…

【多线程】线程不安全问题

文章目录 多线程不安全的原因大的层面->多线程是随机调度的容易产生死锁 小的层面->内存不可见性引入volatile关键字 指令重排序不是原子性带来的隐患 synchronized锁的互斥性及作用可重入性——解决死锁 wait()和notify()两个突然迸发出的疑问 多线程不安全的原因 大的…

【C++】树和二叉树的实现(下)

本篇博客给大家带来的是用C语言来实现数据结构树和二叉树的实现&#xff01; &#x1f41f;&#x1f41f;文章专栏&#xff1a;数据结构 &#x1f680;&#x1f680;若有问题评论区下讨论&#xff0c;我会及时回答 ❤❤欢迎大家点赞、收藏、分享&#xff01; 今日思想&#xff…

kafka指北

为自己总结一下kafka指北&#xff0c;会持续更新。创作不易&#xff0c;转载请注明出处。 目录 集群controller选举过程broker启动流程 主题创建副本分布ISRleader副本选举机制LEO 生产数据流程同步发送和异步发送 分区策略ack应答生产者发送消息的幂等性跨分区幂等性问题&…

7、vue3做了什么

大佬认为有何优点&#xff1a; 组合式api----逻辑集中、对ts有更好的支持RFC–开放了一个讨论机制&#xff0c;可以看到每一个api的提案&#xff0c;方便源码维护&#xff0c;功能扩展&#xff0c;大家一起讨论 官方rfc响应式独立&#xff0c;new Proxy&#xff0c;天生自带来…

基于大语言模型与知识图谱的智能论文生成工具开发构想

基于大语言模型与知识图谱的智能论文生成工具开发构想 一、研究背景与意义 1.1 学术写作现状分析 #mermaid-svg-FNVHG5EiEgVSCpHK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FNVHG5EiEgVSCpHK .error-icon{fil…

JUC大揭秘:从ConcurrentHashMap到线程池,玩转Java并发编程!

目录 JUC实现类 ConcurrentHashMap 回顾HashMap ConcurrentHashMap CopyOnWriteArrayList 回顾ArrayList CopyOnWriteArrayList: CopyOnWriteArraySet 辅助类 CountDownLatch 线程池 线程池 线程池优点 ThreadPoolExecutor 构造器各个参数含义&#xff1a; 线程…