Rollercoaster

Some (productive?) hours of boredom.

[size=6]Version 2[/size]

Draw with your mouse, this will become the track.
Make sure to start off going downwards or gravity can’t take effect on your cart!

[center][size=4]Play[/size][/center]

import flash.geom.Point;

Stage.scaleMode = "noScale";
Stage.align = "TL";
Stage.showMenu = false;

var track:MovieClip;
var section:MovieClip;
var length:Number = 60;

var ball;
var friction = 0.9999;
var gravity = 0.5;

var points:Array;
var p:Point;
var drawing:Boolean = false;

var lines:Array;

function onMouseDown() {
	points = [];
	lines = [];
	
	drawing = true;
	
	track.clear();
	_root.clear();
	if (ball) ball.removeMovieClip();
	
	section = _root.createEmptyMovieClip("_section", 2);
	section._x = _xmouse;
	section._y = _ymouse;
	section.onMouseMove = function() {
		this.clear();
		this.lineStyle(1, 0x666666);
		this.lineTo(this._xmouse, this._ymouse);
		updateAfterEvent();
	}
	section.onMouseMove();
	
	p = new Point(_xmouse, _ymouse);
	points.push(p);
	
	_root.lineStyle(1, 0xCCCCCC);
	_root.moveTo(0, _ymouse);
	_root.lineTo(2000, _ymouse);
	_root.moveTo(_xmouse, _ymouse);
}

function onMouseUp() {
	points.push(new Point(_xmouse, _ymouse));
	_root.lineTo(_xmouse, _ymouse);
	
	drawing = false;
	section.removeMovieClip();
	
	if (!track) {
		track = _root.createEmptyMovieClip("_track", 1);
	}
	
	var p1:Point, p2:Point, p3:Point, mid1:Point, mid2:Point;
	var p1:Point = linearBezierPoint([points[0], points[1]], 0.5);
	
	track.lineStyle(0, 0x000000);
	track.moveTo(points[0].x, points[0].y);
	track.lineTo(p1.x, p1.y);
	
	lines.push({x: points[0].x, y: points[0].y});
	
	for (var i:Number=0; i<points.length-2; i++) {
		p1 = points*;
		p2 = points[i+1];
		p3 = points[i+2];
		mid1 = linearBezierPoint([p1, p2], 0.5);
		mid2 = linearBezierPoint([p2, p3], 0.5);
		
		lines = lines.concat(track.bezier([mid1, p2, mid2], 20));
	}
	
	lines.push({x: p3.x, y: p3.y});
	track.lineTo(p3.x, p3.y);
	
	for (i=0; i<lines.length; i++) {
		var a = lines*;
		var b = lines[i+1];
		a.dx = b.x - a.x;
		a.dy = b.y - a.y;
		a.a = Math.atan2(a.dy, a.dx);
	}
	
	lines.pop();
	
	ball = track.attachMovie("ball", "_ball", 1);
	ball._x = lines[0].x;
	ball._y = lines[0].y;
	ball._rotation = 180*lines[0].a/Math.PI;
	ball.v = 0;
	ball.line = 0;
	
	_root.onEnterFrame = function() {
		var l = lines[ball.line];
		ball.v += Math.sin(l.a)*gravity;
		ball.v *= friction;
		run(ball.v);
		ball._rotation = 180/Math.PI*lines[ball.line].a;
	};
}

function onMouseMove() {
	if (!drawing) return;
	
	var dx:Number = _xmouse - p.x;
	var dy:Number = _ymouse - p.y;
	var d:Number = Math.sqrt(dx*dx+dy*dy);
	
	if (d >= length) {
		var a:Number = Math.atan2(dy, dx);
		p = new Point(p.x+length*Math.cos(a), p.y+length*Math.sin(a));
		points.push(p);
		
		section._x = p.x;
		section._y = p.y;
		section.onMouseMove();
		
		_root.lineTo(p.x, p.y);
		
		onMouseMove();
	}
}

function run(distance) {
	var l = lines[ball.line];
	if (ball.v == 0) return;
	
	var ex:Number = l.x, ey:Number = l.y;
	if (ball.v >= 0) {
		ex += l.dx;
		ey += l.dy;
	}
	
	var dx:Number = ex - ball._x, dy:Number = ey - ball._y;
	var d:Number = Math.sqrt(dx*dx + dy*dy);
	
	if (d < Math.abs(distance)) {
		ball._x = ex;
		ball._y = ey;
		ball.line += ball.v > 0 ? 1 : -1;
		if (ball.line == -1 || ball.line == lines.length) {
			ball.line -= ball.v > 0 ? 1 : -1;
			ball.v *= -0.3;
		} else {
			run((Math.abs(distance) - d)*Math.abs(distance)/distance);
		}
	} else {
		ball._x += distance*Math.cos(l.a);
		ball._y += distance*Math.sin(l.a);
	}
}

MovieClip.prototype.linearBezierPoint = function(p:Array, t:Number):Point {
    if (t < 0 || t > 1 || p.length != 2) return null;
   
    return new Point(
        p[0].x+(p[1].x-p[0].x)*t,
        p[0].y+(p[1].y-p[0].y)*t
    );
};

MovieClip.prototype.quadraticBezierPoint = function(p:Array, t:Number):Point {
    if (t < 0 || t > 1 || p.length != 3) return null;
   
    var ax:Number, bx:Number;
    bx = 2*(p[1].x-p[0].x);
    ax = p[2].x - p[0].x - bx;
   
    var ay:Number, by:Number;
    by = 2*(p[1].y - p[0].y);
    ay = p[2].y - p[0].y - by;
   
    var t2:Number = t*t;
   
    return new Point(
        ax*t2 + bx*t + p[0].x,
        ay*t2 + by*t + p[0].y
    );
};

MovieClip.prototype.bezier = function (p:Array, segments:Number):Array {
    if (segments < 1) return;
   
    var func:Function;
    if (p.length < 2) {
        return;
    } else if (p.length == 2) {
        func = this.linearBezierPoint;
    } else if (p.length == 3) {
        func = this.quadraticBezierPoint;
    } else if (p.length == 4) {
        func = this.cubicBezierPoint;
    } else {
        return;
    }
    
	var pr:Array = [];
	
    var dt:Number = 1/segments;
    var s:Point = func(p, 0);
    this.moveTo(s.x, s.y);
   
    for (var i:Number=1; i<=segments; i++) {
        s = func(p, i*dt);
		pr.push({x: s.x, y: s.y});
        this.lineTo(s.x, s.y);
    }
	
	return pr;
}

[size=6]Version 1[/size]

Draw with your mouse, this will become the track.
You can create sections of track that will boost your cart.
Holding one of the following keys will create boost sections:
Up: Boost no matter of the direction of your cart
Left: Boost when the cart is moving backwards
Right: Boost when the cart is moving forwards

[center][size=4]Play[/size][/center]

Stage.scaleMode = "noScale";
Stage.align = "TL";

// The previous position of the mouse whilst drawing
var m:Object = {
	x: 0,
	y: 0
};

var friction:Number = 1;	//0.999; // Friction whilst not being boosted
var gravity:Number = 0.5;	// Gravitational force in effect whilst not being boosted
var boost:Number = 0.1;		// Acceleration whilst being boosted

var ball:MovieClip;			// Actually the rollercoaster's cart
var lines:Array = [];		// The track

var drawing:Boolean = false;// Not drawing

/********************
 * CREATELINE
 *		Creates the track in the `lines` array as the user draws
 *******************/
function createLine(dx, dy) {
	// The line object
	var line:Object = {
		x: m.x,		/* Starting X of the line */
		y: m.y,		/* Starting Y of the line */
		dx: dx,		/* Change in X over course of line */
		dy: dy,		/* Change in Y over course of line */
		a: Math.atan2(dy, dx),			/* Angle of line */
		boostB: Key.isDown(Key.UP),		/* Boost in both directions	*/
		boostL: Key.isDown(Key.LEFT),	/* Boost when going left	*/
		boostR: Key.isDown(Key.RIGHT)	/* Boost when going right	*/
	};
	
	// Select which colour to draw in
	var color:Number = 0;
	if (line.boostB) {
		color = 0xFF0000;
	} else if (line.boostL) {
		color = 0x00FF00;
	} else if (line.boostR) {
		color = 0x0000FF;
	}
	
	// Update the previous mouse position variable
	m.x += dx;
	m.y += dy;
	
	// Draw the line
	lines.push(line);
	track.lineStyle(1, color);
	track.lineTo(line.x+line.dx, line.y+line.dy);
}

function onMouseDown() {
	// If we're drawing for a second time then `track` will exist;
	// in which case we want to reset everything
	if (track) {
		track.removeMovieClip();
		delete onEnterFrame;
		lines = [];
	}
	
	// Set off the drawing
	drawing = true;
	m.x = _xmouse;
	m.y = _ymouse;
	
	// Set up our track
	_root.createEmptyMovieClip("track", 1);
	
	track.lineStyle(1, 0xCCCCCC);
	track.moveTo(0, _ymouse);
	track.lineTo(2000, _ymouse);
	
	track.moveTo(m.x, m.y);
	
}
function onMouseUp() {
	// No longer drawing, finish the last line
	drawing = false;
	createLine(_xmouse - m.x, _ymouse - m.y);
	
	// Create the cart
	ball = track.attachMovie("ball", "ball", 1);
	ball._x = lines[0].x;
	ball._y = lines[0].y;
	ball.v = 0.2;
	ball.line = 0;
	
	// Set the main loop
	_root.onEnterFrame = function() {
		// How the cart should react to it's environment:
		//		BoostB [Boost in both directions]
		//		BoostL [Boost when going left]
		//		BoostR [Boost when going right]
		//		Otherwise [Apply gravity and friction]
		var l = lines[ball.line];
		if (l.boostB) {
			ball.v += boost*Math.abs(ball.v)/ball.v;
		} else if (l.boostL && ball.v <= 0) {
			ball.v -= boost;
		} else if (l.boostR && ball.v >= 0) {
			ball.v += boost;
		} else {
			ball.v += Math.sin(l.a)*gravity;
			ball.v *= friction;
		}
		
		// Run the line processing for the cart then update rotation
		run(ball.v);
		ball._rotation = 180/Math.PI*lines[ball.line].a;
		
	};
}

function onMouseMove() {
	if (!drawing) return;
	
	// distance moved
	var dx:Number = _xmouse - m.x, dy:Number = _ymouse - m.y;
	var d:Number = Math.sqrt(dx*dx + dy*dy);
	
	// Keep the lines less than 10 units long
	// (I can't remember why I put it in, but I'm going to leave it.)
	if (d >= 10) {
		var a = Math.atan2(dy, dx);
		dx = 10*Math.cos(a);
		dy = 10*Math.sin(a);
		createLine(dx, dy);
		onMouseMove();
	} else {
		createLine(_xmouse-m.x, _ymouse-m.y);
	}
}

function run(distance) {
	// The line the ball is on.
	var l = lines[ball.line];
	// If the ball is not moving then don't both continuing (unlikely)
	if (ball.v == 0) return;
	
	// Calculate the coordinates of the end the cart is heading to on it's current line
	var ex:Number = l.x, ey:Number = l.y;
	if (ball.v >= 0) {
		ex += l.dx;
		ey += l.dy;
	}
	
	// Distace to the calculated end
	var dx:Number = ex - ball._x, dy:Number = ey - ball._y;
	var d:Number = Math.sqrt(dx*dx + dy*dy);
	
	// If we get to the end of the line before we have travelled the desired distance then we need to move onto the next line
	if (d < Math.abs(distance)) {
		// Move to the end of the line
		ball._x = ex;
		ball._y = ey;
		// Move onto the next line
		ball.line += ball.v > 0 ? 1 : -1;
		// If we've reached either end of the track
		if (ball.line == -1 || ball.line == lines.length) {
			// Rebound
			ball.line -= ball.v > 0 ? 1 : -1;
			ball.v *= -0.3;
		// Otherwise
		} else {
			// Run again on the next line with the distance we have to travel minus the distance we just travelled
			run((Math.abs(distance) - d)*Math.abs(distance)/distance);
		}
	// Or if we can move the desired distance on our current line
	} else {
		// Do so
		ball._x += distance*Math.cos(l.a);
		ball._y += distance*Math.sin(l.a);
	}
}

Hope you enjoy :thumb:
[size=1]Oh, 2.9k posts[/size]