• 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
  • 相关阅读:
    47. Permutations II
    56. Merge Intervals
    57. Insert Interval
    常见算法问题
    67. Unique Paths
    版权声明
    121. Best Time to Buy and Sell Stock
    Leetcode backtracking 合集
    转载 int和string 类型的互换
    prim算法,克鲁斯卡尔算法---最小生成树
  • 原文地址:https://www.cnblogs.com/vuciao/p/10363313.html
Copyright © 2020-2023  润新知