【游戏开发实战】使用Unity制作水果消消乐游戏教程(七):水果消除特效

一、前言

嗨,大家好,我是新发。下班坐地铁的时候,好几次看到其他人在玩消消乐,既然大家都这么喜欢玩,那我就写个Unity制作水果消消乐的教程吧。

我会根据内容点分成好几篇文章来讲,希望对想学Unity的同学有所帮助,创作不易,喜欢的同学欢迎关注、点赞、收藏,文章目录如下:
第一篇:生成冰块阵列
第二篇:随机生成水果
第三篇:水果拖动与交换逻辑
第四篇:使用DOTween插件实现水果的滑动效果
第五篇:水果的消除检测,实现消除效果
第六篇:水果下落与新水果生成
第七篇:水果消除特效
第八篇:游戏得分加分效果
第九篇:使用UGUI显示游戏UI

游戏运行效果如下:
在这里插入图片描述
最终的Demo工程已上传到GitHub,感兴趣的同学可以自行下载下来学习。
GitHub地址:https://github.com/linxinfa/UnityXiaoXiaoLeDemo
注:我使用的Unity版本为2020.1.14f1c1
在这里插入图片描述


本篇讲水果消除特效,本篇的效果:
在这里插入图片描述

二、导入特效素材

导入特效图片到Unity工程中。

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

三、制作序列帧动画

创建一个Animations文件夹,用来放动画文件。
在这里插入图片描述
选中所有的特效图片,拖到Hierarchy窗口中,此时会弹出一个窗口保存Animation文件,我们保存到刚刚的Animations文件夹中,命名为DisappearEffect.anim
在这里插入图片描述
我们刚刚拖动到场景中的图片,最终会以SpriteRenderer的方式显示,并且自动带了一个Animator组件。
在这里插入图片描述
再看回Animations文件夹,这里有两个文件,第一个就是Animator组件的controller文件,第二个就是DisappearEffect.anim动画文件,我们的序列帧动画就是在这个anim动画文件中。
在这里插入图片描述
双击打开打开DisappearEffect.anim文件即可打开动画窗口,选中场景中的SpriteRenderer物体,再点击Animation窗口中的播放按钮,即可在场景中预览动画效果。
在这里插入图片描述

四、调整序列帧帧率

生成的序列帧动画默认是12帧每秒,我们可以在Animation窗口右上角的...按钮处点击弹出菜单,勾选Show Sample Rate,然后就可以调节帧率了。
在这里插入图片描述
我们改为30帧每秒。
在这里插入图片描述

五、保存特效序列帧预设

将场景中的特效节点重命名为DisappearEffect,然后保存为预设,存放到GameRes文件夹中。
在这里插入图片描述

六、特效生成器EffectSpawner

先在场景中创建一个空物体:EffectSpawner
在这里插入图片描述
创建一个EffectSpawner.cs脚本。
在这里插入图片描述
写代码之前,我们先思考一下,水果消除时播放这个特效,特效播放完后是直接销毁掉特效对象吗?当然,我们可以直接销毁,但是由于这个特效在游戏过程中会频繁显示,所以这里我们最好是做成对象池:
1 想要显示特效时,先从对象池里看是否对象,如果有则直接取出来使用;
2 如果对象池中没有对象,则实例化一个对象出来;
3 特效播放完毕之后,将特效隐藏并塞回对象池中;
所以,我们先定义一个队列作为对象池的容器。

// EffectSpawner.cs 

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

public class EffectSpawner : MonoBehaviour
{
    /// <summary>
    /// 水果消除特效预设
    /// </summary>
    public GameObject disappearEffectPrefab;

    /// <summary>
    /// 特效对象池
    /// </summary>
    private Queue<GameObject> m_disappearEffectPool = new Queue<GameObject>();
}

接着,我们就可以写一个显示特效的函数了。

// EffectSpawner.cs 

/// <summary>
/// 显示水果消除特效
/// </summary>
/// <param name="pos">坐标</param>
public void ShowDisappearEffect(Vector3 pos)
{
    GameObject obj;
    if (m_disappearEffectPool.Count > 0)
        obj = m_disappearEffectPool.Dequeue();
    else
    {
        obj = Instantiate(disappearEffectPrefab);
        // TODO ...
        
    }
    obj.SetActive(true);
    obj.transform.position = pos;
}

我上面留了一个TODO,我们还差做什么呢?
1 实例化出来的对象,需要设置一下父节点,方便管理;
2 我们还需要监听特效播放完毕的事件,当特效播放完毕后需要回收特效对象到对象池中。
先完成第一个。

// EffectSpawner.cs 

private Transform m_effectRoot;

private void Awake()
{
    m_effectRoot = transform;
}

/// <summary>
/// 显示水果消除特效
/// </summary>
/// <param name="pos">坐标</param>
public void ShowDisappearEffect(Vector3 pos)
{
    GameObject obj;
    if (m_disappearEffectPool.Count > 0)
        obj = m_disappearEffectPool.Dequeue();
    else
    {
        obj = Instantiate(disappearEffectPrefab);
        // 设置父节点
        obj.transform.SetParent(m_effectRoot, false);
        // TODO 监听动画播放完毕,回收到对象池中
        
    }
    obj.SetActive(true);
    obj.transform.position = pos;
}

七、动画帧事件,监听动画播放完毕

动画帧事件,只能调用动画所在物体的组件的public函数,我们先创建一个AnimationEvent.cs脚本。
在这里插入图片描述
在脚本中定义一个public函数,作为帧事件的响应函数,并提供一个委托供外部使用。

using UnityEngine;
using System;

public class AnimationEvent: MonoBehaviour
{
    /// <summary>
    /// 委托
    /// </summary>
    public Action<string> aniEventCb;

    /// <summary>
    /// 动画帧事件响应函数
    /// </summary>
    public void OnAnimationEvent(string str)
    {
        // 调用委托
        if (null != aniEventCb)
            aniEventCb(str);
    }
}

将脚本挂到之前做的DisappearEffect预设上。
在这里插入图片描述
双击打开动画文件,在最后一帧插入帧事件,选中帧事件,填写响应函数为OnAnimationEvent,填写String参数为finish
在这里插入图片描述
现在,我们就可以监听动画播放完毕的事件并回收对象到对象池了。

// EffectSpawner.cs

/// <summary>
/// 显示水果消除特效
/// </summary>
/// <param name="pos">坐标</param>
public void ShowDisappearEffect(Vector3 pos)
{
    GameObject obj;
    if (m_disappearEffectPool.Count > 0)
        obj = m_disappearEffectPool.Dequeue();
    else
    {
        obj = Instantiate(disappearEffectPrefab);
        obj.transform.SetParent(m_effectRoot, false);
        // 监听动画帧事件
        var bhv = obj.GetComponent<AnimationEvent>();
        bhv.aniEventCb = (str) =>
        {
            if("finish" == str)
            {
                // 动画播放结束,回收对象到对象池中
                obj.SetActive(false);
                m_disappearEffectPool.Enqueue(obj);
            }
        };
    }
    obj.SetActive(true);
    obj.transform.position = pos;
}

八、调用特效

水果消除的逻辑我们是写在FruitItem中的。

// FruitItem.cs

/// <summary>
/// 销毁水果图片
/// </summary>
public void DestroyFruitBg()
{
    Destroy(fruitSpriteObj);
    fruitSpriteObj = null;
}

我们可以在这里抛出一个事件,然后在EffectSpawner中响应这个事件,再调用特效的显示。
定义一个EVENT_FRUIT_DISAPPEAR事件。

// EventDef.cs

public class EventDef
{
	// ...

    /// <summary>
    /// 水果消除
    /// </summary>
    public const string EVENT_FRUIT_DISAPPEAR = "EVENT_FRUIT_DISAPPEAR";
}

FruitItem抛出事件。

// FruitItem.cs

/// <summary>
/// 销毁水果图片
/// </summary>
public void DestroyFruitBg()
{
    Destroy(fruitSpriteObj);
    fruitSpriteObj = null;
    // 抛出事件
    EventDispatcher.instance.DispatchEvent(EventDef.EVENT_FRUIT_DISAPPEAR, m_selfTransform.position);
}

EffectSpawner订阅事件,调用特效。

// EffectSpawner.cs

private void Awake()
{
     m_effectRoot = transform;
     EventDispatcher.instance.Regist(EventDef.EVENT_FRUIT_DISAPPEAR, OnFruitDisappear);
 }

 private void OnDestroy()
 {
     EventDispatcher.instance.UnRegist(EventDef.EVENT_FRUIT_DISAPPEAR, OnFruitDisappear);
 }

 private void OnFruitDisappear(params object[] args)
 {
     ShowDisappearEffect((Vector3)args[0]);
 }

九、挂脚本EffectSpawner

场景中的EffectSpawner物体,挂上EffectSpawner脚本,并赋值预设对象到参数中。
在这里插入图片描述

十、运行测试

运行Unity,测试效果如下,可以看到,特效物体是复用的,达到了我们的目的。
在这里插入图片描述
下一篇讲下得分加分效果的实现。
[点击进入下一篇]

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林新发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值