九、贪吃蛇之蛇身控制

九、贪吃蛇之蛇身控制

1. 目标

(1) 游戏难度决定蛇身移动的速度;

(2) 蛇身增长;

(3) 蛇身移动。

 

2. 蛇身速度控制

用计数器来控制蛇身移动的时间间隔,间隔短,移动快,游戏难度就越难。在游戏难度选择界面,用SW[2:0]选择难度。

//蛇身移动速度

else 

begin

    clk_cnt <= clk_cnt + 1;

    if(clk_cnt == speed)

    begin  

        clk_cnt <= 0;

 

 

 

 

 

速度分3档,0.5s0.25s0.125s蛇身移动一次。

always @(posedge clk or negedge rst_n) begin

    if(!rst_n) begin

        speed <= 24'd12500000;

        direct_r <= RIGHT;

    end

    else begin

        direct_r <= direct_next;

        case (sw[2:0])  //根据难度设置相应的速度

            3'b001:speed <= 24'd12500000;   //0.5s

            3'b010:speed <= 24'd6250000;    //0.25s

            3'b100:speed <= 24'd3125000;    //0.125s

            default:speed <= 24'd12500000;

        endcase    

    end

end 

 

3. 蛇身增长

cube_xcube_y表示蛇身各节的坐标。第1节代表蛇头。

 

 

 

获取蛇头坐标:

assign head_x = cube_x[0];

assign head_y = cube_y[0];

 

 

蛇和食物初始位置:

always @(posedge clk or negedge rst_n) begin

    if(!rst_n) begin

        clk_cnt <= 0;

 

        cube_x[0] <= 10;    //初始化蛇身有3节,这是第1节

        cube_y[0] <= 5;

        cube_x[1] <= 9;    //初始化蛇身有3节,这是第2节

        cube_y[1] <= 5;

        cube_x[2] <= 8;    //初始化蛇身有3节,这是第3节

        cube_y[2] <= 5;

        cube_x[3] <= 0;    

        cube_y[3] <= 0;

 

always @(posedge clk or negedge rst_n) begin

    if(!rst_n) begin

        clk_cnt <= 0;

        apple_x <= 24;

        apple_y <= 10;

        add_cube <= 0;

 

当蛇头和食物的坐标相同时,代表吃了一个食物,蛇身要增长,add_cube1.每吃下一个食物,蛇身加1,相应的is_exist位置变为1is_exist16位,即蛇身最长为16格,is_exist

1,那一格显示,反之,则不显示。

 

 

 

每次add_cube1时,表示蛇身长度的信号cube_num1.is_exist[cube_num] = 1表示让第cube_num位显示出来。

always @(posedge clk or negedge rst_n) begin

    if(!rst_n) begin

        is_exist <= 16'd7;

        cube_num <= 3;  //蛇身初始长度为3,is_exist = 0000_0000_0000_0111表示前3位出现

        addcube_state <= 0;

    end

    else if(game_status == RESTART) begin

          is_exist <= 16'd7;

        cube_num <= 3;  

        addcube_state <= 0;

    end

    else begin

        case(addcube_state) //判断蛇头是否与食物坐标重合

            0:begin

                if(add_cube) begin

                    cube_num <= cube_num + 1;

                    is_exist[cube_num] <= 1;

                    addcube_state <= 1; //吃下食物信号

                end

            end

            1:begin

                if(!add_cube)

                    addcube_state <= 0;

            end

        endcase

    end

end

 

4. 蛇撞墙或撞自身

(1) 撞墙

当蛇头坐标跟墙的坐标重合,代表撞墙:

//撞墙

if((direct_r == UP && cube_y[0] == 1) ||

   (direct_r == DOWN && cube_y[0] == 28) ||

   (direct_r == RIGHT && cube_x[0] == 38) ||  

   (direct_r == LEFT && cube_x[0] == 1))

   hit_wall <= 1;

   

 

(2) 撞自身

当蛇头坐标跟身体某一节的坐标重合,代表撞到自身:

//撞自身

else if((cube_x[0] == cube_x[1] && cube_y[0] == cube_y[1] && is_exist[1] == 1) || //比较第1节

        (cube_x[0] == cube_x[2] && cube_y[0] == cube_y[2] && is_exist[2] == 1) || //比较第2节

        (cube_x[0] == cube_x[3] && cube_y[0] == cube_y[3] && is_exist[3] == 1) || //比较第3节

        (cube_x[0] == cube_x[4] && cube_y[0] == cube_y[4] && is_exist[4] == 1) || //比较第4节

        (cube_x[0] == cube_x[5] && cube_y[0] == cube_y[5] && is_exist[5] == 1) || //比较第5节

        (cube_x[0] == cube_x[6] && cube_y[0] == cube_y[6] && is_exist[6] == 1) || //比较第6节

        (cube_x[0] == cube_x[7] && cube_y[0] == cube_y[7] && is_exist[7] == 1) || //比较第7节

        (cube_x[0] == cube_x[8] && cube_y[0] == cube_y[8] && is_exist[8] == 1) || //比较第8节

        (cube_x[0] == cube_x[9] && cube_y[0] == cube_y[9] && is_exist[9] == 1) || //比较第9节

        (cube_x[0] == cube_x[10] && cube_y[0] == cube_y[10] && is_exist[10] == 1) || //比较第10节

        (cube_x[0] == cube_x[11] && cube_y[0] == cube_y[11] && is_exist[11] == 1) || //比较第11节

        (cube_x[0] == cube_x[12] && cube_y[0] == cube_y[12] && is_exist[12] == 1) || //比较第12节

        (cube_x[0] == cube_x[13] && cube_y[0] == cube_y[13] && is_exist[13] == 1) || //比较第13节

        (cube_x[0] == cube_x[14] && cube_y[0] == cube_y[14] && is_exist[14] == 1) || //比较第14节

        (cube_x[0] == cube_x[15] && cube_y[0] == cube_y[15] && is_exist[15] == 1) || //比较第15节

        )

 

5. 蛇身的移动路径

蛇头移动有三种情况:撞墙、撞自身、移动到下一个位置。蛇身每一节把坐标传递给下一节。

 

 

//蛇身移动,前位坐标传给后位

else begin

    cube_x[1] <= cube_x[0];

    cube_y[1] <= cube_y[0];

 

    cube_x[2] <= cube_x[1];

    cube_y[2] <= cube_y[1];

 

    cube_x[3] <= cube_x[2];

    cube_y[3] <= cube_y[2];

 

    cube_x[4] <= cube_x[3];

    cube_y[4] <= cube_y[3];

 

    cube_x[5] <= cube_x[4];

    cube_y[5] <= cube_y[4];

 

    cube_x[6] <= cube_x[5];

    cube_y[6] <= cube_y[5];

 

    cube_x[7] <= cube_x[6];

    cube_y[7] <= cube_y[6];

 

    cube_x[8] <= cube_x[7];

    cube_y[8] <= cube_y[7];

 

    cube_x[9] <= cube_x[8];

    cube_y[9] <= cube_y[8];

 

    cube_x[10] <= cube_x[9];

    cube_y[10] <= cube_y[9];

 

    cube_x[11] <= cube_x[10];

    cube_y[11] <= cube_y[10];

 

    cube_x[12] <= cube_x[11];

    cube_y[12] <= cube_y[11];

 

    cube_x[13] <= cube_x[12];

    cube_y[13] <= cube_y[12];

 

    cube_x[14] <= cube_x[13];

    cube_y[14] <= cube_y[13];

 

    cube_x[15] <= cube_x[14];

    cube_y[15] <= cube_y[14];

 

6. 蛇头方向控制

蛇头方向通过四个按键KEY[3:0]控制,用状态机实现,参考(八、状态机设计)。

 

7. 蛇色块显示

当坐标pos_x[9:4]pos_y[9:4]与蛇的坐标吻合时,显示蛇身。墙壁的显示也是这样。

//显示墙和蛇身

always @(pos_x or pos_y) begin

    if(pos_x >=0 && pos_x <640 && pos_y >= 0

       && pos_y < 480) begin

        if(pos_x[9:4] == 0 || pos_y[9:4] == 0

        || pos_x[9:4] == 39 || pos_y == 29)

            snake_show = WALL; //显示墙

        else if(pos_x[9:4] == cube_x[0] && 

                pos_y[9:4] == cube_y[0] &&

                is_exist[0] == 1)

                    snake_show = (snake_display == 1)?

                                 HEAD : NONE; //蛇头

        else if

        ((pos_x[9:4] = cube_x[1] && pos_y[9:4] == cube_y[1] && 

        is_exist[1] == 1) ||

        (pos_x[9:4] = cube_x[2] && pos_y[9:4] == cube_y[2] && 

        is_exist[2] == 1) ||

        (pos_x[9:4] = cube_x[3] && pos_y[9:4] == cube_y[3] && 

        is_exist[3] == 1) ||

        (pos_x[9:4] = cube_x[4] && pos_y[9:4] == cube_y[4] && 

        is_exist[4] == 1) ||

        (pos_x[9:4] = cube_x[5] && pos_y[9:4] == cube_y[5] && 

        is_exist[5] == 1) ||

        (pos_x[9:4] = cube_x[6] && pos_y[9:4] == cube_y[6] && 

        is_exist[6] == 1) ||

        (pos_x[9:4] = cube_x[1] && pos_y[9:4] == cube_y[1] && 

        is_exist[1] == 1) ||

        (pos_x[9:4] = cube_x[2] && pos_y[9:4] == cube_y[2] && 

        is_exist[2] == 1) ||

        (pos_x[9:4] = cube_x[3] && pos_y[9:4] == cube_y[3] && 

        is_exist[3] == 1) ||

        (pos_x[9:4] = cube_x[4] && pos_y[9:4] == cube_y[4] && 

        is_exist[4] == 1) ||

        (pos_x[9:4] = cube_x[5] && pos_y[9:4] == cube_y[5] && 

        is_exist[5] == 1) ||

        (pos_x[9:4] = cube_x[6] && pos_y[9:4] == cube_y[6] && 

        is_exist[6] == 1) ||

        (pos_x[9:4] = cube_x[7] && pos_y[9:4] == cube_y[7] && 

        is_exist[7] == 1) ||

        (pos_x[9:4] = cube_x[8] && pos_y[9:4] == cube_y[8] && 

        is_exist[8] == 1) ||

        (pos_x[9:4] = cube_x[9] && pos_y[9:4] == cube_y[9] && 

        is_exist[9] == 1) ||

        (pos_x[9:4] = cube_x[10] && pos_y[9:4] == cube_y[10] && 

        is_exist[10] == 1) ||

        (pos_x[9:4] = cube_x[11] && pos_y[9:4] == cube_y[11] && 

        is_exist[11] == 1) ||

        (pos_x[9:4] = cube_x[12] && pos_y[9:4] == cube_y[12] && 

        is_exist[12] == 1) ||

        (pos_x[9:4] = cube_x[13] && pos_y[9:4] == cube_y[13] && 

        is_exist[13] == 1) ||

        (pos_x[9:4] = cube_x[14] && pos_y[9:4] == cube_y[14] && 

        is_exist[14] == 1) ||

        (pos_x[9:4] = cube_x[15] && pos_y[9:4] == cube_y[15] && 

        is_exist[15] == 1))

        snake_show = (snake_display == 1) ? BODY : NONE; //身体

        else

        snake_show = NONE;

       end

       

 

为了便于分辨蛇身的每节,把每节第一列前三个像素显示黑色。

//给蛇身每节第一列前三个像素显示黑色,便于分辨

else if (game_status == PLAY | game_status == START) begin

    cnt <= 0;

    if(pos_x[9:4] == apple_x && pos_y[9:4] == apple_y) begin

        vga_rgb = PINK;

    end

    else if(snake_show == NONE) begin

        vga_rgb = BLACK;

    end

    else if(snake_show == WALL) begin

        vga_rgb = RED;

    end

    else if(snake_show == HEAD | snake_show == BODY) begin

        case({pos_x[3:0],pos_y[3:0]})

            8'b00000000:vga_rgb = BLACK;

            8'b00000001:vga_rgb = BLACK;

            8'b00000010:vga_rgb = BLACK;

            default:vga_rgb = (snake_show == HEAD) ? GREEN : BLUE;

        endcase

    end

    else begin

        vga_rgb = BLACK;

    end

end

 

 

参考文献:

https://mp.weixin.qq.com/s/4qgOI4xP1nzufEQLQpFUHA

 

热门相关:傅爷怀里的假千金真绝了   重生不嫁豪门   纣临   南少,你老婆又跑了   九星之主