Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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!

share|improve this question
1  
+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? –  William 'MindWorX' Mariager Apr 21 '12 at 18:15
    
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. –  Petr Abdulin Apr 21 '12 at 18:32
    
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. –  William 'MindWorX' Mariager Apr 21 '12 at 19:15
    
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. –  Petr Abdulin Apr 22 '12 at 6:09

2 Answers 2

up vote 3 down vote accepted

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;
share|improve this answer
    
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. –  Petr Abdulin Apr 22 '12 at 9:23
    
Actually your answer was most close one to original behaviour, so I mark it as answer finally. –  Petr Abdulin Apr 28 '12 at 14:10

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.

share|improve this answer
    
Thanks for a complete sample, I appreciate your efforts! –  Petr Abdulin Apr 22 '12 at 17:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.