来自C++群内的贪吃蛇教程。终于不…

来源:互联网 发布:如何隐藏域名信息 编辑:程序博客网 时间:2024/06/09 19:17

文件1:
// Snake4.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "Snake4.h"

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

#include //为了使用Cimage方便读图加入
#include //使用队列
using namespace std; //队列属于C++内容,而C++的内容基本都写在std命名空间内

#include
#pragma comment(lib,"winmm.lib")//使用timeGetTime要加的,一般在最前面加载

enum DIR_ENUM {DIR_UP,DIR_DN,DIR_LT,DIR_RT};//枚举四个方向

HWND g_hWnd; //获取主窗口句柄,方便控制窗口

HDC g_hDC; //主DC(主画布,用于绘画图片等)
HDC g_hBufDC; //缓冲DC,用于双缓冲,没有双缓冲,画面会严重闪烁
HBITMAP g_hBufBmp;//缓冲

CImage g_Bg;//背景图
CImage g_Snake;//蛇身图
CImage g_Head;//蛇头
CImage g_Rear; //蛇尾

CImage g_Food;//食物图

deque g_SnakeDeque;//蛇位置队列,用于存放蛇的节点的位置
POINT g_FoodPos;//食物位置

int iSnakeDir = 1; //蛇的运动方向

int iKey; //记录玩家按下的按键值

int iLife = 3;//三条生命

bool isInSnake(POINT pt)
{
for(UINT i = 0;i
{
if(g_SnakeDeque[i].x==pt.x &&g_SnakeDeque[i].y == pt.y)
{
return true; 
}
}

return false;
}//isInSnake

void RandFood()
{
do
{
g_FoodPos.x = rand() *30; // 宽为600,最多20个30宽的位置,随机一下
g_FoodPos.y = rand() *30;
}while(isInSnake(g_FoodPos));//如果随机生成的位置在蛇身上,则重新随机
}

//初始化数据,读取图片等
void Start()
{
srand(timeGetTime());//随机种子,使得每次随机不一样
RandFood();//调用,随机食物位置
g_hDC = GetDC(g_hWnd); //获得此窗口的主DC,画面画到这个上面

//以下内容为了实现双缓冲,主DC直接显示,如果直接在主DC上面画内容会造成闪烁,解决方法是双缓冲
//先把内容写入缓冲DC中,然后直接把缓冲DC复制到主DC,避免闪烁
g_hBufDC = CreateCompatibleDC(g_hDC);//创建兼容DC与主DC一致
g_hBufBmp =CreateCompatibleBitmap(g_hDC,600,600);//创建兼容DC的位图画图,使得兼容DC可以画上内容
SelectObject(g_hBufDC,g_hBufBmp);//把缓冲图放入缓冲DC中

g_Bg.Load("bg.png");//读取场景图
g_Snake.Load("snake.png");//读取蛇身图
g_Head.Load("head.png");//读取蛇头图
g_Rear.Load("rear.png");//读取蛇尾图
g_Food.Load("food.png");//读取食物图

POINT ptSnake = {300,300};//定义蛇的位置
g_SnakeDeque.push_back(ptSnake);//放入蛇的节点中
iSnakeDir = rand()%4; //随机蛇运动方向

mciSendString("open bg.mp3 aliasbgMusic",NULL,0,NULL);//发送MCI命令:打开bg.MP3,并命名为bgMusic 
mciSendString("play bgMusic from 0 repeat",NULL,0,NULL);//从0开始播放bgMusic,并重复播放其他命令还算有stop,pause等
//

//以此类推,先读取了吃到食物和死亡的音乐,在制定位置播放
mciSendString("open eat.wav aliaseatMusic",NULL,0,NULL);//能支持CD、MP3、midi、avi等格式,不支持rm、ramvb、ogg等
mciSendString("open die.wav aliasdieMusic",NULL,0,NULL);
}//start


//逻辑更新,绘画内容
void Logic()
{
static int iStep = timeGetTime();//速度太快,限制一下
if(timeGetTime()-iStep >400) //300毫秒移动一次,想快可以改小或者进行难度调整
{
iStep = timeGetTime();

//控制蛇的运动
if(iKey == VK_UP) //按下向上键
{
if(iSnakeDir == DIR_LT||iSnakeDir == DIR_RT)
{
iSnakeDir = DIR_UP;
}
}
else if(iKey == VK_DOWN) //按下向下键
{
if(iSnakeDir == DIR_LT||iSnakeDir == DIR_RT)
{
iSnakeDir = DIR_DN;
}
}
else if(iKey == VK_LEFT) //按下向左键
{
if(iSnakeDir == DIR_DN||iSnakeDir == DIR_UP)
{
iSnakeDir = DIR_LT;
}
}
else if(iKey == VK_RIGHT) //按下向上键
{
if(iSnakeDir == DIR_DN||iSnakeDir == DIR_UP)
{
iSnakeDir = DIR_RT;
}
}

//蛇运动,其实蛇的运动就是蛇头根据方向向前走一步,而蛇尾去掉一步
//如果转化为算法就是在队列前根据方向加入一个节点,去掉队列最后一个节点
POINT ptHead = g_SnakeDeque.front();//获取第一个节点

if(iSnakeDir == DIR_UP)
{
ptHead.y -= 30;
}
else if(iSnakeDir == DIR_DN)
{
ptHead.y += 30;
}
else if(iSnakeDir == DIR_LT)
{
ptHead.x -= 30;
}
else if(iSnakeDir == DIR_RT)
{
ptHead.x += 30;
}

//蛇出界了或者蛇吃到自己身体了,就死亡
if(ptHead.x<0||ptHead.x>=600||ptHead.y<0||ptHead.y>=600||isInSnake(ptHead))
{
mciSendString("play dieMusic from0",NULL,0,NULL);//播放死亡音乐

iLife--;//死亡,生命减一
if(iLife<=0)
{
MessageBox(NULL,"Gameover!","游戏结束",MB_OK|MB_ICONWARNING);
DestroyWindow(g_hWnd);//摧毁关闭窗扣口
return;
}

g_SnakeDeque.clear();//全清空
POINT ptSnake = {300,300}; //定义蛇的位置
g_SnakeDeque.push_back(ptSnake);//放入蛇的节点中
iSnakeDir = rand()%4;//随机蛇运动方向
}
else if(ptHead.x == g_FoodPos.x&& ptHead.y == g_FoodPos.y)
{
mciSendString("play eatMusic from0",NULL,0,NULL);//播放吃到食物音乐
g_SnakeDeque.push_front(ptHead);
RandFood();
}
else
{
g_SnakeDeque.push_front(ptHead);//新的位置加入蛇头,蛇头往前移动
g_SnakeDeque.pop_back();//删除最后节点,蛇尾往前收
}
}//if
//以下为绘图内容
RECT rc = {0,0,600,600};
FillRect(g_hBufDC,&rc,(HBRUSH)GetStockObject(WHITE_BRUSH));//清除缓冲DC内容

//内容先绘图入缓冲DC
g_Bg.Draw(g_hBufDC,0,0);

for(UINT i = 0;i
{
if(i==0)
{
g_Head.Draw(g_hBufDC,g_SnakeDeque[i].x,g_SnakeDeque[i].y);//绘画蛇头
}
else if(i== g_SnakeDeque.size()-1)
{
g_Rear.Draw(g_hBufDC,g_SnakeDeque[i].x,g_SnakeDeque[i].y);//绘画蛇尾
}
else
{
g_Snake.Draw(g_hBufDC,g_SnakeDeque[i].x,g_SnakeDeque[i].y);//把蛇身的样子绘制在制定的节点上
}
}//for
//绘画食物
g_Food.Draw(g_hBufDC,g_FoodPos.x,g_FoodPos.y);

//绘画生命数
char sz[32] = {0};
for(int i = 0;i
{
strcat_s(sz,"★"); // 根据生命数,加入红星
}

SetTextColor(g_hBufDC,RGB(255,0,0));//设置字体颜色为红色
SetBkMode(g_hBufDC,TRANSPARENT);//设置字体背景颜色为透明
TextOut(g_hBufDC,0,0,sz,strlen(sz));//输出字体



//缓冲DC的内容写入主DC
BitBlt(g_hDC,0,0,600,600,g_hBufDC,0,0,SRCCOPY);
}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                  _In_opt_ HINSTANCEhPrevInstance,
                  _In_ LPTSTR   lpCmdLine,
                  _In_ int      nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

  // TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;

// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle,MAX_LOADSTRING);
LoadString(hInstance, IDC_SNAKE4, szWindowClass,MAX_LOADSTRING);
MyRegisterClass(hInstance);

// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance,MAKEINTRESOURCE(IDC_SNAKE4));

// 主消息循环:
ZeroMemory(&msg,sizeof(MSG));
while (msg.message != WM_QUIT)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
Logic();//有处理就先处理消息,没有则根据逻辑计算蛇的位置
}
}

return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_SNAKE4));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;// MAKEINTRESOURCE(IDC_SNAKE4);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance,MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目的: 保存实例句柄并创建主窗口
//
//   注释:
//
//       在此函数中,我们在全局变量中保存实例句柄并
//       创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; //将实例句柄存储在全局变量中

   int iW =600+GetSystemMetrics(SM_CXFIXEDFRAME)*2;
   int iH =600+GetSystemMetrics(SM_CYFIXEDFRAME)*2+GetSystemMetrics(SM_CYCAPTION);

   int iX =(GetSystemMetrics(SM_CXSCREEN)-iW)/2;
   int iY =(GetSystemMetrics(SM_CYSCREEN)-iH)/2;

   hWnd =CreateWindow(szWindowClass,"贪吃蛇1.0",WS_SYSMENU,iX,iY,iW,iH,NULL,NULL,hInstance,NULL);
 //  hWnd =CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
   //  CW_USEDEFAULT, 0,
// 600+GetSystemMetrics(SM_CXFRAME)*2,//客户区宽+边框大小*2
//600+GetSystemMetrics(SM_CXFRAME)*2+GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYMENU),//客户区高+边框高+标题高+菜单高
// NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
     return FALSE;
   }

   g_hWnd =hWnd;//获得窗口句柄,以便往后控制窗口
   Start();

   ShowWindow(hWnd,nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM,LPARAM)
//
//  目的: 处理主窗口的消息。
//
//  WM_COMMAND - 处理应用程序菜单
//  WM_PAINT - 绘制主窗口
//  WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAMwParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_KEYDOWN:
iKey = wParam;//玩家按下按键,捕获按键值
break;

case WM_COMMAND:
wmId    =LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd,About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam,LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) ==IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

文件2:
// stdafx.cpp : 只包括标准包含文件的源文件
// Snake4.pch 将作为预编译头
// stdafx.obj 将包含预编译类型信息

#include "stdafx.h"

// TODO: 在 STDAFX.H 中
// 引用任何所需的附加头文件,而不是在此文件中引用

文件3命名为:targetver.h
#pragma once

// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。

// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
// WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。

#include

文件4:
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN           //  从Windows 头文件中排除极少使用的信息
// Windows 头文件:
#include

// C 运行时头文件
#include
#include
#include
#include


// TODO: 在此处引用程序需要的其他头文件

文件5:命名为:Snake4.h
#pragma once

#include "resource.h"

文件6:命名为:resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Snake4.rc
//

#define IDS_APP_TITLE 103

#define IDR_MAINFRAME 128
#define IDD_SNAKE4_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_SNAKE4 107
#define IDI_SMALL 108
#define IDC_SNAKE4 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
// 新对象的下一组默认值
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NO_MFC 130
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif


教程的配置是来自其他朋友精心分享的,但又找不到原作者就不把他的原文字贴出来了
来自C++群内的贪吃蛇教程。终于不报错了


0 0
原创粉丝点击