You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中使用Irrlicht Lime时Dictionary键不存在错误求助

问题

使用Irrlicht Lime引擎进行C#原型开发时遇到错误:刚向Dictionary<Vector3Di, Voxel>添加指定键值对,紧接着通过该键取值时,提示“字典中找不到键”。本人是该引擎新手,Irrlicht Lime无官方文档,只能参考原版Irrlicht文档开发。

报错代码段

Console.WriteLine("added dirt voxel at : " + new Vector3Di(x, y, z));
voxels.Add(new Vector3Di(x, y, z), new Voxel(VoxelType.Dirt, 2, 0, false, true));
Console.WriteLine(voxels[new Vector3Di(x, y, z)].VoxelType);

完整代码

using IrrlichtLime.Core;
using IrrlichtLime.Scene;
using IrrlichtLime.Video;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Krutesia
{
    internal class Chunk
    {
        
        // 存储区块的位置与体素数据
        public Dictionary<Vector3Di, Voxel> voxels = new Dictionary<Vector3Di, Voxel>();
        public Vector3Di position;
        public Texture[] textures;
        public List<SceneNode> meshes = new List<SceneNode>();

        public Chunk (Vector3Di pos)
        {
            textures = new Texture[7];
            GetTextures();
            position = pos;
            GenerateVoxels();
        }

        public void GenerateVoxels()
        {
            // 生成区块内的体素
            for (int x = 0; x < WorldData.chunkWidth; x++)
            {
                for (int y = 0; y < WorldData.chunkHeight; y++)
                {
                    for (int z = 0; z < WorldData.chunkWidth; z++)
                    {
                        // 根据层级设置体素类型
                        if (y == WorldData.chunkHeight)
                        {
                            voxels.Add(new Vector3Di(x, y, z), new Voxel(VoxelType.BedRock, 0, 0, true, false));
                            Console.WriteLine("added bedrock voxel at : " + new Vector3Di(x, y, z));
                            
                        }
                        else if (y > WorldData.rockRange.X && y < WorldData.rockRange.Y)
                        {
                            Console.WriteLine("added stone voxel at : " + new Vector3Di(x, y, z));
                            voxels.Add(new Vector3Di(x, y, z), new Voxel(VoxelType.Stone, 1, 100, true, false));
                        }
                        else if (y >= WorldData.rockRange.X && y < WorldData.terrainHeightMax)
                        {
                            Console.WriteLine("added dirt voxel at : " + new Vector3Di(x, y, z));
                            voxels.Add(new Vector3Di(x, y, z), new Voxel(VoxelType.Dirt, 2, 25, true, false));
                        }
                        else
                        {
                            Console.WriteLine("added dirt voxel at : " + new Vector3Di(x, y, z));
                            voxels.Add(new Vector3Di(x, y, z), new Voxel(VoxelType.Dirt, 2, 0, false, true));
                            Console.WriteLine(voxels[new Vector3Di(x, y, z)].VoxelType);
                            
                        }

                        
                    }
                }
            }
            GenerateMeshes();
        }

        public void DisplayMesh()
        {
            while (Program.device.Run())
            {
                Program.device.VideoDriver.BeginScene();
                Program.device.SceneManager.DrawAll();

                Program.device.VideoDriver.EndScene();
            }

            Program.device.Drop();
        }

        public void GenerateMeshes()
        {
            
            for (int x = 0; x < voxels.Count; x++)
            {
                for (int y = 0; y < voxels.Count; y++)
                {
                    for (int z = 0; z < voxels.Count; z++)
                    {
                        if(voxels.ContainsKey(new Vector3Di(x, y, z)))
                        {
                            if (voxels[new Vector3Di(x + 1, y + 1, z + 1)].VoxelType == VoxelType.BedRock)
                            {
                                Console.WriteLine(voxels[new Vector3Di(x, y, z)].VoxelType);
                                SceneNode node = Program.device.SceneManager.AddCubeSceneNode();
                                node.SetMaterialTexture(0, textures[0]);
                                node.Position = new Vector3Df(x, y, z);
                                meshes.Add(node);
                            }
                            else if (voxels[new Vector3Di((int)x, (int)y, (int)z)].VoxelType == VoxelType.Stone)
                            {
                                SceneNode node = Program.device.SceneManager.AddCubeSceneNode();
                                node.SetMaterialTexture(0, textures[1]);
                                node.Position = new Vector3Df(x, y, z);
                                meshes.Add(node);
                            }
                            else if (voxels[new Vector3Di((int)x, (int)y, (int)z)].VoxelType == VoxelType.Dirt)
                            {
                                SceneNode node = Program.device.SceneManager.AddCubeSceneNode();
                                node.SetMaterialTexture(0, textures[2]);
                                node.Position = new Vector3Df(x, y, z);
                                meshes.Add(node);
                            }
                        }
                    }
                }
            }

            //DisplayMesh();
        }

        public void GetTextures()
        {
                textures[0] = Program.device.VideoDriver.GetTexture("content/textures/blocks/cobble_stone.png");
                textures[1] = Program.device.VideoDriver.GetTexture("content/textures/blocks/stone.png");
                textures[2] = Program.device.VideoDriver.GetTexture("content/textures/blocks/dirt.png");
                textures[3] = Program.device.VideoDriver.GetTexture("content/textures/blocks/grass_side.png");
                textures[4] = Program.device.VideoDriver.GetTexture("content/textures/blocks/grass_top.png");
                textures[5] = Program.device.VideoDriver.GetTexture("content/textures/blocks/gravel.png");
                textures[6] = Program.device.VideoDriver.GetTexture("content/textures/blocks/sand.png");
        }
    }
}
解决方案

核心原因

Vector3Di作为字典的键时,默认使用引用相等性判断而非值相等性判断。每次调用new Vector3Di(x,y,z)都会创建新的对象实例,即使x/y/z数值完全相同,这些实例的内存引用也不一样,字典会将它们判定为不同的键,导致添加后用新实例查找时找不到对应值。

解决办法

方法1:复用同一个Vector3Di实例

在添加和查找时使用同一个对象,避免重复创建新实例:

var pos = new Vector3Di(x, y, z);
Console.WriteLine("added dirt voxel at : " + pos);
voxels.Add(pos, new Voxel(VoxelType.Dirt, 2, 0, false, true));
Console.WriteLine(voxels[pos].VoxelType);

方法2:为Vector3Di实现自定义相等比较器

创建一个实现IEqualityComparer<Vector3Di>的类,让字典基于x/y/z的数值判断键的相等性:

public class Vector3DiEqualityComparer : IEqualityComparer<Vector3Di>
{
    public bool Equals(Vector3Di a, Vector3Di b)
    {
        if (ReferenceEquals(a, b)) return true;
        if (a is null || b is null) return false;
        return a.X == b.X && a.Y == b.Y && a.Z == b.Z;
    }

    public int GetHashCode(Vector3Di vec)
    {
        return HashCode.Combine(vec.X, vec.Y, vec.Z);
    }
}

初始化字典时传入该比较器:

public Dictionary<Vector3Di, Voxel> voxels = new Dictionary<Vector3Di, Voxel>(new Vector3DiEqualityComparer());

额外修复建议

你的GenerateMeshes方法存在多处问题,一并修复:

  1. 循环条件错误:用voxels.Count作为x/y/z的上限不合理,应使用WorldData.chunkWidth和WorldData.chunkHeight
  2. 多次创建Vector3Di实例查找,会触发同样的键不存在问题
  3. voxels[new Vector3Di(x + 1, y + 1, z + 1)]会触发越界,且新实例大概率找不到键

修改后的GenerateMeshes示例:

public void GenerateMeshes()
{
    for (int x = 0; x < WorldData.chunkWidth; x++)
    {
        for (int y = 0; y < WorldData.chunkHeight; y++)
        {
            for (int z = 0; z < WorldData.chunkWidth; z++)
            {
                var pos = new Vector3Di(x, y, z);
                if(voxels.TryGetValue(pos, out var voxel))
                {
                    SceneNode node = Program.device.SceneManager.AddCubeSceneNode();
                    switch(voxel.VoxelType)
                    {
                        case VoxelType.BedRock:
                            node.SetMaterialTexture(0, textures[0]);
                            break;
                        case VoxelType.Stone:
                            node.SetMaterialTexture(0, textures[1]);
                            break;
                        case VoxelType.Dirt:
                            node.SetMaterialTexture(0, textures[2]);
                            break;
                    }
                    node.Position = new Vector3Df(x, y, z);
                    meshes.Add(node);
                }
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者bard mask

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 08:47:02