第十六章 预制件prefab(上)

news2024/12/22 20:02:44

本章节我们介绍一下“预制件”,也有人叫“预制体”,也就是Prefab。在游戏世界中,那些自然环境的游戏对象,我们可以提前创建在场景中,这个大家能够理解。但是,有些游戏对象,需要根据游戏逻辑来通过代码生成,例如刷新怪物,触发机关等等。Unity 的预制件系统允许创建、配置和存储游戏对象及其所有组件、属性值和子游戏对象作为可重用资源(它的本质就是一个资源文件,相当于编程里面的类文件,通过它可以实例化多个对象)。预制件资源充当模板,在此模板的基础之上可以在场景中创建新的预制件实例。对预制件资源所做的任何编辑都会自动反映在该预制件的实例中,因此可以轻松地对整个项目进行广泛的更改,而无需对资源的每个副本重复进行相同的编辑。

预制体是用来存储一个游戏对象的所有组件,属性和子对象,这样就成为了一个可重复使用的资源文件。当需要多次重复使用这个游戏对象时,便可以使用预制体来创建。举一个简单的例子,我们会在游戏世界中创建同一种怪物模型的实例,显然我们不可能将怪物模型一个个的拖拽到游戏世界中,并且这些怪物会死亡消失,也会重新刷新。因此,合理的解决方案应该是,每一种怪物都应该是一个预制体,然后我们可以通过预制体来创建不同的怪物实例,这就是预制体的优势。当然,预制体更重要的是对游戏对象的“封装”,预制体不仅仅是网格模型,同时还可以包含各种功能组件以及脚本,这样的预制体就能承载更多的内容。

接下来,我们回到ScriptDemo项目工程中,创建一个新的“SampleScene7”场景,然后创建一个“Cube”游戏对象和“CubePrefab.cs”脚本文件,并将其附加在一起。

 

接下来,我们在“CubePrefab.cs”脚本文件实现Cube自旋转的代码,如下所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CubePrefab : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(new Vector3(0, 1, 0));
    }
}

为了更好的观察Cube,我们将摄像机向上Y轴方向移动5个单位,且X轴方向旋转20度,这样就能俯视Cube了。

接下来,我们保持相机选中状态,我们点击菜单栏“GameObject”->“Align View to Selected”

然后我们Play当前工程,效果如下:

接下来,我们要做的就是创建十个这样的旋转立方体,粗暴的办法就是再创建9个Cube,然后将“CubePrefab.cs”脚本文件分别附加到这9个Cube身上,显然这种方式不太合理。此时,我们就需要借助预制体来实现了。首先我们要创建预制体,创建的方法非常简单,就是将“Hierarchy”视图中的Cube游戏对象拖拽到“Project”视图中。

这样一个名称为“Cube”的预制体就创建成功了,同时在“Hierarchy”视图中“Cube”的名称颜色会变成蓝色,代表这个名称为“Cube”的游戏对象是一个预制体实例。

请注意,预制体本质是一个资源文件,也就是“Project”视图中的“Cube”文件,如果我们去我们的工程目录下的Asset文件夹下面查看,就能看到一个“Cube.prefab”的预制体文件。

那么“Hierarchy”视图中“Cube”又是什么呢,它其实就是预制体的一个实例而已。我们可以将预制体理解为面向对象变成中的类文件,类就是一个模板,我们可以根据这个类实例化很多很多的对象,这些对象都拥有类中实现的功能,只是每一个对象的内部数据不一样。既然“Cube.prefab”是一个预制体,它与类模板概念相同,我们就可以根据这个预制体来实例化很多很多的游戏对象,也就是“Hierarchy”视图中“Cube”。将一个预制体实例化为一个游戏对象,需要借助“GameObject.Instantiate”方法,该方法有三个参数,第一个就是预制体文件,第二个就是游戏对象的位置,第三个就是游戏对象的朝向。接下来,我们来实现这个功能。我们首先删除调当前场景中的“Cube”游戏对象。然后,我们创建一个“CubePrefabManager.cs”的脚本文件,并将其附加到摄像机上面去。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class CubePrefabManager : MonoBehaviour
{
    // 预制体文件
    public GameObject prefab;

    // 预制体实例数量
    private int count;

    // Start is called before the first frame update
    void Start()
    {
        // 加载预制体文件
        prefab = AssetDatabase.LoadAssetAtPath("Assets/Cube.prefab", typeof(GameObject)) as GameObject;
    }


    // Update is called once per frame
    void Update()
    {
        // 按下空格键就生成一个自旋转的Cube
        if (Input.GetKeyDown(KeyCode.Space))
        {
            count++;
            Vector3 pos = new Vector3(count * 2, 0, 0);
            GameObject.Instantiate(prefab, pos, Quaternion.identity);
        }
    }
}

不要忘了,将“CubePrefabManager.cs”脚本附加到相机上。以上代码有三点需要解释。第一,我们的prefab文件需要载入,它的本质就是GameObject类型。第二,我们按下空格键来根据prefab来创建一个游戏对象实例。第三,创建的prefab实例的位置依次向右递增,但是他们的朝向(旋转)统一是Quaternion.identity(不旋转/默认值)。接下来,我们就可以Play当前工程,查看效果。

当我们实例化出来四个Cube游戏对象实例的时候,他们的名称都是“Cube(Clone)”,也就是复制克隆出来的。这个应该比较容易理解。我们从Hierarchy层次面板中就能看到。

最后我们要说的就是关于prefab文件的加载。上面的代码中,我们使用了AssetDatabase.LoadAssetAtPath方法(还有使用“using UnityEditor;”)。其实我们我们可以将“Cube.prefab”文件放置工程目录“Assets/Resources”下。这个“Resources”目录是一个特殊的目录,它对应Unity API中的Resources类,该类有一个Load静态方法,可以直接读取下面的预制体文件。接下来,我们就创建“Resources”,并将“Cube.prefab”复制到这个文件夹下,重命名为“Cube2.prefab”,如下图所示:

然后我们修改“CubePrefabManager.cs”代码:

// 加载预制体文件
//prefab = AssetDatabase.LoadAssetAtPath("Assets/Cube.prefab", typeof(GameObject)) as GameObject;
prefab = Resources.Load<GameObject>("Cube2 ");

当然,此方法并不只是加载prefab资源文件,还可以加载其他文件(例如贴图)。注意,这个方法不需要我们使用“using UnityEditor;”,并且参数为预制件名称(不需要文件后缀)。为了更好的管理这些资源文件,我们可以继续在“Assets/Resources”规划子目录管理他们。那么,在使用Resources.Load加载的时候,只需要在参数中按照“xxx/yyy”方式加载即可。这种方式还是比较推荐的。接下来,我们Play工程,看到之前一样的效果了。

还有两一种更加省时省力的办法。由于我们声明的prefab变量是public类型,因此可以在Inspector检视面板中编辑这个prefab变量。我们在Hierarchy层次视图中点击选中相机,然后查看Inspector检视面板,如下所示:

然后,我们可以直接将“Project”视图中的“Cube”预制体拖拽到这个属性值上面。

这样,我们就不用使用代码来加载这个预制体文件啦。

我们注释调start中的代码,如下

    // Start is called before the first frame update
    void Start()
    {
        // 加载预制体文件
        //prefab = AssetDatabase.LoadAssetAtPath("Assets/Cube.prefab", typeof(GameObject)) as GameObject;
        //prefab = Resources.Load<GameObject>("Cube2");
    }

接下来,我们重新Play工程,就能看到之前一模一样的效果了。我们就不添加效果图了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/477679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20230430 ICFD学习笔记 管道流动

三个边界: (1) 速度入口 (2) 压力出口 (3) 非滑移边界 一、先利用workbench进行网格的划分,导出K文件。 二、利用Ultraeidt进行K文件的修改 (或者是在lspp中直接删除也行) K文件开头是*Keyword Part *Define coordinate system后面到*Database binary D3prop全部删除 &…

Dubbo总结

目录 什么是分布式系统 单机架构、集群架构、分布式架构 Dubbo的概念 Dubbo的核心组件 Dubbo的常用注解 Dubbo的高级特性&#xff1a; 序列化特性安全 地址缓存 超时机制 重试机制 多版本灰度发布 负载均衡 集群容错 服务降级 服务限流 结果缓存 Dubbo实战&#xff1a;…

40.java-Set集合(HashSet,LinkedHashSet,TreeSet)

Set集合 1.Set集合特点2.Set集合实现类3. HashSet3.1 底层原理3.1.1 哈希表组成3.1.2 哈希值3.1.3 对象的哈希值特点 3.2 数据添加元素的过程3.3 HashSet的三个问题3.4 实例&#xff1a;去除重复元素 4. LinkedHashSet5. TreeSet5.1 特点5.2 集合默认规则5.3 例子5.4 两种比较规…

JavaScript 知识总结上篇(更新版)

1. 为什么 JS 是单线程的&#xff1f; 因为JS里面有可视的Dom&#xff0c;如果是多线程&#xff0c;这个线程正在删除DOM节点&#xff0c;另一个线程正在编辑Dom节点&#xff0c;导致浏览器不知道该听谁的 2.如何理解同步和异步&#xff1f; 同步&#xff1a;按照代码书写顺…

Linux——中断和时间管理(下)

目录 延时控制 定时操作 低分辨率定时器 高分辨率定时器 练习 延时控制 在硬件的操作中经常会用到延时&#xff0c;比如要保持芯片的复位时间持续多久、芯片复位后要至少延时多长时间才能去访问芯片、芯片的上电时序控制等。为此&#xff0c;内核提供了一组延时操作函数。…

DDD系列:三、Repository模式

为什么需要Repository&#xff1f; ​ Anemic Domain Model&#xff08;贫血领域模型&#xff09;特征&#xff1a; 有大量的XxxDO对象&#xff1a;这里DO虽然有时候代表了Domain Object&#xff0c;但实际上仅仅是数据库表结构的映射&#xff0c;里面没有包含&#xff08;或…

kafka整理

kafka整理 一、kafka概述 kafka是apache旗下一款开源的顶级的消息队列的系统, 最早是来源于领英, 后期将其贡献给apache, 采用语言是scala.基于zookeeper, 启动kafka集群需要先启动zookeeper集群, 同时在zookeeper记录kafka相关的元数据 kafka本质上就是消息队列的中间件产品…

Codeforces Round 867 (Div. 3)(A-G2)

文章目录 A. TubeTube Feed1、题目2、分析3、代码&#xff0c; B. Karina and Array1、题目2、分析3、代码 C. Bun Lover1、问题2、分析&#xff08;1&#xff09;观察样例法&#xff08;2&#xff09;正解推导 3、代码 D. Super-Permutation1、问题2、分析&#xff08;1&#…

力扣第343场周赛

第一次力扣&#xff0c;等大二寒暑假&#xff0c;有时间再来系统刷题 目录 &#x1f33c;前言 &#x1f33c;一&#xff0c;6341.保龄球游戏的获胜者 &#x1f33c;二&#xff0c;6342.找出叠涂元素 &#x1f333;第一次 -- 超时 &#x1f333;第二次 -- AC &#x1f33c…

二叉树相关的简单递归oj

二叉树相关的简单递归oj 前言题目二叉树的前序遍历相同的树判断单值二叉树对称二叉树另一棵树的子树创建二叉树并遍历 前言 这篇博客主要是博主感觉对二叉树oj题目不太熟悉&#xff0c;随便整理的一下题目和解答&#xff0c;方便复习&#xff0c;所以讲题部分主要以我自己以及为…

Java 基础入门篇(二)——— Java 基础语法

文章目录 一、注释二、字面量三、变量3.1 变量概述3.2 变量在计算机中的底层原理 四、数据类型五、关键字、标志符六、类型转换6.1 自动类型转换6.2 表达式的自动类型转换6.3 强制类型转换 七、运算符7.1 基本算数运算符7.2 符号做连接符7.3 自增自减运算符7.4 赋值运算符7.5 …

【C++技能树】类的六个成员函数Ⅰ --构造、析构、拷贝构造函数

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 0.this指针1.Class默认成员函数2.构造函数调用规则: 3.析构函数4.拷贝构造函数 0.this指针 在开始本章内容之前&#xff0c;先浅…

Channel-wise Knowledge Distillation for Dense Prediction(ICCV 2021)原理与代码解析

paper&#xff1a;Channel-wise Knowledge Distillation for Dense Prediction official implementation&#xff1a;https://github.com/irfanICMLL/TorchDistiller/tree/main/SemSeg-distill 摘要 之前大多数用于密集预测dense prediction任务的蒸馏方法在空间域spatial…

(求正数数组的最小不可组成和,养兔子)笔试强训

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、选择题1 二、[编程题]养兔子 三、[编程题]求正数数组的最小不可组成和 一、选择题1 reflection是如何工作的__牛客网 (nowcoder.com) 考虑下面这个简单的例子&…

大数据Doris(八):Broker部署和集群启停脚本

文章目录 Broker部署和集群启停脚本 一、Broker部署 1、准备Broker 安装包 2、启动 Broker

PyQt6剑指未来-日期和时间

前言 时间和日期是软件开发中非常重要的概念。在PyQt6中&#xff0c;时间和日期模块提供了处理日期、时间和日期时间的类和函数&#xff0c;以及管理时区和夏令时的特性。这些模块提供了可靠和易于使用的工具&#xff0c;使得在PyQt6中处理和呈现时间和日期的操作变得轻松起来…

Java中Lambda表达式(初学到精通)

目录 一、Lambda表达式是什么&#xff1f;什么场景下使用Lambda&#xff1f; 1.Lambda 表达式是什么 2.函数式接口是什么 第二章、怎么用Lambda 1.必须有一个函数式接口 2.省略规则 3.Lambda经常用来和匿名内部类比较 第三章、具体使用举例&#xff08;&#xff09; 1.案…

跳跃游戏类题目 总结篇

一.跳跃游戏类题目简单介绍 跳跃游戏是一种典型的算法题目&#xff0c;经常是给定一数组arr&#xff0c;从数组的某一位置i出发&#xff0c;根据一定的跳跃规则&#xff0c;比如从i位置能跳arr[i]步&#xff0c;或者小于arr[i]步&#xff0c;或者固定步数&#xff0c;直到到达某…

C++ 链表概述

背景 当需要存储大量数据并需要对其进行操作时&#xff0c;常常需要使用到链表这种数据结构。它可以用来存储一系列的元素并支持插入、删除、遍历等操作。 概念 一般来说&#xff0c;链表是由若干个节点组成的&#xff0c;每个节点包含了两个部分的内容&#xff1a;存储的数…

【嵌入式环境下linux内核及驱动学习笔记-(6-内核 I/O)-阻塞与非阻塞】

目录 1、阻塞与非阻塞1.1 以对recvfrom函数的调用及执行过程来说明阻塞的操作。1.2 以对recvfrom函数的不断轮询调用为例&#xff0c;说明非阻塞时进程的行为。1.3 简单介绍内核链表及等待队列1.4 等待队列1.4.1 定义等待队列头部&#xff08;wait_queue_head_t&#xff09;1.4…