青训营 TS泛型在Cocos开发实践使用 | 豆包MarsCode AI刷题

131 阅读3分钟

本文章是个人在cocos游戏实践开发中所遇到的泛型的使用。

首先说说看什么是泛型

  • 定义:泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而是在使用的时候再去指定类型的一种机制。就好比制作一个可以根据不同需求盛放不同物品的容器,使用的时候再决定这个容器具体用来装什么东西(比如装水果、装书籍等不同类型的物品),这里的 “容器” 就是对应的函数、接口或类,而具体要装的 “物品类型” 就是后面指定的实际类型。

简单来说:在typescript中,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性。

使用示例:

function identity <T>(value: T) : T {
  return value;
}

console.log(identity<Number>(1)) // 1

首次看到 <T> 你会感到奇怪,但他的本质实际上就是像传递参数一样,我们传递了我们想要用于特定函数调用的类型

泛型通过<>的形式进行表述,有三种表达方式分别是函数,类,接口 这里不在细说,我们接下来回归主题泛型在COCOS游戏开发中的实践使用

泛型最主要的两个作用就是:

  • 类型安全性:使用泛型可以在编译时捕获类型错误,避免在运行时出现类型不匹配的错误。通过指定泛型类型参数,可以确保函数或类在处理数据时使用正确的类型,提高代码的可靠性和安全性。

  • 代码重用性:泛型可以使得代码更通用,可以在多个地方使用相同的逻辑,但适用于不同的数据类型。这样可以避免重复编写类似的代码,提高代码的重用性和可维护性。

首先是类型安全性的具体使用。这里用我自己项目的示例:

代码:

import { Card } from '../Card';const { ccclass, property } = _decorator;@ccclass('CardListUI')export class CardListUI extends Component {        @property({type:[Card]})    cardsList:Array<Card> = []
}

编辑器中使用:

这里简单说一下 

在CardListUI这个类实际上是一个卡片栏 用来装上这些植物卡片,而卡片是单独的一个卡片类、因此在这里使用cocos的外部引入我加了一个条件,即如果要加入卡牌栏普通的节点不行,必须要是卡片类的节点才可以,这样可以更好的保证在编辑器中拖拽卡片节点的时候不会出错。从而保证安全行,也为其他使用到了相关逻辑的代码提供了保证。

然后就是**代码重用性,**这里的使用是使用了在unity中使用c#很常用的地方 :泛型单例模式

我们通常是这样写单例的

export default class GameManager
{
    private static instance: GameManager;
    public static getInstance(): GameManager
    {
        if (!GameManager.instance)
        {
            GameManager.instance = new GameManager();
        }
        return GameManager.instance;
    }
   

}

假设我们在游戏中有不同类型的管理器,比如资源管理器、音效管理器等,每个管理器都可以通过单例模式来确保在整个游戏运行过程中只有一个实例存在,方便全局访问和管理相关资源或功能,但是如果管理器一多哪单例也多了起来,这个时候就可以使用**泛型单例模式提高代码复用

**

// 泛型单例类
class GameManagerSingleton<T> {
    private static instance: GameManagerSingleton<any>;
    private manager: T;

    // 私有构造函数,防止外部直接实例化
    private constructor(manager: T) {
        this.manager = manager;
    }

    // 获取单例实例的静态方法
    public static getInstance<T>(manager: T): GameManagerSingleton<T> {
        if (!GameManagerSingleton.instance) {
            GameManagerSingleton.instance = new GameManagerSingleton<T>(manager);
        }
        return GameManagerSingleton.instance as GameManagerSingleton<T>;
    }

    // 获取管理器实例的方法
    public getManager(): T {
        return this.manager;
    }
}

具体使用:
假设我们有一个资源管理器和一个音效管理器的简单实现

// 资源管理器类
class ResourceManager {
    private resources: { [key: string]: any } = {};

    public loadResource(key: string, resource: any) {
        this.resources[key] = resource;
    }

    public getResource(key: string): any {
        return this.resources[key];
    }
}

// 音效管理器类
class AudioManager {
    private sounds: { [key: string]: any } = {};

    public loadSound(key: string, sound: any) {
        this.sounds[key] = sound;
    }

    public playSound(key: string) {
        // 这里假设已经有播放音效的实际逻辑,这里只是模拟
        console.log(`Playing sound: ${key}`);
    }
}

// 创建资源管理器单例实例并使用
let resourceManagerSingleton = GameManagerSingleton.getInstance(new ResourceManager());
resourceManagerSingleton.getManager().loadResource("image1", "path/to/image1.jpg");
console.log(resourceManagerSingleton.getManager().getResource("image1"));

// 创建音效管理器单例实例并使用
let audioManagerSingleton = GameManagerSingleton.getInstance(new AudioManager());
audioManagerSingleton.getManager().loadSound("sound1", "path/to/sound1.mp3");
audioManagerSingleton.getManager().playSound("sound1");

在上述代码中:

  • GameManagerSingleton 类实现了泛型单例模式,它可以用于创建不同类型的游戏管理器的单例实例。
  • ResourceManagerAudioManager 分别是资源管理器和音效管理器的简单示例类,用于演示如何将它们作为单例进行管理和使用。通过 GameManagerSingletongetInstance 方法,我们可以确保在游戏中每个类型的管理器都只有一个实例存在,同时提高单例代码的复用。

好的,以上就是个人在游戏开发实践中所使用到的,有关Ts泛型的具体实践的两个使用。