14.Unity2D 横版 粒子系统特效 飙血粒子+高处落地粒子+对象池管理所有粒子

主目录https://blog.csdn.net/qq_54263076/category_11900070.html?spm=1001.2014.3001.5482

粒子系统常用属性介绍

开始

1.制作飙血粒子预制体

(1)创建 右键->效果->粒子系统物体,并重命名为bloodEffect

 (2)各种参数的调整,使得粒子特效相似飙血粒子

完成效果,突然爆发+血液缓慢变淡+地形碰撞

(3)拖到文件夹里面形成预制体

(4)将粒子生成加入到角色脚本受伤函数里面

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CharacterPanel : MonoBehaviour
{
    public bool Iscanjump = false;//是否能跳跃,默认不能
    public float Hpmax = 100;//最大生命
    public float Hp = 100;//生命
    public float Atk = 10;//攻击力
    public float AtkRan = 1;//攻击浮动
    public float Def = 10;//防御力
    public float lookdir;//获取初始Scale.x,用于转向 
    public float dropConst = 15;//下坠常数
    public float speed = 10;//地面移动速度
    public float jumpspeedUp = 20;//上升速度
    public float jumpspeedVertiacal = 0.5f;//空中左右移动速度
    private Rigidbody2D rig;//2D刚体
    private Animator ani;
    private Transform Canvas;//获取角色个人UI面板
    // Start is called before the first frame update
    void Start()
    {
        Canvas = transform.Find("Canvas");
        ani = transform.GetComponent<Animator>();
      
        rig = GetComponent<Rigidbody2D>();//获取刚体
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        lookdir = transform.localScale.x;
        check();
        //跳跃优化手感
        Quickjump();
        //检测卡地面脱离
        OutLockGround();
    }
    //标准化检查
    private void check()
    {
        if (Hp > Hpmax) Hp = Hpmax;//血量不超过上限
        if (Hp <= 0) { Hp = 0;death(); };//血量不超过下限,且死亡
    }
    //受伤,其他脚本调用
    public void hurt(float atk)
    {
       float hurtnum= (20 * atk / (20 + Def)) + Random.Range(-AtkRan, AtkRan);
        //受伤动画 为什么要将触发器中的受伤动画移动在角色面板的hurt函数下?因为受伤不仅有角色攻击受伤,还有陷阱,掉落等伤害,所以直接血量减少时播放受伤动画更加好
        transform.GetComponent<Animator>().SetBool("Ishurt", true);
        StartCoroutine(endHurt());//开启协程结束受伤动画
        //伤害数值显示
        Canvas.GetComponent<CharaCanvas>().ShowHurtText(hurtnum);
        Hp -= hurtnum;
        //受伤粒子生成
        Instantiate(GameObject.Find("bloodEffect"),new Vector3(transform.position.x, transform.position.y, transform.position.z),new Quaternion(0,0,0,0));

    
    }
    IEnumerator endHurt()
    {
        yield return 0;//此处暂停,下一帧执行
        transform.GetComponent<Animator>().SetBool("Ishurt", false);
    }
    //死亡
    private void death()
    {
        ani.SetBool("Isdeath",true);
    }
    //跳跃
    public void jump()
    {
        if (Iscanjump == true)
        {
            rig.velocity = new Vector2(0, jumpspeedUp);//设置刚体速度,给予向量
        }
    }
    //优化跳跃手感,迅速下落,放入帧频率更新函数里面
    public void Quickjump()
    {
        float a = dropConst * 5 - Mathf.Abs(rig.velocity.y);//通过下坠常数,空中速度快为0时,下坠常数a越大,即越快速 度过这个状态
        rig.velocity -= Vector2.up * a * Time.deltaTime;
    }
    //卡住地面脱离
    public void OutLockGround()
    {
        RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(0, -1),
                                 0.5f, LayerMask.GetMask("tilemap"));
        if (hit)
        {
            if (hit.collider.CompareTag("ground"))//检测到地面
            {
                transform.Translate(new Vector3(0, 1, 0));
            }
            Debug.DrawRay(transform.position, new Vector2(0, -1), Color.blue);
        }
    }









    //以下是敌人的调运行为函数,主角有自己的控制脚本
    //转向
    public void Turndir()
    {
          transform.localScale = new Vector3(-lookdir, transform.localScale.y, transform.localScale.z);//通过改变scale改变方向
    }
    //移动
    public void move()
    {
        if (lookdir == 0) lookdir =0.001f;//预防除零问题弹错
        Vector3 vt = new Vector3(lookdir/Mathf.Abs(lookdir), 0, 0);
        //空中左右移动,为地面jumpcharacterPanel.speedVertiacal倍
        if (Iscanjump == false)
        {
            gameObject.transform.Translate(jumpspeedVertiacal * speed * Time.deltaTime * vt);//通过这个函数来使用vt使得左右移动
        }
        //地面左右移动
        else { gameObject.transform.Translate(speed * Time.deltaTime * vt); }
        ani.SetBool("Ismove", true);

    }
    //等待
    public void idle()
    {
        ani.SetBool("Ismove", false);
    }
}

注意:

 注意:可以选择粒子板块“停止行动”中的“销毁”来自动销毁粒子物体,会使得面板上没有粒子效果

Gameobject.Find()函数是查找面板上存在且激活的物体。如果粒子效果物体面板上没有或者未激活状态,可以通过修改脚本,增加Gameobject变量然后拖动形成粒子预制体参数

(5)效果展示

2.制作高处落地粒子

(1)创建 右键->效果->粒子系统物体,并重命名为dropEffect

 (2)各种参数的调整,使得粒子特效相似高处落地粒子

(3)拖到文件夹里面形成预制体

(4)将粒子生成加入到落地检测的函数里面

我将此代码放在了跳跃判断脚本里面,当然根据自己的情况可以放在自己的例如射线检测或者其他脚本里面

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

public class IsJump : MonoBehaviour
{
    //为了优化跳跃碰撞盒,在父物体下面新建空对象bottoom,并添加碰撞盒,用来检测底部(以前碰撞盒头碰到顶部地面还能跳不符合常理,所以需要作此操作)
    private CharacterPanel characterPanel;
    //代码优化,将是否能跳跃的检测从原脚本CharacterControl的公共成员变量IscanJump移动到player和enemy共有的脚本CharacterPanel下
    //将原脚本下的局部变量IscanJump,改成transform.GetComponent<CharacterPanel>().Iscanjump
    private float befConV;//碰撞前一刻速度
    public float partDropV=30;//掉落粒子显示的最小速度
    // Start is called before the first frame update
    void Start()
    {
        characterPanel = transform.GetComponentInParent<CharacterPanel>();
    }

    // Update is called once per frame
    void Update()
    {
        befConV = GetComponentInParent<Rigidbody2D>().velocity.magnitude;
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        //掉落粒子生成
        if (befConV>partDropV)
        Instantiate(GameObject.Find("dropEffect"), new Vector3(transform.position.x, transform.position.y, transform.position.z), new Quaternion(0, 0, 0, 0));

    }
    //检测否在地面碰撞盒子检测,通过给地面碰撞盒子transform的tag标签为ground
    private void OnTriggerStay2D(Collider2D collision)
    {
        if (collision.transform.tag != "untagged" && collision.transform.tag != "notAtkCheckCollision"&& characterPanel.Iscanjump == false)
        {
            characterPanel.Iscanjump = true;//获取父物体脚本变量并赋值      
        }
       

    }
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.transform.tag != "untagged"&& collision.transform.tag != "notAtkCheckCollision" && characterPanel.Iscanjump == true)
        {

            characterPanel.Iscanjump = false;//获取父物体脚本变量并赋值
            
        }
    }
}

(5)效果展示

高级一些,方便一些

1.建立粒子统一管理器空对象,将生成的粒子统一作为它的子物体

(1)建立空对象,并命名为粒子管理器ParticalManager

(2)粒子管理器脚本,加入到粒子管理器的空对象ParticalManager中,并拖动填入粒子对象参数

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

public class particalmanager : MonoBehaviour
{

    public GameObject[] particals;//粒子组


}

2.利用对象池机制,避免频繁的创建,销毁。导致运行卡顿

注意右边面板粒子对象池的激活与非激活

回顾5.Unity2D 横版 对象池的创建_ζั͡ ั͡雾 ั͡狼 ั͡✾的博客-CSDN博客Unity2D 横版 对象池的创建。对象池的好处是不用频繁的毁坏和创建重复预制体,而是通过激活与非激活来对重复预制体进行管理。增加了游戏的流畅性。https://blog.csdn.net/qq_54263076/article/details/125667394?spm=1001.2014.3001.5501

(1)关闭自动销毁,设置“停止行动”为“禁用“”

(2)对象池Pool脚本 (未更改)

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

public class Pool : MonoBehaviour
{
    //集合
    public List<GameObject> list = new List<GameObject>();
    //游戏预设体
    public GameObject GoPrefab;
    //最大个数
    public int MaxCount = 50;
    //对象保存对象池
    public void Push(GameObject go)
    {
        if (list.Count < MaxCount)
        {
            list.Add(go);
            //改为非激活
            go.SetActive(false);
        }
        else {
            Destroy(go);
        }

    }
    //对象池中取出对象
    public GameObject Pop() 
    {
        if (list.Count > 0) {
            GameObject go = list[0];
            list.RemoveAt(0);
            //如果对象池里的对象是敌人
            if (go.tag == "enemy")
            {
                go.GetComponent<CharacterPanel>().Hp = go.GetComponent<CharacterPanel>().Hpmax;
            }
            //设置为激活状态
            go.SetActive(true);
            
            return go;

        }
        return Instantiate(GoPrefab);
    }
    //清除对象池
    public void Clear()
    {
        list.Clear();

    }
}

 (2)粒子管器particalmanager脚本扩展

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

public class particalmanager : MonoBehaviour
{
    private List<Pool> partPool=new List<Pool>();//粒子组对象池
    public GameObject[] particals;//粒子组
/* 0-血液粒子
 * 1-高处掉落粒子
 
 */
    void Start()
    {
        //对象池初始化
        
        for (int i = 0; i < particals.Length; i++)
        {
            Pool p = new Pool();
            p.GoPrefab = particals[i];//将每个对象池的对象改成粒子对象
            partPool.Add(p);      

        }
        Debug.Log(partPool.Count);
    }
    //粒子保存对象池中
    public void Pushpart( GameObject part) 
    {
  
        int i= partPool.FindIndex(x=>x.GoPrefab.name==part.name||((x.GoPrefab.name+"(Clone)")==part.name));//i为找到的对象池下标
        if (i != -1)
        {
            partPool[i].Push(part);
            Debug.Log(partPool[i].list.Count + " " + partPool[i].list[partPool[i].list.Count-1].name);
        }


    }
    //粒子取出对象池中
    public GameObject Poppart(int i) //i为对象池下标
    {
       GameObject part=partPool[i].Pop();//对象池取出
        Debug.Log(part.name+" pop");
        return part;
    }

}

(3)CharacterPanel脚本更新血液粒子效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CharacterPanel : MonoBehaviour
{
    public bool Iscanjump = false;//是否能跳跃,默认不能
    public float Hpmax = 100;//最大生命
    public float Hp = 100;//生命
    public float Atk = 10;//攻击力
    public float AtkRan = 1;//攻击浮动
    public float Def = 10;//防御力
    public float lookdir;//获取初始Scale.x,用于转向 
    public float dropConst = 15;//下坠常数
    public float speed = 10;//地面移动速度
    public float jumpspeedUp = 20;//上升速度
    public float jumpspeedVertiacal = 0.5f;//空中左右移动速度
    private Rigidbody2D rig;//2D刚体
    private Animator ani;
    private Transform Canvas;//获取角色个人UI面板
    private GameObject pm;//获取粒子管理器
    // Start is called before the first frame update
    void Start()
    {
        Canvas = transform.Find("Canvas");
        ani = transform.GetComponent<Animator>();
      
        rig = GetComponent<Rigidbody2D>();//获取刚体
        pm = GameObject.Find("ParticalManager");
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        lookdir = transform.localScale.x;
        check();
        //跳跃优化手感
        Quickjump();
        //检测卡地面脱离
        OutLockGround();
    }
    //标准化检查
    private void check()
    {
        if (Hp > Hpmax) Hp = Hpmax;//血量不超过上限
        if (Hp <= 0) { Hp = 0;death(); };//血量不超过下限,且死亡
    }
    //受伤,其他脚本调用
    public void hurt(float atk)
    {
       float hurtnum= (20 * atk / (20 + Def)) + Random.Range(-AtkRan, AtkRan);
        //受伤动画 为什么要将触发器中的受伤动画移动在角色面板的hurt函数下?因为受伤不仅有角色攻击受伤,还有陷阱,掉落等伤害,所以直接血量减少时播放受伤动画更加好
        transform.GetComponent<Animator>().SetBool("Ishurt", true);
        StartCoroutine(endHurt());//开启协程结束受伤动画
        //伤害数值显示
        Canvas.GetComponent<CharaCanvas>().ShowHurtText(hurtnum);
        Hp -= hurtnum;
        //受伤粒子生成,不需要对存活粒子进行管理,就不用创建存活粒子集合了
        GameObject bloodeffect = pm.GetComponent<particalmanager>().Poppart(0);
        bloodeffect.transform.position = transform.position;//设置位置
        bloodeffect.transform.parent = pm.transform;//设置父物体
        
    
    }
    IEnumerator endHurt()
    {
        yield return 0;//此处暂停,下一帧执行
        transform.GetComponent<Animator>().SetBool("Ishurt", false);
    }
    //死亡
    private void death()
    {
        ani.SetBool("Isdeath",true);
    }
    //跳跃
    public void jump()
    {
        if (Iscanjump == true)
        {
            rig.velocity = new Vector2(0, jumpspeedUp);//设置刚体速度,给予向量
        }
    }
    //优化跳跃手感,迅速下落,放入帧频率更新函数里面
    public void Quickjump()
    {
        float a = dropConst * 5 - Mathf.Abs(rig.velocity.y);//通过下坠常数,空中速度快为0时,下坠常数a越大,即越快速 度过这个状态
        rig.velocity -= Vector2.up * a * Time.deltaTime;
    }
    //卡住地面脱离
    public void OutLockGround()
    {
        RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(0, -1),
                                 0.5f, LayerMask.GetMask("tilemap"));
        if (hit)
        {
            if (hit.collider.CompareTag("ground"))//检测到地面
            {
                transform.Translate(new Vector3(0, 1, 0));
            }
            Debug.DrawRay(transform.position, new Vector2(0, -1), Color.blue);
        }
    }









    //以下是敌人的调运行为函数,主角有自己的控制脚本
    //转向
    public void Turndir()
    {
          transform.localScale = new Vector3(-lookdir, transform.localScale.y, transform.localScale.z);//通过改变scale改变方向
    }
    //移动
    public void move()
    {
        if (lookdir == 0) lookdir =0.001f;//预防除零问题弹错
        Vector3 vt = new Vector3(lookdir/Mathf.Abs(lookdir), 0, 0);
        //空中左右移动,为地面jumpcharacterPanel.speedVertiacal倍
        if (Iscanjump == false)
        {
            gameObject.transform.Translate(jumpspeedVertiacal * speed * Time.deltaTime * vt);//通过这个函数来使用vt使得左右移动
        }
        //地面左右移动
        else { gameObject.transform.Translate(speed * Time.deltaTime * vt); }
        ani.SetBool("Ismove", true);

    }
    //等待
    public void idle()
    {
        ani.SetBool("Ismove", false);
    }
}

(4)跳跃判断IsJump脚本扩展高处掉落粒子

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

public class IsJump : MonoBehaviour
{
    //为了优化跳跃碰撞盒,在父物体下面新建空对象bottoom,并添加碰撞盒,用来检测底部(以前碰撞盒头碰到顶部地面还能跳不符合常理,所以需要作此操作)
    private CharacterPanel characterPanel;
    //代码优化,将是否能跳跃的检测从原脚本CharacterControl的公共成员变量IscanJump移动到player和enemy共有的脚本CharacterPanel下
    //将原脚本下的局部变量IscanJump,改成transform.GetComponent<CharacterPanel>().Iscanjump
    private float befConV;//碰撞前一刻速度
    public float partDropV=30;//掉落粒子显示的最小速度
    private GameObject pm;//获取粒子管理器
    // Start is called before the first frame update
    void Start()
    {
        characterPanel = transform.GetComponentInParent<CharacterPanel>();
        pm = GameObject.Find("ParticalManager");
    }

    // Update is called once per frame
    void Update()
    {
        befConV = GetComponentInParent<Rigidbody2D>().velocity.magnitude;
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        //掉落粒子生成
        if (befConV > partDropV)
        {
           
            GameObject dropeffect = pm.GetComponent<particalmanager>().Poppart(1);
            dropeffect.transform.position = transform.position;
            dropeffect.transform.parent = pm.transform;
        }
        

    }
    //检测否在地面碰撞盒子检测,通过给地面碰撞盒子transform的tag标签为ground
    private void OnTriggerStay2D(Collider2D collision)
    {
        if (collision.transform.tag != "untagged" && collision.transform.tag != "notAtkCheckCollision"&& characterPanel.Iscanjump == false)
        {
            characterPanel.Iscanjump = true;//获取父物体脚本变量并赋值      
        }
       

    }
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.transform.tag != "untagged"&& collision.transform.tag != "notAtkCheckCollision" && characterPanel.Iscanjump == true)
        {

            characterPanel.Iscanjump = false;//获取父物体脚本变量并赋值
            
        }
    }
}

  • 6
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ζั͡ ั͡雾 ั͡狼 ั͡✾

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

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

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

打赏作者

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

抵扣说明:

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

余额充值