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的死角,玩家可以活到把用户送走
相关文章:

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 mobs dir…...

ardupilot 中坐标变换矩阵和坐标系变换矩阵区别
目录 文章目录 目录摘要1.坐标变换矩阵与坐标系变换矩阵摘要 本节主要记录ardupilot 中坐标变换矩阵和坐标系变换矩阵的区别,这里非常重要,特别是进行姿态误差计算时,如果理解错误,很难搞明白后面算法。 1.坐标变换矩阵与坐标系变换矩阵 坐标变换矩阵的本质含义:是可以把…...

VR内容研发公司 | VR流感病毒实验虚拟现实课件
由广州华锐互动开发的《VR流感病毒实验虚拟现实课件》是一种新型的教学模式,可以为学生提供更加真实和直观的流感病毒分离鉴定实验操作体验,从而提高学生的实验技能和工作效率。 《VR流感病毒实验虚拟现实课件》涉及了生物安全二级实验室(BSL-2)和流感病…...

python——案例10:认识if、elif、else
案例10:认识if、elif、elsenumfloat(input("输入数值:")) #用户输入数字if num>0:print("正数")elif num0:print("零") else:print("负数")#输出结果如下:输入数值:-1 负数 输入数值…...
Hadoop中命令检查hdfs的文件是否存在
Hadoop中命令检查hdfs的文件是否存在 在Hadoop中,可以使用以下命令检查HDFS文件是否存在: hadoop fs -test -e 其中,是要检查的HDFS文件的路径。 如果文件存在,命令返回0;如果文件不存在,命令返回非0值…...
计算机网络用户接入层设计
用户接入层为用户提供访问核心网络的能力, 为用户提供共享/交换的带宽分配,按照业主要求,并考虑到端口密度的要求以及 设备的性能价格比,建议选用 Catalyst 3524XL和 Catalyst 3548XL 工作组交换 机,分别放置于配线间中。如同一配线间需两台以…...

全志F1C200S嵌入式驱动开发(应用程序开发)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 我们在开发soc驱动的时候,很多情况下也要验证下当前的驱动功能是否正确。当然除了验证驱动功能之外,我们还要编写业务代码和流程代码。这中间就和各行各业有关了,有的是算法,有…...

人工智能学习07--pytorch23--目标检测:Deformable-DETR训练自己的数据集
参考 https://blog.csdn.net/qq_44808827/article/details/125326909https://blog.csdn.net/dystsp/article/details/125949720?utm_mediumdistribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-125949720-blog-125326909.235^v38^pc_releva…...

Statefulset 实战 1
上一部分与大家分享到 Statefulset 与 RplicaSet 的区别,以及 Statefulset 的特点,能做的一些事情及一些注意事项 现在我们来尝试使用 Statefulset 来部署我们的应用,我们可以需要有应用程序,然后有持久化卷 开始使用 Statefuls…...
没有jodatime,rust怎么方便高效的操作时间呢?
关注我,学习Rust不迷路!! 当使用Rust进行日期操作时,可以使用 chrono 库。下面给出了二十个常见的日期操作的例子: 1. 获取当前日期和时间: use chrono::prelude::*;let current_datetime Local::now()…...

如何把pdf转成cad版本?这种转换方法非常简单
将PDF转换成CAD格式的优势在于,CAD格式通常是用于工程设计和绘图的标准格式。这种格式的文件可以在计算机上进行编辑和修改,而不需要纸质副本。此外,CAD文件通常可以与其他CAD软件进行交互,从而使得工程设计和绘图过程更加高效和精…...
MySQL常用函数方法
字符串函数 函数描述举例left(str, length)从左开始截取字符串,截取length个left(2023-08-04, 7) 2023-08right(str, length)从右开始截取字符串,截取length个 right(2023-08-04, 5) 08-04 substring(str, pos, length) substring(被截取字…...

Linux命令200例专栏导读:实用指南助你成为Linux大师
🏆作者简介,黑夜开发者,全栈领域新星创作者✌,阿里云社区专家博主,2023年6月csdn上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已…...
ICN6202 MIPIDSI转LVDS桥接芯片的功能及特征 调试文档资料
产品特征功能: 输入:MIPI DSI 支持MIPI D-PHY Version 1.00.00 和 MIPI DSI Version 1.02.00. 可接收MIPI DSI 18bpp RGB666 and 24bpp RGB888 packets 4 lane data1 lane clock 4对数据线可以选择1、2、3、4lane data 每对差分数据传输线最大可…...

vscode 格式问题
1、EditorConfig for VS Code 插件 shift alt f 格式化文件(VS Code格式化按键),如下图,每个缩进4空格 代码如下 创建文件名 .editorconfig root true [*] charset utf-8 indent_style space indent_size 2 end_of_…...
OPENCV C++(三)二值化灰度函数+调用摄像头+鼠标响应+肤色检测
RGB转灰度函数 cvtColor(image, gray, COLOR_BGR2GRAY); 图像 目标图像 rgb转灰度 大津法二值化函数 threshold(gray, result1, 84, 255, THRESH_OTSU); 灰度图,目标图,阈值,大于阈值的转换的像素值,方法为大津法 自适应二值…...

zabbix简易入门:基本的网络监控、WEB监控
需求背景: 我们越来越发现:网络越来越复杂,网络、应用、云端……故障点随时可能发生,而我们不能人工盯着所有的问题,所以,网管软件是必须的。那么没有预算的情况下,我们只好自己布署简单的…...

51单片机学习--DS1302可调时钟
之前学习过用定时器做的时钟,但是那样不仅误差大还费CPU,接下来利用DS1302时钟模块做一个可调实时时钟 这一次直接编写DS1302模块,首先要在DS1392.c 中根据下面的模块原理图进行位声明: sbit DS1302_SCLK P3^6; sbit DS1302_IO …...
Matlab统计字符串中共有多少种字符以及每种字符出现次数的功能实现(Matlab R2021a)
在做2023年深圳杯B题的时候,需要使用隐写技术(将特定信息嵌入信息载体且不易被察觉,可被广泛地应用于著作权保护、数据附加等领域)将《中华人民共和国著作权法》全篇10314个字符写入图片,首先我想到的是利用霍夫曼编码…...
HTTPS文件传输
目录 0.https概述1.单钥匙锁2.双钥匙锁 - 防篡改3.双钥匙锁 - 防泄漏4.单双钥匙锁相互配合 0.https概述 HTTPS其实就是HTTP协议加上TLS/SSL,SSL是个加密套件,负责对HTTP的数据进行加密,TLS是SSL的升级版,现在提到HTTPS࿰…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...