当前位置: 首页 > news >正文

FPGA实战项目1——坦克大战

FPGA实战项目1——坦克大战


根据模块化思想,可将此任务简单的进行模块拆分:
系统原理,模块划分,硬件架构,算法支持,Verilog实现框架
 


一,系统总体原理

1.   核心设计思想


      硬件并行处理:利用 FPGA 的并行特性,同时处理多个坦克移动、子弹飞行、碰撞检测等逻辑。
      时序clk驱动:所有模块在统一时钟域下同步运行,通过状态机和计数器实现精准时序控制。
      像素级渲染显示:基于 VGA 接口,将游戏元素(坦克、子弹、得分)渲染为像素信号,驱动显示器显示。

2.   系统架构


(系统时钟模块,输入模块,游戏状态模块,我方坦克,敌方坦克群,子弹管理,得分模块,VGA渲染模块(坐标转为像素),VGA接口,显示器连接)


二,核心模块详细设计

      具体的讲解均体现在随文Verilog代码中!

1.    输入模块(Input Controller)


       功能

处理用户输入(按键 ),输出我方坦克控制信号(移动方向、射击指令),包含按键消抖。
       需要注意的是:按键消抖模块,一般的设计部分都必须要有属于固定部分。
       该模块的主要功能是对机械按键输入的信号进行消抖处理,去除按键在按下和释放瞬间产生的抖动信号,输出稳定的按键状态。同时,检测按键的按下和释放动作,分别产生单周期的脉冲信号,方便后续模块根据这些脉冲信号执行相应的操作。
       算法
采用计数器计时的方法。当检测到按键输入信号与当前存储的按键状态不一致时,计数器开始计数。如果在计数过程中按键状态再次发生变化,计数器清零重新开始计数。当计数器达到预设值(对应约 20ms 的消抖时间)时,认为按键状态稳定,更新存储的按键状态。
       状态
计数器状态:包括计数中、计数完成(达到 20ms)和清零状态。
按键状态:存储当前按键的稳定状态(按下或释放)。
       逻辑
在每个时钟上升沿检查复位信号和按键状态。复位时,将所有状态初始化为默认值。正常工作时,根据按键状态的变化控制计数器的计数,并在合适的时机更新按键状态和产生脉冲信号。
       原理
机械按键在按下和释放时会产生短暂的抖动,这些抖动信号可能会被误判为多次按键操作。通过设置一个固定的消抖时间(20ms),可以避开抖动期,确保只有当按键状态稳定一段时间后才被认为是有效的按键操作。
      代码如下:

//此模块为按键消抖模块。确保用户按键信号稳定
module key_debounce(input wire clk,         input wire rst_n,       input wire key_in,      // 原始按键输入信号,由于机械特性,该信号可能存在抖动output wire key_out,    // 消抖后的稳定按键输出信号output reg key_pulse_down, // 按键按下时产生的单周期脉冲信号,可用于触发特定操作output reg key_pulse_up    // 按键释放时产生的单周期脉冲信号,可用于停止特定操作
);// 20位计数器,用于计时消抖时间。在50MHz时钟频率下,计数到1000000对应约20ms
reg [19:0] cnt; 
// 存储当前经过消抖处理后的按键状态
reg key_reg; 
// 存储上一个时钟周期的按键状态,用于检测按键状态的变化
reg key_prev; // 时序逻辑,在时钟上升沿或复位信号下降沿触发;
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 复位时,计数器清零,按键状态寄存器和上一状态寄存器置为高电平cnt <= 0; key_reg <= 1; key_prev <= 1;// 按键按下和释放脉冲信号清零key_pulse_down <= 0; key_pulse_up <= 0;end else begin// 保存上一个时钟周期的按键状态,随时更新,在CLK到来时,先更新PREV,确保状态存储key_prev <= key_reg; if (key_in != key_reg) begin // 检测到按键状态发生变化,原信号是肯定能采集到的;// 计数器开始计数cnt <= cnt + 1; if (cnt == 20'd1000000) begin // 经过约20ms的消抖时间// 更新按键状态寄存器key_reg <= key_in; // 计数器清零cnt <= 0; endend else begincnt <= 0; end// 检测按键按下的下降沿,出现上升沿,未进行亚稳态消除操作(可进行打拍)key_pulse_down <= key_prev & (!key_reg); // 检测按键释放的上升沿,出现上升沿key_pulse_up <= (!key_prev) & key_reg; end
end// 消抖后的按键输出信号等于按键状态寄存器的值
assign key_out = key_reg; endmodule

相应的仿真测试文件代码如下所示:
 

`timescale 1ns / 1psmodule tb_key_debounce;// 定义信号reg clk;reg rst_n;reg key_in;wire key_out;wire key_pulse_down;wire key_pulse_up;// 实例化按键消抖模块key_debounce uut (.clk(clk), .rst_n(rst_n), .key_in(key_in), .key_out(key_out), .key_pulse_down(key_pulse_down), .key_pulse_up(key_pulse_up));// 生成时钟信号initial beginclk = 0;forever #10 clk = ~clk; // 50MHz时钟,周期20nsend// 测试序列initial begin// 初始化信号rst_n = 0;key_in = 1;#20; // 保持复位20nsrst_n = 1;// 模拟按键抖动#100;key_in = 0;#10; key_in = 1;#10; key_in = 0;#10; key_in = 1;#10; key_in = 0;#20000000; // 等待消抖时间// 模拟按键释放抖动key_in = 1;#10; key_in = 0;#10; key_in = 1;#10; key_in = 0;#10; key_in = 1;#20000000; // 等待消抖时间#1000$finish;endendmodule    


仿真效果如图:


2.   我方坦克模块(player_tank.v)


      功能
该模块负责控制我方坦克的移动和射击。根据输入的移动方向控制信号,在一定的速度控制下,更新坦克的坐标位置。同时,根据射击请求信号,触发子弹发射。
      算法
使用计数器控制坦克的移动速度,每 16 个时钟周期坦克移动一次。根据移动方向控制信号的不同编码,更新坦克的 x 或 y 坐标。在移动过程中,进行边界检查,确保坦克不会移出屏幕范围。
      状态
      坦克位置状态:通过 x 和 y 坐标表示坦克在屏幕上的位置。
      速度计数器状态:控制坦克移动的时间间隔。
      射击状态:根据射击请求信号决定是否发射子弹。
      逻辑
      在每个时钟上升沿检查复位信号和输入信号。复位时,将坦克位置和射击状态初始化为默认值。正常工作时,计数器不断计数,当计数达到 15 时,根据移动方向控制信号更新坦克位置,然后计数器清零。对于射击请求信号,直接将其传递给发射使能信号。
      原理
      通过计数器实现坦克移动速度的控制,避免坦克移动过快。边界检查可以保证游戏的合理性和稳定性,防止坦克移出屏幕范围。射击请求信号的传递实现了坦克的射击功能。

      代码如下:

//我方坦克模型
module player_tank(input wire clk,         input wire rst_n,       // 将坦克的状态恢复到初始位置input wire [2:0] move_dir, // 移动方向控制信号input wire fire_req,    // 射击请求信号,单周期脉冲,触发坦克射击output reg [9:0] x,     // 坦克的x坐标,10位宽度可表示较大范围的屏幕位置output reg [9:0] y,     // 坦克的y坐标,10位宽度可表示较大范围的屏幕位置output reg fire_en      // 子弹发射使能信号,单周期脉冲,用于触发子弹发射模块
);// 4位计数器,用于控制坦克的移动速度,每16个时钟周期移动一次
reg [3:0] speed_cnt; // 时序逻辑,在时钟上升沿或复位信号下降沿触发
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 复位时,坦克初始位置设置在屏幕底部中央x <= 10'd320; y <= 10'd450; // 发射使能信号清零fire_en <= 0; end else begin// 每来一个clk,速度计数器加1speed_cnt <= speed_cnt + 1; if (speed_cnt == 4'd15) begin // 当计数满16个时钟周期,进行移动方向的判别case (move_dir)3'b001: y <= (y > 8) ? y - 1 : y; // 向上移动,同时进行边界检查3'b010: y <= (y < 472) ? y + 1 : y; // 向下移动,同时进行边界检查3'b100: x <= (x > 8) ? x - 1 : x; // 向左移动,同时进行边界检查3'b101: x <= (x < 632) ? x + 1 : x; // 向右移动,同时进行边界检查endcase// 速度计数器清零speed_cnt <= 0; end// 射击处理,将射击请求信号直接传递给发射使能信号fire_en <= fire_req; end
endendmodule

注意到在代码中,有左右移动的指令: 3'b001: y <= (y > 8) ? y - 1 : y;
这是一个条件赋值语句,运用了三元运算符 ? :。其语法形式为 condition ? expression1 : expression2,也就是当 condition 为真时,表达式的值是 expression1;反之则是 expression2。
综合起来,3'b001: y <= (y > 8) ? y - 1 : y; 表示当 move_dir 为 3'b001(向上移动)时,若坦克的 y 坐标大于 8,就将 y 坐标减 1,让坦克向上移动;若 y 坐标小于等于 8,就维持 y 坐标不变,以此避免坦克移出屏幕顶部。


边界值 8 是人为设定的一个常量,其目的是防止坦克移出屏幕范围。因为坦克自身有一定的大小,为了保证坦克不会完全移出屏幕,所以设定了一个安全边界。8 这个值是根据坦克的大小和屏幕分辨率来确定的,以确保坦克在移动到屏幕边缘时能够正确停止。

人眼具有视觉暂留特性,对于一定频率内的连续变化,能够感知到的是平滑的运动。只要坦克的移动速度不是极快,在每 16 个时钟周期移动一个单位的情况下,人眼通常会将其视为连续的移动,而不会察觉到明显的停顿或瞬移。


  3. 敌方坦克模块(enemy_tank.v)


     功能
该模块实现敌方坦克的随机移动和自动射击功能。敌方坦克会在一定时间间隔内随机改变移动方向,并以一定的概率进行射击。
     算法
使用计数器控制敌方坦克改变移动方向的时间间隔,每 50 个时钟周期随机选择一个新的移动方向。同时,使用随机数生成器,每 1000 个时钟周期有 1% 的概率触发射击。在移动过程中,进行边界检查,确保敌方坦克不会移出屏幕范围。
    状态
敌方坦克位置状态:通过 x 和 y 坐标表示敌方坦克在屏幕上的位置。
方向计数器状态:控制敌方坦克改变移动方向的时间间隔。
移动方向状态:记录敌方坦克当前的移动方向。
射击状态:根据随机数决定是否发射子弹。
    逻辑
在每个时钟上升沿检查复位信号和进行相应操作。复位时,将敌方坦克位置、方向计数器、移动方向和射击状态初始化为默认值。正常工作时,方向计数器不断计数,当计数达到 50 时,随机改变移动方向并清零计数器。同时,根据随机数判断是否进行射击。
    原理
通过计数器和随机数生成器实现敌方坦克的随机移动和射击。边界检查保证了敌方坦克在屏幕范围内活动,增加了游戏的合理性。
    代码如下:

//地方坦克模型
module enemy_tank(input wire clk,        input wire rst_n,       output reg [9:0] x,     output reg [9:0] y,     output reg enemy_fire   // 敌方坦克射击信号,单周期脉冲,触发敌方子弹发射
);// 用于控制敌方坦克改变移动方向的计数器
reg [6:0] cnt_dir; 
// 敌方坦克的移动方向,0-上,1-下,2-左,3-右
reg [2:0] dir; always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 复位时,敌方坦克初始位置设置在屏幕顶部中央x <= 10'd320; y <= 10'd30; // 方向计数器清零cnt_dir <= 0; // 初始方向设置为向下dir <= 1; // 射击信号清零enemy_fire <= 0; end else begincnt_dir <= cnt_dir + 1; if (cnt_dir == 7'd50) begin // 每50个时钟周期// 随机改变移动方向dir <= $urandom_range(3, 0); // 方向计数器清零cnt_dir <= 0; end// 按方向移动case (dir)0: y <= (y > 8) ? y - 1 : y; // 向上移动,同时进行边界检查1: y <= (y < 472) ? y + 1 : y; // 向下移动,同时进行边界检查2: x <= (x > 8) ? x - 1 : x; // 向左移动,同时进行边界检查3: x <= (x < 632) ? x + 1 : x; // 向右移动,同时进行边界检查endcase// 随机射击逻辑,每1000个时钟周期有1%的概率射击if ($urandom_range(999, 0) == 0) begin enemy_fire <= 1; endelse beginenemy_fire <= 0; endend
endendmodule

4. 子弹管理模块(bullet_manager.v)


    功能
该模块负责管理我方和敌方发射的子弹,包括子弹的发射、飞行和碰撞检测。根据坦克的发射信号,设置子弹的初始位置和有效标志,控制子弹的飞行方向,并检测子弹是否超出屏幕范围。同时,提供碰撞检测函数,用于判断子弹是否与坦克发生碰撞。
    算法
当接收到我方或敌方坦克的发射信号时,设置子弹的初始位置和有效标志。在子弹有效时,根据子弹的类型(我方或敌方)控制其飞行方向(向上或向下)。当子弹超出屏幕范围时,将其有效标志清零。碰撞检测通过判断子弹的坐标是否在坦克的矩形范围内来实现。
    状态
子弹位置状态:通过 x 和 y 坐标表示子弹在屏幕上的位置。
子弹有效状态:标志子弹是否处于飞行状态。
子弹类型状态:区分子弹是我方还是敌方发射的。
    逻辑
在每个时钟上升沿检查复位信号和发射信号。复位时,将子弹的所有状态初始化为默认值。当接收到发射信号时,初始化子弹状态。在子弹飞行过程中,根据其类型更新位置,并检查是否超出屏幕范围。碰撞检测函数在需要时被调用,判断子弹与坦克是否碰撞。
    原理
通过对发射信号的响应和对子弹位置的更新,实现子弹的发射和飞行。碰撞检测的原理是基于坐标范围的判断,确保只有当子弹进入坦克的一定范围内才判定为碰撞。
    代码如下:

//子弹模型管理
module bullet_manager(input wire clk,         input wire rst_n,       input wire [9:0] player_x, // 我方坦克的x坐标,用于确定我方子弹的发射初始位置input wire [9:0] player_y, // 我方坦克的y坐标,用于确定我方子弹的发射初始位置input wire [9:0] enemy_x,  // 敌方坦克的x坐标,用于确定敌方子弹的发射初始位置input wire [9:0] enemy_y,  // 敌方坦克的y坐标,用于确定敌方子弹的发射初始位置input wire fire_en,       // 我方坦克发射使能信号,触发我方子弹发射input wire enemy_fire,    // 敌方坦克发射信号,触发敌方子弹发射output reg [9:0] bullet_x, // 子弹的x坐标,输出给其他模块用于显示和碰撞检测output reg [9:0] bullet_y, // 子弹的y坐标,输出给其他模块用于显示和碰撞检测output reg bullet_valid,  // 子弹有效标志,1表示子弹处于飞行状态output reg bullet_is_enemy // 子弹是否为敌方子弹标志,1表示敌方子弹
);// 替代结构体的寄存器,子弹封装打包包括位置坐标,有效状态,敌我子弹
reg [9:0] bullet_x_reg;
reg [9:0] bullet_y_reg;
reg bullet_valid_reg;
reg bullet_is_enemy_reg;// 时序逻辑,在时钟上升沿或复位信号下降沿触发
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 复位时,子弹坐标清零,有效标志清零,敌方子弹标志清零bullet_x_reg <= 0; bullet_y_reg <= 0; bullet_valid_reg <= 0; bullet_is_enemy_reg <= 0; end else beginif (fire_en) begin // 我方坦克发射子弹// 子弹初始位置设置在我方坦克中央bullet_x_reg <= player_x + 8; bullet_y_reg <= player_y; // 子弹有效标志置1bullet_valid_reg <= 1; // 子弹为我方子弹,标志置0bullet_is_enemy_reg <= 0; end else if (enemy_fire) begin // 敌方坦克发射子弹// 子弹初始位置设置在敌方坦克中央bullet_x_reg <= enemy_x + 8; bullet_y_reg <= enemy_y; // 子弹有效标志置1bullet_valid_reg <= 1; // 子弹为敌方子弹,标志置1bullet_is_enemy_reg <= 1; endif (bullet_valid_reg) beginif (bullet_is_enemy_reg) begin// 敌方子弹向下飞行,y坐标加5bullet_y_reg <= (bullet_y_reg < 480) ? bullet_y_reg + 5 : 0; if (bullet_y_reg == 0) bullet_valid_reg <= 0; // 子弹超出屏幕范围,有效标志清零end else begin// 我方子弹向上飞行,y坐标减5bullet_y_reg <= (bullet_y_reg > 0) ? bullet_y_reg - 5 : 0; if (bullet_y_reg == 0) bullet_valid_reg <= 0; // 子弹超出屏幕范围,有效标志清零endendend// 将寄存器的值输出bullet_x <= bullet_x_reg; bullet_y <= bullet_y_reg; bullet_valid <= bullet_valid_reg; bullet_is_enemy <= bullet_is_enemy_reg; 
end// 碰撞检测函数,判断子弹是否与坦克发生碰撞
function reg collision_detect(input [9:0] bx, // 子弹的x坐标input [9:0] by, // 子弹的y坐标input [9:0] tx, // 坦克的x坐标input [9:0] ty  // 坦克的y坐标
);// 通过判断子弹坐标是否在坦克矩形范围内来检测碰撞collision_detect = (bx >= tx - 8 && bx <= tx + 8 && by >= ty - 8 && by <= ty + 8);
endfunctionendmodule

5. 得分模块(score_module.v)
 

    功能
该模块根据子弹击中敌方坦克或我方坦克的情况更新玩家的得分。当我方子弹击中敌方坦克时,得分增加;当敌方子弹击中我方坦克时,得分减少。
    算法
在每个时钟上升沿检查复位信号和击中信号。复位时,将得分清零。当检测到子弹击中敌方坦克信号时,得分加 10;当检测到子弹击中我方坦克信号时,得分减 5,但得分最低为 0。
    状态
得分状态:通过 16 位寄存器存储玩家的得分。
    逻辑
在每个时钟上升沿,首先检查复位信号。如果复位信号有效,将得分清零。然后检查击中信号,根据不同的击中情况更新得分。
    原理
根据游戏规则,通过对击中信号的判断,实现得分的增加或减少,为玩家提供游戏成绩的反馈。
    代码如下:

//得分模块管理
module score_module(input wire clk,          input wire rst_n,         // 复位信号,低电平有效,将得分清零input wire bullet_hit_enemy, // 子弹击中敌方坦克信号,高电平有效input wire bullet_hit_player, // 子弹击中我方坦克信号,高电平有效output reg [15:0] score    // 玩家的得分,16位寄存器存储
);// 时序逻辑,在时钟上升沿或复位信号下降沿触发
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 复位时,得分清零score <= 0;end else beginif (bullet_hit_enemy) begin// 子弹击中敌方坦克,得分加10score <= score + 10;end else if (bullet_hit_player) begin// 子弹击中我方坦克,得分减5,最低为0score <= (score > 5) ? score - 5 : 0;endend
endendmodule

6. VGA 显示模块(vga_controller.v)

关于VGA需要补充的一点:在代码中的计数器原理!
    功能
该模块生成 VGA 时序信号(行同步信号和场同步信号),并根据坦克和子弹的位置信息,在 VGA 屏幕上绘制游戏画面。同时,根据不同的元素(我方坦克、敌方坦克、子弹)设置不同的颜色。
    算法
使用行计数器和场计数器生成 VGA 时序信号。在行计数器和场计数器的计数过程中,根据特定的范围设置行同步信号和场同步信号的电平。在有效显示区域内,根据坦克和子弹的坐标判断当前像素点是否属于某个元素,如果是,则设置相应的 RGB 颜色;否则,设置为黑色。在消隐期,将 RGB 颜色信号清零。
     状态
行计数器状态:记录当前行的位置。
场计数器状态:记录当前场的位置。
同步信号状态:行同步信号和场同步信号的电平。
RGB 颜色状态:当前像素点的颜色。
     逻辑
在每个时钟上升沿检查复位信号和进行相应操作。复位时,将所有状态初始化为默认值。正常工作时,行计数器和场计数器不断计数,根据计数结果生成同步信号和判断是否处于有效显示区域。在有效显示区域内,根据元素的位置信息设置 RGB 颜色。
     原理
VGA 显示的原理是通过行同步信号和场同步信号同步显示器的扫描过程,在有效显示区域内,根据 RGB 颜色信号控制每个像素点的颜色,从而实现图像的显示。


假设 704 个像素的行时序和 512 行的场时序配置如下:

行时序:
有效显示区域:640 个像素
行同步脉冲:48 个像素
后沿:8 个像素
前沿:8 个像素
总计:640 + 48 + 8 + 8 = 704 个像素

场时序:
有效显示行数:480 行
场同步脉冲:2 行
后沿:20 行
前沿:10 行
总计:480 + 2 + 20 + 10 = 512 行

   代码如下:

//VGA显示控制模块
module vga_controller(input wire clk,         // 25MHz像素时钟,为VGA时序提供基准,确保图像正确显示input wire rst_n,       // 复位信号,低电平有效,将VGA状态初始化input wire [9:0] player_x, // 我方坦克的x坐标,用于在VGA屏幕上绘制我方坦克input wire [9:0] player_y, // 我方坦克的y坐标,用于在VGA屏幕上绘制我方坦克input wire [9:0] enemy_x,  // 敌方坦克的x坐标,用于在VGA屏幕上绘制敌方坦克input wire [9:0] enemy_y,  // 敌方坦克的y坐标,用于在VGA屏幕上绘制敌方坦克input wire enemy_alive,    // 敌方坦克存活标志,1表示存活input wire [9:0] bullet_x, // 子弹的x坐标,用于在VGA屏幕上绘制子弹input wire [9:0] bullet_y, // 子弹的y坐标,用于在VGA屏幕上绘制子弹input wire bullet_valid,  // 子弹有效标志,1表示子弹处于飞行状态input wire bullet_is_enemy, // 子弹是否为敌方子弹标志,1表示敌方子弹input wire [15:0] score,    // 玩家的得分output reg hsync,         // VGA行同步信号,用于同步显示器的行扫描output reg vsync,         // VGA场同步信号,用于同步显示器的场扫描output reg [2:0] rgb      // RGB颜色信号,简化为3位,控制显示器的像素颜色
);// 行计数器,用于生成行同步信号和确定当前行位置
reg [9:0] hcnt; 
// 场计数器,用于生成场同步信号和确定当前场位置
reg [9:0] vcnt; // 时序逻辑,在时钟上升沿或复位信号下降沿触发
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin// 复位时,行计数器和场计数器清零hcnt <= 0; vcnt <= 0; // 行同步信号和场同步信号置高电平hsync <= 1; vsync <= 1; // RGB颜色信号清零rgb <= 0; end else begin// 行计数器加1hcnt <= hcnt + 1; if (hcnt == 10'd704) begin // 一行结束// 行计数器清零hcnt <= 0; // 场计数器加1vcnt <= vcnt + 1; if (vcnt == 10'd512) begin // 一场结束// 场计数器清零vcnt <= 0; endend// 行同步信号生成if (hcnt >= 10'd656 && hcnt < 10'd704) begin hsync <= 0; end else beginhsync <= 1; end// 场同步信号生成if (vcnt >= 10'd490 && vcnt < 10'd492) begin vsync <= 0; end else beginvsync <= 1; end// 有效显示区域判断if (hcnt < 10'd640 && vcnt < 10'd480) begin // 绘制我方坦克if (hcnt >= player_x && hcnt < player_x + 16 && vcnt >= player_y && vcnt < player_y + 16) begin// 我方坦克显示为白色rgb <= 3'b111; end // 绘制敌方坦克else if (enemy_alive && hcnt >= enemy_x && hcnt < enemy_x + 16 && vcnt >= enemy_y && vcnt < enemy_y + 16) begin// 敌方坦克显示为红色rgb <= 3'b001; end // 绘制子弹else if (bullet_valid && hcnt >= bullet_x && hcnt < bullet_x + 4 && vcnt >= bullet_y && vcnt < bullet_y + 4) beginif (bullet_is_enemy) begin// 敌方子弹显示为蓝色rgb <= 3'b010; end else begin// 我方子弹显示为绿色rgb <= 3'b100; endend else begin// 其他区域显示为黑色rgb <= 3'b000; endend else begin// 消隐期无显示,RGB颜色信号清零rgb <= 0; endend
endendmodule

7. 顶层模块

   功能
该模块是整个坦克大战游戏系统的顶层模块,将各个子模块连接起来,实现游戏的整体功能。包括按键消抖、坦克控制、子弹管理、得分计算和 VGA 显示等功能的集成。
    算法
通过实例化各个子模块,并将它们的输入输出信号进行连接,实现模块之间的通信和协同工作。根据按键输入信号,控制我方坦克的移动和射击;敌方坦克自动进行随机移动和射击;子弹管理模块处理子弹的发射和飞行;得分模块根据子弹击中情况更新得分;VGA 显示模块将游戏画面显示在显示器上。
    状态
整个游戏系统的状态由各个子模块的状态共同决定,包括按键状态、坦克位置状态、子弹状态、得分状态等。
    逻辑
在顶层模块中,首先对按键输入信号进行消抖处理,生成稳定的按键脉冲信号。然后根据按键脉冲信号生成移动方向控制信号,控制我方坦克的移动和射击。敌方坦克独立进行随机移动和射击。子弹管理模块根据坦克的发射信号处理子弹的发射和飞行,并进行碰撞检测。得分模块根据碰撞检测结果更新得分。最后,VGA 显示模块根据坦克、子弹的位置和得分信息,将游戏画面显示在显示器上。
    原理
通过模块化设计,将游戏的不同功能拆分成多个子模块,每个子模块负责特定的任务。顶层模块将这些子模块连接起来,通过信号的传递和处,实现整个游戏系统的功能。
    代码如下:

//顶层模块
module tank_war_top(input wire clk_50m,     // 开发板50MHz晶振时钟input wire rst_n,       // 全局复位(低有效)input wire [3:0] keys,  // 方向按键(上/下/左/右)input wire fire_key,    // 射击按键output wire hsync,      // VGA行同步信号output wire vsync,      // VGA场同步信号output wire [2:0] rgb   // RGB颜色信号(3位简化)
);// ================== 1. 时钟处理(50MHz→25MHz分频)==================
reg clk_25m;
clk_25m instance_name(// Clock out ports.clk_out1(clk_25m),     // output clk_out1// Status and control signals.reset(rst_n), // input reset.locked(),       // output locked// Clock in ports.clk_in1(clk_50m));      // input clk_in1// ================== 2. 按键消抖模块实例化 ==================
wire [3:0] key_pulse_down; // 方向键按下脉冲
wire fire_pulse_down;      // 射击键按下脉冲// 方向键消抖(4个按键,使用generate循环实例化)
genvar i;
generate
for(i=0; i<4; i=i+1) begin: dir_key_debouncekey_debounce u_dir_key_debounce(.clk(clk_50m),.rst_n(rst_n),.key_in(keys[i]),.key_out(),       // 消抖后电平信号(未使用,悬空).key_pulse_down(key_pulse_down[i]), // 按下脉冲.key_pulse_up()   // 释放脉冲(未使用,悬空));
end
endgenerate// 射击键消抖(单独实例化)
key_debounce u_fire_key_debounce(.clk(clk_50m),.rst_n(rst_n),.key_in(fire_key),.key_out(),       // 消抖后电平信号(未使用).key_pulse_down(fire_pulse_down), // 射击按下脉冲.key_pulse_up()   // 释放脉冲(未使用)
);// ================== 3. 移动方向生成 ==================
reg [2:0] move_dir; // 001-上,010-下,100-左,101-右
always @(*) beginmove_dir = 3'b000;if(key_pulse_down[0]) move_dir = 3'b001; // 上if(key_pulse_down[1]) move_dir = 3'b010; // 下if(key_pulse_down[2]) move_dir = 3'b100; // 左if(key_pulse_down[3]) move_dir = 3'b101; // 右
end// ================== 4. 我方坦克模块实例化 ==================
wire [9:0] player_x, player_y;
wire fire_en; // 我方坦克射击使能
player_tank u_player_tank(.clk(clk_50m),.rst_n(rst_n),.move_dir(move_dir),.fire_req(fire_pulse_down), // 射击请求来自射击键消抖脉冲.x(player_x),.y(player_y),.fire_en(fire_en)
);// ================== 5. 敌方坦克模块实例化 ==================
wire [9:0] enemy_x, enemy_y;
wire enemy_fire; // 敌方坦克射击信号
enemy_tank u_enemy_tank(.clk(clk_50m),.rst_n(rst_n),.x(enemy_x),.y(enemy_y),.enemy_fire(enemy_fire)
);// ================== 6. 子弹管理模块实例化 ==================
wire [9:0] bullet_x, bullet_y;
wire bullet_valid, bullet_is_enemy;
bullet_manager u_bullet_manager(.clk(clk_50m),.rst_n(rst_n),.player_x(player_x),.player_y(player_y),.enemy_x(enemy_x),.enemy_y(enemy_y),.fire_en(fire_en),       // 我方发射使能.enemy_fire(enemy_fire), // 敌方发射信号.bullet_x(bullet_x),.bullet_y(bullet_y),.bullet_valid(bullet_valid),.bullet_is_enemy(bullet_is_enemy)
);// ================== 7. 碰撞检测与得分模块 ==================
wire bullet_hit_enemy, bullet_hit_player;
// 我方子弹击中敌方坦克(子弹有效+我方子弹+碰撞检测)
assign bullet_hit_enemy = bullet_valid && !bullet_is_enemy && u_bullet_manager.collision_detect(bullet_x, bullet_y, enemy_x, enemy_y);
// 敌方子弹击中我方坦克(子弹有效+敌方子弹+碰撞检测)
assign bullet_hit_player = bullet_valid && bullet_is_enemy && u_bullet_manager.collision_detect(bullet_x, bullet_y, player_x, player_y);wire [15:0] score;
score_module u_score_module(.clk(clk_50m),.rst_n(rst_n),.bullet_hit_enemy(bullet_hit_enemy),.bullet_hit_player(bullet_hit_player),.score(score)
);// ================== 8. VGA显示模块实例化(25MHz时钟)==================
wire enemy_alive;
assign enemy_alive = 1'b1; // 简化处理,敌方坦克始终存活(可扩展为生命值系统)
vga_controller u_vga_controller(.clk(clk_25m),       .rst_n(rst_n),.player_x(player_x),.player_y(player_y),.enemy_x(enemy_x),.enemy_y(enemy_y),.enemy_alive(enemy_alive),.bullet_x(bullet_x),.bullet_y(bullet_y),.bullet_valid(bullet_valid),.bullet_is_enemy(bullet_is_enemy),.score(score),.hsync(hsync),.vsync(vsync),.rgb(rgb)
);endmodule

三,最终效果

1. 与开发板连接的约束文件如下所示


# 系统时钟与复位
set_property PACKAGE_PIN W19 [get_ports clk_50m]          ;# 50MHz有源晶振
set_property IOSTANDARD LVCMOS33 [get_ports clk_50m]set_property PACKAGE_PIN B21 [get_ports rst_n]            ;# 复位信号(拨码开关SW[0],闭合时低电平)
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]# 方向按键(轻触按键S[0]-S[3],按下低电平有效)
set_property PACKAGE_PIN D21 [get_ports {keys[0]}]         ;# 上
set_property PACKAGE_PIN E21 [get_ports {keys[1]}]         ;# 下
set_property PACKAGE_PIN G22 [get_ports {keys[2]}]         ;# 左
set_property PACKAGE_PIN G21 [get_ports {keys[3]}]         ;# 右
set_property IOSTANDARD LVCMOS33 [get_ports {keys[3:0]}]# 射击按键(轻触按键S[4],按下低电平有效)
set_property PACKAGE_PIN B22 [get_ports fire_key]
set_property IOSTANDARD LVCMOS33 [get_ports fire_key]# VGA显示接口(使用GPIO0扩展接口,单端信号)
set_property PACKAGE_PIN C18 [get_ports hsync]             ;# 行同步
set_property PACKAGE_PIN C19 [get_ports vsync]             ;# 场同步
set_property PACKAGE_PIN E19 [get_ports {rgb[0]}]          ;# 红色(最低位)
set_property IOSTANDARD LVCMOS33 [get_ports {rgb[0]}]      ;# 修改此处,将rgb[0]的IO标准改为LVCMOS33
set_property PACKAGE_PIN D19 [get_ports {rgb[1]}]          ;# 绿色
set_property PACKAGE_PIN F19 [get_ports {rgb[2]}]          ;# 蓝色(最高位)
set_property IOSTANDARD LVCMOS33 [get_ports {hsync vsync rgb[2:1]}]# 未使用的信号(保留注释,避免误分配)
# set_property PACKAGE_PIN ... [get_ports {LED[*] UART_*}]  ;# 其他外设可根据需要分配    

2. 改进与发展

我们可以改变显示路径,使其在TFT显示屏上显示,并不非要通过电脑显示屏,

因为通常情况下,开发板通过 HDMI 线连接笔记本电脑,是无法仅用笔记本电脑显示的。因为笔记本电脑的 HDMI 接口一般是输出接口,只能将笔记本电脑的画面输出到外部显示设备,而不能接收外部设备的信号来在笔记本电脑屏幕上显示。

想在笔记本电脑上显示开发板的内容,一种可能的方法是使用带有视频输入功能的采集卡。将开发板通过 HDMI 线连接到采集卡的 HDMI 输入接口,然后将采集卡通过 USB 等接口连接到笔记本电脑,再在笔记本电脑上安装采集卡对应的软件,通过软件来显示和录制开发板输出的视频信号。

同时更加详细的改进,可以增设多个坦克,优化坦克的显示设置等。同时还可以进行设置多个小游戏,通过开发板的按钮选择进入不同的游戏界面等。

详看下回分解!


相关文章:

FPGA实战项目1——坦克大战

FPGA实战项目1——坦克大战 根据模块化思想&#xff0c;可将此任务简单的进行模块拆分&#xff1a; 系统原理&#xff0c;模块划分&#xff0c;硬件架构&#xff0c;算法支持&#xff0c;Verilog实现框架 一&#xff0c;系统总体原理 1. 核心设计思想 硬件并行处理&#x…...

LeetCode 1781. 所有子字符串美丽值之和 题解

示例 输入&#xff1a;s "aabcb" 输出&#xff1a;5 解释&#xff1a;美丽值不为零的字符串包括 ["aab","aabc","aabcb","abcb","bcb"] &#xff0c;每一个字符串的美丽值都为 1这题光用文字解说还是无法达到讲…...

Spring Web MVC————入门(1)

今天开始正式带大家学习Spring部分的内容了&#xff0c;大家尝试去弄个专业版嗷&#xff0c;学习起来爽一点 在idea中下载这个插件就行了 我们之后开始创建Spring项目&#xff0c; 蓝色 部分自己起名&#xff0c;type选Maven&#xff0c;其他的默认就好了&#xff0c;之后nex…...

关于 js:1. 基础语法与核心概念

js 全称 JavaScript&#xff08;简称 JS&#xff09;&#xff0c;是 一种运行在浏览器和服务器端的脚本语言。 用途&#xff1a; 浏览器端交互&#xff08;如&#xff1a;点击按钮出现弹窗&#xff09; 网页动态内容渲染&#xff08;如&#xff1a;淘宝、京东页面更新&#xf…...

云境天合水陆安全漏电监测仪—迅速确定是否存在漏电现象

云境天合水陆安全漏电监测仪是一种专为水下及潮湿环境设计的电气安全检测设备&#xff0c;通过高灵敏度电磁传感器探测漏电电流产生的交变磁场&#xff0c;基于法拉第电磁感应定律&#xff0c;自动区分高灵敏度信号和低灵敏度信号&#xff0c;精准定位泄漏电源的具体位置。一旦…...

二、Hadoop狭义和广义的理解

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月6日&#x1fae0; 专栏&#xff1a;Hadoop教程 Hadoop 的双重身份&#xff1a;核心框架与生态系统 在大数据领域&#xff0c;Hadoop 是一个广为人知的概念&#xff0c;但它并非单指某一个软件&#xff0c;而是涵盖了两个层…...

DTU_DTU厂家_5G/4G DTU终端_DTU模块_厦门计讯物联科技有限公司

在物联网蓬勃发展的当下&#xff0c;数据的高效、稳定、可靠的传输成为关键。厦门计讯物联科技有限公司(以下简称“计讯物联”)作为国内工业物联网领域的核心厂商&#xff0c;专注于5G/4G DTU终端、DTU模块及无线数传设备的研发与生产&#xff0c;致力于为智慧城市、能源电力、…...

学习alpha,第2个alpha

alphas (-1 * ts_corr(rank(ts_delta(log(volume), 2)), rank(((close - open) / open)), 6)) 先分析操作符从左到右 ts_corr: Pearson 相关度量两个变量之间的线性关系。当变量呈正态分布且关系呈线性时&#xff0c;它最有效。 ts_corr(vwap, close, 20)是一个计算时间序列相…...

如何用爬虫获得按关键字搜索淘宝商品

在电商领域&#xff0c;获取淘宝商品的详细信息对于市场分析、选品上架、库存管理和价格策略制定等方面至关重要。淘宝作为国内知名的电商平台&#xff0c;提供了丰富的商品资源。通过 Python 爬虫技术&#xff0c;我们可以高效地获取淘宝商品的详细信息&#xff0c;包括商品名…...

Android SDK 开发中的 AAR 与 JAR 区别详解

在 Android SDK 开发中&#xff0c;构建项目时我们常常会看到生成两个不同的文件&#xff1a;一个是 build/outputs/aar/*.aar&#xff0c;另一个是 build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar。很多初学者会疑惑&#xff1a;它们之间有什么区别&am…...

Python cv2滤波与模糊处理:从原理到实战

在图像处理领域&#xff0c;滤波与模糊是预处理阶段的两大核心操作&#xff0c;既能消除噪声干扰&#xff0c;又能实现艺术化效果。本文将结合OpenCV的cv2库&#xff0c;系统讲解滤波与模糊的原理及Python实现&#xff0c;带你从理论到实战全面掌握这项技术。 一、滤波与模糊的…...

【SpringBoot3】idea找不到log符号

解决idea找不到log符号&#xff0c;Slf4j注解不起作用 如图 解决办法 pom.xml文件里要手动添加版本号 插件也要添加对应的版本号 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30&l…...

Android学习总结之Java和kotlin区别

一、空安全机制 真题 1&#xff1a;Kotlin 如何解决 Java 的 NullPointerException&#xff1f;对比两者在空安全上的设计差异 解析&#xff1a; 核心考点&#xff1a;Kotlin 可空类型系统&#xff08;?&#xff09;、安全操作符&#xff08;?./?:&#xff09;、非空断言&…...

C++笔记-二叉搜索树(包括key,key/value搜索场景等)

1.二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 1.若它的左子树不为空&#xff0c;则左子树上所有结点的值都小于等于根结点的值若它的右子树不为空&#xff0c;则2.右子树上所有结点的值都大于等于根结点…...

【从零开始学习RabbitMQ | 第二篇】生成交换机到MQ的可靠性保障

目录 ​编辑前言 交换机 Direct交换机与Fanout交换机的差异 Topic交换机 Topic交换机相比Direct交换机的差异 生成我们的交换机&#xff0c;队列&#xff0c;以及绑定关系 基于代码去生成交换机和队列 基于注解去声明队列和交换机 消息转换器 消息队列的高可靠性 发送…...

在 Sheel 中运行 Spark:开启高效数据处理之旅

在大数据处理领域&#xff0c;Apache Spark 凭借其强大的分布式计算能力&#xff0c;成为了众多开发者和企业处理海量数据的首选工具之一。而 Sheel 作为一种便捷的运行环境&#xff0c;在其中运行 Spark 可以充分发挥两者优势&#xff0c;实现高效的数据处理与分析。本文将详细…...

前端、XSS(跨站脚本攻击,Cross-Site Scripting)

XSS 攻击的三种主要类型 存储型 XSS&#xff08;持久型&#xff09; 原理&#xff1a;恶意脚本被永久存储在服务器&#xff08;如数据库、评论内容&#xff09;&#xff0c;用户访问包含恶意脚本的页面时触发示例&#xff1a;攻击者在论坛的评论区提交 &#xff0c;其他用户查…...

第六节:图像基本操作-像素级操作

一、数字图像处理基础 1.1 图像数字化原理 数字图像本质上是二维离散信号&#xff0c;由按矩阵排列的像素点构成。每个像素点的数值代表特定位置的亮度或色彩信息... 1.2 OpenCV核心数据结构 import cv2 import numpy as np# 读取图像文件 img cv2.imread(image.jpg)# 获取…...

【东枫科技】代理销售 NVIDIA DGX Spark 您的桌上有一台 Grace Blackwell AI 超级计算机。

NVIDIA GB10 Grace Blackwell超级芯片 FP4 AI 性能达到 1,000 AI TOPS 128GB 一致、统一的系统内存 ConnectX-7 智能网卡 高达 4TB 存储空间 150毫米长 x 150毫米宽 x 50.5毫米高 NVIDIA DGX™ Spark 搭载 NVIDIA GB10 Grace Blackwell 超级芯片&#xff0c;以节能紧凑的外形提…...

即插即用!长安汽车复旦提出LMPOcc:长期记忆先验实现占用预测任务新SOTA

导读 在基于视觉的自动驾驶感知算法当中&#xff0c;3D语义占用预测任务可以出色的对静态场景和动态目标同时进行建模&#xff0c;实现细粒度的场景理解&#xff0c;目前受到了来自学术界和工业界的广泛关注。 ©️【深蓝AI】编译 论文题目&#xff1a;ConRFT: A Reinfo…...

Kubernetes弹性伸缩:让应用自动应对流量洪峰与低谷

&#x1f680; Kubernetes弹性伸缩&#xff1a;让应用自动应对流量洪峰与低谷 &#x1f30d; 什么是弹性伸缩&#xff1f; 弹性伸缩&#xff08;Auto Scaling&#xff09;是指系统能够根据实时负载自动调整计算资源&#xff0c;以优化性能并降低成本。在 Kubernetes&#xff0…...

深入解析 Linux/Unix 通信机制:从原理到观测实践

深入解析 Linux/Unix 通信机制&#xff1a;从原理到观测实践 配图建议&#xff1a;Linux系统架构与通信机制全景示意图 一、开篇&#xff1a;理解“一切皆文件”的哲学 Unix/Linux 操作系统的核心灵魂在于其独特的设计哲学。当 Dennis Ritchie 和 Ken Thompson 在贝尔实验室开…...

Vue 2.0 详解全教程(含 Axios 封装 + 路由守卫 + 实战进阶)

目录 一、Vue 2.0 简介1.1 什么是 Vue&#xff1f;1.2 Vue 2.x 的主要特性 二、快速上手2.1 引入 Vue2.2 创建第一个 Vue 实例 三、核心概念详解3.1 模板语法3.2 数据绑定3.3 事件绑定3.4 计算属性 & 侦听器 四、组件系统4.1 定义全局组件4.2 单文件组件&#xff08;*.vue …...

《Python星球日记》 第36天:线性代数基础

名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏&#xff1a;《Python星球日记》&#xff0c;限时特价订阅中ing 目录 一、标量、…...

使用 Spring Boot 构建 REST API

使用 Spring Boot 构建 REST API 使用 Spring Boot 构建 REST API1. Spring Initializr构建springboot2. API 合同 & JSONAPI 协定什么是 JSON&#xff1f; 3.先测试什么是测试驱动开发&#xff1f;测试金字塔Red&#xff0c; Green&#xff0c; Refactor 循环 4. 实施 GET…...

PHP分页显示数据,在phpMyadmin中添加数据

<?php $conmysqli_connect(localhost,root,,stu); mysqli_query($con,"set names utf8"); //设置字符集为utf8 $sql"select * from teacher"; $resultmysqli_query($con,$sql); $countmysqli_num_rows($result); //记录总条数$count。 $pagesize10;//每…...

Spring Boot操作MongoDB的完整示例大全

以下是基于Spring Boot操作MongoDB的完整示例大全&#xff0c;涵盖增删改查、聚合查询、索引、事务等核心功能&#xff1a; 一、基础CRUD操作 1. 环境配置 依赖配置&#xff08;pom.xml&#xff09; <dependency><groupId>org.springframework.boot</groupId…...

SpringCloud入门教程合集(1)-SpringCloud简介与Eureka+Feign实现服务注册中心、服务提供与服务消费

场景 SpringCloud 总体架构与核心子项目 SpringCloud 总体架构 1. 基础设施层 服务注册与发现&#xff1a;Eureka/Nacos 配置中心&#xff1a;Spring Cloud Config/Nacos 消息总线&#xff1a;Spring Cloud Bus 2. 服务通信层 负载均衡&#xff1a;Ribbon/LoadBalancer…...

【Linuc】深入理解 Linux 文件权限

文章目录 一、权限基础解析1. 权限三元组2. 权限类型与数字映射二、查看文件权限三、修改权限实战1. chmod 命令符号模式数字模式(推荐)2. chown 修改归属四、特殊权限机制1. SetUID (Set User ID)2. SetGID (Set Group ID)3. Sticky Bit五、高级权限管理1. 默认权限控制2. A…...

ExtraMAME:复古游戏的快乐“时光机”

嘿&#xff0c;小伙伴们&#xff01;今天电脑天空要给大家安利一款超有趣的软件——ExtraMAME&#xff01;如果你对复古街机游戏念念不忘&#xff0c;那它绝对能成为你的快乐源泉&#xff0c;带你瞬间穿越回那个充满游戏机的黄金时代。 ExtraMAME是一款基于MAME&#xff08;Mu…...

没有 Mac,如何把 iOS App 成功上架?

开发者的 iOS 上架折腾记&#xff1a;没有 Mac&#xff0c;也能搞定&#xff1f; 最近在帮朋友把一个跨平台 Flutter 项目上架到 App Store&#xff0c;结果被 iOS 上架的那套流程卡得头都大了。其实这也不是第一次碰壁了——每次到“申请证书 打包 上传”的时候&#xff0c…...

使用VMware Workstation pro 17.5.1在Windows上安装Ubuntu 24.04.2的 详细步骤

一、准备工作 1. 下载Ubuntu 24.04.2 ISO镜像 官方下载地址&#xff1a;Ubuntu 24.04.2 (Noble Numbat) 选择 ubuntu-24.04.2-desktop-amd64.iso&#xff08;桌面版&#xff09;或 ubuntu-24.04.2-live-server-amd64.iso&#xff08;服务器版&#xff09;。 2. 确认系统要求…...

栈与队列详解及模拟实现

目录 ​​一、栈&#xff08;Stack&#xff09;&#xff1a;后进先出​ ​​1.1 什么是栈​ ​​1.2 栈的使用​​ ​​1.3 栈的模拟实现​ ​​1.4 栈的经典应用​​ ​​二、队列&#xff08;Queue&#xff09;&#xff1a;先进先出​ ​​2.1 什么是队列 2.2 队列的使…...

Cursor无法SSH远程连接服务器免密登录问题

在本地机器和Ubuntu服务器之间实现SSH远程免密连接&#xff0c;可按如下步骤操作&#xff1a; 1. 生成SSH密钥对 在本地机器上开启终端&#xff0c;使用以下命令生成SSH密钥对&#xff1a; ssh-keygen -t rsa按提示操作&#xff0c;一般直接回车&#xff0c;这样密钥会生成在…...

【Vue】全局事件总线 TodoList 事件总线

目录 一、 实现所有组件看到x事件 二、 实现$on $off 以及 $emit 总结不易~ 本章节对我有很大的收获&#xff0c; 希望对你也是&#xff01;&#xff01;&#xff01; 本节素材已上传至Gitee&#xff1a;yihaohhh/我爱Vue - Gitee.com 全局事件总线图&#xff1a; 本节素材…...

动态规划背包问题

一、0-1背包问题 0-1背包问题就是给定n个物品和一个容量为C的背包&#xff0c;物品i的重量是Wi,其价值是Vi。问&#xff1a;应该如何选择装入背包的物品&#xff0c;使总价值最大且总重量不超过C&#xff1f; 1.确定状态表示 dp[i][j] 表示在背包容量为j时&#xff0c;从下标…...

ctfshow web入门 web49

信息收集 此%非彼%&#xff0c;%0a中的%不会被识别&#xff0c;因为识别之前就已经自动转化为了换行符 所以和之前一样的解法&#xff0c;没什么好说的 if(isset($_GET[c])){$c$_GET[c];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|…...

AI+浏览器自动化:Nanobrowser Chrome 扩展的使用「详细教程」

AI+浏览器自动化:Nanobrowser Chrome 扩展的使用「详细教程」 一、前言二、Nanobrowser简介2.1 项目背景2.2 核心特性三、安装与配置3.1 安装方式3.1.1 Chrome Web Store安装3.1.2 手动安装最新版3.2 基本配置3.2.1 添加API Key3.2.2 选择模型3.2.3 其他设置四、核心功能详解4…...

【表设计】外键的取舍-分布式中逐渐消失的外键

在分布式大行其道的今天&#xff0c;为什么外键约束越来越少&#xff1f; 外键-数据链接带来强制完整性 在关系型数据库中&#xff0c;外键&#xff08;Foreign Key&#xff09;可以用于建立和强制两个表之间的数据链接。 在层次数据结构一篇的闭包表简单设计中&#xff0c;…...

HarmonyOS 5.0 分布式数据协同与跨设备同步​​

大家好&#xff0c;我是 V 哥。 使用 Mate 70有一段时间了&#xff0c;系统的丝滑使用起来那是爽得不要不要的&#xff0c;随着越来越多的应用适配&#xff0c;目前使用起来已经和4.3的兼容版本功能差异无碍了&#xff0c;还有些纯血鸿蒙独特的能力很是好用&#xff0c;比如&am…...

多行文本省略

方式1 兼容性不好 height: 100px; line-height: 25px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;方式2 利用浮动环绕、空白元素站位margin-top调整位置 <div class"wrap"><div class"more"…...

Adobe卸载清理工具Creative Cloud Cleaner Tool下载

Adobe Creative Cloud Cleaner Tool 是 Adobe 公司官方推出的一款卸载清理工具&#xff0c;主要用于清理 Creative Cloud 应用程序在安装、更新或卸载过程中可能遗留下来的错误配置文件、缓存、注册表项或其他系统级残留内容。相比一般的卸载程序&#xff0c;它更深入地处理系统…...

分布式、高并发-Day03

以下是 Day 3 详细学习内容&#xff08;线程池拒绝策略实战&#xff1a;DiscardOldestPolicy与CallerRunsPolicy&#xff0c;30 分钟完整计划&#xff09;&#xff0c;包含策略原理、分步代码实战和场景解析&#xff1a; &#x1f4d6; 今日学习目标 掌握DiscardOldestPolicy…...

高等数学第四章---不定积分(4.4有理函数的不定积分2)

&4.4有理函数的不定积分2 篇幅有限制&#xff0c;例题的解答会占大量字符&#xff0c;html限制字符为22000个左右。这里继续探讨上文的有理函数的不定积分。 一、三角函数有理式的不定积分 由 sin ⁡ x \sin x sinx, cos ⁡ x \cos x cosx 以及常数经过有限次加、减、…...

C++中指针使用详解(4)指针的高级应用汇总

C 中指针的高级应用非常丰富&#xff0c;掌握这些内容能让你写出更高性能、更底层控制力强的代码。下面是应用模块梳理和例子讲解。 目录预览 函数指针与回调机制指针数组 vs 数组指针指针与类成员函数&#xff08;成员函数指针&#xff09;智能指针&#xff08;unique_ptr, s…...

Java 8 非对称加密代码示例

以下是使用Java 8实现RSA非对称加密的完整代码示例&#xff0c;包括密钥生成、加密和解密过程。 1. 生成RSA密钥对 import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; impor…...

Linux环境基础与开发工具使用

1. Linux编译器vim 1.1 vim的基本概念讲解 vim有很多种模式&#xff0c;我们初学者常用的就是命令模式&#xff08;command mode&#xff09;、插入模式&#xff08;Insert mode&#xff09;和底行模式&#xff08;last line mode&#xff09;。 命令/正常模式(Normal mode) …...

【BUG】‘DetDataSample‘ object has no attribute ‘_gt_sem_seg‘

问题&#xff1a; 使用mmdetection框架使用COCO格式训练自定义数据集时&#xff0c;其中模型使用HTC模型时出现如下问题&#xff1a; AttributeError: ‘DetDataSample’ object has no attribute ‘_gt_sem_seg’. Did you mean: ‘gt_sem_seg’? results self(**data, mode…...

C# Winforms 本地化 多语言支持 字符串资源

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…...

ts bug 找不到模块或相应类型的声明,@符有红色波浪线

解决方法&#xff1a;在env.d.ts文件中添加以下代码&#xff0c;这段代码是一个 TypeScript 的声明文件&#xff0c;用于让 TypeScript 知道如何处理 Vue 单文件组件&#xff08;.vue 文件&#xff09;的导入。 /// <reference types"vite/client" /> // 声明…...