Parallax Scrolling experiment

Khao

Member
I came up with a "different" way of approaching parallax scrolling this morning, one that deals with individual objects rather than backgrounds and layers. Basically, each object calculates the position it should draw itself by checking its distance to the center of the screen and its direction. Then it draws itself in that direction while multiplying the distance by a 'z' variable.

The end result is this:

Looks... a heck of a lot better than I expected to tell the truth! I can already imagine using it for a game, combining small objects with large scenery to create something that almost looks 3D, yet runs completely in 2D. Would be brilliant for a platformer with more complex backgrounds.

The one thing I'm worried about, is performance. With something around 80 objects that are doing absolutely nothing beyond calculating the spot they should be drawn at, my framerate goes from 1900 to less than 1000, which would mean it's... probably not feasible to do in a real game for every background object.

But that's where I think I could use some help. Do you guys think there's a more effective way of doing the same thing?

This is the code I'm currently using:
Code:
draw_x=x+(point_distance(view_xview+640,view_yview+360,x,y)*-z)*cos(degtorad(point_direction(view_xview+640,view_yview+360,x,y)))
draw_y=y-(point_distance(view_xview+640,view_yview+360,x,y)*-z)*sin(degtorad(point_direction(view_xview+640,view_yview+360,x,y)))
draw_xscale=1-z
draw_yscale=1-z

draw_sprite_ext(sprite_index,0,draw_x,draw_y,draw_xscale,draw_yscale,image_angle,image_blend,image_alpha)
'z' is a variable I defined previously, and the one I change to adjust the position the object is drawn at. Depth is also set to z every step of course, so objects that are further back are drawn under the rest. I could easily just eliminate z entirely and use depth instead of it, now that I think about it.

Any ideas?
 
L

Lotias

Guest
I came up with a "different" way of approaching parallax scrolling this morning, one that deals with individual objects rather than backgrounds and layers. Basically, each object calculates the position it should draw itself by checking its distance to the center of the screen and its direction. Then it draws itself in that direction while multiplying the distance by a 'z' variable.

The end result is this:

Looks... a heck of a lot better than I expected to tell the truth! I can already imagine using it for a game, combining small objects with large scenery to create something that almost looks 3D, yet runs completely in 2D (would be brilliant for a platformer).

The one thing I'm worried about, is performance. With something around 80 objects that are doing absolutely nothing beyond calculating the spot they should be drawn at, my framerate goes from 1900 to less than 1000, which would mean it's... probably not feasible to do in a real game for every background object.

But that's where I think I could use some help. Do you guys think there's a more effective way of doing the same thing?

This is the code I'm currently using:
Code:
draw_x=x+(point_distance(view_xview+640,view_yview+360,x,y)*-z)*cos(degtorad(point_direction(view_xview+640,view_yview+360,x,y)))
draw_y=y-(point_distance(view_xview+640,view_yview+360,x,y)*-z)*sin(degtorad(point_direction(view_xview+640,view_yview+360,x,y)))
draw_xscale=1-z
draw_yscale=1-z

draw_sprite_ext(sprite_index,0,draw_x,draw_y,draw_xscale,draw_yscale,image_angle,image_blend,image_alpha)
'z' is a variable I defined previously, and the one I change to adjust the position the object is drawn at. Depth is also set to z every step of course, so objects that are further back are drawn under the rest. I could easily just eliminate z entirely and use depth instead of it, now that I think about it.

Any ideas?
Try using tiles, and shift the entire layer the tiles are on when the view moves.
 

Khao

Member
Wouldn't quite work the same way, I want individual objects drawn at individual depths, not layers. The method I'm using also scales the object so they appear further back, and also allows background objects to move. Theoretically, I could even make them move through z, making things look as if they're moving away or towards the screen. Shifting tile layers wouldn't allow any of those things.
 

TLH14

Member
Wouldn't quite work the same way...
Could you go into more detail on this? I've been trying to achieve similar results to this using tiles, and I would like to know, before going too much farther, just how this approach would be limited in comparison to yours.
 
L

Lotias

Guest
Wouldn't quite work the same way, I want individual objects drawn at individual depths, not layers. The method I'm using also scales the object so they appear further back, and also allows background objects to move. Theoretically, I could even make them move through z, making things look as if they're moving away or towards the screen. Shifting tile layers wouldn't allow any of those things.
Then try a combination of the two, using tiles for things that don't need to get closer or farther away and aren't animated. Tiles are significantly less resource intensive and support xscale/yscale. The depths ARE layers.
 

Khao

Member
Could you go into more detail on this? I've been trying to achieve similar results to this using tiles, and I would like to know, before going too much farther, just how this approach would be limited in comparison to yours.
Not sure how else to explain it, there's just potentially more things you can do with this. For example, if you set your tiles to move when the view shifts around, that'd mean that the parallax wouldn't work when the view is static, but the tiles are moving.

I'm pretty sure that the thing I'm doing in the video up there, placing objects in the room and having them all with the correct scaling and positioning in real time, wouldn't be possible with tiles.
Then try a combination of the two, using tiles for things that don't need to get closer or farther away and aren't animated. Tiles are significantly less resource intensive and support xscale/yscale. The depths ARE layers.
That would probably be for the best. I could use tiles for static objects, and this other method for everything else. Also note that when I said moving objects, I didn't mean animated, I meant actually moving through X and Y. This systems would automatically make them appear as if they're moving at a slower pace if they're further away, even if every object is actually moving at the same speed in the same direction. Doesn't really change anything though, as static background objects wouldn't be doing that anyway.
What is z defined as? I wanna mess with your system!
z starts at 0. Positive goes into the background, negative goes towards the screen. It's kind of directly tied to the scaling of objects, but I want to change this for a few reasons (mainly because as the scaling approaches 0, the difference between each object's scaling is more and more extreme, which looks extremely distorted, dunno if I'm explaining right, but it's something you'll notice if you start messing around with it).
 
Last edited:

Khao

Member
Not at all, it's not even much beyond what I posted alread.

Create Event:
Code:
z=0;
placed=0;
Step Event:
Code:
if placed==0
{
    z+=(mouse_wheel_up()-mouse_wheel_down())*0.01;
  
    x=mouse_x;
    y=mouse_y;
  
    view_xview+=(keyboard_check(vk_right)-keyboard_check(vk_left))*5;
    view_yview+=(keyboard_check(vk_down)-keyboard_check(vk_up))*5;
}
depth=z;

draw_x=x+(point_distance(view_xview+640,view_yview+360,x,y)*-z)*cos(degtorad(point_direction(view_xview+640,view_yview+360,x,y)));
draw_y=y-(point_distance(view_xview+640,view_yview+360,x,y)*-z)*sin(degtorad(point_direction(view_xview+640,view_yview+360,x,y)));
draw_xscale=1-z;
draw_yscale=1-z;
Mouse Event for Glob Left Pressed:
Code:
if placed==0
{
    a=instance_create(x,y,parallax);
    a.z=z;
    a.placed=1;
}
Draw Event:
Code:
draw_sprite_ext(sprite_index,0,draw_x,draw_y,draw_xscale,draw_yscale,image_angle,image_blend,image_alpha);
This is all made to work on a 1280x720 view by the way. If you want it to work correctly with a different resolution, you're gonna have to replace all those 640s and 360s.
 

Zerb Games

Member
For the Z function do something like z=lengthdir_x(200,dir) see what that looks like. I've always found that little formula there to make things interesting.
 
Don't use point_distance and trig. I do the same thing for my game but you don't need to worry about both x and y offset when getting the x position and same for the y position.
Code:
draw_x=x+(point_distance(view_xview+640,view_yview+360,x,y)*-z)*cos(degtorad(point_direction(view_xview+640,view_yview+360,x,y)));
draw_y=y-(point_distance(view_xview+640,view_yview+360,x,y)*-z)*sin(degtorad(point_direction(view_xview+640,view_yview+360,x,y)));
Code:
draw_x=x+(view_xview+640-x)*z;
draw_y=y+(view_yview+360-y)*z;
That should help with speed.
 

Khao

Member
Don't use point_distance and trig. I do the same thing for my game but you don't need to worry about both x and y offset when getting the x position and same for the y position.
Code:
draw_x=x+(point_distance(view_xview+640,view_yview+360,x,y)*-z)*cos(degtorad(point_direction(view_xview+640,view_yview+360,x,y)));
draw_y=y-(point_distance(view_xview+640,view_yview+360,x,y)*-z)*sin(degtorad(point_direction(view_xview+640,view_yview+360,x,y)));
Code:
draw_x=x+(view_xview+640-x)*z;
draw_y=y+(view_yview+360-y)*z;
That should help with speed.
Okay, you're a life saver! I was way overcomplicating things, that's way more simple and effective!

I also found that dividing instead of multiplying was a lot more accurate for the scaling of the sprites. Now it almost looks like an actual 3D projection.

Did a test with some backgrounds taken from the Rayman games, and I'm kind of amazed at the results.

 
Top