Note: It is recommended to watch the corresponding video tutorial before reading this article. This article is mainly used as a reference for learners who still have questions about the video tutorial
Video tutorial: https://www.bilibili.com/video/BV12s411g7gU?p=136
Table of contents
EnemyStatusInfo Enemy status information class
EnemyAnimation enemy animation class
EnemySpawn enemy spawner class
enemy module
1. The enemy moves along the designated route
2. Blood loss and death after being hit
3. Arrive at the end and attack the player
4. Play running animation for sports, attack animation for attack, idle animation for attack interval, and death animation for death
demand analysis
EnemyMotor enemy motor class : provides movement, rotation, pathfinding functions
EnemyStatusInfo Enemy status information class : define blood volume, provide functions of injury and death
EnemyAnimation Enemy animation class : define various animation names and provide the function of playing animation
ps: Since there is no corresponding game material, this article uses print("play XX animation") to replace the part of the animation in the scene. Secondly, this article recommends creating several corresponding empty animations to meet the running conditions
For the creation of animation and precautions for using the old animation system in the new version, please read the content about Animation in Unity script (3)
EnemyAI Enemy AI class: By judging the state , perform pathfinding or attack
ps: The above components should be mounted on the enemy's prefab, but it is not recommended to directly construct the components on the model in actual development, you should create an empty object as its parent and mount the components to the parent on, as shown below
EnemyMotor enemy motor class
1. Define the method to move forward
2. Define the method of rotation towards the target point
3. Define the method of pathfinding
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人马达,提供移动、旋转、寻路功能
public class EnemyMotor : MonoBehaviour
{
public Path path;
//当前路径点的索引
private int currentPointIndex = 0;
public float moveSpeed;
private void Update()
{
PathFinding();
}
//向前移动
public void MovementForward()
{
transform.Translate(0, 0, moveSpeed * Time.deltaTime);
}
//注视选转
public void LookRotation(Vector3 target)
{
this.transform.LookAt(target);
}
//寻路,沿路线移动
public bool PathFinding()
{
//return true;继续寻路
//return false;到达终点,无需寻路
//如果到达目标点
//更新目标点(向下一个点移动)
if (path == null || currentPointIndex >= path.wayPoints.Length) return false;
//朝向目标点
LookRotation(path.wayPoints[currentPointIndex]);
//向前移动
MovementForward();
//如果达到目标(当前位置接近于目标点)
if (Vector3.Distance(transform.position, path.wayPoints[currentPointIndex]) < 0.1f)
{
currentPointIndex++;
}
return true;
}
}
EnemyStatusInfo Enemy status information class
1. Define variable: current health value
2. DEFINING METHODS: INJURY, DEATH
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人状态信息类,定义敌人信息,提供受伤死亡功能
public class EnemyStatusInfo : MonoBehaviour
{
public float HP = 200;
public float maxHP = 200;
//死亡延迟销毁时间
public float deathDelay = 5;
public EnemySpawn spawn;
//受伤
public void Damage(float damage)
{
//扣血
HP -= damage;
//血量为0时,调用死亡方法
if (HP <= 0)
Death();
}
//死亡
public void Death()
{
//播放死亡动画
var anim = GetComponent<EnemyAnimation>();
anim.action.Play(anim.deathAnimName);
print("播放死亡动画");
//销毁当前物体
Destroy(gameObject, deathDelay);
//设置路线状态
GetComponent<EnemyMotor>().path.isUsable = true;
//产生下一个物体
spawn.GenerateEnemy();
}
//测试方法,鼠标点击敌人,对其造成50点伤害
private void OnMouseDown() {
Damage(50);
}
}
ps: Be sure to mount the collision component (Collider) on the enemy's prefab, otherwise OnMouseDown() will not take effect
EnemyAnimation enemy animation class
1. Define variables for various animation names, such as running, idle, attack
2. Define the AnimationAction class to provide animation-related behaviors
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAnimation : MonoBehaviour
{
//跑步动画名称
public string runAnimName;
//攻击动画名称
public string attackAnimName;
//闲置动画名称
public string idleAnimaName;
//死亡动画名称
public string deathAnimName;
//行为类
public AnimationAction action;
private void Awake()
{
//action=new AnimationAction(?);
action=new AnimationAction(GetComponent<Animation>());
}
}
AnimationAction animation behavior class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//动画行为类,提供有关动画的行为
public class AnimationAction
{
//附加在敌人模型上的动画组件引用
private Animation anim;
//创建动画行为类
public AnimationAction(Animation anim)
{
this.anim=anim;
}
//播放动画
public void Play(string animName)
{
anim.CrossFade(animName);
}
public bool IsPlaying(string animName)
{
return anim.IsPlaying(animName);
}
}
enemy generator
1. Spawn the specified number of enemies at the beginning
2. Randomly select an available route for each person
3. The enemy type and the delay time generated are random
4. When the enemy dies, the next enemy will be spawned until the number of spawns reaches the upper limit
demand analysis
1. Create a root route and add multiple routes with waypoints
2. EnemySpawn enemy generator class : attached to the root route, providing the function of generating enemies
3. Path route class : the included attributes are, waypoint coordinates Vector3[] wayPoints, bool IsUseable or not
4. When the generator is enabled, calculate all possible routes
5. When an enemy is generated, randomly select an available route
EnemySpawn enemy spawner class
Define variables:
GameObject[] enemyTypes : used to record enemy prefabs
int startCount : Used to record the number of enemies that need to be created at the beginning
int spawnedCount : Used to record the number of enemies that have been spawned
int maxCount : Used to record the upper limit of the number of enemies generated
int maxDelay : used to record the maximum delay time for spawning enemies
Definition method:
CalculateWayLines() : used to calculate all routes and coordinates
GenerateEnemy() : Used to generate an enemy.
using System.Net.Mail;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour
{
//需要创建的敌人预制件
public GameObject[] enmeyType;
//需要创建敌人的最大数目
public int maxCount = 5;
//初始创建的敌人数目
public int startCount = 2;
//已经创建的敌人数目
private int spawnCount;
private Path[] paths;
public int maxDelay = 10;
//生成一个敌人
public void GenerateEnemy()
{
spawnCount++;
//如果生成数量已达到上限
if(spawnCount>=maxCount) return;
//延迟时间随机
Invoke("GreateEnemy", Random.Range(0, maxDelay));
}
private void GreateEnemy()
{
//选择所有可以使用的路线
Path[] usablePaths = SelectUsablePath();
//随机选择一条可以使用的路线
Path path = usablePaths[Random.Range(0, usablePaths.Length)];
int randomIndex = Random.Range(0, enmeyType.Length);
//Instantiate(敌人预制件,位置,旋转角度);
GameObject go = Instantiate(enmeyType[randomIndex], path.wayPoints[0], Quaternion.identity) as GameObject;
//配置信息
EnemyMotor motor = go.GetComponent<EnemyMotor>();
motor.path = path;
path.isUsable = false;
//传递生成器引用
go.GetComponent<EnemyStatusInfo>().spawn=this;
}
private void Start()
{
CalculateWayLines();
GenerateEnemy();
}
private void CalculateWayLines()
{
paths = new Path[this.transform.childCount];
for (int i = 0; i < paths.Length; i++)
{
//每一条路线
//路线变换组件的引用
Transform path = this.transform.GetChild(i);
//创建路线对象
paths[i] = new Path(path.childCount);
for (int pointIndex = 0; pointIndex < path.childCount; pointIndex++)
{
paths[i].wayPoints[pointIndex] = path.GetChild(pointIndex).position;
}
}
}
//闲置可以使用的路线
private Path[] SelectUsablePath()
{
List<Path> res = new List<Path>(paths.Length);
//遍历所有路线
foreach (var path in paths)
{
//如果可以使用,添加到res中
if (path.isUsable) res.Add(path);
}
return res.ToArray();
}
}
Path route class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path
{
public Vector3[] wayPoints { get; set; }
public bool isUsable{ get; set; }
//构造函数
public Path(int wayPointCount){
wayPoints=new Vector3[wayPointCount];
isUsable=true;
}
}