Unity Netcode for GameObjects多人联机(源文件)

news2025/1/16 8:57:15

一、安装Netcode for Gameobjects
Netcode for Gameobjects是Unity新推出的联机解决方案,该解决方案目前处于初期,相关功能不是很完善,但是用起来还不错。下面介绍一下相关用法。
首先下载安装Netcode for Gameobjects,其官方网站为Netcode for Gameobjects,安装方式也很简单,新建好Unity项目,打开菜单栏Window-Package Manager,点击加号add Package from git Url,输入以下地址,点击add等待安装完成即可。

com.unity.netcode.gameobjects

在这里插入图片描述
如果出现报错,首先是版本问题,需要更换其他版本的Unity。

二、NetworkManager组件
新建空物体,命名NetworkManager,添加NetworkManager组件
在这里插入图片描述
同时注意点击Selec transport下拉选项,选择Unity Transport即可。
在这里插入图片描述
在NetworkManager组件里,有几个参数比较重要:
Player Prefab:玩家预制体
Network Prefab:网络预制体
TickRate:编译和发送数据的频率,一般默认为30,发送的间隔时间就为1/30s
在UnityTransport组件下,也有几个参数要注意:
在这里插入图片描述
Address:IP地址
Port:网络端口号
Server Listen Adress:服务器监听地址

三、创建登陆界面
在Unity中创建如下界面:
在这里插入图片描述
注意不能缺少EventSystem组件。
在这里插入图片描述

创建服务器:创建一个游戏服务器,该服务器只作为服务器使用,不同时作为客户端
加入服务器:以客户端身份加入已存在的服务器
输入框:输入ip地址加入
四、创建场景和玩家角色
在场景中创建一个地面和一些方块即可
在这里插入图片描述
在Unity资源商店,导入第三人称免费资源包
在这里插入图片描述
在Assets文件夹找到如下资源包,拖入场景中,为两个预制体添加父对象Player
在这里插入图片描述
同时取消两个预制体预制体关联。
在这里插入图片描述
为Player添加网络组件NetworkObject。
在这里插入图片描述
将Player拖入Prefabs文件夹制作成预制体,然后删除场景中的Player。
在这里插入图片描述
将预制体Player拖到NetworkManager的玩家预制体中。
在这里插入图片描述
为了让联机更有趣,我们给玩家制作发射子弹击中减血的效果,因此在玩家的头顶,添加一个立体的UI画布,如下图所示:
在这里插入图片描述
同时,为了发射子弹,添加两个空物体,作为子弹生成和发射方向的物体,然后保存预制体。
在这里插入图片描述
创建一个球体作为子弹,给球体添加如下组件,其中Bullet为创建的新脚本。
在这里插入图片描述

删除MainCamera,新建Camera,在Scene中调整到合适视角,然后选中Camera点击Align With View,对齐视角。该摄像机作为服务器端使用的摄像机,进入的如果是客户端,隐藏该相机。
在这里插入图片描述
五、创建服务器和加入服务器

首先创建一个脚本NetMain,挂在NetworkManager下,主要代码如下:

using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using UnityEngine.UI;

public class NetMain : MonoBehaviour
{
    /// <summary>
    /// 单例
    /// </summary>
    public static NetMain instance;
    /// <summary>
    /// 创建服务器按钮
    /// </summary>
    public Button createServerBtn;
    /// <summary>
    /// 加入服务器按钮
    /// </summary>
    public Button joinServerBtn;
    /// <summary>
    /// 输入IP地址框
    /// </summary>
    public InputField ipinput;
    //玩家生成点
    public Transform playerSpawnPos;
    //默认相机
    public GameObject _camera;

    /// <summary>
    /// 测试面板
    /// </summary>
    public GameObject testPanel;
    /// <summary>
    /// 本地玩家
    /// </summary>
    public NetPlayer localPlayer;
    private void Awake()
    {
        instance = this;
    }
    void Start()
    {
        //绑定按钮事件
        createServerBtn.onClick.AddListener(CreateServerBtnClick);
        joinServerBtn.onClick.AddListener(JoinServerBtnClick);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    /// <summary>
    /// 创建服务器按钮绑定事件
    /// </summary>
    private void CreateServerBtnClick()
    {
        //获取Unity传输组件
        UnityTransport unityTransport = NetworkManager.Singleton.GetComponent<UnityTransport>();
        //设置ip地址和端口,0.0.0.0代表任意ip地址
        unityTransport.SetConnectionData("0.0.0.0", 7777);
        //启动服务器
        NetworkManager.Singleton.StartServer();
        //隐藏UI界面
        createServerBtn.transform.parent.gameObject.SetActive(false);
        testPanel.SetActive(true);
    }

    /// <summary>
    /// 加入服务器按钮点击事件
    /// </summary>
    private void JoinServerBtnClick()
    {
        //获取输入的ip地址
        string ip = ipinput.text;
        //判断ip地址是否为空
        if (ipinput.text.Equals(""))
        {
            //如果为空,默认ip地址为127.0.0.1
            ip = "127.0.0.1";
            print("未输入IP地址,使用默认地址");
        }
        //获取Unity传输组件
        UnityTransport unityTransport = NetworkManager.Singleton.GetComponent<UnityTransport>();
        //设置要连接的ip地址和端口
        unityTransport.SetConnectionData(ip, 7777);
        //启动连接到服务器,以客户端的身份
        NetworkManager.Singleton.StartClient();
        //隐藏UI界面
        joinServerBtn.transform.parent.gameObject.SetActive(false);
        //关闭消息调试面板
        testPanel.SetActive(true);
    }
}


六、玩家和发射子弹同步
新建一个NetPlayerSync脚本,挂在第三人称控制器的玩家身上,主要代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using UnityEngine.UI;
using Invector.vCharacterController;

public class NetPlayerSync : NetworkBehaviour
{
    /// <summary>
    /// 动画组件
    /// </summary>
    private Animator _animator;

    /// <summary>
    /// 需要进行服务端更新的参数,包含发射和接收
    /// </summary>
    NetworkVariable<float> _SyncInputHorizontal = new NetworkVariable<float>();
    NetworkVariable<float> _SyncInputVertical = new NetworkVariable<float>();
    NetworkVariable<float> _SyncInputMagnitude = new NetworkVariable<float>();
    NetworkVariable<bool> _SyncIsGrounded = new NetworkVariable<bool>();
    NetworkVariable<bool> _SyncIsStrafing = new NetworkVariable<bool>();
    NetworkVariable<bool> _SyncIsSprinting = new NetworkVariable<bool>();
    NetworkVariable<float> _SyncGroundDistance = new NetworkVariable<float>();

    NetworkVariable<Vector3> _SyncPosition = new NetworkVariable<Vector3>();
    NetworkVariable<Quaternion> _SyncRotation = new NetworkVariable<Quaternion>();
    NetworkVariable<float> _SyncHealth = new NetworkVariable<float>();


    #region Variables       

    [Header("Controller Input")]
    public string horizontalInput = "Horizontal";
    public string verticallInput = "Vertical";
    public KeyCode jumpInput = KeyCode.Space;
    public KeyCode strafeInput = KeyCode.Tab;
    public KeyCode sprintInput = KeyCode.LeftShift;

    [Header("Camera Input")]
    public string rotateCameraXInput = "Mouse X";
    public string rotateCameraYInput = "Mouse Y";

    [HideInInspector] public vThirdPersonController cc;
    [HideInInspector] public vThirdPersonCamera tpCamera;
    [HideInInspector] public Camera cameraMain;

    #endregion

    public Image fillImage;
    public float currentHealth;
    public float maxHealth = 100;

    public NetPlayer mPlayer;



    private void Start()
    {
        _animator = GetComponent<Animator>();
        maxHealth = 100;
        currentHealth = maxHealth;
        mPlayer.playerSync = this;


        InitilizeController();
        InitializeTpCamera();

    }

    private void FixedUpdate()
    {
        if (IsLocalPlayer)
        {
            cc.UpdateMotor();               // updates the ThirdPersonMotor methods
            cc.ControlLocomotionType();     // handle the controller locomotion type and movespeed
            cc.ControlRotationType();       // handle the controller rotation type
        }
    }
    private void Update()
    {
        if (IsLocalPlayer)
        {
            InputHandle();                  // update the input methods
            cc.UpdateAnimator();            // updates the Animator Parameters
            UpdateAnimator();
            SetHealthToServerRpc(currentHealth);
        }
    }
    private void LateUpdate()
    {
        if (!IsLocalPlayer)
        {
            SyncInput();
            UpdateOtherPlayerHealth();
        }
        UpdateHealthImage();
    }
    public virtual void OnAnimatorMove()
    {
        if (cc!=null)
        {
            cc.ControlAnimatorRootMotion(); // handle root motion animations 
        }
    
    }

    private void UpdateAnimator()
    {
        float h = _animator.GetFloat("InputHorizontal");
        float v = _animator.GetFloat("InputVertical");
        float m = _animator.GetFloat("InputMagnitude");
        bool ground = _animator.GetBool("IsGrounded");
        bool straf = _animator.GetBool("IsStrafing");
        bool sprint = _animator.GetBool("IsSprinting");
        float grounddistance = _animator.GetFloat("GroundDistance");
        //上面先获取到当前参数,然后发给服务端,服务端再发给各个客户端进行同步
        UpdatePlayerAnimatorServerRpc(h, v, m, ground, straf, sprint, grounddistance);
        UpdatePlayerMovementServerRpc(transform.position, transform.rotation);

    }

    [ServerRpc]
    private void UpdatePlayerAnimatorServerRpc(float h, float v, float m, bool ground, bool straf, bool sprint, float grounddistance)
    {
        _SyncInputHorizontal.Value = h;
        _SyncInputVertical.Value = v;
        _SyncInputMagnitude.Value = m;
        _SyncIsGrounded.Value = ground;
        _SyncIsStrafing.Value = straf;
        _SyncIsSprinting.Value = sprint;
        _SyncGroundDistance.Value = grounddistance;


    }
    [ServerRpc]
    private void UpdatePlayerMovementServerRpc(Vector3 pos, Quaternion rot)
    {
        _SyncPosition.Value = pos;
        _SyncRotation.Value = rot;
    }

    [ServerRpc]
    private void SetHealthToServerRpc(float health)
    {
        _SyncHealth.Value = health;
    }
    /// <summary>
    /// 如果不是本地玩家,只需要接收服务端更新的数据
    /// </summary>
    private void SyncInput()
    {
        _animator.SetFloat("InputHorizontal", _SyncInputHorizontal.Value);
        _animator.SetFloat("InputVertical", _SyncInputVertical.Value);
        _animator.SetFloat("InputMagnitude", _SyncInputMagnitude.Value);
        _animator.SetBool("IsGrounded", _SyncIsGrounded.Value);
        _animator.SetBool("IsStrafing", _SyncIsStrafing.Value);
        _animator.SetBool("IsSprinting", _SyncIsSprinting.Value);
        _animator.SetFloat("GroundDistance", _SyncGroundDistance.Value);

        transform.position = _SyncPosition.Value;
        transform.rotation = _SyncRotation.Value;
    }
    private void UpdateOtherPlayerHealth()
    {
        currentHealth = _SyncHealth.Value;
    }

    private void UpdateHealthImage()
    {
        fillImage.fillAmount = currentHealth / maxHealth;
    }



    #region Basic Locomotion Inputs

    protected virtual void InitilizeController()
    {
        cc = GetComponent<vThirdPersonController>();

        if (cc != null)
            cc.Init();
    }

    protected virtual void InitializeTpCamera()
    {
        if (tpCamera == null)
        {
            tpCamera = FindObjectOfType<vThirdPersonCamera>();
            if (tpCamera == null)
                return;
            if (tpCamera)
            {
                tpCamera.SetMainTarget(this.transform);
                tpCamera.Init();
            }
        }
    }

    protected virtual void InputHandle()
    {
        MoveInput();
        CameraInput();
        SprintInput();
        StrafeInput();
        JumpInput();
    }

    public virtual void MoveInput()
    {
        cc.input.x = Input.GetAxis(horizontalInput);
        cc.input.z = Input.GetAxis(verticallInput);
    }

    protected virtual void CameraInput()
    {
        if (!cameraMain)
        {
            if (!Camera.main) Debug.Log("Missing a Camera with the tag MainCamera, please add one.");
            else
            {
                cameraMain = Camera.main;
                cc.rotateTarget = cameraMain.transform;
            }
        }

        if (cameraMain)
        {
            cc.UpdateMoveDirection(cameraMain.transform);
        }

        if (tpCamera == null)
            return;

        var Y = Input.GetAxis(rotateCameraYInput);
        var X = Input.GetAxis(rotateCameraXInput);

        tpCamera.RotateCamera(X, Y);
    }

    protected virtual void StrafeInput()
    {
        if (Input.GetKeyDown(strafeInput))
            cc.Strafe();
    }

    protected virtual void SprintInput()
    {
        if (Input.GetKeyDown(sprintInput))
            cc.Sprint(true);
        else if (Input.GetKeyUp(sprintInput))
            cc.Sprint(false);
    }

    /// <summary>
    /// Conditions to trigger the Jump animation & behavior
    /// </summary>
    /// <returns></returns>
    protected virtual bool JumpConditions()
    {
        return cc.isGrounded && cc.GroundAngle() < cc.slopeLimit && !cc.isJumping && !cc.stopMove;
    }

    /// <summary>
    /// Input to trigger the Jump 
    /// </summary>
    protected virtual void JumpInput()
    {
        if (Input.GetKeyDown(jumpInput) && JumpConditions())
            cc.Jump();
    }

    #endregion
}

新建一个NetPlayer脚本,挂在Player玩家预制体身上。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using Invector.vCharacterController;
using Invector.Utils;
using UnityEngine.UI;

public class NetPlayer : NetworkBehaviour
{
  

    /// <summary>
    /// 子物体玩家
    /// </summary>
    public GameObject _player;
    /// <summary>
    /// 子物体第三人称相机
    /// </summary>
    public GameObject _camera;

    //子弹出生点或者说生成点
    public Transform spawnPoint;
    //子弹发射方向
    public Transform endpoint;
    //子弹预制体
    public GameObject bullet;
    //玩家同步脚本
    public NetPlayerSync playerSync;

    /// <summary>
    /// 当玩家生成时
    /// </summary>
    public override void OnNetworkSpawn()
    {
        //显示调试信息
        NetMain.instance.testPanel.GetComponentInChildren<Text>().text += OwnerClientId;
        //设置玩家名字
        gameObject.name = OwnerClientId.ToString();

        //如果是本地玩家
        if (IsLocalPlayer)
        {
            //初始化参数
            NetMain.instance.localPlayer = this;
            NetMain.instance._camera.SetActive(false);
            transform.position = NetMain.instance.playerSpawnPos.position;
           

        }

        //不是本地玩家
        if (!IsLocalPlayer)
        {
            //删除部分组件
            if (_player.GetComponent<vThirdPersonController>()!=null)
            {
                Destroy(_player.GetComponent<vThirdPersonController>());
            }
            if (_player.GetComponent<vComment>()!=null)
            {
                Destroy(_player.GetComponent<vComment>());
            }
            if (_player.GetComponent<vThirdPersonInput>()!=null)
            {
                Destroy(_player.GetComponent<vThirdPersonInput>());
            }
            if (_camera!=null)
            {
                Destroy(_camera);
            }
         
        }

    }

    private void Update()
    {
        //如果是本地玩家,才可以进行发射子弹,其他玩家收到服务器控制,不接受本地的控制
        if (IsLocalPlayer)
        {
            TestAttack();

        }

    }


    private void TestAttack()
    {
        if (Input.GetKeyDown(KeyCode.F))
        {
            SpawnBulletServerRpc();
        }

    }

    /// <summary>
    /// 客户端向服务器发射消息
    /// </summary>
    [ServerRpc]
    private void SpawnBulletServerRpc()
    {
        SpawnBulletToClientRpc();
    }

    /// <summary>
    /// 服务端向客户端发射消息
    /// </summary>
    [ClientRpc]
    private void SpawnBulletToClientRpc()
    {
        if (IsLocalPlayer)
        {
            GameObject go = NetworkManager.Instantiate(bullet, spawnPoint.position, spawnPoint.rotation);
            go.GetComponent<Bullet>().dir = endpoint.position - spawnPoint.position;
        }
        else
        {
            GameObject go = NetworkManager.Instantiate(bullet, spawnPoint.position, spawnPoint.rotation);
            go.GetComponent<Bullet>().dir = endpoint.position - spawnPoint.position;
            
        }
        
    }

    /// <summary>
    /// 本地测试发射子弹
    /// </summary>
    private void LocalSpawnBullet()
    {
        GameObject go = NetworkManager.Instantiate(bullet, spawnPoint.position, spawnPoint.rotation);
        go.GetComponent<Bullet>().dir = endpoint.position - spawnPoint.position;
    }


    /// <summary>
    /// 向服务端发射消息:更新被射中的玩家的血量
    /// </summary>
    /// <param name="id"></param>
    [ServerRpc]
    public void SendHealthInfoToServerRpc(ulong id)
    {
        SetHealthChangeToClientRpc(id);
    }


    /// <summary>
    /// 通知所有玩家,判断是否为射中的玩家,如果是减血量
    /// </summary>
    /// <param name="id"></param>
    [ClientRpc]
    public void SetHealthChangeToClientRpc(ulong id)
    {
        if (id == NetMain.instance.localPlayer.OwnerClientId)
        {
            NetMain.instance.testPanel.GetComponentInChildren<Text>().text += "\n当前的id为" + id;
            NetMain.instance.localPlayer.playerSync.currentHealth -= 1;
        }
    }

}

新建一个HealthImage脚本,挂在玩家血条上,使血条始终朝向摄像机。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HealthImage : MonoBehaviour
{
    //朝向相机
    private Transform Camtarget;
    private void Update()
    {
        //不断查找所有的摄像机,判断哪个是已启用的,把已启用的摄像机设置为目标摄像机
        GameObject[] gos = GameObject.FindGameObjectsWithTag("MainCamera");
        for (int i = 0; i < gos.Length; i++)
        {
            if (gos[i].activeInHierarchy)
            {
                Camtarget = gos[i].transform;
                break;
            }
        }
        if (Camtarget!=null)
        {
            transform.rotation = Camtarget.rotation;
        }
    }
}

子弹Bullet代码:

using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;

public class Bullet : NetworkBehaviour
{
    public Vector3 dir;
    Rigidbody rigid;

    

    private void Start()
    {
        rigid = GetComponent<Rigidbody>();
        Destroy(gameObject, 5);
    }

    private void FixedUpdate()
    {
        rigid.velocity = dir * Time.deltaTime * 5000;
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Player"))
        {
            NetPlayerSync mps = collision.collider.GetComponent<NetPlayerSync>();
            if (!mps.mPlayer.IsLocalPlayer)
            {
                print(mps.OwnerClientId);
             NetMain.instance.localPlayer.SendHealthInfoToServerRpc(mps.OwnerClientId);
                Destroy(gameObject);
            }
        }
    }



}

七、测试与总结
完成代码之后需要手动拖的游戏物体:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
效果如下:
在这里插入图片描述
项目源文件,使用版本为2020.3.28
链接:https://pan.baidu.com/s/1d8zuQzzekIefJGgQvJKfXw
提取码:yrpc

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

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

相关文章

Elasticsearch入门介绍及Linux安装

前言Elasticsearch是一款分布式高性能的全文搜索引擎&#xff0c;为什么会需要这个呢&#xff0c;像我们平常使用的最多的存储工具就是Mysql&#xff0c;在业界也是非常有名的&#xff0c;我们大部分结构化数据都是用它来存储的&#xff0c;sql语言的操作也是非常方便&#xff…

助力“智慧港口”建设,北部湾港联合美创打造的主数据管理系统上线!

数字化浪潮下&#xff0c;港口企业发展将致力于以数据价值提升和数据价值创造为目标&#xff0c;打造新型“智慧港口”&#xff0c;实现更加经济的港口资源统筹高效利用并降低各类成本&#xff0c;企业通过逐步转变信息资源管理工作模式和利用方式&#xff0c;深度应用新一代信…

在GCP上创建Cloud SQL的三种方式(Console,gcloud,Terraform)

1 简介 Cloud SQL 是GCP上的关系型数据库&#xff0c;常用的有三种方式来创建&#xff1a; (1) 界面操作 (2) 命令行 gcloud (3) Terraform 在开始之前&#xff0c;可以查看&#xff1a;《初始化一个GCP项目并用gcloud访问操作》。 2 GCP 操作界面 登陆GCP&#xff0c;选…

Chevereto V4 首页显示图片托管数量的实现方法

博主最近一直想在Chevereto V4 首页显示图片托管数量&#xff0c;但是网上的方法只针对于Chevereto V3&#xff0c;V4却没有相关教程&#xff0c;查询谷歌也无结果&#xff0c;经博主查询大量案例今天为大家分享一下 Chevereto V4 首页显示图片托管数量的实现方法。1. 修改后台…

70.语义分割和数据集

在 之前讨论的目标检测问题中&#xff0c;我们一直使用方形边界框来标注和预测图像中的目标。 本节将探讨语义分割&#xff08;semantic segmentation&#xff09;问题&#xff0c;它重点关注于如何将图像分割成属于不同语义类别的区域。 与目标检测不同&#xff0c;语义分割可…

20230112编译AIO-3568J的Buildroot(rk356x_linux_release_v1.3.0b_20221213)

20230112编译AIO-3568J的Buildroot&#xff08;rk356x_linux_release_v1.3.0b_20221213&#xff09; 2023/1/12 20:40 当前可以拿到的Buildroot的SDK&#xff1b; rk356x_linux_release_v1.0.0_20210511_split_dir rk356x_linux_release_v1.2.0_20211019_split_dir rk356x_linu…

Ventoy主题美化,以及自行制作方法

Ventoy是基于grub2 所制作的&#xff0c;所以可以自行制作或者将现成的主题套用到Ventoy 方法一&#xff1a; 主题下载地址 上面是两个可以直接使用的Ventoy主题地址&#xff0c;然后下载下来解压文件&#xff0c;我们可以得到 接着往下走&#xff0c;我们可以的得到 现在我们…

Oracle Apex低码平台-定制验证方案

Oracle Apex低码平台-定制验证方案 0 APEX简介&#xff1a; Oracle APEX 是一个低代码开发平台&#xff0c;您可以在该平台上构建可扩展的安全企业应用程序。这些应用程序具有先进的功能&#xff0c;而且可以在任何地方部署。 构建企业应用速度提高 20 倍&#xff0c;代码减…

Dubbo服务降级

Dubbo服务降级 1. 为什么需要服务降级 RPC 是解决分布式系统通信问题的一大利器&#xff0c;而分布式系统的一大特点就是高并发&#xff0c;所以说 RPC 也会面临高并发的场景。在这样的情况下&#xff0c;我们提供服务的每个服务节点就都可能由于访问量过大而引起一系列的问题…

Chrome浏览器插件推荐【第一期】

1、Tampermonkey Tampermonkey&#xff08;油猴&#xff09;是一款免费的浏览器扩展和最为流行的用户脚本管理器&#xff0c;它适用于 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox。虽然有些受支持的浏览器拥有原生的用户脚本支持&#xff0c;但 Tampermonkey 将在…

Webpack的应用

处理css文件 总共有src目录下的index.css和index.js、同根的index.html和webpack.config.js文件&#xff0c;然后npm init之后生成package.json文件&#xff0c;npm install后生成package-lock.json文件&#xff0c;最后npm run webpack之后有dist目录下各种文件 index.js i…

nodejs使JWT(全)

Token token表示令牌&#xff0c;用户的登录凭证。 基于 Token 的身份验证方法&#xff0c;使用基于 Token 的身份验证方法&#xff0c;在服务端不需要存储用户的登录记录。大概的流程是这样的&#xff1a; 客户端使用用户名跟密码请求登录服务端收到请求&#xff0c;去验证…

css动画效果之transform

transformTransform属性应用于元素的2D或3D转换。这个属性允许你将元素旋转&#xff0c;缩放&#xff0c;移动&#xff0c;倾斜等。旋转rotate、扭曲skew、移动translate、缩放scale、矩阵变形matrix属性名扩展写法属性含义none定义不进行转换。rotaterotateX()&#xff08;3D写…

rabbitmq+netcore6 【5】Topics:主题

文章目录1&#xff09;前言2&#xff09;Topic exchange 主题交换机3&#xff09;举例4&#xff09;总结5&#xff09;综合以上代码准备工作生产者消费者1消费者2结果验证官网参考链接&#xff1a; https://www.rabbitmq.com/tutorials/tutorial-five-dotnet.html其他人的翻译版…

臻图信息搭建智慧水务管理平台,保障供水安全运行

伴随着城市智慧化进程&#xff0c;供水系统也在朝着高度集成化、数字化、智能化的管理模式发展。在2022年&#xff0c;水利部也印发了相关水务保障规划&#xff0c;对供水工程的建设、运行管理、水源保护等管理提出了明确要求&#xff0c;采取物联网、互联网等措施&#xff0c;…

从“以旧换新”送手机看年礼消费新风尚

千门万户曈曈日&#xff0c;总把新桃换旧符。每年的春节&#xff0c;都是中国人辞旧迎新的重要时刻。在新春年礼的选择上&#xff0c;曾经的“烟酒糖茶”老四样正在逐渐被其他新潮年礼所替代&#xff0c;手机等诸多科技好物被纳入到送年礼清单。手机年货很“潮”&#xff0c;让…

Redis整理合集

SQL和NOSQL的区别?SQLNOSQL数据结构结构化非结构化数据关联关联的非关联的查询方式SQL查询非SQL查询事物特性ACID&#xff08;事务&#xff09;BASE存储方式磁盘内存扩展性垂直水平使用场景数据结构固定相对业务对数据的安全性一致性需求较高数据结构不固定对一致性、安全性需…

论文投稿指南——中文核心期刊推荐(地质学 2)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

【性能优化】Mybatis Plus:优化查询速度 - SQL替换Service

优化查询速度 - SQL替换Service Service 接口问题 下面是原先的 Service 实现类代码&#xff0c;有门店 ID、订单状态、查询时间段&#xff0c;然后查出了所有的结果&#xff0c;继续使用 java8 的特性获取汇总结果&#xff0c;随着项目的推移&#xff0c;数据量越来越大&…

Webpack 中使用source map 在开发过程中进行调试

我们都知道webpack在打包的时候会将源代码打包成一个bundle文件&#xff0c;bundle文件就是经过了loader转换&#xff0c;还有webpack的一些插件处理&#xff0c;以及webpack构建过程中的一些转换&#xff0c;最后会生成一个大的JS文件&#xff0c;直接去看这个文件是没法调试的…