Pixel Adventure Unity2D开发完整指南

news2024/9/20 20:21:20

本文参考:2-2. Get and Setup Assets_哔哩哔哩_bilibili

1、下载资源

在Asset Store中下载Pix Adventure1 +2的资源:

在import的时候,不用到Scene import进来,如下图所示,Scenes目录反勾选一下。

两个资源都下载完成后,则下图所示:

在Assets下创建目录名为Graphiics,然后把Pixel Adventure1和Pixel Adventure2下Assets中的内容全部都copy到Graphics目录下,如下图所示:

2、Unity使用基础说明

(1)CSharp知识

1)virtual和abstract的区别

abstract方法只能声明在抽象类中,同时不能有方法体,必须被子类重写

virtual方法可以声明在任何非密封类中,它可以有方法体,可以被子类选择性重写

2)使用父类的方法

base.func()

(2)Collider 

我们经常使用的Collider是Box Collider,这个是矩形的碰撞体。实际上还有Circle Collider。这样,针对不同的物体形状,我们可以选择不同的Collider。

(3)Debug模式

在Normal模式下,我们看到的是对象所有组件的Inspector信息,如下:

在Debug模式下,我们可以看到组件中各个变量的详细信息,所以debug时该模式非常有用。

(4)SerializeField字段

在C#脚本中

如果我们希望公开某个变量,那么可以使用public进行标识

如果我们不希望公开某个变量,那么可以使用private进行标识

如果我们不希望公开某个变量,但是在Inspector中又可以看到该变量的值,那么可以使用private进行标识,同时加上[SerializeField]的标识,示例如下:

[SerializeField] private float speed;

(5)Header字段

用于标记下方在Inspector中显示字段的header名称,效果如下:

(6)ContextMenu字段

创建类的选项,本身继承自MonoBehaviour。

比如在脚本中创建ContextMenu信息如下:

  [ContextMenu("Debug here")]
  private void print()
  {
      Debug.Log("here!");
  }

此时当脚本挂载到某个对象后,右击该脚本,即可看到"Debug here"的选项,点击该选项即可执行对应的函数。

(7)Camera方式让Game视野变大

如果Game中物体过大,一种做法是分别缩小每个物体,如果物体特别多操作起来特别麻烦。另一种做法是,修改Camera的size大小,值越大视野也就越大。

(8)Flip左右翻转的方法

transform.Rotate(0, 180, 0);

通过rb.velocity.x判断朝向,如果该值小于0则朝左,否则朝右。

完整方法:

(9)例程(类似进程)

启动例程:StartCoroutine(func()), func的返回值为IEnumerator类型。

使用例程的好处:单独开进程,不会阻塞当前主进程。

 比如例程中含有sleep时间,主线程就不用卡在那里了。

等待时间的写法:yield return new WaitForSeconds(xx);

例程举例:

假如两个例程存在相互干扰,那么可以在例程开始时停止已有的所有例程,使用StopAllCoroutines()方法。

比如:

(10)物体移动的方式

方法1: transform.position = new Vector2(pos.x, pos.y);

方法2:transform.position = Vector2.MoveTowards(pos1, pos2, moveSpeed * Time.deltaTime);

(11)给物体施加力量rb.AddForce

方法1:AddForce(Vector3 force),使用Vector3类型参数,可以分别向刚体按Vector3对象指定x/y/z分量施加力。使用该方法会对刚体施加一个持续的力。rb.AddForce(new Vector3(10f, 0f, 0f));

方法2:AddForce(x, y, x),与方法1类似,只是把Vector3的3个值拆开了。

方法3:AddForce(Vector3 force, ForceMode mode),其中force是施加力的矢量,参数mode是一个枚举类型的参数,用于指定力的模式。模式有:

  • ForceMode.Force:施加一个持续的力
  • ForceMode.Impulse:施加一个瞬间的冲击力
  • ForceMode.Acceleration:施加一个持续的加速度
  • ForceMode.VelocityChange:施加一个改变刚体速度的力

方法4:AddForce(x, y, z, ForceMode mode),与方法3类似,只是把Vector3的3个值拆开了。

(12)创建预设体

GameObject newObj = Instantiate(prefab, position, Quaternion.identity);

其中,prefab也是GameObject的对象

position是Vector3的位置信息

Quaternion.identity表示新复制的对象不会有初始旋转,假如使用prefab.transform.rotation表示新复制的对象会保留原来的旋转。

(13)Invoke委托函数

它是U3D的一种委托机制。

Invoke("SendMsg", 5); 意思是:5秒钟之后调用SendMsg()方法。

注意:

1)只能在脚本的生命周期里的(Start、Update、OnGUI、FixedUpdate、LateUpdate)中被调用

2)Invoke不接受含有参数的方法

3)当Time.ScaleTime = 0 时 invoke()方法无效

(14)Raycast检测光线

Physics2D.Raycast是用于检测2D光线命中图层的函数,可以用于检测光线是否与指定图层中的物体相交,并返回相交点的信息。

常见的使用方法:

RaycastHit2D hit = Physics2D.Raycast(rayOrigin, rayDirection, rayLength, layerMask);

rayOrigin:光线的起点

rayDirection:光线的方向

rayLength:光线的长度

layerMask:需要检测的图层

(15)设置物体间的碰撞矩阵

在Edit -> Project Settings -> Physics 2D -> Layer Collision Matrix

通过设置该矩阵,可以让反选的两个物体之间不会发生碰撞。

3、Animator和Animation的使用

(1)创建Idle动画

Animation用来将图片生成动画,Animator可控制动画的状态。

首先,在Assets下创建Animations的目录,并在该目录下右击创建Animator Controller,如下图所示:

其次,在SampleScene中Create Empty,命名为Player。将Graphics -> Main Characters -> Virtual Guy中Idle的一张图片拖入Scene,该对象重命名为Animator,并移到Player的下一级。同时将上一步的Animator Controller拖到Animator下作为组件。如下图所示:

此时,双击Animator组件的Controller的Player,会进入Animator的编辑界面。

点击Window -> Animation -> Animation,就出来Animation的编辑界面。

点击Create,重命名为PlayerIdle,此时在Animator界面中会自动出现PlayerIdle的节点,并且与Entry相连。

然后将Assets的Graphics下所有的Idle图片拖入Animation中,点击播放就可以看到Idle状态下的动画了。

播放动画时会发现对象运动过快,此时需要调整Sample Rate的值,但是目前Animation界面中没有操作该值的选项。需要点击Animation界面右上角的三个点,然后点击"Show Sample Rate"。

默认情况下Samples的值为60,我们调整到15.

(2)创建Move动画

在Animation界面,点击PlayerIdle下的Create NewClip,并且重命名为PlayerMove。

  ->   

然后,把Run的所有动画拖入Animation中,并且设置Samples的值为15。

此时在Animator下会有两个节点,然后分别右击两个节点Make Transition。

点击Animator的Parameters,添加变量isRunning。后续通过该变量实现两个动画之间的切换。

然后设置Transition的条件,Idle -> Move的isRunning为true,Move -> Idle的isRunning为false。

并且反选Has Exist Time。如下图所示:

(3)编写脚本

创建一个Squre,然后挂载Box Collider 2D的组件。

给Animator对象挂载Rigidbody 2D + Capsule Collider 2D这两个组件,Rigidbody 2d-> Constraints -> Freeze Rotation 勾选Z,否则物体移动时会滚动着移动。

然后在Assets下创建Scripts的目录,并创建AnimatorPlay的C#脚本,挂载到Animator对象下,完整的C#脚本如下:

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

public class AnimatorPlay : MonoBehaviour
{
    Rigidbody2D rb;
    public float x;
    Animator anim;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }

    void Start()
    {
        
    }


    void Update()
    {
        x = Input.GetAxisRaw("Horizontal");
        anim.SetBool("isRunning", rb.velocity.x != 0);
        rb.velocity = new Vector2(x, rb.velocity.y);
    }
}

(4)创建Blend Tree混合树

混合树:就是将多个动画混合在一起,通过阈值控制动画的变换。也需要通过变量控制别的节点transition到该Blend Tree。

首先,点中Animator这个角色,然后在Animation下Create New Clip,命名为PlayerJump如下:

然后把Jump的图片拖进去,只有一帧。

同样的方法创建PlayerFall,然后把Fall的图片拖进去,同样也只有一帧。

接着,在Animator界面中,将其他Animator动画都删除掉,只剩一个PlayerIdle,右击Create State -> From New Blend Tree。

创建的Blend Tree重命名为Jump/Fall。

双击该Blend Tree进入编辑界面

1)增加Parameters:yVelocity

2)在右边Inspector -> Parameter中选择yVelocity

3)点击"+",Add Motion Field,分别把PlayerJump和PlayerFall添加进去

4)反勾选Automate Thresholds,然后修改PlayerJump和PlayerFall的Threshold分别为-1和1

5)增加一个isGround的Bool变量,通过该变量控制PlayerIdle到Jump/Fall之间的切换,到isGround=false时变为Jump/Fall,否则变为PlayerIdle。

6)代码中控制transition的变化(isGound的判断并不是很严谨,只是演示使用)

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

public class AnimatorPlay : MonoBehaviour
{
    Rigidbody2D rb;
    public float x;
    Animator anim;
    Boolean isGround;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
    }

    void Start()
    {
        
    }


    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            rb.velocity = new Vector2(rb.velocity.x, 5);
        }

        anim.SetFloat("yVelocity", rb.velocity.y);
        anim.SetBool("isGround", isGround);

        if(rb.velocity.y != 0)
        {
            isGround = false;
        }
        else
        {
            isGround = true;
        }
        
    }

}

(5)触发类型Trigger

该类型没有任何状态值,通过trigger可以直接触发某个动画。

演示:

1)创建PlayerKnockback的动画,并把所有的Hit动画拖入其中,设置Samples为12。

2)在Animator中,创建Any State 到PlayerKnockback的transition,条件中反勾选“Can Transition to S”(不能重新回到原来状态,否则会导致循环了),设置Transition Duration为0。

增加knockback的trigger类型变量,在transition中也指定一下该变量。

3)在Animator中,创建PlayerKnockback到PlayerIdle的transition,设置Exit Time为1(表示knockback的动画需要存在1秒钟),设置Transition Duration为0。

4)脚本中编写使用逻辑:

在Update()函数中,

if(Input.GetKeyDown(KeyCode.K)){

        Knockback():

}

编写和Update平级的函数Knockback:

public void Knockback(){

        anim.SetTrigger("knockback");

}

(6)Add Event

在Animation界面,可以选择一帧Add Event,如下图所示:

add Event的函数只能选择当前gameobject挂载脚本中的方法。

4、Tile Palette瓦片调色板

(1)打开Tile Palette

Window -> 2D -> Tile Palette,点击Create New Palette。

将新的Palette命名为:Tile Palette

保存到Assets的新建的Tile Palette目录下:

(2)使用Tile Palette

打开Assets -> Graphics,选择Terrain Slice -> Sprite Editor

点击后可以看到各种切片好的图片:

选择Pixels Per Unit为16,然后点击Apply。

将Terrain Slice拖入到Tile Palette 下,然后选择保存目录为新建的 Assets -> Tile Paletter -> Assets:

此时,在Tile Palette界面可以看到切片后的的图片,同时在Assets -> Tile Palette -> Assets下也可以看到一个个切片后的图片。

在Hierachry下2D Object -> Tilemap -> Rectangular,此时在Hierachry下会出现Grid -> Tilemap,重命名为Ground。

点击Brush画笔,在Scene界面左击即可添加元素。

按Shift左击可以删除元素

(3)创建基于Tile的组件

1)添加Tilemap Collider 2D组件

ps:Tilemap:瓦片地图

此时角色可以站在我们创建的Ground之上。

但是此时存在一个问题,就是Ground上创建了很多的Collider检测器如下:

我们需要合并这些Collider以提高性能。

2)添加Composite Collider 2D组件

添加完之后会自动添加了Rigidbody 2D组件,

在Rigidbody 2D -> Body Type中选择Static,表明这个对象不需要动态计算刚体。

然后在Tilemap Collider 2D的Used By Composite打勾,此时Collider就会合并。

对比如下:

合并前:

合并后:

(4)创建Background

在Assets -> Graphics -> Background下选择图片,并设置Sprite mode为multiple, Pixels Per Unit为16.

点击Sprite Editor, Slice -> Grid By Cell Size,并设置Pixel Size为16 * 16,与之前的参数吧保持一致。

将切片后的图片拖入Tile Palette,然后根据提示保存到Assets -> Tile Palette -> Assets下。

在Hierachry的Grid下创建新的Tilemap命名为Background。

选中背景图,然后点击paint,即可在scene中画出一条。

选择Pick,然后在Scene中选中那一条,即可整条的进行draw,而不用一块一块的。

如下,整条进行draw。

画完之后,之前的ground不见了,只剩下角色和背景了。

(5)调整显示优先级

在Hierachy中点击Ground,点击Tilemap Renderer,在Sorting Layer中,点击Add Sorting Layer。

新增3个Sorting Layers如下:

Layer值越大,显示时越靠前。

然后Animator角色、Ground、Background分别选择对应的Sorting Layer,就可以正确显示如下:

5、GameManager

GameManager游戏管理器,可以做很多的事情:

  • 可以用于获取玩家的参考
  • 用于重生玩家
  • 记录您收集的水果或硬币的分数
  • 重新启动游戏、保存游戏

我们需要在Hierachy中Create Empty命名为***GameManager***,加*号是为了区分普通的对象。同时创建GameManager的脚本挂载到该对象下。

正常情况下,在一个游戏中我们只需要一个GameManager。

6、创建类秋千的SpikedBall

(1)创建秋千的支点

将Terrain的一个图标拖入scene

设置Scale大小均为0.5,并且设置Sorting Layer为Ground

在Hierarchy中重命名为Base

在这个过程中,点击下方小按钮即可让xyz的数值保持一致,修改其中一个,其他两个同步修改。

添加组件Distance Joint 2D,则会自动添加Rigidbody 2D的组件。

设置Rigidbody的Body Type为Static,表示支点不可移动。

(2)创建Empty Parent并命名为SpikedBall

这个是秋千的根元素,而秋千实际上是由多个部件组成的。

(3)插入秋千元素SpikedBall

选择Assets -> Graphics -> Traps -> Spiked Ball,设置Pixels Per Unit为16

将Spiked Ball拖入Scene中,并使其成为SpikedBall的子节点

同时新增Rigidbody 2D组件 ,设置Collision Detection和interpolate的值如下。

(4)配置支点和ball之间的铰链

点击Base,然后将Spiked Ball拖入Base的Distance Joint 2D组件的Connected Rigid Body中,使得Base和Spiked Ball形成距离关节。

然后执行程序,在Scene界面拉一下Spiked Ball,便可使其左右摆动

(5)通过脚本初始化Spiked Ball的摆动

在此之前,程序执行时需要手工拉一下ball才能使其摆动,现在我们希望通过在脚本初始化时推一下ball使其可以自行进行摆动。

public class SpikedBall : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private float pushForce;

    void Start()
    {
        Vector2 pushVector = new Vector2(pushForce, 0);
        rb.AddForce(pushVector, ForceMode2D.Impulse);
    }
  
}

(6)增加秋千的链子

同样在Traps -> Spiked Ball下

设置Chain的Pixels Per Unit 为16,拖入Scene中,并且再复制3份,将这4个Chain同时拖到Hierarchy的SpikedBall父节点下方。

给这四个Chain同时添加Hinge Joint 2D组件。

然后依次设置Joint 2D中Connected Rigid Body的值:

Base设置Chain

Chain设置Chain(1)

Chain(1)设置Chain(2)

Chain(2)设置Chain(3)

Chain(3)设置Spiked Ball

此时再次执行程序,可以看到Spiked Ball带动Chain一起摆动了。

(7)Joint之间的区别

可以参考:Unity 2DJoint 物理关节功能与总结_target joint 2d-CSDN博客

Distance Joint距离关节:该关节使得两物体保持一定的距离。

Hinge Joint铰链关节:允许rb对象围绕空间中的点火另一个对象上的点旋转的关节,常用于绳子模拟、开门等铰链。

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

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

相关文章

朱利亚集合和曼德布洛特集合及其图像

朱利亚集合和曼德布洛特集合及其图像 朱利亚集合&#xff08;Julia Set&#xff09;和 曼德布洛特集合&#xff08;Mandelbrot Set&#xff09;除了数学理论上的意义&#xff0c;所生成的分形图像&#xff0c;因其独特的几何美感和无限的复杂性&#xff0c;还被广泛应用于计算机…

增强现实系列—深入探索ARKit:平面检测、三维模型放置与增强现实交互

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

技术研究:Redis 实现消息队列

综述 我们先看看消息队列的消息存取到底有哪些需求吧&#xff1a; 需求1&#xff1a;消息保序&#xff1a;由于消费者是异步处理消息&#xff0c;但是消费者需要按照生产者发送消息的顺序来处理消息&#xff0c;避免后发送的消息被先处理了。 需求2&#xff1a;重复消息处理&…

【区块链+金融服务】河北股权交易所综合金融服务平台 | FISCO BCOS应用案例

区域性股权市场是我国资本市场的重要组成部分&#xff0c;是多层次资本市场体系的基石。河北股权交易所&#xff08;简称&#xff1a;河交所&#xff09; 作为河北省唯一一家区域性股权市场运营机构&#xff0c;打造河北股权交易所综合金融服务平台&#xff0c;将区块链技术与区…

信号与系统MATLAB实验:连续信号的采样与恢复

一、实验内容 &#xff08;1&#xff09;修改示例中的门信号宽度、采样周期等参数&#xff0c;重新运行程序&#xff0c;观察得到的采样信号时域和频域特性&#xff0c;以及重构信号与误差信号的变化。 示例1&#xff1a;选取门信号f(t) g2(t)为被采样信号。利用MATLAB实现对…

搭建超好用的个人网盘

目录 前言filebrowser下载地址介绍优点缺点部署效果 cloudreve官网介绍优点缺点部署效果 总结 前言 目前我使用过的文件存储管理软件&#xff0c;包括fastdfs、minio、filebrowser、cloudreve&#xff0c;这4款软件&#xff0c;我自己亲自搭建且都使用过&#xff0c;fastdfs很…

Datawhale X 魔搭 AI夏令营第四期 AIGC方向 task02笔记

AI工具使用 1. baseline 代码2. 使用通义千问理解代码2.1 工作流程2.2 逐行释意 3. 使用通义千问生成 Prompt3.1 生成的 Prompt3.1 根据 Prompt 生成的图片 1. baseline 代码 !pip install simple-aesthetics-predictor!pip install -v -e data-juicer!pip uninstall pytorch-…

docker的基本管理和应用

1、概念 docker是一个开源的应用容器引擎&#xff0c;基于go语言开发的。 docker是运行在linux的容器化工具&#xff0c;可以理解为轻量级的虚拟机。 可以在任何主机上轻松创建的一个轻量级、可移植的、自给自足的容器 2、设计的理念 鲸鱼——宿主机 集装箱——独立运行的…

07:【stm32】中断一:NVIC的配置

中断 1、中断的简介1.1、什么是中断1.2、为什么需要中断 2、中断的优先级2.1、中断优先级的表示方法 3、NVIC3.1、什么的NVIC3.2、NVIC的内部结构3.3、中断向量表3.4、程序实现①开启中断源②配置NVIC③中断响应函数 1、中断的简介 1.1、什么是中断 正在进行的事务被突发事件打…

1688商品详情API返回值中的供应商信息

在使用1688&#xff08;阿里巴巴中国站&#xff09;的商品详情API时&#xff0c;API的返回值中通常会包含丰富的产品信息&#xff0c;包括供应商&#xff08;卖家&#xff09;的信息。不过&#xff0c;具体的返回值内容可能会根据API的版本、调用参数以及API的更新情况有所不同…

什么是国际网络组网?

国际网络组网是指通过互联网技术将全球各地的不同网络相互连接&#xff0c;以实现信息交换与资源共享的过程。在这一过程中&#xff0c;涉及到数据传输、协议转换、跨网络通信等多个技术领域&#xff0c;旨在实现全球信息的无缝互联互通。国际网络组网的主要目标是扩大网络覆盖…

半导体RFID识别系统134.2K低频读写器|读写头JY-V610之SECS协议通信说明

什么是SECS协议&#xff1f; SECS&#xff08;Semiconductor Equipment Communication Standard&#xff09;协议是半导体设备通讯标准&#xff0c;用来统一各个生产设备之间以及生产设备和控制设备之间的通讯&#xff0c;由SEMI&#xff08;Semiconductor Equipment and Mate…

手搓滑动窗口

前言&#xff1a;好久没写滑动窗口&#xff0c;导致一些边界问题处理不好&#xff0c;back和top的初始值都搞不好 #include<bits/stdc.h> using namespace std;const int N (int)1e65; int n,m; int a[N],b[N]; int back,top;int main(){cin >> n >> m;for(…

【前端设计方案】H5 图片懒加载 SDK

实现思路 定义<img srcloading.png data-srcxxx.png/>页面滚动&#xff0c;图片露出时&#xff0c;将 data-src 赋值给 src 注意事项&#xff1a;滚动要节流 技术要点 获取图片的位置 elem.getBoundingClientRect() 图片 top < window.innerHeight 时&#xff0c;图片…

【学习笔记】爱立信SPO 1400 CRAFT软件基础知识9——Bridge(网桥)显示参数

一、前期准备 条件1.确认已正确使用爱立信SPO 1400 CRAFT软件通过网络登录设备&#xff08;以下简称NE&#xff09; 具体登录教程参考&#xff1a;使用爱立信SPO 1400 CRAFT软件通过网络登录设备的详细过程 二、学习内容&#xff1a; 提示&#xff1a;学习爱立信SPO 1400 CRA…

阿里员工:33岁,房贷还剩223万,每月还1.5W,失业中

中年失业 中年失业&#xff0c;真的很难。 虽然人到中年&#xff0c;一般多少都会有些储蓄&#xff0c;但也意味着会有更多的支出。 最近&#xff0c;一位阿里员工&#xff08;这会可能是前阿里员工了&#xff09;在社区分享到自己的经历。 贴主 33 岁&#xff0c;作为已结婚有…

搬瓦工日本软银线路VPS测评

搬瓦工日本VPS支持softbank/软银&#xff0c;Japan: Osaka (Softbank) &#xff0c;网络在2.5Gbps-10Gbps之间&#xff0c;底层为KVM虚拟、纯SSD阵列、支持在多机房之间切换。搬瓦工软银来国内的网络情况怎么样&#xff1f;测评数据大致如下&#xff1a; CPU具体型号不知道&…

深入了解指针(6)

文章目录 1.函数指针数组2.转移表3.回调函数 1.函数指针数组 存放函数指针的数组 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int add(int x, int y) {return x y; } int sub(int x, int y) {return x - y; }int main() {int (*p1)(int x, int y) add;int (…

基于python的百度迁徙迁入、迁出数据分析(八)

副标题&#xff1a;从百度迁徙数据看——重大公共卫生事件的影响 先来回顾一下&#xff0c;迁徙规模指数定义&#xff1a;反映迁入或迁出人口规模&#xff0c;城市间可横向对比。 2019年—2022年的部分春运数据已经不可查&#xff0c;用的环哥的数据&#xff0c;可参考环哥的…

LVS原理及相关配置

1. 描述以及工作原理 1. 什么是 LVS linux virtural server 的简称&#xff0c;也就是 linxu 虚拟机服务器&#xff0c;这是一个 由章文嵩博士发起的开源项目&#xff0c;官网是 http://www.linuxvirtualserver.org,现在 lvs 已经是 linux 内核标 准的一部分&#xff0c;使用…