SDL2基本使用

news2025/2/23 23:25:42

前言

  在这里记录SDL的环境基本搭建和使用,方便回忆。使用该图形库也是为了方便在没有单片机和显示模块的使用,也能对简单验证些关于图形构建或界面管理的猜想和测试,所以下述不会探讨过于深入的东西。当然,也可以通过SDL官网查看介绍。


下载

在官网看到,SDL2的最新稳定版本为2.30.11,点击跳转到其GitHub下,下载SDL2-devel-2.30.11-mingw.zip

在这里插入图片描述


CLion中

这里我是在CLion创建Demo项目,并且将下载好的SDL2-devel-2.30.11-mingw.zip解压后放入,并且编写好CMakeLists.txt,对SDL2进行链接。具体CMake流程可以参考CMake。

在这里插入图片描述


SDL2的基本使用

  教程参考至SDL2官网维基中C++ Programming (thenumb.at),其目录 - SDL2章节下的8个基本教程,下述将基于C语言实现代码,且加上对应的注释。还有就是为了保证代码看起来清晰些,把创建是否成功的状态判断,都去掉了,默认成功创建窗口、表面、图形等对象。然后在没有看到SDL2的交互函数时,暂且先用while(1);,来保持绘制界面。除此之外,除了在第一个案例末尾演示内存释放流程外其它案例,也不在演示释放函数。

如果觉得下述讲解的太过累赘的,可以直接看上面的原教程链接。

  1. 创建窗口并显示出来的基本流程:

    调用SDL_Init()初始化SDL2,在通过SDL_CreateWindow()创建窗口对象win,在通过SDL_GetWindowSurface基于窗口对象创建表面对象winSurface。然后将要绘制的矩形通过SDL_FillRect()绘制到表面对象winSurface上,最后通过SDL_UpdateWindowSurface()更新窗口对象win,显示在界面上。在结束时,调用SDL_DestroyWindow()SDL_Quit()完成释放变量和关闭SDL2。

    表面:SDL 将您可以绘制的任何区域(包括加载的图像)抽象为“表面”

    #include <stdio.h>
    #include <stdlib.h>
    #include <SDL.h>
    #include <synchapi.h>
    
    int main(int argc, char* argv[]) {
        SDL_Init( SDL_INIT_EVERYTHING );    // 初始化SDL2所有部分
    
        SDL_Window* win = SDL_CreateWindow( "my window", 100, 100, 640, 480, SDL_WINDOW_SHOWN );    // 创建窗口
        SDL_Surface* winSurface = SDL_GetWindowSurface( win );   // 基于窗口创建“表面”
        SDL_UpdateWindowSurface( win );                          // 更新窗口
        SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 255, 90, 120 ));            // 绘制矩形
        SDL_UpdateWindowSurface( win ); // 更新窗口
        while (1);                              // 卡住界面(保持窗口,)
        SDL_DestroyWindow( win );       // 销毁窗口
        win = NULL; winSurface = NULL;         // 释放变量
        SDL_Quit();                            // 关闭SDL2
    
        return 0;
    }
    

    在这里插入图片描述

  2. 位图显示

    该节主要讲解了位图在SDL的保存、绘制和缩放等。同时相比于第1节教程多了SDL_Rect结构,来控制显示位置。

    #include <SDL.h>
    
    SDL_Window* win;
    SDL_Surface* winSurface;
    SDL_Rect dest;
    SDL_Surface* image1;
    SDL_Surface* image2;
    
    // 显示BMP图片
    void ShowBMP()
    {
        dest.x = 20; dest.y = 20;
        SDL_BlitSurface( image1, NULL, winSurface, &dest );  // 图片1加载到表面
        SDL_UpdateWindowSurface( win ); // 更新窗口
        while (1);                              // 卡住界面(保持窗口)
    }
    // 缩放BMP图片
    void ScaledBMP()
    {
        dest.x = 20; dest.y = 20; dest.w = 200; dest.h = 100;                      // 重新设置SDL_Rect结构对象
        SDL_BlitScaled( image1, NULL, winSurface, &dest );  // 缩放图片
        SDL_UpdateWindowSurface( win ); // 更新窗口
        while (1);                              // 卡住界面(保持窗口)
    }
    // 转换表面
    void ConvertSurface()
    {
        dest.x = 20; dest.y = 20;
        image2 = SDL_ConvertSurface( image1, winSurface->format, 0 ); // 将图片1进行转换
        SDL_BlitSurface( image2, NULL, winSurface, &dest );  // 图片2加载到表面
        SDL_FreeSurface(image1);                                             // 图片1随后释放
        SDL_UpdateWindowSurface( win ); // 更新窗口
        while (1);                              // 卡住界面(保持窗口)
    }
    
    int main(int argc, char* argv[]) {
    
        SDL_Init( SDL_INIT_EVERYTHING );                                                             // 初始化SDL2所有部分
        win = SDL_CreateWindow( "MyWindow", 100, 100, 640, 480, SDL_WINDOW_SHOWN ); // 创建窗口
        winSurface = SDL_GetWindowSurface( win );                                                  // 基于窗口创建“表面”
        image1 = SDL_LoadBMP( "../../../../logo.bmp" );                                                    // 加载BMP图形(相对路径)
    
        // 下述三个案例逐个解开注释查看
        // ShowBMP();
        // ScaledBMP();
        ConvertSurface();
    
        return 0;
    }
    

    在这里插入图片描述

  3. 事件活动

    在教程中该节主要讲解到了事件,如健值的输入事件,关闭事件等。且通过SDL_Event 定义的结构对象,和SDL_PollEvent()函数获取到事件的类型和触发健值等等。还有我怀疑,教程是不是把SDL_PollEvent函数写成SDL_PolLEvent,这压根就没有找到。下述就健值获取事件退出事件鼠标事件列出函数案例,至于官方不推荐的健值轮询,和自定义用户就举例了,有需要的可以调整教程详细观看,还有更多的其它事件可以浏览SDL的文档网站查看。

    #include <SDL.h>
    #include "SDL_events.h"
    
    SDL_Window* win;
    SDL_Surface* winSurface;
    SDL_Event ev;
    
    char running = 1;
    
    // 按键输入验证
    // 简述:通过识别按下的'1'、'2'、'3'键来,切换纯色界面。按下'4'退出案例。
    void KeyInPut()
    {
        running = 1;
        while( running)
        {
            while ( SDL_PollEvent( &ev ) != 0 ) {
                switch (ev.type) {
                    case SDL_KEYDOWN:
                        // 健值分支
                        switch ( ev.key.keysym.sym ) {
                            case SDLK_KP_1:
                                SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 255, 0, 0 ));            // 绘制红色矩形
                                break;
                            case SDLK_KP_2:
                                SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 0, 255, 0 ));            // 绘制绿色矩形
                                break;
                            case SDLK_KP_3:
                                SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 0, 0, 255 ));            // 绘制蓝色矩形
                                break;
                            case SDLK_KP_4:
                                // 退出该案例
                                running = 0;
                                break;
                        }
                        break;
                }
            }
            SDL_UpdateWindowSurface( win ); // 更新窗口
            SDL_Delay(100);
        }
    }
    
    // 窗口退出验证
    // 简述:只有在 SDL_QUIT 类型的事件下,点击'x'关闭窗口,才能得到响应关闭。
    void WinClose()
    {
        running = 1;
        while ( running ) {
            // Event loop
            while ( SDL_PollEvent( &ev ) != 0 ) {
                switch (ev.type) {
                    case SDL_QUIT:
                        running = 0;
                        break;
                }
            }
            SDL_Delay(100);
        }
    }
    
    // 鼠标输入
    // 简述:通过按下鼠标左键、中键、右键切换纯色界面。
    void MouseInput()
    {
        while ( running ) {
            while ( SDL_PollEvent( &ev ) != 0 ) {
                switch (ev.type) {
                    case SDL_MOUSEBUTTONUP:
                        // test button
                        switch ( ev.button.button ) {
                            case SDL_BUTTON_LEFT:   // 鼠标左键
                                SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 255, 255, 0 ));
                                break;
                            case SDL_BUTTON_RIGHT:  // 鼠标右键
                                SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 0, 255, 255 ));
                                break;
                            case SDL_BUTTON_MIDDLE: // 鼠标中键
                                SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 255, 0, 255 ));
                                break;
                        }
                        break;
                }
            }
            SDL_UpdateWindowSurface( win ); // 更新窗口
            SDL_Delay(100);
        }
    }
    
    // 退出后清理释放内存
    void kill() {
        // Free images
        SDL_FreeSurface( winSurface );
        // Quit
        SDL_DestroyWindow( win );
        SDL_Quit();
    }
    
    int main(int argc, char* argv[]) {
    
        SDL_Init( SDL_INIT_EVERYTHING );                                                              // 初始化SDL2所有部分
        win = SDL_CreateWindow( "MyWindow", 100, 100, 640, 480, SDL_WINDOW_SHOWN );  // 创建窗口
        winSurface = SDL_GetWindowSurface( win );                                                   // 基于窗口创建“表面”
        SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 0, 0, 0 )); // 绘制矩形
        SDL_UpdateWindowSurface( win ); // 更新窗口
    
        // KeyInPut();
        // WinClose();
        MouseInput();
    	
        kill(); // 清理释放
        return 0;
    }
    
    

    在这里插入图片描述

  4. 几何渲染

    区别于上述几个案例,在绘制图形时都是使用基于软件或 CPU 渲染。在下述中将会引入到渲染器SDL_Renderer结构来进行渲染绘制,它的渲染速度会得到提高。而且不在采用基于SDL_Surface表面结构的绘制,而是通过操作渲染器SDL_Renderer结构,定位,绘制,更新,来完成帧图。如何更多的绘制,如点、线、面等可以查看函数文档。

    代码案例中,实现的效果和案例1基本相同,被注释掉的是,之前基于表面结构实现的方法。

    SDL_Window* win;
    SDL_Renderer* renderer;
    
    int main(int argc, char* argv[]) {
        SDL_Rect rect;
        rect.x = 20; rect.y = 20; rect.w = 100; rect.h = 100;
    
        SDL_Init( SDL_INIT_EVERYTHING );                                                              // 初始化SDL2所有部分
        // win = SDL_CreateWindow( "MyWindow", 100, 100, 640, 480, SDL_WINDOW_SHOWN );  // 创建窗口
        SDL_CreateWindowAndRenderer( 640, 480, NULL, &win, &renderer );     // 创建窗口和渲染器
        // winSurface = SDL_GetWindowSurface( win );                                                   // 基于窗口创建“表面”
        // SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 0, 0, 0 )); // 绘制矩形
        SDL_SetRenderDrawColor( renderer, 255, 0, 0, 255 );
        SDL_RenderFillRect(renderer, &rect);
        // SDL_UpdateWindowSurface( win ); // 更新窗口
        SDL_RenderPresent( renderer );  // 更新窗口
    
        while (1);  // 卡住界面(保持窗口)
        // 清理释放
        SDL_DestroyRenderer( renderer );
        SDL_DestroyWindow( win );
        return 0;
    }
    
    SDL_Window* win;
    SDL_Renderer* renderer;
    
    int main(int argc, char* argv[]) {
        SDL_Rect rect;
        rect.x = 20; rect.y = 20; rect.w = 100; rect.h = 100;
    
        SDL_Init( SDL_INIT_EVERYTHING );                                                              // 初始化SDL2所有部分
        // win = SDL_CreateWindow( "MyWindow", 100, 100, 640, 480, SDL_WINDOW_SHOWN );  // 创建窗口
        SDL_CreateWindowAndRenderer( 640, 480, NULL, &win, &renderer );     // 创建窗口和渲染器
        // winSurface = SDL_GetWindowSurface( win );                                                   // 基于窗口创建“表面”
        // SDL_FillRect( winSurface, NULL, SDL_MapRGB( winSurface->format, 0, 0, 0 )); // 绘制矩形
        SDL_SetRenderDrawColor( renderer, 255, 0, 0, 255 );
        SDL_RenderFillRect(renderer, &rect);
        // SDL_UpdateWindowSurface( win ); // 更新窗口
        SDL_RenderPresent( renderer );  // 更新窗口
    
        while (1);  // 卡住界面(保持窗口)
        // 清理释放
        SDL_DestroyRenderer( renderer );
        SDL_DestroyWindow( win );
        return 0;
    }
    

    在这里插入图片描述

  5. 创建纹理

    因为上述讲了渲染可以通过表面结构或渲染器结构,所以有两种。在接下来的代码案例中,将只会介绍渲染器来进行渲染的结构。关于纹理,“纹理是表面的 GPU 渲染等效物”,应该寓意着纹理的渲染是通过GPU,且更高效的。

    下述代码只提供,纹理创建到渲染。像原教程中还举例有,纹理透明度改变,图形翻转以及改变模式之类的暂不列出。

    SDL_Rect rect;
    SDL_Window* win;
    SDL_Surface* image;
    SDL_Texture* texture;
    SDL_Renderer* renderer;
    
    int main(int argc, char* argv[]) {
        SDL_Init( SDL_INIT_EVERYTHING );                                                               // 初始化SDL2所有部分
        SDL_CreateWindowAndRenderer( 640, 480, NULL, &win, &renderer );     // 创建窗口和渲染器
    
        // 获取logo图片,并创建纹理
        image = SDL_LoadBMP( "D:\\Desktop\\MyData\\CLion\\Demo\\logo.bmp" );
        texture = SDL_CreateTextureFromSurface( renderer, image );
        SDL_FreeSurface( image );
        rect.x = 20; rect.y = 20; rect.w = 300; rect.h = 300;
        SDL_RenderCopy( renderer, texture, NULL, &rect );
        SDL_RenderPresent(renderer);
    
        // 清理释放
        SDL_DestroyRenderer( renderer );
        SDL_DestroyWindow( win );
        return 0;
    }
    
    

    在这里插入图片描述

  6. 声音和扩展库

    SDL虽然有着广泛的API,但是部分区域还是要借助扩展库。这里将介绍使用到SDL_ImageSDL_Mixer库。扩展库都需要导入其代码,这里暂时就先不介绍了。

    // SDL_Image
    IMG_Init( IMG_INIT_JPG | IMG_INIT_PNG );	// 初始化
    SDL_Surface* image = IMG_Load("image.png");	// 导入图片
    IMG_Quit();	// 销毁
    
    // SDL_Mixer
    Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 2, 1024 );	// 初始化
    /* 加载音频 */
    Mix_Music* music;
    Mix_Chunk* sound;
    music = Mix_LoadMUS("music.wav");	// 加载音乐
    sound = Mix_LoadWAV("sound.mp3");	// 加载声音
    /* 播放音乐 */
    Mix_PlayMusic( music, -1 );
    /* 播放声音 */
    Mix_PlayChannel( -1, sound, 0 );	
    Mix_Pause( channel );
    SDL_Delay( 1000 );
    Mix_Resume( channel );
    /* 销毁 */
    Mix_FreeChunk( sound );
    Mix_FreeMusic( music );
    Mix_Quit();
    
  7. 文本渲染和输入

    该功能也是基于扩展库,基于SDL_ttf.h

    SDL_Surface* text;
    SDL_Texture* text_texture;
    SDL_Color color = { 0, 0, 0 };
    
    /* 初始化 */
    TTF_Init();
    /* 渲染文本 */
    text = TTF_RenderText_Solid( font, "Hello World!", color );
    text_texture = SDL_CreateTextureFromSurface( renderer, text );
    SDL_Rect dest = { 0, 0, text->w, text->h };
    SDL_RenderCopy( renderer, text_texture, &dest );
    /* 文本输入 */
    SDL_StartTextInput();
    string in;
    bool running = true;
    
    while ( running ) {
        SDL_Event ev;
        while ( SDL_PollEvent( &ev ) ) {
            if ( ev.type == SDL_TEXTINPUTEVENT ) {
                in += ev.text.text;
                // cout << " > " << in << endl;
            } else if ( ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_BACKSPACE && in.size()) {
                in.pop_back();
                // cout << " > " << in << endl;
            } eles if ( ev.type == SDL_QUIT ) {
                running = false;
            }
        }
    }
    
    SDL_StopTextInput();
    /* 关闭 */
    TTF_CloseFont( font );
    TTF_Quit();
    
  8. 计时:帧速率、物理、动画

    该章节讲到的概念案例都较为易懂,所以直接附上教程中的代码。现在先看个大概,可以在使用到的时候在多看看。

    /***** 定时 *****/
    Uint32 ticks = SDL_GetTicks();
    // ...中途操作
    Uint32 end = SDL_GetTicks();
    float secondsElapsed = (end - start) / 1000.0f;
    
    /***** 更高精度计数器 *****/
    Uint64 start = SDL_GetPerformanceCounter();
    // ...中途操作
    Uint64 end = SDL_GetPerformanceCounter();
    float secondsElapsed = (end - start) / (float)SDL_GetPerformanceFrequency();
    
    /***** 更高精度计数器 *****/
    bool running = true;
    while (running) {
    	Uint64 start = SDL_GetPerformanceCounter();
    	// 事件循环
    	// 物理循环
    	// 呈现循环
    	Uint64 end = SDL_GetPerformanceCounter();
    	float elapsed = (end - start) / (float)SDL_GetPerformanceFrequency();
        printf("Current FPS: %s\r\n",to_string(1.0f / elapsed));
    }
    
    /***** 限制FPS速率 *****/
    bool running = true;
    while (running) {
    	
    	Uint64 start = SDL_GetPerformanceCounter();
    	// 事件循环
    	// 物理循环
    	// 呈现循环
    	Uint64 end = SDL_GetPerformanceCounter();
    	float elapsedMS = (end - start) / (float)SDL_GetPerformanceFrequency() * 1000.0f;
    	// 上限为60FPS
    	SDL_Delay(floor(16.666f - elapsedMS));
    
    }
    
    /***** 垂直同步 *****/
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED );
    
    /***** 物理 *****/
    bool running;
    Uint32 lastUpdate = SDL_GetTicks();
    
    while (running) {
    	// 事件循环
    	// 物理循环
    	Uint32 current = SDL_GetTicks();
    	// 计算dT(单位:秒)
    	float dT = (current - lastUpdate) / 1000.0f;
    	for ( /* 对象列表 */ ) {
    		object.position += object.velocity * dT;
    	}
    	// 设置更新时间
    	lastUpdate = current;
    	// 呈现循环
    }
    
    /***** 动画 *****/
    float animatedFPS = 24.0f;
    bool running;
    
    while (running) {
    	// 事件循环
    	// 物理循环
    	// 呈现循环
    	Uint32 current = SDL_GetTicks();
    	// 计算dT(单位:秒)
    	for ( /* 对象列表 */ ) {
    		float dT = (current - object.lastUpdate) / 1000.0f;
    
    		int framesToUpdate = floor(dT / (1.0f / animatedFPS));
    		if (framesToUpdate > 0) {
    			object.lastFrame += framesToUpdate;
    			object.lastFrame %= object.numFrames;
    			object.lastUpdate = current;
    		}
    
    		render(object.frames[object.lastFrame]);
    	}
    }
    

    资料

    原版教程用到的扩展库,和目前SDL最新稳定版的包

    SDL2-devel-2.30.11-mingw.zip * 1

    SDL2_image-devel-2.8.4-mingw.zip * 1

    SDL2_mixer-devel-2.7.2-mingw.zip * 1

    SDL2_ttf-devel-2.22.0-mingw.zip * 1

    链接: https://pan.baidu.com/s/1A8wkpUpnAkLihmaocIvxeA  提取码: v6wk

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

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

相关文章

【Linux系统编程】—— 从零开始实现一个简单的自定义Shell

文章目录 什么是自主shell命令行解释器&#xff1f;实现shell的基础认识全局变量的配置初始化环境变量实现内置命令&#xff08;如 cd 和 echo&#xff09;cd命令&#xff1a;echo命令&#xff1a; 构建命令行提示符获取并解析用户输入的命令执行内置命令与外部命令Shell的主循…

认识BOM

BOM 弹出层 可视窗口尺寸 屏幕宽高 浏览器内核和其操作系统的版本 剪贴板 是否允许使用cookie 语言 是否在线

[c语言日寄]结构体的使用及其拓展

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

Linux系统的第一个进程是什么?

Linux进程的生命周期从创建开始&#xff0c;直至终止&#xff0c;贯穿了一个进程的整个存在过程。我们可以通过系统调用fork()或vfork()来创建一个新的子进程&#xff0c;这标志着一个新进程的诞生。 实际上&#xff0c;Linux系统中的所有进程都是由其父进程创建的。 既然所有…

5. 马科维茨资产组合模型+AI金融智能体(qwen-max)识别政策意图方案(理论+Python实战)

目录 0. 承前1. AI金融智能体1.1 What is AI金融智能体1.2 Why is AI金融智能体1.3 How to AI金融智能体 2. 数据要素&计算流程2.1 参数集设置2.2 数据获取&预处理2.3 收益率计算2.4 因子构建与预期收益率计算2.5 协方差矩阵计算2.6 投资组合优化2.7 持仓筛选2.8 AI金融…

PostMan最新版本及离线安装指南

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;PostMan是一款流行的API测试工具&#xff0c;它提供了一个直观的用户界面&#xff0c;方便Web开发者和测试人员进行接口测试。本文将指导你如何安装最新版的PostMan&#xff0c;包括在线安装和离线安装两种方法。…

记录一次k8s起不来的排查过程

我在k8s集群&#xff0c;重启了一个node宿主机&#xff0c;竟然发现kubelet起不来了&#xff01;报错如下 这个报错很模糊&#xff0c;怎么排查呢。这样&#xff0c;开两个界面&#xff0c;一个重启kubelet&#xff0c;一个看系统日志(/var/log/message:centos&#xff0c;/va…

grafana + Prometheus + node_exporter搭建监控大屏

本文介绍生产系统监控大屏的搭建&#xff0c;比较实用也是实际应用比较多的方式&#xff0c;希望能够帮助大家对监控系统有一定的认识。 0、规划 grafana主要是展示和报警&#xff0c;Prometheus用于保存监控数据&#xff0c;node_exporter用于实时采集各个应用服务器的事实状…

2024年博客之星主题创作|从零到一:我的技术成长与创作之路

2024年博客之星主题创作&#xff5c;从零到一&#xff1a;我的技术成长与创作之路 个人简介个人主页个人成就热门专栏 历程回顾初来CSDN&#xff1a;怀揣憧憬&#xff0c;开启创作之旅成长之路&#xff1a;从平凡到榜一的蜕变持续分享&#xff1a;打卡基地与成长复盘四年历程&a…

Golang的网络编程安全

Golang的网络编程安全 一、Golang网络编程的基本概念 作为一种现代化的编程语言&#xff0c;具有优秀的并发特性和网络编程能力。在Golang中&#xff0c;网络编程是非常常见的需求&#xff0c;可以用于开发各种类型的网络应用&#xff0c;比如Web服务、API服务、消息队列等。Go…

【2024年华为OD机试】(C/D卷,200分)- 5G网络建设 (JavaScriptJava PythonC/C++)

一、问题描述 题目描述 现需要在某城市进行5G网络建设&#xff0c;已经选取N个地点设置5G基站&#xff0c;编号固定为1到N。接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通。不同基站之间假设光纤的成本各不相同&#xff0c;且有些节点之间已经存在光纤相连。 …

消息队列篇--原理篇--RabbitMQ和Kafka对比分析

RabbitMQ和Kafka是两种非常流行的消息队列系统&#xff0c;但它们的设计哲学、架构特点和适用场景存在显著差异。对比如下。 1、架构设计 RabbitMQ&#xff1a; 基AMQP协议&#xff1a;RabbitMQ是基于AMQP&#xff08;高级消息队列协议&#xff09;构建的&#xff0c;支持多…

玻璃样式的登录界面

AI越来越火了,我们想要不被淘汰就得主动拥抱。推荐一个人工智能学习网站,通俗易懂,风趣幽默,最重要的屌图甚多,忍不住分享一下给大家。点击跳转到网站 先看样式: 源码: <div class="wrapper">

Python数据可视化(够用版):懂基础 + 专业的图表抛给Tableau等专业绘图工具

我先说说文章标题中的“够用版”啥意思&#xff0c;为什么这么写。 按照我个人观点&#xff0c;在使用Python进行数据分析时&#xff0c;我们有时候肯定要结合到图表去进行分析&#xff0c;去直观展现数据的规律和特定&#xff0c;那么我们肯定要做一些简单的可视化&#xff0…

物联网网关Web服务器--CGI开发实例BMI计算

本例子通一个计算体重指数的程序来演示Web服务器CGI开发。 硬件环境&#xff1a;飞腾派开发板&#xff08;国产E2000处理器&#xff09; 软件环境&#xff1a;飞腾派OS&#xff08;Phytium Pi OS&#xff09; 硬件平台参考另一篇博客&#xff1a;国产化ARM平台-飞腾派开发板…

HTML新春烟花

系列文章 序号目录1HTML满屏跳动的爱心&#xff08;可写字&#xff09;2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心&#xff08;简易版&#xff09;7HTML粒子爱心8HTML蓝色动态爱心9HTML跳动的爱心&#xff08;双心版&#xff09;1…

从结构嵌套的幻梦里:递归与数据构建的精巧和鸣

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节我们来学习递归的相关知识 函数递归 一、什么是递归1.1 递归的思想 二、递归的限制条件三、递归的…

【Linux系统】—— 编译器 gcc/g++ 的使用

【Linux系统】—— 编译器 gcc/g 的使用 1 用 gcc 直接编译2 翻译环境2.1 预处理&#xff08;进行宏替换&#xff09;2.2 编译&#xff08;生成汇编&#xff09;2.3 汇编&#xff08;生成机器可识别代码&#xff09;2.4 链接2.5 记忆小技巧2.6 编译方式2.7 几个问题2.7.1 如何理…

【Unity3D】3D物体摆放、场景优化案例Demo

目录 PlaceManager.cs(放置管理类) Ground.cs(地板类) 和 GroundData.cs(地板数据类) 额外知识点说明 1、MeshFilter和MeshRenderer的Bounds区别 2、Gizmos 绘制一个平行于斜面的立方体 通过网盘分享的文件&#xff1a;PlaceGameDemo2.unitypackage 链接: https://pan.baid…

智能系统的感知和决策

智能系统在感知和决策过程中具备的关键能力表现在智能感知/自主判定上&#xff0c;下面可以从感知的本质、自主判断的含义及其在智能系统中的作用进行深入分析。 1、智能感知&#xff1a;信息获取与理解 智能感知是指智能系统通过传感器或其他数据采集手段获取环境中的信息&…