Reputation: 1
I'm trying to learn Monogame / XNA by creating a little Terraria-like game. I currently want to create a world, which is completely filled with dirt. My problem is that only one block from an block array gets drawn. Here is my code:
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);
}
}
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);
}
}
}
}
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);
}
}
}
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);
}
}
When i run the game its completely empty.
I think it has something to do with me only creating one dirt block and not a new one everytime i add it to the array. I so far only have experience with unity and there i would handle this with Scriptable Objects.
Upvotes: 0
Views: 110
Reputation: 2860
There is a subtle bug in your game, but the critical issue is in your code for WorldGeneration.GenerateWorld()
In your implementation, you have done
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; // THIS IS THE PROBLEM
world.blocks[x, y].Position = new Vector2(x * 16, y * 16);
}
}
}
}
You see, in C# all class instances are reference types, meaning when you do world.blocks[x,y]=dirtblock
for all iterations, All of your instances are referring/pointing at the same instance dirtblock
.
To explain the above step by step, let's say the dirtblock instance is allocated in memory location 0x10
. In the first iteration (x=0, y=0
), the following is executed:
world.blocks[0, 0] = dirtblock;
Then world.blocks[0,0]
is also at the same location 0x10
. At this point, changing the value dirtblock
or the value of world.blocks[0,0]
would change the same variable. So at the end of the iteration, dirtblock
and world.block[0, 0]
has their Position
variable at (0, 0)
In the next iteration, world.blocks[0, 1]
is also assigned to the same memory location, and its Position
is made to be (0, 16)
. But this also means that world.blocks[0, 0]
and dirtblock
's Position value is made to be (0, 16)
since all these variables are pointing at the same reference.
At the end of the entire loop, all blocks in your code have their Position
value the same as the Position
value of the last block, i.e world.blocks[99, 99]
. Since this is outside the dimension of the game (i.e 640x480) You don't see anything.
So to get your code working, make a new memory instance for every block, as shown:
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] = new Block(dirtBlock.Texture, new Vector2(x * 16, y * 16));
}
}
}
}
Alternatively, you can keep the world generation code unchanged, and have the Block
as a struct instead of a class, and that will work just as you expect.
struct Block
{
....// Rest of the code unchanged
}
This will work because now Block
is a value type, and on each assignment, a new instance is automatically created.
I recommend you to give this article a read for some more info on value and reference types in C# if this is a new topic for you. You only need to read up to the first 3 headings ("Introduction", "First, What Are Structs?", "Value and Reference Types").
Upvotes: 2