Godot 4 源码分析 - Path2D与PathFollow2D

学习演示项目dodge_the_creeps,发现里面多了一个Path2D与PathFollow2D

 研究GDScript代码发现,它主要用于随机生成Mob

	var mob_spawn_location = get_node(^"MobPath/MobSpawnLocation")mob_spawn_location.progress = randi()# Set the mob's direction perpendicular to the path direction.var direction = mob_spawn_location.rotation + PI / 2# Set the mob's position to a random location.mob.position = mob_spawn_location.position# Add some randomness to the direction.direction += randf_range(-PI / 4, PI / 4)mob.rotation = direction# Choose the velocity for the mob.var velocity = Vector2(randf_range(150.0, 250.0), 0.0)mob.linear_velocity = velocity.rotated(direction)

这个有这么大的作用,不明觉厉

但不知道如何下手

查看源码,有编辑器及类源码

先从应用角度,到B站上找找有没有视频,结果发现这个

Godot塔防游戏 - 01 -核心路径制作 Path2D_哔哩哔哩_bilibili

看了之后,就知道使用方法了:

  • 添加Path2D
  • 在编辑器中设置路径各关键点,形成路径

  • 在Path2D下增加PathFollow2D

这就OK了。剩下的就是使用

所谓使用,输入为PathFollow2D的progress,输出为路径上的点信息(position, rotation...),然后用户再根据这些信息去确定相应的属性

比如演示项目中,Path2D定制了一个外框路径(左上角 > 右上角 > 右下角 > 左下角 > 左上角),在生成MOB时,随机指定其下的PathFollow2D的progress值为randi(),即为0 ~ 2^32 - 1的随机整数。因为路径是有长度的,本例中为2400,randi()值将按2400取模得到最终的随机值0 - 2399,当然也可以归一化,设置其progress_ratio值为0.0 - 1.0,意思一样。

查看源码,set_progress的逻辑不只是取模,还有限制范围。即PathFollow2D还有一个Loop属性,如果Loop为真,才会取模,为false时,会直接限制在路径长度范围内 progress = CLAMP(progress, 0, path_length); 之后统一更新_update_transform

void PathFollow2D::set_progress(real_t p_progress) {ERR_FAIL_COND(!isfinite(p_progress));progress = p_progress;if (path) {if (path->get_curve().is_valid()) {real_t path_length = path->get_curve()->get_baked_length();if (loop && path_length) {progress = Math::fposmod(progress, path_length);if (!Math::is_zero_approx(p_progress) && Math::is_zero_approx(progress)) {progress = path_length;}} else {progress = CLAMP(progress, 0, path_length);}}_update_transform();}
}void PathFollow2D::_update_transform() {if (!path) {return;}Ref<Curve2D> c = path->get_curve();if (!c.is_valid()) {return;}real_t path_length = c->get_baked_length();if (path_length == 0) {return;}if (rotates) {Transform2D xform = c->sample_baked_with_rotation(progress, cubic);xform.translate_local(v_offset, h_offset);set_rotation(xform[1].angle());set_position(xform[2]);} else {Vector2 pos = c->sample_baked(progress, cubic);pos.x += h_offset;pos.y += v_offset;set_position(pos);}
}

从PathFollow2D代码来看,它派生于Node2D,所以具备transform属性:Position、Rotation、Scale、Skew,对于路径上的点使用而言,这些信息就足够了,能够确定这些点的位置、方向,其实就是一个矢量 

Loop属性值的含义前面已明确,Rotates、Cubic、H Offsets、V Offsets都是在_update_transform中起作用,具体算法可以不深究。但lookahead没找到具体用处,感觉影响不大。

class PathFollow2D : public Node2D {GDCLASS(PathFollow2D, Node2D);public:
private:Path2D *path = nullptr;real_t progress = 0.0;Timer *update_timer = nullptr;real_t h_offset = 0.0;real_t v_offset = 0.0;real_t lookahead = 4.0;bool cubic = true;bool loop = true;bool rotates = true;void _update_transform();protected:void _validate_property(PropertyInfo &p_property) const;void _notification(int p_what);static void _bind_methods();public:void path_changed();void set_progress(real_t p_progress);real_t get_progress() const;void set_h_offset(real_t p_h_offset);real_t get_h_offset() const;void set_v_offset(real_t p_v_offset);real_t get_v_offset() const;void set_progress_ratio(real_t p_ratio);real_t get_progress_ratio() const;void set_lookahead(real_t p_lookahead);real_t get_lookahead() const;void set_loop(bool p_loop);bool has_loop() const;void set_rotates(bool p_rotates);bool is_rotating() const;void set_cubic_interpolation(bool p_enable);bool get_cubic_interpolation() const;PackedStringArray get_configuration_warnings() const override;PathFollow2D() {}
};void PathFollow2D::path_changed() {if (update_timer && !update_timer->is_stopped()) {update_timer->start();} else {_update_transform();}
}void PathFollow2D::_update_transform() {if (!path) {return;}Ref<Curve2D> c = path->get_curve();if (!c.is_valid()) {return;}real_t path_length = c->get_baked_length();if (path_length == 0) {return;}if (rotates) {Transform2D xform = c->sample_baked_with_rotation(progress, cubic);xform.translate_local(v_offset, h_offset);set_rotation(xform[1].angle());set_position(xform[2]);} else {Vector2 pos = c->sample_baked(progress, cubic);pos.x += h_offset;pos.y += v_offset;set_position(pos);}
}void PathFollow2D::_notification(int p_what) {switch (p_what) {case NOTIFICATION_READY: {if (Engine::get_singleton()->is_editor_hint()) {update_timer = memnew(Timer);update_timer->set_wait_time(0.2);update_timer->set_one_shot(true);update_timer->connect("timeout", callable_mp(this, &PathFollow2D::_update_transform));add_child(update_timer, false, Node::INTERNAL_MODE_BACK);}} break;case NOTIFICATION_ENTER_TREE: {path = Object::cast_to<Path2D>(get_parent());if (path) {_update_transform();}} break;case NOTIFICATION_EXIT_TREE: {path = nullptr;} break;}
}void PathFollow2D::set_cubic_interpolation(bool p_enable) {cubic = p_enable;
}bool PathFollow2D::get_cubic_interpolation() const {return cubic;
}void PathFollow2D::_validate_property(PropertyInfo &p_property) const {if (p_property.name == "offset") {real_t max = 10000.0;if (path && path->get_curve().is_valid()) {max = path->get_curve()->get_baked_length();}p_property.hint_string = "0," + rtos(max) + ",0.01,or_less,or_greater";}
}PackedStringArray PathFollow2D::get_configuration_warnings() const {PackedStringArray warnings = Node::get_configuration_warnings();if (is_visible_in_tree() && is_inside_tree()) {if (!Object::cast_to<Path2D>(get_parent())) {warnings.push_back(RTR("PathFollow2D only works when set as a child of a Path2D node."));}}return warnings;
}void PathFollow2D::_bind_methods() {ClassDB::bind_method(D_METHOD("set_progress", "progress"), &PathFollow2D::set_progress);ClassDB::bind_method(D_METHOD("get_progress"), &PathFollow2D::get_progress);ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &PathFollow2D::set_h_offset);ClassDB::bind_method(D_METHOD("get_h_offset"), &PathFollow2D::get_h_offset);ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &PathFollow2D::set_v_offset);ClassDB::bind_method(D_METHOD("get_v_offset"), &PathFollow2D::get_v_offset);ClassDB::bind_method(D_METHOD("set_progress_ratio", "ratio"), &PathFollow2D::set_progress_ratio);ClassDB::bind_method(D_METHOD("get_progress_ratio"), &PathFollow2D::get_progress_ratio);ClassDB::bind_method(D_METHOD("set_rotates", "enable"), &PathFollow2D::set_rotates);ClassDB::bind_method(D_METHOD("is_rotating"), &PathFollow2D::is_rotating);ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow2D::set_cubic_interpolation);ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow2D::get_cubic_interpolation);ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow2D::set_loop);ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow2D::has_loop);ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead);ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead);ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress", PROPERTY_HINT_RANGE, "0,10000,0.01,or_less,or_greater,suffix:px"), "set_progress", "get_progress");ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "progress_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001,or_less,or_greater", PROPERTY_USAGE_EDITOR), "set_progress_ratio", "get_progress_ratio");ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset"), "set_h_offset", "get_h_offset");ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset"), "set_v_offset", "get_v_offset");ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotates"), "set_rotates", "is_rotating");ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"), "set_lookahead", "get_lookahead");
}void PathFollow2D::set_progress(real_t p_progress) {ERR_FAIL_COND(!isfinite(p_progress));progress = p_progress;if (path) {if (path->get_curve().is_valid()) {real_t path_length = path->get_curve()->get_baked_length();if (loop && path_length) {progress = Math::fposmod(progress, path_length);if (!Math::is_zero_approx(p_progress) && Math::is_zero_approx(progress)) {progress = path_length;}} else {progress = CLAMP(progress, 0, path_length);}}_update_transform();}
}void PathFollow2D::set_h_offset(real_t p_h_offset) {h_offset = p_h_offset;if (path) {_update_transform();}
}real_t PathFollow2D::get_h_offset() const {return h_offset;
}void PathFollow2D::set_v_offset(real_t p_v_offset) {v_offset = p_v_offset;if (path) {_update_transform();}
}real_t PathFollow2D::get_v_offset() const {return v_offset;
}real_t PathFollow2D::get_progress() const {return progress;
}void PathFollow2D::set_progress_ratio(real_t p_ratio) {if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {set_progress(p_ratio * path->get_curve()->get_baked_length());}
}real_t PathFollow2D::get_progress_ratio() const {if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {return get_progress() / path->get_curve()->get_baked_length();} else {return 0;}
}void PathFollow2D::set_lookahead(real_t p_lookahead) {lookahead = p_lookahead;
}real_t PathFollow2D::get_lookahead() const {return lookahead;
}void PathFollow2D::set_rotates(bool p_rotates) {rotates = p_rotates;_update_transform();
}bool PathFollow2D::is_rotating() const {return rotates;
}void PathFollow2D::set_loop(bool p_loop) {loop = p_loop;
}bool PathFollow2D::has_loop() const {return loop;
}

从代码与用途来看,Path2D就没啥看头了,就负责提供一条曲线路径

class Path2D : public Node2D {GDCLASS(Path2D, Node2D);Ref<Curve2D> curve;void _curve_changed();protected:void _notification(int p_what);static void _bind_methods();public:
#ifdef TOOLS_ENABLEDvirtual Rect2 _edit_get_rect() const override;virtual bool _edit_use_rect() const override;virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
#endifvoid set_curve(const Ref<Curve2D> &p_curve);Ref<Curve2D> get_curve() const;Path2D() {}
};#ifdef TOOLS_ENABLED
Rect2 Path2D::_edit_get_rect() const {if (!curve.is_valid() || curve->get_point_count() == 0) {return Rect2(0, 0, 0, 0);}Rect2 aabb = Rect2(curve->get_point_position(0), Vector2(0, 0));for (int i = 0; i < curve->get_point_count(); i++) {for (int j = 0; j <= 8; j++) {real_t frac = j / 8.0;Vector2 p = curve->sample(i, frac);aabb.expand_to(p);}}return aabb;
}bool Path2D::_edit_use_rect() const {return curve.is_valid() && curve->get_point_count() != 0;
}bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {if (curve.is_null()) {return false;}for (int i = 0; i < curve->get_point_count(); i++) {Vector2 s[2];s[0] = curve->get_point_position(i);for (int j = 1; j <= 8; j++) {real_t frac = j / 8.0;s[1] = curve->sample(i, frac);Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s);if (p.distance_to(p_point) <= p_tolerance) {return true;}s[0] = s[1];}}return false;
}
#endifvoid Path2D::_notification(int p_what) {switch (p_what) {// Draw the curve if path debugging is enabled.case NOTIFICATION_DRAW: {if (!curve.is_valid()) {break;}if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_paths_hint()) {return;}if (curve->get_point_count() < 2) {return;}#ifdef TOOLS_ENABLEDconst real_t line_width = get_tree()->get_debug_paths_width() * EDSCALE;
#elseconst real_t line_width = get_tree()->get_debug_paths_width();
#endifreal_t interval = 10;const real_t length = curve->get_baked_length();if (length > CMP_EPSILON) {const int sample_count = int(length / interval) + 2;interval = length / (sample_count - 1); // Recalculate real interval length.Vector<Transform2D> frames;frames.resize(sample_count);{Transform2D *w = frames.ptrw();for (int i = 0; i < sample_count; i++) {w[i] = curve->sample_baked_with_rotation(i * interval, false);}}const Transform2D *r = frames.ptr();// Draw curve segments{PackedVector2Array v2p;v2p.resize(sample_count);Vector2 *w = v2p.ptrw();for (int i = 0; i < sample_count; i++) {w[i] = r[i].get_origin();}draw_polyline(v2p, get_tree()->get_debug_paths_color(), line_width, false);}// Draw fish bones{PackedVector2Array v2p;v2p.resize(3);Vector2 *w = v2p.ptrw();for (int i = 0; i < sample_count; i++) {const Vector2 p = r[i].get_origin();const Vector2 side = r[i].columns[0];const Vector2 forward = r[i].columns[1];// Fish Bone.w[0] = p + (side - forward) * 5;w[1] = p;w[2] = p + (-side - forward) * 5;draw_polyline(v2p, get_tree()->get_debug_paths_color(), line_width * 0.5, false);}}}} break;}
}void Path2D::_curve_changed() {if (!is_inside_tree()) {return;}if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_paths_hint()) {return;}queue_redraw();for (int i = 0; i < get_child_count(); i++) {PathFollow2D *follow = Object::cast_to<PathFollow2D>(get_child(i));if (follow) {follow->path_changed();}}
}void Path2D::set_curve(const Ref<Curve2D> &p_curve) {if (curve.is_valid()) {curve->disconnect("changed", callable_mp(this, &Path2D::_curve_changed));}curve = p_curve;if (curve.is_valid()) {curve->connect("changed", callable_mp(this, &Path2D::_curve_changed));}_curve_changed();
}Ref<Curve2D> Path2D::get_curve() const {return curve;
}void Path2D::_bind_methods() {ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Path2D::set_curve);ClassDB::bind_method(D_METHOD("get_curve"), &Path2D::get_curve);ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_curve", "get_curve");
}

当然,也不是啥用处没有,比如动态指定路径的时候,就可以设置一条Curve2D,然后赋给Path2D,后面就照此行事。

比如,该演示项目中,

var curve = Curve2D.new()
curve.add_point(Vector2i(100, 100))
curve.add_point(Vector2i(400, 600))
$MobPath.curve = curve

然后,玩家呆在右上角,这就是那些MOB的死角,玩家可以活到把用户送走

 

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

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

相关文章

【C语言】初阶完结练习题

&#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;C语言初阶 ✨其他专栏&#xff1a;代码小游戏 &#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论…

Misc取证学习

文章目录 Misc取证学习磁盘取证工具veracryto挂载fat文件DiskGenius 磁盘取证例题[RCTF2019]disk 磁盘[](https://ciphersaw.me/ctf-wiki/misc/disk-memory/introduction/#_2)内存取证工具volatility 内存取证例题数字取证赛题0x01.从内存中获取到用户admin的密码并且破解密码 …

如何搭建一个成功的家具小程序

家具行业近年来发展迅猛&#xff0c;越来越多的消费者开始选择在小程序商城上购买家具。因此&#xff0c;制作一款家具小程序商城成为了许多家具商家的必然选择。那么&#xff0c;如何制作一款个性化、功能齐全的家具小程序商城呢&#xff1f;下面将为大家介绍一种简单且高效的…

观察者模式(C++)

定义 定义对象间的一种一对多(变化)的依赖关系&#xff0c;以便当一个对象(Subject)的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并自动更新。 ——《设计模式》GoF 使用场景 一个对象&#xff08;目标对象&#xff09;的状态发生改变&#xff0c;所有的依赖对…

Pytorch Tutorial【Chapter 3. Simple Neural Network】

Pytorch Tutorial【Chapter 3. Simple Neural Network】 文章目录 Pytorch Tutorial【Chapter 3. Simple Neural Network】Chapter 3. Simple Neural Network3.1 Train Neural Network Procedure训练神经网络流程3.2 Build Neural Network Procedure 搭建神经网络3.3 Use Loss …

【LeetCode】24.两两交换链表中的节点

题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xff1a…

SQL-每日一题【1193. 每月交易 I】

题目 Table: Transactions 编写一个 sql 查询来查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数及其总金额。 以 任意顺序 返回结果表。 查询结果格式如下所示。 示例 1: 解题思路 1.题目要求我们查找每个月和每个国家/地区的事务数及其总金额、已批准的事务数…

实验笔记之——Windows下的Android环境开发搭建

好久一段时间没有进行Android开发了&#xff0c;最新在用的电脑也没有了Android studio了。为此&#xff0c;本博文记录一下最近重新搭建Android开发的过程。本博文仅为本人学习记录用&#xff08;**别看&#xff09; 目录 安装Android Studio以及JDK JDK Android Studiio …

Java并发系列之七:ConcurrentHashMap

回顾HashMap 既然说到HashMap了&#xff0c;那么我们就先来简单总结一下HashMap的重点。 1.基本结构 HashMap存储的是存在映射关系的键值对&#xff0c;存储在被称为哈希表(数组链表/红黑树)的数据结构中。通过计算key的hashCode值来确定键值对在数组中的位置&#xff0c;假…

【机器学习】西瓜书学习心得及课后习题参考答案—第5章神经网络

笔记心得 5.1神经元模型——这是神经网络中最基本的成分。 5.2感知机与多层网络——由简单的感知机循序渐进引出多层前馈神经网络。 5.3误差逆传播算法——BP算法&#xff0c;迄今最成功的神经网络学习算法。算法如下&#xff08;公式参考西瓜书&#xff09; 停止条件与缓解…

Laravel 框架路由参数.重定向.视图回退.当前路由.单行为 ②

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…

Redis压缩列表

区分一下 3.2之前 Redis中的List有两种编码格式 一个是LINKEDLIST 一个是ZIPLIST 这个ZIPLIST就是压缩列表 3.2之后来了一个QUICKLIST QUICKLIST是ZIPLIST和LINKEDLIST的结合体 也就是说Redis中没有ZIPLIST和LINKEDLIST了 然后在Redis5.0引入了LISTPACK用来替换QUiCKLIST中的…

【C++】深入浅出STL之vector类

文章篇幅较长&#xff0c;越3万余字&#xff0c;建议电脑端访问 文章目录 一、前言二、vector的介绍及使用1、vector的介绍2、常用接口细述1&#xff09;vector类对象的默认成员函数① 构造函数② 拷贝构造③ 赋值重载 2&#xff09;vector类对象的访问及遍历操作① operator[]…

学习左耳听风栏目90天——第一天 1-90(学习左耳朵耗子的工匠精神,对技术的热爱)【洞悉技术的本质,享受科技的乐趣】

洞悉技术的本质&#xff0c;享受科技的乐趣 第一篇&#xff0c;我的感受就是 耗叔是一个热爱技术&#xff0c;可以通过代码找到快乐的技术人。 作为it从业者&#xff0c;我们如何可以通过代码找到快乐呢&#xff1f;这是一个问题&#xff1f; 至少目前&#xff0c;我还没有这种…

Qt之C++

Qt之C 类的定义 C语言的灵魂是指针 C的灵魂是类&#xff0c;类可以看出C语言结构体的升级版&#xff0c;类的成员可以是变量&#xff0c;也可是函数。 class Box { public://确定类成员的访问属性double length;//长double breadth;//宽度double heigth;//高度 };定义对象 …

TestNG中实现多线程并行,提速用例的执行时间

TestNG是一个开源自动化测试工具&#xff0c;TestNG源于Junit&#xff0c;最初用来做单元测试&#xff0c;可支持异常测试&#xff0c;忽略测试&#xff0c;超时测试&#xff0c;参数化测试和依赖测试。 除了单元测试&#xff0c;TestNG的强大功能让他在接口和UI自动化中也占有…

UE4 Cesium 学习笔记

Cesium中CesiumGeoreference的原点Orgin&#xff0c;设置到新的位置上过后&#xff0c;将FloatingPawn的Translation全改为0&#xff0c;才能到对应的目标点上去 在该位置可以修改整体建筑的材质 防止刚运行的时候&#xff0c;人物就掉下场景之下&#xff0c;controller控制的…

导出LLaMA等LLM模型为onnx

通过onnx模型可以在支持onnx推理的推理引擎上进行推理&#xff0c;从而可以将LLM部署在更加广泛的平台上面。此外还可以具有避免pytorch依赖&#xff0c;获得更好的性能等优势。 这篇博客&#xff08;大模型LLaMa及周边项目&#xff08;二&#xff09; - 知乎&#xff09;进行…

堆的模板:使用一维数组实现小根堆,实现删除操作和堆顶元素输出功能

一、链接 838. 堆排序 二、题目 输入一个长度为 nn 的整数数列&#xff0c;从小到大输出前 mm 小的数。 输入格式 第一行包含整数 nn 和 mm。 第二行包含 nn 个整数&#xff0c;表示整数数列。 输出格式 共一行&#xff0c;包含 mm 个整数&#xff0c;表示整数数列中前…

外国(境外)机构在中国境内提供金融信息服务许可8家名单

6月30日&#xff0c;国家互联网信息办公室公布8家外国&#xff08;境外&#xff09;机构在中国境内提供金融信息服务许可名单&#xff0c;如下&#xff1a;