DEV Community

Cover image for Moving 2D Backgrounds in Unity
Game Dev 4K
Game Dev 4K

Posted on

1

Moving 2D Backgrounds in Unity

In my Unity 2D Game, I need to move some items in the background, including:

  • Pavement
  • Street Lights
  • Buildings

Then it feels like the player is moving. But actually, the items are moving backward.

Have a look at this video:

It'd be great if we automatically spawn these items & move. That's where we can get some help from a C# script. Here's how to do that:

  • Create a script called "InfiniteMovingBelt.cs" in your project
  • Then add the following content
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InfiniteMovingBelt : MonoBehaviour
{
public GameObject item;
public float speed = -1f;
public float spawnOffsetFromScreen = 1f;
public float destroyOffsetFromScreen = 1f;
public float spawnDistance = 5f;
public float spawnProbability = 1f;
private readonly List<GameObject> instances = new List<GameObject>();
void Start()
{
// Create the instance
MakeInitialSpawning();
}
void Update()
{
foreach(GameObject instance in instances.ToArray())
{
if (CanDestroy(instance))
{
Destroy(instance);
instances.Remove(instance);
continue;
}
Vector3 originalPos = instance.transform.position;
float posX = originalPos.x + (speed * Time.deltaTime);
Vector3 newPosition = new Vector3(posX, originalPos.y, originalPos.z);
instance.transform.position = newPosition;
}
SpawnIfNeeded();
}
private Vector3 GetScreenSize()
{
var screenSizePixels = new Vector3(Screen.width, Screen.height, 0);
return Camera.main.ScreenToWorldPoint(screenSizePixels);
}
private bool CanDestroy(GameObject i)
{
Vector3 screenSize = GetScreenSize();
return i.transform.position.x < GetDestroyX();
}
private void SpawnIfNeeded()
{
GameObject last = instances[instances.Count - 1];
float diff = GetSpawnX() - last.transform.position.x;
if (diff >= spawnDistance)
{
SpawnWithProbability(GetDefaultStartPos());
}
}
private float GetSpawnX()
{
return GetScreenSize().x + spawnOffsetFromScreen;
}
private float GetDestroyX()
{
return -(GetScreenSize().x + destroyOffsetFromScreen);
}
private GameObject SpawnWithProbability(Vector3 startPos)
{
bool canSpawn = Random.Range(0f, 1f) < spawnProbability;
if (canSpawn)
{
return SpawnItem(startPos);
}
return SpawnItemAndHide(startPos);
}
private GameObject SpawnItem(Vector3 startPos, bool addToFront = false)
{
GameObject instance = Instantiate<GameObject>(item, startPos, Quaternion.identity);
if (addToFront)
{
instances.Insert(0, instance);
} else
{
instances.Add(instance);
}
return instance;
}
private GameObject SpawnItemAndHide(Vector3 startPos)
{
startPos.y = 10;
GameObject instance = Instantiate<GameObject>(item, startPos, Quaternion.identity);
instances.Add(instance);
return instance;
}
private Vector3 GetDefaultStartPos()
{
return new Vector3(
GetSpawnX(),
gameObject.transform.position.y,
gameObject.transform.position.z
);
}
private void MakeInitialSpawning()
{
Vector3 startPos = GetDefaultStartPos();
while(startPos.x > GetDestroyX())
{
SpawnItem(startPos, addToFront:true);
startPos.x -= spawnDistance;
}
}
}
  • Then Create an "Empty Game Object" and add this script

After that, customize the parameters of this script as shown in the above video.

Better Solution

Since this might be a common task in 2D games, I think there is a much better solution than this. If you know a better solution, just let me know.

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay