24年了,想写些游戏。今年会慢慢学unity和qt,这个月先把文件读写练熟练。当然写的时候还是不熟练的,参考了很多帖子,请教了下copilot。
如题,这是一个实现游戏登录/注册/存档的框架,其他功能是暂时没有实现的。不过既然是框架,其他功能实现起来也并不困难。
框架由UI(用户界面)类,Player(玩家信息)类,和未实现的RunGame(游戏玩法)类组成。界面全程由UI类把握,每个流程实时捆绑正在交互的Player类对象,方便获取信息和修改信息。
框架接口基本没限制,RunGame类在此框架下是自由度很高的。可以写任意类型的小游戏。在我的构想下,分为单人/双人/人机对抗,这将通过继承RunGame类实现。
玩家在流程中始终通过选择a/b/c/d操作。我觉得对于控制台游戏来说,这是很理想的状态。
实现效果如下
下面展示类与方法接口。
//玩家类
class Player
{
protected:
string _name = "未命名"; //玩家名
string _password = "000000"; //玩家密码
string _likedSentence = "这个人很懒 什么都没有留下"; //玩家个性签名
int _level = 0; //玩家等级
public:
//构造函数
Player(string name, string password, string likedSentence, int level);
Player() { }
static Player signIn(); //注册
static Player logIn(); //登录
static void Pos(int, int); //设置光标位置
static bool isUsernameExist(const string& username); //判断用户名是否存在
//获取信息
string getName() { return this->_name; }
string getPassword() { return this->_password; }
string getSentence() { return this->_likedSentence; }
int getLevel() { return this->_level; }
//友元类UI
friend class UI;
};
//用户交互界面类
class UI
{
private:
string _process = "start";// 记录当前的进程 用于显示在屏幕上 每个选项都设置了进程
void versionInfo() { return; } //版本信息 待完成
void gameRule() { return; } //游戏规则 待完成
void mainScreen(Player); //主界面
void mainScreen_a(Player); //主界面的a选项
public:
Player start(); //整个登录注册流程在此完成 返回登录的玩家对象
void Pos(int, int); //设置光标位置
void showMainScreen(Player player) { this->mainScreen(player); } //显示主界面
void showChoice(char[7][50]); //通用显示选项
void showplayerInfo(Player); //显示玩家信息
void hideCursor(); //隐藏控制台光标
void newName(Player); //修改玩家名
};
能够看到在友元UI类中,几乎每个方法都使用了Player类对象。
这个框架的重点在于如何读写文件。
需要实现:
1、对未注册的玩家,能够注册一个新账号,包含名称、密码、个性签名、等级(0级) 等等信息(可以根据游戏类型写入丰富多样的信息);
2、拒绝注册已经被注册过的名称,防止出现重名,也方便管理账号信息;
3、对已经注册的玩家,能够通过名称和密码登录,这包括从文件中读取改名称下的密码,并与登录输入的密码比对;
4、拒绝登录没有注册过的账号名;
5、(未来将要实现的)在游戏的各种奖励机制中,赋予玩家游戏货币和经验值等,对于Player类是很容易实现的。
一下是核心方法实现。完整的代码已经放在文章最后。
/*
注册方法
当玩家输入新用户名时判断名称是否已经存在
注册成功后把信息赋予当前Player类对象并返回
同时把信息写入玩家列表更新
*/
Player Player::signIn()
{
string name, password, likedSentence;
while (true)
{
system("cls");
cout << "未知玩家-注册界面"; //流程
Pos(30, 10); cout << "注册";
Pos(30, 12); cout << "新玩家名:";
getline(cin, name);
if (Player::isUsernameExist(name) == true) //用户名已经存在
{
system("cls");
Pos(30, 16); cout << "玩家名已存在, ";
cin.clear(); //清空输入缓存
system("pause");
}
else break;
}
Pos(30, 14); cout << "设置密码:";
getline(cin, password);
Pos(30, 16); cout << "设置个性签名:";
getline(cin, likedSentence);
//写入玩家信息列表
ofstream fout("Player_List.txt", ios::app);
Player player(name, password, likedSentence, 0);
fout << player._name << ","
<< player._password << ","
<< player._likedSentence << ","
<< player._level << "\n";
fout.close();
Pos(30, 18); cout << "注册成功, ";
system("pause");
return player;
}
/*
登录方法
首先读取玩家输入的已注册名称
在文件中查找名称是否存在
如果存在便读取与该玩家注册的密码
等待判断用户输入密码是否匹配
匹配成功则注册成功
*/
Player Player::logIn()
{
while(true)
{
system("cls");
string knownName, knownPassword;
cout << "未知玩家-登录界面";
Pos(30, 10); cout << "登录";
Pos(30, 12); cout << "用户名:";
getline(cin, knownName);
Pos(30, 14); cout << "密码:";
getline(cin, knownPassword);
//读取玩家信息列表
ifstream fin("Player_List.txt");
string line;
bool found = false;
while (getline(fin, line))
{
stringstream ss(line);
string name, password, likedSentence, levelStr;
getline(ss, name, ',');
getline(ss, password, ',');
getline(ss, likedSentence, ',');
getline(ss, levelStr, ',');
if (name == knownName) //用户名匹配
{
found = true;
if (password == knownPassword)
{
int level = stoi(levelStr);
Pos(30, 16); cout << "登录成功, ";
Player player(name, password, likedSentence, level);
system("pause");
return player;
}
else //密码错误
{
Pos(30, 16); cout << "密码错误, ";
system("pause");
break;
}
}
}
if (!found) //没有找到该玩家
{
Pos(30, 16); cout << "没有找到该玩家, ";
system("pause");
}
fin.close();
}
}
//判断用户名是否存在
bool Player::isUsernameExist(const string& username)
{
ifstream fin("Player_List.txt");
if (fin.is_open())
{
string line;
while (getline(fin, line))
{
stringstream ss(line);
string existingName;
getline(ss, existingName, ',');
if (existingName == username) //用户名匹配
return true;
}
fin.close();
}
else //文件打开失败
cout << "出现错误!!";
return false;
}
/*
修改玩家名和修改其他任何信息都一样麻烦
需要修改Player类中的对应信息
然后将修改后的信息写入Player_List.txt文件
最后删除原文件,将临时文件重命名为原文件
*/
void UI::newName(Player player)
{
system("cls");
string Newname;
Pos(30, 10); cout << "请输入新用户名:";
getline(cin, Newname);
player._name = Newname;
ifstream fin("Player_List.txt"); // 打开玩家信息列表txt文件
ofstream fout("temp.txt"); // 创建临时文件
string line;
while (getline(fin, line))
{
stringstream ss(line);
string name, password, likedSentence, levelStr;
//用逗号隔开信息
getline(ss, name, ',');
getline(ss, password, ',');
getline(ss, likedSentence, ',');
getline(ss, levelStr, ',');
if (name == player._name)
{
name = Newname;
line = name + "," + password + "," + likedSentence + "," + levelStr;
}
fout << line << endl; // 将修改后的内容写入临时文件
}
fin.close();
fout.close();
remove("Player_List.txt"); // 删除原文件
rename("temp.txt", "Player_List.txt"); // 将临时文件重命名为原文件
return showplayerInfo(player);
}
这个框架也有不足之处,以下问题是困扰我很久但是我没有解决,只能放弃或者绕过的:
1、没有设置违法输入页面(之前做了一个,并尝试传入游戏进程给解决违法输入的函数,以返回正确的界面,但由于函数返回值可能为Player类成员,违法界面就不能return空对象,从而无限递归,最终导致栈溢出);
2、测试的时候界面出现了乱码,不知起因是何处,估计是Pos的控制台方法有些冲突;
3、显示版本信息、改个性签名这些函数在未来会实现
完整代码如下
/*
* @Author: Specptr(Github), cin在等Enter(CSDN)
* @Date: 2024.1.17
* @LastEditTime: 2024.1.24
* @Description:
实现了玩家类功能 包括登录注册 写入信息 修改信息等
* @FilePath: \C_Player.hpp
*/
#ifndef C_PLAYER_HPP
#define C_PLAYER_HPP
#include
#include
#include
#include
#include
using namespace std;
//玩家类
class Player
{
protected:
string _name = "未命名"; //玩家名
string _password = "000000"; //玩家密码
string _likedSentence = "这个人很懒 什么都没有留下"; //玩家个性签名
int _level = 0; //玩家等级
public:
//构造函数
Player(string name, string password, string likedSentence, int level)
{
this->_name = name;
this->_password = password;
this->_likedSentence = likedSentence;
this->_level = level;
}
Player() { }
static Player signIn(); //注册
static Player logIn(); //登录
static void Pos(int, int); //设置光标位置
static bool isUsernameExist(const string& username); //判断用户名是否存在
//获取信息
string getName() { return this->_name; }
string getPassword() { return this->_password; }
string getSentence() { return this->_likedSentence; }
int getLevel() { return this->_level; }
//友元类UI
friend class UI;
};
//设置光标位置
void Player::Pos(int x,int y)
{
COORD pos;
HANDLE hOutput;
pos.X=x;
pos.Y=y;
hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput,pos);
}
//注册
Player Player::signIn()
{
string name, password, likedSentence;
while (true)
{
system("cls");
cout << "未知玩家-注册界面"; //流程
Pos(30, 10); cout << "注册";
Pos(30, 12); cout << "新玩家名:";
getline(cin, name);
if (Player::isUsernameExist(name) == true) //用户名已经存在
{
system("cls");
Pos(30, 16); cout << "玩家名已存在, ";
cin.clear(); //清空输入缓存
system("pause");
}
else break;
}
Pos(30, 14); cout << "设置密码:";
getline(cin, password);
Pos(30, 16); cout << "设置个性签名:";
getline(cin, likedSentence);
//写入玩家信息列表
ofstream fout("Player_List.txt", ios::app);
Player player(name, password, likedSentence, 0);
fout << player._name << ","
<< player._password << ","
<< player._likedSentence << ","
<< player._level << "\n";
fout.close();
Pos(30, 18); cout << "注册成功, ";
system("pause");
return player;
}
//登录
Player Player::logIn()
{
while(true)
{
system("cls");
string knownName, knownPassword;
cout << "未知玩家-登录界面";
Pos(30, 10); cout << "登录";
Pos(30, 12); cout << "用户名:";
getline(cin, knownName);
Pos(30, 14); cout << "密码:";
getline(cin, knownPassword);
//读取玩家信息列表
ifstream fin("Player_List.txt");
string line;
bool found = false;
while (getline(fin, line))
{
stringstream ss(line);
string name, password, likedSentence, levelStr;
getline(ss, name, ',');
getline(ss, password, ',');
getline(ss, likedSentence, ',');
getline(ss, levelStr, ',');
if (name == knownName) //用户名匹配
{
found = true;
if (password == knownPassword)
{
int level = stoi(levelStr);
Pos(30, 16); cout << "登录成功, ";
Player player(name, password, likedSentence, level);
system("pause");
return player;
}
else //密码错误
{
Pos(30, 16); cout << "密码错误, ";
system("pause");
break;
}
}
}
if (!found) //没有找到该玩家
{
Pos(30, 16); cout << "没有找到该玩家, ";
system("pause");
}
fin.close();
}
}
//判断用户名是否存在
bool Player::isUsernameExist(const string& username)
{
ifstream fin("Player_List.txt");
if (fin.is_open())
{
string line;
while (getline(fin, line))
{
stringstream ss(line);
string existingName;
getline(ss, existingName, ',');
if (existingName == username) //用户名匹配
return true;
}
fin.close();
}
else //文件打开失败
cout << "出现错误!!";
return false;
}
#endif
/*
* @Author: Specptr(Github), cin在等Enter(CSDN)
* @Date: 2024.1.14
* @LastEditTime: 2024.1.24
* @Description:
实现了UI类的功能,包括登录注册(并写入玩家信息列表txt文件),主界面,角色信息,游戏规则,版本信息等功能。
* @FilePath: \C_UI.hpp
*/
#ifndef C_UI_HPP
#define C_UI_HPP
#include
#include
#include
#include "C_Player.hpp"
//#include "C_RunGame.hpp" 游戏的具体实现(不在这里放出)
//用户交互界面类
class UI
{
private:
string _process = "start";// 记录当前的进程 用于显示在屏幕上 每个选项都设置了进程
void versionInfo() { return; } //版本信息 待完成
void gameRule() { return; } //游戏规则 待完成
void mainScreen(Player); //主界面
void mainScreen_a(Player); //主界面的a选项
public:
Player start(); //整个登录注册流程在此完成 返回登录的玩家对象
void Pos(int, int); //设置光标位置
void showMainScreen(Player player) { this->mainScreen(player); } //显示主界面
void showChoice(char[7][50]); //通用显示选项
void showplayerInfo(Player); //显示玩家信息
void hideCursor(); //隐藏控制台光标
void newName(Player); //修改玩家名
//void newSentence(Player); //修改个性签名
//void newColor(Player); //修改主题颜色
};
// 设置光标位置
void UI::Pos(int x,int y)
{
COORD pos;
HANDLE hOutput;
pos.X=x;
pos.Y=y;
hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput,pos);
}
// 隐藏控制台光标
void UI::hideCursor()
{
HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO info;
info.dwSize = 100;
info.bVisible = FALSE;
SetConsoleCursorInfo(consoleHandle, &info);
}
// 通用显示选项
void UI::showChoice(char ch[6][50])
{
cout << this->_process;
Pos(30, 10); cout << ch[0];
Pos(30, 12); cout << ch[1];
Pos(30, 14); cout << ch[2];
Pos(30, 16); cout << ch[3];
Pos(30, 18); cout << ch[4];
Pos(30, 20); cout << ch[5];
}
// 登录注册流程
Player UI::start()
{
_process = "未知玩家-登录/注册界面"; //更新流程
system("cls");
system("color 1e");
char Choices[6][50] =
{
"欢迎来到游戏!",
"亲爱的用户 请先 登录/注册",
"a) 登录已有角色",
"b) 注册新角色",
"c) 游客模式",
"d) 退出"
};
string input;
do
{
system("cls");
showChoice(Choices);
getline(cin, input);
if (input == "a") return Player::logIn();
else if (input == "b") return Player::signIn();
else if (input == "c") { // 处理 'C' 键的情况... }
else if (input == "d") { // 处理 'D' 键的情况... }
}
while (input != "a" && input != "b" && input != "c" && input != "d");
return Player();
}
//主界面
void UI::mainScreen(Player player)
{
_process = player.getName() + "-主界面"; //更新流程
system("cls");
system("color e5");
char Choices[6][50] =
{
"欢迎来到游戏!",
"开始新征程吧",
"a) 新游戏",
"b) 角色信息",
"c) 游戏规则",
"d) 版本信息"
};
string input;
do
{
system("cls");
showChoice(Choices);
getline(cin, input);
if (input == "a") return this->mainScreen_a(player);
else if (input == "b") return this->showplayerInfo(player);
else if (input == "c") { //return this->gameRule(); }
else if (input == "d") { // 处理 'D' 键的情况... }
}
while (input != "a" && input != "b" && input != "c" && input != "d");
system("cls");
return;
};
// 主界面a选项
void UI::mainScreen_a(Player player)
{
_process = player.getName() + "-新游戏"; //更新流程
system("cls");
system("color e5");
char Choices[6][50] =
{
"新一场游戏已经准备就绪",
"请选择模式",
"a) 单人游玩",
"b) 双人游玩",
"c) 人机对抗",
"d) 返回上级"
};
string input;
do
{
system("cls");
showChoice(Choices);
getline(cin, input);
if (input == "a") return;
else if (input == "b") return;
else if (input == "c")
{
return;// this->gameRule();
}
else if (input == "d")
{
// 处理 'D' 键的情况...
}
}
while (input != "a" && input != "b" && input != "c" && input != "d");
};
//修改玩家名
void UI::newName(Player player)
{
system("cls");
string Newname;
Pos(30, 10); cout << "请输入新用户名:";
getline(cin, Newname);
player._name = Newname;
ifstream fin("Player_List.txt"); // 打开玩家信息列表txt文件
ofstream fout("temp.txt"); // 创建临时文件
string line;
while (getline(fin, line))
{
stringstream ss(line);
string name, password, likedSentence, levelStr;
//用逗号隔开信息
getline(ss, name, ',');
getline(ss, password, ',');
getline(ss, likedSentence, ',');
getline(ss, levelStr, ',');
if (name == player._name)
{
name = Newname;
line = name + "," + password + "," + likedSentence + "," + levelStr;
}
fout << line << endl; // 将修改后的内容写入临时文件
}
fin.close();
fout.close();
remove("Player_List.txt"); // 删除原文件
rename("temp.txt", "Player_List.txt"); // 将临时文件重命名为原文件
return showplayerInfo(player);
}
// 显示玩家信息
void UI::showplayerInfo(Player player)
{
system("cls");
string input;
do
{
_process = player.getName() + "-玩家信息";
cout << this->_process;
Pos(30, 10); cout << "玩家名:" << player.getName();
Pos(30, 12); cout << "个性签名:" << player.getSentence();
Pos(30, 14); cout << "玩家等级:" << player.getLevel();
Pos(30, 18); cout << "a) 修改玩家名?";
Pos(30, 20); cout << "b) 修改个性签名?";
Pos(30, 22); cout << "c) 修改主题颜色?";
Pos(30, 24); cout << "d) 返回上级";
Pos(30, 26);
getline(cin, input);
if (input == "a") return newName(player);
else if (input == "b") return;// player.newSentence(); 待完成
else if (input == "c") return;// player.newColor(); 待完成
else if (input == "d") return mainScreen(player);
}
while (input != "a" && input != "b" && input != "c" && input != "d");
system("pause");
system("cls");
return mainScreen(player);
}
#endif
/*
* @Author: Specptr(Github), cin在等Enter(CSDN)
* @Date: 2024.1.17
* @LastEditTime: 2024.1.24
* @Description:
这是main文件
* @FilePath: \main.cpp
*/
#include "C_Player.hpp"
#include "C_UI.hpp"
//#include "C_RunGame.hpp"
int main()
{
UI ui;
ui.hideCursor();
Player player = ui.start();
ui.showMainScreen(player);
return 0;
}
——完——