Click here to Skip to main content
Click here to Skip to main content

XNA Scrolling Images at Windows Phone

, 7 Feb 2014
Rate this:
Please Sign up or sign in to vote.
Learn how to scroll images at xna to create worlds, level, so on.

Introduction

This article is mainly focused on how to create a progressive scrolling background method at XNA and handling images partially according as the user drag the finger over the screen in a Windows Phone Device using XNA. It does not show how to drag and drop items separately.

We’ll also navigate through the way to handle the clicks(touches) on several objects over the screen depending on if they have to be presented, only if the screen was dragged within their interval of touchable action.

It´s not necessary using any kind of dragging events and that sort of features.

MSDN contains some tips that explain how to show different backgrounds according to certain level advances.

One example is here:

http://msdn.microsoft.com/en-us/library/dd254919(v=xnagamestudio.31).aspx

Here, the idea is just using one image as background in order to scroll it when dragging.
At the beginning if you’re not used to, dealing with some concepts as translating coordinates may result convoluted, but I can ensure to all of you that is simple. However, you can jump directly to the code and see what easy can be realize these functions on your own.
Here there are a mini video-example of what kind of stuff we really do want to do:

http://www.youtube.com/watch?v=W6Eco_ZPplM

The project and test of this article can be reproduced using the next repository:

https://scrolling.codeplex.com/

and I also attach a image to show the concept.

Context

This kind of task is a common one used in many games which use world maps, some sorts of levels, and certain parts of a whole graphic element. Such games are aimed to show just a part of all of those elements.

The situation is indeed the fact we are working with a resolution screen of WxH and our image is equal or bigger on any dimension (not necessary both of them, just one),

that is: W x Z --> W >= X or Y >= Z

In this article, we’re going to work with a screen resolution of 480x800, and the image used will be maximum on the Y dimension, I mean: 480x2048.

Resources

At the very beginning, you should get or create a few random images to test, which has to be at least greater at Y dimension than the larger dimension of the phone, as well as some small images that will be used later as clickable items, or simply you can download the images example used in this article to test, which are located in the repository specified above.

How to focus it

Main idea

The most important thing to understand is the two sorts of coordinates coexisting, the absolute ones (bounded to the whole background-image) and relative ones (bounded to the current print-view phone screen). From here, some calculations have to be done to set and locate properly the items over the screen.

What do we need, then?

Once the way to translate the coordinates is known, there’s to find what requirements and operations will be necessary to perform to work with the screen and its printings.
- Absolute position of the top border that has to be printed on the screen.
- Flag to control the activation of slidings or pulls. (onDrag)
- Flag to tell the system to drag if enabled. (dragging)
- Vectors to locate the items on the screen.
So, how to realize the drag action?
When the user touches/clicks the screen holding without releasing, it’ll have to be calculated the current frame that must be printed and indeed printing it. This will be doing in a loop unless the user releases.
The result value for the top border of the part of the image to be printed is calculated as:
The Y mileage is Yf - Yo or Pf - Po.
Important: Something obvious but the reader might overlook is the fact that when the drag is downward the screen goes up and viceversa (upward dragging makes the screen going down). This changes the sign of the value got.
TB(new) = TB(previous) - (Yf - Y0) = TB(previous)+ Y0 - Yf
To make the items be able to run any kind of action when touched, we have to keep their position and check if the current range of positions shown on the screen contain the position of such items. This can be checked out translating the local coordinates (obtained by the touching) to absolute coordinates, and then there's to compare it with the current range of positions. Linq can be used to get all the items within a certain range, so if just one is returned, it is surely one item was touched, otherwise no-action will be executed.
The positions should be properly stored into a Dictionary or Hash Table to provide a quickly access.
Once again, the translation of local coordinates to absolute coordinates is done by the current top border.

Saving the items with their positions to enable the clicking would be like that:

Using the code

public class Game1 : Microsoft.Xna.Framework.Game
    {
        const float MAX_POS_START_WORLD_MAP_Y = 1248;
        const int WIDTH = 480, HEIGHT = 800;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // We need to check the top border nearly every moment:
        float TopBorder;

        // Reference to the last touching 
        TouchLocation LastTouch;

        // We also have to know when the screen is starting to move, moving and ending its movement
        // Screen is being scrolled
        bool dragging;

        // Gets the current position clicked over the screen to start properly the screen //movement
        bool onDrag;

        // Background: It’s supposed to be a background larger than the screen
        Rectangle backgroundRectangle; // Rectangle to locate/resize it
        Texture2D backgroundTexture; // Texture to be drawn over the screen just in a certain part

        Texture2D itemClickedItem; // Example item to implement clickable parts
        Texture2D itemUnclickedItem; // Example item to implement clickable parts

        // Simple dictionary to detect clicks
        KeyValuePair<Int32, Int32>[] clickableZones;

        KeyValuePair<Int32, Int32> exampleClickedItem;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Frame speed
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            InactiveSleepTime = TimeSpan.FromSeconds(1);
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            graphics.PreferredBackBufferWidth = WIDTH;
            graphics.PreferredBackBufferHeight = HEIGHT;
            graphics.IsFullScreen = true;
            graphics.SupportedOrientations = DisplayOrientation.Portrait;
            graphics.ApplyChanges();

            // Here we load the resources
            backgroundRectangle = new Rectangle(0, 0, WIDTH, HEIGHT);

            // Here we use an image called "background.png" located in the resources folder content of the project
            backgroundTexture = Content.Load<Texture2D>("background");

            // When a clickable part be clicked, we will draw an star on, and off when unclicked
            itemClickedItem = Content.Load<Texture2D>("staron");
            itemUnclickedItem = Content.Load<Texture2D>("staroff");

            dragging = false;

            // Here we decide the ABSOLUTE coordinates of the clickable parts
            // We take for granted a 100x100 clickable part and we set the center planets as clickable parts
            clickableZones = new KeyValuePair<int, int>[4];
            clickableZones[0] = new KeyValuePair<Int32,Int32>(170, 1850);
            clickableZones[1] = new KeyValuePair<Int32, Int32>(170, 1400);
            clickableZones[2] = new KeyValuePair<Int32, Int32>(170, 1100);
            clickableZones[3] = new KeyValuePair<Int32, Int32>(170, 800);
            // Not use a dictionary because of X couldn't be repeated


            exampleClickedItem = new KeyValuePair<int, int>(0, 0);
        }

        protected override void UnloadContent()
        {
          
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // Catching the position and status of the click
            var touch = TouchPanel.GetState().FirstOrDefault();
            
            bool pressed = touch.State == TouchLocationState.Pressed;
            bool released = touch.State == TouchLocationState.Released;

            if (released) // we release the screen
            {
                dragging = false;
            }
            else if (pressed) // we click again (or drag) the screen
            {
                onDrag = true;
                dragging = true;
            }

            if (onDrag) // beginning a dragging
            {
                LastTouch = touch;
                onDrag = false;
                exampleClickedItem = new KeyValuePair<int, int>(0, 0);
            }
            if (dragging) // scrolling the screen
            {
                float difference = (touch.Position.Y - LastTouch.Position.Y);
                TopBorder -= difference;
                if (TopBorder > MAX_POS_START_WORLD_MAP_Y) TopBorder = MAX_POS_START_WORLD_MAP_Y;
                else if (TopBorder < 0) TopBorder = 0;
                LastTouch = touch;
            }
            if (pressed) // action when clicked
            {
                int X = (int)touch.Position.X, Y = (int)touch.Position.Y;

                // Of course there are other better performances to make this detection, this is just a random one
                var clickableRectangles = clickableZones
                    .Select((i)=> new Rectangle(i.Key, i.Value, 100, 100));

                foreach (var rectangle in clickableRectangles)
                {
                    if (rectangle.Contains(X, Y + (int)TopBorder))
                    {
                        exampleClickedItem = new KeyValuePair<Int32, Int32>(X, Y + (int)TopBorder);
                        break;
                    }
                }
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            spriteBatch.Begin();

            int startYscreen = (int)TopBorder;

            spriteBatch.Draw(backgroundTexture, new Rectangle(0, -startYscreen, 480, 2048), Color.White);

            foreach (var kvp in clickableZones)
            {
                if (new Rectangle(kvp.Key, kvp.Value, 100, 100).Contains(exampleClickedItem.Key, exampleClickedItem.Value))
                {
                    spriteBatch.Draw(itemClickedItem, new Vector2((float)kvp.Key, (float)kvp.Value - TopBorder), Color.White);
                }
                else
                {
                    spriteBatch.Draw(itemUnclickedItem, new Vector2((float)kvp.Key, (float)kvp.Value - TopBorder), Color.White);
                }
            }
            spriteBatch.End();

            base.Draw(gameTime);
        }
    } 

I hope it was of your interest.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Juan Carlos Recio Abad
Software Developer
Spain Spain
I´m a Computer Engineer from Spain with a lot of interest and passion about the world of technologies, I love all kind of them, as well as I do it for the science.
I like to create many sorts of utilities and tools, inventing solutions and methods to get solving the challenges which can appear.
 
I also love math and Physics, its misteries and incredible solutions, so I've always been automatizing algorithms and processes related to them on my own, whether for fun or another affairs.
 
Of course, I enjoy reading history, literature, music, and play sport!

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 7 Feb 2014
Article Copyright 2014 by Juan Carlos Recio Abad
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid