SFML 小demo

news2024/11/27 7:36:09

文章目录

    • 项目搭建
    • 代码实现
      • main.cpp
      • object.h
      • snake.h
      • common.h
    • 使用 demo

做到最后的话其实就只是验证了以前自己的一个想法,但是没有做成一个真正的游戏,可以算是一个 demo 而已吧,没做游戏的界面和关卡,不过完成了核心显式机制和功能之后,其实再搭建其他的玩法数值倒是很容易的,只不过现在时间紧张,只做了最关键的可玩部分,也没有很好的封装代码,不过可以明显感觉到的是,自己比以前本科的时候更具有设计的思想了,整个框架的设计会比较清晰一些,更会考虑封装和防御性编程方面的事情。

本来是想把这个画布分成均等的网格然后做普通的贪吃蛇的,做着做着想着做好玩一点于是改成跟着鼠标移动的轨迹了……

在这里插入图片描述

项目搭建

不想重新像本科大一大二的时候一样用字符界面做黑框框的程序,所以使用一个比较好的容易上手的 2D 图像库就是很重要的事情了,一般来说分为以下几种选择:

  1. EasyX、SFML
  2. Cocos2D、SDL2
  3. OpenGL、DirectX

为了快速实现简易的项目验证,所以这里使用 SFML2.1 框架,并且重新写一个 CMakeLists.txt 来完成项目的构建,而不是使用我之前那个非常死板的 CMake 模板,为此踩了不少坑,因为 CMake 更新的也有点快,但是 CMake 的官方文档除了函数的参数解释之外,没有使用示例。这里先记录一下 CMakeLists.txt 的几个坑:

  1. CMake 的函数名是不区分大小写的,但是 CMake 的很多选项是只能大写,坑
  2. CMake 的 include_directories 不会包含最顶层的目录!从给定的目录往下开始,也就是说如果头文件都是放在 abc/ 目录下,那么 include_directories(abc/) 不能使得在代码中可以 include<abc/xxx.h> ,因为顶层目录是不包含的,如果想在引入头文件的时候带上相对路径,则头文件的根目录本身需要被 include_directories 之下,比如 include_directories<include/abc> ,也就是还需要多建立一层额外的包装目录
  3. VScode 的 C++ intellisense 基于 CMake 在配置过程中生成的一条指令来解决头文件和库目录的解析问题,所以如果 CMake 不输出这个 compile_commands.json 的话,C++ 的 intellisense 就无法正常高亮代码工作
  4. CMake 无法把外部预编译好的第三方动态库视作内部的 target 类型变量,也就是说不能 install 这些外部的动态库,当然解决办法也是有的,不过要求 CMake 的版本比较新,要达到 3.21 才有这个功能,那就是 INSTALL(IMPORTED_RUNTIME_ARTIFACTS) 如果不查文档不知道还要琢磨到什么时候,反正就是一直报错 ADD_LIBRARY(IMPORTED) 导入的外部库没有新建 target 类型变量,具体的原因和为什么 CMake 这么设计可以查看这篇 StackOverflow
  5. 为什么需要 install ?因为一般使用动态库的话,最终生成的可执行文件必须和动态库在同一个目录下才能正常运行调试
  6. FOREACH i RANGE n 不是常见编程语言那样理解的从 i 到 n-1 而真的是从 i 到 n ,问题 CMAKE 里面列表的下标又是从 0 开始的,所以如果通过这种取下标的方式同时遍历多个等长的序列的话会越界……只能傻傻地在循环中再添加一个 IF 判断 i 是不是到了 n ……

临时起意做这个项目主要是最近找实习投了一些 C++ 的岗位,但是最近因为都在写 Golang 有段时间没写 C++ 了,很多特性以及八股也都有点忘了,所以回过头来捡起 C++ 用一下吧,复习复习。另外越来越觉得智能指针的好用了,用过 Python 或者 Golang 这种高级语言带有自动垃圾回收(比如标记清楚法之类的),C++ 就不想手动写释放内存了,还是智能指针好用。

CMake 两个自己写的觉得比较有用的小函数是找外部库的路径和名字以及头文件所在的路径,返回结果列表,然后再对这些列表中的每个元素进行 INCLUDE_DIRECTORIES 或者 ADD_LIBRARY 之类的操作,很方便,这样就不用自己手动添加每个头文件路径和外部预编译动态库了。

FUNCTION(FIND_LIBS lib_dir suffix return_lib_name_list return_lib_path_list)
    UNSET(return_lib_name_list CACHE)
    UNSET(return_lib_path_list CACHE)

    FILE(
        GLOB lib_path_list
        ${lib_dir}/*.${suffix}
    )

    FOREACH(_lib_path ${lib_path_list})
        GET_FILENAME_COMPONENT(_lib_name ${_lib_path} NAME_WE)
        # MESSAGE("_lib_name ${_lib_name}")
        LIST(APPEND lib_name_list ${_lib_name})
    ENDFOREACH(_lib_path ${lib_path_list})

    # MESSAGE("lib_name_list ${lib_name_list}")
    # MESSAGE("lib_path_list ${lib_path_list}")
    SET(return_lib_name_list ${lib_name_list} PARENT_SCOPE)
    SET(return_lib_path_list ${lib_path_list} PARENT_SCOPE)
ENDFUNCTION()

FUNCTION(FIND_HDRS hdr_dir return_hdr_dir_list)
    UNSET(hdr_dir_list CACHE)
    SET(hdr_dir_list)
    FILE(
        GLOB_RECURSE hdr_path_list
        ${hdr_dir}/*.h
        ${hdr_dir}/*.hpp
    )
    SET(hdr_dir_list ${hdr_dir})
    FOREACH(_hdr_path ${hdr_path_list})
        GET_FILENAME_COMPONENT(_hdr_dir ${_hdr_path} PATH)
        LIST(APPEND hdr_dir_list ${_hdr_dir})
    ENDFOREACH(_hdr_path ${hdr_path_list})
    LIST(REMOVE_DUPLICATES hdr_dir_list)
    SET(return_hdr_dir_list ${hdr_dir_list} PARENT_SCOPE)
ENDFUNCTION()


# print list item
FUNCTION(PRINT_LIST list_item title prefix)
    IF(NOT list_item OR (list_item STREQUAL ""))
        RETURN()
    ENDIF()
    MESSAGE("┌────────────────── ${title}")
    FOREACH(item ${list_item})
        MESSAGE("│ ${prefix} ${item}")
    ENDFOREACH()
    MESSAGE("└──────────────────]\n")
ENDFUNCTION()

把这些 CMake 函数或者宏单独放到一个文件里然后通过 INCLUDE 来在根目录的 CMakeLists.txt 包含这些子文件以使用函数或者宏。

目录结构如下所示:

C:\USERS\FREDOM\DESKTOP\TEMP
├─.vscode
├─app
├─build
│  ├─.cmake
│  │  └─api
│  │      └─v1
│  │          ├─query
│  │          │  └─client-vscode
│  │          └─reply
│  ├─app
│  │  └─CMakeFiles
│  │      └─main.dir
│  ├─CMakeFiles
│  │  ├─3.27.0-rc4
│  │  │  ├─CompilerIdC
│  │  │  │  └─tmp
│  │  │  └─CompilerIdCXX
│  │  │      └─tmp
│  │  └─pkgRedirects
│  └─install
│      └─Debug
│          └─bin
├─cmake
├─include
│  └─SFML
│      ├─Audio
│      ├─Graphics
│      ├─Network
│      ├─System
│      └─Window
├─lib
└─src

PS:Windows 下自带的这个 tree 树形目录结构显式可以使用的参数太少了,不支持指定展开到第几级?输出的都是目录名,但是输出带文件名的话根本看不完

项目根目录的 CMakeLists.txt 如下:

CMAKE_MINIMUM_REQUIRED(VERSION 3.21)
PROJECT(temp)

MESSAGE("build system: ${CMAKE_SYSTEM_NAME}")
MESSAGE("${CMAKE_CURRENT_BINARY_DIR}")
SET(CMAKE_CXX_STANDARD 17)
SET(CAMKE_C_STANDARD 17)

SET(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install/${CMAKE_BUILD_TYPE})
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) # binary executable
# set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) # shared library
# set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) # static library

INCLUDE(cmake/macro.cmake)

# help vscode find compiler_command.json
ADD_DEFINITIONS(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON)
# add_definitions(-DSFML_STATIC)

# 指定库目录
LINK_DIRECTORIES(lib)
# 包含头文件目录
FIND_HDRS(include return_hdr_dir_list)
PRINT_LIST("${return_hdr_dir_list}" "HDR_PATH" "")
FOREACH(_hdr_dir ${return_hdr_dir_list})
    INCLUDE_DIRECTORIES(${_hdr_dir})
ENDFOREACH(_hdr_dir ${return_hdr_dir_list})

FIND_LIBS(lib "dll" return_lib_name_list return_lib_path_list)
PRINT_LIST("${return_lib_name_list}" "LIB_NAME" "")
PRINT_LIST("${return_lib_path_list}" "LIB_PATH" "")


LIST(LENGTH return_lib_name_list num_lib)
FOREACH(i RANGE 0 ${num_lib})
    IF(${i} EQUAL ${num_lib})
        BREAK()
    ENDIF()
    LIST(GET return_lib_name_list ${i} _lib_name)
    LIST(GET return_lib_path_list ${i} _lib_path)
    # MESSAGE("${i} ${_lib_name} ${_lib_path}")
    ADD_LIBRARY(${_lib_name} SHARED IMPORTED)
    SET_PROPERTY(
        TARGET ${_lib_name}
        PROPERTY IMPORTED_LOCATION ${_lib_path}
    )
    # only for windows dll, that's suck
    SET_PROPERTY(
        TARGET ${_lib_name}
        PROPERTY IMPORTED_IMPLIB ${_lib_path}
    )
    INSTALL(
        IMPORTED_RUNTIME_ARTIFACTS ${_lib_name}
    )
ENDFOREACH()

# 移动动态库到 bin 文件同目录下
FOREACH(_lib ${sfmllibs})
    INSTALL(
        TARGETS ${sfmlibs}
        RUNTIME DESTINATION bin
    )
ENDFOREACH(_lib ${sfmllibs})

# 添加可执行文件
ADD_SUBDIRECTORY(app)

然后 app 目录下面的 CMakeList.txt 内容如下:

ADD_EXECUTABLE(main main.cpp)
TARGET_LINK_LIBRARIES(main ${return_lib_name_list})
INSTALL(TARGETS main RUNTIME DESTINATION bin)


# 链接库
# TARGET_LINK_LIBRARIES(
#     main
#     ${sfmllibs}
# )

不过后来发现其实 LINK_DIRECTORIES 不是很需要了,因为这次没有项目内部生成的库目标文件,都是外部动态库链接,不过为了模板具有一些普适性还是放着吧。在 VScode 里面使用快捷键 Ctrl+Shift+B 来执行构建,CMake 插件的配置过程输出如下:

[cmake] Not searching for unused variables given on the command line.
[cmake] build system: Windows
[cmake] C:/Users/fredom/Desktop/temp/build
[cmake] ┌────────────────── HDR_PATH
[cmake] │  include
[cmake] │  C:/Users/fredom/Desktop/temp/include/SFML
[cmake] │  C:/Users/fredom/Desktop/temp/include/SFML/Audio
[cmake] │  C:/Users/fredom/Desktop/temp/include/SFML/Graphics
[cmake] │  C:/Users/fredom/Desktop/temp/include/SFML/Network
[cmake] │  C:/Users/fredom/Desktop/temp/include/SFML/System
[cmake] │  C:/Users/fredom/Desktop/temp/include/SFML/Window
[cmake] │  C:/Users/fredom/Desktop/temp/include
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── LIB_NAME
[cmake] │  openal32
[cmake] │  sfml-audio-d-2
[cmake] │  sfml-graphics-d-2
[cmake] │  sfml-network-d-2
[cmake] │  sfml-system-d-2
[cmake] │  sfml-window-d-2
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── LIB_PATH
[cmake] │  C:/Users/fredom/Desktop/temp/lib/openal32.dll
[cmake] │  C:/Users/fredom/Desktop/temp/lib/sfml-audio-d-2.dll
[cmake] │  C:/Users/fredom/Desktop/temp/lib/sfml-graphics-d-2.dll
[cmake] │  C:/Users/fredom/Desktop/temp/lib/sfml-network-d-2.dll
[cmake] │  C:/Users/fredom/Desktop/temp/lib/sfml-system-d-2.dll
[cmake] │  C:/Users/fredom/Desktop/temp/lib/sfml-window-d-2.dll
[cmake] └──────────────────]
[cmake] 
[cmake] -- Configuring done (0.0s)
[cmake] -- Generating done (0.0s)
[cmake] -- Build files have been written to: C:/Users/fredom/Desktop/temp/build

这样一来就把 SFML 库的动态库和头文件目录都引入项目了。

代码实现

主要构想是:

  1. 画布分成均等的网格
  2. 每个网格亮起之后渐弱变暗直到完全变黑(这样比较好看……)
  3. 网格的亮度变化和可移动对象分开,计算可移动对象的位置,可移动对象移动的路径上重置网格的亮度

根据 SFML 官网的入门教程,其实画面的渲染可以做成单独线程的离屏渲染,这样性能会好很多,不过时间关系这里就没有跟着写了,其实本身代码的封装性也不强,一般来说这种小游戏 demo 怎么来说也会有个 runloop、start、stop、eventloop、drawloop 之类的分离模块。

main.cpp

#include <iostream>
#include <vector>

#include <common.h>
#include <SFML/Graphics.hpp>
#include <object.h>
#include <snake.h>

const int WIN_WID = 1000;
const int WIN_HEI = 1000;

const int NUM_ROW = 100;
const int NUM_COL = 100;

int main()
{
    // create the window
    std::shared_ptr<sf::RenderWindow> window = std::make_shared<sf::RenderWindow>(sf::VideoMode(WIN_WID, WIN_HEI), "snake");
    std::vector<ObjectDrawable> board;

    for (int i = 0; i < NUM_ROW; ++i)
    {
        for (int j = 0; j < NUM_COL; ++j)
        {
            ObjectDrawable newObj(window);
            newObj.setBoarder(WIN_WID/NUM_COL, WIN_HEI/NUM_ROW); // width, height
            newObj.setPosition(WIN_WID/NUM_COL*j, WIN_HEI/NUM_ROW*i);
            board.emplace_back(newObj);
        }
    }

    Snake snake;
    snake.extendBody();
    
    // run the program as long as the window is open
    sf::Clock clock;
    float deltaTime = 0.0;
    while (window->isOpen())
    {
        // calculate delta time elapsed
        deltaTime = clock.getElapsedTime().asSeconds();
        clock.restart();
        // std::cout << deltaTime << std::endl;

        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window->pollEvent(event))
        {
            // "close requested" event: we close the window
            switch (event.type)
            {
            case sf::Event::Closed:
                window->close();
                break;
            
            case sf::Event::KeyPressed:
                switch (event.key.code)
                {
                case sf::Keyboard::Up:
                    snake.changeHeadSpd(sf::Vector2f(0.0f, -5.0f));
                    break;
                case sf::Keyboard::Down:
                    snake.changeHeadSpd(sf::Vector2f(0.0f, +5.0f));
                    break;
                case sf::Keyboard::Right:
                    snake.changeHeadSpd(sf::Vector2f(+5.0f, 0.0f));
                    break;
                case sf::Keyboard::Left:
                    snake.changeHeadSpd(sf::Vector2f(-5.0f, 0.0f));
                    break;
                }
                break;

            case sf::Event::MouseButtonPressed:
                // board[event.mouseMove.y/(WIN_HEI/NUM_ROW) * NUM_COL + event.mouseMove.x/(WIN_WID/NUM_COL)].resetColor();
                snake.extendBody();
                break;
            
            case sf::Event::MouseMoved:
                const sf::Vector2f& headPos = snake.getHeadPos();
                float xdspd = (event.mouseMove.x - headPos.x) * 2e-2;
                float ydspd = (event.mouseMove.y - headPos.y) * 2e-2;
                snake.changeHeadSpd(sf::Vector2f(xdspd, ydspd));
                break;
            }
        }
        // clear the window with black color
        window->clear(sf::Color());


        // draw everything here...
        snake.update(deltaTime);
        for (const auto& item : snake.getBody())
        {
            sf::Vector2f itemPos = item->getPos();
            // std::cout << itemPos.x << ' ' << itemPos.y << std::endl;
            int boardIdx = int(itemPos.y)/(WIN_HEI/NUM_ROW) * NUM_COL + int(itemPos.x)/(WIN_WID/NUM_COL);
            if (boardIdx < 0) boardIdx = 0;
            boardIdx = boardIdx % (NUM_ROW * NUM_COL);
            board[boardIdx].resetColor();
        }

        for (ObjectDrawable& obj : board)
        {
            obj.update(deltaTime);
            obj.draw();
        }


        // end the current frame
        window->display();

        sf::sleep(sf::milliseconds(16));
    }

    return 0;
}

这里还有一个关于 C++ 的 switch 块的注意点,就是 case 中不能声明跨作用域的变量,不过由于我这里每个 case 分支里面的变量都是当前 case 使用的,没有跨 case 使用,所以编译没有报错(是这样吗?也可能是因为 MinGW 的编译支持这么做而已)。

object.h

#ifndef OBJECT_H
#define OBJECT_H

#include <common.h>
#include <algorithm>
#include <SFML/Graphics.hpp>

class ObjectDrawable
{
private:
    std::shared_ptr<sf::RenderWindow> renderWd;
    std::shared_ptr<sf::Shape> shape;
    sf::Color baseColor;
    sf::Color currColor;

    float xpos;
    float ypos;
    float brdx;
    float brdy;

public:
    ObjectDrawable(std::shared_ptr<sf::RenderWindow> _rdwp) : renderWd(_rdwp)
    {
        this->shape = std::make_shared<sf::RectangleShape>(sf::Vector2f(10.0f, 10.0f));
        // this->baseColor = sf::Color(
        //     150 + rand() % 100,
        //     150 + rand() % 100,
        //     150 + rand() % 100
        // );
        this->baseColor = sf::Color(50, 255, 50);
        this->shape->setFillColor(this->baseColor);
    }

    void setPosition(const float& _xpos, const float& _ypos)
    {
        this->xpos = _xpos;
        this->ypos = _ypos;
    }

    void setBoarder(const float& _brdx, const float& _brdy)
    {
        this->brdx = _brdx;
        this->brdy = _brdy;
    }

    void resetColor()
    {
        this->currColor = this->baseColor;
    }

    void draw()
    {
        this->shape->setPosition(this->xpos, this->ypos);
        this->shape->setFillColor(this->currColor);
        std::dynamic_pointer_cast<sf::RectangleShape>(this->shape)->setSize(sf::Vector2f(this->brdx, this->brdy));

        if (renderWd != nullptr)
            this->renderWd->draw(*(this->shape.get()));
    }

    void update(const float& dt)
    {
        this->currColor.r = std::max(0, int(this->currColor.r - dt * 100));
        this->currColor.g = std::max(0, int(this->currColor.g - dt * 100));
        this->currColor.b = std::max(0, int(this->currColor.b - dt * 100));
    }
};

class ObjectMovable
{
private:
    float xpos;
    float ypos;
    float xspd;
    float yspd;

public:
    ObjectMovable() {}
    ObjectMovable(
        const float& _xpos,
        const float& _ypos,
        const float& _xspd = 0.0f,
        const float& _yspd = 0.0f
    ) : xpos(_xpos), ypos(_ypos), xspd(_xspd), yspd(_yspd) {}

    ObjectMovable(
        const sf::Vector2f& _pos,
        const sf::Vector2f& _spd = sf::Vector2f(0.0f, 0.0f)
    ) : xpos(_pos.x), ypos(_pos.y), xspd(_spd.x), yspd(_spd.y) {}

    void update(const float& dt)
    {
        this->xpos += this->xspd * dt;
        this->ypos += this->yspd * dt;
    }

    void changePos(const float& _xpos, const float& _ypos)
    {
        this->xpos = _xpos;
        this->ypos = _ypos;
    }
    void changePos(const sf::Vector2f& _pos)
    {
        this->xpos = _pos.x;
        this->ypos = _pos.y;
    }
    sf::Vector2f getPos() const
    {
        return sf::Vector2f(this->xpos, this->ypos);
    }

    void changeSpd(const float& _xspd, const float& _yspd)
    {
        this->xspd = _xspd;
        this->yspd = _yspd;
    }
    void changeSpd(const sf::Vector2f& _spd)
    {
        this->xspd = _spd.x;
        this->yspd = _spd.y;
    }
    sf::Vector2f getSpd() const{
        return sf::Vector2f(this->xspd, this->yspd);
    }
};

#endif

Object 头文件一开始的设想是有一个纯虚基类 Object 的,但是没时间实现了,只想快速验证 demo 于是分开了两个类来写,实际上 update 这个函数应该是 Object 基类的一个空方法才对。

snake.h

#ifndef SNAKE_H
#define SNAKE_H

#include <common.h>
#include <object.h>

class Snake
{
private:
    std::vector<std::shared_ptr<ObjectMovable>> body;
    int bodyLen;

public:
    Snake()
    {
        this->body.emplace_back(
            std::make_shared<ObjectMovable>(
                0.0, 0.0, 0.0, 0.0
            )
        );
        this->bodyLen = this->body.size();
    }

    void extendBody()
    {
        this->body.emplace_back(
            std::make_shared<ObjectMovable>(
                this->body.back()->getPos(),
                this->body.back()->getSpd()
            )
        );
        bodyLen++;
    }

    void update(const float& dt)
    {
        this->body[0]->update(dt);

        for (int i = 1; i < this->bodyLen; ++i)
        {
            this->body[i]->changeSpd(
                this->body[i-1]->getPos() - this->body[i]->getPos()
            );
            this->body[i]->update(dt);
        }
    }

    void changeHeadSpd(const sf::Vector2f& dspd)
    {
        this->body[0]->changeSpd(
            this->body[0]->getSpd() + dspd
        );
    }

    const std::vector<std::shared_ptr<ObjectMovable>>& getBody() const {
        return this->body;
    }

    sf::Vector2f getHeadPos() {
        return this->body[0]->getPos();
    }
};

#endif

common.h

#ifndef COMMON_H
#define COMMON_H

#include <memory>
#include <random>

#include <vector>

#endif

使用 demo

  1. 鼠标移动来吸引可移动对象
  2. 鼠标左键添加可移动对象

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

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

相关文章

Day32 实现登录注册接口服务

​ 本章节,实现登录和注册接口服务 一.完善登录注册接口 完善登录和注册接口,对登录明文密码获取到MD5 字符串后,进行对比校验或注册明文密码进行MD5 加密后再插入到数据库。在MyToDo.Shared 项目中创建一个Extensions 文件夹,并创建一个 StringExtensions 静态扩展类,其中…

Redis学习(十二)Redis的三种删除策略

目录 一、背景二、Redis 的三种删除策略2.1 定时删除&#xff08;用CPU换内存空间&#xff09;2.2 定期删除2.3 惰性删除&#xff08;用内存换CPU性能&#xff09; 三、总结 一、背景 我们都知道 Redis 是一种内存数据&#xff0c;所有的数据均存储在内存中&#xff0c;可以通…

用于精准治疗和预防细菌感染的生物功能脂质纳米颗粒

引用信息 文 章&#xff1a;Biofunctional lipid nanoparticles for precision treatment and prophylaxis of bacterial infections. 期 刊&#xff1a;Science Advances&#xff08;影响因子&#xff1a;13.6&#xff09; 发表时间&#xff1a;2024年4月5日 作 者&a…

Deploy PostgreSQL12 for Centos 7

介绍 PostgreSQL是一种对象-关系型数据库管理系统&#xff0c;由加州大学伯克利分校计算机系开发&#xff0c;以POSTGRES&#xff0c;4.2版本为基础。 PostgreSQL支持大部分的SQL标准并且提供了很多其他现代特性&#xff0c;如复杂查询、外键、触发器、视图、事务完整性、多版…

C++ - 查找算法 和 其他 算法

目录 一. 查找算法&#xff1a; 1.顺序查找&#xff1a; 2.二分查找&#xff1a; 二. 其他算法&#xff1a; 1.遍历算法&#xff1a; 2.求和、求平均值等聚合算法。 a.求和算法&#xff1a; b.求平均值算法&#xff1a; 一. 查找算法&#xff1a; 1.顺序查找&#xff1…

【十大排序算法】冒泡排序

在排序的大海上&#xff0c;冒泡排序像一朵花朵般绽放&#xff0c; 每个元素都像是水珠&#xff0c;跃动在涟漪的波浪中。 它们轻轻上浮&#xff0c;与周围的元素相比较&#xff0c; 若自身更大&#xff0c;便悄然交换位置。 这是一场缓慢的舞蹈&#xff0c;每一步都小心翼翼&…

【工作流】 工作流相关概念及Activiti基本介绍

目录 工作流作用工作流的几个要素应用具体应用场景 工作流系统工作流系统的组成部分&#xff1a;都有哪些工作流系统 工作流引擎工作流引擎的特点&#xff1a;都有哪些工作流引擎 工作流、工作流引擎、工作流系统三者区别ActivitiActiviti的主要特点发展历史优缺点优点&#xf…

【Linux操作系统】进程状态(1)

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 Linux操作系统 进程状态 的相关内容。 如果看到最后您觉得这篇文章…

AXI Quad SPI IP核AXI4接口下的三种操作模式

当选择Enable Performance Mode选项时&#xff0c;AXI4接口包括在内。在该模式下&#xff0c;IP核可以在增强模式下操作&#xff08;未选择启用XIP模式&#xff09;或XIP模式&#xff08;选择启用XIP模式&#xff09;。在性能模式下&#xff0c;AXI4接口用于在DTR和DRR位置的突…

网络编程(UPD和TCP)

//发送数据 //UDP协议发送数据 package com.example.mysocketnet.a02UDPdemo;import java.io.IOException; import java.net.*;public class SendMessageDemo {public static void main(String[] args) throws IOException {//发送数据//1.创建DatagramSocket对象(快递公司)//…

机器学习-降维算法,PCK,LDA,NMF,LLE

目录 一:数据降维 二:PCA降维算法 1.概念 2.算法原理: 3.降维流程: 4.如何找到方差最大的方向 5.降维公式: 1.协方差和散度矩阵: 2.特征值分解矩阵原理 3.SVD分解矩阵原理 6.PCA算法的两种实现方法: 1.基于特征值分解协方差矩阵实现PCA算法 2. 基于SVD分解协…

面试成功的不二法门:详解Vue3答题章法

前言 面试题在网络上有如海洋之深&#xff0c;对于同一知识点&#xff0c;每个人的理解也各有千秋。我们在面试中常常会遇到一个瞬息间脑海里一片空白的情况&#xff0c;其实这并不是因为我们不懂&#xff0c;而是因为我们在回答的时候缺乏一个清晰的思路。那么问题来了&#x…

服务器远程连接工具有哪些?

【天联】是一款功能强大的服务器远程连接工具&#xff0c;它可以让用户通过网络远程连接到目标服务器&#xff0c;实现远程操作和管理。【天联】的使用场景非常广泛&#xff0c;特别适用于以下几个领域&#xff1a; 零售、收银软件应用的远程管理&#xff1a;【天联】可以结合医…

Ubuntu22.04之解决:terminal使用alt+1/alt+2/alt+3失效问题(二百三十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

我的名字叫大数据: 第7章 我的自拍展

7.1 生活瞬间:通过数据图像呈现 数据健身达人们!在经过一系列的辛勤锻炼后,是时候来看看我的“自拍展”了。通过数据图像,我们不仅可以更直观地了解数据,还能将复杂的信息以简单而美观的方式呈现出来。在这一节中,我将带你领略各种数据图像的魅力,从色彩缤纷的条形图到…

C#之EntityFramework的应用

目录 1&#xff0c;名词概述。 2&#xff0c;实体数据模型EDM介绍。 3&#xff0c;规范函数。 4&#xff0c;查看Linq转换成的SQL语句。 5&#xff0c;数据的增删改查。 5.1&#xff0c;数据查询 5.2&#xff0c;数据插入 5.3&#xff0c;数据更新 5.4&#xff0c;数据…

go语言接口之接口值

概念上讲一个接口的值&#xff0c;接口值&#xff0c;由两个部分组成&#xff0c;一个具体的类型和那个类型的值。它们 被称为接口的动态类型和动态值。对于像Go语言这种静态类型的语言&#xff0c;类型是编译期的概 念&#xff1b;因此一个类型不是一个值。在我们的概念模型中…

MySQL-权限管理(二)

一 host中的含义 /usr/local/mysql/bin/mysql -pLXYlxy2:024.#8u} -S /data/mysql/tmp/mysqld.sock select user,host,authentication_string from mysql.user; %:主要允许从任何主机连接到MySQL服务器&#xff0c;即外部连接localhost: 代表只允许本地主机连接到MySQL服务器&…

spring boot2.7.x遇到问题

validation报错 高版本已移除了validation以来&#xff0c;需手动添加 <dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId> </dependency>mybatis报错 升级版本 <dependency>&…

07-指针的概念与引用,索引

指针的概念与引用&#xff0c;索引 一、内存地址 字节&#xff1a; 定义&#xff1a; 字节&#xff08;byte&#xff09;是内存容量的一个单位&#xff0c;一个字节包含8个位&#xff08;bit&#xff09;。 地址&#xff1a; 定义&#xff1a; 内存地址是系统为了方便区分…