Icio’s rollercoaster code (like line rider)

A few years ago I saved a copy of AS2 code titled “rollercoaster” posted by kirupa user icio, because it’s so cool.
I’m now getting around to converting it to AS3 and there’s a little bug I’m stuck on (bug also exists in original as2 version)

Play as3 swf here:
http://www.fastswf.com/QrbAmzE
-Draw a slope and the sprite rides the line.
-Use Arrow keys to move left/right (I added that part)

Problem:
Try to slow yourself to a near stop on a flat surface, then move again, you will coast off the line.
The ENTER_FRAME event calls tick() which calls run(); I believe the problem exists in the run function logic.
I have tried to figure it out, but failed…a lot :frowning:

Here’s all the **AS3 **code
You can copy/paste this to a new document and run it. Sorry its sloppy I was in the process of converting it from as2
I have also attached the original AS2 .fla file

EDIT: found the original as2 kirupa thread from 2006!!! i feel old… I guess they were aware of the issue as well


import flash.geom.Point;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;

//CHILDREN

var ball:MovieClip= new MovieClip();
ball.graphics.lineStyle(3,0x00ff00);
ball.graphics.beginFill(0x0000FF);
ball.graphics.drawRect(-10,-10,10,10);
ball.graphics.endFill();

var track:MovieClip = new MovieClip();
var section:Shape= new Shape();
addChild(track);

//WORLD
var friction:Number = 0.97;
var gravity:Number = 0.8;
var v:Number=0;
var precision:Number = 25;//length of line segments

//VALUES
var points:Array=[];
var p:Point= new Point();
var lines:Array=[];

//USER
var drawing:Boolean = false;
var r:Boolean;
var left:Boolean;

//LISTENERS
stage.addEventListener(MouseEvent.MOUSE_DOWN, mDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mMove);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keysDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keysUp);


//GAME

function mDown(e:MouseEvent){
    
    points = [];
    lines = [];
    
    drawing = true;
    
    track.removeChildren();    
    track.graphics.clear();

    section.x = mouseX;
    section.y = mouseY;
    
    
    p = new Point(mouseX, mouseY);
    points.push(p);
    
    track.graphics.lineStyle(2, 0xCCCCCC);
    track.graphics.moveTo(0, mouseY);
    track.graphics.lineTo(2000, mouseY);
    track.graphics.moveTo(mouseX, mouseY);
}


function mUp(e:MouseEvent) {
    points.push(new Point(mouseX, mouseY));
    track.graphics.lineTo(mouseX,mouseY);
    
    drawing = false;

    var p1:Point, p2:Point, p3:Point, mid1:Point, mid2:Point;
    p1 = linearBezierPoint([points[0], points[1]], 0.5);
    
    //track.graphics.lineStyle(0, 0x000000);
    //track.graphics.moveTo(points[0].x, points[0].y);
    //track.graphics.lineTo(p1.x, p1.y);
    
    lines.push({x: points[0].x, y: points[0].y});

    if(points.length<=2){
        //    i added this otherwise it errored when click on stage and dont move
        return;
    }
    
    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(bezier([mid1, p2, mid2], 20));
    }
    

    lines.push({x: p3.x, y: p3.y});
    
    
    //track.graphics.lineTo(p3.x, p3.y);
    
    
    for (var j:int=0; j<lines.length-1; j++) {

        var a:Object = lines[j];
        var b:Object = lines[j+1];
        a.dx = b.x - a.x;
        a.dy = b.y - a.y;
        a.a = Math.atan2(a.dy, a.dx);
    }
    
    lines.pop();
    track.addChild(ball);
    
    ball.x = lines[0].x;
    ball.y = lines[0].y;
    ball.rotation = 180*lines[0].a/Math.PI;
    v = 0;
    ball.line = 0;
    
    addEventListener(Event.ENTER_FRAME, tick);

}

function tick(e:Event){
            
        if(drawing){
            return;
        }
        
        //USER MOVEMENT
        if(left&& v>-10){
            v-=.4;
            //friction=.999;
        }else if(r&&v<10){
            v+=.4;
            //friction=.999;
        }else{
            //friction=.9999;
        }
        

        var l:Object = lines[ball.line];
        if(!l){return;}
        v += Math.sin(l.a)*gravity;
        v *= friction;
        run(v);
        ball.rotation = 180/Math.PI*lines[ball.line].a;
        
/*        for(var id:String in l) {
            var value:Object = l[id];
            trace(id + " = " + value);
        }
        removeEventListener(Event.ENTER_FRAME, tick);*/
        
}

function run(distance) {
    
    var l = lines[ball.line];
    //trace(ball.line);
    if (v == 0) {
        return;
    }

    var ex:Number = l.x, ey:Number = l.y;
    if (v >= 0) {
        
        ex += l.dx;
        ey += l.dy;
    }
    
    var dx:Number = ex - ball.x;
    var 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 += v > 0 ? 1 : -1;
        if (ball.line == -1 || ball.line == lines.length) {
            ball.line -= v > 0 ? 1 : -1;
            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);
    }
}


function mMove(e:Event) {
    if (!drawing) return;
    
    var dx:Number = mouseX - p.x;
    var dy:Number = mouseY - p.y;
    var d:Number = Math.sqrt(dx*dx+dy*dy);
    
    if (d >= precision) {
        var a:Number = Math.atan2(dy, dx);
        p = new Point(p.x+precision*Math.cos(a), p.y+precision*Math.sin(a));
        points.push(p);
        
        section.x = p.x;
        section.y = p.y;
        //unknownDrawFunction()
        track.graphics.lineTo(p.x, p.y);
        
        mMove(null);
        //trace("why is it calling itself");
    }
}



function linearBezierPoint(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
    );
}

function quadraticBezierPoint(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
    );
}

function bezier(p:Array, segments:Number):Array {
    if (segments < 1){
        return null;
    }
   
    var func:Function;
    if (p.length < 2) {
        return null;
    } 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 null;
    }
    
    var pr:Array = [];
    
    var dt:Number = 1/segments;
    var s:Point = func(p, 0);
    track.graphics.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});
        track.graphics.lineTo(s.x, s.y);
    }
    
    return pr;
}

//UNKNOWN
function unknownDrawFunction(){
    //I dont know what this function was for, im not calling it
        if(!drawing){
            return;
        }
        section.graphics.clear();
        section.graphics.lineStyle(1, 0x666666);
        section.graphics.lineTo(mouseX, mouseY);
    //    updateAfterEvent();
}

//USER
function keysDown(e:KeyboardEvent){
    if(e.keyCode==37){

        left=true;
        r=false;
    }else if(e.keyCode==39){
        r=true;
        left=false;
    }
}
function keysUp(e:KeyboardEvent){
    if(e.keyCode==37){
        left=false;
    }else if(e.keyCode==39){
        r=false;
    
    }
}



Did you ever get this resolved in your project?

This is what I had put together after learning how to make a game out of that code, wasn’t amazing in my eyes but came out decent.

http://3dmitchell.com/miner/

[QUOTE=tbo;2660674]Yeah looks like you did fix that, nice!
I spent quite a while with it last night, I got to the understanding that

ball.line += v > 0 ? 1 : -1; 

increases the pointer of which lines object (coordinates and more)to target next,
lines[ball.line] // (coordinates and more)

but because of the failed logic it gets stuck where

if (d < Math.abs(distance)) {

is always false, thus never updating the next pointer

…or something

[/QUOTE]

I did not get it fixed, I dont even remember what I was doing with it but I still would like to fix it some day. I assume you have the same problem?

I can’t see your swf at the moment

[QUOTE=tbo;2662988]I did not get it fixed, I dont even remember what I was doing with it but I still would like to fix it some day. I assume you have the same problem?

I can’t see your swf at the moment[/QUOTE]

Well, once you can see the game with a modern browser I’d say that’s a start.

I am using IE10, Firefox, and Chrome with no issue viewing.

i meant to say I was on a phone at the time with no flash.

anyway I like it, its a nice game for sure, I do recommend some 1920’s chimney sweep looking guys piled in the cart that plummet to their death upon crashing.:be: but then again that’s always my solution

So did you end up dealing with the original issue of the cart veering off track at low velocity? I did not seem to have the problem with your game, perhaps you just added enough friction that it stops before you get to that point?

No problem, thanks! :slight_smile:

Yes, I did end up dealing with the cart veering off at low velocity and I didn’t proceed with making a second level until I had that figured out. I believe I did some sort of check on the ball speed and adjusted accordingly, I would have to open up my source to check for ya but if ya need it I can definitely open it up. The boost pads were something extra I added in and they can only be used a few times each because initially you could draw a ultra long line and use the boost pad to go in circles. An ultra long line could end up crashing flash on a slow system as the computation required to draw 1k-10k track pieces (the actual curve the ball takes as well as the visual line to the user) was quite intense. I did end up adding in a maximum track length check for drawing and boost limiter with colors that changed to let you know the level of boost left.

im not doing anything right now, so no need to open your code up but thanks. if u do come by it some time post it here though. I might make something somedayI think I recall experimenting with making the track segments longer as a way to pull a little more performance out of it also