【Unity学习笔记】New Input System 部分源码和测试用例补充

news2025/1/16 6:46:03

转载请注明出处:🔗https://blog.csdn.net/weixin_44013533/article/details/135630016
作者:CSDN@|Ringleader|

主要参考:

  • Unity官方Input System手册与API
  • 【Unity学习笔记】Unity TestRunner使用
  • 【Unity学习笔记】第十二 · New Input System 及其系统结构 和 源码浅析

注:本文使用的unity版本是2021.3.25f,InputSystem版本为1.5.1

deafault Interaction测试用例

  1. ButtonAndValue

    [Test]
    public void Actions_ButtonActions_RespectButtonPressPoints()
    {
        InputSystem.settings.defaultButtonPressPoint = 0.5f;
        InputSystem.settings.buttonReleaseThreshold = 0.9f; // Release at 90% of press point puts release at 0.45.
    
        // Customize gamepad with specific button press point on right trigger (but not on left).
        InputSystem.RegisterLayoutOverride(@"
            {
                ""name"" : ""GamepadRightTriggerWithPressPoint"",
                ""extend"" : ""Gamepad"",
                ""controls"" : [
                    { ""name"" : ""rightTrigger"", ""parameters"" : ""pressPoint=0.75"" }
                ]
            }
        ");
    
        var gamepad = InputSystem.AddDevice<Gamepad>();
    
        var rightTriggerButton = new InputAction(type: InputActionType.Button, binding: "<Gamepad>/rightTrigger");
        var rightTriggerValue = new InputAction(type: InputActionType.Value, binding: "<Gamepad>/rightTrigger");
        var leftTriggerButton = new InputAction(type: InputActionType.Button, binding: "<Gamepad>/leftTrigger");
        var leftTriggerValue = new InputAction(type: InputActionType.Value, binding: "<Gamepad>/leftTrigger");
    
        rightTriggerButton.Enable();
        rightTriggerValue.Enable();
        leftTriggerButton.Enable();
        leftTriggerValue.Enable();
    
        using (var rightTriggerButtonTrace = new InputActionTrace(rightTriggerButton))
        using (var rightTriggerValueTrace = new InputActionTrace(rightTriggerValue))
        using (var leftTriggerButtonTrace = new InputActionTrace(leftTriggerButton))
        using (var leftTriggerValueTrace = new InputActionTrace(leftTriggerValue))
        {
            Set(gamepad.leftTrigger, 0.25f);
            Set(gamepad.rightTrigger, 0.25f);
    
            // Have crossed neither button threshold. Actions start but don't perform.
            Assert.That(leftTriggerButtonTrace, Started(leftTriggerButton, gamepad.leftTrigger, value: 0.25f));
            Assert.That(rightTriggerButtonTrace, Started(rightTriggerButton, gamepad.rightTrigger, value: 0.25f));
    
            Assert.That(leftTriggerValueTrace,
                Started(leftTriggerValue, gamepad.leftTrigger, value: 0.25f)
                    .AndThen(Performed(leftTriggerValue, gamepad.leftTrigger, value: 0.25f)));
            Assert.That(rightTriggerValueTrace,
                Started(rightTriggerValue, gamepad.rightTrigger, value: 0.25f)
                    .AndThen(Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.25f)));
    
            rightTriggerButtonTrace.Clear();
            rightTriggerValueTrace.Clear();
            leftTriggerButtonTrace.Clear();
            leftTriggerValueTrace.Clear();
    
            Set(gamepad.leftTrigger, 0.6f);
            Set(gamepad.rightTrigger, 0.6f);
    
            // Have grossed global but not right trigger threshold.
            Assert.That(leftTriggerButtonTrace, Performed(leftTriggerButton, gamepad.leftTrigger, value: 0.6f));
            Assert.That(rightTriggerButtonTrace, Is.Empty);
    
            Assert.That(leftTriggerValueTrace, Performed(leftTriggerValue, gamepad.leftTrigger, value: 0.6f));
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.6f));
    
            rightTriggerButtonTrace.Clear();
            rightTriggerValueTrace.Clear();
            leftTriggerButtonTrace.Clear();
            leftTriggerValueTrace.Clear();
    
            Set(gamepad.leftTrigger, 0.9f);
            Set(gamepad.rightTrigger, 0.9f);
    
            // No change on left trigger action, right trigger crossed threshold.
            Assert.That(leftTriggerButtonTrace, Is.Empty);
            Assert.That(rightTriggerButtonTrace, Performed(rightTriggerButton, gamepad.rightTrigger, value: 0.9f));
    
            Assert.That(leftTriggerValueTrace, Performed(leftTriggerValue, gamepad.leftTrigger, value: 0.9f));
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.9f));
    
            rightTriggerButtonTrace.Clear();
            rightTriggerValueTrace.Clear();
            leftTriggerButtonTrace.Clear();
            leftTriggerValueTrace.Clear();
    
            Set(gamepad.leftTrigger, 0.6f);
            Set(gamepad.rightTrigger, 0.6f);
    
            // No change on left trigger action, right trigger action cancels.
            Assert.That(leftTriggerButtonTrace, Is.Empty);
            Assert.That(rightTriggerButtonTrace, Started(rightTriggerButton, gamepad.rightTrigger, value: 0.6f));
    
            Assert.That(leftTriggerValueTrace, Performed(leftTriggerValue, gamepad.leftTrigger, value: 0.6f));
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.6f));
    
            rightTriggerButtonTrace.Clear();
            rightTriggerValueTrace.Clear();
            leftTriggerButtonTrace.Clear();
            leftTriggerValueTrace.Clear();
    
            Set(gamepad.leftTrigger, 0.4f);
            Set(gamepad.rightTrigger, 0.4f);
    
            // Left trigger cancels, right trigger *starts* again (but doesn't perform).
            Assert.That(leftTriggerButtonTrace, Started(leftTriggerButton, gamepad.leftTrigger, value: 0.4f));
            Assert.That(rightTriggerButtonTrace, Is.Empty);
    
            Assert.That(leftTriggerValueTrace, Performed(leftTriggerValue, gamepad.leftTrigger, value: 0.4f));
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.4f));
    
            rightTriggerButtonTrace.Clear();
            rightTriggerValueTrace.Clear();
            leftTriggerButtonTrace.Clear();
            leftTriggerValueTrace.Clear();
    
            Set(gamepad.leftTrigger, 0f);
            Set(gamepad.rightTrigger, 0f);
    
            // No change on left and right trigger actions.
            Assert.That(leftTriggerButtonTrace, Canceled(leftTriggerButton, gamepad.leftTrigger, 0f));
            Assert.That(rightTriggerButtonTrace, Canceled(rightTriggerButton, gamepad.rightTrigger, 0f));
    
            Assert.That(leftTriggerValueTrace, Canceled(leftTriggerValue, gamepad.leftTrigger, value: 0f));
            Assert.That(rightTriggerValueTrace, Canceled(rightTriggerValue, gamepad.rightTrigger, value: 0f));
    
            rightTriggerButtonTrace.Clear();
            leftTriggerButtonTrace.Clear();
    
            // Make sure that we start and cancel if we press the buttons just a little and then release.
            Set(gamepad.leftTrigger, 0.25f);
            Set(gamepad.rightTrigger, 0.25f);
    
            Assert.That(leftTriggerButtonTrace, Started(leftTriggerButton, gamepad.leftTrigger, value: 0.25f));
            Assert.That(rightTriggerButtonTrace, Started(rightTriggerButton, gamepad.rightTrigger, value: 0.25f));
    
            rightTriggerButtonTrace.Clear();
            leftTriggerButtonTrace.Clear();
    
            Set(gamepad.leftTrigger, 0f);
            Set(gamepad.rightTrigger, 0f);
    
            Assert.That(leftTriggerButtonTrace, Canceled(leftTriggerButton, gamepad.leftTrigger, value: 0f));
            Assert.That(rightTriggerButtonTrace, Canceled(rightTriggerButton, gamepad.rightTrigger, value: 0f));
        }
    }
    
  2. passthrough

    [Test]
    public void Actions_passthroughActions()
    {
    
        var gamepad = InputSystem.AddDevice<Gamepad>();
    
        var rightTriggerValue = new InputAction(type: InputActionType.PassThrough, binding: "<Gamepad>/rightTrigger");
    
        rightTriggerValue.Enable();
    
        using (var rightTriggerValueTrace = new InputActionTrace(rightTriggerValue))
        {
            Set(gamepad.rightTrigger, 0.25f);
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.25f));
            rightTriggerValueTrace.Clear();
    
            Set(gamepad.rightTrigger, 0.6f);
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.6f));
            rightTriggerValueTrace.Clear();
            
            Set(gamepad.rightTrigger, 0.9f);
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0.9f));
            rightTriggerValueTrace.Clear();
            
            Set(gamepad.rightTrigger, 0f);
            Assert.That(rightTriggerValueTrace, Performed(rightTriggerValue, gamepad.rightTrigger, value: 0f));
            rightTriggerValueTrace.Clear();
        }
    }
    

Press Interaction测试用例

using System.Diagnostics.CodeAnalysis;
using NUnit.Framework;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Utilities;

namespace Scenes.TestAction
{
    [SuppressMessage("ReSharper", "AccessToStaticMemberViaDerivedType")]
    public class TestAction : InputTestFixture
    {

        [Test]
        [TestCase(InputActionType.Value)]
        [TestCase(InputActionType.Button)]
        [TestCase(InputActionType.PassThrough)]
        public void Actions_PressOnly(InputActionType type)
        {
            var gamepad = InputSystem.AddDevice<Gamepad>();

            InputSystem.settings.defaultButtonPressPoint = 0.5f;
            InputSystem.settings.buttonReleaseThreshold = 0.75f; // Puts release point at 0.375.

            var action = new InputAction(type:type, binding: "<Gamepad>/leftTrigger", interactions: "press(behavior=0)");
            action.Enable();

            using (var trace = new InputActionTrace(action))
            {
                Set(gamepad.leftTrigger, 0.35f);

                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.35f));

                trace.Clear();

                Set(gamepad.leftTrigger, 0.5f);

                Assert.That(trace, Performed(action, control: gamepad.leftTrigger, value: 0.5f));

                trace.Clear();

                Set(gamepad.leftTrigger, 0.6f);

                Assert.That(trace, Is.Empty);

                Set(gamepad.leftTrigger, 0.4f);

                Assert.That(trace, Is.Empty);

                Set(gamepad.leftTrigger, 0.3f);

                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.3f));
                
                trace.Clear();
                Set(gamepad.leftTrigger, 0);
                Assert.That(trace, Canceled(action, control: gamepad.leftTrigger, value: 0));
            }
        }
        
        [Test]
        [TestCase(InputActionType.Value)]
        [TestCase(InputActionType.Button)]
        [TestCase(InputActionType.PassThrough)]
        public void Actions_RealeasOnly(InputActionType type)
        {
            var gamepad = InputSystem.AddDevice<Gamepad>();

            InputSystem.settings.defaultButtonPressPoint = 0.5f;
            InputSystem.settings.buttonReleaseThreshold = 0.75f; // Puts release point at 0.375.

            var action = new InputAction(type:type, binding: "<Gamepad>/leftTrigger", interactions: "press(behavior=1)");
            action.Enable();

            using (var trace = new InputActionTrace(action))
            {
                Set(gamepad.leftTrigger, 0.35f);
                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.35f));
                trace.Clear();

                Set(gamepad.leftTrigger, 0.5f);
                Assert.That(trace, Is.Empty);
                trace.Clear();

                Set(gamepad.leftTrigger, 0.6f);
                Assert.That(trace, Is.Empty);
                trace.Clear();

                Set(gamepad.leftTrigger, 0.4f);
                Assert.That(trace, Is.Empty);
                trace.Clear();

                Set(gamepad.leftTrigger, 0.3f);
                Assert.That(trace,
                    Performed(action, control: gamepad.leftTrigger, value: 0.3f)
                        .AndThen(Canceled(action, control: gamepad.leftTrigger, value: 0)));
                trace.Clear();

                Set(gamepad.leftTrigger, 0.1f);
                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.1f));
                trace.Clear();

                Set(gamepad.leftTrigger, 0);
                Assert.That(trace, Canceled(action, control: gamepad.leftTrigger, value: 0));
                trace.Clear();
                
                Set(gamepad.leftTrigger, 0.4f);
                Set(gamepad.leftTrigger, 0.3f);//onlyRelease 不超过press-threshold只回落release是不会触发的
                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.4f));
                trace.Clear();
                
                
            }
        }
        
        [Test]
        [TestCase(InputActionType.Value)]
        [TestCase(InputActionType.Button)]
        [TestCase(InputActionType.PassThrough)]
        public void Actions_PressAndRelease(InputActionType type)
        {
            var gamepad = InputSystem.AddDevice<Gamepad>();

            InputSystem.settings.defaultButtonPressPoint = 0.5f;
            InputSystem.settings.buttonReleaseThreshold = 0.75f; // Puts release point at 0.375.

            var action = new InputAction(type:type, binding: "<Gamepad>/leftTrigger", interactions: "press(behavior=2)");
            action.Enable();

            using (var trace = new InputActionTrace(action))
            {
                Set(gamepad.leftTrigger, 0.35f);
                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.35f));
                trace.Clear();

                Set(gamepad.leftTrigger, 0.5f);
                Assert.That(trace, Performed(action, control: gamepad.leftTrigger, value: 0.5f));
                trace.Clear();

                Set(gamepad.leftTrigger, 0.6f);
                Assert.That(trace, Is.Empty);
                trace.Clear();
                
                Set(gamepad.leftTrigger, 0.4f);
                Assert.That(trace, Is.Empty);
                trace.Clear();

                Set(gamepad.leftTrigger, 0.3f);
                Assert.That(trace,
                    Performed(action, control: gamepad.leftTrigger, value: 0.3f));
                trace.Clear();
                
                Set(gamepad.leftTrigger, 0);
                Assert.That(trace, Is.Empty);
                trace.Clear();
                
                Set(gamepad.leftTrigger, 0.9f);
                Assert.That(trace, 
                    Started(action, control: gamepad.leftTrigger, value: 0.9f)
                        .AndThen(Performed(action, control: gamepad.leftTrigger, value: 0.9f)));
                trace.Clear();
                
                Set(gamepad.leftTrigger, 0);
                Assert.That(trace, 
                    Performed(action, control: gamepad.leftTrigger, value: 0f)
                        .AndThen(Canceled(action, control: gamepad.leftTrigger, value: 0f)));
                trace.Clear();
                
                Set(gamepad.leftTrigger, 0.4f);
                Set(gamepad.leftTrigger, 0.3f);//press&release 不超过press-threshold只回落release是不会触发的
                Assert.That(trace, Started(action, control: gamepad.leftTrigger, value: 0.4f));
                trace.Clear();
            }
        }
        
    }

}

UnityEvent方式注册回调关键逻辑

  1. PlayerInput.InitializeActions()
    UnityEvent方式注册回调的核心在于PlayerInput组件enable时进行InitializeActions初始化操作
    在这里插入图片描述
  2. PlayerInputEditor
    但对于上面ActionEvent的eventId怎么匹配上actionId,则要看PlayerInputEditor.OnInspectorGUI()OnActionAssetChange()
    在这里插入图片描述
  3. UnityEvent<T0>
    上面action触发后,对应actionEvent调用Invoke,但此时真正的回调方法并没有注册,是在下面的InvokableCall中注册的,最后在InvokableCall.Invoke中执行最终的方法回调,处理输入处理。
    在这里插入图片描述
  4. InvokableCall<T1> 注册回调
    在这里插入图片描述
    从上面看,UnityAction方式远比原生C#事件复杂得多。(而且我也并没完全弄懂)

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

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

相关文章

k8s资源介绍

Kubernetes架构图 Kubernetes系统用于管理分布式节点集群中的微服务或容器化应用程序&#xff0c;并且其提供了零停机时间部署、自动回滚、缩放和容器的自愈&#xff08;其中包括自动配置、自动重启、自动复制的高弹性基础设施&#xff0c;以及容器的自动缩放等&#xff09;等…

java黑马学习笔记

数组 变量存在栈中&#xff0c;变量值存放在堆中。 数组反转 public class test{public static void main(String[] args){//目标&#xff1a;完成数组反转int[] arr {10,20,30,40,50};for (int i 0,j arr.length - 1;i < j;i,j--){int tep arr[j]; //后一个值赋给临时…

数学建模常见算法的通俗理解(2)

目录 6 K-Means&#xff08;K-均值&#xff09;聚类算法&#xff08;无需分割数据即可分类&#xff09; 6.1 粗浅理解 6.2 算法过程 6.2.1 选定质心 6.2.2 分配点 6.2.3 评价 7 KNN算法&#xff08;K近邻算法&#xff09;&#xff08;K个最近的决定方案&#xff09; 7.…

怎么从视频中提取动图?一个方法快速提取gif

视频以连续的方式播放一系列图像帧&#xff0c;通过每秒播放的帧数&#xff08;帧率&#xff09;来创做&#xff0c;由于GIF动图则以循环播放一系列静态图像帧的方式展现动画效果。由于视频的优势在于流畅的动画、丰富的细节和长时间播放&#xff0c;因此常用于电影、电视节目、…

DAG最小路径点覆盖,最小路径可重复覆盖,详解

文章目录 前言有向无环图的最小路径点覆盖概念拆点二分图定理**证明** 最小路径可重复覆盖解决策略代码实现 OJ练习 前言 关于二分图&#xff1a;二分图及染色法判定 关于二分图最大匹配&#xff1a;二分图最大匹配——匈牙利算法详解 关于二分图带权最大完备匹配&#xff1…

Docker使用及部署python项目

一、准备项目 ​ 我写的是一个爬取某ppt网站的代码&#xff0c;就一个ppt1.py是爬虫&#xff0c;然后&#xff0c;ppts是存放下载的ppt的 二、准备requirement.txt文件 这个是需要哪些python库支持&#xff0c;写好 ​ 三、准备Dockerfile文件 需要一个名为Dockerfile的文件&…

基于SpringBoot的船运物流管理系统

文章目录 项目介绍主要功能部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &#x1f345;文末获取…

计算机组成原理04:一位乘法

原码的一位乘法是基于加法设计的。回想我们在竖式计算乘法时&#xff0c;都是通过一个数与另外一个数的另外一位相乘&#xff0c;最后相加得到结果。计算机计算原码一位乘法也是一样的原理。这里就涉及到计算时一个非常重要的操作&#xff1a;数据移位。 原码乘法问题分析 需…

13.浮动面板(PaletteSet)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET FrameWork4.5、ObjectArx 2016 64bit、Entity Framework 6. 在CAD中进行通用组件开发或常驻界面的控件开发时&#xff0c;可使用PaletteSet作为停靠面板&#xff0c;然后将自己的空间放入其中。 1.示例 SearchRe…

React初探:从环境搭建到Hooks应用全解析

React初探&#xff1a;从环境搭建到Hooks应用全解析 一、React介绍 1、React是什么 React是由Facebook开发的一款用于构建用户界面的JavaScript库。它主要用于构建单页面应用中的UI组件&#xff0c;通过组件化的方式让开发者能够更轻松地构建可维护且高效的用户界面。 Reac…

初始RabbitMQ(入门篇)

消息队列(MQ) 本质上就是一个队列,一个先进先出的队列,队列中存放的内容是message(消息),是一种跨进程的通信机制,用于上下游传递消息, 为什么使用MQ: 削峰填谷: MQ可以很好的做一个缓冲机制,例如在一个系统中有A和B两个应用,A是接收用户的请求的,然后A调用B进行处理. 这时…

前端基础面试题大全

一、Vue 文章目录 一、Vue1、vue 修改数据页面不重新渲染**数组/对象的响应式 &#xff0c;vue 里面是怎么处理的&#xff1f;** 2、生命周期Vue 生命周期都有哪些&#xff1f;父子组件生命周期执行顺序 3、watch 和 computed 的区别4、组件通信&#xff08;组件间传值&#xf…

gin中间件篇

1. 全局中间件 所有请求都经过此中间件 package mainimport ("fmt""time""github.com/gin-gonic/gin" )// 定义中间 func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t : time.Now()fmt.Println("中间件开始执行了&quo…

力扣每日一题---1547. 切棍子的最小成本

//当我们将棍子分段之后&#xff0c;我们是不是想到了怎么组合这些棍子 //并且这些棍子有一个性质就是只能与相邻的进行组合 //暴力搜索的话复杂度很高 //在思考暴力搜索的时候&#xff0c;我们发现一个规律 //比如棍子长度1 2 1 1 2 //那么与最后一个2组合的棍子有&#xff0c…

Vue3+ElementUIPlus颜色选择器,Ruoyi框架动态替换图片

需求为&#xff0c;需要动态的替换头部和底部图片的颜色&#xff0c;通过固定的颜色 要实现可以动态的通过颜色&#xff0c;去替换的效果。 一、通过将选择的颜色&#xff0c;通过Vuex来进行一个存储&#xff0c;用户后续的使用 <el-form-item label"顶部底部背景&quo…

LabVIEW滚动轴承故障在线监测

展示了如何将LabVIEW开发出一种有效的滚动轴承故障在线监测系统。介绍了该系统的开发过程、工作原理及其在实际应用中的效果。该系统成功地应用于对滚动轴承故障的早期诊断&#xff0c;提高了故障检测的准确性和效率。 滚动轴承在工作过程中会产生复杂的振动信号&#xff0c;包…

19. JDK8以后的时间类(Date类、日期格式化类、日历类、工具类)

JDK8以后的时间类 Date类1. ZoneID类1.1 方法1.2 代码示例 2. Instant类2.1 方法2.2 代码示例 3. ZoneDateTime类3.1 方法3.2 代码示例 日期格式化类1. DateTimeFormatter类1.1 方法1.2 代码示例 日历类1. LocalDate类1.1 方法1.2 代码示例 2. LocalTime类2.1 方法2.2 代码示例…

Java设计模式详解-更新中

收藏和关注的同时&#xff0c;请也关注 公众号 “IT技术馆” 各位大家好&#xff0c;从今天开始&#xff0c;作者开始整理 《JAVA软件设计模式&#xff08;GOF&#xff09;》 专栏。请各位多多关注&#xff01; 该专栏是根据作者的技术经验和设计模式的了解&#xff0c;进行详…

ospf综合实验配置

实验规则如上&#xff1a; 划分ip地址&#xff1a;七个骨干&#xff0c;五个环回 首先划分两个ip&#xff0c;一个给骨干&#xff0c;一个给环回 192.168.1.0/24 -- 1.划分七个骨干网络- 2.划分5个环回网络- 192.168.1.0/25--骨干-----192.168.1.0/28 192.168.1.0 000 0…

numpy中数组的操作

目录 一&#xff1a;数组的属性 二&#xff1a;翻转数组 三&#xff1a;数组的计算 一&#xff1a;数组的属性 NumPy 数组&#xff08;通常称为 ndarray&#xff09;有许多有用的属性&#xff0c;这些属性可以帮助你了解数组的各个方面。以下是一些主要的属性&#xff1a; …