- Home /
How can I stop my endless runners background sprites from falling out of sync?
Hey folks!
So I'm doing an infinitely scrolling background using 4 copies of the same sprite scrolling to the left, teleporting a distance to the right off-screen if the sprite passes a specific distance to the left and then scrolling to the left again.
Unfortunately, after a few circulations of the background(s), the sprites start to separate from each other, or, alternately, overlap; their movement and reset fall out of sync. This is not good, I do not like this, can anybody divine a means by which I can enforce perfect sync on my background?
The functionality is handled in two scripts, reproduced below...
THE MOVER!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// This component is used to move scenary and obstacles from the right of the screen to the left.
public class Mover : MonoBehaviour {
// statePlay: so this is a bit of a mess. Ideally we'd drive an OnUpdate function from the
// statePlay OnUpdate, but there are so many Movers switching from active to inactive and
// back that it seemed to make more sense to just get a bool in statePlay.
// On mature reflection, should have driven from statePlay to the object pool to the
// active movers. Ah well.
private StatePlay statePlay;
public enum MoverType
{ // Related switch gets appropriate speed from GC.
dummy,
background,
midground,
obstacleAir,
obstacleGnd,
airHeli,
airCloud,
airBalloons,
airHotBall,
custom,
}
public MoverType moveType;
[Header("Custom Speed only used")]
[Header("with Custom Move Type.")] // Wish there was a way to auto-wrap this stuff. Custom inspector?
public float customSpeed = 6.0f;
private Rigidbody2D RB2D;
public Vector2 moveMeDirection = new Vector2(-1, 0);
public bool goAlongSineWave = false;
public float sineFrequency = 20.0f; // Speed of sine movement
public float sineMagnitude = 0.5f; // Size of sine movement
// private Vector3 axis = Vector3.up;
// private Vector3 pos;
private void Awake()
{
RB2D = GetComponent<Rigidbody2D>();
}
// Use this for initialization
void Start () {
statePlay = GameObject.Find("TextScorePlay").GetComponent<StatePlay>();
}
// Update is called once per frame
void Update () {
float currentSpeedMultiplier = 1.0f;
switch (moveType)
{ // Probably should have done this in START - when no longer tweaking speeds, move it back there!
case MoverType.dummy:
Debug.LogWarning("Move Type set to Dummy! Switching to Background...");
moveType = MoverType.background;
break;
case MoverType.background:
currentSpeedMultiplier = GC.Inst.moveSpd.backgrnd;
break;
case MoverType.midground:
currentSpeedMultiplier = GC.Inst.moveSpd.midgnd;
break;
case MoverType.obstacleAir:
currentSpeedMultiplier = GC.Inst.moveSpd.airDefault;
break;
case MoverType.obstacleGnd:
currentSpeedMultiplier = GC.Inst.moveSpd.groundDefault;
break;
case MoverType.airHeli:
currentSpeedMultiplier = GC.Inst.moveSpd.airHeli;
break;
case MoverType.airCloud:
currentSpeedMultiplier = GC.Inst.moveSpd.airCloud;
break;
case MoverType.airBalloons:
currentSpeedMultiplier = GC.Inst.moveSpd.airBalloons;
break;
case MoverType.airHotBall:
currentSpeedMultiplier = GC.Inst.moveSpd.airHotBall;
break;
case MoverType.custom:
currentSpeedMultiplier = customSpeed;
break;
default:
break;
}
if (statePlay.amActive)
{
Vector2 moveOffset = moveMeDirection.normalized * GC.Inst.DTime() * currentSpeedMultiplier;
if (goAlongSineWave)
{
Vector2 sineOffset = new Vector3(0.0f, Mathf.Sin(Time.time * sineFrequency) * sineMagnitude);
RB2D.MovePosition(RB2D.position + sineOffset + moveOffset);
}
else
{
RB2D.MovePosition(RB2D.position + moveOffset);
}
}
}
}
THE RESETTER!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ResetMovement : MonoBehaviour {
private Collider2D col;
private Transform tf;
public float groundHorizontalLength;
private Vector2 startPos;
private void Awake()
{
tf = GetComponent<Transform>();
startPos = tf.position;
col = GetComponent<Collider2D>();
groundHorizontalLength = col.bounds.size.x * 2;
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(tf.position.x < -groundHorizontalLength)
{
RepositionBackground();
}
}
public void RepositionBackground ()
{
// tf.position = new Vector2(groundHorizontalLength, tf.position.y);
Vector2 groundOffset = new Vector2(groundHorizontalLength * 2, 0);
tf.position = (Vector2) tf.position + groundOffset;
}
public void StartingPositions ()
{
tf.position = startPos;
}
}
Much appreciated for taking a look at this, and thank you in advance for any help you can give me.
--Rev
PS: I realise I could do a UV scroll as an alternative solution, but I'd like to find a specific answer to this issue if possible! Thank you!
Answer by Reverend-Speed · Aug 23, 2017 at 09:41 AM
So the solution was to move in FixedUpdate. Like always.
--RoB
Your answer
Follow this Question
Related Questions
How to hide game window but show sprite? 0 Answers
How to make the background follow the camera 5 Answers
2d backgrounds - filling all screen sizes 1 Answer
2D Sprite Background Rendering 1 Answer