1.14 从0开始学习Unity游戏开发--物理引擎

news2024/11/8 20:59:19

上一篇文章我们讲了如何动态创建物体,以及如何用Prefab机制复用我们制作好的物体和逻辑,那么本篇我们将会讲解如何利用这些功能,结合Unity自带的物理引擎,实现一个简单的FPS游戏的效果。

物理组件

首先我们需要先了解如何使用Unity自带的物理引擎,在前面的系列文章里面我们其实心理都有了方法论,Unity的绝大部分功能都是靠物体上挂的组件来实现的,那么物理引擎相关的功能也不例外。

我们之前一直在操作Cube,那么剩下还有一个Box Collider组件没有讲,其实这个就是物理引擎相关的组件之一:

Collider顾名思义,就是碰撞体的意思,很显然,如果一个物体需要参与到物理计算中,那么这个物体一般需要具有一定的形状大小,这样才能提供合理的物理效果。

那么Box Collider其实也很显然,这是一个Box形状的碰撞体,我们如何去掉勾选Mesh Renderer组件,那么可以在Scene场景中看到绿色的线框,这其实就是编辑器帮我们可视化了这个Collider的形状:

除了Box的形状,我们还可以使用球形(Sphere Collider),胶囊体(Capsule Collider)等等,可以直接在Add Component里面搜索Collider就能比较快的找到所有可以用的内置Collider组件,注意带2D后缀的是专门给2D游戏用的,我们现在做的是3D游戏,不应该使用2D组件。

我们现在还是用Box来做示范,那么这里这个Box的大小就是我们这个Cube所参与物理计算的大小,这里你可能就问了,既然这个Collider组件本身有大小设置的参数,那我们是不是可以让参与物理计算的大小和实际渲染看到的大小不一致呢?

当然是可以的,我们如果稍微调整一下Collider的Size,XYZ都+1,重新勾选Mesh Renderer组件,那么可以看到绿色的线框是会出现在渲染效果(橙黄色线框)外部,像是包裹了的样子:

这也就说明了,物理计算和渲染是完全分离的,并不是你看到这个物体渲染成什么样子,物理计算就是按照这个视觉效果来,而是由独立的一个大小轮廓计算的。

当然如果你希望物理计算跟你的渲染效果强行绑定,那么Unity也提供了一个Mesh Collider,顾名思义,这个Collider是跟渲染用的Mesh绑定,会直接贴合Mesh的形状创建Collider参与物理计算:

我们将Box Collider删掉(点击右边三个点,选择Remove Component),添加Mesh Collider,Unity默认给我们设置了当前Mesh Filter组件里面的Mesh到Mesh Collider组件中,如图红框。当然你也可以设置其他Mesh作为Collider的形状,同样我们取消Mesh Renderer组件的勾选,那么可以看到绿色线框显示的虽然轮廓上跟Box Collider类似,但是明显是由三角形组成的,也就是说这个Collider是由跟Mesh一样的三角形组合而成。

当然Mesh Collider也不会给调整大小的选项,如果要调整大小,需要调整GameObject本身的Transform大小(Scale),也就是说这个Collider就和渲染效果直接绑定的。

那么你可能会问了,既然Mesh Collider这么好,为什么默认给的是Box Collider?

很显然,Collider的形状越复杂,计算的性能消耗也就越大,如果我们能用简单的形状近似我们目前渲染的形状,那么就可以减轻物理引擎计算的性能消耗,并且物理效果又不会有太多不真实。这其实就是游戏开发中常常会做的性能和效果之间的权衡,也就是大差不差就好,游戏能流畅跑才能玩的了,计算的再准确,但是帧率很低没法玩也是白搭。

说了这么半天,我们还没讲解这个Collider到底如何使用,其实很简单,只要包含了Collider组件的物体,这个物体上挂的任何组件,都会在Collider发生碰撞的时候触发特定的函数:

也即是红框里面的这个函数:

看函数名就比较清楚,三个为一组,比如OnCollisionXXX表示Enter,Stay,Exit这三个状态,这三个状态的触发时机也比较简单:

  1. 当Collider第一次和另外一个Collider发生碰撞时,触发OnCollisionEnter
  2. 两个Collider发生碰撞的本质就是形状相交了,如果是刚体,那么碰撞后一般都会弹开,但是弹开的速度可能没那么快,也可能因为设置或者代码逻辑导致继续相交,那么在触发OnCollisionEnter之后每个物理帧都会检查一次是不是还在相交,如果是的话就会触发OnCollisionStay
  3. 当不再相交之后,会触发一次OnCollisionExit,如果再触发相交,那么继续从1开始跑流程

这个状态流转应该还是很好懂的,OnTriggerXXX后面讲。

那么我们现在试验一下这个功能,因为发生碰撞肯定是两个物体,那么我们需要有两个Cube。

Unity里面快捷键Ctrl+D可以对当前选中的物体快速复制一个出来,我们对场景里面已有的这个Cube进行Ctrl+D,得到一个Cube(1),它的位置形状大小组件都和被复制的Cube完全一模一样。

选中这个Cube(1)我们稍微操作一下Scene视图里面,按住蓝箭头之一拖动一下,移动一下这个Cube的位置,但是不要挪太远,保持和Cube是相交的状态:

这样我们保证了这两个Collider是处于碰撞状态。

好我们现在开始写代码,新建一个组件用于处理碰撞的回调:

using UnityEngine;

public class CollisionHandler : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        Debug.Log("OnCollisionEnter from " + collision.gameObject.name);
    }
}

然后给我们任意一个Cube放上这个组件,这里我放到Cube(1)里面:

然后我们跑一下游戏看看,嗯?怎么没有看到相关的日志输出到Console窗口里面呢?

我们再看一下官方文档的说明:

Notes: Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached. Collision events will be sent to disabled MonoBehaviours, to allow enabling Behaviours in response to collisions.

这里有个Notes,告诉我们Collision事件只有当碰撞的两个Collider中间至少有一个有rigidbody才会发送。

那么rigidbody是什么呢?这里就引申出来第二个物理组件:RigidBody

直接Add Component里面搜RigidBody,就能找到同名组件,给我们的任意一个Cube加上去:

可以看到这个组件里面可以配置的参数有:

  • 质量(Mass)
  • 是否使用重力(Use Gravity)

等等参数,这样看起来这个组件是赋予Collider物理学的材料特性,毕竟名字也叫刚体,那是不是表示这是一个不会发生形变的材料?

但是实际情况并不是,官方的文档中也只是表明RigidBody组件只是决定这个物体是不是受物理引擎影响,而并不代表这个物体的物理材料性质,RigidBody组件里面能配置的也都是关于物体受物理引擎影响后,移动和旋转的效果。真正配置物理材料的是在Collider里面有一个Physic Material,当然我们这里不会展开讲这个。

总而言之,可以简单理解为RigidBody只是一个用于控制物体是否参与物理计算的组件。

那么我们现在加上了这个组件后,再次运行游戏看看:

解释一下:

  1. Cube(1)带有RigidBody组件,并且勾选了Use Gravity也就是会受重力影响,那么这个物体在开始运行的时候就会根据内置的重力加速度改变自己的位置
  2. Cube(1)和Cube发生碰撞,同时其中之一有RigidBody组件,所以Collision消息会发送给参与碰撞的双方,而我们Cube(1)里面有脚本接收OnCollisionEnter并打印日志,可以看到Console窗口里面有日志输出,碰撞的对方则是另外一个Cube,如果我们给Cube也加上CollisionHandler组件,那么输出的日志里面带上的名字应该就是Cube(1)
  3. 由于Cube(1)带有RigidBody组件,所以它也会参与真实的物理模拟,可以看到除了受重力掉下,还会因为和另外一个Cube发生碰撞后弹开(可以看到游戏开始的一瞬间向右瞬移了)

那么现在我们能理解了,如果要一个物体参与物理计算,那么需要这个物体两个条件

  1. 拥有Collider组件,这个决定了参与物理计算的时候的形状大小
  2. 给需要受到物理引擎计算改变自身位置的物体加上RigidBody组件,不需要改变自身位置的不要加这个组件,只需要Collider组件即可

物体受物理效果发生位置改变的功能则自动的由Unity内置的物理引擎完成,不需要自己处理。

如果我们要处理碰撞相关的逻辑,那么编写组件,接收OnCollisionXXX回调即可,当然要注意,只有发生碰撞的两个物体之间有至少一个拥有RigidBody组件才会触发这个回调。

FPS游戏试做

当我们初步了解物理引擎如何使用后,我们可以考虑制作FPS游戏了,但其实就是做一个发射子弹然后打到墙壁的功能。

为了制作这个功能,我们需要构思一下要做哪些事情:

  1. 首先是需要做一个受物理引擎影响的子弹,当然不是说要模拟加速度,空气阻力这种,主要是为了做出打到墙壁会停下来的效果
  2. 再做一个墙壁,带上Collider但是自己不会移动
  3. 最后做个发射子弹的逻辑

那么第一步,我们就拿一个Cube当作子弹,但是大小有点大,我们改小点,比如Scale设置成(0.2,0.2,0.2),就我们前面直接配好的Cube(1)就可以直接改名为Bullet,删掉一些无关组件,大概是这样了:

然后我们再做一个墙壁,墙壁其实就是Cube改了Scale变成长条而已,我们直接改Cube,因为Cube已经有BoxCollider了,直接够用了。

然后挪到我们习惯的右手边,当然用哪边都无所谓,拖着蓝色箭头挪一下,然后调整一下Scene场景的视角看向这个Wall:

选中的是我们的Bullet

好,剩下一步就是我们需要将子弹发射出去,很明显我们可以直接写个脚本挂到Bullet上:

using UnityEngine;

public class AddVelocity : MonoBehaviour
{
    public Vector3 initialVelocity; // 初始速度

    void Start()
    {
        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb != null)
        {
            rb.velocity = initialVelocity;
        }
    }
}

这个脚本也比较简单理解,就是Start的时候给RigidBody组件一个速度值,这里是使用物理引擎第一个需要理解的概念,我们希望物体受物理引擎模拟的话,就不应该直接修改物体的位置信息,而是给与一些物理上的信息,比如提供一个速度,或者提供一个力之类的概念。

我们将这个组件挂到Bullet上去,然后配上速度,注意这里速度其实是一个向量,也就是说速度是有方向的,我们希望子弹顺着蓝色箭头飞出去,蓝色是Z轴(可以看Scene场景右上角那个图标快速找到颜色对应的轴是哪个),很显然我们需要配的是(0,0,速度),我们配一个(0,0,1),跑起来看看:

看到我们的子弹还没碰到墙就掉下去了,很明显是因为速度太慢,重力加速度如此之大导致还没飞到位置就掉下去了。那我们直接修改速度,加快8倍:

看起来好多了,中间卡了一下是因为启动游戏会卡一点,然后错过了最开始物体运动的一小段。

这样我们已经做好了一个“子弹”射击墙体反弹掉落的效果。

改进FPS游戏的体验

但是很显然不太爽,FPS游戏应该支持连续射击,枪林弹雨才是这类游戏的爽快点之一。

那么我们要实现这个效果,需要做哪些事情呢?

  1. 需要接收用户输入,比如鼠标左键,按下就定时发射一个子弹
  2. 既然场景内同时存在的子弹肯定不止一个的话,我们需要动态创建子弹
  3. 如果可以的话,希望像真正的FPS游戏一样通过鼠标控制准心进行射击

首先是第一个需求,我们需要处理用户输入,还是像之前那样,新建一个组件脚本,用来处理鼠标按下的事件,每次按下都触发一次开火逻辑,并且如果持续按下的话每隔0.1s触发一次开火逻辑。

using UnityEngine;

public class FireController : MonoBehaviour
{
    private bool isMouseDown = false;
    private float lastFireTime = 0f;
    public float fireInterval = 0.1f;

    void Update()
    {
        if (Input.GetButton("Fire1"))
        {
            if (!isMouseDown)
            {
                isMouseDown = true;
                lastFireTime = Time.time;
                Fire();
            }
            else if (Time.time - lastFireTime > fireInterval)
            {
                lastFireTime = Time.time;
                Fire();
            }
        }
        else
        {
            isMouseDown = false;
        }
    }

    void Fire()
    {
        // 在这里实现每次触发的逻辑
        Debug.Log("Fire!");
    }
}

考虑到这个触发逻辑不属于Bullet也不属于Wall,那我们需要分配一个新的GameObject来承载这个逻辑,我们就新建一个Empty的GameObject叫做FireController,直接挂上这个组件:

然后我们跑起来看看,是不是左键按下和按住的时候都会打印Fire日志了?

然后是第二个需求,前面我们也学到了如何动态的创建一个GameObject,那么现在我们需要动态的创建一个已经制作好的GameObject,其实就是要利用好Prefab功能了。我们先把场景里面已经创建好的Bullet拖到Project窗口中形成一个Prefab,然后删掉场景里面原有的这个Bullet,这个Bullet由于是从场景里面拖入的,带有一定的位置信息,我们给它全部置0:

这样我们的Project窗口里面已经有了一个等待被动态创建的Bullet

还是找到第一步里面我们创建的FireController,我们需要在Fire函数里面动态创建物体,那肯定我们首先需要获取到这个Prefab,很明显我们需要修改一下代码,支持赋值一下这个Prefab:

using UnityEngine;

public class FireController : MonoBehaviour
{
    private bool isMouseDown = false;
    private float lastFireTime = 0f;
    public float fireInterval = 0.1f;
    public GameObject bullet;

    void Update()
    {
        if (Input.GetButton("Fire1"))
        {
            if (!isMouseDown)
            {
                isMouseDown = true;
                lastFireTime = Time.time;
                Fire();
            }
            else if (Time.time - lastFireTime > fireInterval)
            {
                lastFireTime = Time.time;
                Fire();
            }
        }
        else
        {
            isMouseDown = false;
        }
    }

    void Fire()
    {
        // 在这里实现每次触发的逻辑

        // 创建新的子弹,每次都是从模板bullet复制一个出来
        GameObject newBullet = Object.Instantiate(bullet);
    }
}

我们新增了一个public的GameObject成员叫bullet,用来给到编辑器里面赋值我们需要的Bullet到底是哪个Prefab,然后在每次Fire的时候通过Object.Instantiate来创建一个Prefab的实例,其实就类似复制。

然后我们可以看到FireController面板上就会有一个地方给我们赋值GameObject:

这样很顺理成章的,我们将Bullet的Prefab拖上去赋值。赋值完毕,我们跑起来看看:

1.14 从0开始学习Unity游戏开发--物理引擎

看起来还不错

解释一下:

  1. 鼠标点击后,FireController在Update里面处理了鼠标左键按下和长按的逻辑,触发Fire函数
  2. Fire函数通过成员变量bullet拿到了我们赋值的Prefab,通过Object.Instantiate创建新的实例,每个实例都相当于这个Prefab的复制品,但是彼此的数据逻辑都独立,可以看到左边Hierarchy窗口里面每次创建都会新增一个物体,创建的实例的位置默认是原点,当然可以自己指定
  3. 每个实例都自己有一个AddVelocity脚本,Start函数相当于每个实例创建后第一次要执行Update函数之前会跑一次,Start函数里面给了这个GameObject的RigidBody一个Z轴上的速度。
  4. 每个实例会根据物理引擎的计算,飞向墙壁
  5. 碰到墙壁,反弹,并且会因为重力而掉下去

那其实我们已经初步实现了连射功能,但是作为一个程序员,肯定第一时间反应到,每次射击都会创建一个子弹物体,但是没看到哪里销毁啊,这样一直射击岂不是迟早内存爆炸?

没错,我们这里缺少销毁GameObject的逻辑,所以需要补一下,这里可以简单做一下,子弹创建出来后5秒消失:

using UnityEngine;

public class AddVelocity : MonoBehaviour
{
    public Vector3 initialVelocity; // 初始速度
    public float lifeTime = 5.0f;
    private float lifeStartTime;

    void Start()
    {
        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb != null)
        {
            rb.velocity = initialVelocity;
        }

        lifeStartTime = Time.time;
    }

    void Update()
    {
        if (Time.time - lifeStartTime > lifeTime)
        {
            Destroy(gameObject);
        }
    }
}

图省事我们直接写在AddVelocity脚本里面,但是需要注意的是,Destroy传入的必须是gameObject,不能是this,因为this是代表这个AddVelocity类,也就是销毁的是AddVelocity组件,而不是整个GameObject。

下一章

OK,第二个需求也顺利完成了,那么第三个需求就是我们需要用鼠标控制视角,然后有个准心能让我们瞄准,但是在屏幕上画一个准心,这个准心其实是属于UI的部分,也就是单纯的在屏幕上显示的内容,有别于3D物体,开发UI会有所不同,但是每个游戏基本都需要做UI,所以这也是游戏开发里面很重要的部分。

所以下一章我们会讲解如何在Unity游戏中使用内置的功能制作UI界面,并且结合本章的内容制作一个准心出来。

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

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

相关文章

Linux基础(超级无敌认真好用,万字收藏篇!!!!)

文章目录Linux基础1 Linux概述1.1 Linux特点1.2 Linux和Window区别2 Linux安装2.1 什么是虚拟化2.2 安装虚拟机2.3 配置网络环境3 Linux文件目录4 Linux常用命令4.1 文件与目录操作4.2 查看文件内容4.3 文本内容处理4.4 查询操作4.5 网络相关4.6 其他命令4.7 解压缩命令5 VI/VI…

今天面了个腾讯拿 38K 出来的,让我见识到了基础的天花板

今天上班开早会就是新人见面仪式&#xff0c;听说来了个很厉害的大佬&#xff0c;年纪还不大&#xff0c;是上家公司离职过来的&#xff0c;薪资已经达到中高等水平&#xff0c;很多人都好奇不已&#xff0c;能拿到这个薪资应该人不简单&#xff0c;果然&#xff0c;自我介绍的…

0102加权quick_union和路径压缩-union-find-动态连通性-算法研究

3 union-find算法 3.5 加权quick-union算法 3.5.1 算法实现 quick-union出现最坏情况&#xff0c;因为我们是随意将一棵树链接到另外一棵树上&#xff0c;修改如下&#xff1a; 添加一个数组和一些代码记录树中节点数&#xff1b;链接时将节点数较小的树链接到较大的树上&a…

基础算法-双指针,滑动窗口,位运算,区间离散化

双指针 两种类型 for(int i0,j0;i<n;i) {while(i<j&&check(i,j)) j;// 每道题目具体逻辑}双指针最核心的性质 可以优化 输入一个字符串 把每个单词输出出来 i找到单词开头 j找到空格 vector<string> rs; for(int i0,j0;i<s.size();i) {ji;while(j&…

.Net Forms Resize 12 Crack

.Net Forms Resize 12 Crack 添加了对Microsoft Visual Studio 2022(v17.5.3)及更高版本的支持。 增加了对Microsoft Windows Server 2019和2022的支持。 改进并调整引擎大小(大约快20%)。 更新与受支持的第三方控件的新版本的兼容性。 添加了新的Microsoft Framework 4.8.1库。…

in、not in、between and、基本查询数据库实验三 单表查询(一)(头歌实践教学平台)

文章目的初衷是希望学习笔记分享给更多的伙伴&#xff0c;并无盈利目的&#xff0c;尊重版权&#xff0c;如有侵犯&#xff0c;请官方工作人员联系博主谢谢。 目录 第1关&#xff1a;基本查询语句 任务描述 相关知识 查询数据表中指定字段的内容 查询数据表中的所有内容 …

Ucore lab2

练习一&#xff1a;实现first-fit 连续物理内存分配算法 根据实验指导书中的实验执行流程概述&#xff0c;先了解分析ucore如何对物理内存进行管理&#xff0c;再完成实验练习。 在对物理内存进行管理之前&#xff0c;需要先进行物理内存布局的探测&#xff0c;探测得到的内存…

[LeetCode解题报告] 1157. 子数组中占绝大多数的元素

[LeetCode解题报告] 1157. 子数组中占绝大多数的元素 一、 题目1. 题目描述2. 原题链接二、 解题报告1. 思路分析2. 复杂度分析3. 代码实现三、 本题小结四、 参考链接一、 题目 1. 题目描述 2. 原题链接 链接: 1157. 子数组中占绝大多数的元素 二、 解题报告 1. 思路分析 …

python+requests的接口自动化测试框架实例详解教程

目录 前言 一、环境准备 二、设计框架结构 三、实现框架功能 四、执行测试用例 五、总结 前言 Python是一种简单易学、功能强大的编程语言&#xff0c;广泛应用于各种软件开发和测试场景中。requests是Python中流行的HTTP库&#xff0c;支持发送HTTP请求和处理HTTP响应&a…

【c语言】多维数组原理

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

话说~~ HTTP协议请求的工作流程 (Web服务请求过程)最细详解

目录 文章导入 &#xff1a; 概述 &#xff1a; 详解过程 &#xff1a; DNS域名解析 &#xff1a; DNS 域名解析过程分析 &#xff1a; TCP连接搭建 &#xff1a; 等待TCP队列 建立TCP连接 发起 HTTP 请求 &#xff1a; # 哪是如何进行 HTTP 请求的呢 &#…

实验架构的部署

目录 实验要求 实验环境 1、部署静态页面 2、部署负载均衡 3、搭建动态网页 4、 nginx反向代理 5、部署NFS 7、mysql 安装mysql 安装mha 准备主从复制 开启mha 架构的部署 实验要求 完成用户访问时虚拟IP由LVS负责高可用&#xff0c;静态交给nginx处理&#xff0c;…

【工具篇】Spring Boot 整合阿里云短信-SMS

短信服务-SMS 短信服务&#xff08;Short Message Service&#xff09;是广大企业客户快速触达手机用户所优选使用的通信能力&#xff0c;分为国内短信服务和国际/港澳台短信服务。通过 API/SDK、控制台调用短信发送能力&#xff0c;将指定信息发送至国内或境外手机号码。 应…

<数据结构>NO1.算法的时空复杂度

文章目录&#x1f6a9;算法效率算法复杂度&#x1fa85;时间复杂度大O的渐进表示法常见的时间复杂度举例&#x1fa85;空间复杂度大O的渐进表示法常见的空间复杂度举例&#x1f5ef;️常见复杂度对比&#x1f5ef;️&#x1f6a9;算法效率 算法是一个被设计好的&#xff0c;计…

python 读写txt方法

​​​​​​​ 1. Python支持在程序中读写 txt文件。这里有两种方式&#xff1a; 方式一&#xff1a;使用 python内置函数&#xff0c;该函数将一个字符串的长度转换为与这个字符串长度相关的值。 例如&#xff1a;" readme"&#xff08;"r&#xff09;。 prin…

数据结构---递归转化为非递归

递归转化为非递归前言快速排序非递归归并排序的非递归前言 为什么要学习非递归写法呢&#xff1f; 当我们在用递归实现一个程序的时候&#xff0c;要考虑一个问题&#xff0c;这个程序用递归去实现&#xff0c;当数据量庞大的时候&#xff0c;会不会造成栈溢出(STACK OVERFLOW…

学习风`宇blog的websocket模块

文章目录后端代码引入依赖WebSocketConfigWebSocketServiceImpl分析tb_chat_record表WebSocketServiceImplChatConfigurator聊天消息ChatTypeEnumsWebsocketMessageDTO后端 代码 引入依赖 仅需引入以下依赖 <!-- websocket依赖 --> <dependency><groupId>…

ACM8629 立体声50W/100W单声道I2S数字输入D类音频功放IC

概述 ACM8629 一款高度集成、高效率的双通道数字输入功放。供电电压范围在4.5V-26.4V,数字接口电源支持3.3V 。在4 欧负载&#xff0c;BTL模式下输出功率可以到250W1%THDN&#xff0c;在2欧负载&#xff0c;PBTL模式下单通道可以输出1100W 1%THDN. ACM8629采用新型PWM脉宽调制架…

全国青少年软件编程(Scratch)等级考试二级考试真题2023年3月——持续更新.....

一、单选题(共25题,共50分) 1. 小猫的程序如图所示,积木块的颜色与球的颜色一致。点击绿旗执行程序后,下列说法正确的是?( ) A.小猫一直在左右移动,嘴里一直说着“抓到了”。 B.小猫会碰到球,然后停止。 C.小猫一直在左右移动,嘴里一直说着“别跑” D.小猫会碰到球,…

2023MatherCup杯三人小队手搓!(C 题 电商物流网络包裹应急调运与结构优化问题)

一个不知名大学生&#xff0c;江湖人称菜狗original author: Jacky LiEmail : 3435673055qq.com Time of completion&#xff1a;2023.4.16 Last edited: 2023.4.16 实际完成时间&#xff1a;2023/4/17 0:52 Mathematical modeling Author: HandSome Wang、BigTall Hu、Jacky L…