5
\$\begingroup\$

Actually I'm sorry for the title, I'm not sure which short phrase will describe situation best. I'm porting (or remastering, I don't know which word suits better, maybe both) a Win32, pure C, DirectX 8 game to XNA. Here is the problem I'm facing now.

The game background represents dirt through which player can dig, leaving the tunnels in it. The background is erased literally from a background bitmap array (which is static) by applying a "dig step" mask forming a new background picture:

enter image description here

From that little knowledge I have currently on XNA, I assume that it's hard to mimic this approach and modify a background texture in Update method. So I was thinking about storing an array of "tunnels" data and draw tunnels above background. This however seems not so elegant as the original approach. You see, tunnel form depends on direction, so I would need to store following information for a cell: 1) directions of tunnel (from 1 to 4), 2) dig amount (for each direction) ranges from 0 to 5.

So question is, what is a good way to implement that? Am I on a right way?

P.S. BTW anyone can try and see it in action!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ +1: For clarifying the question and adding an image. Are you looking to do freeform "digging" or do you do it tiled like the picture does? \$\endgroup\$
    – William
    Apr 21, 2012 at 18:15
  • \$\begingroup\$ It's actually a somewhat semi-free. Logically field in divided into 20*18 pixel cells. And you can't do turn at any place. But while you and other object are moving the minimal movement step is 4 pixels in horizontal and 3 px in vertical direction. So you can be located between cells, and you can, for example be hit by a monster in either cell. I also added link to original game. \$\endgroup\$ Apr 21, 2012 at 18:32
  • \$\begingroup\$ What I mean is, is the terrain tiled? Because then it may be a better option to simply have multiple small textures, rather than one large texture. If you get what I mean. \$\endgroup\$
    – William
    Apr 21, 2012 at 19:15
  • \$\begingroup\$ Oh, I get you idea (I think). Terrain is not tiled. Bg sprite is a 20*4 pixels image which fills background initially. Then tunnels are drawn by erasing it by applying a kind of erase mask (which is different depending on direction you move). This is how it's done in original. \$\endgroup\$ Apr 22, 2012 at 6:09

2 Answers 2

3
\$\begingroup\$

Don't think this is the best way to go about what you are trying to do but is actually very easy in XNA. Check out how RenderTargets work. Below is a simple Example.

Assuming background is the image in your example that is being drawn each frame.

var _renderTarget = new RenderTarget2D(GraphicsDevice, background.width, background.height);
var _spriteBatch = new SpriteBatch(GraphicsDevice);

// While the render target is not null all draw calls will only 
// affect the rendertarget only not the backbuffer (which is what you see next frame).
GraphicsDevice.SetRenderTarget(_renderTarget); 

// Actually doesn't matter in this example but this will default every pixel
// to this color
GraphicsDevice.Clear(Color.Transparent); 

_spriteBatch.Begin();

// Draw the current background
_spriteBatch.Draw(background, Vector2.Zero, Color.White);

// Draw on top of it to modify is however you wish.
// You can actually draw a mask but you can just draw a black texture 
// with transparent edges over it.

_spriteBatch.End()

//Return to drawing to the backbuffer
GraphicsDevice.SetRenderTarget(null);

//Set the background to the render target that holds its modification
background = _renderTarget;
\$\endgroup\$
2
  • \$\begingroup\$ Great info, thanks! While it's not probably the best way, at least I know how to mimic original game logic in XNA! If you have any suggestions how to emulate it in other way, I would be glad to see them. \$\endgroup\$ Apr 22, 2012 at 9:23
  • \$\begingroup\$ Actually your answer was most close one to original behaviour, so I mark it as answer finally. \$\endgroup\$ Apr 28, 2012 at 14:10
1
\$\begingroup\$

Here's my way of adding transparent holes in your background. It uses a simple pixel shader that subtracts the values. I'm not sure this is the best way, there may be some tricks possible simply by using a custom BlendState and the Subtract function.

What I did was create two rendertargets. One holding your dirt. This could just be a large texture, however, to get it tiling dynamically, rendertargets was a good choice.

First I prepare them in the LoadContent override.

...
    // Prepare the dirt layer.
    this.earthRenderTarget = new RenderTarget2D(this.GraphicsDevice, 1280, 720);
    this.GraphicsDevice.SetRenderTarget(this.earthRenderTarget);
    this.GraphicsDevice.Clear(Color.Black);
    this.spriteBatch.Begin();
    for (var x = 0; x < (1280 / this.earth.Width) + 1; x += 1)
        for (var y = 0; y < (720 / this.earth.Height) + 1; y += 1)
            this.spriteBatch.Draw(this.earth, new Vector2(x * this.earth.Width, y * this.earth.Height), Color.White);
    this.spriteBatch.End();

    // Prepare the "hole" layer, with 100 random holes.
    this.maskRenderTarget = new RenderTarget2D(this.GraphicsDevice, 1280, 720);
    this.GraphicsDevice.SetRenderTarget(this.maskRenderTarget);
    this.GraphicsDevice.Clear(Color.Transparent);
    this.spriteBatch.Begin();
    var random = new Random();
    for (var i = 0; i < 100; i += 1)
        this.spriteBatch.Draw(this.chunkMask, new Vector2(random.Next(1280), random.Next(720)), Color.White);
    this.spriteBatch.End();
...

Here's the pixelshader code I used. It just subtracts the individual color components.

texture Mask;

sampler textureSampler  : register(s0);

sampler maskSampler = sampler_state
{
    Texture = <Mask>;
};

float4 MaskFunction(float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 color = tex2D(textureSampler, texCoord);
    float4 mask = tex2D(maskSampler, texCoord);

    return color - mask;
}

technique MaskTechnique
{
    pass MaskPass
    {
        PixelShader = compile ps_2_0 MaskFunction();
    }
}

And finally, I combine the two rendertargets in the Draw override like this:

...
    this.GraphicsDevice.SetRenderTarget(null);
    this.GraphicsDevice.Clear(Color.CornflowerBlue);

    this.maskEffect.Parameters["Mask"].SetValue(this.maskRenderTarget);
    this.spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, this.maskEffect);
    this.spriteBatch.Draw(this.earthRenderTarget, Vector2.Zero, Color.White);
    this.spriteBatch.End();
...

You can see it in action here.

Now, you'll need to implement a rendertarget swapper to further edit the "hole" rendertarget, but I'll leave that as a challenge for you.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for a complete sample, I appreciate your efforts! \$\endgroup\$ Apr 22, 2012 at 17:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .