文章目录
- 前言
- 一、火箭筒
- 1. 编写火箭筒脚本
- 2. 创建火箭弹和新爆炸特效的预制体
- 3. 编写火箭弹脚本
- 4. 设置好火箭弹和火箭筒的脚本和参数
- 5. 运行效果
- 二、激光枪
- 1. 编写激光枪脚本
- 2. 先运行游戏,看看效果
- 3. 美化射线
- 4. 完善代码
- 5. 再次运行游戏
- 6. 升级URP项目
- 7. 后处理
- 8. 新建Shader Graph
- 9. 新建材质
- 10. 运行效果
- 三、机枪
- 1. 配置LineRenderer子弹
- 2. 编写脚本
- 3. 编写步枪脚本
- 4. 运行效果
- 源码
- 参考
- 完结
前言
本文紧接上篇文章:制作俯视角射击游戏多种射击效果(一)
没看过上期的建议先去看看,这篇文章我们将继续实现曲线射击与两种不需要实体子弹的射击方式
源码在文章末尾
一、火箭筒
除了常规的设置直线速度发射子弹,我们也可以通过不断改变移动方向达到曲线射击的效果
1. 编写火箭筒脚本
为火箭筒新建一个脚本RocketLauncher并进入
首先同样也需要继承父类Gun,和散弹枪的思路类似,火箭筒也可以一次发射出多个火箭弹
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 火箭发射器类
public class RocketLauncher : Gun
{
// 火箭数量
public int rocketNum = 3;
// 火箭发射角度
public float rocketAngle = 15;
// 重写父类的开火方法
protected override void Fire()
{
// 播放射击动画
animator.SetTrigger("Shoot");
// 延迟开火
StartCoroutine(DelayFire(.2f));
}
// 延迟开火协程
IEnumerator DelayFire(float delay)
{
yield return new WaitForSeconds(delay);
// 计算中间位置
int median = rocketNum / 2;
// 循环发射火箭
for (int i = 0; i < rocketNum; i++)
{
// 从对象池中获取火箭
GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
// 设置火箭位置
bullet.transform.position = muzzlePos.position;
// 根据火箭数量的奇偶性设置火箭发射角度
if (rocketNum % 2 == 1)
{
bullet.transform.right = Quaternion.AngleAxis(rocketAngle * (i - median), Vector3.forward) * direction;
}
else
{
bullet.transform.right = Quaternion.AngleAxis(rocketAngle * (i - median) + rocketAngle / 2, Vector3.forward) * direction;
}
// 设置火箭目标
bullet.GetComponent<Rocket>().SetTarget(mousePos);
}
}
}
2. 创建火箭弹和新爆炸特效的预制体
因为和普通子弹基本一致,只是添加了一个烟雾的粒子系统作为子物体,爆炸特效也是一样
所以我提前准备好了预制体就不再演示了
3. 编写火箭弹脚本
但脚本逻辑和子弹还是有很大不同的,所以给火箭弹新建脚本Rocket
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rocket : MonoBehaviour
{
public float lerp; // 插值系数
public float speed = 15; // 移动速度
public GameObject explosionPrefab; // 爆炸特效预制体
new private Rigidbody2D rigidbody; // 刚体组件
private Vector3 targetPos; // 目标位置
private Vector3 direction; // 移动方向
private bool arrived; // 是否到达目标点
private void Awake()
{
rigidbody = GetComponent<Rigidbody2D>(); // 获取刚体组件
}
public void SetTarget(Vector2 _target) // 设置目标点
{
arrived = false; // 重置到达标志位
targetPos = _target; // 设置目标点
}
private void FixedUpdate()
{
direction = (targetPos - transform.position).normalized; // 计算移动方向
if (!arrived) // 如果还没到达目标点
{
transform.right = Vector3.Slerp(transform.right, direction, lerp / Vector2.Distance(transform.position, targetPos)); // 插值旋转
rigidbody.velocity = transform.right * speed; // 设置速度
}
if (Vector2.Distance(transform.position, targetPos) < 1f && !arrived) // 如果到达目标点
{
arrived = true; // 设置到达标志位
}
}
private void OnTriggerEnter2D(Collider2D other) // 碰撞检测
{
GameObject exp = ObjectPool.Instance.GetObject(explosionPrefab); // 从对象池中获取爆炸特效
exp.transform.position = transform.position; // 设置特效位置
rigidbody.velocity = Vector2.zero; // 停止移动
StartCoroutine(Push(gameObject, .3f)); // 延迟回收
}
IEnumerator Push(GameObject _object, float time) // 延迟回收协程
{
yield return new WaitForSeconds(time); // 等待一段时间
ObjectPool.Instance.PushObject(_object); // 回收对象
}
}
4. 设置好火箭弹和火箭筒的脚本和参数
5. 运行效果
可以看到火箭筒的攻击已经达到我们预期的效果
二、激光枪
接下来介绍两种特殊的射击方式
一般的枪械都是通过生成子弹并让子弹移动实现发射的功能
发射出的子弹有一个移动的过程,并且检测击中也是由子弹单独完成
不过有一些特殊的枪械还使用实体子弹,这种枪械”发射”的是一种瞬间到达的子弹
在unity中称为“射线”,激光就是一种典型的射线
接下来我们就从激光枪入手,通过射线检测实现这种特殊的射击方式
1. 编写激光枪脚本
为激光枪创建脚本Lasergun,同样继承枪械父类Gun
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lasergun : Gun
{
private bool isShooting;
// 重写父类的Shoot方法
protected override void Shoot()
{
// 计算枪口朝向
direction = (mousePos - new Vector2(transform.position.x, transform.position.y)).normalized;
transform.right = direction;
// 检测是否按下射击键
if (Input.GetButtonDown("Fire1"))
{
isShooting = true;
}
// 检测是否松开射击键
if (Input.GetButtonUp("Fire1"))
{
isShooting = false;
}
// 设置Shoot动画状态
animator.SetBool("Shoot", isShooting);
// 如果正在射击,则发射子弹
if (isShooting)
{
Fire();
}
}
// 发射子弹
protected override void Fire()
{
// 检测射线碰撞
RaycastHit2D hit2D = Physics2D.Raycast(muzzlePos.position, direction, 30);
// 绘制射线
Debug.DrawLine(muzzlePos.position, hit2D.point);
}
}
2. 先运行游戏,看看效果
回到Unityi运行游戏,按住鼠标左键,可以看到一条射线从枪口向鼠标方向射出了
3. 美化射线
但使用Debug绘制的线条太过简陋,也只能在开发时看到
所以接下来使用其他方法绘制更加美观的射线
我们需要像DrawLine方法一样设置起始和结束位置,LineRenderer就是一个不错的选择
找到激光枪的枪口子物体,添加上LineRenderer组件
LineRenderer将根据可用点的位置绘制多段线条,我们只需要一条直线,所以将size设为2
将图层顺序调高防止被地面层遮挡,然后更改一下可用点的位置
可以看到场景中已经出现了一段线条,适当调整宽度让线条适配激光枪的宽度
可以看到场景中已经出现了一段线条,适当调整宽度让线条适配激光枪的宽度
修改线段的颜色让其符合激光枪的配色,我这里就设置为红色
LineRender的颜色设置窗口和正常的略有不同,可以分别修改两个边界的颜色和透明度
中间的值会自动进行渐变,因为需要纯红色所以两端都设置为红色,也不需要修改透明度
你也可以根据自己的想法设置出更绚丽的颜色
接着增加末端顶点的值让线段的两端更加圆滑,这样一条用作显示激光的线段就完成了
我们希望只有在开枪时才显示线段,所以先将LineRenderer取消激活
4. 完善代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lasergun : Gun
{
private GameObject effect; // 特效
private LineRenderer laser; // 激光线
private bool isShooting; // 是否正在射击
protected override void Start()
{
base.Start();
laser = muzzlePos.GetComponent<LineRenderer>(); // 获取激光线组件
effect = transform.Find("Effect").gameObject; // 获取特效物体
}
protected override void Shoot()
{
direction = (mousePos - new Vector2(transform.position.x, transform.position.y)).normalized; // 获取射击方向
transform.right = direction; // 转向射击方向
if (Input.GetButtonDown("Fire1")) // 按下射击键
{
isShooting = true; // 正在射击
laser.enabled = true; // 显示激光线
effect.SetActive(true); // 显示特效
}
if (Input.GetButtonUp("Fire1")) // 松开射击键
{
isShooting = false; // 停止射击
laser.enabled = false; // 隐藏激光线
effect.SetActive(false); // 隐藏特效
}
animator.SetBool("Shoot", isShooting); // 设置射击动画
if (isShooting) // 正在射击
{
Fire(); // 发射子弹
}
}
protected override void Fire()
{
RaycastHit2D hit2D = Physics2D.Raycast(muzzlePos.position, direction, 30); // 发射射线
// Debug.DrawLine(muzzlePos.position, hit2D.point);
laser.SetPosition(0, muzzlePos.position); // 设置激光线起点
laser.SetPosition(1, hit2D.point); // 设置激光线终点
effect.transform.position = hit2D.point; // 设置特效位置
effect.transform.forward = -direction; // 设置特效方向
}
}
5. 再次运行游戏
回到Unity再次运行游戏,按住鼠标左键,现在就可以看到一条更美观的射线了
不过就算这样这也只是一条线接下来需要让这条线更像激光
我们将使用后处理Post-Processing实现这一效果,你可以将后处理理解为一种滤镜
为了使用后处理,我们需要让项目升级为URP通用渲染管线项目
你可以在创建项目时就选择URP模板创建,不过这里使用第二种方法升级
6. 升级URP项目
我们可以到Windows->Package Manager中搜索universal-导入URP的包
导入包后还需要一些步聚让项自使用URP
右键Creat-.>Rendering创建一个URP的Asset
Unity创建出了两个文件,我们选择名字不带render的文件并勾选HDR选项
然后在Project Setting->Graphics的pipeline setting选项中设置刚创建的URP Asset
最后勾选相机下的poost processingi选项就设置完成了
7. 后处理
接下来就可以在层次视图下右键创建一个volume,选择global volume全局生效
可以看到场景中生成了一个名为Global Volume的物体
将由这个物体下的volumel脚本给相机添加后处理特效
首先在Profile属性下点击新建创建一个新的volume配置文件
创建完成后下方将出现Add Override的按钮,点击按钮创建一个新特效
选择Bloomi泛光效果,Bloom会产生一个明亮的光晕效果,非常适合用在激光上
目前只需要勾选前两个选项,Threshold属性将设定Bloom产生效果的阈值
Intensity属性则将影响Bloom产生的泛光效果的强度
试着将阈值调低,可以看到整个场景稍微亮了一些
8. 新建Shader Graph
但我们需要更精确的调整某个物体的泛光值,这时就需要使用HDR了
HDR可以在设置颜色的同时设置强度,这个强度可用在BIoom的阈值上
也就是说,只要将Bloom的阈值提高到1以上
只有我们设置过强度的HDR颜色,Bloom才会进行渲染
要实现这个功能,我们需要自己创建一个Shader来创建材质
使用URP中的Shader Graph可以很方便的创建Shader
右键Creat->Shader Graph创建一个Shader Graph
重命名为ColorGraph并双击打开
这个Shader需要显示一个HDR的颜色,所以在左侧的黑板创建一个颜色属性
点击加号创建一个Color属性并选择模式为HDR,你可以将属性理解为脚本中的公有变量
要让渲染的物体显现出来还需要一个纹理,所以需要再创建一个Texture属性
你可以注意到主区域已经存在一个节点master
我们需要将对应数值输出到这个节点来显示相应的效果
首先右键创健一个simple texture2d节点来将纹理转换为颜色输出
将刚创建的纹理属性拖拽到主区域中并连接到节点作为输入
然后创建一个add节点并让colorj属性和节点输出的颜色相加达到改变颜色的效果
将这个改变后的颜色连接到master节点的color.上,这样就可以通过color属性改变颜色了
不要忘记点击左上角的Save Asset保存后再退出Shader Graph
9. 新建材质
右键ColorGraphi根据这个shadert创建一个材质起名为BrightMaterial
首先创建一个正方形的sprite传给texture,然后将颜色的强度提高到1以上
将这个材质设置给显示激光的线段渲染器
10. 运行效果
运行游戏,可以看到线段有一种明亮的感觉,看起来更像激光了
也可以在激光末端添加一个粒子特效产生一个击中目标的感觉提升观感
三、机枪
除了激光还有一种射击方式不需要实体子弹
这种发射方式同样使用射线检测,通过模拟子弹的发射轨迹来实现效果
这种方式和实体子弹一样,每次发射会生成一个物体作为弹道轨迹
所以我们需要先创建弹道的预制体,这里同样使用LineRenderer模拟
1. 配置LineRenderer子弹
在场景中创建一个空物体起名为BulletTracer并添加线段渲染器
同样设置端点数为2、末端顶点为90并提高图层顺序避免被遮挡
调整线段的宽度到合适的感觉,也可以在线段的末端右键添加key来过渡线段的宽度
我这里为了美观略微增加了线段的末端宽度
然后调整颜色,点击下方的箭头将轨迹的颜色设置为纯灰色
接着点击上方的箭头设置透明度,让透明度随着距离从0逐渐增加
2. 编写脚本
然后编写脚本BulletTracer并添加给这个物体
这个脚本的功能和弹壳类似,让轨迹生成后逐渐淡出,完全透明后销毁物体
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletTracer : MonoBehaviour
{
public float fadeSpeed; // 渐隐速度
private LineRenderer line; // 线渲染器
private float alpha; // 透明度
private void Awake()
{
line = GetComponent<LineRenderer>(); // 获取线渲染器组件
alpha = line.endColor.a; // 获取线渲染器的结束颜色的透明度
}
private void OnEnable()
{
line.endColor = new Color(line.endColor.r, line.endColor.g, line.endColor.b, alpha); // 设置线渲染器的结束颜色的透明度
StartCoroutine(Fade()); // 开始渐隐协程
}
IEnumerator Fade()
{
while (line.endColor.a > 0) // 当线渲染器的结束颜色的透明度大于0时
{
line.endColor = new Color(line.endColor.r, line.endColor.g, line.endColor.b, line.endColor.a - fadeSpeed); // 透明度减少
yield return new WaitForFixedUpdate(); // 等待下一帧
}
ObjectPool.Instance.PushObject(gameObject); // 回收对象
}
}
回到Unity,将BulletTracer物体拖拽到资源窗口中制作成预制体就制作完成了
3. 编写步枪脚本
然后编写步枪脚本Rifle并添加给步枪子物体
这个枪械的功能与基础枪械基本一致,只是要将生成的子弹替换为弹道预制体
这个步骤只涉及发射部分,所以重写Fire函数即可
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rifle : Gun
{
// 重写父类的Fire方法
protected override void Fire()
{
// 播放射击动画
animator.SetTrigger("Shoot");
// 发射射线检测碰撞
RaycastHit2D hit2D = Physics2D.Raycast(muzzlePos.position, direction, 30);
// 获取对象池中的子弹并设置轨迹
GameObject bullet = ObjectPool.Instance.GetObject(bulletPrefab);
LineRenderer tracer = bullet.GetComponent<LineRenderer>();
tracer.SetPosition(0, muzzlePos.position);
tracer.SetPosition(1, hit2D.point);
// 获取对象池中的弹壳并设置位置和旋转
GameObject shell = ObjectPool.Instance.GetObject(shellPrefab);
shell.transform.position = shellPos.position;
shell.transform.rotation = shellPos.rotation;
}
}
回到Unity,给Rifle脚本配置参数然后运行游戏
4. 运行效果
切换到步枪开始射击,现在可以看到发射的弹道轨迹效果了
到这里所有枪械的射击方式就介绍完了
如果你发现了文章中存在错误或者有更好的解决方法,也欢迎在评论区留言
源码
https://gitcode.net/unity1/TopDownShooter
参考
【视频】:https://www.bilibili.com/video/BV1Mh411v7PU
完结
如果你有其他更好的方法也欢迎评论分享出来,当然如果发现文章中出现了什么问题或者疑问的话,也欢迎评论私信告诉我哦
好了,我是向宇,https://xiangyu.blog.csdn.net/
一位在小公司默默奋斗的开发者,出于兴趣爱好,于是开始自习unity。最近创建了一个新栏目【你问我答】,主要是想收集一下大家的问题,有时候一个问题可能几句话说不清楚,我就会以发布文章的形式来回答。 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~