游戏构思编写

用DevCPP编写游戏

本文基于Windows

约定

不要太颓废。
文中代码有许多头文件,总共#include一次就够了。

核心内容

用户数据保存

存在哪里?

通常来说,一个健全的游戏总是能记录用户的数据。
当程序被关闭后,内存会被释放,所以我们必定需要讲内容存储在硬盘的文件里。但因此新的问题随之而来,游戏的位置随时可能改变,而且一系列空格与中文使得绝对路径的使用变得困难。但如果使用相对路径,有会使得同一台电脑上的不同游戏个体无法共享数据,这里提供两种解决方案。

  • 如果您希望电脑上的不同用户共享同一内容(注意,这里的用户不是游戏中的用户,而是系统启动界面的不同用户,即在C:\Users文件夹下的各文件名),您可以选择在C:\ProgramData下建立游戏用户文件夹。
  • 如果您希望电脑上的不同用户分开储存,您可以选择在%appdata%下建立游戏用户文件夹(通常来说这个路径在系统中会被处理成C:\Users\用户名\appdata\Roaming

如何读写?

推荐使用fstream头文件下的流,语法具体如下:

#include<fstream>
#include<string.h>
//......
std::string FilePath= __balabala__;//你的用户文件路径,最好是绝对路径。
void GetUserInfo()
{
    std::ifstream fin(FilePath.c_str());//接下来像cin一样使用
    fin>>__wabibabu__;//各类信息
    //.......
    fin.close();
}
void SaveUserInfo()
{
    std::ofstream fout(FilePath.c_str());//接下来像cout一样使用
    fout<<__rowrowrow__<<endl;//各类信息
    //.......
    fout.close();
}

这里顺带说一声,写游戏最好不要用using namespace std;,如果嫌std::麻烦,请使用using std::__name__;,其中__name__为你要的变量或函数名

如何记录

  • 如果是数字或者字符串数据,直接存储。
  • 如果是类似于获得物品等,可以考虑二进制,如果有等级,则为 理论最大等级 + 1 \text{理论最大等级} + 1 理论最大等级+1 进制。如果太大,则分段存储。

注意记录方法始终保持一致,这里推荐把信息存在全局变量里。

如何加密

主要分两类:

  • 密码类数据
  • 用户进度数据
密码类数据

可以考虑记录 hash ,而非原串,可以考虑 OI 中的加密方式。

typedef unsigned long long ull;
ull GetStringHash(std::string str)
{
    ull ans=0;
    const ll BASE=131
    int n=str.size();
    for(int i=0; i<n; i++)
        ans=ans*BASE+str[i];
    return ans;
}

注意该函数应始终保持一致。

用户进度数据

我们可以认为全部是整数可以表达的,所以我们只讨论整数储存,此时有两种方法。

特殊进制配映射

可以使用户不知道如何篡改。

typedef unsigned long long ull;
//......
const int BASE=-2;
std::string i2s(ull t)//加密
{
	if(t==0)
		return ".";
	ull r=t/BASE;
	t=t%BASE;
	if(t<0)
		t=t+2,r=r+1;
	return i2s(r)+(t?'_':'.');
}
ull s2i(std::string s)//解密
{
	int n=s.size();
	ull ans=0;
	for(int i=0; i<n; i++)
		ans=ans*BASE+(s[i]=='_'?1:0);
	return ans;
}

BASE不一定是-2,同时不一定.代表0_代表1,您可以自己做出选择。

把所有数首位相连成字符串,记录 hash

可以判断用户是否进行了篡改。

//这里给出拼接代码
#include<sstream>
//......
std::string UserInfo2String()
{
		std::stringstream str;
		str<<__dengdualang__;//the information
		//......
		std::string ans;
		str>>ans;
		return ans;
}

音乐

储存

推荐储存在游戏文件夹同一位置以使用相对路径,或者与用户信息一起存储,此时需要安装时操作,下文会有讲解。

背景音乐播放

我们采用MCI进行音乐播放。注意,此时您的游戏必须是DevCPP中的项目,可以通过file->new->Project->Basic->Console Application(控制台) or Windows Application(窗口程序)建立

你需要在Project->Project options->parameters->linker->中加入以下参数-lwinmm -mwindows

接下来给出参考程序部分:

#include<Windows.h>
#include<Mmsystem.h>
//......
//BGM应为mp3格式
void SendMCIMessage(string y)//发送消息给MCI
{//注意以下函数并非定义于std域内,不要手痒
	char buf[260];
	MCIERROR err;
	err=mciSendString(y.c_str(),buf,sizeof(buf),NULL);
	if(err)//发送失败会报错
	{
		mciGetErrorString(err,buf,sizeof(buf));
		MessageBox(NULL,buf,"ERROR",MB_OK);
		MessageBox(NULL,y.c_str(),"FAIL",MB_OK);
	}
}
void PlayBGM(std::string BGMpath)//相对路径或者绝对路径皆可,最好绝对路径
{
	std::string t;
	t="open "+BGMpath+" Alias bgm";
	SendMCIMessage(t);
	SendMCIMessage("play bgm repeat");//洗脑循环
}
void StopBGM()
{
	SendMCIMessage("stop bgm");//注意要在一个线程内才有效,否则会报错 指定的设备未打开,或不被MCI所识别
}
音效播放

推荐使用MCI或者PlaySound函数。

MCI
void PlayMySound(std::string SoundPath)
{
	std::string t;
	t="open "+SoundPath+" Alias sound";/
		/同时只能播放一个,如要同时多个,采用不能名字,即Sound换其他名字
	SendMCIMessage(t);
	SendMCIMessage("play sound");//异步执行
	//如果指令为play sound wait,则会等待播放完毕,这会导致线程停滞,不建议使用,可以考虑记录音效时长配clock(),到达时间后操作。
}
PlaySound

注意,只能播放不超过10MB的wav文件。
自行搜索,笔者更推荐MCI

其他内容

游戏机制是十分复杂的,我不得不承认这比OI中大模拟还难,所以需要我们的程序编写有序,您应该可以看到,笔者的函数命名十分有规律。

这里希望您善用.h文件,同时使用DevCPP左侧边栏的Classes部件。

好看的游戏

控制台

五彩斑斓

注意setletter函数和个变量意义。

#include<Windows.h>
#include<string.h>
//推荐至于同目录下 output.h内,在main.cpp中#include "output.h" 
//fore: 前景 back: 背景 
WORD _white_fore=FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
WORD _red_fore=FOREGROUND_INTENSITY|FOREGROUND_RED;
WORD _green_fore=FOREGROUND_INTENSITY|FOREGROUND_GREEN;
WORD _blue_fore=FOREGROUND_INTENSITY|FOREGROUND_BLUE;
WORD _yellow_fore=FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN;
WORD _purple_fore=FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE;
WORD _cyan_fore=FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE;
WORD _grey_fore=FOREGROUND_INTENSITY;
WORD _white_back=BACKGROUND_INTENSITY|BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE;
WORD _red_back=BACKGROUND_INTENSITY|BACKGROUND_RED;
WORD _green_back=BACKGROUND_INTENSITY|BACKGROUND_GREEN;
WORD _blue_back=BACKGROUND_INTENSITY|BACKGROUND_BLUE;
WORD _yellow_back=BACKGROUND_INTENSITY|BACKGROUND_RED|BACKGROUND_GREEN;
WORD _purple_back=BACKGROUND_INTENSITY|BACKGROUND_RED|BACKGROUND_BLUE;
WORD _cyan_back=BACKGROUND_INTENSITY|BACKGROUND_GREEN|BACKGROUND_BLUE;
WORD _grey_back=BACKGROUND_INTENSITY;
WORD _white_deep_fore=FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
WORD _red_deep_fore=FOREGROUND_RED;
WORD _green_deep_fore=FOREGROUND_GREEN;
WORD _blue_deep_fore=FOREGROUND_BLUE;
WORD _yellow_deep_fore=FOREGROUND_RED|FOREGROUND_GREEN;
WORD _purple_deep_fore=FOREGROUND_RED|FOREGROUND_BLUE;
WORD _cyan_deep_fore=FOREGROUND_GREEN|FOREGROUND_BLUE;
WORD _black_fore;
WORD _white_deep_back=BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE;
WORD _red_deep_back=BACKGROUND_RED;
WORD _green_deep_back=BACKGROUND_GREEN;
WORD _blue_deep_back=BACKGROUND_BLUE;
WORD _yellow_deep_back=BACKGROUND_RED|BACKGROUND_GREEN;
WORD _purple_deep_back=BACKGROUND_RED|BACKGROUND_BLUE;
WORD _cyan_deep_back=BACKGROUND_GREEN|BACKGROUND_BLUE;
WORD _black_back;
WORD _now;//现在情况,可以不记录 
void setletter(WORD color)//设置文字样式 
{
	_now=color;
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color);
}

满天星

采用setxy函数更换输出位置。

#include <Windows.h>
//......
void setxy(short x,short y)//设置输出位置 
{
	COORD pos= {x,y};
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}

Win32应用程序

要说的太多了,部分内容可以学习DevCPP里的模版,位于File->New->Project下,有很多模版。

窗口注册

参考资料:BDFS C++ Windows编程

推荐采用如下代码

#include <Windows.h>
//.....
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,
                   int nCmdShow)
{
	WNDCLASSEX wc;
	memset(&wc,0,sizeof(wc));
	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.lpfnWndProc	 = WndProc;
	wc.hInstance	 = hInstance;
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszClassName = "AskWindowsClass";
	wc.lpszMenuName  = "MAIN";//你的菜单名称,下文有所叙述
	wc.hIcon		 = LoadIcon(hInstance, "A");//项目图标,于 Project->Project Options->General->Icons下选择
	wc.hIconSm		 = LoadIcon(hInstance, "A")
	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, "Window Registration Failed!","Error!",
		           MB_ICONEXCLAMATION|MB_OK);
		return 1;
	}
		HWND hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"AskWindowsClass","Game",//窗口类名和窗口名
	                           WS_VISIBLE|WS_OVERLAPPEDWINDOW,
	                           CW_USEDEFAULT,
	                           CW_USEDEFAULT,
	                           520,//窗口大小
	                           364,
	                           NULL,NULL,hInst,NULL);
	if(hwnd == NULL)
	{
		MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
		return;
	}
	ShowWindow(hwnd,1);
	UpdateWindow(hwnd);
	while(GetMessage(&msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&msg);//开始消息循环
		DispatchMessage(&msg);
	}
};
处理消息

给出消息处理函数的例子,如有其他需求请自行百度。

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch(Message)
	{
		case WM_CREATE://当窗口创建时
			DWORD style;
			style=GetWindowLong(hwnd, GWL_STYLE);
			style=style&(~WS_THICKFRAME)|WS_DLGFRAME;
			SetWindowLong(hwnd, GWL_STYLE, style);//设置窗口大小不可改变
			break;
		case WM_CHAR://键盘按键
				switch(wParam)
				{
					case 'p':按下p
						//Do something.
						break;
				}
			break;
		case WM_COMMAND:
			switch(LOWORD(wParam))//一堆子消息
			{
				case CM_CLOSE://其实这是一个数值,我们通常在main.h中这样写
				/*
				#define CM_CLOSE 1000
				其中1000是1-32767中随意的数值,通常取[1000,10000)
				*/
					DestroyWindow(hwnd)
					break;
			}
			break;
		case WM_CLOSE://请求关闭
			SendMessage(hwnd,WM_COMMAND,CM_CLOSE,0);//这是发送消息的函数,hwnd为窗口句柄,WM_COMMAND为消息类型,CM_CLOSE为参数,往上看,您很容易理解
			break;
		case WM_DESTROY://窗口已经被销毁
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hwnd, Message, wParam, lParam);
	}
	return 0;
}

窗口菜单

请在项目中新建main.rc,这是一个资源文件。

#include "main.h"//你的子消息定义文件

MAIN MENU//如WinMain函数中wc的menu属性,名字保持相同
{
	POPUP "&Work"//一个组,&之后的是快捷按,Alt+建激活选项
	{
		MENUITEM "&Add", CM_add//一个按键,点击后会类似于SendMessage(hwnd,WM_COMMAND,CM_add,0);向消息处理函数发送消息
		MENUITEM SEPARATOR//分割线
	}
	MENUITEM "&Close", CM_close
}
绘图

自行BDFS

控件

补充内容BDFSC++ Windows编程 控件
通常建议使用buttonedit

HWND BTcsk,EDask,EDtodo;
BTcsk/*窗口句柄*/      = CreateWindow("button"/*按钮*/, "cancel"/*上面的文字*/,
	                     WS_CHILD|WS_VISIBLE|WS_BORDER,
	                     225, 246, /*左上角坐标*/150, 20,/*窗口大小*/
	                     hwnd, (HMENU)CC_ask/*响应的子消息,类似SendMessage(hwnd,WM_COMMAND,CM_ask,0)*/
	                     hInst/*句柄,同Winmain中的hInstance,需要传参或者定义为全局*/, NULL
	                    );
Edask = CreateWindow("edit"/*文本框*/, "",
	                     WS_CHILD|WS_VISIBLE|WS_BORDER|ES_AUTOHSCROLL/*各参数,可自行百度*/,
	                     75, 227, 300, 19,
	                     hwnd, NULL, hInst, NULL
	                    );
SendMessage(Edtodo/*窗口句柄*/,EM_SETREADONLY,1,0);//设置文本框只读

句柄建议命名类型大写+消息小写。

输出图片

自行百度C++ Windows编程 输出图片,不做展开。

杂项

笔者认为这已经比大模拟复杂了,我们在这里接触了许多新语法,所以容易出现bug,推荐先编写完console的游戏内部后再进行界面编写,避免大量出错。

安装包

很明显,上述过程有很多是需要初始化的,这就需要安装包出场了。
无论是WinRARsfx还是专业的安装包软件,都支持bat的预处理,因此接下来我们讲述批处理编写。

批处理

对于目录·,应该一律使用"Path",也就是双引号嵌套。
开头为

@echo off
  • 创建目录: 在CMD中输入md \?并仔细查看。
  • 移动文件: 在CMD中输入move \?并仔细查看。

创建桌面快捷方式:

set Program=D:\bvsg\bvsg.exe %文件位置%
set LnkName=bvsg %快捷方式名称%
set WorkDir=D:\bvsg %文件目录%
set Desc=bvsg
if not defined WorkDir call:GetWorkDir "%Program%"
(echo Set WshShell=CreateObject("WScript.Shell"^)
echo strDesKtop=WshShell.SPEcialFolders("DesKtop"^)
echo Set oShellLink=WshShell.CreateShortcut(strDesKtop^&"\%LnkName%.lnk"^)
echo oShellLink.TargetPath="%Program%"
echo oShellLink.WorkingDirectory="%WorkDir%"
echo oShellLink.Windowstyle=1
echo oShellLink.Description="%Desc%"
echo oShellLink.Save)>makelnk.vbs
makelnk.vbs
del /f /q makelnk.vbs
exit
goto :eof
:GetWorkDir
set WorkDir=%~dp1
set WorkDir=%WorkDir:~,-1%
goto :eof

sfx的使用

自行BDFS,WinRAR 自解压

总结

讲了这么多颓废的方式,不要真颓废啊。
我们发现,要写一个游戏,尤其是Win32的,码量非常大,且及易出错,通常要一个暑假才能写出一个游戏,所以除非非常闲,不要去碰Win32Console写写得了,毕竟OIer学算法不是为了去当前端的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值