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

Monogame/XNA开发类Terraria游戏:数组仅单个方块渲染问题求助

问题:MonoGame生成全泥土方块世界画面空白

我正在开发类Terraria游戏学习MonoGame/XNA,尝试创建一个完全由泥土方块填充的游戏世界,但运行后画面完全空白。推测问题出在数组中的方块仅能渲染出一个,我之前只有Unity开发经验,习惯用Scriptable Objects处理这类场景。

相关代码如下:

Survival2DGame类

public class Survival2DGame : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;

    private World world;

    private Block dirtBlock;

    public Survival2DGame()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);

        dirtBlock = new Block(Content.Load<Texture2D>("Dirt"), Vector2.Zero);

        world = new World(100, 100);

        WorldGeneration.GenerateWorld(world, dirtBlock);

        Debug.WriteLine(world.blocks[50, 50].Texture);
    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        _spriteBatch.Begin();

        world.Draw(_spriteBatch);

        _spriteBatch.End();

        base.Draw(gameTime);
    }
}

WorldGeneration类

static class WorldGeneration
{
    public static void GenerateWorld(World world, Block dirtBlock)
    {
        for (int x = 0; x < world.Length; x++)
        {
            for (int y = 0; y < world.Height; y++)
            {
                world.blocks[x, y] = dirtBlock;
                world.blocks[x, y].Position = new Vector2(x * 16, y * 16);
            }
        }
    }
}

World类

class World
{
    public int Length { get; set; }
    public int Height { get; set; }

    public Block[,] blocks;

    public World(int length, int height)
    {
        Length = length;
        Height = height;

        blocks = new Block[length, height];
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        foreach (Block block in blocks)
        {
            block.Draw(spriteBatch);
        }
    }
}

Block类

class Block
{
    public Texture2D Texture { get; set; }
    public Vector2 Position { get; set; }

    public Block(Texture2D texture, Vector2 position)
    {
        Texture = texture;
        Position = position;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(Texture, Position, Color.White);
    }
}

解决方案

你的猜测完全正确:所有数组元素都指向同一个Block实例。

C#中引用类型的赋值是传递引用,所以world.blocks[x, y] = dirtBlock只是把同一个dirtBlock对象的引用赋值给数组的每个元素。后续修改Position时,每次都会覆盖这个唯一实例的Position属性,最终所有方块的Position都是最后一次循环的坐标(x=99, y=99时的(1584,1584))。如果游戏窗口没有足够大,这个位置会超出可视区域,导致画面空白。

修改方法

在WorldGeneration.GenerateWorld方法中,每次循环都创建新的Block实例,复用已有Texture资源即可:

static class WorldGeneration
{
    public static void GenerateWorld(World world, Block dirtBlockTemplate)
    {
        for (int x = 0; x < world.Length; x++)
        {
            for (int y = 0; y < world.Height; y++)
            {
                // 基于模板的Texture创建新Block,设置当前坐标
                world.blocks[x, y] = new Block(dirtBlockTemplate.Texture, new Vector2(x * 16, y * 16));
            }
        }
    }
}

类似Unity ScriptableObject的优化思路

如果你想统一管理方块的共享属性(比如Texture、硬度等),可以创建一个BlockTemplate类存储这些共享数据,然后Block实例引用这个模板:

// 方块模板,存储共享属性
class BlockTemplate
{
    public Texture2D Texture { get; set; }
    public int Hardness { get; set; }
    // 其他共享属性...
}

// 方块实例,持有模板引用和独立属性
class Block
{
    public BlockTemplate Template { get; set; }
    public Vector2 Position { get; set; }

    public Block(BlockTemplate template, Vector2 position)
    {
        Template = template;
        Position = position;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(Template.Texture, Position, Color.White);
    }
}

这样既避免重复加载资源,又能让每个Block拥有独立的Position等属性,和Unity的ScriptableObject用法逻辑一致。


内容的提问来源于stack exchange,提问作者Tim Schilson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 21:57:41