概要
本記事では、Unityのカスタムシェーダーを用いてシンプルなUVスクロールを実装する方法を紹介します。
環境
Unity: 6000.3.2f1
プロジェクトテンプレート: Universal 2D
実装
1. ImageのWrap ModeをRepeatに
ImageのWrap ModeをRepeatに変更します。これを忘れるとImageがループしなくなります。

2. カスタムシェーダー実装
次に、カスタムシェーダーを作成します。プロジェクトウィンドウを右クリック→「Create」→「Shader」→「URP Unlit Shader」を選択します。
カスタムシェーダーを実装します。
Shader "Custom/UVScrollShader"
{
Properties
{
[MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
[MainTexture] _BaseMap("Base Map", 2D) = "white"
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial)
half4 _BaseColor;
float4 _BaseMap_ST;
float2 _UVOffset; // UVスクロール用オフセット
CBUFFER_END
// 頂点シェーダー
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
// オフセットを加算してUVをスクロール
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap) + _UVOffset;
return OUT;
}
// フラグメントシェーダー(ピクセルシェーダー)
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
return color;
}
ENDHLSL
}
}
}
3. マテリアル作成
作成したカスタムシェーダーを元に、カスタムマテリアルを作成します。プロジェクトウィンドウを右クリック→「Create」→「Material」を選択します。

作成されたマテリアルのShaderを先程作成したカスタムシェーダーに変更します。

Sprite Rendererのマテリアルを作成したマテリアルに変更します。

4. スクリプト実装
UVスクロール用のスクリプトを実装します。
using UnityEngine;
public class UVScroller : MonoBehaviour
{
[SerializeField] private Vector2 scrollSpeed = new Vector2(0.3f, 0.3f);
private Renderer rend;
private MaterialPropertyBlock propBlock;
private Vector2 currentOffset = Vector2.zero; // 累積オフセット
void Start()
{
rend = GetComponent<Renderer>();
propBlock = new MaterialPropertyBlock();
}
void Update()
{
// スピード × deltaTimeを毎フレーム加算
currentOffset += scrollSpeed * Time.deltaTime;
propBlock.SetVector("_UVOffset", currentOffset);
rend.SetPropertyBlock(propBlock);
}
}
