从0到1:C++ 开启游戏开发奇幻之旅(一)

news2025/1/30 8:25:40

目录

为什么选择 C++ 进行游戏开发

性能卓越

内存管理精细

跨平台兼容性强

搭建 C++ 游戏开发环境

集成开发环境(IDE)

Visual Studio

CLion

图形库

SDL(Simple DirectMedia Layer)

SFML(Simple and Fast Multimedia Library)

C++ 游戏开发基础知识

面向对象编程(OOP)

内存管理

C++ 标准库(STL)


为什么选择 C++ 进行游戏开发

在游戏开发的广袤领域中,编程语言的选择犹如基石,奠定着整个项目的成败与走向。C++ 凭借其卓越的特性,在众多编程语言中脱颖而出,成为游戏开发的中流砥柱,深受开发者们的青睐。

性能卓越

游戏,尤其是大型 3A 游戏,对性能有着近乎苛刻的要求。每一次画面的渲染、每一个物理效果的模拟、每一次玩家操作的响应,都需要在极短的时间内完成,以确保游戏的流畅运行和玩家的沉浸体验。C++ 在性能方面的表现堪称惊艳。它允许开发者直接对内存进行操作,这意味着可以精准地控制数据的存储和读取位置,大大提高了数据的访问速度。在处理大量的图形数据、物理模拟数据以及音频数据时,这种直接内存操作的能力显得尤为关键。例如,在渲染一个复杂的 3D 游戏场景时,C++ 能够快速地将顶点数据、纹理数据等传递给图形处理器(GPU),从而实现高效的图形渲染,使得游戏画面更加细腻、逼真,帧率更加稳定。

不仅如此,C++ 的代码经过编译后,会直接转化为机器码,运行时无需解释器或虚拟机的介入,这使得它的执行速度大幅提升。与一些需要在虚拟机环境中运行的编程语言相比,C++ 能够更充分地利用计算机硬件的性能,减少了额外的性能开销。这种高效的执行速度,让游戏能够以更高的帧率运行,为玩家带来更加流畅、丝滑的游戏体验。在激烈的游戏对战中,每一帧的流畅都可能影响着玩家的操作和决策,C++ 的高性能无疑为游戏的竞技性和娱乐性提供了坚实的保障。

内存管理精细

内存管理是游戏开发中至关重要的一环。游戏在运行过程中,需要动态地分配和释放大量的内存,以存储各种游戏资源,如角色模型、场景地图、纹理贴图等。如果内存管理不善,就会导致内存泄漏、内存碎片化等问题,进而影响游戏的性能和稳定性,甚至可能导致游戏崩溃。C++ 赋予了开发者手动管理内存的能力,通过 new 和 delete 操作符(或者 malloc 和 free 函数),开发者可以精确地控制内存的分配和释放时机。这对于游戏开发来说意义重大。在创建和销毁游戏对象(如角色、道具等)时,能够准确地分配和回收内存,避免了内存的浪费和泄漏。在一个大型的多人在线游戏中,同时存在着大量的玩家角色和游戏道具,精细的内存管理可以确保游戏在长时间运行过程中,内存的使用始终保持在合理的范围内,保证游戏的稳定运行。

此外,C++ 还提供了智能指针(如 std::shared_ptr、std::unique_ptr 等)等工具,帮助开发者更方便地管理内存,减少手动管理内存带来的错误风险。智能指针能够自动跟踪对象的引用计数,当对象不再被使用时,自动释放其所占用的内存,有效地避免了内存泄漏的问题。在处理复杂的游戏对象层次结构和资源管理时,智能指针的使用可以大大简化代码,提高代码的可读性和可维护性。

跨平台兼容性强

在当今多元化的游戏市场中,跨平台开发已成为游戏开发者们必须面对的需求。玩家们使用着各种各样的设备和操作系统来玩游戏,包括 Windows、MacOS、Linux 等桌面操作系统,以及 iOS、Android 等移动操作系统,还有各种游戏主机。C++ 作为一种跨平台的编程语言,能够在多个操作系统和硬件平台上编译和运行。这意味着开发者可以使用相同的代码库,针对不同的平台进行简单的适配和优化,就能够创建出多个版本的游戏,大大减少了开发和维护的工作量。

许多流行的游戏引擎,如 Unreal Engine、Unity(底层部分也有 C++ 支持)、Cocos2d - x 等,都是基于 C++ 构建的。这些引擎提供了强大的跨平台支持,开发者可以借助这些引擎,轻松地将游戏移植到不同的平台上,实现一次开发,多平台发布。以 Unreal Engine 为例,它使用 C++ 作为核心编程语言,通过其提供的一系列工具和接口,开发者可以方便地将游戏部署到 PC、游戏机、移动设备等多种平台上,并且能够充分利用各个平台的硬件特性,为玩家提供一致的游戏体验。这种跨平台的能力,不仅扩大了游戏的受众范围,还提高了游戏的商业价值。

搭建 C++ 游戏开发环境

“工欲善其事,必先利其器”,搭建一个高效的开发环境是 C++ 游戏开发的首要任务。一个好的开发环境能够极大地提高开发效率,减少开发过程中的错误和麻烦。下面我们将详细介绍常用的开发工具和库,以及它们的安装和配置步骤。

集成开发环境(IDE)

Visual Studio

Visual Studio 是一款由微软开发的功能强大的集成开发环境,广泛应用于 Windows 平台下的 C++ 开发。它提供了丰富的功能,包括代码编辑、调试、智能感知、代码分析等,能够帮助开发者高效地编写和调试代码。

安装步骤如下:

  1. 进入 Visual Studio 官网 (https://visualstudio.microsoft.com/),选择下载 Windows 版,并选择 Community 2019 社区版本(社区版对于个人开发者和小型团队是免费的,功能也非常齐全,足以满足大多数游戏开发的需求)进行下载,保存软件到电脑中。
  1. 双击运行下载的安装文件,弹出安装界面,选择 “使用 C++ 的桌面开发” 这一选项,如需其他,自行勾选。点击安装按钮开始安装。安装时间会依据网速、电脑设备等因素而有所不同,在此期间可以稍作等待,或者做一些其他的事情。
  1. 安装成功后提示重启,点击重启完成剩余配置。
  1. 在开始菜单找到 Visual Studio 2019 并且点击运行,弹出初始界面,点击 “创建新项目” 按钮 ,如果出现 C++ 项目模板选项,证明安装成功,并可以创建 C++ 项目。
CLion

CLion 是一款由 JetBrains 开发的跨平台 C++ 集成开发环境,它以其智能的代码编辑、强大的调试功能和对多种构建系统的支持而受到开发者的喜爱。无论是在 Windows、MacOS 还是 Linux 系统上,CLion 都能提供一致的高效开发体验。

安装步骤如下:

  1. 首先,从 CLion 官网 (https://www.jetbrains.com/clion/download/) 下载 Clion 安装文件。
  1. 下载完成后,解压缩安装文件,并运行 clion.exe 文件(在 Windows 系统下)。
  1. 进入 Clion 的安装界面,可以选择自定义安装选项或者选择默认选项,然后点击 “Next” 按钮。
  1. 勾选用户协议并继续。
  1. 在安装选项中,可以选择安装路径、添加图标等信息,然后点击 “Install” 按钮。
  1. 安装完成后,进行一些配置才能使用 Clion。在 Clion 主界面中,点击 “File” 菜单,然后选择 “Settings” 选项;找到 “Build, Execution, Deployment” 选项,并选择 “CMake” 选项;在 “CMake” 选项中,设置 CMake 和编译器的路径,以及其他相关选项;设置完成后,点击 “Apply” 按钮保存更改;最后,在 Clion 中创建一个新项目,并将代码添加到项目中。

图形库

SDL(Simple DirectMedia Layer)

SDL 是一个跨平台的多媒体库,它提供了对音频、键盘、鼠标、游戏杆和图形硬件的低级访问,非常适合用于开发 2D 游戏和多媒体应用程序。SDL 的设计目标是简单易用,同时又具有足够的灵活性和强大的功能,能够满足不同类型游戏的开发需求。

安装步骤如下(以 Windows 系统为例):

  1. 从 SDL 官网 (https://www.libsdl.org/download-2.0.php) 下载对应操作系统版本的 SDL2 库。
  1. 解压文件,将下载的压缩包解压至任意位置,得到类似如下的目录结构:
SDL2-2.26.1/
├── include/
│   └── SDL2/
├── lib/
│   ├── x86/
│   │   ├── SDL2.dll
│   │   ├── SDL2main.lib
│   │   └── SDL2.lib
│   └── x64/
│       ├── SDL2.dll
│       ├── SDL2main.lib
│       └── SDL2.lib
└── share/
    └── doc/
        └── SDL2/
  1. 配置开发环境(以 Visual Studio 为例):打开 Visual Studio,创建一个新的 C++ 项目,在项目中右键单击 “项目名称”,选择 “属性”;在左侧选择 “C/C++” -> “常规”,在 “附加包含目录” 中添加 SDL2-2.26.1/include 目录;在左侧选择 “链接器” -> “常规”,在 “附加库目录” 中添加 SDL2-2.26.1/lib/x64(如果是 x86 项目,则选择 x86 目录)目录;在左侧选择 “链接器” -> “输入”,在 “附加依赖项” 中添加 SDL2.lib 和 SDL2main.lib;将 SDL2-2.26.1/lib/x64(或 x86)目录下的 SDL2.dll 文件复制到项目的输出目录(通常是 Debug 或 Release 文件夹)。
SFML(Simple and Fast Multimedia Library)

SFML 也是一个跨平台的 C++ 多媒体库,它提供了简单直观的 API,用于处理图形、音频、网络和窗口等功能。SFML 的优势在于其简洁易用的接口和丰富的文档,对于初学者来说是一个很好的选择,能够快速上手并开发出有趣的 2D 游戏。

安装步骤如下(以 Windows 系统为例):

  1. 从 SFML 官网 (https://www.sfml-dev.org/download/sfml/2.6.0/) 下载适用于 Visual Studio 的 SFML 库。
  1. 解压文件,将下载的压缩包解压至任意位置,得到类似如下的目录结构:
SFML-2.6.0/
├── include/
│   └── SFML/
├── lib/
│   ├── libsfml-graphics-d.lib
│   ├── libsfml-window-d.lib
│   ├── libsfml-system-d.lib
│   ├── libsfml-audio-d.lib
│   ├── libsfml-network-d.lib
│   ├── libsfml-graphics.lib
│   ├── libsfml-window.lib
│   ├── libsfml-system.lib
│   ├── libsfml-audio.lib
│   └── libsfml-network.lib
└── share/
    └── sfml/
        ├── cmake/
        └──...
  1. 配置开发环境(以 Visual Studio 为例):打开 Visual Studio,创建一个新的 C++ 项目,在项目中右键单击 “头文件”,选择 “添加现有项”,并选择 SFML-2.6.0/include 目录下的所有.hpp 文件,以添加 SFML 的头文件;在同样的位置右键单击 “源文件”,选择 “添加现有项”,并选择 SFML-2.6.0/lib 目录下与编译器相匹配的所有库文件(.lib 或.a)。例如,如果使用的是 64 位的编译器,就应该添加 SFML-2.6.0/lib/x64 下的所有库文件;在项目中右键单击,选择 “属性” 窗口。在左侧选择 “C/C++” -> “常规”,在 “附加包含目录” 中添加 SFML-2.6.0/include 目录;在左侧选择 “链接器” -> “常规”,在 “附加库目录” 中添加 SFML-2.6.0/lib 目录;在左侧选择 “链接器” -> “输入”,在 “附加依赖项” 中添加以下库文件(根据需要添加即可):sfml-graphics.lib、sfml-window.lib、sfml-system.lib、sfml-audio.lib、opengl32.lib、freetype.lib、winmm.lib、gdi32.lib ;如果使用的是静态库,应该添加 SFML_STATIC 编译器预处理器定义;将 SFML-2.6.0/bin 目录下的所有.dll 文件复制到项目的输出目录(通常是 Debug 或 Release 文件夹)。

C++ 游戏开发基础知识

在 C++ 游戏开发的旅程中,掌握一些基础知识是开启成功之门的关键。这些知识构成了游戏开发的基石,无论是小型的休闲游戏,还是大型的 3A 游戏,都离不开它们的支撑。下面,让我们深入探讨面向对象编程、内存管理以及 C++ 标准库在游戏开发中的重要性和应用。

面向对象编程(OOP)

面向对象编程(OOP)是 C++ 的核心特性之一,它为游戏开发带来了极大的便利和灵活性。OOP 主要包含封装、继承、多态这几个概念。

封装,就像是给游戏中的各种元素加上了一个 “保护壳”。在游戏中,每个角色都有自己的生命值、攻击力、防御力等属性,以及移动、攻击、防御等行为。通过封装,我们可以将这些属性和行为组合在一起,形成一个类。以《英雄联盟》为例,英雄 “艾克” 就是一个类的实例,他的生命值、攻击力等属性被封装在这个类中,而他的技能 “时间卷曲器”“相位俯冲” 等行为也通过类的成员函数来实现。外部代码不能直接访问这些属性,只能通过类提供的公共方法来操作,这就保证了数据的安全性和一致性。比如,其他玩家不能直接修改 “艾克” 的生命值,只能通过攻击等合法的游戏行为来影响他的生命值,这样就避免了数据被随意篡改,保证了游戏的公平性和稳定性。

继承,则是实现代码复用的重要手段。在游戏中,有很多相似的对象,比如不同类型的怪物,它们可能都有一些共同的属性和行为,如生命值、攻击力、移动等。我们可以创建一个基类,包含这些共同的属性和行为,然后让不同类型的怪物类继承这个基类。以《暗黑破坏神》系列游戏为例,游戏中有各种恶魔和怪物,它们都继承自一个 “怪物” 基类。“沉沦魔” 类继承自 “怪物” 基类,它不仅拥有 “怪物” 基类的基本属性和行为,还可以有自己特有的属性和行为,比如更灵活的移动方式、特殊的攻击技能等。通过继承,我们可以减少代码的重复编写,提高开发效率。当我们需要修改怪物的一些基本属性时,只需要在基类中进行修改,所有继承自这个基类的怪物类都会自动继承这些修改,大大方便了代码的维护和扩展。

多态,是指同一个操作作用于不同的对象,可以产生不同的结果。在游戏中,多态的应用非常广泛。以《鬼泣》系列游戏为例,游戏中的角色 “但丁” 可以使用不同的武器,如大剑、双枪等。每个武器都有自己的攻击方式和效果,但是我们可以通过一个统一的接口来调用这些武器的攻击方法。当 “但丁” 使用大剑时,调用的是大剑的攻击方法,产生大剑的攻击效果;当他使用双枪时,调用的是双枪的攻击方法,产生双枪的攻击效果。这就是多态的体现,它使得代码更加灵活和可扩展。当我们需要添加新的武器时,只需要创建一个新的武器类,实现统一的攻击接口,就可以轻松地将新武器融入游戏中,而不需要大量修改现有的代码。

内存管理

在 C++ 游戏开发中,内存管理是一个至关重要的环节,它直接影响着游戏的性能和稳定性。C++ 提供了两种主要的内存分配方式:堆栈内存和堆内存,它们各有特点和适用场景。

栈内存是一种自动分配和释放的内存区域,它的分配和释放速度非常快,就像在一个栈中放入和取出物品一样简单高效。函数的局部变量和函数调用信息通常存储在栈上。当函数被调用时,局部变量会在栈上自动分配内存空间;当函数执行结束时,这些局部变量所占用的内存会自动被释放。在一个简单的游戏函数中,比如计算角色移动距离的函数:

void calculateMoveDistance() {
    int speed = 5; // 局部变量speed存储在栈上
    int time = 10; // 局部变量time存储在栈上
    int distance = speed * time; // 局部变量distance存储在栈上
    // 函数执行结束,speed、time、distance所占用的栈内存自动释放
}

栈内存的优点是速度快、效率高,但是它的大小是有限的,并且变量的生命周期与函数的作用域紧密相关。如果在栈上分配过多的内存,或者在函数中创建了大型的局部数组,可能会导致栈溢出,从而使程序崩溃。

堆内存则是用于动态分配内存的区域,它由程序员手动控制内存的分配和释放。在游戏中,当我们需要创建一些动态的对象,如角色、道具等,并且这些对象的生命周期需要根据游戏的运行情况来动态管理时,就需要使用堆内存。以创建一个游戏角色为例:

class Character {
public:
    int health;
    int attackPower;
    // 其他属性和方法
};

int main() {
    Character* character = new Character(); // 在堆上分配内存创建Character对象
    character->health = 100;
    character->attackPower = 20;
    // 使用character对象
    delete character; // 手动释放堆内存,避免内存泄漏
    return 0;
}

堆内存的优点是可以根据需要动态分配内存大小,灵活性高,但是它的分配和释放过程相对复杂,需要程序员手动操作,并且速度比栈内存慢。如果在堆上分配了内存,但是在使用完毕后没有及时释放,就会导致内存泄漏,随着游戏的运行,内存占用会越来越高,最终可能导致游戏卡顿甚至崩溃。

为了更好地管理堆内存,C++11 引入了智能指针(如std::shared_ptr、std::unique_ptr等)。智能指针能够自动跟踪对象的引用计数,当对象不再被使用时,自动释放其所占用的内存,有效地避免了内存泄漏的问题。以std::shared_ptr为例:

#include <memory>
#include <iostream>

class Character {
public:
    int health;
    int attackPower;
    Character() : health(100), attackPower(20) {}
    ~Character() { std::cout << "Character destroyed" << std::endl; }
};

int main() {
    std::shared_ptr<Character> character = std::make_shared<Character>();
    // 使用character对象
    // 当character离开作用域时,其引用计数降为0,自动释放所指向的Character对象的内存
    return 0;
}

在这个例子中,std::shared_ptr会自动管理Character对象的生命周期,当character离开作用域时,它所指向的Character对象的内存会被自动释放,无需手动调用delete。智能指针的使用大大简化了内存管理,提高了代码的安全性和可维护性。

C++ 标准库(STL)

C++ 标准库(STL)是 C++ 编程中的强大工具集,它提供了丰富的容器和算法,在游戏开发中有着广泛的应用,能够大大提高开发效率。

STL 中的容器是存储和管理数据的重要工具,其中vector、list、map是比较常用的容器。vector是一个动态数组,它允许在运行时动态地调整大小,并且支持快速的随机访问。在游戏中,vector常用于存储游戏对象的集合,如游戏中的角色列表、道具列表等。以一个简单的射击游戏为例,我们可以使用vector来存储所有的敌人:

#include <vector>
#include <iostream>

class Enemy {
public:
    int health;
    int attackPower;
    Enemy() : health(50), attackPower(10) {}
};

int main() {
    std::vector<Enemy> enemies;
    for (int i = 0; i < 10; i++) {
        enemies.push_back(Enemy());
    }
    // 访问和操作enemies中的敌人
    for (size_t i = 0; i < enemies.size(); i++) {
        std::cout << "Enemy " << i << " health: " << enemies[i].health << std::endl;
    }
    return 0;
}

在这个例子中,vector可以方便地添加和访问敌人对象,并且在需要时自动调整大小,非常适合存储动态变化的游戏对象集合。

list是一个双向链表,它的特点是在链表的任何位置进行插入和删除操作都非常高效,但是不支持随机访问。在游戏中,当需要频繁地插入和删除元素时,list就派上了用场。比如在一个实时策略游戏中,单位的创建和销毁非常频繁,我们可以使用list来存储游戏中的单位:

#include <list>
#include <iostream>

class Unit {
public:
    int health;
    int attackPower;
    Unit() : health(100), attackPower(20) {}
};

int main() {
    std::list<Unit> units;
    units.push_back(Unit()); // 在链表尾部插入一个单位
    units.push_front(Unit()); // 在链表头部插入一个单位
    auto it = units.begin();
    ++it; // 移动到第二个单位
    units.insert(it, Unit()); // 在第二个单位之前插入一个单位
    // 删除单位
    it = units.begin();
    units.erase(it); // 删除第一个单位
    // 遍历list
    for (const auto& unit : units) {
        std::cout << "Unit health: " << unit.health << std::endl;
    }
    return 0;
}

在这个例子中,list的高效插入和删除操作使得它非常适合处理游戏中频繁变化的单位集合。

map是一个关联容器,它存储的是键值对,并且会根据键自动排序。在游戏中,map常用于存储需要根据某个键来查找对应值的数据,如游戏中的道具字典,我们可以使用道具的 ID 作为键,道具的详细信息作为值:

#include <map>
#include <string>
#include <iostream>

class Item {
public:
    std::string name;
    int value;
    Item(const std::string& n, int v) : name(n), value(v) {}
};

int main() {
    std::map<int, Item> items;
    items[1] = Item("Health Potion", 50); // 添加一个ID为1的道具
    items[2] = Item("Mana Potion", 30); // 添加一个ID为2的道具
    // 根据ID查找道具
    auto it = items.find(1);
    if (it!= items.end()) {
        std::cout << "Item name: " << it->second.name << ", value: " << it->second.value << std::endl;
    }
    return 0;
}

在这个例子中,map可以快速地根据道具 ID 查找到对应的道具信息,非常方便管理和查找游戏中的各种数据。

STL 中的算法也是游戏开发中不可或缺的工具,其中sort和find是比较常用的算法。sort算法用于对容器中的元素进行排序,在游戏中,经常需要对游戏对象进行排序,如根据角色的等级对角色进行排序,或者根据玩家的得分对玩家进行排名。以对玩家得分进行排序为例:

#include <vector>
#include <algorithm>
#include <iostream>

struct Player {
    std::string name;
    int score;
    Player(const std::string& n, int s) : name(n), score(s) {}
};

// 自定义比较函数,用于按得分从高到低排序
bool compareByScore(const Player& p1, const Player& p2) {
    return p1.score > p2.score;
}

int main() {
    std::vector<Player> players;
    players.push_back(Player("Alice", 80));
    players.push_back(Player("Bob", 90));
    players.push_back(Player("Charlie", 70));
    // 对玩家按得分进行排序
    std::sort(players.begin(), players.end(), compareByScore);
    // 输出排序后的玩家列表
    for (const auto& player : players) {
        std::cout << "Player: " << player.name << ", Score: " << player.score << std::endl;
    }
    return 0;
}

在这个例子中,std::sort算法可以方便地对玩家列表进行排序,使得玩家按照得分从高到低排列。

find算法用于在容器中查找指定的元素,在游戏中,经常需要查找某个特定的游戏对象,如查找某个 ID 对应的角色,或者查找某个位置的道具。以查找某个 ID 对应的角色为例:

#include <vector>
#include <algorithm>
#include <iostream>

class Character {
public:
    int id;
    std::string name;
    Character(int i, const std::string& n) : id(i), name(n) {}
};

int main() {
    std::vector<Character> characters;
    characters.push_back(Character(1, "Warrior"));
    characters.push_back(Character(2, "Mage"));
    characters.push_back(Character(3, "Rogue"));
    // 查找ID为2的角色
    auto it = std::find_if(characters.begin(), characters.end(), [](const Character& c) {
        return c.id == 2;
    });
    if (it!= characters.end()) {
        std::cout << "Found Character: " << it->name << std::endl;
    } else {
        std::cout << "Character not found" << std::endl;
    }
    return 0;
}

在这个例子中,std::find_if算法可以在characters容器中查找 ID 为 2 的角色,如果找到则输出角色的名字,否则输出提示信息。

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

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

相关文章

MongoDB平替数据库对比

背景 项目一直是与实时在线监测相关&#xff0c;特点数据量大&#xff0c;读写操作大&#xff0c;所以选用的是MongoDB。但按趋势来讲&#xff0c;需要有一款国产数据库可替代&#xff0c;实现信创要求。选型对比如下 1. IoTDB 这款是由清华大学主导的开源时序数据库&#x…

SCRM在企业私域流量与客户管理中的变革之路探索

内容概要 在当今数字化高速发展的时代&#xff0c;SCRM&#xff08;社交客户关系管理&#xff09;作为一种新的管理工具&#xff0c;正逐渐成为企业私域流量管理和客户关系维护的重要基石。它不仅仅是一种软件工具&#xff0c;更是一种整合客户数据和关系管理的全新思维方式。…

爱的魔力转圈圈,基于carsim与simulink模拟仰望u8原地调头

仰望U8原地转向的示意图如下&#xff0c;不动方向盘的情况下&#xff0c;车可以自己转圈圈&#xff1a; 原理也很简单&#xff0c;仰望u8是四轮驱动&#xff0c;四个轮子都单独由四个轮边电机驱动。主要我们将左右的车轮轮速控制成左右两边轮速相同&#xff0c;但是方向相反&am…

2025多目标优化创新路径汇总

多目标优化是当下非常热门且有前景的方向&#xff01;作为AI领域的核心技术之一&#xff0c;其专注于解决多个相互冲突的目标的协同优化问题&#xff0c;核心理念是寻找一组“不完美但均衡”的“帕累托最优解”。在实际中&#xff0c;几乎处处都有它的身影。 但随着需求场景的…

基于微信小程序的新闻资讯系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

vim如何解决‘’文件非法关闭后,遗留交换文件‘’的问题

过程描述&#xff1a; 由于我修改文件时&#xff08;一定得修改了文件&#xff0c;不做任何修改不会产生这个问题&#xff09;的非法关闭&#xff0c;比如直接关闭虚拟机&#xff0c;或者直接断开远程工具的远程连接&#xff0c;产生了以下遗留交换文件的问题&#xff1a; 点击…

vue3+elementPlus之后台管理系统(从0到1)(day4-完结)

面包屑 创建一个面包屑组件 将路由导入然后格式化map对象 key-value 将当前路由的key和value获取然后存入list数组中 遍历list数据&#xff0c;渲染内容 <!--BreadcrumbCom.vue--> <template><el-breadcrumb separator">"><el-breadcrum…

基于Flask的旅游系统的设计与实现

【Flask】基于Flask的旅游系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用Python作为后端开发语言&#xff0c;结合前端Bootstrap框架&#xff0c;为用户提供了丰富…

【公因数匹配——暴力、(质)因数分解、哈希】

题目 暴力代码&#xff0c;Acwing 8/10&#xff0c;官网AC #include <bits/stdc.h> using namespace std; const int N 1e610; vector<int> nums[N]; int main() {ios::sync_with_stdio(0);cin.tie(0);int n;cin >> n;for(int i 1; i < n; i){int x;ci…

WPS数据分析000010

基于数据透视表的内容 一、排序 手动调动 二、筛选 三、值显示方式 四、值汇总依据 五、布局和选项 不显示分类汇总 合并居中带标签的单元格 空单元格显示 六、显示报表筛选页

RabbitMQ 架构分析

文章目录 前言一、RabbitMQ架构分析1、Broker2、Vhost3、Producer4、Messages5、Connections6、Channel7、Exchange7、Queue8、Consumer 二、消息路由机制1、Direct Exchange2、Topic Exchange3、Fanout Exchange4、Headers Exchange5、notice5.1、备用交换机&#xff08;Alter…

Spring--SpringMVC使用(接收和响应数据、RESTFul风格设计、其他扩展)

SpringMVC使用 二.SpringMVC接收数据2.1访问路径设置2.2接收参数1.param和json2.param接收数据3 路径 参数接收4.json参数接收 2.3接收cookie数据2.4接收请求头数据2.5原生api获取2.6共享域对象 三.SringMVC响应数据3.1返回json数据ResponseBodyRestController 3.2返回静态资源…

git Bash通过SSH key 登录github的详细步骤

1 问题 通过在windows 终端中的通过git登录github 不再是通过密码登录了&#xff0c;需要本地生成一个密钥&#xff0c;配置到gihub中才能使用 2 步骤 &#xff08;1&#xff09;首先配置用户名和邮箱 git config --global user.name "用户名"git config --global…

基于51单片机和ESP8266(01S)、LCD1602、DS1302、独立按键的WiFi时钟

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、延时2、定时器03、串口通信4、DS13025、LCD16026、独立按键 四、主函数总结 系列文章目录 前言 之前做了一个WiFi定时器时钟&#xff0c;用八位数码管进行显示&#xff0c;但是定时器时钟的精度较低&#xff0…

机器学习 ---逻辑回归

逻辑回归是属于机器学习里面的监督学习&#xff0c;它是以回归的思想来解决分类问题的一种非常经典的二分类分类器。由于其训练后的参数有较强的可解释性&#xff0c;在诸多领域中&#xff0c;逻辑回归通常用作 baseline 模型&#xff0c;以方便后期更好的挖掘业务相关信息或提…

拟合损失函数

文章目录 拟合损失函数一、线性拟合1.1 介绍1.2 代码可视化1.2.1 生成示例数据1.2.2 损失函数1.2.3 绘制三维图像1.2.4 绘制等高线1.2.5 损失函数关于斜率的函数 二、 多变量拟合2.1 介绍2.2 代码可视化2.2.1 生成示例数据2.2.2 损失函数2.2.3 绘制等高线 三、 多项式拟合3.1 介…

银行卡三要素验证接口:方便快捷地实现银行卡核验功能

银行卡三要素验证API&#xff1a;防止欺诈交易的有力武器 随着互联网的发展&#xff0c;电子支付方式也越来越普及。在支付过程中&#xff0c;银行卡是最常用的支付工具之一。然而&#xff0c;在一些支付场景中&#xff0c;需要对用户的银行卡信息进行验证&#xff0c;以确保支…

利用JSON数据类型优化关系型数据库设计

利用JSON数据类型优化关系型数据库设计 前言 在关系型数据库中&#xff0c;传统的结构化存储方式要求预先定义好所有的列及其数据类型。 然而&#xff0c;随着业务的发展&#xff0c;这种设计可能会显得不够灵活&#xff0c;尤其是在需要扩展单个列的描述功能时。 JSON数据…

极简壁纸js逆向

首先抓包&#xff0c;翻页可以看到数据储存在该包 可以看到随着页面变化&#xff0c;只有current在变化 而且载荷都没有加密&#xff0c;看来不用js逆向了 爬取代码 import os import asyncio import aiohttp import jsonheaders {"accept": "application/j…

JAVA 接口、抽象类的关系和用处 详细解析

接口 - Java教程 - 廖雪峰的官方网站 一个 抽象类 如果实现了一个接口&#xff0c;可以只选择实现接口中的 部分方法&#xff08;所有的方法都要有&#xff0c;可以一部分已经写具体&#xff0c;另一部分继续保留抽象&#xff09;&#xff0c;原因在于&#xff1a; 抽象类本身…