WPF C# Tutorial – Create Parallax Scrolling Endless Runner Game in Visual Studio
- Subject: WPF C# Tutorials
- Learning Time: 3 hours
Full Source for the Parallax Scrolling Endless Runner Game
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading; // add this for the timer
namespace Endless_Runner_Game
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
// create a new instance of the dispatcher timer class called gametimer
DispatcherTimer gameTimer = new DispatcherTimer();
// create three new Rect class instance called player hit box and ground hit box and obstacle hit box
Rect playerHitBox;
Rect groundHitBox;
Rect obstacleHitBox;
// create a new boolean called jumping, by default this will set to false
bool jumping;
// make a new integer called force and set the value to 20
int force = 20;
// make another integer called speed and set the value to 5
int speed = 5;
// create a new instance of the random class called rand
Random rand = new Random();
// game over boolean
bool gameover = false;
// make a sprite int double variable, this will be used to swap the sprites for player
double spriteInt = 0;
// make three image brush instances called player sprite, background sprite and obstacle sprite
ImageBrush playerSprite = new ImageBrush();
ImageBrush backgroundSprite = new ImageBrush();
ImageBrush obstacleSprite = new ImageBrush();
//integer array which we will use to change the obstacle position on screen
int[] obstaclePosition = { 320, 310, 300, 305, 315 };
// empty integer called score
int score = 0;
public MainWindow()
{
InitializeComponent();
//set the focus on my canvas from the WPF
myCanvas.Focus();
// assign the game engine event to the game timer tick
gameTimer.Tick += gameEngine;
// set the game timer interval to 20 milliseconds
gameTimer.Interval = TimeSpan.FromMilliseconds(20);
// first set the background sprite image
backgroundSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/background.gif"));
// add the background sprite to both rectangles
background.Fill = backgroundSprite;
background2.Fill = backgroundSprite;
// run the start game function
StartGame();
}
private void Canvas_KeyDown(object sender, KeyEventArgs e)
{
// if the game over boolean is true and the enter key is pressed
if (e.Key == Key.Enter && gameover)
{
// run the start game function
StartGame();
}
}
private void Canvas_KeyUp(object sender, KeyEventArgs e)
{
// if the space key is pressed AND jumping boolean is true AND player y location is above 260 pixels
if (e.Key == Key.Space && !jumping && Canvas.GetTop(player) > 260)
{
// set jumping to true
jumping = true;
// set force integer to 15
force = 15;
// set speed integer to -12
speed = -12;
// change the player sprite so it looks like he's jumping
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_02.gif"));
}
}
private void StartGame()
{
// this is the start game function
Canvas.SetLeft(background, 0); // set the first background to 0
Canvas.SetLeft(background2, 1262); // set the second background to 1262
// set the player x to 110 and y to 140
Canvas.SetLeft(player, 110);
Canvas.SetTop(player, 140);
// set the obstacle x to 950 and y to 310
Canvas.SetLeft(obstacle, 950);
Canvas.SetTop(obstacle, 310);
// set run sprite function to 1
runSprite(1);
// set the obstacle sprite, load the image from the images folder
obstacleSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/obstacle.png"));
obstacle.Fill = obstacleSprite; // assign the obstacle sprite to the obstacle object
// set jumping to false
jumping = false;
// set game over to false
gameover = false;
// set score to 0
score = 0;
// set the score text to the score integer
scoreText.Content = "Score: " + score;
// start the game timer
gameTimer.Start();
}
private void runSprite(double i)
{
// this is the run sprite function, this function takes one argument inside its brackets
// it takes a double variable called i
// we will use this i to change the images for the player
// below is the switch statement that will change the player sprite
// when the i value changes between 1 and 8 it will assign appropriate sprite to the player sprite
switch (i)
{
case 1:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_01.gif"));
break;
case 2:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_02.gif"));
break;
case 3:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_03.gif"));
break;
case 4:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_04.gif"));
break;
case 5:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_05.gif"));
break;
case 6:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_05.gif"));
break;
case 7:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_07.gif"));
break;
case 8:
playerSprite.ImageSource = new BitmapImage(new Uri("pack://application:,,,/images/newRunner_08.gif"));
break;
}
// finally assign the player rectangle to the player sprite
player.Fill = playerSprite;
}
private void gameEngine(object sender, EventArgs e)
{
// move the player character down using the speed integer
Canvas.SetTop(player, Canvas.GetTop(player) + speed);
// move the background 3 pixels to the left each tick
Canvas.SetLeft(background, Canvas.GetLeft(background) - 3);
Canvas.SetLeft(background2, Canvas.GetLeft(background2) - 3);
// move the obstacle rectangle to the left 12 pixels per tick
Canvas.SetLeft(obstacle, Canvas.GetLeft(obstacle) - 12);
// link the score text label to the score integer
scoreText.Content = "Score: " + score;
// assign the player hit box to the player, gound hit box to the ground rectangle and obstacle hit box to the obstacle rectangle
playerHitBox = new Rect(Canvas.GetLeft(player), Canvas.GetTop(player), player.Width, player.Height);
groundHitBox = new Rect(Canvas.GetLeft(ground), Canvas.GetTop(ground), ground.Width, ground.Height);
obstacleHitBox = new Rect(Canvas.GetLeft(obstacle), Canvas.GetTop(obstacle), obstacle.Width, obstacle.Height);
// check player and ground collision
// IF player hits the ground
if (playerHitBox.IntersectsWith(groundHitBox))
{
//if the player is on the ground set the speed to 0
speed = 0;
// place the character on top of the ground rectangle
Canvas.SetTop(player, Canvas.GetTop(ground) - player.Height);
// set jumping to false
jumping = false;
// add .5 to the sprite int double
spriteInt += .5;
// if the sprite int goes above 8
if (spriteInt > 8)
{
// reset the sprite int to 1
spriteInt = 1;
}
// pass the sprite int values to the run sprite function
runSprite(spriteInt);
}
//if the player hit the obstacle
if (playerHitBox.IntersectsWith(obstacleHitBox))
{
// set game over boolean to true
gameover = true;
// stop the game timer
gameTimer.Stop();
}
//if jumping boolean is true
if (jumping)
{
// set speed integer to -9 so the player will go upwards
speed = -9;
// reduce the force integer
force--;
}
else
{
// if jumping is not true then set speed to 12
speed = 12;
}
// if force is less than 0
if (force < 0)
{
// set jumping boolean to false
jumping = false;
}
// parallax scrolling code for c#
// the code below will scroll the background simlutaniously and make it seem endless
// check the first background
// if the first background X position goes below -1262 pixels
if (Canvas.GetLeft(background) < -1262)
{
// position the first background behind the second background
// below we are setting the backgrounds left, to background2 width position
Canvas.SetLeft(background, Canvas.GetLeft(background2) + background2.Width);
}
// we do the same for the background 2
// if background 2 X position goes below -1262
if (Canvas.GetLeft(background2) < -1262)
{
// position the second background behind the first background
// below we are setting background 2s left position or X position to backgrounds width position
Canvas.SetLeft(background2, Canvas.GetLeft(background) + background.Width);
}
// if the obstacle goes beyond -50 location
if (Canvas.GetLeft(obstacle) < -50)
{
// set the left position of the obstacle to 950 pixels
Canvas.SetLeft(obstacle, 950);
// randomly set the top positio of the obstacle from the array we created earlier
// this will randomly pick a position from the array so it won't be the same each time it comes around the screen
Canvas.SetTop(obstacle, obstaclePosition[rand.Next(0, obstaclePosition.Length)]);
// add 1 to the score
score += 1;
}
// if the game over boolean is set to true
if (gameover)
{
// draw a black border around the obstacle
// and set the border size to 1 pixel
obstacle.Stroke = Brushes.Black;
obstacle.StrokeThickness = 1;
// draw a red border around the player
// and set the border size to 1 pixel
player.Stroke = Brushes.Red;
player.StrokeThickness = 1;
// add the following to the existing score text label
scoreText.Content += " Press Enter to retry";
}
else
{
// if the game is not order then reset the border thickness to 0 pixel
player.StrokeThickness = 0;
obstacle.StrokeThickness = 0;
}
}
}
}
Make sure you have checked the spelling of the variables and objects to match your XAML Window, sometimes it can cause issues where if the objects are spelled differently it will not recognize it in the C# Script Window.
Very nice tutorial, I really appreciate it.
But i got one question:
How do i get a moving ground into the game? when i put an image with the same length as the background and also the nearly same code i fall through the ground.
Pascal
Hi Pascal, glad you liked the tutorial, worked really hard on this one. For your question about moving the ground, you can use similar techniques as being used with the background scrolling animation , have two separate ground rectangles and move them the same way as the backgrounds are. Within the rectangles you can images as it’s texture so it looks like the ground is scrolling with the background.
I think the reason your character is falling through it is because it’s not checking whether you have landed on the ground rectangle. See if that helps.
Happy programming.
Dude your tutorials get better.
I love how you explain the things now.
Thanks for everything you do to us!
realy nice tutorial! big thanks =)
but i have a problem. if i execute the code in vs it is realy slow and lags all the time but if i start the exe under win10 its totaly smooth. so it must be a vs display failure. Do you know a solution for me to solve this issur in vs? its maybe a dump loading property which make it slow. thanks. have a nice day..
maybe its the dispatcher timer because if i try to make a stop watch the displayed counting lags too. it’s the same as i do a normal clock or a timer for a media file. the time is always correct but the displaying is uneven and laggy. i hope you can help me. thanks.
Hello, I was wondering whether this could be done in Win Forms?
Yes you can do similar games in windows form. You won’t get the transparency with the player image and background if you use picture-boxes. This is why I’ve used WPF for this tutorial, it’s a lot easier to have transparency with sprite animation.