【Unity学习心得】如何制作俯视角射击游戏

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、导入素材
  • 二、制作流程
    • 1.制作地图
    • 2.实现人物动画和移动脚本
    • 3.制作单例模式和对象池
    • 4.制作手枪pistol和子弹bullet和子弹壳bulletShell
    • 5.制作散弹枪shotgun
  • 总结


前言

俯视角射击游戏类似元气骑士那种,懂的都懂好吧。

本节课我们将要实现:制作地图,实现人物动画和移动脚本,制作单例模式和对象池,制作手枪pistol和子弹bullet和子弹壳bulletShell,制作散弹枪shotgun。


一、导入素材

素材链接: 、

https://o-lobster.itch.io/simple-dungeon-crawler-16x16-pixel-pack https://humanisred.itch.io/weapons-and-bullets-pixel-art-asset

二、制作流程

1.制作地图

        还是要用我们的老朋友Tilemap来做这种像素地图:

可以看到,我们创建了三个层级的Grid,记得在Sorting Layer分别给它们排好选择顺序,除了Ground的那一层以外其它记得要给 tilemap Collider2D和Composite Collider2D,Rigibody2D记得设置成静态,这些后面都要用到的。 

2.实现人物动画和移动脚本

绘制完简单地图后,我们就要开始导入人物素材了。公式化三件套:Sprite Renderer,Rb2D设置为Kinematic被动检测碰撞,别忘了锁Z轴旋转,Animator自己根据素材创建Idle,Walk

接下来创建PlayerController.cs给Player对象 

代码如下:

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

public class PlayerController : SingletonMono<PlayerController>
{
    public float speed = 3f;
    public bool enabledInput = true;
    private Rigidbody2D rb2d;
    private Animator anim;
    private Camera mainCamera;
    private Vector2 input;
    private Vector2 mousePosition;

    private new void Awake()
    {
        rb2d = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
	    mainCamera = Camera.main;
    }

    void Update()
    {
	if (enabledInput)
	{
        mousePosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
	    input.x = Input.GetAxisRaw("Horizontal");
	    input.y = Input.GetAxisRaw("Vertical");
	    rb2d.velocity = input.normalized * speed;

	    if(input != Vector2.zero)
	    {
		    anim.SetBool("isMoving", true);
	    }
	    else
	    {
		    anim.SetBool("isMoving", false);
	    }

	    if(mousePosition.x >= transform.position.x)
	    {
		transform.rotation = Quaternion.Euler(new Vector3(0f, 0f, 0f));
	    }
	    else
	    {
		transform.rotation = Quaternion.Euler(new Vector3(0f, 180f, 0f));
	    }
	}
    }

}

这些大伙都应该懂了就获取X和Y上的Input,方向化后经典给rb2d设置velocity,然后根据鼠标位置判断玩家是否要沿Y轴翻转。

顺便给camera搞个cinemachine,让相机跟随玩家移动:

至此,我们实现了实现人物动画和移动脚本,接下来该开始制作单例模式和对象池模式了。

3.实现单例模式和对象池模式

单例模式大伙肯定也懂的,但这几天重温C#知识我突然就想用泛型<T>来做个泛型单例类,让所有MONOBehaviour的类都能继承:

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

public class SingletonMono<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T _instance;
    public static T Instance
    {
	get
	{
	    if(_instance == null)
	    {
		GameObject gameObject = new GameObject();
		_instance = gameObject.AddComponent<T>();
		DontDestroyOnLoad(gameObject);
	    }
	    return _instance;
	}
    }

    public void Awake()
    {
	if (_instance == null)
	{
	    _instance = (gameObject as T);
	    DontDestroyOnLoad(gameObject);
	    return;
	}
	if (this != _instance)
	{
	    DestroyImmediate(gameObject);
	    return;
	}
    }
}

单例模式随便完成咯,接下来开始做对象池模式,很多人对对象池的编写还是比较陌生的,这里先写出主要思想:

在Unity中,实时创建(GameObject.Instantiate())和销毁游戏对象(GameObject.Destory ())会造成相当大的开销。

对于一些简单的,可以复用的物体,我们可以考虑用Enable/Disable来代替创建与销毁,这是因为Enable/Disable对性能的消耗搞更小。

我们可以采用对象池的思想实现这个功能。

所谓对象池,就是把所有相同的对象放在一个池子中,每当要使用到一个对象时,就从池子中找找看有没有之前创建过但现在空闲的物体可以直接拿来用,如果没有的话我们再创建物体并扔进池子里。

想要销毁一个物体,我们只需要标记其“空闲”即可,并不会直接销毁它。

 理解过后我们就可以编写一个脚本ObjectPool.cs

using System.Collections.Generic;
using UnityEngine;

public class ObjectPool : SingletonMono<ObjectPool>
{
    private Dictionary<string, Queue<GameObject>> objectPool = new Dictionary<string, Queue<GameObject>>();
    private GameObject pool;

    private new void Awake()
    {
        objectPool = new Dictionary<string, Queue<GameObject>>();
    }

//从对象池中获取对象
    public GameObject GetObject(GameObject prefab)
    {
        GameObject gameObject;
//先从name判断是否存在对应的键值或者队列的内容数量为0
        if (!objectPool.ContainsKey(prefab.name) || objectPool[prefab.name].Count == 0)
        {
//如果没有就新建从Object Pool -> Child Pool -> Prefab
            gameObject = GameObject.Instantiate(prefab);
            PushObject(gameObject);
            if (pool == null)
                pool = new GameObject("ObjectPool");
            GameObject childPool = GameObject.Find(prefab.name + "Pool");
            if (!childPool)
            {
                childPool = new GameObject(prefab.name + "Pool");
                childPool.transform.SetParent(pool.transform);
            }
            gameObject.transform.SetParent(childPool.transform);
        }
        gameObject = objectPool[prefab.name].Dequeue();
        gameObject.SetActive(true); //可视化
        return gameObject;
    }
//从对象池中取出对象
    public void PushObject(GameObject prefab)
    {
//要保证名字和objectPool的名字相等,因此我们要用空的字符串取代Unity新建游戏对象会有个"(Clone)"
        string name = prefab.name.Replace("(Clone)", string.Empty);
        if (!objectPool.ContainsKey(name))
            objectPool.Add(name, new Queue<GameObject>());
        objectPool[name].Enqueue(prefab); //创建该prefab名字的队列并让prefab入栈
        prefab.SetActive(false);//默认为不可见
    }
}

4.制作手枪pistol和子弹bullet和子弹壳bulletShell

终于来到了重点制作这些东西,当一些事物存在共性的时候我们会想使用抽象类来减少代码的耦合度,同样这些手枪火箭筒啥的都输入枪,我们可以创建一个Gun的Prefab,让这些枪都成为Gun的Varient。

其中muzzle是子弹发射位置,bulletshell是生成弹壳的位置。

我们Varient第一个目标是pistol手枪,如图我们给它sprite和animator

制作两个动画并连接即可

别忘了动态调整muzzle和bulletshell的位置

回到脚本当中,新建一个基类脚本Gun.cs,我们打算让所有的枪械类都继承这个脚本:

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

public class Gun : MonoBehaviour
{
    public GameObject bulletPrefab;
    public GameObject bulletShellPrefab;
    public float interval = 0.5f;
    protected Animator animator;
    protected Camera mainCamera;
    protected Transform muzzleTrans;
    protected Transform bulletShellTrans;
    protected float flipY;
    protected Vector2 mousePos;
    protected Vector2 direction;
    private float timer;

    protected virtual void Start()
    {
	animator = GetComponent<Animator>();
	mainCamera = Camera.main;
	muzzleTrans = transform.Find("Muzzle"); ;
	bulletShellTrans = transform.Find("BulletShell");
	flipY = transform.localScale.y;
    }

    protected virtual void Update()
    {
	mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
	if(mousePos.x >= transform.position.x)
	{
	    transform.localScale = new Vector3(transform.localScale.x, flipY, 1);
	}
	else
	{
	    transform.localScale = new Vector3(transform.localScale.x, -flipY, 1);
	}
	Shoot();
    }

//控制枪械发射间隔
    protected virtual void Shoot()
    {
	direction = (mousePos - new Vector2(transform.position.x, transform.position.y)).normalized;
	transform.right = direction;
	if (timer != 0)
	{
	    timer -= Time.deltaTime;
	    if (timer < 0)
		timer = 0;
	}
	if (Input.GetKeyDown(KeyCode.Mouse0)) //按下鼠标左键
	{
	    if(timer == 0)
	    {
		timer = interval;
		Fire();
	    }
	}
    }
//控制开火
    protected virtual void Fire()
    {
	animator.SetTrigger("Shoot");
//生成子弹预制体
	GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
	bullet.transform.position = muzzleTrans.position;
//发射子弹角度偏差
	float angle = UnityEngine.Random.Range(-5f, 5f);
	bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(angle , Vector3.forward) * direction);
//生成子弹壳预制体
	GameObject bulletShell = ObjectPool.Instance.GetObject(bulletShellPrefab);
	bulletShell.transform.position = bulletShellTrans.position;
	bulletShell.transform.rotation = bulletShellTrans.rotation;
    }
}

然后我们再给pistol添加同名脚本:

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

public class Pistol : Gun 
{
   
}

 写到这里我们注意到还要接着制作子弹Bullet和子弹壳BulletShell的预制体:

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

public class Bullet : MonoBehaviour
{
    public float bulletSpeed = 15f;
    public GameObject explosionPrefab;
    private Rigidbody2D rb2d;

    private void Awake()
    {
	rb2d = GetComponent<Rigidbody2D>();
    }
//在这里,我们用通过给子弹的rb2d设置速度velocity控制其移动速度和方向
    public void SetSpeed(Vector2 direction)
    {
	rb2d.velocity = bulletSpeed * direction;
    }
//最后当子弹碰到墙壁时对象池回收该对象并生成爆炸对象
    private void OnTriggerEnter2D(Collider2D other)
    {
	if (other.gameObject.layer == LayerMask.NameToLayer("Wall"))
	{
	    GameObject exp = ObjectPool.Instance.GetObject(explosionPrefab);
	    exp.transform.position = transform.position;
	    ObjectPool.Instance.PushObject(gameObject);
	}
    }

}

新建一个bullet的prefab

新建一个Explosion的prefab

 

给它制作一个爆炸的动画

在它的同名脚本中回收爆炸游戏对象:

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

public class Explosion : MonoBehaviour
{
    private Animator anim;
    private AnimatorStateInfo info;

    private void OnEnable()
    {
	anim = GetComponent<Animator>();
    }

    private void Update()
    {
	info = anim.GetCurrentAnimatorStateInfo(0);
	if(info.normalizedTime >= 1)
	{
	    ObjectPool.Instance.PushObject(gameObject);
	}
    }

}

 同样的操作也给bulletshell

同名脚本中:

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

public class BulletShell : MonoBehaviour
{
    public float speed;
    public float stopTime = 0.5f;
    public float fadeSpeed = 0.01f;
    private Rigidbody2D rb2d;
    private SpriteRenderer sprite;

    void Awake()
    {
        rb2d = GetComponent<Rigidbody2D>();
        sprite = GetComponent<SpriteRenderer>();
    }

    private void OnEnable()
    {
        float angel = Random.Range(-30f, 30f);
        rb2d.velocity = Quaternion.AngleAxis(angel, Vector3.forward) * Vector3.up * speed;

        sprite.color = new Color(sprite.color.r, sprite.color.g, sprite.color.b, 1);
        rb2d.gravityScale = 3;

        StartCoroutine(Stop());
    }

    private IEnumerator Stop()
    {
        yield return new WaitForSeconds(stopTime);
        rb2d.velocity = Vector2.zero;
        rb2d.gravityScale = 0;
//通过spriterenderer的透明度来渐变颜色淡出直到0
        while (sprite.color.a > 0)
        {
            sprite.color = new Color(sprite.color.r, sprite.color.g, sprite.color.g, sprite.color.a - fadeSpeed);
            yield return new WaitForFixedUpdate();
        }
//然后回收该游戏对象
        ObjectPool.Instance.PushObject(gameObject);
    }
}

5.制作散弹枪shotgun

有了前车之鉴,我们就可以照着葫芦画瓢。还是老配方先生成varient:

别忘了调整muzzle和bulletshell的位置。

创建同名脚本:

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

public class ShotGun : Gun
{
    public int bulletNum = 3;
    public float bulletAngle = 15f;
//我们只需要重写fire脚本
    protected override void Fire()
    {
	animator.SetTrigger("Shoot");
	int med = bulletNum / 2;
	for (int i = 0; i < bulletNum; i++)
	{
	    GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
	    bullet.transform.position = muzzleTrans.position;
	    if (bulletNum % 2 == 1)
	    {
//这段代码的意思是如果有奇数个bulletnum那给这个bulletshell设置的角度应该是bulletAngle * (i - //med)
		bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(bulletAngle * (i - med), Vector3.forward) * direction);
	    }
	    else if(bulletNum % 2 == 0)
	    {
//这段代码的意思是如果有偶数个bulletnum那给这个bulletshell设置的角度应该是bulletAngle * (i - //med) + bulletAngle / 2f
		bullet.GetComponent<Bullet>().SetSpeed(Quaternion.AngleAxis(bulletAngle * (i - med) + bulletAngle / 2f, Vector3.forward) * direction);
	    }
	}
	GameObject shell = ObjectPool.Instance.GetObject(bulletShellPrefab);
	shell.transform.position = bulletShellTrans.transform.position;
	shell.transform.rotation = bulletShellTrans.transform.rotation;
    }
}

可以看到奇数个子弹:

偶数个子弹:你看是不是还要再加偏转角的一半即bulletAngle / 2

最后我们还想要根据键盘的Q和E键切换武器,回到playerController.cs当中:

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

public class PlayerController : SingletonMono<PlayerController>
{
    public float speed = 3f;
    public bool enabledInput = true;
    public GameObject[] guns;
    private int currentGun;
    private Rigidbody2D rb2d;
    private Animator anim;
    private Camera mainCamera;
    private Vector2 input;
    private Vector2 mousePosition;

    private new void Awake()
    {
        rb2d = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
	mainCamera = Camera.main;
	guns[0].SetActive(true);
    }

    void Update()
    {
	if (enabledInput)
	{
	    SwitchGun();
	    mousePosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
	    input.x = Input.GetAxisRaw("Horizontal");
	    input.y = Input.GetAxisRaw("Vertical");
	    rb2d.velocity = input.normalized * speed;

	    if(input != Vector2.zero)
	    {
		anim.SetBool("isMoving", true);
	    }
	    else
	    {
		anim.SetBool("isMoving", false);
	    }

	    if(mousePosition.x >= transform.position.x)
	    {
		transform.rotation = Quaternion.Euler(new Vector3(0f, 0f, 0f));
	    }
	    else
	    {
		transform.rotation = Quaternion.Euler(new Vector3(0f, 180f, 0f));
	    }
	}
    }
//切换武器
    private void SwitchGun()
    {
	if (Input.GetKeyDown(KeyCode.Q))
	{
	    guns[currentGun].SetActive(false);
	    if(--currentGun < 0)
	    {
		currentGun = guns.Length - 1;
	    }
	    guns[currentGun].SetActive(true);
	}
	else if (Input.GetKeyDown(KeyCode.E))
	{
	    guns[currentGun].SetActive(false);
	    if (++currentGun > guns.Length - 1)
	    {
		currentGun = 0;
	    }
	    guns[currentGun].SetActive(true);
	}
    }
}

给这两个武器添加上去(其它三个是下一期要讲的先别在意):


总结

最后的效果如图所示:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MMLU-Pro 基准测试数据集上线,含 12k 个跨学科复杂问题,难度提升,更具挑战性!DeepSeek 数学模型一键部署

在大语言模型 (LLM) 蓬勃发展的时代&#xff0c;诸如大规模多任务语言理解 (MMLU) 之类的基准测试&#xff0c;在推动 AI 于不同领域的语言理解与推理能力迈向极限方面&#xff0c;发挥着至关重要的关键作用。 然而&#xff0c;伴随模型的持续改进与优化&#xff0c;LLM 在这些…

Vue路由:Vue router

目录 路由的基本概念 1. 路由 2. 单页应用SPA 3.前端路由的实现方式 3.1Hash模式 3.2History模式 Vue router 4 1.概述 2.安装使用 3.基础用法 3.1路由匹配规则声明 3.2动态路由匹配 3.3路由命名 3.4路由重定向 3.5路由嵌套 3.6命名视图 3.6声明式导航&编程…

el-input设置type=‘number‘和v-model.number的区别

el-input设置typenumber’与设置.number修饰符的区别 1. 设置type‘number’ 使用el-input时想收集数字类型的数据&#xff0c;我们首先会想到typenumber&#xff0c;设置完type为number时会限制我们输入的内容只能为数字&#xff0c;不能为字符/汉字等非数字类型的数值&…

【网络安全】-文件下载漏洞-pikachu

文件操作漏洞包括文件上传漏洞&#xff0c;文件包含漏洞&#xff0c;文件下载漏洞。 文章目录  前言 什么是文件下载漏洞&#xff1f; 1.常见形式&#xff1a; 常见链接形式&#xff1a; 常见参数&#xff1a; 2.利用方式&#xff1a; 3.举例&#xff1a;pikachu不安全的文件…

智能语音技术在人机交互中的应用与发展

摘要&#xff1a;本文主要探讨智能自动语音识别技术与语音合成技术在构建智能口语系统方面的作用。这两项技术实现了人机语音通信&#xff0c;建立起能听能说的智能口语系统。同时&#xff0c;引入开源 AI 智能名片小程序&#xff0c;分析其在智能语音技术应用场景下的意义与发…

使用ESP8266和OLED屏幕实现一个小型电脑性能监控

前言 最近大扫除&#xff0c;发现自己还有几个ESP8266MCU和一个0.96寸的oled小屏幕。又想起最近一直想要买一个屏幕作为性能监控&#xff0c;随机开始自己diy。 硬件&#xff1a; ESP8266 MUColed小屏幕杜邦线可以传输数据的数据线 环境 Windows系统Qt6Arduino Arduino 库…

计算架构模式之负载均衡技巧

通用负载均衡算法 负载均衡算法 -轮询 & 随机 如果服务器挂掉了&#xff0c;那么负载均衡器还是可以感知到的&#xff0c;因为连接已经断掉了。 负载均衡算法-加权轮询 假设你有4核的和8核的&#xff0c;由于你的程序没有办法跑完CPU&#xff0c;那么有可能出现4核的和8核…

Coggle数据科学 | 科大讯飞AI大赛:人岗匹配挑战赛 赛季3

本文来源公众号“Coggle数据科学”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;科大讯飞AI大赛&#xff1a;人岗匹配挑战赛 赛季3 赛题名称&#xff1a;人岗匹配挑战赛 赛季3 赛题类型&#xff1a;自然语言处理、文本匹配 赛题…

Pikachu靶场之csrf

CSRF 跨站请求伪造 CSRF入门及靶场实战 - FreeBuf网络安全行业门户 攻击者伪造恶意链接&#xff0c;诱使用户点击&#xff0c;这个链接附带了用户的认证凭据Cookie、Session等&#xff0c;执行操作如转账。 因为带了cookie、session&#xff0c;服务器认为是用户的行为。借用…

【诉讼流程-健身房-违约认定-私教课-诉讼书前提材料整理-民事诉讼-自我学习-铺平通往法律的阶梯-讲解(2)】

【诉讼流程-健身房-违约-私教课-前期法律流程-民事诉讼-自我学习-铺平通往法律的阶梯-讲解&#xff08;2&#xff09;】 &#xff08;1&#xff09;前言说明1、目的2、一个小测试1、更换原教练2、频繁更换教练3、上课估计拖课&#xff0c;占用上课时间&#xff0c;抽烟等。4、以…

谈谈LLM训练中的“过拟合”与“欠拟合”

如今&#xff0c;由于其出色的理解、生成和操纵人类语言的能力&#xff0c;语言模型已经成为焦点。据最新调查数据显示&#xff0c;大概30%的企业计划使用非结构化数据来提高大型语言模型&#xff08;LLM&#xff09;的准确性。在训练这些语言模型时&#xff0c;一个基本挑战是…

知识笔记合集

文章目录 vsCode可以运行c程序却无法运行c程序帆软填报属性不起作用java-实体类日期类型格式化Java-数据库id字段使用雪花算法IDEA-快捷键 vsCode可以运行c程序却无法运行c程序 vsCode中的tasks.json文件中添加"-lstdc" {"tasks": [{"type": &…

【vuetify】v-select 无法正常显示,踩坑记录!

一、上代码 template <v-selectv-model"editedUser.userRole":items"roles"label"角色"item-value"value":rules"[rules.required]" ></v-select>script const editedUser ref({userRole: customer // 设置…

【LabVIEW学习篇 - 21】:DLL与API的调用

文章目录 DLL与API调用DLLAPIDLL的调用 DLL与API调用 LabVIEW虽然已经足够强大&#xff0c;但不同的语言在不同领域都有着自己的优势&#xff0c;为了强强联合&#xff0c;LabVIEW提供了强大的外部程序接口能力&#xff0c;包括DLL、CIN(C语言接口)、ActiveX、.NET、MATLAB等等…

利用 Zero-1-2-3 进行多视图 3D 重建:从单图像到多视图 3D 模型的生成

3D 模型生成在计算机视觉领域有着广泛的应用&#xff0c;从虚拟现实到自动驾驶&#xff0c;基于单张图像的 3D 重建技术正在迅速发展。这篇博客将带你深入探索如何使用 Zero-1-2-3 框架进行多视图 3D 重建&#xff0c;通过详细解析该框架中的代码结构和功能&#xff0c;帮助你理…

MFC工控项目实例之十五定时刷新PC6325A模拟量输入

承接专栏《MFC工控项目实例之十四模拟量信号名称从文件读写》 1、在BoardTest.h文件中添加代码 class CBoardTest : public CDialog { public:short m_saveData[32];unsigned short m_cardAddr;CBoardTest(CWnd* pParent NULL); // standard constructorCButtonST m_btnS…

【新时代概论】新时代概论书目的结构(LP)

文章目录 前言一、结构导论第一章、新时代坚持和发展中国特色社会主义第二章、以中国式现代化全面推进中华民族伟大复兴第三章、坚持党的全面领导第四章、坚持以人民为中心第五章、全面深化改革开放第六章、推动高质量发展第七章、社会主义现代化建设的教育、科技、人才战略第八…

海外云手机怎么实现TikTok多账号防关联?

TikTok多账号运营&#xff0c;作为众多用户选择的引流策略&#xff0c;旨在通过多账号的协同作用&#xff0c;更快速、高效地推动主账号的流量增长。然而&#xff0c;这一策略面临着一个关键难题——TikTok账号防关联。本文将简要介绍海外云手机如何解决这一问题。 在TikTok多账…

2023年408真题计算机网络篇

https://zhuanlan.zhihu.com/p/6954228062023年网络规划设计师上午真题解析TCP流量计算_哔哩哔哩_bilibili 1 1在下图所示的分组交换网络中&#xff0c;主机H1和H2通过路由器互联&#xff0c;2段链路的数据传输速率为100 Mb/s、时延带宽积 &#xff08;即单向传播时延带宽&am…

Solana核心漏洞技术详解

8月9日&#xff0c;Solana团队齐心协力解决了一个严重的安全漏洞。这次秘密修复详情可以在GitHub上查询到。CertiK团队对这一漏洞进行了深入分析。 1. Solana漏洞起因 8月9日&#xff0c;Solana验证者和客户端团队齐心协力解决了一个严重的安全漏洞。Solana验证者Laine表示&am…