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.

What I'm attempting to do is create a game in which a space ship explores a large area of space. I'm randomly generating placement of planets, space stations etc. I want to use the ParallaxBackground (I actually had to find a custom implementation which supports both x and y parallax values).

When a planet is generated I create a sprite, and add a new parallax entity to the background.

My issue is that when I place the sprite, it is not being placed properly (due to the parallax calculation I'm assuming). As the player navigates further away from the origin (0,0 in world space), the offset from the position the object is supposed to appear in the background and the position in which it actually appears gets larger and larger.

Here is the code for the custom drawing of the ParallaxEntity2D:

public void onDraw(final GLState pGLState, final Camera pCamera, final float pParallaxValueX, final float pParallaxValueY) {
            pGLState.pushModelViewGLMatrix();
            {
                final float cameraWidth = pCamera.getWidth();
                final float cameraHeight = pCamera.getHeight();
                final float shapeWidthScaled = this.mAreaShape.getWidthScaled();
                final float shapeHeightScaled = this.mAreaShape.getHeightScaled();

                //reposition
                float baseOffsetX = (pParallaxValueX * this.mParallaxFactor);
                if (this.mRepeatX) {
                    baseOffsetX = baseOffsetX % shapeWidthScaled;
                    while(baseOffsetX > 0) {
                        baseOffsetX -= shapeWidthScaled;
                    }
                }

                float baseOffsetY = (pParallaxValueY * this.mParallaxFactor);

                if (this.mRepeatY) {
                    baseOffsetY = baseOffsetY % shapeHeightScaled;
                    while(baseOffsetY > 0) {
                        baseOffsetY -= shapeHeightScaled;
                    }
                }

                //draw
                pGLState.translateModelViewGLMatrixf(baseOffsetX, baseOffsetY, 0);

                float currentMaxX = baseOffsetX;
                float currentMaxY = baseOffsetY;

                do {
                    this.mAreaShape.onDraw(pGLState, pCamera);

                    if (this.mRepeatY) {
                        currentMaxY = baseOffsetY;                          
                        do {
                            pGLState.translateModelViewGLMatrixf(0, shapeHeightScaled, 0);
                            currentMaxY += shapeHeightScaled;                       
                            this.mAreaShape.onDraw(pGLState, pCamera);
                        } while(currentMaxY < cameraHeight);
                        pGLState.translateModelViewGLMatrixf(0, -currentMaxY + baseOffsetY, 0);
                    }
                    pGLState.translateModelViewGLMatrixf(shapeWidthScaled, 0, 0);
                    currentMaxX += shapeWidthScaled;
                } while (this.mRepeatX && currentMaxX < cameraWidth);
            }
            pGLState.popModelViewGLMatrix();
        }

This is far beyond my ability to understand in terms of the calculation required to figure out where in screen space the sprite needs to be placed for it to show up in the background properly. I've gotten close by hacking together some simple math, but I want this to be perfect.

I guess first I need to understand what the above onDraw code is doing exactly. Maybe there is another approach that would work better for this instance? I explored being able to use the z component on a sprite, but I couldn't get this to work with GLES2 (the example for GLES1 is here).

share|improve this question
    
I've realized that this is not the appropriate approach to pull of what it is I am trying to do. I need some way of implementing depth with the 2D engine... –  SoWeLie May 20 '12 at 0:22

1 Answer 1

up vote 3 down vote accepted

I think you might have been over thinking the parallax scrolling technique. All it does is make sprites that are 'further away' move slower than those that are near and all the variables we need is a speed and an offset. The code above seems to do a lot more than that, let's look at the core of the parallax scrolling technique. You will see that what you're trying to do is quite easy:

Let's only consider the X-axis for now.

Say we want to make a planet that is quite far away, we give it a parallaxSpeed of 0.9. Now say we would like the planet to be visible when the camera is at X=30. Then we need to think of some offset to make this happen: 30*0.9 = 27 so the offset should be 3.

Let's see if this works:

X=25 gives us 25.5, the planet is still a bit to the right of the screen
X=30 gives us 30, the planet is in the center of the screen
X=35 gives us 34.5, the planet is a bit to the left of the screen

This is what we would expect for a planet quite far away. Now we need to find an offset so that offset + speed*cameraPosition = X. This is easy: it's the difference between the desired position and the position given by the formula without a offset. So the general method is:

SetAtPosition(planet, float parallaxSpeed, vector2 position)
{
    planet.OffsetX = position.X - (position.X * parallaxSpeed);
    planet.OffsetY = position.Y - (position.Y * parallaxSpeed);
    planet.parallaxSpeed = parallaxSpeed;
}

And the method for drawing is:

Draw(planet, cameraPosition)
{
    Sprite sprite;
    sprite.X = planet.OffsetX + planet.parallaxSpeed * cameraPosition.X;
    sprite.Y = planet.OffsetY + planet.parallaxSpeed * cameraPosition.Y;
    sprite.Draw();
}

EDIT: The actual code I used to accomplish this with the AndEngine:

import org.andengine.engine.camera.Camera;
import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.util.GLState;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

public class ParallaxSprite extends Sprite {

    private float parallaxSpeed;
    private float offsetX = 0;
    private float offsetY = 0;

    /**
     * Creates an instance of {@link ParallaxSprite}
     * @param pX
     * @param pY
     * @param parallaxSpeed
     * @param pTextureRegion
     * @param pVertexBufferObjectManager
     */
    public ParallaxSprite(float pX, float pY, float parallaxSpeed, ITextureRegion pTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager) {
        super(pX, pY, pTextureRegion, pVertexBufferObjectManager);

        this.parallaxSpeed = parallaxSpeed;
        this.offsetX = pX - (pX * parallaxSpeed);
        this.offsetY = pY - (pY * parallaxSpeed);
    }

    @Override
    protected void onManagedDraw(GLState pGLState, Camera pCamera) {
        pGLState.pushModelViewGLMatrix();
        {
            final float x = this.offsetX + this.parallaxSpeed * pCamera.getXMin();
            final float y = this.offsetY + this.parallaxSpeed * pCamera.getYMin();

            pGLState.translateModelViewGLMatrixf(x, y, 0);

            /* Draw only self. */
            this.preDraw(pGLState, pCamera);
            this.draw(pGLState, pCamera);
            this.postDraw(pGLState, pCamera);
        }
        pGLState.popModelViewGLMatrix();
    }

}
share|improve this answer
    
Thank you for the response, I will give this a shot and let you know how it goes! –  SoWeLie May 20 '12 at 14:46
    
Dude, you are amazing! I'm just going to edit your answer to post the actual code I used. One last thing though, could you point me in the direction of an explanation of these calculations? Thank you very much! –  SoWeLie May 20 '12 at 15:51
    
@SoWeLie Thanks man, I made the calculations on the spot so I don't have a reference for it, if anything is unclear let me know and I'll try to explain it further –  Roy T. May 20 '12 at 16:42
    
Oh okay, it makes perfect sense I just didn't know if they were related to a larger principle. –  SoWeLie May 20 '12 at 21:58

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.