Unity3d Survival Shooter Tutorial 学习笔记(六)---攻击敌人

本文详细介绍了如何在Unity中实现敌人的健康、攻击、死亡及下沉逻辑,包括使用Animator、AudioSource和ParticleSystem等组件。同时,讲解了玩家射击效果的实现,涉及Raycast、LineRenderer和Light组件的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.给Enemy添加EnemyHealth.cs

using UnityEngine;

public class EnemyHealth : MonoBehaviour
{
    public int startingHealth = 100;
    public int currentHealth;
    public float sinkSpeed = 2.5f;//Enemy死后的下沉速度
    public int scoreValue = 10;
    public AudioClip deathClip;//Enemy死后的发出的声音


    Animator anim;
    AudioSource enemyAudio;
    ParticleSystem hitParticles;
    CapsuleCollider capsuleCollider;
    bool isDead;
    bool isSinking;//Enemy死了会下沉,isSinking判断是否在下沉


    void Awake ()
    {
        anim = GetComponent <Animator> ();
        enemyAudio = GetComponent <AudioSource> ();
        hitParticles = GetComponentInChildren <ParticleSystem> ();
        capsuleCollider = GetComponent <CapsuleCollider> ();

        currentHealth = startingHealth;
    }


    void Update ()
    {
        if(isSinking)
        {
            transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
        }
    }


    public void TakeDamage (int amount, Vector3 hitPoint)
    {
        if(isDead)
            return;

        enemyAudio.Play ();

        currentHealth -= amount;
            
        hitParticles.transform.position = hitPoint;
        hitParticles.Play();

        if(currentHealth <= 0)
        {
            Death ();
        }
    }


    void Death ()
    {
        isDead = true;

        capsuleCollider.isTrigger = true;

        anim.SetTrigger ("Dead");

        enemyAudio.clip = deathClip;
        enemyAudio.Play ();
    }


    public void StartSinking ()
    {
        GetComponent <UnityEngine.AI.NavMeshAgent> ().enabled = false;
        GetComponent <Rigidbody> ().isKinematic = true;
        isSinking = true;
        //ScoreManager.score += scoreValue;
        Destroy (gameObject, 2f);
    }
}

注意几个API:

1)transform.Translate(Vector3 translation)

Vector3 translation的方向移动

transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime)

每次Update都向-Vector3.up移动sinkSpeed * Time.deltaTime的距离

在PlayerMovement.cs里使用的是:PlayerRigidbody.MovePosition(transform.position + movement);

MovePosition是直接移动到位置:transform.position + movement

2)

public void StartSinking ()
    {
        GetComponent<UnityEngine.AI.NavMeshAgent>().enabled = false;//把Enemy的NavMeshAgent关掉
        GetComponent<Rigidbody>().isKinematic = true;
        //Controls whether physics affects the rigidbody.
        //If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore.
        isSinking = true;
        Destroy(gameObject, 2f);//2秒后消除GameObject
    }

3)anim.SetTrigger ("Dead");

触发触发器“Dead”

对应EnemyAC中的其中一个参数“Dead”:

3.回到上一节给EnemyAttack.cs添加两行代码

using UnityEngine;
using System.Collections;

public class EnemyAttack : MonoBehaviour
{
    public float timeBetweenAttacks = 0.5f;
    public int attackDamage = 10;


    Animator anim;
    GameObject player;
    PlayerHealth playerHealth;
    EnemyHealth enemyHealth;
    bool playerInRange;
    float timer;


    void Awake ()
    {
        anim = GetComponent<Animator>();
        player = GameObject.FindGameObjectWithTag("Player");
        playerHealth = player.GetComponent<PlayerHealth>();
        playerInRange = false;
    }


    void OnTriggerEnter (Collider other)
    {
        
        if(other.gameObject == player)
        {
            playerInRange = true;
        }
    }


    void OnTriggerExit (Collider other)
    {
        if (other.gameObject == player)
        {
            playerInRange = false;
        }
    }


    void Update ()
    {
        timer += Time.deltaTime;
        if (timer>=timeBetweenAttacks&& playerInRange&& enemyHealth.currentHealth>0)
        {
            Attack();
        }
        if (playerHealth.currentHealth <= 0)
        {
            anim.SetTrigger("PlayerDead");
        }
    }


    void Attack ()
    {
        timer = 0f;
        if (playerHealth.currentHealth > 0)
            playerHealth.TakeDamage(attackDamage);
    }
}

4.添加射击效果GunParticles

我再这里用到GunParticles.由于要在枪口发射激光,我们把GunParticles效果粘贴到GunBarrelEnd,注意,并不是Player.

注意,不能直接把GunParticles拖到Hierarchy的GunBarrelEnd上!!

1).先选中GunParticles,在Particles System右上角选择 Copy Component

2)再在Hierarchy里选中GunBarrelEnd,在transform右上角选择Paste Component as new

3)成功添加,所以GunParticles效果会出现在枪的尾部

5.添加射击效果Line Renderer

设置Line Renderer参数:

7.添加射击时的Light

看到效果:

取消勾选Light和Particles,射击时我们才enable它

8.编写PlayerShooting.cs

注意:这是GunBarrelEnd的脚本,不是Player的!!

注意void shoot()这个函数

我们从枪口发射一条射线Ray,由两种可能,一是击中某样东西,二是什么都没击中。第二种情况我们会画一条很长的射线。

using UnityEngine;

public class PlayerShooting : MonoBehaviour
{
    public int damagePerShot = 20;
    public float timeBetweenBullets = 0.15f;//控制枪的射击速度
    public float range = 100f;//子弹能打多远


    float timer;
    Ray shootRay = new Ray();
    RaycastHit shootHit;//返回所击中的东西
    int shootableMask;//和FloorMask类似,只能射中标记为shootable的物体
    ParticleSystem gunParticles;
    LineRenderer gunLine;
    AudioSource gunAudio;
    Light gunLight;
    float effectsDisplayTime = 0.2f;//这些效果在消失之前的显示时间


    void Awake ()
    {
        shootableMask = LayerMask.GetMask ("Shootable");
        gunParticles = GetComponent<ParticleSystem> ();
        gunLine = GetComponent <LineRenderer> ();
        gunAudio = GetComponent<AudioSource> ();
        gunLight = GetComponent<Light> ();
    }


    void Update ()
    {
        timer += Time.deltaTime;

		if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets && Time.timeScale != 0)//("Fire1")指鼠标左键
        {
            Shoot ();
        }

        if(timer >= timeBetweenBullets * effectsDisplayTime)
        {
            DisableEffects ();
        }
    }


    public void DisableEffects ()
    {
        gunLine.enabled = false;
        gunLight.enabled = false;
    }


    void Shoot ()
    {
        timer = 0f;

        gunAudio.Play ();

        gunLight.enabled = true;

        gunParticles.Stop ();
        gunParticles.Play ();

        gunLine.enabled = true;
        gunLine.SetPosition (0, transform.position);//表示gunLine这条线0位置在枪管处

        shootRay.origin = transform.position;
        shootRay.direction = transform.forward;//transform.forward为z轴方向,即枪指的方向

        if (Physics.Raycast (shootRay, out shootHit, range, shootableMask))
        {
            EnemyHealth enemyHealth = shootHit.collider.GetComponent <EnemyHealth> ();
            if(enemyHealth != null)
            {
                enemyHealth.TakeDamage (damagePerShot, shootHit.point);
            }
            gunLine.SetPosition (1, shootHit.point);///表示gunLine这条线1位置在shootHit.point处
        }
        else
        {
            gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);///表示gunLine这条线1位置在离枪头100米处
        }
    }
}

LineRenderer.SetPosition()含义

9.保存Player这个Prefabs

由于我们把Player也设置为了一个Prefabs,记得按Apply,保存Player的所有更改,使得这个prefabs可以被调用!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值