【unity笔记】七、Mirror插件使用

news2024/11/19 6:36:01

一、简介

Mirror 是一个用于 Unity 的开源多人游戏网络框架,它提供了一套简单高效的网络同步机制,特别适用于中小型多人游戏的开发。以下是 Mirror 插件的一些关键特点和组件介绍:

  1. 简单高效:Mirror 以其简洁的 API 和高效的网络代码而受到开发者的欢迎。
  2. 基于 UnityEngine 生命周期:Mirror 利用 Unity 的生命周期回调进行数值同步,简化了网络开发流程。
  3. RPC 调用:Mirror 提供了三种远程过程调用(RPC)的方式:[Command]、[ClientRpc] 和[TargetRpc],允许开发者在不同客户端或服务器上执行特定的函数。
  4. 网络组件
  • NetworkManager:用于管理网络会话,包括开始服务器、客户端和处理玩家连接。
  • NetworkIdentity:为游戏对象添加网络身份,使其能够在网络上被识别和同步。
  • NetworkStartPosition:用于设置玩家的初始生成位置。 网络发现:Mirror
  1. 支持局域网内的网络发现功能,方便玩家发现并加入游戏。

  2. 运输层兼容性:Mirror 兼容多种低级运输层,包括但不限于 TCP、UDP 和 KCP 协议。

  3. 开箱即用:Mirror 在 Unity Asset Store 中免费提供,并且具有丰富的文档和社区支持。

  4. 适用于小体量游戏:Mirror 更适合小型到中型的游戏项目,对于大型游戏项目可能需要更复杂的网络解决方案。

Mirror官网方文档:https://mirror-networking.gitbook.io/docs。

二、 使用示例

2.1 效果预览

实现简单多人联机小游戏
在这里插入图片描述

2.2 步骤1 添加网络管理

新建一个空对象,并为其添加Network Manager、Kcp Transport、Network Manager HUD组件。
在这里插入图片描述
其中:在Network Manager中添加在线场景和离线场景以及玩家预制体,玩家预制体可以从网上寻找模型或者使用内置模型代替,注意玩家预制体要添加 Network Identity组件。
最大玩家设为4对应4个玩家生成点,玩家生成方法可以设为随机或轮循。
在这里插入图片描述
在Kcp Transport中的配置可以不用动,也可以根据需要配置。
在这里插入图片描述

2.3 环境设置

  • 地形可以简单用一个Plane来代替,如果对质量要求高可以后期替换为别的素材,添加天气系统,使用HDRP等可以看往期内容。
  • 为玩家添加生成位置,这里放置四个位置生成玩家,前面已经设置了最大玩家数为4,则可以有四个不同玩家进入游戏,并在预设位置生成。可以先设置好一个,在复制加下来几个并设置好想要生成的位置。使用一个空对象添加Network Start Position即可。

在这里插入图片描述

2.3 代码部分

为玩家添加代码来操作玩家移动

playerScript参考代码

using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using Mirror;
using UnityEngine;
using static System.Runtime.CompilerServices.RuntimeHelpers;
using Color = UnityEngine.Color;
using Random = UnityEngine.Random;
using Vector3 = UnityEngine.Vector3;

public class PlayerScrpt : NetworkBehaviour
{

    public GameObject floatingInfo;
    public TextMesh nameText;
    //武器
    public GameObject[] weaponArray;
    private int currentWeapon;
    private Weapon activeWeapon;
    private float coolDownTime;

    private Material playMaterialClone;
    //private UIScript UI;


    [SyncVar(hook = nameof(OnPlayerNameChanged))]
    private string playerName;

    [SyncVar(hook = nameof(OnPlayerColorChanged))]
    private Color playerColor;

    [SyncVar(hook = nameof(OnWeaponChanged))]
    private int currentWeaponSynced;

    //name改变触发
    private void OnPlayerNameChanged(string oldName, string newName)
    {
        nameText.text = newName;
    }

    //颜色改变触发
    private void OnPlayerColorChanged(Color oldColor, Color newColor)
    {
        //同步名称颜色
        nameText.color = newColor;
        //同步材质颜色
        playMaterialClone = new Material(GetComponent<Renderer>().material);
        playMaterialClone.SetColor("_Color", newColor);
    }

    //切换武器
    private void OnWeaponChanged(int oldIndex, int newIndex)
    {
        //判断旧武器是否存在,若存在则隐藏
        if (0 < oldIndex && oldIndex < weaponArray.Length && weaponArray[oldIndex] != null)
        {
            weaponArray[oldIndex].SetActive(false);
        }
        if (0 < newIndex && newIndex < weaponArray.Length && weaponArray[newIndex] != null)
        {
            weaponArray[newIndex].SetActive(true);

            activeWeapon = weaponArray[newIndex].GetComponent<Weapon>();
            //显示当前武器子弹数量
           //UI.canvasBulletText.text = activeWeapon.bulletCount.ToString();
        }
        else
        {
            activeWeapon = null; //若武器不存在,则激活武器为空
            //UI.canvasBulletText.text = "No Weapon";
        }

    }


    [Command]
    public void CmdSetupPlayer(string newName, Color colorValue)
    {
        playerName = newName;
        playerColor = colorValue;
    }

    [Command]
    public void CmdActiveWeapon(int index)
    {
        currentWeaponSynced = index;
    }

    [Command]
    public void CmdFire()
    {
        /*if (activeWeapon == null)
            return;*/
        RpcFire();
    }
    [ClientRpc]
    public void RpcFire()
    {
        var bullet = Instantiate(activeWeapon.bullet, activeWeapon.firePos.position, activeWeapon.firePos.rotation);
        bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * activeWeapon.bulletSpeed;
        Destroy(bullet, activeWeapon.bulletLifetime);
    }

    private void Awake()
    {
        foreach (var weapon in weaponArray)
        {
            if (weapon != null)
            {
                weapon.SetActive(false);
            }
        }
    }

    public override void OnStartLocalPlayer()
    {
        //摄像机与Player绑定
        Camera.main.transform.SetParent(transform);
        Camera.main.transform.localPosition = Vector3.zero;

        floatingInfo.transform.localPosition = new Vector3(0f, -2.7f, 6f);
        floatingInfo.transform.localScale = new Vector3(1f, 1f, 1f);

        changePlayerNameAndColor();
    }

    // Update is called once per frame
    void Update()
    {
        if (!isLocalPlayer)
        {
            floatingInfo.transform.LookAt(Camera.main.transform);
            return;
        }

        var moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 110f;
        var moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 4.0f;

        transform.Rotate(0, moveX, 0);
        transform.Translate(0, 0, moveZ);

        if (Input.GetKeyDown(KeyCode.C))  //按下c改变颜色
        {
            changePlayerNameAndColor();
        }
        if (Input.GetButtonDown("Fire2"))
        {
            currentWeapon += 1;
            if (currentWeapon > weaponArray.Length)
                currentWeapon = 1;
            CmdActiveWeapon(currentWeapon);
        }
        if (Input.GetButtonDown("Fire1"))
        {
            if (activeWeapon != null && Time.time > coolDownTime && activeWeapon.bulletCount > 0)
            {
                coolDownTime = Time.time + activeWeapon.coolDown;
                //更新子弹数量
                activeWeapon.bulletCount--;
                //UI.canvasBulletText.text = activeWeapon.bulletCount.ToString();

                CmdFire();
            }
        }

    }


    //改变玩家名称和颜色
    private void changePlayerNameAndColor()
    {
        var tempName = $"Player {Random.Range(1, 999)}";
        var tempColor = new Color(Random.Range(0, 1f), Random.Range(0, 1f), Random.Range(0, 1f));
        CmdSetupPlayer(tempName, tempColor);
    }
}

Weapon参考代码

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

public class Weapon : MonoBehaviour
{

    public Transform firePos; //子弹位置
    public GameObject bullet; // 子弹对象

    public float bulletSpeed = 5f; //子弹速度
    public float bulletLifetime = 3f;//子弹生命周期
    public int bulletCount = 15; //子弹数量
    public float coolDown = 0.5f;
    
}

三、项目生成

接下来就可以点击文件->构建和运行,把游戏文件发给小伙伴一起玩耍。
在这里插入图片描述
将localhost改成自己当前ip,即可让小伙伴加入房间远程游玩了。如果链接失败,记得关闭防火墙!

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

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

相关文章

前端:Nuxt2 + Vuetify2

想要开发一个网站&#xff0c;并且支持SEO搜索&#xff0c;当然离不开我们的 Nuxt &#xff0c;那通过本篇文章让我们一起了解一下。如果构建一个Nuxt项目 安装 Nuxt&#xff0c;创建项目 安装nuxt2&#xff0c; 需要node v16&#xff0c;大家记得查看自己的node版本。构建脚…

安全技术和防火墙(iptables)

安全技术 入侵检测系统&#xff1a;特点是不阻断网络访问&#xff0c;主要是提供报警和事后监督&#xff0c;不主动介入&#xff0c;类似于监控。 入侵防御系统&#xff1a;透明模式工作&#xff0c;对数据包&#xff0c;网络监控&#xff0c;服务攻击&#xff0c;木马&#…

实时显示用户输入PySide6实例

如何用 PySide6 实现QLabel 实时显示用户在 QLineEdit 内输入的内容&#xff1f; 示例代码&#xff1a; # QLineEdit 用户输入内容&#xff0c;QLabel 即时显示用户输入训练from PySide6.QtWidgets import (QApplication, QWidget,QLabel, QLineEdit, QVBoxLayout)class MyWi…

Python | Leetcode Python题解之第200题岛屿数量

题目&#xff1a; 题解&#xff1a; class Solution:def dfs(self, grid, r, c):grid[r][c] 0nr, nc len(grid), len(grid[0])for x, y in [(r - 1, c), (r 1, c), (r, c - 1), (r, c 1)]:if 0 < x < nr and 0 < y < nc and grid[x][y] "1":self.d…

喂饭级AI神器!免代码一键绘制图表,文本数据秒变惊艳视觉盛宴!

由于目前的AI生成图表工具存在以下几个方面的问题&#xff1a; 大多AI图表平台是纯英文&#xff0c;对国内用户来说不够友好&#xff1b;部分平台在生成图表前仍需选择图表类型、配置项&#xff0c;操作繁琐&#xff1b;他们仍需一份规整的数据表格&#xff0c;需要人为对数据…

Qt Quick Effect Maker 工具使用介绍

一、介绍 随着 Qt 版本的不断升级,越来越多的功能被加入 Qt,一些新的工具也随之应运而生,Qt Quick Effect Maker 工具是 Qt 6.5 之后才新添加的工具,之前的名字应该是叫做 Qt shader tool 这个模块。 以下是官方的释义:Qt Quick Effect Maker是一个用于为Qt Quick创建自定…

3.PyQt6常用基本控件

目录 常用控件 1.文本类控件 1.QLable标签控件 1.设置标签文本 2.设置标签文本和对齐方式 3.换行显示 4.添加超链接 5.为标签设置图片 6.获取标签文本 2.QLineEdit单行文本控件 3.QTextEdit多行富文本控件 4.QPlainTextEdit纯文本控件 5.QSpinBox整数数字选择控件 …

C++ | Leetcode C++题解之第200题岛屿数量

题目&#xff1a; 题解&#xff1a; class Solution { private:void dfs(vector<vector<char>>& grid, int r, int c) {int nr grid.size();int nc grid[0].size();grid[r][c] 0;if (r - 1 > 0 && grid[r-1][c] 1) dfs(grid, r - 1, c);if (r …

小白上手AIGC-基于PAI-DSW部署Stable Diffusion文生图Lora模型

小白上手AIGC-基于PAI-DSW部署Stable Diffusion文生图Lora模型 前言资源准备开启体验服务创建工作空间 部署服务创建DSW实例安装Diffusers启动WebUI 写在最后 前言 在上一篇博文小白上手AIGC-基于FC部署stable-diffusion 中&#xff0c;说到基于函数计算应用模板部署AIGC文生图…

Java之线程相关应用实现

后台线程 一个进程中只有后台进程运行&#xff0c;该进程将会结束。 新创建的线程默认为前台线程&#xff0c;Java中只要有一个前台线程运行&#xff0c;就不会结束程序&#xff0c;如果只有后台线程运行&#xff0c;程序就会结束&#xff0c;可以在线程对象启动前执行setDae…

工业AIoT竞赛流程

不要点到重置&#xff01;&#xff01;&#xff01;要刷新虚拟机就点重启 xshell连接虚拟机&#xff1a;ssh rootPublic IP 环境构建 vim /etc/hosts 按 i 进入插入模式&#xff0c;加内网ip和主机名&#xff0c;按esc&#xff0c;按 : &#xff0c;按wq 三个虚拟机都这样配 …

HQChart使用教程30-K线图如何对接第3方数据41-分钟K线叠加股票增量更新

HQChart使用教程30-K线图如何对接第3方数据40-日K叠加股票增量更新 叠加股票叠加分钟K线更新Request 字段说明Data.symbol 协议截图返回json数据结构overlaydata HQChart代码地址交流 叠加股票 示例地址:https://jones2000.github.io/HQChart/webhqchart.demo/samples/kline_i…

controller不同的后端路径对应vue前端传递数据发送请求的方式

目录 案例一&#xff1a; 为什么使用post发送请求&#xff0c;参数依旧会被拼接带url上呢&#xff1f;这应该就是param 与data传参的区别。即param传参数参数会被拼接到url后&#xff0c;data会以请求体传递 补充&#xff1a;后端controller 参数上如果没写任何注解&#xff0c…

半夜被慢查询告警吵醒,limit深度分页的坑

分享是最有效的学习方式。 博客&#xff1a;https://blog.ktdaddy.com/ 故事 梅雨季&#xff0c;闷热的夜&#xff0c;令人窒息&#xff0c;窗外一道道闪电划破漆黑的夜幕&#xff0c;小猫塞着耳机听着恐怖小说&#xff0c;辗转反侧&#xff0c;终于睡意来了&#xff0c;然而挨…

50、基于NARX神经网络的磁悬浮建模(matlab)

1、NARX神经网络简介 NARX&#xff08;非线性自回归外部输入&#xff09;神经网络是一种用于非线性建模和预测的神经网络结构。与传统的自回归模型不同&#xff0c;NARX网络可以接收外部输入来影响输出结果&#xff0c;从而更好地捕捉系统的复杂性和非线性特征。 NARX神经网络…

正版软件 | DeskScapes:将您的桌面变成生动的画布

您是否厌倦了静态的桌面背景&#xff1f;Stardock 的 DeskScapes 软件赋予您将任何图片或视频动画化的能力&#xff0c;让您的 Windows 桌面焕发活力。 动画桌面&#xff0c;艺术生活 使用 DeskScapes 您可以将任何静态图片或视频转化为桌面背景。不仅如此&#xff0c;通过 60 …

项目1111

中文显示姓名列和手机号 SELECT contact_name AS 姓名, contact_phone AS 手机号 FROM 2_公司id; 使用explain测试给出的查询语句&#xff0c;显示走了索引查询 EXPLAIN SELECT * FROM 7_订单数量 WHERE countid LIKE e%; 统计用户订单信息&#xff0c;查询所有用户的下单数量…

基于 GD32F450 的Zephyr 的基本测试-编译工程

一、cmake 编译 hello world 测试 打开示例工程 hello world cd ~/zephyrproject/zephyr/samples/hello_world新建 build 目前&#xff0c;用于存放临时文件目录&#xff0c;并进入该目录 mkdir -p build && cd build通过 cmake 指令 生成 gd32f450z 工程的 makefil…

Shell 编程入门

优质博文&#xff1a;IT-BLOG-CN 【1】x.sh文件内容编写&#xff1a; 固定开头&#xff1a;#&#xff01;/bin/sh&#xff1b; 【2】学习的第一个命令就是echo输出的意思&#xff1b; 【3】其实shell脚本也就是在文件中写命令&#xff0c;但是我们要写的是绝对路径&#xff1a…

k8s token加新节点

在 master 节点执行 kubeadm token create --print-join-command得到token和cert&#xff0c;这两个参数在2个小时内可以重复使用&#xff0c;超过以后就得再次生成 kubeadm join apiserver.k8s.com --token mpfjma.4vjjg8flqihor4vt --discovery-token-ca-cert-hash sha…