Unity的C#编程教程_52_类 Class 详解及应用练习
Custom Classes
1. Custom Classes
2. Serialized Custom Class RPG Item Database Example
3. When and Why to Use Custom Classes
Challenge: Customer Database
Custom Class – RPG Spell System Presentation
C# Classes for Behaviours
- 我们在 unity 中新建一个 C# 脚本,都会生成一个默认的 Class:
public class Test : MonoBehaviour
,这个类继承于MonoBehaviour
这个基类,这样的脚本可以挂载到游戏对象下面。 - 而 Class 是面向对象编程的核心,这会让我们的程序模块化,更清晰,更容易理解
- 比如我们可以有一个脚本挂载在 Player 这个游戏对象下面,控制 Player 的各种运动
- 同时有一个脚本挂载在 arrow 下面,控制玩家发射的弓箭运动
- 这些脚本都需要继承于
MonoBehaviour
- 当然我们也可以有不继承于
MonoBehaviour
的 Class - 对于一个 Class 下面,我们也可以利用 Methods 用于区分不同的功能
- 比如对于 Player 控制的类
- 我们可以有一个方法用于控制移动
- 还可以有一个方法控制发射弓箭
- 还可以有个方法控制升级和能力提升,等等
- 而在 Update 这个主要的方法中,只需要在需要的时候调用对应的功能方法即可
- 便于管理代码,查询 bug
Custom Classes
1. Custom Classes
- 假设我们有一个射击游戏
- 我们可以获取不同的枪
- 这个时候,对于不同的枪,就不适合定义在 Player 的脚本下面
- 最好单独设计一个 Class 来存放不同的枪的定义
那么问题来了,如何定义一个 Class 呢?
- 首先我们要知道,这个 Class 下面需要涵盖哪些东西
- 比如枪的名字,攻击力,冷却时间,描述等
- 所以我们可以把这里的 Class 理解为一种整体的描述
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon // 创建一个武器的类,这里不需要继承 MonoBehaviour
{
public string name;
public int attack;
public float coolDown;
public string description;
}
public class Player : MonoBehaviour
{
public Weapon smallGun; // 创建一个变量,类型为武器
public Weapon bigGun;
// Start is called before the first frame update
void Start()
{
smallGun = new Weapon(); // 实例化,即武器被实际创建出来
smallGun.name = "Mini"; // 设定名字
smallGun.attack = 2; // 攻击力
smallGun.coolDown = 0.1f; // 冷却时间
smallGun.description = "It's a small gun!"; // 描述
bigGun = new Weapon()
{
name = "Bob",
attack = 20,
coolDown = 2.2f,
description = "It's a very big gun!"
}; // 也可以通过这种方式实例化的时候同时设定属性
}
// Update is called once per frame
void Update()
{
}
}
注意,在 C# 中,也可以在一个 Class 中创建新的 Class。
上面的写法看起来还是比较复杂,我们还可以用构造函数使得写法更清晰明了:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon // 创建一个武器的类,这里不需要继承 MonoBehaviour
{
public string name;
public int attack;
public float coolDown;
public string description;
public Weapon(string name, int attack, float coolDown, string description)// 构造函数
{
this.name = name;
this.attack = attack;
this.coolDown = coolDown;
this.description = description;
}
}
public class Player : MonoBehaviour
{
public Weapon smallGun; // 创建一个变量,类型为武器
public Weapon bigGun;
// Start is called before the first frame update
void Start()
{
smallGun = new Weapon("Mini", 2, 0.1f, "It's a small gun!");
// 实例化,并设定属性
bigGun = new Weapon("Bob", 20, 2.2f, "It's a very big gun!");
// 实例化,并设定属性
}
// Update is called once per frame
void Update()
{
}
}
构造函数,就是在实例化一个 Class 的时候运行的代码,注意构造函数的名字需要和 Class 的名字相同。
我们还可以直接调用显示相应的信息:Debug.Log(smallGun.name);
这样在游戏中,我们就可以换武器了!
2. Serialized Custom Class RPG Item Database Example
-
如何为一个 RPG 游戏创建一个道具的数据库
- 我们需要设计一个 Class 来定义 item
-
首先创建一个 C# 脚本,命名为 Item
- 这里可以把
: MonoBehaviour
删除,因为这个脚本不需要挂载到游戏对象下面 - 假设我们每个道具有 3 个属性:id,名字,描述
- 这里可以把
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Item
{
public int id;
public string name;
public string description;
public Item()
{
// 空的构造函数,用于初始化空的道具
// 这里多个构造函数是允许的
// 即我们实例化的时候会发现两种构造形式,一种不需要传入参数,一种需要 3 个参数
}
public Item(int id,string name,string description)
{
this.id = id;
this.name = name;
this.description = description;
}
}
- 然后我们再创建一个 C# 脚本,命名为 ItemDatabase
- 这个 Class 是继承于
MonoBehaviour
的
- 这个 Class 是继承于
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemDatabase : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
- 再随便创建一个空的游戏对象,命名为 ItemDatabase
- 把 ItemDatabase 脚本挂载到这个游戏对象下面
现在我们想要创建一个道具:一把剑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemDatabase : MonoBehaviour
{
public Item sword;
public Item Axe;
// Start is called before the first frame update
void Start()
{
sword = new Item(0, "sword", "It's a sword");
Axe = new Item(); // 由于有两种构造函数,这里可以选择先生成再赋值
Axe.id = 1;
Axe.name = "axe";
Axe.description = "It's great!";
}
// Update is called once per frame
void Update()
{
}
}
我们也可以把创建道具的过程提取出来放置到一个函数中。
- Unity 中还有一个特殊功能叫做 serialization,即可以让我们创建的 Class 在 inspector 中可见
需要在我们创建的 Class 前面加上语句:[System.Serializable]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Item
{
public int id;
public string name;
public string description;
public Item()
{
// 空的构造函数,用于初始化空的道具
// 这里多个构造函数是允许的
// 即我们实例化的时候会发现两种构造形式,一种不需要传入参数,一种需要 3 个参数
}
public Item(int id,string name,string description)
{
this.id = id;
this.name = name;
this.description = description;
}
}
保存脚本后,进入 unity,可以看到 inspector 中的脚本组件下面已经显示了 sword 和 axe
这时候我们甚至可以直接在 inspector 中对不同的属性进行设定
这个时候我们可以有更加灵活的编写方式,修改 ItemDatabase 的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemDatabase : MonoBehaviour
{
public Item[] items;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
这个时候回到 unity 中,我们可以任意定义想要创建几个武器(比如20个),并输入每个武器的属性。
这里有个小技巧:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Item
{
public string name; // 把名字放在第一个属性
public int id;
public string description;
public Sprite icon; // 设定道具图标
public Item()
{
// 空的构造函数,用于初始化空的道具
// 这里多个构造函数是允许的
// 即我们实例化的时候会发现两种构造形式,一种不需要传入参数,一种需要 3 个参数
}
public Item(int id,string name,string description)
{
this.id = id;
this.name = name;
this.description = description;
}
}
如果我们把 name 放在第一个属性,那么进入 unity 中编辑每个 item 的名字,Element 2
这样的编号就自动会变成 item 的名字!其实只要第一个属性是字符串就会有这样的效果,但是通常我们会将名字放在第一个位置,而不是描述或者其他的属性。
使用 public Sprite icon;
在 unity 中可以拖动赋值道具显示图标。
在脚本中,我们可以调取这些 unity 中设定的武器的具体信息:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemDatabase : MonoBehaviour
{
public Item[] items;
// Start is called before the first frame update
void Start()
{
Debug.Log(items[1].name); // 显示 1 号武器的名字
}
// Update is called once per frame
void Update()
{
}
}
3. When and Why to Use Custom Classes
- 为什么我们需要自己设计定制化的 Class 呢?
- 假设我们有一个 RPG 游戏,而我们创建了一个脚本 sword 来设定一个武器,比如还有其他 20 种武器,那我一共需要创建 21 个脚本来设定武器吗?除了武器还有其他道具,加起来上千种,那难道要创建上千个脚本文件吗?当然不是!
- 我们会发现,这些需要创建的武器也好,道具也好,都有很多的共同点,比如都有名字,都有一个描述,甚至有些游戏中都有一个价格,那就创建一个 Class 吧!
- 所以什么时候需要自己创建一个 Class 呢?
- 比如你制作个什么东西,发现有很多类似的东西需要制作,就像上面提到的武器,或者你要设计多种能力提升的光环,有的光环提升速度,有的提升攻击力,都可以考虑使用定制化 Class。
- 有比如像要创建敌人,各种小怪,包括 boss 大多数属性都是共同的,比如有血量,攻击力,攻击频率,攻击范围等,这时候也可以考虑写个 Class 定义 enemy。
Challenge: Customer Database
- 任务说明:
- 设计一个 database,用于存储角色信息
- 每个角色包含以下信息:名字,年龄,职业,攻击力,防御力,魔法值
- 这里需要设计两个 Class,一个是定义角色,一个是数据库
首先建立一个脚本,定义角色的类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Character // 记住这里不能继承 MonoBehaviour,否则会出错
{
public string name;
public int age;
public string occupation;
public int attack;
public int defence;
public int magic;
public Character(string name,int age,string occupation,int attack, int defence, int magic)
{
this.name = name;
this.age = age;
this.occupation = occupation;
this.attack = attack;
this.defence = defence;
this.magic = magic;
}
}
创建一个空的游戏对象,定义为 CharacterDatabase,在其下面挂载数据库脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterDatabase : MonoBehaviour
{
public Character[] characters;
// Start is called before the first frame update
void Start()
{
foreach(var item in characters)
{
Debug.Log("Name: "+item.name+" Age: "+item.age);
// 打印所有角色的名字和年龄
}
}
// Update is called once per frame
void Update()
{
}
}
在 unity 中将角色个数设定为 3,然后输入 3 个角色的属性。
运行游戏后,即可打印出这 3 个角色的名字和年龄。
Custom Class – RPG Spell System Presentation
- 任务说明:
- 设定一个 cube 当作魔法师,会运用各种魔法
- 魔法包括:名字,等级,伤害,熟练度
- 设计一个魔法升级系统,每使用一次魔法增加熟练度,熟练度到100即升级,增加伤害
- 魔法可以任意切换
创建一个魔法的类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Magic
{
public string name; // 魔法名字
public int level; // 魔法等级
public int damage; // 魔法伤害
public int exp; // 魔法熟练度
public Magic(string name,int level, int damage,int exp)
{
this.name = name;
this.level = level;
this.damage = damage;
this.exp = exp;
}
public void Cast() // 在该类下面设置一个释放魔法的方法
{
Debug.Log("Cast: " + name); // 释放魔法
}
}
创建一个 cube 游戏对象,命名为 Magician,挂载一个同名的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Magician : MonoBehaviour
{
public Magic[] magics;
private int _id;
// Start is called before the first frame update
void Start()
{
magics = new Magic[3];
magics[0] = new Magic("Fire", 1, 10, 0);
magics[1] = new Magic("Ice", 1, 20, 0);
magics[2] = new Magic("Wind", 1, 30, 0);
_id = 0; //设置默认魔法
Debug.Log("You are using: " + magics[_id].name);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
_id = 0; // 按下 Q 切换到第一个魔法
Debug.Log("You are using: " + magics[_id].name);
}
else if (Input.GetKeyDown(KeyCode.W))
{
_id = 1; // 按下 W 切换到第二个魔法
Debug.Log("You are using: " + magics[_id].name);
}
else if (Input.GetKeyDown(KeyCode.E))
{
_id = 2; // 按下 E 切换到第三个魔法
Debug.Log("You are using: " + magics[_id].name);
}
if (Input.GetKeyDown(KeyCode.Space))
{
magics[_id].Cast(); // 释放魔法
magics[_id].exp += 20; // 每次释放增加 20 点熟练度
if (magics[_id].exp == 100) // 熟练度达到 100
{
magics[_id].level += 1; // 该魔法等级提升
magics[_id].damage = magics[_id].damage * magics[_id].level; // 伤害提升
magics[_id].exp = 0; // 熟练度回到 0
}
}
}
}