子弹及其拖尾、子弹射出时的火花、子弹击

子弹及其拖尾、子弹射出时的火花、子弹击

特效分三部分组成:子弹及其拖尾、子弹射出时在枪口的火花、子弹击中处出现的火花,其中大部分地方是用粒子系统实现。然后,子弹的生成和摧毁,以及子弹的方向是用C#脚本实现。

(正常速度,视频时长14秒创作人,无声)

基于粒子系统的攻击特效(无声)

(超慢速unity喷火粒子特效,视频时长13秒,无声)

基于粒子系统的攻击特效 慢速版

1.子弹及其拖尾

先用3ds Max拉一个锥形作为子弹:

unity喷火粒子特效_unity 烟花粒子特效_unity烟雾粒子特效

接下来打开PS画纹理。让图层保持透明。只在子弹头部画上颜色,这样子弹尾翼就可以保持透明。画上去的颜色的不透明度调低到85%,让子弹弹头看起来更透。最后双击图层添加效果--外发光,呈现类似光晕或者bloom的效果。(为了演示的需要3D角色,下面加了一层黑色图层):

unity烟雾粒子特效_unity 烟花粒子特效_unity喷火粒子特效

模型导入到Unity后,新建一个物体挂上粒子系统。找到Render面板,把模型和材质设置进去。设置好Start Color,粒子系统就变成了这样:

unity烟雾粒子特效_unity喷火粒子特效_unity 烟花粒子特效

每次射出时子弹的大小形状都要不一样,而且最好是能旋转。(注意rotation只改y一个维度)。特效的主要颜色是橙色,但是子弹本身的设置成柠檬黄,来fake出一种因为子弹太亮而使其颜色过饱和的感觉:

unity 烟花粒子特效_unity烟雾粒子特效_unity喷火粒子特效

子弹做好了接下来做它的拖尾。添加一个Trail Renderer的component,调整宽度和颜色,让它看起来到尾部会变细变透明,设置如下:

unity喷火粒子特效_unity烟雾粒子特效_unity 烟花粒子特效

现在让子弹在运行的过程中不断闪光,闪光也是用一个粒子系统实现。为了让这一个面片看起来像闪光,打开Color over Lifetime让颜色随时间改变,然后把Renderer中的Render Mode改为billboard是它时时面向摄像机,看起来是一个光球而不是一个片。

unity烟雾粒子特效_unity 烟花粒子特效_unity喷火粒子特效

2.子弹射出时在枪口的火花

枪口会喷火,做喷火的过程和做子弹类似,做一个梯形金字塔的模型+画纹理,挂上粒子系统,此处不再赘述。

unity烟雾粒子特效_unity喷火粒子特效_unity 烟花粒子特效

不同的是,一次特效中喷火只出现一次,所以关掉Looping。

unity喷火粒子特效_unity烟雾粒子特效_unity 烟花粒子特效

闪光和第一步的闪光类似,但是要关掉Looping.

电焊时出现的火花也会在枪口出现,这种火花的特点是会①逐渐消失,②会随重力下落,③会和地面撞击、弹跳。

我们逐个实现:

①打开Size over Lifetime与Color over Lifetime设置尺寸与不透明度在生命周期中逐渐变小。

②然后设置Gravity Modifier赋予其重力。

③勾选Collision使之与地面相撞并弹跳。

3.子弹击中处出现的火花

unity 烟花粒子特效_unity烟雾粒子特效_unity喷火粒子特效

击中的时候,会出现和上一步中的“喷火”类似的效果,此处的喷火与上一步做法基本相同,不同的是把金字塔mesh改成billboard来让它看起来像一个火球。

闪光和“电焊火花”的做法均与上一步相同。

4. C#脚本控制子弹的生成和摧毁,以及子弹的方向

挂在摄像机上的这个脚本需要解决这几个需求:如果鼠标按住不放,子弹需要以一定的频率发射。然后子弹发出的位置是枪管,子弹的方向和枪对齐。具体看我写的注释:

// author: Marcus Xie
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnProjectiles : MonoBehaviour
{
// where the projectile is initially generated
public GameObject firePoint;
// prefab of the projectile
public List<GameObject> vfx = new List<GameObject> ();

private GameObject effectToSpawn;
private float timeToFire = 0f;

void Start()
{
effectToSpawn = vfx[0];
}

void Update()
{
// generate projectiles when mouse is pressed. only allow shots within 1 second
if (Input.GetMouseButton(0) && Time.time >= timeToFire)
{
timeToFire = Time.time + 1f / effectToSpawn.GetComponent<ProjectileMove>().fireRate;
SpawnVFX();
}
}

void SpawnVFX()
{
GameObject vfx;
if (firePoint != null)
{
// generate a projectile at the fire point
vfx = Instantiate(effectToSpawn, firePoint.transform.position, Quaternion.identity);
// and align it with the fire point
vfx.transform.forward = firePoint.transform.forward;
}
else
Debug.Log("Fire Point is not assigned");
}
}

挂在枪上的这个脚本需要解决这几个需求:要让枪旋转到鼠标点到的方向,而鼠标点击的是一个二维坐标,我们需要的是某个物体表面的(如果击中的话)一个三维空间中的点,这就需要做一步转换。得到这个方向之后,要实际地把物体转向这个方向。具体看我写的注释:

// author: Marcus Xie
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateToMouse : MonoBehaviour
{
public Camera cam;
public float maximumLength;

private Ray rayMouse;
private Vector3 pos;
private Vector3 direction;
private Quaternion rotation;

void Update()
{
if (cam != null)
{
// get the position where the mouse clicks ont the screen
var mousePos = Input.mousePosition;
// a ray from camera going towards the direction you clicked
rayMouse = cam.ScreenPointToRay(mousePos);
RaycastHit hit;
// if the ray hit somewhere on a object, rotate the gun towards the hit point
if (Physics.Raycast(rayMouse.origin, rayMouse.direction, out hit, maximumLength))
RotateToMouseDirection(gameObject, hit.point);
else
{
// if the ray fails to hit anywhere, it cannot keep going forever, give it a maximal length to stop
var pos = rayMouse.GetPoint(maximumLength);
RotateToMouseDirection(gameObject, pos);
}
}
else
Debug.Log("Camera is not assigned");
}

void RotateToMouseDirection(GameObject obj, Vector3 destination)
{
direction = destination - obj.transform.position;
rotation = Quaternion.LookRotation(direction);
obj.transform.localRotation = Quaternion.Lerp(obj.transform.rotation, rotation, 1);
}
}

挂在子弹(projectile)上的这个脚本解决这几个需求:枪口处的特效在一开始就要生成unity喷火粒子特效,然后需要在固定的时间被摧毁。然后要根据设置的速度在每一帧改变子弹的位置,让子弹向前移。最后如果击中了,要在击中点生成击中特效,并且在固定的时间后摧毁它。具体看我写的注释:

// author: Marcus Xie
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProjectileMove : MonoBehaviour
{
public float speed;
// controls how many projectiles are generated in 1 sec, which takes effect in SpawnProjectiles.cs
public float fireRate;
public GameObject muzzlePrefab;
public GameObject hitPrefab;

// Start() is provoked every time a projectile comes out
void Start()
{
if (muzzlePrefab != null)
{
// when a projectile comes out, generate a muzzle on its starting point
var muzzleVFX = Instantiate(muzzlePrefab, transform.position, Quaternion.identity);
// align the muzzle with the projectile
muzzleVFX.transform.forward = transform.forward;
var psMuzzle = muzzleVFX.GetComponent<ParticleSystem>();
// the muzzle only flashes one time, and lasts one period (duration) of its particle system
// after the duration, it needs to be destroyed
if (psMuzzle != null)
Destroy(muzzleVFX, psMuzzle.main.duration);
else
{
var psChild = muzzleVFX.transform.GetChild(0).GetComponent<ParticleSystem>();
Destroy(muzzleVFX, psChild.main.duration);
}
}
}

void Update()
{
if (speed != 0)
{
transform.position += transform.forward * (speed * Time.deltaTime);
}
else
Debug.Log("Speed is not set yet");
}

void OnCollisionEnter(Collision co)
{
speed = 0f;

ContactPoint contact = co.contacts[0];
Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal);
Vector3 pos = contact.point;

if (hitPrefab != null)
{
// generate a hit effect where the surface was hit by the projectile
// align the hit effect with the normal of the surface
var hitVFX = Instantiate(hitPrefab, pos, rot);
var psHit = hitVFX.GetComponent<ParticleSystem>();
// the hit effect only flashes one time, and lasts one period (duration) of its particle system
// after the duration, it needs to be destroyed
if (psHit != null)
Destroy(hitVFX, psHit.main.duration);
else
{
var psChild = hitVFX.transform.GetChild(0).GetComponent<ParticleSystem>();
Destroy(hitVFX, psChild.main.duration);
}
}

Destroy(gameObject);
}
}

5. 示例工程

整个Unity工程,包含源码和所有资源,都在这里:

/MarcusXie3D/ParticleBasedAttackVFX

来源知乎专栏: Graphics Fanatic

文章来源:http://mp.weixin.qq.com/s?src=11×tamp=1683865102&ver=4523&signature=vH01ORzzhHNp1REBTDA0bJXErcBHn*tJhT7xLXEf6ke-s1juH*RD2hj9WERWMWSPP8xbkmjv*h3Wde-BUrZB6wxwRyfAQsHdSIGH74Jh4dYVoVywDjpwS4ISSKAeN33t&new=1