B-spline 控制点生成

B-spline 控制点生成

一、概述

本知识文档详细介绍了如何将离散的轨迹点转换为 B-spline 的控制点的方法,包括其背后的数学原理和相应的代码实现。B-spline 曲线在多个领域,如计算机图形学、机器人路径规划、动画制作等,有着广泛的应用,它能够生成平滑且灵活的曲线,而控制点的生成过程对于曲线的最终形状和性质起着决定性的作用。

二、B-Spline 曲线的数学表示

B-spline 曲线可以用以下公式表示:
p ( s ( t ) ) = s ( t ) T M p q m \mathbf{p}(s(t)) = s(t)^T M_p \mathbf{q}_m p(s(t))=s(t)TMpqm
其中:

  • ( p ( s ( t ) ) ) (\mathbf{p}(s(t))) (p(s(t))):表示 B-spline 曲线在参数 ( t ) ( t ) (t) 处的点,它是曲线在不同参数值下的位置描述。
  • ( s ( t ) ) (s(t)) (s(t)):B-spline 基函数向量,该向量是关于参数 ( t ) ( t ) (t) 的函数,其值取决于参数 ( t ) ( t ) (t) 的取值,决定了基函数在不同位置的贡献大小。
  • ( M p ) (M_p) (Mp):B-spline 的基矩阵,它定义了基函数的组合方式,是一个固定的矩阵,描述了基函数之间的内在联系。
  • ( q m ) (\mathbf{q}_m) (qm):B-spline 的控制点向量,它是我们要生成的核心元素,决定了曲线的形状,通过调整控制点的位置,可以改变 B-spline 曲线的形状和走势。

目标
给定一组离散的轨迹点(包含位置信息)以及边界条件(速度和加速度),通过反演过程求解出控制点 ( q m ) ( \mathbf{q}_m ) (qm),使得 B-spline 曲线尽可能拟合这些轨迹点,并满足边界条件。

三、代码实现细节

(一)构造矩阵 ( A ) ( A ) (A)

矩阵 ( A ) ( A ) (A) 是线性方程组 ( A q m = b ) ( A \mathbf{q}_m = \mathbf{b} ) (Aqm=b) 中的系数矩阵,用于建立控制点与轨迹点之间的线性关系,包含位置约束、速度约束和加速度约束。

1. 位置约束
for (int i = 0; i < K; ++i) 
  A.block(i, i, 1, 3) = (1 / 6.0) * prow.transpose();
  • 作用
    • 确保 B-spline 曲线在每个采样点 ( i ) ( i ) (i) 处经过给定的轨迹点。
  • 解释
    • 每个轨迹点 ( p i ) ( \mathbf{p}_i ) (pi) 被表示为三个连续控制点的线性组合,权重来自 B-spline 的位置基函数,具体公式为:
      p i = 1 6 ⋅ ( q i − 1 + 4 ⋅ q i + q i + 1 ) \mathbf{p}_i = \frac{1}{6} \cdot \left( \mathbf{q}_{i-1} + 4 \cdot \mathbf{q}_i + \mathbf{q}_{i+1} \right) pi=61(qi1+4qi+qi+1)
    • 这意味着每个轨迹点是三个控制点的加权平均,权重分别为 ( 1 6 ) ( \frac{1}{6} ) (61) ( 4 6 ) ( \frac{4}{6} ) (64) ( 1 6 ) ( \frac{1}{6} ) (61)
    • 在代码中,A.block(i, i, 1, 3) 表示在矩阵 ( A ) ( A ) (A) 的第 ( i ) ( i ) (i) 行,从第 ( i ) ( i ) (i) 列开始的 1 行 3 列的子矩阵,赋值为 ( 1 6 ) ( \frac{1}{6} ) (61) 乘以基函数向量的转置,以反映位置约束在矩阵中的体现。
2. 速度约束
A.block(K, 0, 1, 3)         = (1 / 2.0 / ts) * vrow.transpose();
A.block(K + 1, K - 1, 1, 3) = (1 / 2.0 / ts) * vrow.transpose();
  • 作用
    • 确保 B-spline 曲线在起点和终点处的速度与给定的边界条件相匹配。
  • 解释
    • 起点速度约束
      v start = 1 2 ⋅ t s ⋅ ( q 1 − q − 1 ) \mathbf{v}_{\text{start}} = \frac{1}{2 \cdot ts} \cdot \left( \mathbf{q}_1 - \mathbf{q}_{-1} \right) vstart=2ts1(q1q1)
      由于控制点索引从 0 开始,可能会涉及虚拟控制点或进行适当的边界处理。
    • 终点速度约束
      v end = 1 2 ⋅ t s ⋅ ( q K + 1 − q K − 1 ) \mathbf{v}_{\text{end}} = \frac{1}{2 \cdot ts} \cdot \left( \mathbf{q}_{K+1} - \mathbf{q}_{K-1} \right) vend=2ts1(qK+1qK1)
    • 代码中,vrow 是速度基函数向量,表示控制点对速度的影响。通过调整矩阵 ( A ) ( A ) (A) 的相应位置,使控制点的差分与给定的速度相匹配,将速度约束融入到矩阵 ( A ) ( A ) (A) 中。
3. 加速度约束
A.block(K + 2, 0, 1, 3)     = (1 / ts / ts) * arow.transpose();
A.block(K + 3, K - 1, 1, 3) = (1 / ts / ts) * arow.transpose();
  • 作用
    • 确保 B-spline 曲线在起点和终点处的加速度与给定的边界条件相匹配。
  • 解释
    • 起点加速度约束
      a start = 1 t s 2 ⋅ ( q − 1 − 2 ⋅ q 0 + q 1 ) \mathbf{a}_{\text{start}} = \frac{1}{ts^2} \cdot \left( \mathbf{q}_{-1} - 2 \cdot \mathbf{q}_0 + \mathbf{q}_1 \right) astart=ts21(q12q0+q1)
    • 终点加速度约束
      a end = 1 t s 2 ⋅ ( q K − 1 − 2 ⋅ q K + q K + 1 ) \mathbf{a}_{\text{end}} = \frac{1}{ts^2} \cdot \left( \mathbf{q}_{K-1} - 2 \cdot \mathbf{q}_K + \mathbf{q}_{K+1} \right) aend=ts21(qK12qK+qK+1)
    • 代码中,arow 是加速度基函数向量,表示控制点对加速度的影响。通过调整矩阵 ( A ) ( A ) (A) 的相应位置,使控制点的二阶差分与给定的加速度相匹配,将加速度约束融入矩阵 ( A ) ( A ) (A) 中。

(二)构造右侧向量 ( b ) ( \mathbf{b} ) (b)

向量 ( b ) ( \mathbf{b} ) (b) 包含了所有的约束条件,包括轨迹点的位置以及边界条件(速度和加速度)。

1. 轨迹点约束
for (int i = 0; i < K; ++i) {
  bx(i) = point_set[i].x;
  by(i) = point_set[i].y;
  bz(i) = point_set[i].z;
}
  • 作用
    • 将每个离散的轨迹点的位置信息填入向量 ( b ) ( \mathbf{b} ) (b) 的前 ( K ) ( K ) (K) 个元素。
  • 解释
    • ( b ) ( \mathbf{b} ) (b) 的前 ( K ) ( K ) (K) 行对应于位置约束,每一行分别对应轨迹点在 ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z) 方向上的坐标,这些轨迹点是 B-spline 曲线需要通过的点。
2. 边界条件约束(速度和加速度)
for (int i = 0; i < 4; ++i) {
  bx(K + i) = start_end_derivative[i].x;
  by(K + i) = start_end_derivative[i].y;
  bz(K + i) = start_end_derivative[i].z;
}
  • 作用
    • 将起点和终点的速度、加速度约束填入向量 ( b ) ( \mathbf{b} ) (b) 的后 4 个元素。
  • 解释
    • ( b ) ( \mathbf{b} ) (b) 的后 4 行分别对应起点和终点的速度与加速度约束,start_end_derivative 包含了这些边界条件的具体数值,确保 B-spline 曲线在起点和终点处的导数(速度和加速度)与预期一致。

(三)求解线性方程 ( A q = b ) ( A \mathbf{q} = \mathbf{b} ) (Aq=b)

Eigen::VectorXd px = A.colPivHouseholderQr().solve(bx);
Eigen::VectorXd py = A.colPivHouseholderQr().solve(by);
Eigen::VectorXd pz = A.colPivHouseholderQr().solve(bz);
  • 作用
    • 分别求解 ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z) 方向上的控制点坐标。
  • 解释
    • 由于每个维度( ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z))是独立的,可以分别构造和求解线性方程组。
    • 使用 Eigen 库中的 colPivHouseholderQr 分解方法进行求解,该方法适用于一般的线性方程组,具有较好的数值稳定性。从数学角度看,求解过程可表示为:
      q m = A − 1 b \mathbf{q}_m = A^{-1} \mathbf{b} qm=A1b
      但实际计算中通过分解方法避免了直接求逆,提高了计算效率和精度。

(四)组合控制点矩阵

ctrl_pts.resize(K + 2, 3);
ctrl_pts.col(0) = px;
ctrl_pts.col(1) = py;
ctrl_pts.col(2) = pz;
  • 作用
    • 将求解得到的 ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z) 方向上的控制点组合成一个 ( ( K + 2 ) × 3 ) ( (K+2) \times 3 ) ((K+2)×3) 的矩阵,表示 B-spline 的控制点。
  • 解释
    • ctrl_pts 是一个矩阵,每一行代表一个控制点的三维坐标。
    • resize(K + 2, 3):假设 ( K ) ( K ) (K) 是轨迹点的数量,通常控制点的数量比轨迹点多 2 个,以满足边界条件的需求。将分别求解得到的 ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z) 坐标赋值给 ctrl_pts 的对应列,形成最终的控制点矩阵。

四、总结公式关系

(一)矩阵 ( A ) ( A ) (A)

  • 数学上
    • ( A ) ( A ) (A) 反映了 B-spline 基函数对控制点的权重,建立了控制点与轨迹点、边界条件之间的线性关系。
  • 代码中
    • 通过位置、速度、加速度约束,构造了包含所有约束的矩阵 ( A ) ( A ) (A)

(二)向量 ( b ) ( \mathbf{b} ) (b)

  • 数学上
    • ( b ) ( \mathbf{b} ) (b) 包含了所有的约束条件,包括轨迹点的位置以及边界条件(速度和加速度)。
  • 代码中
    • 将轨迹点和边界条件的数值填入向量 ( b ) ( \mathbf{b} ) (b)

(三)线性求解

  • 数学上
    • 通过求解线性方程组 ( A q m = b ) ( A \mathbf{q}_m = \mathbf{b} ) (Aqm=b),得到控制点 ( q m ) ( \mathbf{q}_m ) (qm)
  • 代码中
    • 使用 Eigen 库的 QR 分解方法分别求解 ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z) 方向上的控制点坐标。

(四)结果

  • 数学上
    • 通过控制点 ( q m ) ( \mathbf{q}_m ) (qm) 定义了 B-spline 曲线的形状,使其满足给定的轨迹点和边界条件。
  • 代码中
    • 将求解得到的控制点组合成矩阵 ctrl_pts,用于后续的 B-spline 曲线生成与应用。

五、附加说明

(一)B-spline 基函数

B-spline 基函数是一组分段多项式函数,具有局部支撑性和良好的数值稳定性。这意味着它们仅在局部区间内有非零值,使得曲线在局部区域内的修改不会影响远处的曲线形状,从而提供了灵活且易于控制的曲线生成方式。

(二)控制点的作用

控制点不一定位于曲线上,但它们决定了曲线的形状和方向。通过调整控制点的位置,可以精确控制 B-spline 曲线的走势,为用户提供了强大的曲线形状控制能力。

(三)边界条件的重要性

在实际应用中,如路径规划、动画制作等,除了希望曲线通过特定的点外,还常常需要控制曲线在起点和终点的速度与加速度,以确保运动的平滑性和可控性,边界条件的引入能够让生成的 B-spline 曲线更好地满足实际需求。

(四)线性方程组的求解

求解控制点的过程实际上是在优化问题中寻找一组控制点,使得 B-spline 曲线尽可能满足所有的约束条件。选择合适的求解方法(如 QR 分解)可以提高计算效率和结果的稳定性,避免直接求逆带来的数值不稳定性。

六、结论

通过上述步骤,从离散的轨迹点和边界条件出发,通过构造矩阵 ( A ) ( A ) (A) 和向量 ( b ) ( \mathbf{b} ) (b),求解线性方程组,最终得到 B-spline 曲线的控制点。这一过程不仅保证了曲线经过所有指定的轨迹点,还满足了起点和终点的速度与加速度要求,生成了一条平滑且符合预期运动特性的 B-spline 曲线,为多个领域提供了一种高效且灵活的曲线生成与控制手段。

以下是实现上述功能的 C++ 代码:

/*----------------------------------转化成为 B 样条的控制点---------------------------------*/
#include <iostream>
#include <vector>
#include <Eigen/Dense>

class NonUniformBspline {
public:
    void parameterizeToBspline(const double& ts, const std::vector<Eigen::Vector3d>& point_set,
                           const std::vector<Eigen::Vector3d>& start_end_derivative,
                           Eigen::MatrixXd& ctrl_pts) {
        if (ts <= 0) {
            std::cout << "[B-spline]:time step error." << std::endl;
            return;
        }

        if (point_set.size() < 2) {
            std::cout << "[B-spline]:point set have only " << point_set.size() << " points." << std::endl;
            return;
        }

        if (start_end_derivative.size()!= 4) {
            std::cout << "[B-spline]:derivatives error." << std::endl;
        }

        int K = point_set.size();

        // write A
        Eigen::Vector3d prow(3), vrow(3), arow(3);
        prow << 1, 4, 1;
        vrow << -1, 0, 1;
        arow << 1, -2, 1;

        Eigen::MatrixXd A = Eigen::MatrixXd::Zero(K + 4, K + 2);

        for (int i = 0; i < K; ++i) 
            A.block(i, i, 1, 3) = (1 / 6.0) * prow.transpose();

        A.block(K, 0, 1, 3)         = (1 / 2.0 / ts) * vrow.transpose();
        A.block(K + 1, K - 1, 1, 3) = (1 / 2.0 / ts) * vrow.transpose();

        A.block(K + 2, 0, 1, 3)     = (1 / ts / ts) * arow.transpose();
        A.block(K + 3, K - 1, 1, 3) = (1 / ts / ts) * arow.transpose();
        std::cout << "A:\n" << A << std::endl;

        // A.block(0, 0, K, K + 2) = (1 / 6.0) * A.block(0, 0, K, K + 2);
        // A.block(K, 0, 2, K + 2) = (1 / 2.0 / ts) * A.block(K, 0, 2, K + 2);
        // A.row(K + 4) = (1 / ts / ts) * A.row(K + 4);
        // A.row(K + 5) = (1 / ts / ts) * A.row(K + 5);

        // write b
        Eigen::VectorXd bx(K + 4), by(K + 4), bz(K + 4);
        for (int i = 0; i < K; ++i) {
            bx(i) = point_set[i](0);
            by(i) = point_set[i](1);
            bz(i) = point_set[i](2);
        }

        for (int i = 0; i < 4; ++i) {
            bx(K + i) = start_end_derivative[i](0);
            by(K + i) = start_end_derivative[i](1);
            bz(K + i) = start_end_derivative[i](2);
        }

        // solve Ax = b
        Eigen::VectorXd px = A.colPivHouseholderQr().solve(bx);
        Eigen::VectorXd py = A.colPivHouseholderQr().solve(by);
        Eigen::VectorXd pz = A.colPivHouseholderQr().solve(bz);

        // convert to control pts
        ctrl_pts.resize(K + 2, 3);
        ctrl_pts.col(0) = px;
        ctrl_pts.col(1) = py;
        ctrl_pts.col(2) = pz;

        std::cout << "[B-spline]: parameterization ok." << std::endl;//B 样条控制点
    }
};

代码解释

  • 该代码定义了一个名为 NonUniformBspline 的类,其中包含一个成员函数 parameterizeToBspline
  • 首先,代码会检查输入参数 ts 的合法性,确保其大于 0,以及 point_set 中至少有 2 个点,start_end_derivative 的大小为 4。
  • 然后,定义了用于位置、速度和加速度约束的向量 prowvrowarow,并初始化矩阵 A 为零矩阵。
  • 通过循环和矩阵块操作将不同约束信息存储在矩阵 A 中,将轨迹点和边界条件存储在向量 bxbybz 中。
  • 使用 colPivHouseholderQr 分解方法求解线性方程组得到控制点在 ( x ) ( x ) (x) ( y ) ( y ) (y) ( z ) ( z ) (z) 方向上的坐标,并存储在 pxpypz 中。
  • 最后将这些控制点存储在 ctrl_pts 矩阵中,完成 B-spline 控制点的生成。

使用该代码时,需注意输入参数的合法性,确保 ts 合理,point_set 包含足够的轨迹点,start_end_derivative 包含正确的边界条件信息。

此代码为 B-spline 曲线的控制点生成提供了一个有效的实现,可用于生成平滑且满足边界条件的曲线,为相关领域的应用提供了实用的工具。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Linux系统用中挂载和管理磁盘分区

在 Linux 系统中&#xff0c;挂载和管理磁盘分区是系统管理的基本任务之一。以下是详细步骤&#xff0c;帮助你完成这一过程。 1. 查看现有磁盘和分区 首先&#xff0c;使用以下命令来查看系统中的磁盘和分区&#xff1a; bash 复制 lsblk或者使用&#xff1a; bash 复制…

Opencv图片的旋转和图片的模板匹配

图片的旋转和图片的模板匹配 目录 图片的旋转和图片的模板匹配1 图片的旋转1.1 numpy旋转1.1.1 函数1.1.2 测试 1.2 opencv旋转1.2.1 函数1.2.2 测试 2 图片的模板匹配2.1 函数2.2 实际测试 1 图片的旋转 1.1 numpy旋转 1.1.1 函数 np.rot90(kl,k1)&#xff0c;k1逆时针旋转9…

【YOLOv8杂草作物目标检测】

YOLOv8杂草目标检测 算法介绍模型和数据集下载 算法介绍 YOLOv8在禾本科杂草目标检测方面有显著的应用和效果。以下是一些关键信息的总结&#xff1a; 农作物幼苗与杂草检测系统&#xff1a;基于YOLOv8深度学习框架&#xff0c;通过2822张图片训练了一个目标检测模型&#xff…

vue3 react使用高德离线地图,最新解决内网情况首次不能加载离线地图2025年1月10日

下载离线资源 下载地址 https://download.csdn.net/download/u010843503/90234612 2、部署私有化瓦片资源 ngxin中配置如下 server{listen 18082;server_name localhost;location / {root D:/GisMap/_alllayers;#try_files $uri $uri/ /index.html;#index index.html;} }下载…

Hbuilder ios 离线打包sdk版本4.36,HbuilderX 4.36生成打包资源 问题记录

1、打包文档地址https://nativesupport.dcloud.net.cn/AppDocs/usesdk/ios.html#%E9%85%8D%E7%BD%AE%E5%BA%94%E7%94%A8%E7%89%88%E6%9C%AC%E5%8F%B7 2、配置应用图标 如果没有appicon文件&#xff0c;此时找到 Assets.xcassets 或者 Images.xcassets(看你sdk引入的启动文件中…

Unity中 Xlua使用整理(二)

1.Xlua的配置应用 xLua所有的配置都支持三种方式&#xff1a;打标签&#xff1b;静态列表&#xff1b;动态列表。配置要求&#xff1a; 列表方式均必须是static的字段/属性 列表方式均必须放到一个static类 建议不用标签方式 建议列表方式配置放Editor目录&#xff08;如果是H…

【计算机网络】课程 实验二 交换机基本配置和VLAN 间路由实现

实验二 交换机基本配置和VLAN 间路由实现 一、实验目的 1&#xff0e;了解交换机的管理方式。 2&#xff0e;掌握通过Console接口对交换机进行配置的方法。 3&#xff0e;掌握交换机命令行各种模式的区别&#xff0c;能够使用各种帮助信息以及命令进行基本的配置。 4&…

【数据结构:前缀树Trie】

目录 前言前缀树介绍和应用一、前缀树的定义前缀树的问题和思考前缀树的映射思想前缀树三大性质 二.前缀树节点结构三. 前缀树接口介绍和实现四个接口API1. insert(String word)2. search(String word)3. startsWith(String pre)4. delete(String word) API实现1. 查询操作sear…

Jenkins触发器--在其他项目执行后构建

前言&#xff1a; jenkins中有多种触发器可用&#xff0c;可以方便的控制构建的启动 这里简单介绍下项目后构建的配置方法 1. 解释&#xff1a; Build after other projects are built Set up a trigger so that when some other projects finish building, a new build is…

Linux(18)——提高命令行运行效率

目录 一、创建和执行 shell 脚本&#xff1a; 1、命令解释器&#xff1a; 2、执行 Bash Shell 脚本&#xff1a; 3、从 shell 脚本提供输出&#xff1a; 二、对特殊字符加引号&#xff1a; 1、反斜杠 &#xff08;\&#xff09;&#xff1a; 2、单引号 &#xff08; &…

软件系统安全逆向分析-混淆对抗

1. 概述 在一般的软件中&#xff0c;我们逆向分析时候通常都不能直接看到软件的明文源代码&#xff0c;或多或少存在着混淆对抗的操作。下面&#xff0c;我会实践操作一个例子从无从下手到攻破目标。 花指令对抗虚函数表RC4 2. 实战-donntyousee 题目载体为具有漏洞的小型软…

计算机网络 (33)传输控制协议TCP概述

一、定义与基本概念 TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在OSI模型的第四层&#xff0c;即传输层&#xff0c;为用户提供可靠的、有序的和无差错的数据传输服务。TCP协议与UDP协议是传输层的两大主要协议&#xff0c;但两者在设计上有明显的不同&…

【从0带做】基于Springboot3+Vue3的高校食堂点餐系统

大家好&#xff0c;我是武哥&#xff0c;最近给大家手撸了一个基于SpringBoot3Vue3的高校食堂点餐系统&#xff0c;可用于毕业设计、课程设计、练手学习&#xff0c;系统全部原创&#xff0c;如有遇到网上抄袭站长的&#xff0c;欢迎联系博主~ 详细介绍 https://www.javaxm.c…

一文说清dockerfile编写

docker用的时间比较久了&#xff0c;关于怎样把jar打成镜像&#xff0c;怎样基于已有mysql镜像添加额外初始化后封装成新的镜像&#xff0c;进行简单的说明。 1.jar封装镜像 from centos # 设置本地为中文&#xff0c;解决中文乱码问题 RUN localedef -i zh_CN -f UTF-8 zh_CN…

基于Python实现的通用小规模搜索引擎

基于Python实现的通用小规模搜索引擎 1.项目简介 1.1背景 《信息内容安全》网络信息内容获取技术课程项目设计 一个至少能支持10个以上网站的爬虫程序&#xff0c;且支持增量式数据采集;并至少采集10000个实际网页;针对采集回来的网页内容&#xff0c; 能够实现网页文本的分…

ssm旅游攻略网站设计+jsp

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 需要源码或者定制看文章最下面或看我的主页 目 录 目 录 III 1 绪论 1 1.1 研究背景 1 1.2 目的和意义 1 1.3 论文结构安排 2 2 相关技术 3 2.1 SSM框架介绍 3 2.2 B/S结构介绍 3 …

算法提高 图形输出

时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 512MB&#xff0c;其他语言 1024MB 难度&#xff1a;困难 分数&#xff1a;100 OI排行榜得分&#xff1a;14(0.1*分数2*难度) 描述 编写一程序&#xff0c;在屏幕上输出如下内容&#xff1…

[程序设计]—代理模式

[程序设计]—代理模式&#x1f473; 本文章记录学习于——52.面向切面&#xff1a;AOP-场景模拟_哔哩哔哩_bilibili 最近闲来无事&#xff0c;在学习Spring的源码&#xff1a; 后面慢慢更新源码系列blog&#xff0c;希望多多关注&#x1f64f;&#x1f64f; 目前已经总结的b…

ue5玩家角色添加武器。切换武器位置,手上武器放到背上。演示一下人体插槽和武器的连接。仅仅演示,实际项目不是这么用的

把第一人称资源包导进来 这就是我们枪的骨骼网格体 我们找到这个骨骼 右手添加插槽 取个名字 因为武器上也有动画&#xff0c;所有武器单独写个蓝图类 新建一个蓝图类 BP_Weapon 把枪的蓝图拖到人的静态网格体下&#xff0c;成为一个部分 选中BP_Weapon的父类套接字…

如何选择适合的证件照制作软件,让您的照片制作更轻松

在当今数字化的时代&#xff0c;制作证件照不再需要专门前往照相馆。选择一款合适的证件照制作软件&#xff0c;您可以在家中轻松完成标准证件照的拍摄与制作。然而&#xff0c;面对市面上琳琅满目的软件&#xff0c;找到最适合您需求的软件并不简单。本文将为您详细介绍选择证…