(七)C++自制植物大战僵尸游戏关卡数据加载代码讲解

news2024/11/16 15:49:56

植物大战僵尸游戏开发教程专栏地址icon-default.png?t=N7T8http://t.csdnimg.cn/xjvbb 


打开LevelData.h和LevelData.cpp文件。文件位置如下图所示。

 LevelData.h

此头文件中定义了两个类,分别是OpenLevelData、LevelData,其中OpenLevelData用于加载文件数据。LevelData解析数据,将数据保存到数据结构中。

#pragma once
#include "cocos2d.h"
#include "json/writer.h"
#include "json/document.h"
#include "json/stringbuffer.h"

using namespace std;
using namespace cocos2d;
using namespace rapidjson;

class LevelData;

class OpenLevelData
{
public:
	/**
	 *单例
	 */
	static OpenLevelData* getInstance();

	/**
	 *打开关卡数据
	 */
	bool openLevelsData(const string& worlddata);

	/*
	 *解密关卡数据
	 */
	void decrypt(char* cSrc, char* cDest);
	void decrypt(string& cSrc, char* cDest);

	/**
	 *获取所有关卡数据
	 */
	Document* getDocument();

	/**
	 *创建某一个关卡数据
	 */
	void createLevelData(const int level, const char* levelName);

	/**
	 *读取某一个关卡
	 */
	LevelData* readLevelData(const int level);

	/**
	 *设置关卡数
	 */
	void setLevelNumber(const int levelNumber);

	/**
	 *获取关卡数
	 */
	int getLevelNumber() const;

	/**
	 初始化
	 */
	void documentInit();

private:
	OpenLevelData():_document(new Document), _levelNumber(-1){}
	~OpenLevelData() {}

private:
	static OpenLevelData* _instance;
	Document* _document;
	map<int, LevelData*>_levelData;
	int _levelNumber;
};

struct MyPoint
{
	MyPoint():x(0),y(0){}
	int x, y;
};

class LevelData
{
public:
	bool readLevelData(const char* LevelName);
	bool getZombiesVisible() const { return _zombiesIsVisible; }
	bool getZombiesIsSmall() const { return _zombiesIsSmall; }
	bool getZombiesIsBig() const { return _zombiesIsBig; }
	bool getIsNoPlants() const { return _isNoPlants; }
	int getZombiesFrequency() const { return _zombiesFrequency; }
	int getCoinNumbers() const { return _coinNumbers; }
	int getAtLeastSunNumbers() const { return _atLeastSunNumbers; }
	int getFlowerPosition() const { return _flowerPosition; }
	int getCarNumbers() const { return _carNumbers; }
	int getUsePlantsNumbers() const { return _usePlantsNumbers; }
	int getFirstFrequencyTime() const { return _firstFrequencyTime; }
	float getUserLostPosition() const { return _userLose; }
	vector<int>& getGameType() { return _gameType; }
	vector<int>& getZombiesType() { return _zombiesType; }
	vector<int>& getZombiesNumbers() { return _zombiesNumbers; }
	vector<int>& getMunchZombiesFrequency() { return _munchZombiesFrequency; }
	vector<MyPoint>& getNoPlantsPosition() { return _noPlantsPosition; }
	vector<vector<int> >& getZombiesTypeProbabilityFrequency() { return _zombiesTypeProbabilityFrequency; }

CC_CONSTRUCTOR_ACCESS:
	LevelData();
	~LevelData();

private:
	void setGameTypes(const char* LevelName);
	
private:
	bool _isEncryption;                                              /* 是否加密 */
	bool _zombiesIsVisible;                                          /* 僵尸是否隐身 */
    bool _zombiesIsSmall;                                            /* 是否是小僵尸 */
	bool _zombiesIsBig;                                              /* 是否是巨人僵尸 */
	bool _isNoPlants;                                                /* 是否不可种植 */
	int _zombiesFrequency;                                           /* 僵尸总波数 */
	int _coinNumbers;                                                /* 金币数 */
	int _atLeastSunNumbers;                                          /* 至少产生的阳光数 */
	int _flowerPosition;                                             /* 花坛位置 */
	int _carNumbers;                                                 /* 小车数量 */
	int _usePlantsNumbers;                                           /* 使用植物数量 */
	int _firstFrequencyTime;                                         /* 第一波僵尸出现时间 */
	float _userLose;                                                 /* 玩家失败 */
	vector<int>_gameType;                                            /* 游戏类型 */
    vector<int>_zombiesType;                                         /* 僵尸类型 */
	vector<int>_zombiesNumbers;                                      /* 僵尸数 */
	vector<int>_munchZombiesFrequency;                               /* 多僵尸波数 */
	vector<vector<int> >_zombiesTypeProbabilityFrequency;            /* 每一波每种僵尸出现的概率 */
	vector<MyPoint>_noPlantsPosition;                                /* 不可以种植的地方 */
	Document* _document;
};

LevelData.cpp

getInstance()函数

OpenLevelData使用了单例模式,这样保证了只会创建唯一的一个实例。该实例会使用map数据结构保存所有关卡数据。

map<int, LevelData*>_levelData;
OpenLevelData* OpenLevelData::getInstance()
{
	if (_instance == nullptr)
	{
		_instance = new (std::nothrow)OpenLevelData;
	}
	return _instance;
}

openLevelsData()函数

函数有一个参数,表示要加载的文件名称。使用Cocos2d-xFileUtils类加载磁盘文件中的数据,函数返回字符串类型数据。

由于该文件存在加密,所以首先对字符串数据进行解密,然后使用RapidJson库来解析json字符串数据。如果解析失败返回false,解析成功返回true

bool OpenLevelData::openLevelsData(const string& worlddata)
{
	char* passWords;
	string str = FileUtils::getInstance()->getStringFromFile(worlddata);
	passWords = (char*)malloc(sizeof(char) * str.size());

	/* 解密 */
	decrypt(str, passWords);
	
	documentInit();
	_document->Parse<rapidjson::kParseDefaultFlags>(passWords);

	free(passWords);

	if (_document->HasParseError())return false;

	return true;
}

decrypt()函数

函数有两个参数,第一个参数表示要解密的字符串,第二个参数表示解密后的字符串。通过逐个遍历字符进行字符串解密,其算法如下代码所示。

void OpenLevelData::decrypt(string& cSrc, char* cDest)
{
	int   i, h, l, m, n, j = 0;
	for (i = 0; i < static_cast<int>(cSrc.size()); i = i + 2)
	{
		h = (cSrc[i] - 'x');
		l = (cSrc[i + 1] - 'z');
		m = (h << 4);
		n = (l & 0xf);
		cDest[j] = m + n;
		j++;
	}
	cDest[j] = '\0';
}

createLevelData()函数

函数有两个参数,第一参数表示关卡编号,用作map中的key值,当需要获取某一关卡数据时,只需要根据key值就可以获取相关数据。第二参数是关卡名称,根据关卡名称获取文件中的关卡数据。

void OpenLevelData::createLevelData(const int level, const char* levelName)
{
	/* map中如果没有关卡数据 */
	if (!_levelData.count(level))
	{
		LevelData* levelData = new LevelData;
		levelData->readLevelData(levelName);
		_levelData.insert(pair<int, LevelData*>(level, levelData));
	}
}

这个函数功能是解析某一关卡数据,并将其保存的map数据结构中。首先判断map数据结构中是否已经存在该关卡数据,如果不存在,则使用LevelData类中的readLevelData()函数解析文件中该关卡的数据然后将其保存到map数据结构中供后续使用。


readLevelData()函数

函数有一个参数,表示关卡名称,函数根据关卡名称来解析json文件。

bool LevelData::readLevelData(const char* LevelName)
{
	_document = OpenLevelData::getInstance()->getDocument();

	if (_document->HasMember(LevelName))
	{
		_isEncryption = (*_document)[LevelName]["IsEncryption"].GetBool();
		_coinNumbers = (*_document)[LevelName]["CoinNumbers"].GetInt();
		_zombiesFrequency = (*_document)[LevelName]["Frequency"].GetInt();
		_firstFrequencyTime = (*_document)[LevelName]["FirstFrequencyTime"].GetInt();
		_userLose = (*_document)[LevelName]["UserLose"].GetFloat();

		for (unsigned int i = 0; i < (*_document)[LevelName]["GameType"].Size(); i++)
		{
			_gameType.push_back((*_document)[LevelName]["GameType"][i].GetInt());
		}

		for (unsigned int i = 0; i < (*_document)[LevelName]["ZombiesType"].Size(); i++)
		{
			_zombiesType.push_back((*_document)[LevelName]["ZombiesType"][i].GetInt());
		}

		for (unsigned int i = 0; i < (*_document)[LevelName]["MunchZombiesFrequency"].Size(); i++)
		{
			_munchZombiesFrequency.push_back((*_document)[LevelName]["MunchZombiesFrequency"][i].GetInt());
		}

		for (unsigned int i = 0; i < (*_document)[LevelName]["ZombiesNumbers"].Size(); i++)
		{
			_zombiesNumbers.push_back((*_document)[LevelName]["ZombiesNumbers"][i].GetInt());
		}

		vector<int> v;
		for (unsigned int i = 0; i < (*_document)[LevelName]["ZombiesTypeProbability"].Size(); i++)
		{
			v.clear();
			for (unsigned int j = 0; j < (*_document)[LevelName]["ZombiesTypeProbability"][i].Size(); j++)
			{
				v.push_back((*_document)[LevelName]["ZombiesTypeProbability"][i][j].GetInt());
			}
			_zombiesTypeProbabilityFrequency.push_back(v);
		}

		setGameTypes(LevelName);

		return true;
	}
	return false;
}

函数首先判断json文件中是否存在以形参变量命名的关卡名称,如果有则进行数据解析,最后返回true表示解析成功,否则返回false表示解析失败。


setGameTypes()函数

函数有一个参数,表示关卡名称,函数根据关卡名称来解析json文件。

void LevelData::setGameTypes(const char* LevelName)
{
	for (unsigned int i = 0; i < _gameType.size(); i++)
	{
		switch (static_cast<GameTypes>(_gameType.at(i)))
		{
		case GameTypes::AtLeastSunNumbers:
			_atLeastSunNumbers = (*_document)[LevelName]["AtLeastSunNumbers"].GetInt();
			break;
		case GameTypes::FlowerPosition:
			_flowerPosition = 570 + 122 * (*_document)[LevelName]["FlowerPosition"].GetInt();
			break;
		case GameTypes::CarNumbers:
			_carNumbers = (*_document)[LevelName]["CarNumbers"].GetInt();
			break;
		case GameTypes::UserPlantsNumbers:
			_usePlantsNumbers = (*_document)[LevelName]["UserPlantsNumbers"].GetInt();
			break;
		case GameTypes::ZombiesInvisible:
			_zombiesIsVisible = true;
			break;
		case GameTypes::SmallZombies:
			_zombiesIsSmall = true;
			break;
		case GameTypes::BigZombies:
			_zombiesIsBig = true;
			break;
		case GameTypes::NoPlants:
		{
			_isNoPlants = true;
			MyPoint MyPoint;
			for (unsigned int i = 0; i < (*_document)[LevelName]["NoPlants"].Size(); i++)
			{
				MyPoint.x = (*_document)[LevelName]["NoPlants"][i][0].GetInt();
				MyPoint.y = (*_document)[LevelName]["NoPlants"][i][1].GetInt();
				_noPlantsPosition.push_back(MyPoint);
			}
		}
		break;
		}
	}
}

函数根据不同的游戏类型解析不同的数据,使用switch case语句来判断不同的游戏类型。 


其他函数

其他函数就不一一介绍了,可以自行查看阅读代码。

后续

后续将讲解游戏多语言切换功能的实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1595296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ansible创建用户账户和更新ansible库的密钥

1.创建⽤户帐户 从 http://materials/user_list.yml 下载要创建的⽤户的列表&#xff0c;并将它保存到 /home/greg/ansible 在本次考试中使⽤在其他位置创建的密码库 /home/greg/ansible/locker.yml 。创建名为 /home/greg/ansible/users.yml 的 playbook &#xff0c;从⽽…

攻防世界13-simple_php

13-simple_php <?php show_source(*__FILE__*);//高亮文件 include("config.php");//文件包含在内 $a$_GET[a];//获得a $b$_GET[b];//获得b if($a0 and $a){ //判断a是否满足条件echo $flag1; //满足就输出flag1 } if(is_numeric($b)){ //判断b的条件&#x…

解决方案ImportError: cannot import name ‘BertTokenizerFast‘ from ‘transformers‘

文章目录 一、现象二、解决方案 一、现象 从transformers 库调用该包的时候 from transformers import BertTokenizer, AdamW, BertTokenizerFast报错显示 ImportError: cannot import name ‘BertTokenizerFast’ from ‘transformers’ 二、解决方案 追溯查看transforme…

【OpenGL开发】PyQt在关闭应用程序时没有运行析构函数的问题

PyQt在关闭应用程序时没有运行析构函数的问题 目录 一、说明二、python的析构函数三、QT5 存在一些问题四、PyQt5 存在一些问题五、OpenGL的析构问题 一、说明 应用QT做程序界面&#xff0c;在程序退出的时候&#xff0c;需要调用析构函数释放资源&#xff0c;这个操作在Pytho…

跟TED演讲学英文:Why AI is incredibly smart and shockingly stupid by Yejin Choi

Why AI is incredibly smart and shockingly stupid Link: https://www.ted.com/talks/yejin_choi_why_ai_is_incredibly_smart_and_shockingly_stupid Speaker: Yejin Choi Date: April 2023 文章目录 Why AI is incredibly smart and shockingly stupidIntroductionVocabul…

通过调用Vcenter-Api获取Vcenter中服务器信息

通过调用Vcenter-Api获取Vcenter中服务器信息 文章目录 通过调用Vcenter-Api获取Vcenter中服务器信息1. 获取Vmware API帮助文档2. 获取访问凭证3. 获取服务器清单4. 获取服务器更多信息5. 获取虚机更多信息6. 获取磁盘信息7. 获取操作系统相关 1. 获取Vmware API帮助文档 htt…

面试八股——Spring——AOP与事务

AOP的定义 事务的实现 事务的失效场景 异常捕获处理 下图中由于②导致异常&#xff1a; 原因&#xff1a; 解决办法&#xff1a;自己抛出一个非检查异常&#xff08;具体原因看“抛出检查异常”&#xff09;。 抛出检查异常 由于①出错&#xff0c;导致抛出了检查异常 原因&…

[linux api] of_irq_init

总结: 以如下级联的中断控制器为例: of_irq_init会确保先初始化父控制器再初始化子控制器,也即整体按照层序遍历的顺序进行初始化,以上图为例,其初始化顺序为: intc0intc1-2intc3-6具体实现则分为两个阶段: 第一阶段 遍历所有设备节点,并与参数matches进行匹配,找…

Servlet实现常用功能及其他方法

getParameter 获取body或url中指定的key/value值 String classIdreq.getParameter("classId"); getQueryString 获取请求的所有查询参数key,values1 String queryStringreq.getQueryString(); from表单提交 前端通过from表单提交用户名和密码 <!DOCTYPE htm…

# 达梦sql查询 Sql 优化

达梦sql查询 Sql 优化 文章目录 达梦sql查询 Sql 优化注意点测试数据单表查询 Sort 语句优化优化过程 多表关联SORT 优化函数索引的使用 注意点 关于优化过程中工具的选用&#xff0c;推荐使用自带的DM Manage&#xff0c;其它工具在查看执行计划等时候不明确在执行计划中命中…

计算机网络常问面试题

一.HTTPS是如何保证安全传输的 https通过使⽤对称加密、⾮对称加密、数字证书等⽅式来保证数据的安全传输。 客户端向服务端发送数据之前&#xff0c;需要先建⽴TCP连接&#xff0c;所以需要先建⽴TCP连接&#xff0c;建⽴完TCP连接后&#xff0c;服务端会先给客户端发送公钥…

网络网络层之(2)ARP协议

网络网络层之(2)ARP协议 Author&#xff1a;Once Day Date: 2024年4月1日 漫漫长路&#xff0c;有人对你笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的博客-CSDN博客。 参考文档: 《TCP/IP详解卷一》arp(8) - Linux manual page (man7.org)彻底搞懂系…

配置香橙派摄像头服务每次开机自动启动

目录 1.创建一个mjpg.sh脚本 2.在脚本中添加以下内容 3.增加可执行权限 4.在/etc/xdg/autostart/下创建mjpg.desktop 文件输入以下内容 1.创建一个mjpg.sh脚本 touch mjpg.sh 2.在脚本中添加以下内容 #!/bin/bash cd /home/orangepi/Myorangepi/zhinenglajitong/mjpg-stre…

分布式幂等性

1. 什么是幂等性&#xff1f; 幂等性是指在分布式系统中&#xff0c;一个操作多次执行的结果与其执行一次的结果相同。设计具有幂等性的分布式系统可以有效避免数据不一致和重复处理的问题。 幂等系统的应用场景 在微服务架构下&#xff0c;由于分布式天然特性的时序问题, 以…

解决动态规划问题

文章目录 动态规划的定义动态规划的核心思想青蛙跳阶问题解法一&#xff1a;暴力递归解法二&#xff1a;带备忘录的递归解法&#xff08;自顶向下&#xff09;解法三&#xff1a;动态规划&#xff08;自底向上&#xff09; 动态规划的解题套路什么样的问题考虑使用动态规划&…

英语新概念2-回译法-lesson6

我刚刚搬家去柏林大街的房子里。昨天一个流浪汉敲我的门,他想我寻求一顿饭和一杯啤酒。未拒绝了这个请求之后,这个流浪汉倒立着唱歌,我给他了一顿饭,他吃了食物并且喝了啤酒,然后他把一片奶酪放到他的口袋里然后走开了。过了一会儿,一个领居告诉我关于这个流浪汉的事情。…

GAN:对抗生成网络【通俗易懂】

一、概述 对抗生成网络&#xff08;GAN&#xff09;是一种深度学习模型&#xff0c;由两个神经网络组成&#xff1a;生成器G和判别器D。这两个网络被训练来协同工作&#xff0c;以生成接近真实数据的新样本。 生成器的任务是接收一个随机噪声向量&#xff0c;并将其转换为与真…

java数据结构与算法刷题-----LeetCode371. 两整数之和

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 位运算 位运算 解题思路&#xff1a;时间复杂度O( l o g 2 m a …

网络篇09 | 运输层 udp

网络篇09 | 运输层 udp 01 简介UDP 是面向报文的 02 报文协议 01 简介 UDP 只在 IP 的数据报服务之上增加了一些功能&#xff1a;复用和分用、差错检测 UDP 的主要特点&#xff1a;无连接。发送数据之前不需要建立连接。 使用尽最大努力交付。即不保证可靠交付。 面向报文。…

C# dynamic 数据类型

在C#中&#xff0c;dynamic是一种数据类型&#xff0c;它允许在运行时推迟类型检查和绑定。使用dynamic类型&#xff0c;可以编写更具灵活性的代码&#xff0c;因为它允许在编译时不指定变量的类型&#xff0c;而是在运行时根据实际情况进行解析。 dynamic类型的变量可以存储任…