我在 XNA 中开发了一个名为“voxel”的小游戏已经有一段时间了。一个类似 Minecraft 的 .NET C# 游戏。我使用一个简单的概念来保存和读取我的游戏中的数据来构建世界地图。一切都存储在一个 xml 文件中。
现在我正在尝试加载更大的地图,并且在生成地图期间引发了“paf”异常:
[系统内存不足异常]
我不明白为什么,因为引发异常后我的文件不是很重,大约70 mb。在生成高达 70 mb的 XML 文件期间出现异常是否正常?
使用的开发环境
- Microsoft Windows 7 (x64) 位
- Visual Studio 2012 专业更新 3
- 8192 公羊
- 英特尔 i5 CPU 2.5 Ghz (4 cpu)
我确实使用 CLR 分析器进行了测试,以检查垃圾收集器的工作原理:
我初始化 XmlWritter 的代码:
private XmlTextWriter myXmlTextWriter ;
#region prepareNewWorldXmlFIle
private void PreparedNewWorldXmlFile()
{
Stream fs = new FileStream(currentPath + "World\\world.xml", FileMode.Create);
myXmlTextWriter = new XmlTextWriter(fs,Encoding.ASCII);
myXmlTextWriter.Formatting = Formatting.Indented;
myXmlTextWriter.WriteStartDocument(false);
myXmlTextWriter.WriteComment("World Map ID:");
//World and his attribute
myXmlTextWriter.WriteStartElement("World");
myXmlTextWriter.WriteStartElement("Matrix", null);
myXmlTextWriter.WriteStartElement("Regions");
}
#endregion
我用来生成世界地图并编写 xml 文件的代码:
//Octree calcul and generate map to xml file
foreach (Region region in arcadia.world.Regions)
{
isAllCheckSameIdOctree = false;
if (isFirstGenerationWorld)
{
//Regions and attributes
myXmlTextWriter.WriteStartElement("Region");
myXmlTextWriter.WriteAttributeString("id", indexRegion.ToString());
myXmlTextWriter.WriteAttributeString("min", "x:" + region.PositionMin.X + ";y:" + region.PositionMin.Y + ";z:" + region.PositionMin.Z);
myXmlTextWriter.WriteAttributeString("max", "x:" + region.PositionMax.X + ";y:" + region.PositionMax.Y + ";z:" + region.PositionMax.Z);
myXmlTextWriter.WriteStartElement("Structures");
myXmlTextWriter.WriteAttributeString("type", "cube");
}
indexRegion++;
if (region.Matrice != null)
{
//If the node to generate contain minimum a height divisible by 2
if (((region.PositionMax.Y - region.PositionMin.Y) / 2) > 2)
{
//generate and octree by 8
GenerateNodes(region, region.PositionMin, 8);
}
else if (((region.PositionMax.Y - region.PositionMin.Y) / 2) <= 2)
{
//generate and octree by 4
GenerateNodes(region, region.PositionMin, 4);
}
while (!isAllCheckSameIdOctree)
{
if (nodeToRegenerate != null && needRecurseBuild)
{
//if the node is greater than 2
if (nodeToRegenerate.TotalHeight > 2)
{
nodeToRegenerate = GenerateNodes(nodeToRegenerate, region, nodeToRegenerate.Position, 8);
if (nodeToRegenerate == null)
{
isAllCheckSameIdOctree = true;
}
}
else if (nodeToRegenerate.TotalHeight <= 2)
{
nodeToRegenerate = GenerateNodes(nodeToRegenerate, region, nodeToRegenerate.Position, 4);
if (nodeToRegenerate == null)
{
isAllCheckSameIdOctree = true;
}
}
}
else
{
isAllCheckSameIdOctree = true;
}
}
if (isFirstGenerationWorld)
{
myXmlTextWriter.WriteEndElement();//Ferme le noeud Structures
myXmlTextWriter.WriteEndElement();//Ferme le noeud Region
myXmlTextWriter.Flush();
}
}
else
{
if (isFirstGenerationWorld)
{
myXmlTextWriter.WriteEndElement();//Ferme le noeud Structures
myXmlTextWriter.WriteEndElement();//Ferme le noeud Region
myXmlTextWriter.Flush();
}
}
}
if (isFirstGenerationWorld)
{
myXmlTextWriter.WriteEndElement();//Ferme le noeud Regions
myXmlTextWriter.WriteEndElement();//Ferme le noeud World
myXmlTextWriter.Flush();
myXmlTextWriter.Close();
}
在我的 generatedNode 函数中捕获异常,有关更多信息,请参见下文
我的 generateNode 函数递归,其中异常触发“系统内存不足异常”
#region ReGenerateWorld
private Node GenerateNodes(Node nodeToRegenerate, Region region, Vector3 position, int countToCut)
{
//Relative dimension of the parent octree
int widthParent = (int)nodeToRegenerate.TotalWidth / 2;
int heightParent = (int)nodeToRegenerate.TotalHeight / 2;
int lenghtParent = (int)nodeToRegenerate.TotalLenght / 2;
//Relative dimension of the parent octree
int widthNode = (widthParent) / (countToCut / (countToCut / 2));
int heightNode = (heightParent) / (countToCut / (countToCut / 2));
int lenghtNode = (lenghtParent) / (countToCut / (countToCut / 2));
if (heightNode < 1)
{
heightNode = 1;
}
int refX = (int)position.X / 2;
int refY = (int)position.Y / 2;
int refZ = (int)position.Z / 2;
int indexStartX = 0;
int indexStartY = 0;
int indexStartZ = 0;
int nbrToCut = 0;
if (heightParent >= 2)
{
nbrToCut = ((widthParent / (widthParent / 2))) * ((heightParent / (heightParent / 2))) * ((lenghtParent / (lenghtParent / 2)));
}
else
{
nbrToCut = 4;
heightNode = 1;
}
//Calculate the number of cubic to cut
//Génére les noeud racine
int countVertical = 0;
int calcPosX = 0;
int calcPosY = 0;
int calcPosZ = 0;
int[][][] nodeMatriceWorld = null;
bool firstTime;
newNode = null;
int idGroup = 0;
bool isSameId = true;
int idToCheck = 0;
for (int index = 0; (index < nbrToCut) && (refY < 32); index++)
{
indexStartX = refX;
indexStartY = refY;
indexStartZ = refZ;
try
{
nodeMatriceWorld = new int[widthNode][][];
for (int i = 0; i < widthNode; i++)
{
nodeMatriceWorld[i] = new int[lenghtNode][];
for (int j = 0; j < lenghtNode; j++)
{
nodeMatriceWorld[i][j] = new int[heightNode];
}
}
}
catch (Exception ex)
{
// OUT OF MEMORY EXCEPTION HERE
Console.Out.WriteLine(ex.Message);
}
firstTime = true;
for (int epaisseur = 0; epaisseur < heightNode; epaisseur++, indexStartY++)
{
for (int ligne = 0; ligne < lenghtNode; ligne++, indexStartZ++)
{
for (int collone = 0; collone < widthNode; collone++, indexStartX++)
{
if (firstTime)
{
calcPosX = indexStartX;
calcPosY = indexStartY;
calcPosZ = indexStartZ;
firstTime = false;
}
nodeMatriceWorld[collone][ligne][epaisseur] = matriceWorld[indexStartX][indexStartZ][indexStartY];
}
indexStartX = refX;
}
indexStartZ = refZ;
}
indexStartY = refY;
idGroup = matriceWorld[calcPosX][calcPosZ][calcPosY];
countVertical++;
if (newNode != null)
{
newNode.Dispose();
}
newNode = new Node(nodeMatriceWorld, new Vector3(calcPosX, calcPosY, calcPosZ), idGroup, widthNode, heightNode, lenghtNode);
region.Nodes[idGroup].Add(newNode);
//Regions.Add(new Node(nodeMatriceWorld, new Vector3(calcPosX, calcPosY, calcPosZ), idGroup));
refX += widthNode;
if (countVertical >= 4)
{
refY = ((int)position.Y / 2) + heightNode;
refX = ((int)position.X / 2);
refZ = (int)position.Z / 2;
countVertical = 0;
}
else if (countVertical == 2)
{
refZ = ((int)position.Z / 2) + lenghtNode;
refX = ((int)position.X / 2);
}
}
isSameId = true;
nodeToRegenerate = null;
needRecurseBuild = false;
idToCheck = 0;
// Check for each octree node if all are the same id
foreach (List<Node> listNode in region.Nodes)
{
foreach (Node node in listNode.Where(m => m.isGroupSameId == false))
{
isSameId = true;
idToCheck = node.matriceNode[0][0][0];
node.isGroupSameId = true;//Le met a true au depart
for (int epaisseur = 0; epaisseur < node.TotalHeight / 2 && isSameId; epaisseur++)
{
for (int ligne = 0; ligne < node.TotalLenght / 2 && isSameId; ligne++)
{
for (int collone = 0; collone < node.TotalWidth / 2 && isSameId; collone++)
{
if (node.matriceNode[collone][ligne][epaisseur] != idToCheck)
{
isSameId = false;//si au moin un cube est différent on le marque
node.isGroupSameId = false;
//node.ItemGroup = node.matriceNode[collone, epaisseur, ligne];
nodeToRegenerate = node;
needRecurseBuild = true;
break;
}
}
}
}
if (!isSameId)
{
break;
}
else
{
if (idToCheck != 0)
{
isSameId = true;
node.isGroupSameId = true;
node.ItemGroup = idToCheck;
node.matriceNode = null;
node.Cube = new Primitives3D.Cube(node.ItemGroup, node.Position, new Vector3(0, 0, 0), node.TotalWidth, node.TotalHeight, node.TotalLenght);
//Initialise le cube qui représente le noeud
node.Cube.BuildCubeStart();
if (isFirstGenerationWorld)
{
myXmlTextWriter.WriteStartElement("Cube");
//Structures et ses attributs
myXmlTextWriter.WriteAttributeString("id", node.ItemGroup.ToString());
myXmlTextWriter.WriteAttributeString("min", "x:" + node.Cube.BoundingBox.Min.X + ";y:" + node.Cube.BoundingBox.Min.Y + ";z:" + node.Cube.BoundingBox.Min.Z);
myXmlTextWriter.WriteAttributeString("max", "x:" + node.Cube.BoundingBox.Max.X + ";y:" + node.Cube.BoundingBox.Max.Y + ";z:" + node.Cube.BoundingBox.Max.Z);
myXmlTextWriter.WriteEndElement();//Ferme le noeud xml cube
myXmlTextWriter.Flush();
}
//Ajoute l'id du noeud aux groupe d'id de la region s'il n'y était pas auparavant
if (!region.IdFound.Contains(node.ItemGroup) && node.ItemGroup != 0)
{
region.IdFound.Add(node.ItemGroup);
}
}
// If the node group is equal to an empty group id -> 0 it removes entire else
{
nodeToDelete = node;
}
}
}
if (!isSameId)
{
break;
}
//Console.Out.WriteLine("Nodes cout generated : " + Nodes.Count.ToString());
}
// If a node does not contain all the same id -> go remove it
if (!isSameId)
{
region.Nodes[nodeToRegenerate.ItemGroup].Remove(nodeToRegenerate);
}
if (nodeToDelete != null)
{
region.Nodes[nodeToDelete.ItemGroup].Remove(nodeToDelete);
}
nodeToDelete = null;
//Dispose the resources
newNode.Dispose();
nodeMatriceWorld = null;
return nodeToRegenerate;
}
#endregion