How to get scrolling tiled background image?

It’s because of the way Defold makes atlases and uvs. The width and height don’t have to be even. You could have 256x512 and it would work. You could also make some blank space in your image such as having top 50px of your image be transparent, but important thing is width and height both be powers of 2 otherwise that method won’t work right.

1 Like

Let’s look at a real-world example:
Jesse Munguia’s Jungle Asset Pack
Layers are 384x216.
I don’t see where I should put transparent.
plx-5

Neither of TheKing’s methods are bad or a workaround, they are -the- ways to do it. Godot and Gamemaker have these options built-in…but how do you think they are doing it? It is either in a shader, or by drawing the same image repeatedly and offsetting them.

In fact you can look at Godot’s code (background, layer) and see. Basically all the ParallaxBackground node does is offset all its child layers whenever the camera moves. The mirroring feature looks like a shader thing, presumably the same as Pkeod’s method (but with all the work done for you already).


Anyway. So are you using premade assets you find? Can you simply scale them to a power of 2 size? I imagine it’s hard to find assets that match the exact size you want anyway. It should also be possible to wrap the UVs with a non-power of 2 size, provided you input the image size into your shader. I seem to recall there was a way to calculate the remapped UVs inside an atlas (see this thread), but I don’t remember how much of pain it was to do. Defold definitely could be better in this area.

3 Likes

The easiest way is to have each layer of the parallax scroll in a separate game object each containing enough images to cover your screen and then move them with a speed dependent upon the z value of the game object. Easy and not a workaround, but you have to do some work yourself.

2 Likes

This is what I’ve done a couple times. Works perfectly well. Calculate it out before hand, and pretty simple. If you have a tile-able (repeating) image and set it to update position at a percentage of camera position, basically making it scroll slower than your mid-ground tilemap, you will know based on total level size and image size how many sprites you need, OR, in code spawn/delete them end-to-end as needed, so there are never more than 2 (if the image is full screen width).

1 Like

At the moment separate object route for me would be easiest, but I’d like to get shader working which is not cooperating with me.
this is supposed to be 10x tiling like @Pkeod showed in example (at the moment moving offset):


they are not 1:1 blocks and they got weird pading.

I’m creating parallax background as a collection with:

  • background_controller: with a bg_controller.script
  • number of other bg objects with: sprites and bg.script:

image

bg_controller.script:

go.property("move_vertical", false)

function init(self)
	BG_CTRL_URL = msg.url()
	self.bgs = {}
	self.speeds = {}
	self.pos_y = go.get_position().y
end

function update(self, dt)
	local cam_x = CAM_POS.x
	local cam_y = CAM_POS.y
	for _,bg in ipairs(self.bgs) do
		go.set(bg.id, "position.x", (1-(0.1*bg.no))*cam_x)
		if self.move_vertical then go.set(bg.id, "position.y", self.pos_y + 0.5*(1-(0.05*bg.no))*cam_y) end
		if bg.speed ~= 0 then
			local pos = go.get_position(bg.id).x
			if self.speeds[bg.no] == nil then self.speeds[bg.no] = 0 end
			self.speeds[bg.no] = self.speeds[bg.no] + bg.speed
			go.set(bg.id, "position.x", pos + self.speeds[bg.no])
		end
	end
end

function on_message(self, message_id, message, sender)
	if message_id == m.REGISTER then
		table.insert(self.bgs, {id = message.id, no = message.no, speed = message.speed} )
	end
end

function final(self)
	BG_CTRL_URL = nil
end

You can check move_vertical property, if you want also vertical parallax effect.

bg.script:

go.property("no", 0)
go.property("speed", 0)

local m = require "messages" -- module with available messages, for convenience and pre-hashing

function init(self)
	assert(self.no ~= 0, "Provide no of bg")
	if self.no < 10 then 
		timer.delay(0.1, false, function()
			msg.post(BG_CTRL_URL, m.REGISTER, {id = go.get_id(), no = self.no, speed = self.speed} )
		end)
	end
end

where m.REGISTER is:

hash("register")

I do specify in each bg properties:

  • No - is my arbitrary number of layer - closer to 0 are far layers, and closer to 10 are slower, closer layers (10 is a static layer - not moving)
  • Speed - it is used for some constantly floating layers like clouds

All you need to do for this collection to work is to constantly save camera’s (or just hero’s) position in this global variable - CAM_POS

You can add as many different layers as you want :wink:

3 Likes

Since several people told me that Defold get imprecise positioning when moving the camera far from 0,0, so I’m playing it safe and move world instead (just like in my example of TrueTileCollision). I have a bit simpler solution in my head for moving multiple sprites for a background but I wanted to get a more elegant approach for that.

2 Likes

This is not unique to Defold it’s every game engine because of the way computer numbers work. Most major games move the world around the player to solve this common problem.

1 Like

It doesn’t change the fact that using Defold developer has to do the managing that other engines do for developer (same for tiled background).

Still, they are things you learn to do with any engine. Even Unity or Unreal.

A well designed builtin tiled component would be cool and useful. Not sure if there is a feature request for that.

1 Like

This is no longer as much of a problem as it used to be. In the last release (1.2.156) we did the following:

  • #3128 - Changed : Changed precision for the position value in shaders to highp .

This will very likely remove the problem for most games, except maybe infinite runners and huge open worlds.

4 Likes

Have you measured distance from world origin point when first deformation happen?

1 Like

It is up to each developer and application.

A good suggestions is to read up on what floating point precision actually is and how it manifests itself. It’s a vital thing that is recurring in all engines.

5 Likes

And also this post OpenGL ES shader precision modifiers (lowp, mediump, highp)

2 Likes

Dear Pawel,

please help me! I don’t understand this code: “m.REGISTER is hash(“register”)”…how and where can I declarate this “m”?
I’m sorry but I’m new in Defold and Lua!!

This relates to Lua modules:

1 Like

Thanks for help!! So I understand at now…modules…I forgot… :wink:

1 Like

Yes, precisely - whenever I am using a hash - I’m prehashing it and storing it in a Lua module. I agree that “m” is too enigmatic, but for me it means “messages” and I use it all the time - I edited the above post to explain it a bit there.

You can name your module however you like - because you are creating a Lua table that you are assigning to a variable:

local m = require "messages"

and in some other place:

local messages = require "messages"

I advice though to use some senseful naming convention and stick to it. I use one letter like this “m” only for modules I use everywhere and all the time - like pre-hashed messages. I used also “d” for “dispatcher”, but after I tinkered with it a lot and named it strangely “pigeon” - I stick to this name :smiley:

Btw, nowadays, I use a module by Sergey Lerg for prehashing (and I also use enigmatic name “H” for it always :sweat_smile:)

Hi Pawel,
many thanks for reply and help!!