?
Solved

Problem with parallax scrolling and camera easing

Posted on 2007-10-06
14
Medium Priority
?
206 Views
Last Modified: 2008-01-09
Hey All,

I am trying to make a template for a side scrolling/exploration engine. Basically as you walk there are different depth levels that scroll at different rates; also there is a camera easing effect as you walk. You can also "warp" from one region to the next by walking through a door at the end of a region.

The problem I am having is that sometimes the foreground and backgrounds continue to creep left or right after the user has stopped moving... and I can't seem to counter it no matter what I try. Can some of you savvy coders look at the attached FLA (you can get it at http://board.flashkit.com/board/showthread.php?t=746644 ) and try to help me figure this out please. Note: the problem seems to be more apparent after you've "warped" a few times. Try rapidly going right and left near the area where the easing starts and stops to make the glitch appear. It may sound confusing but its easy to see; just run the movie and play around for a little while.

Thank you very much!!
0
Comment
Question by:andrewaiello
  • 8
  • 6
14 Comments
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20029885
My first observation here is that you only see the background creep when you have scrolled all the way to one or the other edge of the screen, but have not warped yet. When the dude is moved away from his central position. At all other times, when you stop moving and the dude is centered in the screen, you don't get this problem.

I can get this problem the very first time if you simply scroll to the right all the way but don't warp.

So, what part of the code handles the case where the dude is not moving (you've let go of the cursor keys) but he's not centered? That's where I'd start looking for rounding errors.

Tom
0
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20029902
Okay, you've taken a really strange approach in your logic here. Although I haven't bothered to figure out exactly WHY the error is creeping in, I've gotten it to stop.

It appears that every frame, if the dude is in motion, you move the background by a certain amount, depending on the direction of motion.

Then, when you detect that we've gotten close enough to one or the other edge of the screen (b.xMin > 0) or (b.xMax < c.margin) you REVERSE the background motion, hoping to cancel out the effect so that the background stops moving.

I guess there's a rounding error where you're just not adding or subtracting quite exactly the same opposite amount from the background motion, and so the background creeps a little in those circumstances.

Much easier, and IMO a better approach, is to simply check your margin BEFORE moving the background at all. In other words, if we are near the edge of the screen, don't change your g.mc.FarGround._x value at all!

// here's the revised relevant sections of the code. I haven't tested exhaustively (actually, I compiled and ran once) but I think this should do the trick.

if (b.xMin <= 0 and b.xMax >= c.margin){
            g.mc.FarGround._x += m *.9;      
            g.mc.ForGround._x -= m *.5;
      }
      
      //
      var      b = g.mc.BGground.getBounds(this);
            //trace (b.xMax - c.margin);
      //
      if (b.xMin > 0) {
            m = b.xMin;  //stop roundoff error
            g.mc._x -= b.xMin;
            g.mc.sky._x += b.xMin;
            //g.mc.FarGround._x += b.xMin *.9;
            //g.mc.ForGround._x -= b.xMin *.5;
      }
      if (b.xMax < c.margin) {
            m = b.xMax - c.margin; //stop roundoff error
            g.mc._x -= b.xMax - c.margin;
            g.mc.sky._x += b.xMax - c.margin;
            //g.mc.FarGround._x += (b.xMax - c.margin) *.9;
            //g.mc.ForGround._x -= (b.xMax - c.margin) *.5;
      }


Nice easing effect, though.
0
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20029916
I really think you could refactor your KeyPress handling code. It just seems extremely cumbersome the way you've done it there.

Rather than first check which key isDown and create a pseudo bitwise OR, which you then later run against an extremely cumbersome switch statement, couldn't you calculate the direction change right in the keyDown section?

if (Key.isDown(Key.RIGHT)) {
      d.Xdirection = d.Xspeed;
}
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
LVL 1

Author Comment

by:andrewaiello
ID: 20030491
Hey Tom;

    Thanks alot for taking the time to get back to me!    I apologize if my code is all wonky...I was really just grasping at straws to try and make this work.  I'll try your code ASAP to see if it works for me.  

I need to think about your refactoring suggestion.  Would this still work if I need something different to happen in all 16 cases (pressing only right,  only left,right and left, etc)?     Thanks.
0
 
LVL 1

Author Comment

by:andrewaiello
ID: 20030493
Oh, and btw, I think your right about roundoff error causing the problem.. Thats what i came up with but couldn't fix it.
0
 
LVL 1

Author Comment

by:andrewaiello
ID: 20030500
Oh and another btw;  I did try the whole "check before you move" thing beforehand, but for some reason it didnt work...hence my bizaro logic way; please forgive me ;)
0
 
LVL 1

Author Comment

by:andrewaiello
ID: 20030533
Hmm... I tried to integrate this into the code; and the mountains are flying all over the place... can you past the entirety of the code on that frame so I can make sure I'm not doing something stupid?  Thanks.
0
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20033137
stop();

_global.MyRoot = this;

MyRoot.topboundary = -125;
MyRoot.bottomboundary = -25;
MyRoot.rightmarker = 0;
MyRoot.leftmarker = 0;
MyRoot.upmarker = 0;
MyRoot.downmarker = 0;
MyRoot.buttonTotal = 0;
MyRoot.CurrentZone = 1;
MyRoot.AllowMove = 1;
MyRoot.EnterFrom = null;

view      = { w: 550 };
ground      = { mc: groundMc, margin: groundMc.BGground._width * .5};
dude      = { mc: groundMc.dudeMc,  Xspeed : 10, Yspeed : 2, Xdirection: 0, Ydirection: 0 };
cam            = { x: view.w * .5, to: view.w * .5, margin: view.w, easing: .2 };

this.onEnterFrame = function ()
{
      MyRoot.ground      = { mc: groundMc, margin: groundMc.BGground._width * .5};
      moveDude();
      moveCamera();
}

//
function moveCamera() {
      var      d = dude;
      var      g = ground;
      var      c = cam;
      var sG = g.mc._x;
      var sFarG = g.mc.FarGround._x;
      var sForG = g.mc.ForGround._x;
      var sSky = g.mc.sky._x;              //save last positions
      //
      var      p = { x: 0, y: 0 };
      d.mc.localToGlobal(p);
      c.to = p.x ;
      //
      var      m = (c.to - c.x) * cam.easing;
      g.mc._x -= m;
      g.mc.sky._x +=m;
      if (b.xMin <= 0 and b.xMax >= c.margin){
            g.mc.FarGround._x += m *.9;      
            g.mc.ForGround._x -= m *.5;
      }
      
      //
      var      b = g.mc.BGground.getBounds(this);
            //trace (b.xMax - c.margin);
      //
      if (b.xMin > 0) {
            m = b.xMin;  //stop roundoff error
            g.mc._x -= b.xMin;
            g.mc.sky._x += b.xMin;
            //g.mc.FarGround._x += b.xMin *.9;
            //g.mc.ForGround._x -= b.xMin *.5;
      }
      if (b.xMax < c.margin) { trace ("gothere");
            m = b.xMax - c.margin; //stop roundoff error
            g.mc._x -= b.xMax - c.margin;
            g.mc.sky._x += b.xMax - c.margin;
            //g.mc.FarGround._x += (b.xMax - c.margin) *.9;
            //g.mc.ForGround._x -= (b.xMax - c.margin) *.5;
      }
      
      
      //this offset counters some of the problem; try removing this code to see problem worse
      /*
      if (SG - g.mc._x == 0) {  
            g.mc.FarGround._x += (sFarG - g.mc.FarGround._x);
            g.mc.ForGround._x += (sForG - g.mc.ForGround._x);
            g.mc.sky._x += (sSky - g.mc.sky._x);
      }
      */
      
      //trace (g.mc.sky._x);
      //var douche = b.xMax - c.margin;
    //trace (m *.9+ " : " + douche*.9);  //round off error is part of the problem here!
      //trace (g.mc.FarGround._x);
      //trace (cam.x); //this aint moving, no problem here
      //trace (cam.to); //this aint moving, no problem here
      //trace (cam.margin); //this aint moving, no problem here
      //trace (c.to - c.x); //this aint moving, no problem here
      //trace (b.xMax);
      
      // the camera could be calibraiting for tiny offsets in the ground position
      // For and Far grounds won't get this!
}
//
function      moveDude() {
      var      d = dude;
      var      g = ground;
      var      c = cam;
      var      s = d.mc._x;      // Save the last position
      var sy = d.mc._y;   // last y position
      //var saveFarG = g.mc.FarGround._x;
      //var saveForG = g.mc.ForGround._x;
      //

      d.Xdirection = 0;
      d.Ydirection = 0;
      //
      if (MyRoot.AllowMove == 1) {
            
            if (Key.isDown(Key.RIGHT)) {
                  MyRoot.rightmarker = 1;
            } else {
                  MyRoot.rightmarker = 0;
            }
            
            if (Key.isDown(Key.LEFT)) {
                  MyRoot.leftmarker = 2;
            } else {
                  MyRoot.leftmarker = 0;
            }
            
            if (Key.isDown(Key.UP)) {
                  MyRoot.upmarker = 4;
            } else {
                  MyRoot.upmarker = 0;
            }
            
            if (Key.isDown(Key.DOWN)) {
                  MyRoot.downmarker = 8;
            } else {
                  MyRoot.downmarker = 0;
            }
            
            MyRoot.buttonTotal = MyRoot.rightmarker+MyRoot.leftmarker+MyRoot.upmarker+MyRoot.downmarker;
      
            switch (MyRoot.buttonTotal) {
            
            case 0 :
                  break;
            
            case 1 :
                  d.Xdirection = d.Xspeed;      
                  break;
            
            case 2 :
                  d.Xdirection = -d.Xspeed;
                  break;
            
            case 3 :
                  break;
            
            case 4 :
                  d.Ydirection  = -d.Yspeed;
                  break;
            
            case 5 :
                  d.Xdirection = d.Xspeed;      
                  d.Ydirection  = -d.Yspeed;
                  break;
      
            case 6 :
                  d.Xdirection = -d.Xspeed;      
                  d.Ydirection  = -d.Yspeed;
                  break;
                  
            case 7 :
                  d.Ydirection  = -d.Yspeed;
                  break;
                  
            case 8 :
                  d.Ydirection = d.Yspeed;
                  break;
            
            case 9 :
                  d.Xdirection = d.Xspeed;      
                  d.Ydirection  = d.Yspeed;
                  break;
            
            case 10 :
                  d.Xdirection = -d.Xspeed;      
                  d.Ydirection  = d.Yspeed;
                  break;
                  
            case 11 :
                  d.Ydirection  = d.Yspeed;
                  break;
                  
            case 12 :
                  break;
                  
            case 13 :
                  d.Xdirection = d.Xspeed;      
                  break;
                  
            case 14 :
                  d.Xdirection = -d.Xspeed;      
                  break;
                  
            case 15 :
                  break;            
                  
            default :
                  trace("we got a problem here");
            }
            
            d.mc._x += d.Xdirection;                        // Move the dude
            d.mc._y += d.Ydirection;                        // Move the dude
            
            
            //
            var b = d.mc.getBounds(g.mc); // Get the dude's bounds relative to the ground
            //
            if (b.xMin < -g.margin || b.xMax > g.margin) {
                  d.mc._x = s;
            }
            if (d.mc._y < MyRoot.topboundary || d.mc._y > MyRoot.bottomboundary  ) {
                  d.mc._y = sy;
            }
      }
      
}
0
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20033147
And I might be wrong about the onKeyPress thing. If you absolutely want to be able to trap for a user pressing both the Left and Right arrows at the same time, and want to do something different if he's pressing them both versus pressing only 1 at a time, then yeah, you need to use your method, or a construct like this:

if (left key pressed){
  if (right key pressed){
      do something for both keys
   }
   do something for just the left key
} else if (right key pressed){
  do something for just the right key)
}

But that becomes really brutal if you then have to factor in the Up and Down keys. So no, your method works best in that case.

T
0
 
LVL 1

Author Comment

by:andrewaiello
ID: 20034554
Hey Tom,

     This as it stands, this code destroys the parallax scrolling (the mountains and the red forground scroll at the same rate as the ground the dude walks on... this isn't what i want).   Also...in your code... how can this:

[AS]
    if (b.xMin <= 0 and b.xMax >= c.margin){
            g.mc.FarGround._x += m *.9;      
            g.mc.ForGround._x -= m *.5;
      }
[/AS]

Be above where the variable b is defined:

[AS]
      var      b = g.mc.BGground.getBounds(this);
[/AS]


When I tried moving that block of code before the definition... it restored the parallax scrolling but there was an odd "snapping" sort of a effect when the dude gets close to the end of the screen..  This was causing problems.   Any ideas?

Thanks.
 
0
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20034694
Hmm. It appears I may have been a little too hasty in presenting a potential solution. I'll have to go back to the drawing board when I get the time. Sorry for that.

Tom
0
 
LVL 14

Accepted Solution

by:
tomaugerdotcom earned 2000 total points
ID: 20034762
Yep. I feel like an idiot. You're right, that code I gave you was garbage.

However.

I think that there is some truth to my suggestion that you might want to not simply negate the scrolling when you reach the border, but rather not scroll at all when you reach the border, if you know what I'm saying.

Trying to zero out the scrolling by supplying a negative scroll is almost guaranteed to give you a rounding error unless you keep yourself to integers, and even then. I have noticed some really weird things in flash when you move something, even by a whole number and then read the new _x coordinate and it turns out to be a fractional number anyway.

One way to work around this is instead of incrementing the object's position (object._x += .9) create a parallel coordinate system and increment that, then copy the value to the object's position:
myX += .9;
object._x = myX;

Because you're never reading the object's Stage coordinates, you can avoid any of the weirdness I mention above. Perhaps you could give this a quick try before we get too deep into the code?

Tom
0
 
LVL 1

Author Comment

by:andrewaiello
ID: 20035031
Hey Tom,

     I think tonypa at flashkit nailed it:  http://board.flashkit.com/board/showthread.php?t=746644
Looks like all it was was a rounding error as we suspected...he just cleverly used math.floor (and i think in some way that alternate coordinate system method you mentioned).  Work for you too?

Thanks.
0
 
LVL 14

Expert Comment

by:tomaugerdotcom
ID: 20035149
Feels right. The calculations you were using were creating fractional values that exceeded the rounding point of the Flash compiler. By making sure that you're always performing your calculations against integers, you avoid this issue.

T
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This is intended to introduce all collision detection principles in flash, their strengths, weaknesses and workarounds. The main method for Collision Detection in flash is using hitTestObject. But unless you'll be pushing rectangular shapes without …
While working over numerous projects I often had the requirement for doing a screen capture in AS3.0. Unfortunately I found no "ready made" solutions in google search that suited my requirements. But I did come across some great resources which help…
In this tutorial viewers will learn how to create a basic shape tween animation in Flash including shape hints for smooth animation Open a new document in Flash: Draw a shape: Select another frame (how long you want the tween to be): Right click and…
The goal of the tutorial is to teach the user how to live broadcast using Flash Media Live Encoder and connecting it to YouTube to broadcast. Log into your Youtube account, choose live stream settings, start live stream from Flash Media Live Enc…
Suggested Courses
Course of the Month13 days, 23 hours left to enroll

807 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question