学习目标:
- 应用Unity中的AI赋予敌人追击玩家
- 角色的移动控制
- 子弹的生成以及发射
- 敌人的生成以及波数设置
- Unity的ParticleSystem的简单基本应用
- LeanTween的简单运用
- 本期主要来源于B站Up小joe老师的教学视频https://www.bilibili.com/video/BV1ia4y1j78A/?spm_id_from=333.999.0.0&vd_source=f3700c962af09e81e2314d3bd816d4a1
- 自动门:https://www.bilibili.com/video/BV1Jt4y1q7mn/?spm_id_from=333.999.0.0
- 效果:
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="T95ujQYY-1732598583404" src="https://live.csdn.net/v/embed/435816"></iframe>
Unity-波数-自动追击玩家学习视频
学习内容:
-
场景的摆放以及自动门的脚本已经发过--“Unity自动门笔记”,可以前往观看摆放
-
这里主要注意的是AI的NavMesh 使用
-
接下来是角色的移动以及发射子弹,需要准备子弹的预支体以及简单的枪管
using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class PlayerMovement : LivingEntity { private Rigidbody rb; private Vector3 moveInput; [SerializeField] private float moveSpeed; //private void Start() //{ // rb = GetComponent<Rigidbody>(); //} protected override void Start() { base.Start(); rb = GetComponent<Rigidbody>(); } private void Update() { moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); LookAtCursor(); } private void FixedUpdate() { rb.MovePosition(rb.position + moveInput * moveSpeed * Time.fixedDeltaTime); } private void LookAtCursor() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); Plane plane = new Plane(Vector3.up, Vector3.zero); float distToGround; if (plane.Raycast(ray, out distToGround)) { Vector3 point = ray.GetPoint(distToGround); Vector3 rightPoint = new Vector3(point.x, transform.position.y, point.z); transform.LookAt(rightPoint); } } }
放置在预制体的子弹脚本:
using UnityEngine; public class GunController : MonoBehaviour { public Transform firePoint; public GameObject projectilePrefab; [SerializeField] private float fireRate = 0.5f; private float timer; private void Update() { if (Input.GetMouseButton(0)) { shot(); } } public void shot() { timer += Time.deltaTime; if (timer > fireRate) { timer = 0; GameObject spawnProjectile = Instantiate(projectilePrefab, firePoint.position, firePoint.rotation); } } }
4.接下来是敌人的生成以及追击角色
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.UIElements;
public class Spawner : MonoBehaviour
{
public GameObject enemyPrefab;
public Wave[] waves;
[Header("JUST FOR CHECK")]
[SerializeField] private Wave currentWave;//当前的波数
[SerializeField] private int currentIndex;//当前波数索引
public int spawnAliveNum;//存活的个数
public float nextSpawnTime;//下一波怪的生成时间
public int waitSpawnNum;//等待怪物的生成时间
[SerializeField]private Text spawnSum;//波数总和
private int enemySum;//怪物总量
private int spawnCount;//记录总波数
[SerializeField]private Text Score;//分数
private int temp;//记录杀敌数
public Text gameOver;//记录怪物的个数,无就游戏结束
private void Start()
{
NextWave();
for (int i = 0; i < waves.Length; i++)
{
currentWave = waves[i];
enemySum += currentWave.enemyNum;
spawnCount++;
}
Debug.Log("<color=green>怪物总数 </color>" + enemySum);
spawnSum.text = "总波数:" + spawnCount;
}
private void NextWave()
{
currentIndex++;
Debug.Log(string.Format("Current Wave :{0}", currentIndex));
if (currentIndex - 1 < waves.Length)
{
currentWave = waves[currentIndex - 1];
waitSpawnNum = currentWave.enemyNum;//记录等待生成的敌人
spawnAliveNum = currentWave.enemyNum;//记录存活的敌人
}
}
private void Update()
{
if (waitSpawnNum > 0 && Time.time > nextSpawnTime)
{
waitSpawnNum--;
GameObject spawnEnemy = Instantiate(enemyPrefab, transform.position, Quaternion.identity);//生成怪物
spawnEnemy.GetComponent<Enemy>().onDeath += EnemyDeath;//订阅事件
nextSpawnTime = Time.time + currentWave.timeBtwSpawn;
}
GameEnd();
Score.text = "杀敌数:" + temp;
}
private void EnemyDeath()
{
spawnAliveNum--;
if (spawnAliveNum <= 0)
{
NextWave();
}
temp++;
enemySum--; //总敌人数减少,直到为0游戏结束
Debug.Log("<color=yellow> enemySum </color>" + enemySum);
}
public void GameEnd()
{
if (enemySum <= 0)
{
gameOver.gameObject.SetActive(true);
}
}
}
using UnityEngine.AI;
using UnityEngine;
using System.Collections;
using UnityEditor.Search;
using System;
[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : LivingEntity
{
private NavMeshAgent navMeshAgent;
private Transform target;
[SerializeField] private float updateRate;
[SerializeField] private float scaleDownDuration = 0.6f; // 缩小到消失的持续时间
protected override void Start()
{
base.Start();
navMeshAgent = GetComponent<NavMeshAgent>();
if (navMeshAgent != null)
{
target = GameObject.FindWithTag("Player").GetComponent<Transform>();
}
onDeath += PlayDeathParticle;
StartCoroutine(UpdatePath());
}
private void PlayDeathParticle()
{
ParticleSystem particleSystem = GetComponent<ParticleSystem>();
if (particleSystem != null)
{
particleSystem.Play(); // 播放死亡粒子效果
}
// 停止敌人移动
if (navMeshAgent != null)
{
navMeshAgent.isStopped = true; // 停止NavMeshAgent的移动
navMeshAgent.velocity = Vector3.zero; // 防止任何剩余的移动
}
// 启动缩小并消失的过程
StartCoroutine(ScaleDownAndDestroy());
}
IEnumerator ScaleDownAndDestroy()
{
Vector3 originalScale = transform.localScale; // 记录原始尺寸
float timeElapsed = 0f;
// 循环以逐渐缩小敌人
while (timeElapsed < scaleDownDuration)
{
float t = timeElapsed / scaleDownDuration; // 计算插值因子
transform.localScale = Vector3.Lerp(originalScale, Vector3.zero, t); // 插值缩小
timeElapsed += Time.deltaTime; // 增加时间经过量
yield return null; // 等待下一帧
}
transform.localScale = Vector3.zero; // 确保最终设置为零
Destroy(gameObject); // 销毁敌人对象
}
IEnumerator UpdatePath()
{
while (target != null)
{
Vector3 preTarPos = new Vector3(target.position.x, 0, target.position.z);
navMeshAgent.SetDestination(preTarPos);
yield return new WaitForSeconds(updateRate);
}
}
}
[System.Serializable]
public class Wave
{
public int enemyNum;
public float timeBtwSpawn;
}
5.判定击杀与敌人的死亡
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LivingEntity : MonoBehaviour,IDamageable
{
public float maxHealth;
[SerializeField] protected float health;
protected bool isDead;
//事件的声明格式:public / private + event key + delegate + evetname (on + Foo );
public event Action onDeath;
//private void Start()
//{
// health = maxHealth;
//}
protected virtual void Start()//子类可以覆盖,覆写,也可以使用Base.Start
{
health = maxHealth;
}
//事件的触发,是有事件拥有者的内部逻辑触发的
protected void Die()
{
isDead = true;
Destroy(gameObject,0.5f);//延迟0.5s销毁对象
if(onDeath != null)
{
onDeath.Invoke();
}
}
//当人物手上的时候扣血,触发内部逻辑中的时间,人物:GameOver ,敌人取消追击
//敌人: 判断场上剩下还有没有敌人,如果被消灭则开始下一波
public void TakenDamage(float _damageAmount)
{
health -= _damageAmount;
if (health <= 0 && isDead == false)
{
Die();
}
}
}
internal interface IDamageable
{
void TakenDamage(float _damageAmount);
}
粒子死亡动画这个要取消勾选
学习时间:
学习产出:
存储-JoeStudy(提醒自己的)学习资料位置
标签:Vector3,杀怪,void,float,private,Unity,波数,using,public From: https://blog.csdn.net/2402_83615482/article/details/144055724