[Home] [Puzzles & Projects] [Delphi Techniques] [Math topics] [Library] [Utilities]
Here's another in the Physics simulation series - a roller coaster this time. The program allows users to -
If you are not into reading about how it was done, click here to skip to the download section at the bottom of this page.
Background & Techniques
This is an advanced program, 2500 or so lines of code. A complete description would take a book, so here we'll just write enough here to orient any adventurous Delphi programmers that want to improve the program. I've spent 6 weeks of spare time and this is the 4th version so I'm ready to move on. The number of ways to screw up a program increases with program size in the same way as the number of possible handshakes in a group of people - exponentially. So I'm sure there are a few bugs left for y'all.
Here are the main design considerations and a few of the techniques used.
RCoaster4 has three units: U_RCoaster4 is the main form which runs the user interface fields in the tab sheet controls at the bottom of the screen. U_Coaster contains the TCoaster class defining the track and coaster stuff running the coaster image in the top half of the screen. U_Splines is a unit which contains the B-Splines class used to define track curves..
The TCoaster class is a TPaintBox descendent containing properties and methods for the virtual world cart and track, and the information to display the coaster on the screen. I call it a "pseudo" component. It has the features of a component but doesn't require installation and takes its visual characteristics (location, size, and parent) from information passed at create time. This avoids the hassle of installing a component which will probably only be used in a single program, but still gives full graphics capability.
Scaling operations maintain external views in real world units and, for efficiency, keep internal values in pixel based units. A number of properties in TCoaster exist to support automatic conversion as required.
Scaling has been the biggest headache in developing this program. I think that this is at least partly due to cumulative differences introduced by approximating the continuous track with a set of line segments. Run characteristics, especially the extremes, can be affected by the screen resolution or number of line segments chosen. Also, the program tries to maintain the height to width ratio across screen resolutions. To do this we keep track of max and min x & y coordinates in virtual world coordinates and as % of image are width and height - a total of 8 numbers. These are saved and restored in coaster files.
Here's a fairly standard way to efficiently move an object, the cart train in this case, across an image. This is usually called sprite movement. The idea is to build images off screen so that we can update the on screen image with a single Copyrect operation. This avoids the flicker that would occur if we erased and the redrew the cart in separate operations.
Between the initial "onchain" portion of the ride at the beginning and the "braking" portion at the end, gravity rules! When off the track the cart behaves exactly like the bouncing ball program (without the bounce). When on the track, the constant force due to gravity acts straight down. This resolves itself into two parts - one acting parallel to the current track directions trying to speed up or slow down the cart, and one acting at right angles to the first part and which holds the cart on the track. Most discussions of coaster physics speak in terms of maintenance of total energy - potential energy at the top of hills (mass*gravity*height) is converted to kinetic energy at the bottoms (1/2*mass*velocity2). This is true but doesn't account for friction effects or centripetal forces through dips and through loops. These complicate things considerably.
Rolling friction always acts to in the oppose the direction of cart travel. The force applied is proportional to cart mass and velocity. Loops need to be elongated (clothoid shape) to allow carts to move through tighter radii as they slow down at the tops of loops. This shape allows rider to remain conscious throughout the entire loop. Circular loops require such high entry velocities that G forces would cause black outs - typically above 5 G's or so if applied for several seconds.
StepTime is the key procedure which recalculates forces and and moves the cart by one timestep, typically .05 seconds to produce a fairly smooth illusion of motion. It just takes previous cart parameters (acceleration, velocity, angle, location) and assumes that we moved a distance corresponding to one timestep. We then call the GetNextPosition procedure which check the array of track points to determine which line segment we're on and how for from the end of the segment. A record is returned with lots of information about the current track location, including angle and an estimate of the current turning radius. Radius is important to determine G forces.
Starting/Stopping the Cart
The chainpoint is set to release the cart at the first point when the coaster track dips below the horizon. From the beginning of the track to chainpoint, the coaster in moved at a constant velocity as specified in VZero, an initial velocity field. Uniform braking is applied from the next-to-last control point to the end of track by reducing velocity by a fraction, "1/(estimated time steps remaining)", at each time step.
Track Design -B-Splines and "Control Points"
Unit U_Splines is a modified version of a freeware Delphi B-Spline component written by M. v. Engeland. I modified the code to convert it from a component to a unit and added procedures to load and save the spline "control points" that define the track. A B-spline curve is a set of piecewise (cubic in this case) polynomial segments that pass close to a set of control points.
Left click on the coaster image invokes designmode. This mode allows click and drag rearrangement of control points. A popup menu, invoked by right clicking on the coaster, or a control point, allows new control points to be added or deleted.
Procedures LoadFromStream and SaveToStream load and save coaster information. One problem with streams is maintaining compatibility if stream structure is changed during development. A "version" feature here maintains compatibility with previously saved coasters by inserting a 4 character version string with format "Vx.x" at the beginning of saved streams. SaveToStream will always save the current new version number, and all of the current save fields. LoadFromStream will check the version number and adjust reading as necessary to read or set defaults for fields.
Lots more to tell, but that's enough for starters. Have fun!
Addendum: November 15, 2002 - RCoaster4 has been replaced by RCoaster5 which fixes a few problems and has some minor improvements.
Addendum August 15, 2003: RCoaster6 has been posted. It introduces "Templates" , circles or straight lines that can be placed and resized on the screen when in Design Mode. The idea is to allow students to build tracks with specific characteristics to check against their calculated values. (Or perhaps to check my coding, I'm not sure that the university that requested the feature has tested it thoroughly yet.) Right click the screen while in design mode to show a pop-up menu that will allow you to define or delete a template. Click the center of a circle template to select it for dragging to a new location. Click the circumference to select the circle for resizing. For lines, click either end to select that end for moving. In all cases click unselects the template. You will also see an option on the popup menu to lock/unlock the templates. This is to avoid accidentally picking up a templates when working with track control points. Templates will be saved and restored with coasters.
If you have problems using the feature, find bugs, or think up enhancements (idealized clothoid template?), use the feedback link to let me know.
Suggestions for Further Explorations
Copyright © 2000-2016, Gary Darby All rights reserved.