Зацикленная прокрутка фона для 2D

В первую очередь это может пригодиться для создания скроллера. Например, какая-нибудь аркада, где не камера движется за игроком, а создается иллюзия движения, потому что двигается фон. Точнее говоря, сдвигается только текстура, сам объект, на котором она висит – тоже не движется. При этом, возможно сделать сразу несколько фонов, которые как бы перемещаются в пространстве, с разной скоростью. Это позволяет добавить эффект глубины. Данный скрипт анимирует до трех фонов, что должно быть достаточно в принципе для любой игры, хотя если внести несколько правок по примеру, можно добавить еще.


Так как нам нужно управлять материалом, мы будем использовать в качестве фона - Mesh Renderer, а не спрайты. Добавляем на сцену 3D объект Quad и назовем его например BG_1. Теперь кидаем на наш квадрат нужную текстуру, она должна быть бесшовной. Растягиваем квадрат по экрану и настраиваем материал меша, выбираем шейдер Unlit:

Зацикленная прокрутка фона для 2D

Если нужна прозрачность, то выбираем Transparent.

Не обязательно использовать именно этот шейдер, но если вы делаете игру из спрайтов, тогда это то - что нужно.



Теперь добавим пустой объект и вешаем на него скрипт MoveBackground:

using UnityEngine;
using System.Collections;

public class MoveBackground : MonoBehaviour {

	public enum ProjectMode {moveX = 0, moveY = 1};
	public ProjectMode projectMode = ProjectMode.moveX;

	public MeshRenderer firstBG;
	public float firstBGSpeed = 0.01f;

	public MeshRenderer secondBG;
	public float secondBGSpeed = 0.05f;

	public MeshRenderer thirdBG;
	public float thirdBGSpeed = 0.1f;

	private Vector2 savedFirst;
	private Vector2 savedSecond;
	private Vector2 savedThird;
	
	void Awake()
	{
		if(firstBG) savedFirst = firstBG.sharedMaterial.GetTextureOffset("_MainTex");
		if(secondBG) savedSecond = secondBG.sharedMaterial.GetTextureOffset("_MainTex");
		if(thirdBG) savedThird = thirdBG.sharedMaterial.GetTextureOffset("_MainTex");
	}

	void Move(MeshRenderer mesh, Vector2 savedOffset, float speed)
	{
		Vector2 offset = Vector2.zero;
		float tmp = Mathf.Repeat(Time.time * speed, 1);
		if(projectMode == ProjectMode.moveY) offset = new Vector2(savedOffset.x, tmp);
		else offset = new Vector2(tmp, savedOffset.y);
		mesh.sharedMaterial.SetTextureOffset("_MainTex", offset);
	}
	
	void Update()
	{
		if(firstBG) Move(firstBG, savedFirst, firstBGSpeed);
		if(secondBG) Move(secondBG, savedSecond, secondBGSpeed);
		if(thirdBG) Move(thirdBG, savedThird, thirdBGSpeed);
	}
	
	void OnDisable()
	{
		if(firstBG) firstBG.sharedMaterial.SetTextureOffset("_MainTex", savedFirst);
		if(secondBG) secondBG.sharedMaterial.SetTextureOffset("_MainTex", savedSecond);
		if(thirdBG) thirdBG.sharedMaterial.SetTextureOffset("_MainTex", savedThird);
	}
}

Итак, тут мы устанавливаем наши фоны целых три, и регулируем их скорость. Фон может быть и один, не обязательно три ставить. Кроме того, есть возможность выбрать направление движения - по оси Х или по Y. Если нужен реверс по осям, то просто ставим отрицательное значение скорости.

Вот как-то так.. Вроде мелочь, но может быть полезной.

Комментариев 14

Офлайн
AltaiR 21 декабря 2015
Пытался сделать движение фона по нажатию на кнопку:


Но есть нюанс, когда например двигаешься вправо, а потом поворачиваешься влево то фон сильно дергается и только потом начинает плавно прокручиваться.
Можно ли это как то исправить?
Зарание спасибо!
Офлайн
Light 22 декабря 2015
Цитата: AltaiR
Можно ли это как то исправить?

Нужно изменить функцию прокрутки Move.

Меняем ее на:

private float tmp;
void Move(MeshRenderer mesh, Vector2 savedOffset, float speed)
{
	float h = Input.GetAxis("Horizontal");
	Vector2 offset = Vector2.zero;
	tmp += Mathf.Abs(speed) * Time.deltaTime * h;
	tmp = Mathf.Clamp01(tmp);
	if(h > 0 && tmp == 1) tmp = 0; else if(h < 0 && tmp == 0) tmp = 1;
	if(projectMode == ProjectMode.moveY) offset = new Vector2(savedOffset.x, tmp);
	else offset = new Vector2(tmp, savedOffset.y);
	mesh.sharedMaterial.SetTextureOffset("_MainTex", offset);
}

Должно работать...
Офлайн
AltaiR 23 декабря 2015
Light, Спасибо огромное за помощь!

Скрипт повесил на камеру и добавил переменную для слежки за объектом.
Заранее извеняюсь за доставучесть, но возможно Вы знаете как реализовать тоже самое только не по нажатию кнопки, а в момент когда персонаж двигается (по оси X). Дело в том что на данный момент когда персонаж врезается в стену а кнопка остается нажатой то он вроде как стоит на месте а фон продолжает двигаться...
Делал так:

void Update()
{
	newPosition = player.transform.position.x;
	moveDirection = newPosition - oldPosition;

	if (firstBG) Move(firstBG, savedFirst, firstBGSpeed);
	if (secondBG) Move(secondBG, savedSecond, secondBGSpeed);
	if (thirdBG) Move(thirdBG, savedThird, thirdBGSpeed);
}

private float tmp;
void Move(MeshRenderer mesh, Vector2 savedOffset, float speed) 
{                                           
	{             
		Vector2 offset = Vector2.zero;
		tmp += Mathf.Abs(speed) * Time.deltaTime;
		tmp = Mathf.Clamp01(tmp);
		if (newPosition != oldPosition && moveDirection <= newPosition && tmp == 1) tmp = 0; else if (newPosition != oldPosition && moveDirection >= newPosition && tmp == 0) tmp = 1;
		if (projectMode == ProjectMode.moveY) offset = new Vector2(savedOffset.x, tmp);
		else offset = new Vector2(tmp, savedOffset.y);
		mesh.sharedMaterial.SetTextureOffset("_MainTex", offset);
		oldPosition = newPosition;
	}        
}

Но что то я явно напутал.
Еще раз заранее спасибо!
Офлайн
Light 23 декабря 2015
AltaiR, если нужно относительно скорости персонажа, то надо получить его скорость.

Для 2D:

public Rigidbody2D body;


Или 3D соответственно:

public Rigidbody body;


Теперь когда у нас есть компонент, берем его скорость. Но скорость тела может быть высокой, а нам надо в пределах -1 и 1, поэтому ограничиваем конечное значение:

private float tmp;
void Move(MeshRenderer mesh, Vector2 savedOffset, float speed)
{
	float h = Mathf.Clamp(body.velocity.x, -1, 1);
	Vector2 offset = Vector2.zero;
	tmp += Mathf.Abs(speed) * Time.deltaTime * h;
	tmp = Mathf.Clamp01(tmp);
	if(h > 0 && tmp == 1) tmp = 0; else if(h < 0 && tmp == 0) tmp = 1;
	if(projectMode == ProjectMode.moveY) offset = new Vector2(savedOffset.x, tmp);
	else offset = new Vector2(tmp, savedOffset.y);
	mesh.sharedMaterial.SetTextureOffset("_MainTex", offset);
}
Офлайн
AltaiR 23 декабря 2015
Light, Благодарю то что нужно!
Жаль только, что теперь скорость у трех фонов одинаковая (пропал эффект параллакса). Но в целом отлично!
Офлайн
Light 24 декабря 2015
Цитата: AltaiR
Жаль только, что теперь скорость у трех фонов одинаковая

Кстати да, это надо исправить. Причина в общей переменной.

Вот так, должно быть получше:

private float first, second, third;
float Move(MeshRenderer mesh, Vector2 savedOffset, float speed, float value)
{
	float h = Mathf.Clamp(body.velocity.x, -1, 1);
	Vector2 offset = Vector2.zero;
	value += Mathf.Abs(speed) * Time.deltaTime * h;
	if(h > 0 && value >= 1) value = 0; else if(h < 0 && value <= 0) value = 1;
	if(projectMode == ProjectMode.moveY) offset = new Vector2(savedOffset.x, value);
	else offset = new Vector2(value, savedOffset.y);
	mesh.sharedMaterial.SetTextureOffset("_MainTex", offset);
	return value;
}

void Update()
{
	if(firstBG) first = Move(firstBG, savedFirst, firstBGSpeed, first);
	if(secondBG) second = Move(secondBG, savedSecond, secondBGSpeed, second);
	if(thirdBG) third = Move(thirdBG, savedThird, thirdBGSpeed, third);
}

Здесь для каждого фона своя переменная, в которую функция Move возвращает текущее значение прокрутки.
Офлайн
AltaiR 28 декабря 2015
Light, Действительно так намного лучше! Спасибо за помощь.
С наступающим Вас!
Офлайн
Добрый день. Скажите пожалуйста, каким образом были реализованы облака?
Офлайн
Light 12 сентября 2016
Женя Мартынив, обычная текстура с прозрачным фоном.
Офлайн
Light,
Спасибо
Офлайн
XpycTee 18 марта 2017
Здравствуйте, это нормально чтобы фон прокручивался 1й текстурой пока она не вышла за box оставляя за собой черный фон, а потом начинал сначала
Офлайн
Light 18 марта 2017
XpycTee, нет, так быть не должно, проверь настройки текстуры:

Офлайн
XpycTee 19 марта 2017
Light,
Спасибо большое, нашел warp mode и переключил на повторение, теперь все отлично
Офлайн
добавь демо сцену у меня не работает!!!
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика