Episode 04

发布时间 2023-03-22 21:08:46作者: Felix-Fu

Damage ystem——伤害系统

Projectile

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Projectile : MonoBehaviour
{
    //设置渲染层级
    public LayerMask collisionMask;
    float speed = 10;
    float damage = 1;

    public void SetSpeed(float newSpeed)
    {
        speed = newSpeed;
    }

    void Update()
    {
        float moveDistance = Time.deltaTime * speed;
        //判断是否发生碰撞
        CheckCollisions(moveDistance);
        //Projectile移动
        transform.Translate(Vector3.forward * moveDistance);
    }

    void CheckCollisions(float moveDistance)
    {
        Ray ray = new Ray(transform.position, transform.forward);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit, moveDistance, collisionMask, QueryTriggerInteraction.Collide))
        {
            OnHitObject(hit);
        }
    }

    //击中物体后销毁
    void OnHitObject(RaycastHit hit)
    {
        IDamageable damageableObject = hit.collider.GetComponent<IDamageable>();
        damageableObject.TakeHit(damage, hit);
        Destroy(gameObject);
    }
}

CheckCollisions(float moveDistance):此方法检查是否发生碰撞,碰撞后调用OnHitObjec(RaycastHit hit)销毁碰撞的LivingEntity以及本身。

IDamageable

using UnityEngine;

public interface IDamageable
{
    void TakeHit(float damage, RaycastHit hit);
}

Interface:接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。

LivingEntity

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LivingEnitity : MonoBehaviour, IDamageable
{
    public float startingHealth;
    protected float health;
    protected bool dead;

    //赋初始生命值
    protected virtual void Start()
    {
        health = startingHealth;
    }

    //被击中后health值减少到0,调用Die()
    public void TakeHit(float damage, RaycastHit hit)
    {
        health -= damage;
        if (health <= 0 && !dead)
        {
            Die();
        }
    }

    protected void Die()
    {
        dead = true;
        Destroy(gameObject);
    }
}

LivingEnitity:LivingEnitity类(派生类)继承自MonoBehaviour, IDamageable(基类),实现了IDamageable接口中的TakeHit()

继承:继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类

virtual:用于修改方法、属性、索引器或事件声明,并使它们可以在派生类中被重写。

Player

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(PlayerController))]
[RequireComponent(typeof(GunController))]

public class Player : LivingEnitity
{

    public float moveSpeed = 5;

    Camera viewCamera;
    PlayerController controller;
    GunController gunController;

    protected override void Start()
    {
        base.Start();
        controller = GetComponent<PlayerController>();
        gunController = GetComponent<GunController>();
        viewCamera = Camera.main;
    }

    void Update()
    {
        //实现Player移动
        Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
        Vector3 moveVelocity = moveInput.normalized * moveSpeed;
        controller.Move(moveVelocity);

        //实现Player朝向鼠标位置
        Ray ray = viewCamera.ScreenPointToRay(Input.mousePosition);
        Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
        float rayDistance;

        if (groundPlane.Raycast(ray, out rayDistance))
        {
            Vector3 point = ray.GetPoint(rayDistance);
            //Debug.DrawLine(ray.origin, point, Color.red);
            //Debug.DrawRay(ray.origin,ray.direction * 100,Color.red);
            controller.LookAt(point);
        }

        //实现Gun射击
        if (Input.GetMouseButton(0))
        {
            gunController.Shoot();
        }
    }
}

Enemy

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshAgent))]

public class Enemy : LivingEnitity
{
    NavMeshAgent pathfinder;
    Transform target;

    protected override void Start()
    {
        base.Start();
        //追踪Player
        pathfinder = GetComponent<NavMeshAgent>();
        target = GameObject.FindGameObjectWithTag("Player").transform;
        StartCoroutine(UpdatePath());
    }

    void Update()
    {

    }

    //不在Update里面使用SetDestination()防止计算开销过大
    IEnumerator UpdatePath()
    {
        float refreshRate = .25f;//0.25s计算一次
        while (target != null)
        {
            Vector3 targetPosition = new Vector3(target.position.x, 0, target.position.z);
            //防止Die()后继续调用SetDestination()报错
            if (!dead)
            {
                pathfinder.SetDestination(targetPosition);
            }
            yield return new WaitForSeconds(refreshRate);
        }
    }
}

override:EnemyPlayer继承自LivingEntity,重写Start();如果不重写,则还是调用LivingEntity中的方法,而不会调用EnemyPlayer中的Start()

子类中重写父类中的方法,两个函数的函数特征(函数名、参数类型与个数)相同。用于扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现。提供从基类继承的成员的新实现,而通过override声明重写的方法称为基方法。

拓展:

接口 - 定义多种类型的行为 | Microsoft Learn

继承 - 派生用于创建更具体的行为的类型 | Microsoft Learn

virtual - C# 参考 | Microsoft Learn

override 修饰符 - C# 参考 | Microsoft Learn