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
相关产品推荐
相关产品推荐

