八、贪吃蛇之状态机设计

八、贪吃蛇之状态机设计

贪吃游戏采用mealy状态机。

1. 游戏状态控制状态机

用一段式编码来完成游戏控制过程:

 

 

各状态说明:

(1) RESTART:复位后进入重启状态,屏幕显示欢迎界面,6秒后进入游戏难度选择界面,等待选择难度。

(2) START:用SW[2:0]选择游戏难度,按下KEY[3:0]任意键,开始游戏。进入PLAY状态。

(3) PLAY:按上下左右方向键,控制蛇身移动,蛇身最长16格,吃食物获得分数显示在数码管上。

(4) DIE:当蛇撞墙、撞自身或分数达到100时,蛇身闪烁3秒,屏幕显示总分,游戏结束。

//游戏控制模块,根据游戏状态,产生相应控制信号

module game_ctrl_unit

(

    input clk, //25MHz

    input rst_n,

 

    input key0_right, //选方向

    input key1_left,

    input key2_down,

    input key3_up,

    input [2:0] sw, //选难度

 

    input hit_wall, //撞墙

    input hit_body, //撞自身

    input [11:0]bcd_data, //分数

 

    output reg snake_display, //蛇整体显示标识

    output reg [1:0]game_status //当前游戏状态

);

 

localparam RESTART = 2'b00; //游戏重启

localparam START = 2'b01; //游戏开始

localparam PLAY = 2'b10; //游戏进行

localparam DIE = 2'b11; //游戏结束

 

reg [32:0]cnt_clk ;

reg [31:0]flash_cnt ; //蛇闪烁时间计数器

 

//状态机定义初始状态,并描述状态转移与输出

always @(posedge clk or negedge rst_n) begin

    if (!rst_n) begin

        cnt_clk<=0;

        flash_cnt<=0;

        snake_display<=1;

        game_status<=RESTART; //复位后进入重启状态

    end

 

    else begin

        case (game_status)

            RESTART:begin

                cnt_clk<=cnt_clk+1;

                if(cnt_clk>150000000)begin

                    //欢迎界面

                    if(sw[0]||sw[1]||sw[2]) begin

                        game_status <= START;

                        //选择游戏难度

                    end

                end

                else begin

                    game_status <= RESTART;

                end

            end

 

            START:begin

                if((~key0_right) || (~key1_left)

                   || (~key2_down) || (~key3_up))

                   game_status <= PLAY; //按键开始

                else

                    game_status <= START;

            end

 

            PLAY:begin

                if(hit_wall || hit_body || bcd_data[11:8]>=1'd1)

                    game_status <= DIE;

                else    

                    game_status <= PLAY;

            end

 

            //蛇身闪烁,间隔0.5秒

            DIE:begin

                if(flash_cnt <= 100_000_000) begin //

                    flash_cnt <= flash_cnt+1'b1;

                    if(flash_cnt == 12_500_000) begin

                        snake_display <= 1'b0;

                    end

                    else if(flash_cnt == 25_000_000) begin

                        snake_display <= 1'b1;

                    end

                    else if(flash_cnt == 37_500_000) begin

                        snake_display <= 1'b0;

                    end

                    else if(flash_cnt == 50_000_000) begin

                        snake_display <= 1'b1;

                    end

                    else if(flash_cnt == 62_500_000) begin

                        snake_display <= 1'b0;

                    end

                    else if(flash_cnt == 75_000_000) begin

                        snake_display <= 1'b1;

                    end

                end

                //游戏结束,按任意键,重新开始

                else if((~key0_right) || (~key1_left)

                   || (~key2_down) || (~key3_up))begin

                    cnt_clk<=0;

                    flash_cnt<=0;

                    game_status <= RESTART;

                   end

 

                else begin

                    game_status <= DIE;

                end

            end

 

            default: begin

                game_status <= RESTART;

            end 

        endcase

    end

end

endmodule

 

其中,在RESTART状态,cnt_clk >150000000,这里用大于而不是判断相等,原因是如果等了6秒,没有及时选择难度,就错过了,所有用大于。在DIE状态,cnt_clkflash_cnt都要清零,是为了游戏重启后,和初始状态一样。

2. 蛇身方向控制状态机

蛇身方向状态用三段式代码编写,分上下左右四个状态。

 

 

 

(1) 第一段:状态转换时序逻辑,初始化状态。

//蛇身方向初始化状态

always @(posedge clk or negedge rst_n) begin

    if (!rst_n) begin

        speed<=24'd12500000;

        direct_r<=RIGHT;

    end

   

这里初始化的方向是右,实际是根据key值判断。

 

(2) 第二段:状态转换的组合逻辑。

always @(*) begin

    case(direct_r)

        UP: begin

            if(~key1_left)

                direct_next = LEFT;

            else if(~key0_right)

                direct_next = RIGHT;

            else    

                direct_next = UP;

        end

        DOWN: begin

            if(~key1_left)

                direct_next = LEFT;

            else if(~key0_right)

                direct_next = RIGHT;

            else    

                direct_next = DOWN;

        end

        LEFT: begin

            if(~key3_up)

                direct_next = UP;

            else if(~key2_down)

                direct_next = DOWN;

            else    

                direct_next = LEFT;

        end

        RIGHT: begin

            if(~key3_up)

                direct_next = UP;

            else if(~key2_down)

                direct_next = DOWN;

            else    

                direct_next = RIGHT;

        end

    endcase

end

 

 

(3) 第三段:根据当前状态和输入产生输出的时序逻辑。

if(direct_r==UP)begin

    if(cube_y[0] == 1)

        hit_wall <= 1; //撞上墙

    else    

        cube_y[0] <= cube_y[0]-1;

end

 

else if(direct_r==DOWN)begin

    if(cube_y[0] == 28)

        hit_wall <= 1; //撞下墙

    else

        cube_y[0] <= cube_y[0]+1;

end

 

if(direct_r==LEFT)begin

    if(cube_x[0] == 1)

        hit_wall <= 1; //撞左墙

    else    

        cube_x[0] <= cube_x[0]-1;

end

 

else if(direct_r==RIGHT)begin

    if(cube_x[0] == 38)

        hit_wall <= 1; //撞右墙

    else

        cube_x[0] <= cube_x[0]+1;

end

 

参考文献

https://mp.weixin.qq.com/s/YSrbtbRneUtc1d8IYhk-RA

posted on 2024-05-08 11:41  yf.x  阅读(41)  评论(0编辑  收藏  举报

导航