游戏理解入门:Rust+Bracket开发一个小游戏

1. Game loop

使用game loop可以使得游戏运行更加流畅和顺滑,它可以:

  • 初始化窗口、图形和其他资源;
  • 每当屏幕刷新他都会运行(通常是每秒30,60 );
  • 每次通过循环,他都会调用游戏的tick()函数。

大致的原理流程如下:

image-20240428105116174


2. 游戏引擎/库

这里选择使用一款名为bracket-Lib的游戏编程库,这是基于rust

  • 抽象了游戏开发中很多复杂的东西,但是保留了相关的概念,可以作为简单的教学工具。
  • 包括了随机数生成、几何、寻路、颜色处理、常用算法等。

2.1 Bracket-terminal

这个终端主要负责Bracket-Lib中的显示部分。

  • 提供了模拟控制台;
  • 可以与多种渲染平台配合
    • 从文本控制台到Web Assembly
    • 例如:OpenGL,Vulkan,Metal;
  • 支持sprites和原生的OpenGL开发。

2.2 Codepage437

  • 这是IBM扩展的ACSLL字符集。来自Dos PC上得到字符,用于终端输出,除了字母和数字,还提供一些符号。
  • Bracket-lib会把字符翻译为图形sprites并提供一个有限的字符集,字符所展示的是相应的图片;

3. 开始编码

3.1 游戏窗口初始化

使用cargo new创建游戏项目并导入Gracket-lib依赖。下面是第一部分代码实现,创建了游戏终窗口并打印一条简单的输出:

use bracket_lib::prelude::*;

// 保留帧状态

struct State {

}

// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {
    // 实现tick函数
    fn tick(&mut self, ctx: &mut BTerm) {
        // 清屏
        ctx.cls();
        // 在屏幕上打印输出,坐标系x,y从屏幕左上角开始计算(0,0)
        ctx.print(1, 1, "Hello,Bracket Terminall!");


    }
}
fn main() -> BError {
    // 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功
    let context = BTermBuilder::simple80x50()
    .with_title("Flappy Dragon")
    .build()?;
    
    main_loop (context,State{})
}

运行结果:

image-20240428112645545


3.2 游戏模式

一般情况下,游戏都是有一些明确的游戏模式,每种模式会明确游戏在当前的tick()中应该作的任务。

这个游戏也不例外,主要涉及三种模式:

  • 菜单
  • 游戏中
  • 结束

下面先将大致的框架构建好。

use bracket_lib::prelude::*;

// 保留帧状态
struct State {
    mode:GameMode,

}
// 为游戏状态实现一个叫new的关联函数
impl State {
    fn new() ->Self {
        State {
            mode:GameMode::Menu, // 设置游戏初始状态为菜单模式
        }
    }

    // 实现play方法
    fn play(&mut self,ctx:&mut BTerm) {
        //TODO
		self.mode = GameMode::End;
    }
    
  	// restart
    fn resatrt(&mut self) {
        self.mode = GameMode::Playing;
    }
    
    fn main_menu(&mut self, ctx: &mut BTerm) {
        // TODO

    }
    
    // 实现end方法
    fn dead(&mut self, ctx: &mut BTerm) {
        
    }
    
    // 实现menu方法
    
}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{
    Menu,
    Playing,
    End,
}


// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {
    // 实现tick函数
    fn tick(&mut self, ctx: &mut BTerm) {
        // 根据游戏状态选择方向
        match self.mode {
            GameMode::Menu =>self.main_menu(ctx),
            GameMode::Playing => self.dead(ctx),
            GameMode::End => self.play(ctx),
        }
        
    }
}
fn main() -> BError {
    // 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功
    let context = BTermBuilder::simple80x50()
    .with_title("Flappy Dragon")
    .build()?;
    
    main_loop (context,State::new())
}

3.2.1 游戏菜单实现

游戏菜单的实现逻辑比较简单,主要是提供一个游戏操作的入口以供玩家进行选择操作:

  • 清理屏幕
  • 打印欢迎语
  • 开始游戏§
  • 离开游戏(Q)
fn main_menu(&self, ctx: &mut BTerm) {
    // TODO
    ctx.cls();
    // print_centered会在屏幕水平中间位置进行打印
    ctx.print_centered( 5,"欢迎来到Flappy Dragon!");
    ctx.print_centered( 8, "(P) 开始游戏");
    ctx.print_centered(9, " (Q) 离开游戏");

    if let Some(key) =ctx.key {
        match key {
            VirtualKeyCode::P => self.resatrt(),
            VirtualKeyCode::Q => ctx.quitting = true,
            _ => {}
        }
    }
}

3.2.2 游戏结束的实现

这块代码和游戏菜单差不多,把提示词换一下

// 实现end方法
fn dead(&mut self, ctx: &mut BTerm) {
    ctx.cls();
    // print_centered会在屏幕水平中间位置进行打印
    ctx.print_centered( 5,"小菜鸡,你已经嘎了!");
    ctx.print_centered( 8, "(P) 不服,再战");
    ctx.print_centered(9, " (Q) 离开游戏" );

    if let Some(key) =ctx.key {
        match key {
            VirtualKeyCode::P => self.resatrt(),
            VirtualKeyCode::Q => ctx.quitting = true,
            _ => {}
        }
    }
}

3.3.3 第一阶段效果

下面是该阶段全部代码,实现了游戏基本窗口以及三个基本模式的逻辑。

use bracket_lib::prelude::*;

// 保留帧状态
struct State {
    mode:GameMode,

}
// 为游戏状态实现一个叫new的关联函数
impl State {
    fn new() ->Self {
        State {
            mode:GameMode::Menu, // 设置游戏初始状态为菜单模式
        }
    }

    // 实现play方法
    fn play(&mut self,ctx:&mut BTerm) {
        //TODO
        self.mode = GameMode::End;
    }

    // menu方法
    fn main_menu(&mut self, ctx: &mut BTerm) {
        // TODO
        ctx.cls();
        // print_centered会在屏幕水平中间位置进行打印
        ctx.print_centered( 5,"Welcome to Flappy Dragon!");
        ctx.print_centered( 8, "(P) Start play");
        ctx.print_centered(9, " (Q) Quit game");
        
        if let Some(key) =ctx.key {
            match key {
                VirtualKeyCode::P => self.resatrt(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    // restart
    fn resatrt(&mut self) {
        self.mode = GameMode::Playing;
    }
    
    // 实现end方法
    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        // print_centered会在屏幕水平中间位置进行打印
        ctx.print_centered( 5,"You are dead!");
        ctx.print_centered( 8, "(P) replay");
        ctx.print_centered(9,  "(Q) quit game");
        
        if let Some(key) =ctx.key {
            match key {
                VirtualKeyCode::P => self.resatrt(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
    
    
}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{
    Menu,
    Playing,
    End,
}


// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {
    // 实现tick函数
    fn tick(&mut self, ctx: &mut BTerm) {
        // 根据游戏状态选择方向
        match self.mode {
            GameMode::Menu =>self.main_menu(ctx),
            GameMode::Playing => self.dead(ctx),
            GameMode::End => self.play(ctx),
        }
        
    }
}
fn main() -> BError {
    // 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功
    let context = BTermBuilder::simple80x50()
    .with_title("Flappy Dragon")
    .build()?;
    
    main_loop (context,State::new())
}

  • 运行效果:

image-20240428121025278


3.3 添加play

这部分主要在游戏窗口添加一个玩家角色,这里以字符@作为龙,实现玩家通过空格键控制该角色的上下移动:

  • 一定时间不按空格,角色会下落,当下落碰到屏幕时游戏失败并结束游戏;
  • 按下空格时,龙会网上移动。
use bracket_lib::prelude::*;

// 保留帧状态
struct State {
    player:Player,
    frame_time:f32,// 结果多少帧后累计的时间
    mode:GameMode,

}

const SCREEN_WIDTH:i32 = 80; // 屏幕宽度
const SCREEN_HEIGHT:i32 = 50; // 屏幕高度
const FRAME_DURATION:f32 = 75.0; //

struct Player {
    x:i32,
    y:i32,
    velocity:f32,// 纵向速度 > 0 玩家就会往下掉
}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{
    Menu,
    Playing,
    End,
}
impl Player {
    fn new(x:i32,y:i32)-> Self {
        Player {
            x:0,
            y:0,
            velocity:0.0, // 下落更加丝滑
        }
    }

    // 使用'@’在屏幕上表示玩家
    fn render (&mut self,ctx:&mut BTerm) {
        ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'))
    }


    fn gravity_and_move (&mut self) {
        // 当下降速度小于2.0时让它的重力加速度每次增加0.2
        if self.velocity < 2.0 {
            self.velocity += 0.2;
        }

        self.y += self.velocity as i32;
        self.x += 1;

        if self.y < 0 {
            self.y = 0;
        }
    }
    // 按下空格实现玩家角色的向上移动
    fn flap (&mut self) {
        self.velocity = -2.0;
    }

}

// 为游戏状态实现一个叫new的关联函数
impl State {
    fn new() ->Self {
        State {
            player:Player::new(5,25),
            frame_time:0.0,
            mode:GameMode::Menu, // 设置游戏初始状态为菜单模式
        }
    }

    // 实现play方法
    fn play(&mut self,ctx:&mut BTerm) {
        ctx.cls_bg(NAVY);
        self.frame_time += ctx.frame_time_ms;

        if self.frame_time >FRAME_DURATION {
            self.frame_time = 0.0;
            self.player.gravity_and_move();
        }

        if let Some(VirtualKeyCode::Space) = ctx.key {
            self.player.flap();
        }

        self.player.render(ctx);
        ctx.print(0,0,  "Press Space to Flap");
        
        if self.player.y > SCREEN_HEIGHT {
            self.mode = GameMode::End;
        }
    }

    // menu方法
    fn main_menu(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        // print_centered会在屏幕水平中间位置进行打印
        ctx.print_centered( 5,"Welcome to Flappy Dragon!");
        ctx.print_centered( 8, "(P) Play Game");
        ctx.print_centered(9,  "(Q) Quit Game");
        
        if let Some(key) =ctx.key {
            match key {
                VirtualKeyCode::P => self.mode = GameMode::Playing,
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    // restart
    fn resatrt(&mut self) {
        self.player = Player::new(5,25);
        self.frame_time = 0.0;
        self.mode = GameMode::Menu;
    }
    
    // 实现end方法
    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        // print_centered会在屏幕水平中间位置进行打印
        ctx.print_centered( 5,"You are dead!");
        ctx.print_centered( 8, "(P) replay");
        ctx.print_centered(9,  "(Q) quit game");
        
        if let Some(key) =ctx.key {
            match key {
                VirtualKeyCode::P => self.resatrt(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
    
    
}



// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {
    // 实现tick函数
    fn tick(&mut self, ctx: &mut BTerm) {
        // 根据游戏状态选择方向
        match self.mode {
            GameMode::Menu =>self.main_menu(ctx),
            GameMode::Playing => self.play(ctx),
            GameMode::End => self.dead(ctx),
        }
    }
}
fn main() -> BError {
    // 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功
    let context = BTermBuilder::simple80x50()
    .with_title("Flappy Dragon")
    .build()?;
    
    main_loop (context,State::new())
}

3.4 添加障碍物

use std::fmt::format;

use bracket_lib::{prelude::*, random};

// 保留帧状态
struct State {
    player:Player,
    frame_time:f32,// 结果多少帧后累计的时间
    mode:GameMode,
    obstacle:Obstacle,
    score:i32,

}

const SCREEN_WIDTH:i32 = 80; // 屏幕宽度
const SCREEN_HEIGHT:i32 = 50; // 屏幕高度
const FRAME_DURATION:f32 = 75.0; //

struct Player {
    x:i32,
    y:i32,
    velocity:f32,// 纵向速度 > 0 玩家就会往下掉
}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{
    Menu,
    Playing,
    End,
}
impl Player {
    fn new(x:i32,y:i32)-> Self {
        Player {
            x:0,
            y:0,
            velocity:0.0, // 下落更加丝滑
        }
    }

    // 使用'@’在屏幕上表示玩家
    fn render (&mut self,ctx:&mut BTerm) {
        ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'))
    }


    fn gravity_and_move (&mut self) {
        // 当下降速度小于2.0时让它的重力加速度每次增加0.2
        if self.velocity < 2.0 {
            self.velocity += 0.2;
        }

        self.y += self.velocity as i32;
        self.x += 1;

        if self.y < 0 {
            self.y = 0;
        }
    }
    // 按下空格实现玩家角色的向上移动
    fn flap (&mut self) {
        self.velocity = -2.0;
    }

}

// 为游戏状态实现一个叫new的关联函数
impl State {
    fn new() ->Self {
        State {
            player:Player::new(5,25),
            frame_time:0.0,
            mode:GameMode::Menu, // 设置游戏初始状态为菜单模式
            obstacle:Obstacle::new(SCREEN_WIDTH,0),
            score:0,
        }
    }

    // 实现play方法
    fn play(&mut self,ctx:&mut BTerm) {
        ctx.cls_bg(NAVY);
        self.frame_time += ctx.frame_time_ms;

        if self.frame_time >FRAME_DURATION {
            self.frame_time = 0.0;
            self.player.gravity_and_move();
        }

        if let Some(VirtualKeyCode::Space) = ctx.key {
            self.player.flap();
        }

        self.player.render(ctx);
        ctx.print(0,0,  "Press Space to Flap");
        ctx.print(0, 1, &format!("Score:{}",self.score));

        self.obstacle.render(ctx, self.player.x);
        
        if self.player.x > self.obstacle.x {
            self.score += 1;
            self.obstacle = Obstacle::new(self.player.x + SCREEN_WIDTH,self.score);
        }

        if self.player.y > SCREEN_HEIGHT || self.obstacle.hit_obstacle(&self.player) {
            self.mode = GameMode::End;
        }

        if self.player.y > SCREEN_HEIGHT {
            self.mode = GameMode::End;
        }
    }

    // menu方法
    fn main_menu(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        // print_centered会在屏幕水平中间位置进行打印
        ctx.print_centered( 5,"Welcome to Flappy Dragon!");
        ctx.print_color_right(60, 7, WEB_GREEN, BLACK,"by:Gemini48");
        ctx.print_centered( 8, "(P) Play Game");
        ctx.print_centered(9,  "(Q) Quit Game");
        
        if let Some(key) =ctx.key {
            match key {
                VirtualKeyCode::P => self.mode = GameMode::Playing,
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    // restart
    fn resatrt(&mut self) {
        self.player = Player::new(5,25);
        self.frame_time = 0.0;
        //self.mode = GameMode::Menu;
        self.mode = GameMode::Playing;
        self.obstacle = Obstacle::new(SCREEN_WIDTH,0);
        self.score = 0;

    }
    
    // 实现end方法
    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        // print_centered会在屏幕水平中间位置进行打印
        ctx.print_centered( 5,"You are dead!");
        ctx.print_centered(6,&format!("You earned {} points",self.score));
        ctx.print_centered( 8, "(P) Play Again");
        ctx.print_centered(9,  "(Q) Quit Game");
        
        if let Some(key) =ctx.key {
            match key {
                VirtualKeyCode::P => self.resatrt(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }
    
    
}



// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {
    // 实现tick函数
    fn tick(&mut self, ctx: &mut BTerm) {
        // 根据游戏状态选择方向
        match self.mode {
            GameMode::Menu =>self.main_menu(ctx),
            GameMode::Playing => self.play(ctx),
            GameMode::End => self.dead(ctx),
        }
    }
}

struct Obstacle {
    x:i32,
    gap_y:i32, // 表示上下两个障碍物之间的空隙
    size:i32,
}

impl Obstacle {
    fn new(x:i32,score:i32) -> Self {
        let mut random = RandomNumberGenerator::new();
        Obstacle {
            x,
            gap_y:random.range(10, 40), // 障碍纵向高度缝隙随机
            size:i32::max(2,20-score),
        }
    }

    fn render(&mut self,ctx:&mut BTerm,player_x:i32) {
        let screen_x = self.x -  player_x; // 屏幕空间
        let half_size:i32  = self.size / 2;

        for y in 0..self.gap_y - half_size {
            ctx.set(screen_x,y, RED,BLACK, to_cp437('|'));
        }

        for y in self.gap_y + half_size..SCREEN_HEIGHT {
            ctx.set(screen_x,y,RED,BLACK,to_cp437('|'));
        }
    }

    // 玩家碰撞到障碍物的处理
    fn hit_obstacle(&self,player:&Player) -> bool {
        let half_size = self.size / 2;
        let does_x_match = player.x == self.x; // 玩家x和障碍物x坐标
        let player_above_gap  =player.y < self.gap_y - half_size;
        let player_below_gap = player.y > self.gap_y + half_size;
        does_x_match && (player_above_gap || player_below_gap)
    }
}


fn main() -> BError {
    // 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功
    let context = BTermBuilder::simple80x50()
    .with_title("Flappy Dragon")
    .build()?;
    
    main_loop (context,State::new())
}


4. 效果截图

image-20240508210136226

image-20240508210155766

源码地址

参考&引用

  • Rust依赖库:crates.io
  • bracket-lib

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

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

相关文章

利用生成式AI重新构想ITSM的未来

对注入 AI 的生成式 ITSM 的需求&#xff0c;在 2023 年 Gartner AI 炒作周期中&#xff0c;生成式 AI 达到预期值达到顶峰后&#xff0c;三分之二的企业已经将生成式 AI 集成到其流程中。 你问为什么这种追求&#xff1f;在预定义算法的驱动下&#xff0c;IT 服务交付和管理中…

又发现一个ai生成音乐的网站-heymusic

网址 https://heymusic.ai/ 尴尬&#xff0c;不挂梯子能登录进来&#xff0c;但是谷歌账号注册不了&#xff0c;刷新了几遍也没注册上。 看了下价格&#xff0c;应该不是免费的&#xff0c;所以也没了试用的兴趣。 我也不想用别的邮箱注册了&#xff0c;所以只能简单的水一…

固定资产管理系统参考论文(论文 + 源码)

【免费】固定资产管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89282536 固定资产管理系统 摘 要 随着计算机信息技术的发展以及对资产、设备的管理科学化、合理化的高要求&#xff0c;利用计算机实现设备及资产的信息化管理已经显得非常重要。 固…

IO 5.8日

1&#xff1a;使用 dup2 实现错误日志功能 使用 write 和 read 实现文件的拷贝功能&#xff0c;注意&#xff0c;代码中所有函数后面&#xff0c;紧跟perror输出错误信息&#xff0c;要求这些错误信息重定向到错误日志 err.txt 中去 2&#xff1a;判断一个文件是否拥有用户可写…

LeetCode509:斐波那契数(使用动态规划)

题目描述 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;其中 n > 1…

BFS专题——FloodFill算法:200.岛屿数量

文章目录 题目描述算法原理代码实现CJava 题目描述 题目链接&#xff1a;200.岛屿数量 PS:注意题目中每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。也就是说斜角是不算了&#xff0c; 例如示例二&#xff0c;是三个岛屿。 算法原理 这道题目是 DFS&#xff0…

三维天地助力实验室质量管理工作无纸化、流程化、标准化

质量管理是实验室日常管理工作中的重点内容,目前大多数实验室信息化的方向还是以实验主流程向无纸化转变为主,质量管理工作仍然依靠线下纸质文件或者线上登记台账的方式进行,这种方式存在任务流转效率低、资源浪费等问题,此外历史数据难以保存也是实验室管理人员的一大痛点。 …

【Android】Room数据库的简单使用方法

Room数据库的使用方法 目录 1、添加Room数据库的依赖2、Entity——定义实体类 2.1 定义主键——PrimaryKey2.2 字段注解——ColumnInfo 3、Dao——定义数据访问对象4、Database——数据库 4.1 通过回调观察数据库是否创建成功 5、使用时注意点6、编写异步 DAO 查询 6.1 写异步…

24_Scala集合Map

文章目录 Scala集合Map1.构建Map2.增删改查3.Map的get操作细节 Scala集合Map –默认immutable –概念和Java一致 1.构建Map –创建kv键值对 && kv键值对的表达 –创建immutable map –创建mutable map //1.1 构建一个kv键值对 val kv "a" -> 1 print…

Java IO流(二)

1. 缓冲流 1.1 字节缓冲流概述 当对文件或其他数据源进行频繁的读/写操作时&#xff0c;效率比较低&#xff0c;这时如果使用缓存流就能够更高效地读/写信息。 比如&#xff0c;可以使用缓冲输出流来一次性批量写出若干数据减少写出次数来提高写出效率。 如果用生活中的例子做…

多模态EDA论文小记

论文地址 该论文主要改进点是&#xff1a;通过动态化局部搜索中每个集群大小&#xff0c;高斯和柯西分布共同产生个体。总的来说改进点不多&#xff0c;当然也可能是笔者还没发现。 局部搜索 划分集群 划分集群有两个策略分别是&#xff1a; 随机生成一个点作为中心点&…

什么软件可以把视频合并在一起?6个软件教你快速进行视频编辑

什么软件可以把视频合并在一起&#xff1f;6个软件教你快速进行视频编辑 当你需要对视频进行编辑和合并时&#xff0c;选择合适的软件可以极大地提高工作效率和编辑质量。以下是六款被广泛认可且功能强大的视频编辑软件&#xff0c;它们可以帮助你快速、高效地进行视频编辑和合…

PDF转word转ppt软件

下载地址&#xff1a;PDF转word转ppt软件.zip 平时工作生活经常要用到PDF转word转ppt软件&#xff0c;电脑自带的又要开会员啥的很麻烦&#xff0c;现在分享这款软件直接激活就可以免费使用了&#xff0c;超级好用&#xff0c;喜欢的可以下载

C++从入门到精通---模版

文章目录 泛型编程函数模版模版参数的匹配原则类模版类模版的定义格式类模版的实例化 总结 泛型编程 泛型编程是一种编程范式&#xff0c;旨在实现通用性和灵活性。它允许在编写代码时使用参数化类型&#xff0c;而不是具体的类型&#xff0c;从而使代码更加灵活和可重用。 在…

NodeMCU ESP8266 操作 SSD1306 OLED显示屏详解(图文并茂)

文章目录 1 模块介绍2 接线介绍3 安装SSD1306驱动库4 源码分析4.1 硬件兼容性4.2 可能存在的问题总结1 模块介绍 我们将在本教程中使用的OLED显示屏是SSD1306型号:单色0.96英寸显示屏,像素为12864,如下图所示。 OLED显示屏不需要背光,这在黑暗环境中会产生非常好的对比度。…

全面的Partisia Blockchain 生态 4 月市场进展解读

Partisia Blockchain 是一个以高迸发、隐私、高度可互操作性、可拓展为特性的 Layer1 网络。通过将 MPC 技术方案引入到区块链系统中&#xff0c;以零知识证明&#xff08;ZK&#xff09;技术和多方计算&#xff08;MPC&#xff09;为基础&#xff0c;共同保障在不影响网络完整…

Springboot+Vue项目-基于Java+MySQL的个人云盘管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

去除图片水印软件-inpaint

一、普通使用教程 亲眼看看使用 Inpaint 从照片中删除不需要的元素是多么容易&#xff1a; 1.1加载图片 1.2 选择要纠正的问题区域 1.3 告别不需要的对象并保存 二、功能 1 修复旧照片 老并不总是意味着坏。我们拥有的一些旧照片对我们来说仍然很重要&#xff0c;因为它们仍…

FPGA ov5640视频以太网传输

1 实验任务 使用DFZU4EV MPSoC 开发板及双目OV5640摄像头其中一个摄像头实现图像采集&#xff0c;并通过开发板上的以太网接口发送给上位机实时显示。 2 Verilog代码 2.1 顶层模块 timescale 1ns / 1ps //以太网传输视频顶层模块module ov5640_udp_pc (input sys_cl…

威客网上招标系统(五)

目录 5 详细设计 5.1 系统首页 5.1.1系统首页&#xff08;网站首页index.jsp&#xff09; 5.1.2 下沙派威客网首页界面说明 5.2 站内新闻信息 5.2.1站内新闻操作界面 5.2.2系统主操作界面说明 5.3威客在线操作界面 5.3.1 威客在线操作界面 5.3.2威客在线说明 5.4系统…