Episode 04
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:Enemy、Player继承自LivingEntity,重写Start();如果不重写,则还是调用LivingEntity中的方法,而不会调用Enemy、Player中的Start()。
子类中重写父类中的方法,两个函数的函数特征(函数名、参数类型与个数)相同。用于扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现。提供从基类继承的成员的新实现,而通过override声明重写的方法称为基方法。
拓展:
接口 - 定义多种类型的行为 | Microsoft Learn
继承 - 派生用于创建更具体的行为的类型 | Microsoft Learn