Unity的C#编程教程_52_类 Class 详解及应用练习(一)

2023-11-18

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
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
            }
        }
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity的C#编程教程_52_类 Class 详解及应用练习(一) 的相关文章

  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • C - 找到极限之间的所有友好数字

    首先是定义 一对友好的数字由两个不同的整数组成 其中 第一个整数的除数之和等于第二个整数 并且 第二个整数的除数之和等于第一个整数 完美数是等于其自身约数之和的数 我想做的是制作一个程序 询问用户一个下限和一个上限 然后向他 她提供这两个限
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 如何返回 json 结果并将 unicode 字符转义为 \u1234

    我正在实现一个返回 json 结果的方法 例如 public JsonResult MethodName Guid key var result ApiHelper GetData key Data is stored in db as v
  • 将自定义元数据添加到 jpeg 文件

    我正在开发一个图像处理项目 C 我需要在处理完成后将自定义元数据写入 jpeg 文件 我怎样才能做到这一点 有没有可用的图书馆可以做到这一点 如果您正在谈论 EXIF 元数据 您可能需要查看exiv2 http www exiv2 org
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • clang 实例化后静态成员初始化

    这样的代码可以用 GCC 编译 但 clang 3 5 失败 include
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 当操作繁忙时,表单不执行任何操作(冻结)

    我有一个使用 C 的 WinForms 应用程序 我尝试从文件中读取一些数据并将其插入数据表中 当此操作很忙时 我的表单冻结并且无法移动它 有谁知道我该如何解决这个问题 这可能是因为您在 UI 线程上执行了操作 将文件和数据库操作移至另一个
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 控制到达非 void 函数末尾 -wreturn-type

    这是查找四个数字中的最大值的代码 include
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐