Help about looping background objects [Solved]

Started by rmonic79, Fri 28/11/2014 11:57:41

Previous topic - Next topic

rmonic79

hy guys i'm tryng to make clouds with parallax scrolling on four level. Is it possible to move objects having them reappear on the opposite side of the screen to create a perpetual movement?

Snarky

Yes. Simply write some code in repeatedly_execute() to scroll the clouds a bit each game cycle (something along the lines of cloud1.X = cloud1.X-1 to scroll from right to left), and when they've gone off-screen (cloud1.X + Game.SpriteWidth[cloud1.Graphic] < 0) move them over to the other edge.

I'm sure there's a module for it as well, but it's simple enough to do by yourself.

rmonic79

#2
Quote from: Snarky on Fri 28/11/2014 12:14:03
Yes. Simply write some code in repeatedly_execute() to scroll the clouds a bit each game cycle (something along the lines of cloud1.X = cloud1.X-1 to scroll from right to left), and when they've gone off-screen (cloud1.X + Game.SpriteWidth[cloud1.Graphic] < 0) move them over to the other edge.

I'm sure there's a module for it as well, but it's simple enough to do by yourself.
thanks for the answer :) i have a little problem with this, because i want that when the cloud reaches the end of the screen, the part offscreen reappears on the other side immediately without wait to all the sprite goes off, i'd tried this method and works using two sprites with the same image but perhaps there's a better way to do this, because with four levels of parallax it will be a little confused. I set timer to have less than 1 because i can't use float like x - 0.5 is there also a solution to this?
Code: ags

function room_FirstLoad()
{SetRestartPoint();

SetTimer(1, 400);
SetTimer(2, 202);
SetTimer(3, 302);
SetTimer(4, 10);
SetTimer(5, 10);
}

function room_RepExec()
{
if (IsTimerExpired(1)){player.Walk(250, 250, eNoBlock, eAnywhere); oCarica.Visible=true;oNewgame.Visible=true;}
if (IsTimerExpired(2)){object[1].Animate(1, 6, eOnce, eNoBlock, eForwards);SetTimer(2, 200);}
if (IsTimerExpired(3)){EnableInterface();}
if (IsTimerExpired(4)){ocloud.X=ocloud.X - 1;SetTimer(4, 10);}
if (IsTimerExpired(5)){ocloud1.X=ocloud1.X - 1;SetTimer(5, 10);}
if (ocloud.X + Game.SpriteWidth[ocloud.Graphic]<0){ocloud.X=321;}
if (ocloud1.X + Game.SpriteWidth[ocloud1.Graphic]<0){ocloud1.X=321;}

Thanks

Snarky

I suppose I should dig up my old cloud code, but let's see if we can get this to work from scratch.

The best way to move less than one pixel per turn is to have a "real position" as a float, and just round it to the nearest integer for display. So (untested code):

Code: AGS
float cloud1_X=Maths.IntToFloat(System.ViewportWidth); // Set to start pos. This is just off the right edge of the screen.
float cloud2_x=Maths.IntToFloat(System.ViewportWidth);
float cloud1_speed=-0.2; // Negative to move left
float cloud2_speed=-0.25;

void moveClouds()
{
  cloud1_X += cloud1_speed;
  cloud2_X += cloud2_speed;

  oCloud1.X = Maths.FloatToInt(cloud1_X);
  oCloud2.X = Maths.FloatToInt(cloud2_X);
  
  if(oCloud1.X + Game.SpriteWidth[oCloud1.Graphic]<0)
    cloud1_X = Maths.IntToFloat(System.ViewportWidth);
  else if(oCloud1.X >= System.ViewportWidth)
    cloud1_X = Maths.IntToFloat(0-Game.SpriteWidth[oCloud1.Graphic]);

  if(oCloud2.X + Game.SpriteWidth[oCloud2.Graphic]<0)
    cloud2_X = Maths.IntToFloat(System.ViewportWidth);
  else if(oCloud2.X >= System.ViewportWidth)
    cloud2_X = Maths.IntToFloat(0-Game.SpriteWidth[oCloud2.Graphic]);
}

function room_RepExec()
{
  moveClouds();
  // Do the other stuff with interface and walking, not sure exactly what that's about.
}


Not sure why they'd disappear as soon as they reach the edge, the Game.SpriteWidth[] bit should take care of that. Are the clouds animating?

If you have more than 2-3 clouds it might be easier to put them in arrays and loop through them rather than duplicate all the code for each cloud, but this is fine for now.

rmonic79

Quote from: Snarky on Fri 28/11/2014 20:37:12
I suppose I should dig up my old cloud code, but let's see if we can get this to work from scratch.

The best way to move less than one pixel per turn is to have a "real position" as a float, and just round it to the nearest integer for display. So (untested code):

Code: AGS
float cloud1_X=Maths.IntToFloat(System.ViewportWidth); // Set to start pos. This is just off the right edge of the screen.
float cloud2_x=Maths.IntToFloat(System.ViewportWidth);
float cloud1_speed=-0.2; // Negative to move left
float cloud2_speed=-0.25;

void moveClouds()
{
  cloud1_X += cloud1_speed;
  cloud2_X += cloud2_speed;

  oCloud1.X = Maths.FloatToInt(cloud1_X);
  oCloud2.X = Maths.FloatToInt(cloud2_X);
  
  if(oCloud1.X + Game.SpriteWidth[oCloud1.Graphic]<0)
    cloud1_X = Maths.IntToFloat(System.ViewportWidth);
  else if(oCloud1.X >= System.ViewportWidth)
    cloud1_X = Maths.IntToFloat(0-Game.SpriteWidth[oCloud1.Graphic]);

  if(oCloud2.X + Game.SpriteWidth[oCloud2.Graphic]<0)
    cloud2_X = Maths.IntToFloat(System.ViewportWidth);
  else if(oCloud2.X >= System.ViewportWidth)
    cloud2_X = Maths.IntToFloat(0-Game.SpriteWidth[oCloud2.Graphic]);
}

function room_RepExec()
{
  moveClouds();
  // Do the other stuff with interface and walking, not sure exactly what that's about.
}

thanks very interesting :) i will try it later along with manual to understand all steps :)
Quote from: Snarky on Fri 28/11/2014 20:37:12
Not sure why they'd disappear as soon as they reach the edge, the Game.SpriteWidth[] bit should take care of that. Are the clouds animating?
It works fine but (let me try to explain in english, it's hard :) ) the screen is 320px and the front cloud (the one i'm using to try the scroll) is 256 px. in my mind i was thinking that while the first pixel of cloud goes offscreen he can reappear on the other side creating something like this
d          clou
ud          clo
oud          cl   
without waiting that the entire sprite goes offscreen (sorry for the example but maybe it explains better that my bad english)   

Quote from: Snarky on Fri 28/11/2014 20:37:12
If you have more than 2-3 clouds it might be easier to put them in arrays and loop through them rather than duplicate all the code for each cloud, but this is fine for now.
i never worked with array in ags but i 'll give it a try.

thanks for advice ;)


Vincent

Uncreative hint,

Likewise, use Y++; to move the cloud down or  Y--; to move it up.
X--; to move it left, X++; to move it to the right   :)

Snarky

It's pretty simple, really. All the Maths.IntToFloat/FloatToInt is just to convert between floats and ints (floats are numbers with decimals, while ints are whole numbers). If you move -0.2 pixels per turn, it will take 5 turns to move a whole pixel (note that when things move this slowly, it starts to look jerky). I made it so you can change the speed and direction easily, and a test to check if they've gone off the right side of the screen as well.

Quote from: Vincent on Fri 28/11/2014 21:21:49
X--; to move it left, X++; to move it to the right   :)

We're already doing (essentially) that, Vincent.

Quote from: rmonic79 on Fri 28/11/2014 21:03:18in my mind i was thinking that while the first pixel of cloud goes offscreen he can reappear on the other side creating something like this
d          clou
ud          clo
oud          cl   
without waiting that the entire sprite goes offscreen (sorry for the example but maybe it explains better that my bad english)

Oh right. No, you can't do that, I'm afraid. A sprite can't "wrap around" the screen, and an object can only be in one place at one time. To get this effect, you'll need two copies of each cloud (i.e. two objects which both use the same sprite as their graphic).

Exactly how to best write the code depends on the details of the situation. It really depends on how many clouds you're going to want on-screen at the same time, in how many layers, how quickly they should move, and whether they need to "interact" with other things in the scene (move in front of or behind parts of the background, like a mountain for example).

rmonic79

Quote from: Snarky on Fri 28/11/2014 21:49:32
It's pretty simple, really. All the Maths.IntToFloat/FloatToInt is just to convert between floats and ints (floats are numbers with decimals, while ints are whole numbers). If you move -0.2 pixels per turn, it will take 5 turns to move a whole pixel (note that when things move this slowly, it starts to look jerky). I made it so you can change the speed and direction easily, and a test to check if they've gone off the right side of the screen as well.

Quote from: Vincent on Fri 28/11/2014 21:21:49
X--; to move it left, X++; to move it to the right   :)

We're already doing (essentially) that, Vincent.

Quote from: rmonic79 on Fri 28/11/2014 21:03:18in my mind i was thinking that while the first pixel of cloud goes offscreen he can reappear on the other side creating something like this
d          clou
ud          clo
oud          cl   
without waiting that the entire sprite goes offscreen (sorry for the example but maybe it explains better that my bad english)

Oh right. No, you can't do that, I'm afraid. A sprite can't "wrap around" the screen, and an object can only be in one place at one time. To get this effect, you'll need two copies of each cloud (i.e. two objects which both use the same sprite as their graphic).

Exactly how to best write the code depends on the details of the situation. It really depends on how many clouds you're going to want on-screen at the same time, in how many layers, how quickly they should move, and whether they need to "interact" with other things in the scene (move in front of or behind parts of the background, like a mountain for example).
perfect! all clear. Thanks so much. :)

Monsieur OUXX

Please note that more and more people go for the "Tween module" solution. You'll probably start using it at some stage in your project, even though you thought you could spare it at the beginning.
This demo uses the Tween module to scroll a background image continuously right-to-left.
 

Snarky

I love the Tween module, but what I don't like about it for situations like this is that you control the overall duration rather than the speed. Rather than saying "this cloud should move 1 pixel every 4 loops" (or whatever), you have to say "this cloud should cross the screen in 30 seconds." I find it takes more tweaking to get right. Depends on what exactly you're trying to do, of course.

Monsieur OUXX

Quote from: Snarky on Sat 29/11/2014 00:19:07
Rather than saying "this cloud should move that-many pixels every that-many loops" you have to say "this cloud should cross the screen in 30 seconds."
You are right, that's a completely different approach. I just mentioned it for exhaustiveness's sake. The issues are usually solved by just checking every game loop if the tween's movement is over.
 

rmonic79

Quote from: Snarky on Fri 28/11/2014 20:37:12
I suppose I should dig up my old cloud code, but let's see if we can get this to work from scratch.

The best way to move less than one pixel per turn is to have a "real position" as a float, and just round it to the nearest integer for display. So (untested code):

Code: AGS
float cloud1_X=Maths.IntToFloat(System.ViewportWidth); // Set to start pos. This is just off the right edge of the screen.
float cloud2_x=Maths.IntToFloat(System.ViewportWidth);
float cloud1_speed=-0.2; // Negative to move left
float cloud2_speed=-0.25;

void moveClouds()
{
  cloud1_X += cloud1_speed;
  cloud2_X += cloud2_speed;

  oCloud1.X = Maths.FloatToInt(cloud1_X);
  oCloud2.X = Maths.FloatToInt(cloud2_X);
  
  if(oCloud1.X + Game.SpriteWidth[oCloud1.Graphic]<0)
    cloud1_X = Maths.IntToFloat(System.ViewportWidth);
  else if(oCloud1.X >= System.ViewportWidth)
    cloud1_X = Maths.IntToFloat(0-Game.SpriteWidth[oCloud1.Graphic]);

  if(oCloud2.X + Game.SpriteWidth[oCloud2.Graphic]<0)
    cloud2_X = Maths.IntToFloat(System.ViewportWidth);
  else if(oCloud2.X >= System.ViewportWidth)
    cloud2_X = Maths.IntToFloat(0-Game.SpriteWidth[oCloud2.Graphic]);
}

function room_RepExec()
{
  moveClouds();
  // Do the other stuff with interface and walking, not sure exactly what that's about.
}


Not sure why they'd disappear as soon as they reach the edge, the Game.SpriteWidth[] bit should take care of that. Are the clouds animating?

If you have more than 2-3 clouds it might be easier to put them in arrays and loop through them rather than duplicate all the code for each cloud, but this is fine for now.

it gives me this error



i  tried also without math like manual but it's the same



rmonic79

Quote from: Monsieur OUXX on Fri 28/11/2014 23:16:42
Please note that more and more people go for the "Tween module" solution. You'll probably start using it at some stage in your project, even though you thought you could spare it at the beginning.
This demo uses the Tween module to scroll a background image continuously right-to-left.
thanks for advice i'll take a look ;)

Snarky

Quote from: rmonic79 on Sat 29/11/2014 17:38:45
it gives me this error

It probably doesn't allow using a function call in a global assignment (outside of any function). Try moving the assignment to room_FirstLoad():
Code: AGS
float cloud1_X;
float cloud2_x;

function room_FirstLoad()
{
  cloud1_X=Maths.IntToFloat(System.ViewportWidth); // Set to start pos. This is just off the right edge of the screen.
  cloud2_x=Maths.IntToFloat(System.ViewportWidth);
}

rmonic79

Quote from: Snarky on Sat 29/11/2014 19:00:41
Quote from: rmonic79 on Sat 29/11/2014 17:38:45
it gives me this error

It probably doesn't allow using a function call in a global assignment (outside of any function). Try moving the assignment to room_FirstLoad():
Code: AGS
float cloud1_X;
float cloud2_x;

function room_FirstLoad()
{
  cloud1_X=Maths.IntToFloat(System.ViewportWidth); // Set to start pos. This is just off the right edge of the screen.
  cloud2_x=Maths.IntToFloat(System.ViewportWidth);
}


it works  assigning a float number directly like this(otherwise the objects with the same image are overlapped)

Code: ags

float cloud1_X= 318.0;
float cloud2_X=43.0;
float cloud1_speed=-0.05; // Negative to move left
float cloud2_speed=-0.05;

 void moveClouds()
{
  cloud1_X += cloud1_speed;
  cloud2_X += cloud2_speed;
 
  ocloud.X = FloatToInt(cloud1_X);
  ocloud1.X = FloatToInt(cloud2_X);
  
  if(ocloud.X + Game.SpriteWidth[ocloud.Graphic]<0)
    cloud1_X = IntToFloat(System.ViewportWidth);
  else if(ocloud.X >= System.ViewportWidth)
    cloud1_X = IntToFloat(0-Game.SpriteWidth[ocloud.Graphic]);
 
  if(ocloud1.X + Game.SpriteWidth[ocloud1.Graphic]<0)
    cloud2_X = IntToFloat(System.ViewportWidth);
  else if(ocloud1.X >= System.ViewportWidth)
    cloud2_X = IntToFloat(0-Game.SpriteWidth[ocloud1.Graphic]);
}



function room_Load()
{//float cloud1_X =IntToFloat(System.ViewportWidth); // Set to start pos. This is just off the right edge of the screen.
//float cloud2_X=IntToFloat(System.ViewportWidth);


The assignment works inside room load but there's no need with direct coordinates on top i think

or

Code: ags

float cloud1_X;
float cloud2_X;
float cloud1_speed=-0.05; // Negative to move left
float cloud2_speed=-0.05;

 void moveClouds()
{
  cloud1_X += cloud1_speed;
  cloud2_X += cloud2_speed;
 
  ocloud.X = FloatToInt(cloud1_X);
  ocloud1.X = FloatToInt(cloud2_X);
  
  if(ocloud.X + Game.SpriteWidth[ocloud.Graphic]<0)
    cloud1_X = IntToFloat(System.ViewportWidth);
  else if(ocloud.X >= System.ViewportWidth)
    cloud1_X = IntToFloat(0-Game.SpriteWidth[ocloud.Graphic]);
 
  if(ocloud1.X + Game.SpriteWidth[ocloud1.Graphic]<0)
    cloud2_X = IntToFloat(System.ViewportWidth);
  else if(ocloud1.X >= System.ViewportWidth)
    cloud2_X = IntToFloat(0-Game.SpriteWidth[ocloud1.Graphic]);
}



function room_FirstLoad()
{ cloud1_X =IntToFloat(ocloud.X); // Set to start pos. This is just off the right edge of the screen.
 cloud2_X=IntToFloat(ocloud1.X)

this way i get the position of the object as in the graphical interface

thanks very, very interesting  :-D

Nixxon

Hey Guys,

I'm desperately trying to achieve the same thing for my game.

Don't suppose I could borrow the room code? Just not sure where to put things (less than rudimentary understanding).

SMF spam blocked by CleanTalk