在游戏开发中,经常会用到全局的屏幕点击特效,就是玩家在点击屏幕的时候,在屏幕最上层,播放一个特效,这个教程使用UGUI来实现,UI使用独立的相机渲染。

1. 先看效果图

image

示例工程 -> Github

示例工具中用到的特效资源来自于 Unity 的 AssetStore 免费资源 JMO Assets

2. 在实现之前,首先要准备好的东西

  1. 准备好一个要使用的粒子特效

    • 将特效 Prefab 的 Layer 全部设置成UI
    • 将特效Prefab中所有的 Particle System 组件上 Renderer 属性中的 SortingLayer 设置为UI,如果没有这一层,则先添加
    • 然后将 Renderer 属性中的 Order in Layer 设置为100 ,确保比所有的UI层级高
  2. 创建UI结构

    • UIRoot (一个空物体,用作UI的Root)
      • UICamera (用于渲染UI的相机)
      • Canvas (用于显示UI元素)
        • FXContainer (空物体,用作屏幕特效的父物体)
  3. 编写播放逻辑

3. 下面是UI结构各层的设置

UI层级结构中的那个Background忽略就好,就是随便加的一个 Image 作为背景

  1. UIRoot

    image
  2. UICamera

    image
  3. Canvas

    image
  4. FXContainer

    image

4. 下面是挂在 FXContainer 上的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScreenTapFX : MonoBehaviour
{
/// <summary>
/// 屏幕特效原始资源
/// </summary>
public GameObject fxSample;

/// <summary>
/// 屏幕特效的生命时长,超过后会进行缓存
/// </summary>
public float fxLifeTime = 1.0f;

/// <summary>
/// 屏幕特效的容器(父对象)
/// </summary>
public RectTransform fxContainer;

/// <summary>
/// 屏幕特效渲染使用的相机
/// </summary>
public Camera fxRenderCamera;

private Queue<GameObject> pool = new Queue<GameObject>(20);
private List<GameObject> activatedFXList = new List<GameObject>();

private void Awake()
{
if(fxSample == null)
{
Debug.LogErrorFormat("没有找到屏幕特效");
this.enabled = false;
}
else
{
fxSample.SetActive(false);
}
}

private void Update()
{
for (int i = activatedFXList.Count - 1; i >= 0; --i)
{
GameObject fx = activatedFXList[i];
float fxTime = float.Parse(fx.name);
if(Time.time - fxTime > fxLifeTime)
{
RecycleFX(fx);
activatedFXList.RemoveAt(i);
}
}

if (Application.isMobilePlatform)
{
for(int i = 0; i < Input.touchCount; ++i)
{
Touch touch = Input.GetTouch(i);
if(touch.phase == TouchPhase.Began)
{
PlayFX(touch.position);
}
}
}
else
{
if (Input.GetMouseButtonDown(0))
{
PlayFX(Input.mousePosition);
}
}
}


private void PlayFX(Vector2 tapPos)
{
GameObject fx = CreateFX();
fx.name = Time.time.ToString();
activatedFXList.Add(fx);

RectTransform fxRectTrans = fx.GetComponent<RectTransform>();
Vector2 fxLocalPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(fxContainer, tapPos, fxRenderCamera, out fxLocalPos);
fxRectTrans.SetParent(fxContainer);
fxRectTrans.anchoredPosition3D = fxLocalPos;
fx.SetActive(true);
}


private GameObject CreateFX()
{
GameObject newFX = null;
if(pool.Count > 0)
{
newFX = pool.Dequeue();
}
else
{
newFX = Instantiate(fxSample);
}
return newFX;
}

private void RecycleFX(GameObject fx)
{
fx.SetActive(false);
pool.Enqueue(fx);
}
}