C语言版贪吃蛇:第四部分

C语言版贪吃蛇:第四部分

“马上就要结束了!”

本章学习:

  1. 判断是否吃到食物并增长身体
  2. 判断是否撞墙或吃到自己


判断是否存活

  1. 定义一个变量 life 表示是否活着
    1. life=1表示活着,life=0表示挂掉
    2. 记得在 init( ) 里给 life 初始值1
    3. 主函数的while条件里要加life
  2. 判断撞墙
    1. 判断蛇头坐标与墙是否重合
  3. 判断吃到自己
    1. 依次判断蛇头坐标是否与每节蛇身重合
  4. 记得要在move()函数的最后加上check_life( )
    1. 每移动一次都要判断是否活着
      参考代码如下
// 判断蛇是否还活着
void check_life()
{
    // 判断是否撞墙,只需判断蛇头坐标是否与墙重合即可
    if (Snake[head].x == 1 || Snake[head].x == 70 || Snake[head].y == 1 || Snake[head].y == 20)
        Game_over();
    // 判断是否吃到自己
    // 方法:依次判断蛇头是否与蛇身重合
    int i, j = Snake[head].next;
    for (i = 1; i < lenth; i++)
    {
        // 重合就结束游戏
        if (Snake[j].x == Snake[head].x && Snake[j].y == Snake[head].y)
            Game_over();
        j = Snake[j].next;
    }
}


增长身体

  1. 直接判断蛇头坐标与食物是否重合
  2. 若重合,则增长
    1. 增长分为水平和竖直增长
  3. 具体可参考代码

参考代码如下

// 判断是否吃到食物
void check_food()
{
    // 检验蛇头是否与食物重合
    // 重合则增长身体(在蛇尾后增加一节),并重新放置一个食物
    if (Snake[head].x == food_x && Snake[head].y == food_y)
    {
        // 增长身体,要注意水平增长还是竖直增长
        // 通过最后两节来判断
        // 水平坐标相同则水平增长
        // 竖直坐标相同则竖直增长
        lenth++;
        // 水平增长
        if (Snake[nail].x == Snake[Snake[nail].pre].x)
        {
            // 水平坐标 x 不变
            Snake[lenth].x = Snake[nail].x;
            // 竖直坐标向蛇尾方向加 1 或 减 1
            // 为了省去判断,这里用蛇尾竖直坐标减蛇尾前一个竖直坐标表示
            Snake[lenth].y = Snake[nail].y + (Snake[nail].y - Snake[Snake[nail].pre].y);
            // 将新的一节接上去
            Snake[nail].next = lenth;
            Snake[lenth].pre = nail;
            // 设置新的蛇尾
            nail = lenth;
        }
        // 竖直增长,将上面的 x 变成 y 即可
        if (Snake[nail].y == Snake[Snake[nail].pre].y)
        {
            Snake[lenth].y = Snake[nail].y;
            Snake[lenth].x = Snake[nail].x + (Snake[nail].x - Snake[Snake[nail].pre].x);
            Snake[nail].next = lenth;
            Snake[lenth].pre = nail;
            nail = lenth;
        }
        // 放置新的食物
        printfood();
    }
}



完整源代码

/*
    这里是贪吃蛇源代码

    chapter 1:
    解释下头文件:
    time.h 生成随机数要用到
    windows.h 要用到里面的函数 gotoxy
    ---------
    1.画围墙

    ===========================

    chapter 2:
    目标:绘制蛇,食物
        蛇的存储结构:简单的链表(数组实现功能)
        食物:随机数的生成

    ===========================

    chapter 3:
    目标: 实现蛇的运动
        判断键盘输入
        改变蛇的坐标
        重绘图像

    ===========================

    chapter 4:
    目标: 判断是否吃到食物
        增长身体
        判断是否撞墙,是否吃到自己

    ===========================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#include <conio.h>
// 定义一个结构体用来存储蛇的每节身体的坐标
struct snake
{
    int x, y;
    int next; //保存当前节点的下一个节点的位置(数组下标)
    int pre;  //保存上一个节点的位置
} Snake[100];

// 定义三个变量存 蛇长,蛇头,蛇尾
int lenth, head, nail;
// 食物位置
int food_x, food_y;
// 储存蛇的当前,和上一次的移动方向
int direation, direation_pre;
// 存储蛇是否还活着,1活着
int life;
// 光标移动函数
void gotoxy(int x, int y)
{
    COORD coord = {x, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// 打印蛇
void printsnake()
{
    int i, j = head;
    for (i = 1; i <= lenth; i++)
    {
        // 注意 里面的参数是j,j从蛇头移动到蛇尾
        gotoxy(Snake[j].x, Snake[j].y);
        printf("*");
        j = Snake[j].next;
    }
}
// 清除蛇
void clear()
{
    int i, j = head;
    for (i = 1; i <= lenth; i++)
    {
        // 注意 里面的参数是j,j从蛇头移动到蛇尾
        gotoxy(Snake[j].x, Snake[j].y);
        printf(" ");
        j = Snake[j].next;
    }
}
// 游戏结束
void Game_over()
{
    // 将 life 设置为0,表示游戏结束,用来结束主函数中的while循环
    life = 0;
    // 清屏
    system("cls");
    // 打印提示信息
    gotoxy(10, 10);
    printf("GAME OVER");
}
// 判断食物是否合法
int ok_food()
{
    // 判断是否和墙重合
    // 横坐标不能等于1,或70 ; 纵坐标不能等于1,或20
    if (food_x <= 1 || food_x >= 70)
        return 0;
    if (food_y <= 1 || food_y >= 20)
        return 0;
    // 判断是否和蛇重合
    int j = head;
    for (int i = 1; i <= lenth; i++)
    {
        if (food_x == Snake[j].x && food_y == Snake[j].y)
            return 0;
        j = Snake[j].next;
    }
    // 都没有,则返回1
    return 1;
}

// 打印食物
void printfood()
{
    // 不断产生随机数,知道坐标符合要求
    do
    {
        srand((unsigned long)time(0));
        food_x = rand() % 70;
        food_y = rand() % 20;
    } while (!ok_food());
    // ok_food()为判断食物是否合法的函数,合法返回1,不合法返回0
    gotoxy(food_x, food_y);
    printf("@");
}
// 判断是否吃到食物
void check_food()
{
    // 检验蛇头是否与食物重合
    // 重合则增长身体(在蛇尾后增加一节),并重新放置一个食物
    if (Snake[head].x == food_x && Snake[head].y == food_y)
    {
        // 增长身体,要注意水平增长还是竖直增长
        // 通过最后两节来判断
        // 水平坐标相同则水平增长
        // 竖直坐标相同则竖直增长
        lenth++;
        // 水平增长
        if (Snake[nail].x == Snake[Snake[nail].pre].x)
        {
            // 水平坐标 x 不变
            Snake[lenth].x = Snake[nail].x;
            // 竖直坐标向蛇尾方向加 1 或 减 1
            // 为了省去判断,这里用蛇尾竖直坐标减蛇尾前一个竖直坐标表示
            Snake[lenth].y = Snake[nail].y + (Snake[nail].y - Snake[Snake[nail].pre].y);
            // 将新的一节接上去
            Snake[nail].next = lenth;
            Snake[lenth].pre = nail;
            // 设置新的蛇尾
            nail = lenth;
        }
        // 竖直增长,将上面的 x 变成 y 即可
        if (Snake[nail].y == Snake[Snake[nail].pre].y)
        {
            Snake[lenth].y = Snake[nail].y;
            Snake[lenth].x = Snake[nail].x + (Snake[nail].x - Snake[Snake[nail].pre].x);
            Snake[nail].next = lenth;
            Snake[lenth].pre = nail;
            nail = lenth;
        }
        // 放置新的食物
        printfood();
    }
}
// 判断蛇是否还活着
void check_life()
{
    // 判断是否撞墙,只需判断蛇头坐标是否与墙重合即可
    if (Snake[head].x == 1 || Snake[head].x == 70 || Snake[head].y == 1 || Snake[head].y == 20)
        Game_over();
    // 判断是否吃到自己
    // 方法:依次判断蛇头是否与蛇身重合
    int i, j = Snake[head].next;
    for (i = 1; i < lenth; i++)
    {
        // 重合就结束游戏
        if (Snake[j].x == Snake[head].x && Snake[j].y == Snake[head].y)
            Game_over();
        j = Snake[j].next;
    }
}
// 实现蛇的坐标改变
void move()
{
    // 擦掉原来的蛇
    clear();
    // 根据 direation 选择运动方向
    switch (direation)
    {
    case 'w':
    {
        // 蛇尾变到蛇头前面,向上运动,则纵坐标减一(回忆之前介绍的坐标轴)
        Snake[nail].x = Snake[head].x;
        Snake[nail].y = Snake[head].y - 1;
        // 将新蛇头的下一个位置指向旧蛇头
        Snake[nail].next = head;
        // 将旧蛇头的前一个位置指向新蛇头
        Snake[head].pre = nail;
        // 新蛇头是原来的蛇尾
        head = nail;
        // 新蛇尾是原来蛇尾的前一节
        nail = Snake[nail].pre;
        break;
    }
    case 's':
    {
        Snake[nail].x = Snake[head].x;
        Snake[nail].y = Snake[head].y + 1;
        Snake[nail].next = head;
        Snake[head].pre = nail;
        head = nail;
        nail = Snake[nail].pre;
        break;
    }
    case 'a':
    {
        Snake[nail].x = Snake[head].x - 1;
        Snake[nail].y = Snake[head].y;
        Snake[nail].next = head;
        Snake[head].pre = nail;
        head = nail;
        nail = Snake[nail].pre;
        break;
    }
    case 'd':
    {
        Snake[nail].x = Snake[head].x + 1;
        Snake[nail].y = Snake[head].y;
        Snake[nail].next = head;
        Snake[head].pre = nail;
        head = nail;
        nail = Snake[nail].pre;
        break;
    }
    }
    printsnake();
    check_food();
    check_life();
}

// 绘制围墙
void printwall()
{
    /*
        chapter 1 绘制围墙部分
        围墙大小 70*20, 长70,宽20
    */

    // 绘制水平围墙,
    for (int i = 1; i <= 70; i++)
    {
        gotoxy(i, 1);
        printf("#");
        gotoxy(i, 20);
        printf("#");
    }
    // 绘制竖直围墙
    for (int i = 1; i <= 20; i++)
    {
        gotoxy(1, i);
        printf("#");
        gotoxy(70, i);
        printf("#");
    }
}
void init()
{
    printwall();
    // 初始化蛇
    Snake[1].x = 4;
    Snake[1].y = 4;
    Snake[2].x = 4;
    Snake[2].y = 5; 
    head = 1;
    nail = 2;
    lenth = 2;
    Snake[head].next = nail;
    Snake[nail].pre = head;
    direation = 's';
    direation_pre = 's';
    life=1;
    // 第一次打印蛇
    printsnake();
    // 第一次打印食物
    printfood();
}
int main()
{
    init(); // 初始化函数,绘制围墙
    char c;
    c=getch();
    while (1)
    {
        // 没有读入则按照之前的方向一直运动
        while (!kbhit() && life)
        {
            move();
            Sleep(150);
        }
        // 有读入则停下来改变方向
        // 先记下原来的方向
        direation_pre = direation;
        // 读入要改变的方向
        direation = getch();
        // 判断方向是否合法,如果和上次相反,则保持原来方向
        if (direation == 'w' && direation_pre == 's' || direation == 's' && direation_pre == 'w')
            direation = direation_pre;
        if (direation == 'a' && direation_pre == 'd' || direation == 'd' && direation_pre == 'a')
            direation = direation_pre;
    }
    system("pause");
    return 0;
}
//游戏状态定义 #define GAME_STATE_WAIT 0 //游戏等待状态 #define GAME_STATE_RUN 1 //游戏运行状态 #define GAME_STATE_END 2 //游戏结束状态 //界面相关物件尺寸定义 #define WALL_WIDTH 80 //外墙从左部到游戏区的宽度 #define WALL_HEIGHT 80 //外墙从顶部到游戏区的高度 #define BMP_SCORE_BOARD_WIDTH 256 //分数位图板的宽度 #define BMP_SCORE_BOARD_HEIGHT 55 //分数位图板的高度 #define BMP_WALL_WIDTH 16 //墙位图的宽度 #define BMP_WALL_HEIGHT 16 //墙位图的高度 LRESULT CALLBACK WndProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam); void DrawGame(void); void ShellDraw( HDC hdc ); void GameAreaDraw(HDC hdc); void OnTimer(UINT uTIMER_ID); void StartGame( void ); void EndGame( void ); //创建一个桌子 CTable table; int tableBlockWidth = 0; //桌子的格子的宽度 int tableBlockHeight = 0; //桌子的格子的高度 int iScores = 0; //游戏的得分 UINT uGameState = GAME_STATE_WAIT; //当前游戏状态 HDC windowDC = NULL; //windows屏幕设备 HDC bufferDC = NULL; //缓冲设备环境 HDC picDC = NULL; //snake图像内存设备 HDC endDC = NULL; //游戏终结图像内存设备 HDC scoreDC = NULL; //分数板内存设备 HWND hAppWnd = NULL; //本application窗口句柄 HBITMAP picBMP = NULL; //snake图像位图句柄 HBITMAP bufferBMP = NULL; //缓冲位图句柄 HBITMAP endBMP = NULL; //游戏终结图像内存句柄 HBITMAP hbmpWall = NULL; //墙位图句柄 HBITMAP hbmpScore = NULL; //分数板位图句柄 HBRUSH hbrushWall = NULL; //墙画刷 //定时器标识 UINT uSnakeMoveTimer; //蛇的移动 UINT uFoodAddTimer; //水果的产生
用windows api 做的贪吃蛇 #include #include"resource.h" #include"Node.h" #include #include TCHAR szAppname[] = TEXT("Snack_eat"); #define SIDE (x_Client/80) #define x_Client 800 #define y_Client 800 #define X_MAX 800-20-SIDE //点x的范围 #define Y_MAX 800-60-SIDE //点y的范围 #define TIME_ID 1 #define SECOND 100 #define NUM_POINT 10 //点的总个数 #define ADD_SCORE 10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd; //窗口句柄 MSG msg; //消息 WNDCLASS wndclass; //窗口类 HACCEL hAccel;//加速键句柄 wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口的水平和垂直尺寸被改变时,窗口被重绘 wndclass.lpfnWndProc = WndProc; //窗口过程为WndProc函数 wndclass.cbClsExtra = 0; //预留额外空间 wndclass.cbWndExtra = 0; //预留额外空间 wndclass.hInstance = hInstance; //应用程序的实例句柄,WinMain的第一个参数 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); //设置图标 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); //载入预定义的鼠标指针 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //设置画刷 wndclass.lpszMenuName = szAppname; //设置菜单 wndclass.lpszClassName = szAppname; //设置窗口类的名字 if (!RegisterClass(&wndclass))//注册窗口类 { MessageBox(NULL, TEXT("这个程序需要windows NT!"), szAppname, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppname, TEXT("Snack_eat"),//CreateWindow函数调用时,WndProc将受到WM_CREATE WS_OVERLAPPEDWINDOW&~WS_THICKFRAME& ~WS_MAXIMIZEBOX,//普通的层叠窗口&禁止改变大小&禁止最大化 CW_USEDEFAULT, //初始x坐标(默认) CW_USEDEFAULT, //初始y坐标 x_Client, //初始x方向尺寸 770 y_Client, //初始y方向尺寸 750 NULL, //父窗口句柄 NULL, //窗口菜单句柄 hInstance, //程序实例句柄 WinMain函数中第二个参数 NULL); //创建参数 ShowWindow(hwnd, iCmdShow);//显示窗口,iCmdShow是WinMain的第四个参数,决定窗口在屏幕中的初始化显示形式,例:SW_SHOWNORMAL表示正常显示 UpdateWindow(hwnd);//使窗口客户区重绘,通过向WndProc发送一条WM_PAINT消息而完成的 hAccel = LoadAccelerators(hInstance, szAppname);//加载加速键 while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(hwnd, hAccel, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }/* while (GetMessage(&msg, NULL, 0, 0))//GetMessage函数从消息队列中得到消息,填充msg。如果msg.message等于WM_QUIT,返回0,否则返回非0 { TranslateMessage(&msg);//将msg返回给windows已进行某些键盘消息的转换 DispatchMessage(&msg);//将msg再次返回给windows }*/ return msg.wParam;//msg.wParam是PostQuitMessage函数的参数值,通常是0 } ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值