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

时间:2025-02-27 15:34:02
新动画系统:
反向动力学动画(IK功能):
魔兽世界(头部动画),神秘海域(手部动画),人类一败涂地(手部动画)
如何启用(调整)
1、必须是新动画系统Animator
Unity3D学习笔记(十七):IK动画、粒子系统和塔防

设置头、手、肘的目标点

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

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

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

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

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

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();
//调整四肢IK的目标点
anim.SetIKPosition(AvatarIKGoal.LeftHand, HandPoint.position);//AvatarIKGoal是枚举
anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, );
//调整四肢IK关节的目标点
anim.SetIKHintPosition(AvatarIKHint.RightElbow, ElbowPoint.position);
anim.SetIKHintPositionWeight(AvatarIKHint.RightElbow, );
//调整四肢IK的朝向
//anim.SetIKRotation();
}
}

取消物体描边

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

粒子系统:
Particle System一统江湖,主流离子发射器思想,调整发射器参数发射离子,如AE
Legacy都是老的粒子系统
Unity3D学习笔记(十七):IK动画、粒子系统和塔防

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

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

修改大小
我们没有办法通过GameObject的Scale改大小,Scale只是改变发射区域的大小
可以通过StartSize
Unity3D学习笔记(十七):IK动画、粒子系统和塔防

可以通过SizeOverLife曲线

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

启用碰撞
碰撞系统,火焰溅射效果
打开粒子的Collision功能,选择Type为World,平面改3D
Unity3D学习笔记(十七):IK动画、粒子系统和塔防

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

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

拖尾效果

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

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

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只负责自身

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

取消唤醒,改为代码播放

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

塔防游戏:
怪物管理器:
添加路点(路点放在拐点处),给怪物子类添加路点列表
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 = ; i < pathParent.childCount; i++)
{
pathNodeList.Add(pathParent.GetChild(i));
}
//把自己放在路点的第一个位置
transform.position = pathNodeList[].position;
currentPathNodeID = ;
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-)
{
//向下一个点前进
float distance = Vector3.Distance(transform.position, pathNodeList[currentPathNodeID + ].position);
transform.position = Vector3.Lerp(transform.position, pathNodeList[currentPathNodeID + ].position, speed/ distance*Time.deltaTime);
//如果我离下一个点的距离到达某一个值 Quaternion targetRot = Quaternion.LookRotation(pathNodeList[currentPathNodeID + ].position - pathNodeList[currentPathNodeID].position);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, 0.1f);
//transform.LookAt(pathNodeList[currentPathNodeID + 1]);
if (distance < speed * Time.deltaTime)
{
//改变我的当前点,进而改变目标点,成下一个点
currentPathNodeID++;
}
}
}
}

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

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

机枪塔,需要瞄准,攻击速快
炮塔,不用瞄准,攻速慢
塔基
外层(空物体):缩放(1,1,1)
内层(Tower_Base):缩放(0.4,0.4,0.4)
外层添加刚体
Unity3D学习笔记(十七):IK动画、粒子系统和塔防

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

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

塔基父类代码逻辑

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 = ;
Quaternion targetRot = Quaternion.LookRotation(dir);
gunPos.rotation = Quaternion.Slerp(gunPos.rotation, targetRot, 0.5f);
}
float fireCD = 0.1f;
float fireCDTime;
public void Fire() {
fireCDTime = ;
tragetMonster.Damage();
ps.Play();//特效代码逻辑
}
public bool AttackCheck() {
Vector3 monsterDir = tragetMonster.transform.position - gunPos.position;
monsterDir.y = ;
if (Vector3.Angle(gunPos.forward, monsterDir)<)
{
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;
}
}
}
}

设置攻击范围

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

塔基父类代码逻辑:设置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 = ;
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 = ;
Quaternion targetRot = Quaternion.LookRotation(dir);
gunPos.rotation = Quaternion.Slerp(gunPos.rotation, targetRot, 0.5f);
} float fireCD = 0.1f;
float fireCDTime;
public void Fire() {
fireCDTime = ;
tragetMonster.Damage();
ps.Play();//特效代码逻辑
} public bool AttackCheck() {
Vector3 monsterDir = tragetMonster.transform.position - gunPos.position;
monsterDir.y = ;
if (Vector3.Angle(gunPos.forward, monsterDir)<)
{
return true;
}
re