Browse Source

Add repeating background example with scrolling texture (#144)

* Add repeating background example with scrolling texture

* Add thumbnail image
Artsiom Trubchyk 4 weeks ago
parent
commit
e9bc30285c

BIN
material/repeating_background/assets/bg_icon.png


BIN
material/repeating_background/assets/coin_06.png


+ 10 - 0
material/repeating_background/assets/custom.font

@@ -0,0 +1,10 @@
+font: "/builtins/fonts/vera_mo_bd.ttf"
+material: "/builtins/fonts/font-df.material"
+size: 50
+outline_alpha: 1.0
+outline_width: 5.0
+shadow_alpha: 1.0
+shadow_y: -4.0
+output_format: TYPE_DISTANCE_FIELD
+render_mode: MODE_MULTI_LAYER
+characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"

+ 4 - 0
material/repeating_background/assets/sprites.atlas

@@ -0,0 +1,4 @@
+images {
+  image: "/assets/coin_06.png"
+}
+extrude_borders: 2

+ 18 - 0
material/repeating_background/example.md

@@ -0,0 +1,18 @@
+---
+tags: material
+title: Repeating Background
+brief: Create a scrolling background using a repeating texture on a model quad.
+author: Artsiom Trubchyk
+scripts: repeating_background.script, repeating_background.vp, repeating_background.fp
+thumbnail: thumbnail.png
+---
+
+A repeating, scrolling texture can add visual interest to a static background. This example demonstrates how to create an infinitely tiling background using a model quad with a repeating texture. The effect is achieved by scrolling the UV coordinates over time, creating smooth, continuous motion.
+
+The script driving the effect works as follows:
+
+* Each frame it reads the current window size and scales the `background` game object so the quad covers the full viewport. The rotation is set via `euler.z` (Rotation Z in the IDE).
+* It converts the window size into a UV repeat scale (`uv_params.x/y`) so the texture tiles across the screen.
+* It advances a scrolling offset based on `scroll_speed` and `tile_size`, wraps it to the 0..1 range, and sends `uv_params` to the model material.
+
+The asset used in this example is from Kenney's [Puzzle Pack 2](https://www.kenney.nl/assets/puzzle-pack-2), licensed under CC0.

+ 169 - 0
material/repeating_background/example/repeating_background.collection

@@ -0,0 +1,169 @@
+name: "repeating_background"
+scale_along_z: 0
+embedded_instances {
+  id: "background"
+  data: "components {\n"
+  "  id: \"repeating_background\"\n"
+  "  component: \"/example/repeating_background.script\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"model\"\n"
+  "  type: \"model\"\n"
+  "  data: \"mesh: \\\"/builtins/assets/meshes/quad.dae\\\"\\n"
+  "name: \\\"{{NAME}}\\\"\\n"
+  "materials {\\n"
+  "  name: \\\"default\\\"\\n"
+  "  material: \\\"/example/repeating_background.material\\\"\\n"
+  "  textures {\\n"
+  "    sampler: \\\"texture0\\\"\\n"
+  "    texture: \\\"/assets/bg_icon.png\\\"\\n"
+  "  }\\n"
+  "}\\n"
+  "\"\n"
+  "}\n"
+  ""
+  rotation {
+    z: 0.13052619
+    w: 0.9914449
+  }
+  scale3 {
+    x: 512.0
+    y: 512.0
+  }
+}
+embedded_instances {
+  id: "camera"
+  data: "embedded_components {\n"
+  "  id: \"camera\"\n"
+  "  type: \"camera\"\n"
+  "  data: \"aspect_ratio: 1.0\\n"
+  "fov: 0.7854\\n"
+  "near_z: -1.0\\n"
+  "far_z: 1.0\\n"
+  "orthographic_projection: 1\\n"
+  "\"\n"
+  "}\n"
+  ""
+}
+embedded_instances {
+  id: "ui"
+  data: "embedded_components {\n"
+  "  id: \"label1\"\n"
+  "  type: \"label\"\n"
+  "  data: \"size {\\n"
+  "  x: 512.0\\n"
+  "  y: 60.0\\n"
+  "}\\n"
+  "text: \\\"YOU EARNED\\\"\\n"
+  "font: \\\"/assets/custom.font\\\"\\n"
+  "material: \\\"/builtins/fonts/label-df.material\\\"\\n"
+  "\"\n"
+  "  position {\n"
+  "    y: 187.0\n"
+  "    z: 0.1\n"
+  "  }\n"
+  "  scale {\n"
+  "    x: 0.75\n"
+  "    y: 0.75\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"label2\"\n"
+  "  type: \"label\"\n"
+  "  data: \"size {\\n"
+  "  x: 512.0\\n"
+  "  y: 60.0\\n"
+  "}\\n"
+  "text: \\\"999 COINS!\\\"\\n"
+  "font: \\\"/assets/custom.font\\\"\\n"
+  "material: \\\"/builtins/fonts/label-df.material\\\"\\n"
+  "\"\n"
+  "  position {\n"
+  "    y: -169.0\n"
+  "    z: 0.1\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"coin3\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"default_animation: \\\"coin_06\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "size {\\n"
+  "  x: 96.0\\n"
+  "  y: 96.0\\n"
+  "}\\n"
+  "size_mode: SIZE_MODE_MANUAL\\n"
+  "textures {\\n"
+  "  sampler: \\\"texture_sampler\\\"\\n"
+  "  texture: \\\"/assets/sprites.atlas\\\"\\n"
+  "}\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 20.0\n"
+  "    y: 41.0\n"
+  "    z: 0.1\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"coin1\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"default_animation: \\\"coin_06\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "size {\\n"
+  "  x: 96.0\\n"
+  "  y: 96.0\\n"
+  "}\\n"
+  "size_mode: SIZE_MODE_MANUAL\\n"
+  "textures {\\n"
+  "  sampler: \\\"texture_sampler\\\"\\n"
+  "  texture: \\\"/assets/sprites.atlas\\\"\\n"
+  "}\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: -41.0\n"
+  "    y: 15.0\n"
+  "    z: 0.1\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"coin2\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"default_animation: \\\"coin_06\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "size {\\n"
+  "  x: 96.0\\n"
+  "  y: 96.0\\n"
+  "}\\n"
+  "size_mode: SIZE_MODE_MANUAL\\n"
+  "textures {\\n"
+  "  sampler: \\\"texture_sampler\\\"\\n"
+  "  texture: \\\"/assets/sprites.atlas\\\"\\n"
+  "}\\n"
+  "\"\n"
+  "  position {\n"
+  "    x: 6.0\n"
+  "    y: -30.0\n"
+  "    z: 0.1\n"
+  "  }\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"glow\"\n"
+  "  type: \"sprite\"\n"
+  "  data: \"default_animation: \\\"anim\\\"\\n"
+  "material: \\\"/builtins/materials/sprite.material\\\"\\n"
+  "size {\\n"
+  "  x: 600.0\\n"
+  "  y: 600.0\\n"
+  "}\\n"
+  "size_mode: SIZE_MODE_MANUAL\\n"
+  "textures {\\n"
+  "  sampler: \\\"texture_sampler\\\"\\n"
+  "  texture: \\\"/builtins/graphics/particle_blob.tilesource\\\"\\n"
+  "}\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    z: 0.5
+  }
+}

+ 12 - 0
material/repeating_background/example/repeating_background.fp

@@ -0,0 +1,12 @@
+#version 140
+
+in mediump vec2 var_texcoord0;
+
+out vec4 out_fragColor;
+
+uniform mediump sampler2D texture0;
+
+void main()
+{
+    out_fragColor = texture(texture0, var_texcoord0);
+}

+ 31 - 0
material/repeating_background/example/repeating_background.material

@@ -0,0 +1,31 @@
+name: "repeating_background"
+tags: "model"
+vertex_program: "/example/repeating_background.vp"
+fragment_program: "/example/repeating_background.fp"
+vertex_space: VERTEX_SPACE_WORLD
+vertex_constants {
+  name: "mtx_worldview"
+  type: CONSTANT_TYPE_WORLDVIEW
+}
+vertex_constants {
+  name: "mtx_proj"
+  type: CONSTANT_TYPE_PROJECTION
+}
+vertex_constants {
+  name: "uv_params"
+  type: CONSTANT_TYPE_USER
+  value {
+    x: 1.0
+    y: 1.0
+    z: 0.0
+    w: 0.0
+  }
+}
+samplers {
+  name: "texture0"
+  wrap_u: WRAP_MODE_REPEAT
+  wrap_v: WRAP_MODE_REPEAT
+  filter_min: FILTER_MODE_MIN_LINEAR
+  filter_mag: FILTER_MODE_MAG_LINEAR
+  max_anisotropy: 0.0
+}

+ 51 - 0
material/repeating_background/example/repeating_background.script

@@ -0,0 +1,51 @@
+-- Size of a single tile in pixels
+go.property("tile_size", 128)
+-- Scroll speed vector (x, y, z) in pixels per second
+go.property("scroll_speed", vmath.vector3(50, 0, 0))
+
+-- Applies layout based on current window size
+-- Scales the game object to fill the entire window and calculates UV scale
+local function apply_layout(self)
+	local width, height = window.get_size()
+	-- Scale the game object to match window dimensions
+	go.set(".", "scale", vmath.vector3(width, height, 1))
+
+	-- Calculate how many tiles fit in the window (for UV tiling)
+	self.uv_scale = vmath.vector3(width / self.tile_size, height / self.tile_size, 0)
+
+	-- Send UV parameters to the shader: scale (x, y) and offset (z, w)
+	local uv_params = vmath.vector4(self.uv_scale.x, self.uv_scale.y, self.offset.x, self.offset.y)
+	go.set("#model", "uv_params", uv_params)
+end
+
+-- Updates UV offset for scrolling animation
+-- Moves the texture offset based on scroll speed and wraps it using modulo
+local function update_uv_params(self, dt)
+	-- Calculate offset delta in tile units (0-1 range)
+	local delta = self.scroll_speed * dt / self.tile_size
+	-- Update offset (subtract because we want to scroll in the direction of scroll_speed)
+	self.offset = self.offset - delta
+	-- Wrap offset to 0-1 range to create seamless repeating
+	self.offset.x = self.offset.x % 1
+	self.offset.y = self.offset.y % 1
+
+	-- Send updated UV parameters to the shader
+	local uv_params = vmath.vector4(self.uv_scale.x, self.uv_scale.y, self.offset.x, self.offset.y)
+	go.set("#model", "uv_params", uv_params)
+end
+
+-- Initialize the script
+-- Sets up the initial UV offset to zero
+function init(self)
+	self.offset = vmath.vector3(0)
+end
+
+function final(self)
+end
+
+-- Update function called every frame
+-- Applies layout and updates UV parameters for scrolling
+function update(self, dt)
+	apply_layout(self)
+	update_uv_params(self, dt)
+end

+ 22 - 0
material/repeating_background/example/repeating_background.vp

@@ -0,0 +1,22 @@
+#version 140
+
+in vec4 position;
+in vec2 texcoord0;
+uniform vp_uniforms
+{
+    mat4 mtx_worldview;
+    mat4 mtx_proj;
+    vec4 uv_params;
+};
+
+out vec2 var_texcoord0;
+
+void main()
+{
+    // uv_params.x = repeat scale on U axis (tiles across width)
+    // uv_params.y = repeat scale on V axis (tiles across height)
+    // uv_params.z = scroll offset on U axis (normalized 0..1)
+    // uv_params.w = scroll offset on V axis (normalized 0..1)
+    var_texcoord0 = texcoord0 * uv_params.xy + uv_params.zw;
+    gl_Position = mtx_proj * mtx_worldview * vec4(position.xyz, 1.0);
+}

+ 47 - 0
material/repeating_background/game.project

@@ -0,0 +1,47 @@
+[project]
+title = material-repeating_background
+version = 1.0.0
+
+[bootstrap]
+main_collection = /example/repeating_background.collectionc
+
+[input]
+game_binding = /builtins/input/all.input_bindingc
+repeat_interval = 0.05
+
+[display]
+width = 720
+height = 720
+high_dpi = 1
+
+[script]
+shared_state = 1
+
+[label]
+subpixels = 1
+
+[sprite]
+subpixels = 1
+
+[windows]
+iap_provider = 
+
+[android]
+package = com.defold.examples
+
+[ios]
+bundle_identifier = com.defold.examples
+
+[osx]
+bundle_identifier = com.defold.examples
+
+[html5]
+show_fullscreen_button = 0
+show_made_with_defold = 0
+scale_mode = no_scale
+heap_size = 64
+
+[render]
+clear_color_blue = 0.9
+clear_color_green = 0.9
+clear_color_red = 0.9

BIN
material/repeating_background/thumbnail.png