[U3D Learning Note] Unity C# Survival Guide (16) -- Abstract Classes

C# Abstract Classes and Methods

Abstract classes(抽象类) allow us to force inheritance and create easily manageable and maintainable code. With abstract methods, we can force implementations. Essentially, this allows us to create a partical template for our class and allows the inherited implementation to finish the class.

举个栗子 比如在进行敌方的设计时 我们通常会有不同的怪(就像下面这样
在这里插入图片描述
因为内置的功能是一样的,重复书写就显得很冗杂。这个时候,我们就可以写一个抽象类作为这些类的基底,其他的类去继承它。
在这里插入图片描述
(啊有点迷茫,继承不是第九章讲过了吗,啊为什么这样就是抽象类了
如果是用第九章的知识的话 我们通常这样做 写个父类 然后继承他的子类 如果父类那边的函数是虚拟函数的话 我们还可以在子类重写它 就像下面这样

namespace ChapterAbstract{
    public class Enemy : MonoBehaviour
    {
        public int speed;
        public int health;
        public int gems;
        public void Attack()
        {
        }

    public class MossGiant : Enemy
    {
        private void Start()
        {
            Attack();
        }
    }
}

现在我们的MossGiant类虽然是继承了Enemy类,但是不一定用Enemy里面声明的元素,比如Attack()函数。如果我们要强制继承/强制执行的话,就要用到抽象类(虚函数也不是强制要重写的)。如果我们在父类声明了一个抽象函数,那么子类就必须重写/执行这个函数。
在这里插入图片描述

    public abstract class Enemy : MonoBehaviour
    {
        public int speed;
        public int health;
        public int gems;
        public abstract void Attack();
        public virtual void Die(){
            Destroy(this.gameObject);
        }
    }

    public class MossGiant : Enemy
    {
        public override void Attack(){
            throw new System.NotImplementedException();
        }

        public override void Die(){
            // custom particles
            base.Die();
        }
	
	}

另外 抽象类不能实例化 也就是说 抽象类不能附着在gameobject上
在这里插入图片描述
还是有点懵 大概总结一下抽象类相当于一个模板,所有继承它的类都要强制执行里面的抽象函数

Challenge: Employee Experience

在这里插入图片描述

	public abstract class Employee : MonoBehaviour
    {
       public string companyName;
       public string employeeName;
       public abstract int CalculateMonthlySalary();
    }
    
    public class FullTime : Employee
    {
        public int salary;
        public override int CalculateMonthlySalary(){
            return salary*20;
        }
    }

    public class PartTime : Employee
    {
        public int hoursWorked;
        public int hourlyRate;

        public override int CalculateMonthlySalary(){
            return hoursWorked*hourlyRate;
        }

    }

Interfaces(接口)

  • Interface is similar to abstract class. Abstract can be thought as a patical template, while interface can be thought of a contract(合约) and whatever in that contract must be implemented.
  • Interfaces don’t allow for any implementaions(u can’t complete the method, just declare) and cannot contain any fields(not int, string etc). You are only able to use methods and properties. 抽象类和接口都是强制执行,但是抽象类可以有具体的方法内容以及变量赋值,接口有且仅能声明属性和方法,不能做任何赋值。
  • 创建Interface的命名规则: 文件名以大写字母I做开头来表示这是一个interface,后面跟随名字,通常情况下以-able作为结尾, 比如IDamagable, IFlexiable
    下面是一个interface的例子
public interface IDamagable
{
    int Health {get; set; } //properties

    void Damage(int damageAmout); //method(u can't implement
} 

当我们将一个类继承这个接口, 我们要把接口的东西都得执行

	public class EnemyInter : MonoBehaviour, IDamagable
    {
        public int Health {get; set;}
        public void Damage(int damageAmout){
            Health -= damageAmout;
        }
	}

一个类有且仅能继承一个父类,但是可以继承多个接口, 父类也可以继承祖父类。接口不能继承类,但接口之间是多继承的。
在这里插入图片描述

  • Polymorphism(多态) : an object shares relevancy. 多态允许我们接口内的函数可以以不同类型的数据作为参数输入,下面是我们对同一函数创造不同输入合约的方式。

在这里,我们的函数出现名字相同但是输入类型的不同,也就是进行了重载(overload)(ps. 名字相同输入不同的就是重载) 。重载在函数的层面上改变输入参数。举个栗子,构造函数的重载。

    public interface IDamagable
    {
        int Health {get; set; } //properties

        void Damage(int damageAmout);
        void Damage(float damageAmout);
        
		void Attack(int attackSpeed);
		void Attack(float attackSpeed);
    } 

但是 ,万一我们有超级多种输入类型,这样子写就显得很冗杂。这时候就要用到generic type(通用类型)的方式。T表示它是一个通用类型。这就是多态的实现,在类的层面上改变了输入参数类型。

    public interface IDamagable<T>
    {
        int Health {get; set; } //properties
        void Damage(T damageAmout);// method(u can't implement
        void Attack(T attackSpeed);
    } 


	public class Player : MonoBehaviour,IDamagable<int>
    {
        public int Health {get; set;}
        public void Damage(int damageAmout){
            Health -= damageAmout;
            GetComponent<MeshRenderer>().material.color = Color.blue;
        }
	}

多态的使用就是避免类中函数的大量重载

Polymorphism(多态)

这节是一个多态相关的应用 但是代码也涉及到一些常见的功能实现
KEY:

  • Input.GetMouseButtonDown(0) : left key of button
  • RayCasting Code: Check for user input where the mouse is, and we’re going to cast a ray on those objs.
void Update()
        {
            if(Input.GetMouseButtonDown(0)){
            	//cast a ray from our mouse position
                Ray rayOrigin = Camera.main.ScreenPointToRay(Input.mousePosition);
                // store whatever i hit
                RaycastHit hitInfo;

                if(Physics.Raycast(rayOrigin, out hitInfo)){
                	/*ERROR
                	hitInfo.collider.GetComponent<Player>().Damage(10); 
                	hitInfo.collider.GetComponent<EnemyInter>().Damage(100); 
                	*/
                	if(hitInfo.collider.name == "Player"){
                        hitInfo.collider.GetComponent<Player>().Damage(100); 
                    } else{
                        hitInfo.collider.GetComponent<EnemyInter>().Damage(100);  
                    }
                }
            }                     
        }

OK 现在的问题时我们不能以上面注释块的方式来写(会出现以下报错) 我们要进行判断我们是hit的对象是什么来分别针对的写。 那么问题来了 如果场景有好多不同类型的怪那是不是就得写好几行的ifelse或者switch? 我不想要知道我hit的对象是什么类型,我只想要当我打到它时会调用Damage()函数就行,那么这个时候就要靠接口啦。
在这里插入图片描述
我们前面已经把PlayerEnemy都继承了IDamagable接口,所以,在这边其实我们只要判断我们打击到的东西是否继承了IDamagable接口就行了。 上面的代码就可以改成

	if(Physics.Raycast(rayOrigin, out hitInfo)){
		IDamagable<int> obj = hitInfo.collider.GetComponent<IDamagable<int>>();
		if(obj != null){
			obj.Damage(500);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值