Unity3D学习笔记(十七):IK动画、粒子系统和塔防

新动画系统:
反向动力学动画(IK功能):
魔兽世界(头部动画),神秘海域(手部动画),人类一败涂地(手部动画)
如何启用(调整)
1、必须是新动画系统Animator

设置头、手、肘的目标点

2、动画类型必须是Humanoid,除此之外其他类型都不可以

3、动画系统对应层级的IKPass必须开启

4、相应的IK调整方法只能写在OnAnimatorIK(脚本挂载和Animator同一级别)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DefaultAvatarIK : MonoBehaviour {
    public Animator anim;
    public Transform lookPoint;
    public Transform HandPoint;
    public Transform ElbowPoint;
    // Use this for initialization
    void Start () {
              
       }
       
       // Update is called once per frame
       void Update () {
              
       }
    private void OnAnimatorIK(int layerIndex)
    {
        //用代码调整头部看向的方向
        anim.SetLookAtPosition(lookPoint.position);
        //调整IK动画的权重
        //如果是1代表完全按代码逻辑播放动画(完全融合)
        //如果是0完全按原动画播放
        anim.SetLookAtWeight(1);
        //调整四肢IK的目标点
        anim.SetIKPosition(AvatarIKGoal.LeftHand, HandPoint.position);//AvatarIKGoal是枚举
        anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1);
        //调整四肢IK关节的目标点
        anim.SetIKHintPosition(AvatarIKHint.RightElbow, ElbowPoint.position);
        anim.SetIKHintPositionWeight(AvatarIKHint.RightElbow, 1);
        //调整四肢IK的朝向
        //anim.SetIKRotation();
    }
}

取消物体描边

粒子系统:
Particle System一统江湖,主流离子发射器思想,调整发射器参数发射离子,如AE
Legacy都是老的粒子系统

一个粒子效果由若干个Particle System构成

修改大小
我们没有办法通过GameObject的Scale改大小,Scale只是改变发射区域的大小
可以通过StartSize

可以通过SizeOverLife曲线

启用碰撞
碰撞系统,火焰溅射效果
打开粒子的Collision功能,选择Type为World,平面改3D

也可以选择Type为Planes,设置一个平面触发效果

拖尾效果

脚本使用:播放,停止,销毁

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ParticleTest : MonoBehaviour {
    public ParticleSystem ps;
       // Use this for initialization
       void Start () {
              
       }
       
       // Update is called once per frame
       void Update () {
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            //调用粒子系统播放
            ps.Play();
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            //调用粒子系统停止
            ps.Stop();
        }
        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
            if (ps.isStopped)
            {
                //失活粒子
                gameObject.SetActive(false);
            }
        }
    }
}

重载,连同子级一起处理,填flase只负责自身

取消唤醒,改为代码播放

 
塔防游戏:
怪物管理器:
添加路点(路点放在拐点处),给怪物子类添加路点列表
currentPathNodeID:记录当前路点的变量
pathNodeList.Count-1:路点的最后一个点
移动逻辑
数组越界问题:调整currentPathNodeID++的位置到最后
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterA : MonsterBase {
    public Animator anim;
    public Transform pathParent;
    public List<Transform> pathNodeList;
    public int currentPathNodeID;
    public float speed;
       // Use this for initialization
       void Start () {
        anim = GetComponent<Animator>();
        //初始化路点列表
        pathNodeList = new List<Transform>();
        //把路点导入到路点列表
        for (int i = 0; i < pathParent.childCount; i++)
        {
            pathNodeList.Add(pathParent.GetChild(i));
        }
        //把自己放在路点的第一个位置
        transform.position = pathNodeList[0].position;
        currentPathNodeID = 0;
        monsterSta = MonsterSta.Move;
        anim.SetBool("isMove", true);
     
    }
       
       // Update is called once per frame
       void Update () {
        #region 动画测试
        //if (Input.GetKeyDown(KeyCode.Alpha1))
        //{
        //    monsterSta = MonsterSta.Move;
        //    anim.SetBool("isMove", true);
        //}
        //if (Input.GetKeyDown(KeyCode.Alpha2))
        //{
        //    monsterSta = MonsterSta.Idle;
        //    anim.SetBool("isMove", false);
        //}
        //if (Input.GetKeyDown(KeyCode.Alpha3))
        //{
        //    monsterSta = MonsterSta.Death;
        //    anim.SetTrigger("Death");
        //}
        #endregion
        Action();
    }
    public override void Action()
    {
        switch (monsterSta)
        {
            case MonsterSta.Idle:
                Idle();
                break;
            case MonsterSta.Move:
                Move();
                break;
            case MonsterSta.Death:
                Death();
                break;
            default:
                break;
        }
    }
    public override void Move()
    {
        //如果怪物还没有到达路点中最后一个点
        if (currentPathNodeID < pathNodeList.Count-1)
        {
            //向下一个点前进
            float distance = Vector3.Distance(transform.position, pathNodeList[currentPathNodeID + 1].position);
            transform.position = Vector3.Lerp(transform.position, pathNodeList[currentPathNodeID + 1].position, speed/ distance*Time.deltaTime);
            //如果我离下一个点的距离到达某一个值
        
            Quaternion targetRot = Quaternion.LookRotation(pathNodeList[currentPathNodeID + 1].position - pathNodeList[currentPathNodeID].position);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, 0.1f);
            //transform.LookAt(pathNodeList[currentPathNodeID + 1]);
            if (distance < speed * Time.deltaTime)
            {
                //改变我的当前点,进而改变目标点,成下一个点
                currentPathNodeID++;
            }
        }
    }
}

保留原动画状态机的逻辑,可以替换原动画片段

机枪塔,需要瞄准,攻击速快
炮塔,不用瞄准,攻速慢
塔基
外层(空物体):缩放(1,1,1)
内层(Tower_Base):缩放(0.4,0.4,0.4)
外层添加刚体

内层添加球形碰撞体,勾选Is Trigger

塔基父类代码逻辑

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunBase : MonoBehaviour {
    public float attackRange;
    public MonsterBase tragetMonster;
    public SphereCollider attackTrigger;
    // Use this for initialization
    void Start () {
      
    }
       
       // Update is called once per frame
       void Update () {
              
       }
    public virtual void Indit() {
        attackTrigger.radius = attackRange;
    }
    public virtual void Attack() {
    }
}

塔基子类代码逻辑

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunB : GunBase {
    public Transform gunPos;
    public ParticleSystem ps;
       // Use this for initialization
    void Start () {
        Indit();
    }
       
       // Update is called once per frame
    void Update () {
        Attack();
        fireCDTime += Time.deltaTime;
       }
    public override void Attack()
    {
        if (tragetMonster!=null)
        {
            if (AttackCheck())
            {
                if (fireCDTime> fireCD)
                {
                    Fire();
                }
            }
            else
            {
                RotatGun();
            }
        }
        else
        {
            ps.Stop();
        }
    }
    public void RotatGun() {
        Vector3 dir = tragetMonster.transform.position - gunPos.position;
        dir.y = 0;
        Quaternion targetRot = Quaternion.LookRotation(dir);
        gunPos.rotation = Quaternion.Slerp(gunPos.rotation, targetRot, 0.5f);
    }
    float fireCD = 0.1f;
    float fireCDTime;
    public void Fire() {
        fireCDTime = 0;
        tragetMonster.Damage();
        ps.Play();//特效代码逻辑
    }
    public bool AttackCheck() {
        Vector3 monsterDir = tragetMonster.transform.position - gunPos.position;
        monsterDir.y = 0;
        if (Vector3.Angle(gunPos.forward, monsterDir)<5)
        {
            return true;
        }
        return false;
    }
    //如果有物体进入我的攻击范围
    private void OnTriggerEnter(Collider other)
    {
        //如果我没有目标
        if (tragetMonster==null)
        {
            //如果进入我攻击范围的Collider标签是"Monster"
            if (other.tag == "Monster")
            {
                //把这个Monster设置成我的目标
                tragetMonster = other.GetComponent<MonsterBase>();
            }
        }
    }
    //如果有物体离开我的攻击范围
    private void OnTriggerExit(Collider other)
    {
        //如果我有目标
        if (tragetMonster != null)
        {
            //如果离开的目标是我的目标
            if (tragetMonster == other.GetComponent<MonsterBase>())
            {
                //我的目标为空
                tragetMonster = null;
            }
        }
    }
}

设置攻击范围

塔基父类代码逻辑:设置Indit初始化,给碰撞器添加攻击范围

public virtual void Indit() {
    attackTrigger.radius = attackRange;
}
攻击检测:炮塔转向
球形差值,正负转向
线性差值,只能正向
    public override void Attack()
    {
        if (tragetMonster!=null)
        {
            if (AttackCheck())
            {
                if (fireCDTime> fireCD)
                {
                    Fire();
                }
            }
            else
            {
                RotatGun();
            }
        }
        else
        {
            ps.Stop();
        }
    }


    public void RotatGun() {
        Vector3 dir = tragetMonster.transform.position - gunPos.position;
        dir.y = 0;
        Quaternion targetRot = Quaternion.LookRotation(dir);
        gunPos.rotation = Quaternion.Slerp(gunPos.rotation, targetRot, 0.5f);
    }
目标点不对的问题:
dir.y = 0;转向方法的y轴清零
monsterDir.y = 0;攻击检测的角度也要清零
    public void RotatGun() {
        Vector3 dir = tragetMonster.transform.position - gunPos.position;
        dir.y = 0;
        Quaternion targetRot = Quaternion.LookRotation(dir);
        gunPos.rotation = Quaternion.Slerp(gunPos.rotation, targetRot, 0.5f);
    }


    float fireCD = 0.1f;
    float fireCDTime;
    public void Fire() {
        fireCDTime = 0;
        tragetMonster.Damage();
        ps.Play();//特效代码逻辑
    }


    public bool AttackCheck() {
        Vector3 monsterDir = tragetMonster.transform.position - gunPos.position;
        monsterDir.y = 0;
        if (Vector3.Angle(gunPos.forward, monsterDir)<5)
        {
            return true;
        }
        re

 

posted @ 2019-02-11 21:51  vuciao  阅读(1332)  评论(0编辑  收藏  举报