【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判断是否存在对应的键值或者队列的内容数量为0if (!objectPool.ContainsKey(prefab.name) || objectPool[prefab.name].Count == 0){
//如果没有就新建从Object Pool -> Child Pool -> PrefabgameObject = 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的透明度来渐变颜色淡出直到0while (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 / 2fbullet.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);}}
}
给这两个武器添加上去(其它三个是下一期要讲的先别在意):

总结
最后的效果如图所示:




相关文章:
【Unity学习心得】如何制作俯视角射击游戏
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、导入素材二、制作流程 1.制作地图2.实现人物动画和移动脚本3.制作单例模式和对象池4.制作手枪pistol和子弹bullet和子弹壳bulletShell5.制作散弹枪shotgun总…...
【资料分析】常见的坑
in 比较或计数类问题 差别大的基期比较,可以直接用现期进行比较 注意单位可能不同! 注意顺序是从小到大还是从大到小 以及老问题,名字本身就叫XX增量,XX增加值,而非还要另外去算的东东 给出的图表可能是不完整的 2…...
GitLab权限及设置
之前很少关注这些,项目的权限,一般由专门的管理人员设置。 但自己创建的项目自己可以设置权限。下面是一些笔记。 GitLab中用户权限_gitlab 权限-CSDN博客 开发中遇到要将自己这块的代码上传到Git,由其他组的同事拉取后继续开发。上传代码后…...
算法练习题24——查找杨辉三角中的组合数
题目描述 杨辉三角中的每个元素是一个组合数。第 ( i ) 行的第 ( j ) 个元素表示组合数 ( C(i, j) ) ,即从 ( i ) 个元素中选 ( j ) 个元素的组合方式。已知一个正整数 ( N ),要求在杨辉三角中找到这个数,并输出它在杨辉三角中的具体位置。位…...
string类的模拟实现
实现string的模拟实现分为三个文件,分别为:string.h、sting.cpp、test.cpp string.h 其中包含一些短小常用的函数的实现,头文件,函数的声明 #include<iostream> #include<string> #include<assert.h>using n…...
如何训练机器学习力场
机器学习力场(MLFF)的训练主要依赖于通过量子力学计算生成的高质量训练数据集,并利用不同的机器学习算法来拟合分子系统中的势能面(Potential Energy Surface, PES)和原子间作用力。这种训练过程包括数据准备、特征提取…...
AI创作新手册:精通Prompt提示词的提问策略
文章目录 🍊AI创作核心:提示词 Prompt 的重要性1. 什么是提示词工程?1.1 提示词的作用原理1.2 提示词工程师的薪资与行业前景1.3 提示词工程的适用性 2. 提示词的编写技巧3. 常见的提示词框架3.1 CO-STAR 框架3.2 BORKE 框架 4. 提示词的实际…...
gingivitis
gingivitis 牙龈炎 1)这个是啥不知道 2)七叶莲片 3)甲硝唑芬布芬胶囊 4)盐酸左氧氟沙星胶囊 5)纳珍 开始学习记录医生开的药。日常备药记录一下。【不要乱吃药哈】...
开源 AI 智能名片小程序:开启内容营销新境界
摘要:本文深入探讨了在当今数字化时代,内容营销的重要性以及如何实现让用户主动找你的最佳效果。通过引入开源 AI 智能名片小程序这一创新工具,阐述了其在明确目标用户群体、迎合用户需求痛点和打造风格特色方面的独特优势,为企业…...
p12docker 进入容器的命令和拷贝的命令
进入当前正在运行的容器 第一种方式是执行docker exec -it 8d57ffda7a29 /bin/bash这个时候可以根据docker容器的id进入到指定id的容器当中***(这个是比较常用的)*** 老师的笔记 第二种方式是docker attach 8d57ffda7a29 这里还是直接引用老师的笔记吧 从容器内部拷贝文…...
代码随想录Day 45|leetcode题目:115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离
提示:DDU,供自己复习使用。欢迎大家前来讨论~ 文章目录 题目题目一: 115.不同的子序列解题思路:1. 确定dp数组(dp table)以及下标的含义2. 确定递推公式3. dp数组如何初始化4. 确定遍历顺序5. 举例推导dp数…...
浮点数在内存中的存储详解(超详细)
目录 1. 浮点数存储规则 2. IEEE754规定: 3. 关于M的说明: 4. 关于E的说明: 5. 关于S的说明: 6.浮点数从内存中取出(三种情况) 情况1:E不全为0或不全为1 情况2:E全为0 情况3&a…...
Maven下载安装
下载 下载地址:Maven – Download Apache Maven 选择合适的版本进行下载 windows&Linux安装 1, 解压apache-maven-3.6.1.rar即安装完成 2, 配置环境变量MAVEN_HOME为安装路径,并将MAVEN_HOME的bin目录配置到PATH下 3,…...
Qt:Q_GLOBAL_STATIC实现单例(附带单例使用和内存管理)
前言 本文主要写Q_GLOBAL_STATIC实现单例以及单例的释放,网上很多教程只有单例的创建,但是并没有告诉我们单例的内存管理,这就很头疼。 正文 使用 Qt 的 Q_GLOBAL_STATIC // Singleton.h #ifndef SINGLETON_H #define SINGLETON_H#includ…...
URL.createObjectURL 与 FileReader:Web 文件处理两大法宝的对比
URL.createObjectURL 与 FileReader:Web 文件处理两大法宝的对比 在Web开发中,处理用户上传的文件是一项常见且重要的任务。URL.createObjectURL和FileReader是两种常用于此目的的Web API,它们各有特点,适用于不同的场景。本文将…...
零基础考过软考信息系统项目管理师经验分享
选择适合的课程:如果你是零基础,建议找一些专门针对新手的课程,讲解通俗易懂。 刷题至关重要:软考的题库很庞大,多做题是必须的。 做好笔记和复习:上课时要做好笔记,课后及时复习,…...
机器学习课程学习周报十二
机器学习课程学习周报十二 文章目录 机器学习课程学习周报十二摘要Abstract一、机器学习部分1.1 fGAN: General Framework of GAN1.2 CycleGAN1.3 Auto-Encoder1.4 概率论复习(一) 总结 摘要 本周的学习内容涵盖了fGAN框架、CycleGAN、自编码器以及概率…...
python多线程程序设计 之二
python多线程程序设计 之二 线程同步机制lock对象acquirereleaselocked RLock对象条件变量条件变量应用实列实列代码 线程同步机制 lock对象 原语锁是一种同步原语,锁定时不属于特定线程。在Python中,它是目前可用的最低级别的同步原语,由_…...
k8s用StatefulSet部署redis
redis-config.yaml (配置文件) apiVersion: v1 kind: ConfigMap metadata:name: redis-config data:redis.conf: |# Redis general configuration bind 0.0.0.0 protected-mode no port 6379 dir /data appendonly yesse…...
flink on k8s
1.修改host文件 vi /etc/hosts 添加如下内容 这样搭集群的时候就不用记ip了 #::1 localhost localhost.localdomain localhost6 localhost6.localdomain6127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 165.154.221.97 tlb-001 k8s01 k8s-m…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
