A Flash Developer Resource Site

Results 1 to 6 of 6

Thread: Train Simulation; Need physics instead of TweenMax

  1. #1
    Pumpkin Carving 2008 ImprisonedPride's Avatar
    Join Date
    Apr 2006
    Location
    Grand Rapids MI
    Posts
    2,378

    Train Simulation; Need physics instead of TweenMax

    Hey all. I've been busy over the past year or so with college and work, but I've finally transferred to another school and now I have a couple weeks to play with flash again. I've always envisioned finishing the train engine that I started here in Eager Beaver's thread at post #15. I originally had worked with Mr. Malee to come up with a working, though not 100%, piece of code to take an x/y velocity for the train and manipulate it through straight rails as well as 90 degree turns.

    Fast forward a year. I've since formatted and thus lost my code. So I started a completely new project and took a completely different approach to the engine. I had previously read up a bit on TweenMax, which seemed to fit the bill quite nicely, so I continued on with the data side of the project and boiled the whole engine down into 12 actual tweens:
    Code:
    var NS:Object = {sx:8, sy:0, ex:8, ey:16, cx:8, cy:16};
    var SN:Object = {sx:8, sy:16, ex:8, ey:0, cx:8, cy:0};
    var NW:Object = {sx:8, sy:0, ex:0, ey:8, cx:16, cy:16};
    var WN:Object = {sx:0, sy:8, ex:8, ey:0, cx:16, cy:16};
    var NE:Object = {sx:8, sy:0, ex:16, ey:8, cx:0, cy:16};
    var EN:Object = {sx:16, sy:8, ex:8, ey:0, cx:0, cy:16};
    var SE:Object = {sx:8, sy:16, ex:16, ey:8, cx:16, cy:16};
    var ES:Object = {sx:16, sy:8, ex:8, ey:16, cx:16, cy:16};
    var WS:Object = {sx:0, sy:8, ex:8, ey:16, cx:0, cy:16};
    var SW:Object = {sx:8, sy:16, ex:0, ey:8, cx:0, cy:16};
    var EW:Object = {sx:16, sy:8, ex:0, ey:8, cx:0, cy:8};
    var WE:Object = {sx:0, sy:8, ex:16, ey:8, cx:16, cy:8};
    These would be to/from any side to any other side, assuming each side only has a single exit (which in my engine is true). From here, I started writing a way to dynamically look up each tile's tween object:
    Code:
    var paths:Array = [];
    paths[0] = {switched:false};	
    paths[1] = {switched:false};
    paths[2] = {switched:false};	
    paths[3] = {switched:false};	
    paths[4] = {switched:false};	
    paths[5] = {switched:false};
    paths[6] = {switched:false};	
    paths[7] = {switched:false};	
    paths[8] = {switched:false};	
    paths[9] = {switched:false};	
    paths[10]= {switched:false};	
    paths[11]= {switched:false};
    paths[12]= {switched:false};	
    paths[13]= {switched:false};	
    paths[14]= {switched:false};
    This array contains the start of would be an array to look up the tween objects, one for each of the 15 different types of tiles I have. Continuing further with this approach, I wanted to find a way to have the vehicle calculate it's next move on the fly. Since it would only ever be in one of four cardinal directions when it does this, I took the rotation of the vehicle, divided it by 90, and added 2. Voila, I always get a number 0-3. From there, and in the above code, you can see I needed a way for "switchable" sections of the track to be kept track of. Combine the "switched" boolean with a private member of "canSwitch", and you can probably decipher what the follow code means:

    Code:
    paths[0].e0 = {canSwitch:false, p00:WN};
    paths[0].e1 = {canSwitch:false, p00:NW};
    //...
    paths[12].e1 = {canSwitch:true, p10:NS, p11:NE};
    paths[12].e2 = {canSwitch:false, p00:EN};
    paths[12].e3 = {canSwitch:false, p00:SN};
    //...
    paths[14].e0 = {canSwitch:false, p00:WE};
    paths[14].e1 = {canSwitch:false, p00:NS};
    paths[14].e2 = {canSwitch:false, p00:EW};
    paths[14].e3 = {canSwitch:false, p00:SN};
    On a side note, depending on the entry direction, the "switched" variable would be changed to true to simulate real life tracks. When the car comes from one direction it actually pushes the track to the side. Another car would have to come from a different entry direction to push it back.

    In case you can't tell what it means, let me explain by showing you another small example like the above:
    Code:
    paths[ww].px = {canSwitch:false, pyz:WE};
    Where:
    • ww - tile number
    • x - path value (direction car is traveling)
    • y - canSwitch variable's value (0 false, 1 true)
    • z - switched variable's value (0 false, 1 true)


    I might use a syntax like the following in the end to get the tween info:

    Code:
    var tileNum = map[car.x][car.y];
    var tileObj = paths[tileNum];
    var pathObj = tileObj["e"+car.d];
    var tweenObj = pathObj["p"+tileObj.switched+""+pathObj.canSwitch];
    From here, I thought it was as simple as implementing TweenMax to do the dirty work, and just feed it my bezier tween anchor and control points from each tile. I created a test file, imported TweenMax, and started playing around. (My first thought was how %&@*#&% polished that bad boy is. If anyone hasn't tried it, USE IT.) Unfortunately for me, what I wanted to do was tween the car from the start of one tile to the start of the next, then calculate the next tween in the same frame and start the next tween (in calculating, it would decide from the above data what path to take "out" of a tile if it can switch). But damnit, no matter what I tried, there is an extremely small pause right at the end of the tween before the second one starts. I did a lot of research and have sadly come to the conclusion that I'm not going to be able to "fake" my way out of this and I have to do actual math (trig + physics) to simulate how the car might make those 90 degree turns, staying equidistant from the control point (the "corner" of the tile corresponding to the inside "corner" of the turn). To make things worse, TweenMax automatically oriented the MC's rotation accordingly based on the tween path, which I thought would be a treat. But now I also have to find a way to do that.

    If you're still reading at this point, major Kudos to you. The problem boils down to using this engine (the data specifically, I'm proud of it ) and expanding it to use a given x/y velocity for the car and use trig to move the car. Anyone care to point me in the right direction?
    The 'Boose':
    ASUS Sabertooth P67 TUF
    Intel Core i7-2600K Quad-Core Sandy Bridge 3.4GHz Overclocked to 4.2GHz
    8GB G.Skill Ripjaws 1600 DDR3
    ASUS ENGTX550 TI DC/DI/1GD5 GeForce GTX 550 Ti (Fermi) 1GB 1GDDR5 (Overclocked to 1.1GHz)
    New addition: OCZ Vertex 240GB SATA III SSD
    WEI Score: 7.6

  2. #2
    Funkalicious TOdorus's Avatar
    Join Date
    Nov 2006
    Location
    Nijmegen, Netherlands
    Posts
    697
    I think your problem lies in how to calculate the next position of the train on a (circular) turn given a certain velocity?

    You don't really use the X and Y components of the velocity, just the length of the vector (the actual velocity). So you first have to convert the x and y to the vector length by using pythagoras.

    Given that you know the radius of the turn, you then can calculate the radial velocity the train moves at by converting the distance traveled along the bend (called arc in math) to an angle. Linky


    Now you know the angle the train travels along a circle with a known radius. You can use this info to calculate the movement of the train during your used interval (in reality the direction of the velocity changes constantly, but in the end we're talking a game which updates every interval so you end up with a movement vector which basicly is the product of all that velocity changing). I think you know this part of trig well.

    Code:
    //angle isn't the traveled (thus relative angle) but the absolute angle (0 = pointing right).
    
    train.x = radius * cos(angle)
    train.y = radius * sin(angle)
    
    train.rotation = tan(train.y/train.x)
    Check this thread for a nice graphic explaining some more.

    EDIT:
    This does bring up the problem on how to properly store it. You could use the trains rotation or have a angle property to show at what angle to the centre of the turn it is.
    Last edited by TOdorus; 08-22-2009 at 06:55 PM.

  3. #3
    Pumpkin Carving 2008 ImprisonedPride's Avatar
    Join Date
    Apr 2006
    Location
    Grand Rapids MI
    Posts
    2,378
    Let me show you an example:


    I think your method will work for me, the problem is deciding what way to travel. My initial velocity is 42.7 px/s or .7 px/ frame. You can see from the image that if my velocity were .7 and _rotation was -90, I'd need to know which "way" I'm heading towards--either 0 or -180. This was one of the problems that melee and I ran into. I know that the turn is exactly and always 90 degrees, and the radius of the turn is 22.627 (sqrt(16^2+16^2)), but the problem is I don't know how to correct the rotation. It's too picky to leave to chance. It needs to rotate exactly 90 degrees or it'll run off the track.
    The 'Boose':
    ASUS Sabertooth P67 TUF
    Intel Core i7-2600K Quad-Core Sandy Bridge 3.4GHz Overclocked to 4.2GHz
    8GB G.Skill Ripjaws 1600 DDR3
    ASUS ENGTX550 TI DC/DI/1GD5 GeForce GTX 550 Ti (Fermi) 1GB 1GDDR5 (Overclocked to 1.1GHz)
    New addition: OCZ Vertex 240GB SATA III SSD
    WEI Score: 7.6

  4. #4
    Funkalicious TOdorus's Avatar
    Join Date
    Nov 2006
    Location
    Nijmegen, Netherlands
    Posts
    697
    Quote Originally Posted by ImprisonedPride View Post
    I think your method will work for me, the problem is deciding what way to travel. My initial velocity is 42.7 px/s or .7 px/ frame. You can see from the image that if my velocity were .7 and _rotation was -90, I'd need to know which "way" I'm heading towards--either 0 or -180. This was one of the problems that melee and I ran into. I know that the turn is exactly and always 90 degrees, and the radius of the turn is 22.627 (sqrt(16^2+16^2)), but the problem is I don't know how to correct the rotation. It's too picky to leave to chance. It needs to rotate exactly 90 degrees or it'll run off the track.
    I'm not entirely sure but you mean by that. Do you mean heading as in I'm coming from the north and I'm moving to the west? I advise you to use a 0-360 or 0-2 PI definition of a circle and not -180 - 180, as that's what math uses. A movieClip will convert these to it's right rotation, so that's no biggie.

    You don't need a diagonal to know the radius, as the radius is the same at the top, bottom, left and right side of the circle. When I look at your tileformat:

    Quote Originally Posted by ImprisonedPride View Post
    Code:
    var NS:Object = {sx:8, sy:0, ex:8, ey:16, cx:8, cy:16};
    var SN:Object = {sx:8, sy:16, ex:8, ey:0, cx:8, cy:0};
    var NW:Object = {sx:8, sy:0, ex:0, ey:8, cx:16, cy:16};
    var WN:Object = {sx:0, sy:8, ex:8, ey:0, cx:16, cy:16};
    var NE:Object = {sx:8, sy:0, ex:16, ey:8, cx:0, cy:16};
    var EN:Object = {sx:16, sy:8, ex:8, ey:0, cx:0, cy:16};
    var SE:Object = {sx:8, sy:16, ex:16, ey:8, cx:16, cy:16};
    var ES:Object = {sx:16, sy:8, ex:8, ey:16, cx:16, cy:16};
    var WS:Object = {sx:0, sy:8, ex:8, ey:16, cx:0, cy:16};
    var SW:Object = {sx:8, sy:16, ex:0, ey:8, cx:0, cy:16};
    var EW:Object = {sx:16, sy:8, ex:0, ey:8, cx:0, cy:8};
    var WE:Object = {sx:0, sy:8, ex:16, ey:8, cx:16, cy:8};
    The NW tile would have a entry position relative to the topleft position of the tile of (8,0) and a exit position of (0,8)? That would mean the circle has a radius of 8. The centre position of the circle would be located at (0,0).

    To extend the previous code a bit:
    Code:
    train.x = centreX + (radius * cos(angle))
    train.y = centreY + (radius * sin(angle))
    
    train.rotation = tan((train.y-centreY)/(train.x-centreX))
    Then the calculation becomes something like this:

    radial velocity = velocity / radius = .7 / 8 = .0875

    so the angle increases by .0875 radians per frame.

    say the tile would be at tileposition (1,1) than the actual topleft position is at (16,16) (I gather your tiles are 16x16?). Since in this example the centre position is at the topleft of the tile the centre is at (16+0,16+0) or simply just (16,16). So the train turns around the topleft corner.

    To calculate the x and y would be.

    train.x = 16 + (8* cos(angle))
    train.y = 16 + (8* sin(angle))

    Since we're in the first frame into the turn angle = .0875 so:

    train.x = 16 + (8* cos(.0875)) = 23.99999067
    train.y = 16 + (8* sin(.0875 )) = 16.0122173

    the next frame the angle would increase with the radial velocity so it would be .0875 + .0875 = .175. In third frame into the turn .175+.0875 = .2625 and so on.

    Now one problem in this example is that the radial velocity is negative, as it's turning clockwise (radial velocity is anti-clockwise). I can't think of something at the moment (morning and a bit sleepy), but that should be solvable by converting the components of your velocity vector.

    Another problem is when the train has exited the turn. The total arc the train needs to travel would be (.5*PI) * radius, so when a train exeeds the arc you can move it into the next tile and move it according to the remainder of velocity left.

    Sorry if I'm a bit vague, as I said it's morning here. If you want I can try to whip up a prototype if that explains better.

  5. #5
    Pumpkin Carving 2008 ImprisonedPride's Avatar
    Join Date
    Apr 2006
    Location
    Grand Rapids MI
    Posts
    2,378
    Alright well I'll have to take some time to digest that but if I understand you right, it seems like if I feed it the point I'm arc'ing around, it shouldn't matter what side I'm coming from.

    Also, you were close. sx and sy are adjustments from the registration point of the tile mc, which would be the upper left hand corner. So {sx:8, sy:0} would say that the starting position would be half way across the top of the tile (16x16 tiles as you said). The exit point of {ex:0, ey:8} would be 1/2 way down the left side of the tile. Ignore cx/cy, that was for the bezierTo feature of TweenMax. I'm coming off a late night (early morning ) so let me get some z's and get back to you if I have any more problems. Thanks.
    The 'Boose':
    ASUS Sabertooth P67 TUF
    Intel Core i7-2600K Quad-Core Sandy Bridge 3.4GHz Overclocked to 4.2GHz
    8GB G.Skill Ripjaws 1600 DDR3
    ASUS ENGTX550 TI DC/DI/1GD5 GeForce GTX 550 Ti (Fermi) 1GB 1GDDR5 (Overclocked to 1.1GHz)
    New addition: OCZ Vertex 240GB SATA III SSD
    WEI Score: 7.6

  6. #6
    When in doubt ask Eager Beaver's Avatar
    Join Date
    Feb 2007
    Location
    Planet Earth
    Posts
    911
    Sorry for hijacking the thread:
    I could complete the project in July 2008.
    I used the following code for the engine to negotiate the type of curve shown below:

    For other types of curves the code is modified suitably.
    Code:
    engine._x=px+20+rad*Math.sin(th1);
    engine._y=py+rad-rad*Math.cos(th1);
    ean=eth1*180/Math.PI;
    engine._rotation=90-ean;
    engine._xscale=-scale;
    eth1=eth1+dth;
    if(engine._x<epx-30){
    engine._rotation=180;
    engine._xscale=scale;
    j++;
    eth1=0;
    em1=0;
    You can play this movie at:
    http://www.gamesmostwanted.com/build...-ralroad1.html
    Last edited by Eager Beaver; 08-27-2009 at 10:30 AM.
    <signature removed by admin>

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center