• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Tiling backgrounds infinitely [SOLVED]

Niften

Member
Hi,

I think I have everything set up right but the backgrounds don't tile infinitely.
Code:
/***************************************************
  Usage :   draw_background_tiled_horizontal(back,x,y)
  Arguments :   argument0   ==  background
                argument1   ==  x
                argument2   ==  y
                argument3   ==  alpha
                argument4   ==  blend
  Event :   draw event
 ***************************************************/
 ///@param bg
 ///@param x
 ///@param y
 ///@param alpha
 ///@param blend
 
 var back,width,xx,yy,left,right,i;
 
 back=argument0;
 width=sprite_get_width(back);
 xx=argument1;
 yy=argument2;
 alpha=argument3;
 left=-1;
 right=__view_get( e__VW.XView, view_current )/width+__view_get( e__VW.WView, view_current )/width+1;
 if (view_enabled)
 {
    for (i=left; i<right; i+=1)
    {
        //draw_background(back,xx mod width+width*i,yy);
        draw_sprite_ext(back,0,xx mod width+width*i,yy,1,1,0,argument4,alpha);
    };
 }
 else
 {
    for (i=-1; i<room_width/width+1; i+=1)
    {
        //draw_background(back,xx mod width+width*i,yy);
        draw_sprite_ext(back,0,xx mod width+width*i,yy,1,1,0,argument4,alpha);
    };
 };
Code:
draw_background_tiled_horizontal(spr_clouds,obj_camera.x/1.1+xoff,obj_camera.y-50,1,noone);
draw_background_tiled_horizontal(spr_hills,obj_camera.x/1.12+200,obj_camera.y-50,1,make_color_rgb(191,191,191));
draw_background_tiled_horizontal(spr_hills,obj_camera.x/1.2,obj_camera.y-50,1,noone);
draw_background_tiled_horizontal(spr_tree_silhouette,obj_camera.x/1.3+200,obj_camera.y-50,1,make_color_rgb(191,191,191));
draw_background_tiled_horizontal(spr_tree_bg,obj_camera.x/1.35,obj_camera.y-50,1,noone);
draw_background_tiled_horizontal(spr_tree_bg2,obj_camera.x/1.4+300,obj_camera.y-50,1,noone);
 

CMAllen

Member
At a guess, it's not being fed the proper spatial coordinates to repeat additional draw requests. I'd start by troubleshooting this line, since it's the most likely the cause of the failed for() loop you use to tile:
Code:
right=__view_get( e__VW.XView, view_current )/width+__view_get( e__VW.WView, view_current )/width+1;
 
It looks like you're trying to do parallax scrolling. Instead of drawing the background, why not make it tiled in your room settings and simply change the background's x position (or layer x position if using GMS2)?
 

Niften

Member
Didn't even know you could do that, but if it's through the room's properties it seems like it would be way less flexible.
 

TheouAegis

Member
Before your for loops are run, put

show_debug_message(right);

What are the values being output?

...But it's a market asset that it doesn't look like you edited at all, so it should be fine... Hm..
 

toxigames

Member
This line seems unoptimized (assuming I didn't make a mistake in the calculations):
right=__view_get( e__VW.XView, view_current )/width+__view_get( e__VW.WView, view_current )/width+1;

Say for example your xview were 3000, the width of the sprite 512, the width of the view 1920 then the calculation would look like this:

right = (3000/512) + (1920/512) + 1;
right = 5.859375 + 3.75 + 1;
right = 10.609375;

Here you would be repeating drawing the background 10 times when xview is 3000. Drawing the background that many times is unnecessary, but it only gets worse the higher your xview is. I don't know how large you plan to make your levels, but say the levels were to be 30000px wide, that would make this script repeat the for loop 63(!) times if your view were 1920 wide and the background sprite has a width of 512. Smaller backgrounds would make it worse of course.

Anyway you tested it and right for some reason always has the value 1... now that's weird because from the calculation above it's obvious that right should always be more than 1. Unless xview were negative of course, but then it should very rarely be exactly 1 and it would change when moving the xview.

For example:
right = (-2000/512) + (1920/512) + 1;
right = 0.84375;

If on the other hand xview and wview were always 0 then right would indeed be always exactly 1 (because 1 = (0/width) + (0/width) + 1). However how/why would xview and wview be 0?
Could you perhaps output all the values in the calculation of right to see the actual reason right seems to be always 1?:

xview = __view_get( e__VW.XView, view_current );
wview = __view_get( e__VW.WView, view_current );
show_debug_message( "xview: " + string(xview) + " wview: " + string(wview) + " width " + string(width) + " view_enabled: " + string(view_enabled));

And perhaps just for the sake of it use parenthesis to enclose the calculations just to be sure to avoid accidentally adding when you want to divide and vice versa:
right=( __view_get( e__VW.XView, view_current ) / width ) + ( __view_get( e__VW.WView, view_current ) / width ) + 1;

Another reason for the bug could be that you do not have views enabled AND that yuo have small room width. That would make the for loop in the else part iterate very few times, which I guess would look like in your gif animation?
 
Last edited:

Niften

Member
Thanks for the response! Yeah, the rooms are actually tiny. Maybe that's one of the reasons... I do have views enabled though. The room is 64x64. Unfortunately changing the room size to 1920x1080 didn't affect anything. Also, the return data:
Code:
xview = 0
wview = 0
width = 592
view_enabled = 1
 

Niften

Member
Okay, so for some reason there's something wrong getting the xview and wview. So, what I did was I used the camera's x:
Code:
xview = obj_camera.x-CAMERA_WIDTH/2;
wview = obj_camera.x+CAMERA_WIDTH/2;
This fixed it. I'm still curious as to why the issue happened, though.
 

Niften

Member
So it's not fixed I guess. Works fine going to the right but they disappear when I go to the left. Seems this is because 'right' is outputting negative values. I think the script was made for it to only go right... I'm thinking we add a variable similar to 'right' to the script and replace it with left = -1.
 

toxigames

Member
Okay, so for some reason there's something wrong getting the xview and wview. So, what I did was I used the camera's x:
Code:
xview = obj_camera.x-CAMERA_WIDTH/2;
wview = obj_camera.x+CAMERA_WIDTH/2;
This fixed it. I'm still curious as to why the issue happened, though.
Im glad that you fixed this. Well it must has to do with the __view_get function, since obviously it didn't actually return the correct xview and wview values. I have no idea what goes on in that function, I guess it's part of the script asset that you bought...?

So it's not fixed I guess. Works fine going to the right but they disappear when I go to the left. Seems this is because 'right' is outputting negative values. I think the script was made for it to only go right... I'm thinking we add a variable similar to 'right' to the script and replace it with left = -1.
Yes, as I found out and mentioned in my (rather long) post there is no support for xview being negative. So if you want to go left, then you better start with the player being position at a high x value in the room. For example place the player at x = 50000 (or more?). That way you will have a long way to go to to the left before xview becomes negative where the tiling backgrounds script doesn't work anymore.
 
T

Trasoside

Guest
hi sir Niften, I was a lil bored so I wrote a script that I believe will solve your problem:
Code:
/***************************************************
  Usage :   draw_background_tiled_horizontal(back,x,y,alpha,blend)
  Arguments :   argument0   ==  background
                argument1   ==  x
                argument2   ==  y
                argument3   ==  alpha
                argument4   ==  blend
  Event :   draw event
 ***************************************************/
 ///@param bg
 ///@param x
 ///@param y
 ///@param alpha
 ///@param blend
 
var back = argument0,
    width = sprite_get_width(back),
    xx = argument1 mod width,
    yy = argument2,
    alpha = argument3,
    blend = argument4,
    camera = camera_get_active(),
    cam_x = camera_get_view_x(camera),
    left = floor(cam_x/width-1)*width+xx,
    right = ceil((cam_x+camera_get_view_width(camera))/width)*width;
for(var i = left; i < right; i+=width)
{
    draw_sprite_ext(back, 0, i, yy, 1, 1, 0, blend, alpha);
}
  • it should work with/without views.
  • supports negative positions
  • you also forgot to set one of your variables as var
and here's another bonus script:
Code:
/***************************************************
  Usage :   draw_background_tiled(back,x,y, alpha, blend, horizontal, vertical)
  Arguments :   argument0   ==  background
                argument1   ==  x
                argument2   ==  y
                argument3   ==  alpha
                argument4   ==  blend
                argument5   ==  horizontal
                argument6   ==  vertical
  Event :   draw event
 ***************************************************/
 ///@param bg
 ///@param x
 ///@param y
 ///@param alpha
 ///@param blend
 ///@param horizontal
 ///@param vertical

var back = argument0,
    width = sprite_get_width(back),
    height = sprite_get_height(back),
    xx = argument1,
    yy = argument2,
    alpha = argument3,
    blend = argument4,
    horizontal = argument5,
    vertical = argument6,
    camera = camera_get_active(),
    left = xx,
    right = left+1,
    top = yy,
    bottom = top+1;
 
    if(horizontal)
    {
        xx = xx mod width;
        var cam_x = camera_get_view_x(camera);
        left = floor(cam_x/width-1)*width+xx;
        right = ceil((cam_x+camera_get_view_width(camera))/width)*width;
    }
    if(vertical)
    {
        yy = yy mod height;
        var cam_y = camera_get_view_y(camera);
        top = floor(cam_y/height-1)*height+yy;
        bottom = ceil((cam_y+camera_get_view_height(camera))/height)*height;
    }
for(var iy = top; iy < bottom; iy += height)
{
    for(var ix = left; ix < right; ix += width)
    {
        draw_sprite_ext(back, 0, ix, iy, 1, 1, 0, blend, alpha);
    }
}
  • here you can tell the script whether you want it to tile the background horizontaly/verticaly or both ;)
Code:
draw_text(camera_get_view_x(camera), camera_get_view_y(camera),
            "left: "+string(left)+
            "\nright: "+string(right)+
            "\ntop: "+string(top)+
            "\nbottom: "+string(bottom));

hope it helps, it should be super efficient since it draws only backgrounds that are inside the view
 
Last edited by a moderator:

Niften

Member
Hi!

Thank you so much for all the time you put into this. However, I used the script that you posted and I experienced the same glitch.
 

Niften

Member
Putting the player at a very high X value isn't a very good workaround because the worlds are generated infinitely. 50,000 pixels isn't very much in the context of my game; that's probably the width of a short exploration trip. The __view_get function is a compatibility function generated by GameMaker 2, so I would THINK it works properly.
 
T

Trasoside

Guest
Hi!

Thank you so much for all the time you put into this. However, I used the script that you posted and I experienced the same glitch.
What's the glitch you get this time? cuz I've tested it and it works on negative x/y and on very high x/y values, do you mind sharing a gmz of your project? (or only the part that is glitched)
 

Niften

Member
It's the same glitch outlined in the GIF. I think it has something to do with the camera object. It doesn't glitch at high x/y or negative x/y values, it doesn't repeat more than twice.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
What is the __view_get() script?
Compatibility script from GMS2. It is created automatically for you on importing a 1.4 project. Tbh, you shouldn't be editing these and if you need something other than the functionality they offer, you should remove them and use the new GMS2 functions instead, otherwise it's a nightmare to debug...
 

toxigames

Member
It's the same glitch outlined in the GIF. I think it has something to do with the camera object. It doesn't glitch at high x/y or negative x/y values, it doesn't repeat more than twice.
Trasosides script works, however it doesn't seem to include parallax. From your screenshot in the OP it can be seen that you are trying to do parallax effect, so I believe that Trasosides script would have to be expanded upon to implement a parallax solution.
 

Niften

Member
The way that I incorporate the scripts turn it into parallax. Also, please read my posts before asking questions :p
 

TheouAegis

Member
Compatibility script from GMS2. It is created automatically for you on importing a 1.4 project. Tbh, you shouldn't be editing these and if you need something other than the functionality they offer, you should remove them and use the new GMS2 functions instead, otherwise it's a nightmare to debug...
I wasn't going to edit it, but I couldn't make sense of the algorithm he was using because it was based on the output of that function.

And yeah, the marketplace script wasn't optimized either. The line everyone keeps looking at for example could be shortened to

right=(__view_get( e__VW.XView, view_current )+__view_get( e__VW.WView, view_current ))/width+1;

And no that's not a nitpick, it's an actual boost in speed.


Oh Niften, don't use noone when you don't want to use a color blend. Use -1. noone is off-white so it will tint your colors ever so slightly cyan; -1 is the value of pure white.
 

toxigames

Member
The way that I incorporate the scripts turn it into parallax.
You're right, sorry. Of course you divide by some value (like 1.1) and Trasoside did make the script so that is takes that into account. I just tested the script again and it does work perfectly for me now, both positive and negative and large positions. No glitches. I think you may have some code unknown to us that messes with the values somehow, perhaps that camera object as you mention? Can you post the code from your camera object?

Also, please read my posts before asking questions :p
Hey I read all your posts, but Im not perfect. Im just trying to help you :)
 
Last edited:
T

Trasoside

Guest
Ok, check this out
(https://www.dropbox.com/s/xzdyp3d2amo2753/TrasosideParallaxExample.yyz?dl=0)

This yyz is a project I made that has 2 rooms
first room uses the script I mentioned earlier to do parallax backgrounds
second room uses GM's background layers to do the exact same thing,

some points to note:
  • move player holding mouse left button
  • change room using any keyboard key
  • I just noticed my script doesn't support sprites with origin, when you use sprites with origin with my script it will mess up the right/left calculations
  • GM's background layers doesn't support them as well, when you use sprites with origin with background layers the origin will always be at top left(0,0)
  • It could be written better but I wanted to show the main idea..

pick whatever you prefer
hope it helps.
 
Last edited by a moderator:

Niften

Member
Here's the obj_camera code.
Code:
camera = camera_create();

var vm = matrix_build_lookat(x,y,-10,x,y,0,0,1,0);
var pm = matrix_build_projection_ortho(CAMERA_WIDTH/2,CAMERA_HEIGHT/2,1,10000);

camera_set_view_mat(camera,vm);
camera_set_proj_mat(camera,pm);

view_camera[0] = camera;

follow = obj_player;
xTo = x;
yTo = y;

global.camera_shake = false;
global.shake_intensity = 5;

view_x = camera_get_view_x(camera);
view_y = camera_get_view_y(camera);
Code:
x += (xTo - x)/12;
y += (yTo - y)/12;

if global.camera_shake = true {
    x += irandom_range(global.shake_intensity,-global.shake_intensity);
    y += irandom_range(global.shake_intensity,-global.shake_intensity);
}

//x = xTo;
//y = yTo;

if (follow != noone) {
    xTo = follow.x;
    yTo = follow.y;
}

var vm = matrix_build_lookat(x,y,-10,x,y,0,0,1,0);
camera_set_view_mat(camera,vm);
It also seems that the background had an origin... thank you for pointing that out. Changing it to zero did nothing however. I played the demo you sent and it worked perfectly - thanks for putting it together! I'm really confused about what could be causing this problem... Thank you for pointing out that noone is not the value of white, I'll keep that in mind for future stuff.
 
T

Trasoside

Guest
Here's the obj_camera code.
Code:
camera = camera_create();

var vm = matrix_build_lookat(x,y,-10,x,y,0,0,1,0);
var pm = matrix_build_projection_ortho(CAMERA_WIDTH/2,CAMERA_HEIGHT/2,1,10000);

camera_set_view_mat(camera,vm);
camera_set_proj_mat(camera,pm);

view_camera[0] = camera;

follow = obj_player;
xTo = x;
yTo = y;

global.camera_shake = false;
global.shake_intensity = 5;

view_x = camera_get_view_x(camera);
view_y = camera_get_view_y(camera);
Code:
x += (xTo - x)/12;
y += (yTo - y)/12;

if global.camera_shake = true {
    x += irandom_range(global.shake_intensity,-global.shake_intensity);
    y += irandom_range(global.shake_intensity,-global.shake_intensity);
}

//x = xTo;
//y = yTo;

if (follow != noone) {
    xTo = follow.x;
    yTo = follow.y;
}

var vm = matrix_build_lookat(x,y,-10,x,y,0,0,1,0);
camera_set_view_mat(camera,vm);
It also seems that the background had an origin... thank you for pointing that out. Changing it to zero did nothing however. I played the demo you sent and it worked perfectly - thanks for putting it together! I'm really confused about what could be causing this problem... Thank you for pointing out that noone is not the value of white, I'll keep that in mind for future stuff.
Maybe try
camera_set_view_pos
instead of
camera_set_view_mat?
 
T

Trasoside

Guest
I messaged you a download.
Alright, I finally figured out what was your miserable problem.
you used matrix scripts to scale and move the camera around, from researching around, I found that these functions are mainly for 3D view purposes.
when you used camera_get_view_x/y it always returned the same, because you never moved the camera, only played with matrix values.
also, camera_get_view_width/height were incorrect because you zoomed the camera.
SO, I tried replacing the matrix functions with simple view functions and now it works.
to zoom use :
camera_set_view_size/view_wport[0]/view_hport[0]
to move the camera around:
camera_set_view_pos

the code looks something like this now:
create:
Code:
camera = view_camera[0]; // you already allowed this camera in the room as view 0
follow = obj_player;
xTo = x;
yTo = y;

w = camera_get_view_width(camera)/2; //play with these values to scale (you might want to try view_wport[0] as well)
h = camera_get_view_height(camera)/2; //play with these values to scale (you might want to try view_hport[0] as well)
camera_set_view_size(camera, w, h);

global.camera_shake = false;
global.shake_intensity = 5;

view_x = camera_get_view_x(camera);
view_y = camera_get_view_y(camera);
step:
Code:
x += (xTo - x)/12;
y += (yTo - y)/12;

if global.camera_shake = true {
    x += irandom_range(global.shake_intensity,-global.shake_intensity);
    y += irandom_range(global.shake_intensity,-global.shake_intensity);
}
if (follow != noone) {
    xTo = follow.x;
    yTo = follow.y;
}
camera_set_view_pos(camera,
    x-camera_get_view_width(camera)/2,
    y-camera_get_view_height(camera)/2);
lemme know if this is what you were looking for
 

Niften

Member
It is what I was looking for! Thank you so much for all of the time you put into this, it's finally fixed :D
 
Top