Talking about Line Rider game, Flash and Users contributions.
June 15th update: part 3 released
I am glad to publish a very interesting upgrade to Create a flash draw game like Line Rider or others – A different approach by Kevin Ward.
Kevin strikes back with three very interesting prototypes I am currently studying in order to optimize his ideas.
I suggest you all to read carefully his article.
——
The physics in the file has quite a flaw in that it seems to bounce the right direction but it loses speed.
If you change it to use the variation I sent you after that, it has all the speed, but it goes in the wrong direction.
This is quite a quandary to me with my lack of experience, so I’ve been working on it exhaustively for the past few days.
The physics has not quite improved yet, but I did create a platform for testing the physics that allows you to draw, erase, and reset the terrain, and I came up with some interesting outcomes.
Natually I did some research after my attempt proved itself imperfect and I found an excellent document covering 2D collisions of two objects with vectors, available here http://www.geocities.com/vobarian/2dcollisions/.
It outlines the seven steps to finding the final velocities of the two objects. You find the vector between the two objects, called the normal, then give it a magnitude of one.
The next step is to find a vector perpendicular to the normal vector.
This I knew nothing about before.
It’s called the tangent, and basically you add the normal velocity and the tangent velocity vectors together.
This works beautifully for two round objects with masses, but it implies that one object will lose velocity and another will gain.
Unfortunately, things get complicated when you’re trying to keep the velocity all in one object and bounce it off the terrain.
I think the answer lies in working the equations pretending that the terrain is the one moving and it has a mass equal to that of the ball.
I understand that in a collision between a stationary object and a moving object of the same mass, velocity will be traded.
I haven’t tried this yet because I think I’ve worked on one project enough for one night.
I’m going to include three files for your perusal.
The first is a successful simulation of 2D collisions between round objects.
The second and third are attempts at ball and terrain interaction with drawable terrain.
You’ll notice that both have a tendency toward one direction of bounce, which is the problem.
The problem of course if bad math, and I’m abandoning that approach for one I believe might actually work.
However, the third is interesting because velocity is transferred along the tangential vector and it seems to slide.
I’m interesting to know if that’s similar to how you plan to show us.
One more thing: I’m in the process of turning the first file’s functions into classes so I can more easily handle more than two objects.
Almost more than anything else, I would like to know how you think I could perform a collision check and collision reaction amongst many objects.
Thanks again, and by the way, I am from north-eastern Texas, USA, I’m seventeen-years old, a high school student, and I’ve been working in Flash and interested in other programming since January.
I’m also a guitarist and I draw a little here and there.
——
The “collision check and collision reaction amongst many objects” is a thing I am practicing right now, so expect a tutorial within some days.
Meanwhile, I am going to publish Kevin’s attempts
Momentum
var ballOne:MovieClip = _root.attachMovie("ball", "ballOne", 1, {_x:350, _y:200});
var ballTwo:MovieClip = _root.attachMovie("ball", "ballTwo", 2, {_x:150, _y:180});
var border:MovieClip = _root.createEmptyMovieClip("border", 3);
var distance:Number = 0;
var screen_width:Number = 550;
var screen_height:Number = 400;
var colliding:Boolean = true;
ballOne.gotoAndStop(1);
ballTwo.gotoAndStop(1);
ballOne.speed = {_vx:0, _vy:0, _m:.07};
ballTwo.speed = {_vx:8, _vy:0, _m:.01};
var V1:Object = {x:0, y:0};
var V2:Object = {x:0, y:0};
function drawBorder():Void {
border.lineStyle(4, 000000, 75);
border.moveTo(0, 0);
border.lineTo(0, screen_height);
border.lineTo(screen_width, screen_height);
border.lineTo(screen_width, 0);
border.lineTo(0, 0);
}
function Collisions(ball1:MovieClip, ball2:MovieClip):Void {
var px1 = ball1._x;
var px2 = ball2._x;
var py1 = ball1._y;
var py2 = ball2._y;
var vx1 = ball1.speed._vx;
var vx2 = ball2.speed._vx;
var vy1 = ball1.speed._vy;
var vy2 = ball2.speed._vy;
var m1 = ball1.speed._m;
var m2 = ball2.speed._m;
//Normal Object
var v_N_1:Object = {x:px2-px1, y:py2-py1};
var v_N_2:Object = {x:px2-px1, y:py2-py1};
//Unit Normal Object
var v_UN_1:Object = {x:v_N_1.x/(Math.sqrt(v_N_1.x*v_N_1.x+v_N_1.y*v_N_1.y)), y:v_N_1.y/(Math.sqrt(v_N_1.x*v_N_1.x+v_N_1.y*v_N_1.y))};
var v_UN_2:Object = {x:v_N_2.x/(Math.sqrt(v_N_2.x*v_N_2.x+v_N_2.y*v_N_2.y)), y:v_N_2.y/(Math.sqrt(v_N_2.x*v_N_2.x+v_N_2.y*v_N_2.y))};
//Unit Tangent Object
var v_UT_1:Object = {x:-v_UN_1.y, y:v_UN_1.x};
var v_UT_2:Object = {x:-v_UN_2.y, y:v_UN_2.x};
//Initial Velocity Object
var v_V1:Object = {x:vx1, y:vy1};
var v_V2:Object = {x:vx2, y:vy2};
//Initial Normal Velocity value
var n_V1N:Number = v_UN_1.x*v_V1.x+v_UN_1.y*v_V1.y;
var n_V2N:Number = v_UN_2.x*v_V2.x+v_UN_2.y*v_V2.y;
//Initial Tangent velocity value
var n_V1T:Number = v_UT_1.x*v_V1.x+v_UT_1.y*v_V1.y;
var n_V2T:Number = v_UT_2.x*v_V2.x+v_UT_2.y*v_V2.y;
//New Tangent velocity value
var n_V1TP:Number = n_V1T;
var n_V2TP:Number = n_V2T;
//New Normal velocity value
var n_V1NP:Number = (n_V1N*(m1-m2)+2*m2*n_V2N)/(m1+m2);
var n_V2NP:Number = (n_V2N*(m2-m1)+2*m1*n_V1N)/(m1+m2);
//New Normal velocity Object
var v_V1NP:Object = {x:n_V1NP*v_UN_1.x, y:n_V1NP*v_UN_1.y};
var v_V2NP:Object = {x:n_V2NP*v_UN_2.x, y:n_V2NP*v_UN_2.y};
//New Tangent velocity Object
var v_V1TP:Object = {x:n_V1TP*v_UT_1.x, y:n_V1TP*v_UT_1.y};
var v_V2TP:Object = {x:n_V2TP*v_UT_2.x, y:n_V2TP*v_UT_2.y};
//Final velocity Object
var v_V1P:Object = {x:v_V1NP.x+v_V1TP.x, y:v_V1NP.y+v_V1TP.y};
var v_V2P:Object = {x:v_V2NP.x+v_V2TP.x, y:v_V2NP.y+v_V2TP.y};
//Store results
_root.V1 = v_V1P;
_root.V2 = v_V2P;
}
////////////////////////////////
//Animation functions///////////
////////////////////////////////
function collisionCheck():Void {
var xSum:Number = ballOne._x-ballTwo._x;
var ySum:Number = ballOne._y-ballTwo._y;
var size:Number = ballOne._width/2+ballTwo._width/2;
distance = Math.sqrt(Math.pow(xSum, 2)+Math.pow(ySum, 2));
distance = Math.ceil(distance);
if (distancesize) {
colliding = true;
}
}
function collisionReact():Void {
var m1:MovieClip = ballOne;
var m2:MovieClip = ballTwo;
Collisions(m1, m2);
m1.speed._vx = V1.x;
m2.speed._vx = V2.x;
m1.speed._vy = V1.y;
m2.speed._vy = V2.y;
}
// Prevents objects from leaving the stage
function wallBounce():Void {
var m1:MovieClip = ballOne;
var m2:MovieClip = ballTwo;
if (m1._x<=0+m1._width/2) {
var mc = m1;
if (mc._x<(0+m2._width/2)) {
mc._x -= mc._x+(0-m2._width/2);
}
if (mc.speed._vx<0) {
mc.speed._vx *= -1;
}
}
if (m2._x<=0+m2._width/2) {
var mc = m2;
if (mc._x<(0+m2._width/2)) {
mc._x -= mc._x+(0-m2._width/2);
}
if (mc.speed._vx<0) {
mc.speed._vx *= -1;
}
}
if (m1._x>=(screen_width-m1._width/2)) {
var mc = m1;
if (mc._x>(screen_width-m2._width/2)) {
mc._x -= mc._x-(screen_width-m2._width/2);
}
if (mc.speed._vx>0) {
mc.speed._vx *= -1;
}
}
if (m2._x>=(screen_width-m2._width/2)) {
var mc = m2;
if (mc._x>(screen_width-m2._width/2)) {
mc._x -= mc._x-(screen_width-m2._width/2);
}
if (mc.speed._vx>0) {
mc.speed._vx *= -1;
}
}
if (m1._y<=0+m1._width/2) {
var mc = m1;
if (mc._y>0+mc._width/2) {
mc._y += mc._y-(0+mc._width/2);
}
if (mc.speed._vy<0) {
mc.speed._vy *= -1;
}
}
if (m2._y<=0+m2._width/2) {
var mc = m2;
if (mc._y>0+mc._width/2) {
mc._y += mc._y-(0+mc._width/2);
}
if (mc.speed._vy<0) {
mc.speed._vy *= -1;
}
}
if (m1._y>=(screen_height-m1._width/2)) {
var mc = m1;
if (mc._y>screen_height-mc._width/2) {
mc._y -= mc._y-(screen_height-mc._width/2);
}
if (mc.speed._vy>0) {
mc.speed._vy *= -1;
}
}
if (m2._y>=(screen_height-m2._width/2)) {
var mc = m2;
if (mc._y>(screen_height-mc._width/2)) {
mc._y -= mc._y-(screen_height-mc._width/2);
}
if (mc.speed._vy>0) {
mc.speed._vy *= -1;
}
}
}
function ballMove():Void {
ballOne._x += ballOne.speed._vx;
ballOne._y += ballOne.speed._vy;
ballTwo._x += ballTwo.speed._vx;
ballTwo._y += ballTwo.speed._vy;
}
function everyThingUpdate():Void {
ballMove();
collisionCheck();
wallBounce();
}
drawBorder();
_root.onEnterFrame = function():Void {
everyThingUpdate();
};
Line rider drawing
var m:MovieClip = _root.attachMovie("ball", "ball", 1, {_x:125, _y:15});
var t:MovieClip = _root.createEmptyMovieClip("terrain", 0);
var g:MovieClip = _root.attachMovie("button", "go_button", 105, {_x:0, _y:0});
var r:MovieClip = _root.attachMovie("button", "reset_button", 106, {_x:20, _y:0});
var c:MovieClip = _root.attachMovie("button", "clear_button", 107, {_x:40, _y:0});
var precision:Number = 1;
var radius:Number = m._height/2;
var sum_x:Number = 0;
var sum_y:Number = 0;
var collisions:Number = 0;
var normal_x:Number = 0;
var normal_y:Number = 0;
var vector_length = 0;
var go:Boolean = false;
var im_drawing:Boolean = false;
var friction:Number = 0.9;
var vector_length = m._height/2;
var speed:Object = {x:0, y:0, g:0.25};
t.lineStyle(10, 0x000000, 50);
r.gotoAndStop(2);
c.gotoAndStop(3);
_root.onMouseDown = function() {
if (!im_drawing) {
t.moveTo(_xmouse, _ymouse);
im_drawing = true;
t.onEnterFrame = function() {
linesDrawn = t.lineTo(_xmouse, _ymouse);
};
}
};
_root.onMouseUp = function() {
im_drawing = false;
t.onEnterFrame = function() {
};
};
g.onRelease = function() {
go = true;
};
r.onRelease = function() {
go = false;
m._x = 125;
m._y = 15;
};
c.onRelease = function() {
t.clear();
t.lineStyle(10, 0x000000, 50);
};
_root.onEnterFrame = function() {
sum_x = 0;
sum_y = 0;
collisions = 0;
if (go) {
for (var i:Number = 1; i<360; i += precision) {
var angle = (i*precision)*(Math.PI/180);
var spot_x = m._x+radius*(Math.sin(angle));
var spot_y = m._y-radius*(Math.cos(angle));
if (t.hitTest(spot_x, spot_y, true)) {
collisions++;
sum_x += spot_x;
sum_y += spot_y;
}
}
if (collisions>0) {
var total_speed = Math.sqrt(Math.pow(speed.x, 2)+Math.pow(speed.y, 2));
var spot_x = -((sum_x/collisions)-m._x);
var spot_y = -((sum_y/collisions)-m._y);
var tangent_x = --spot_y;
var tangent_y = -spot_x;
/*
if (speed.x<0 && speed.y>0){
tangent_x *= -1;
tangent_y *= 1;
}
if (speed.x<0 && speed.y<0){
tangent_x *= 1;
tangent_y *= -1;
}
*/
var vector_length = Math.sqrt(spot_x*spot_x+spot_y*spot_y);
var sumX = (spot_x+(tangent_x));
var sumY = (spot_y+(tangent_y));
f = m.createEmptyMovieClip("f", 5);
f.lineStyle(1, 0x00FF00);
f.moveTo(0, 0);
f.lineTo(spot_x*20, spot_y*20);
f.moveTo(0, 0);
f.lineTo(tangent_x*20, tangent_y*20);
var normalizer = Math.sqrt(sumX*sumX+sumY*sumY);
sumX /= normalizer;
sumY /= normalizer;
sumX *= total_speed;
sumY *= total_speed;
var sumXspeed = sumX;
var sumYspeed = sumY;
/*
if(speed.x<0){
sumXspeed*=-1;
}
if( speed.y<0){
sumYspeed*=-1;
}
*/
speed.x = sumXspeed;
speed.y = sumYspeed;
/*
m._x = old_x;
m._y = old_y;
*/
}
else {
old_x = m._x;
old_y = m._y;
}
speed.y += speed.g;
m._x += speed.x;
m._y += speed.y;
}
if (!go) {
speed.x = 0;
speed.y = 0;
}
};
Line rider sliding
var m:MovieClip = _root.attachMovie("ball", "ball", 1, {_x:125, _y:15});
var t:MovieClip = _root.createEmptyMovieClip("terrain", 0);
var g:MovieClip = _root.attachMovie("button", "go_button", 105, {_x:0, _y:0});
var r:MovieClip = _root.attachMovie("button", "reset_button", 106, {_x:20, _y:0});
var c:MovieClip = _root.attachMovie("button", "clear_button", 107, {_x:40, _y:0});
var precision:Number = 1;
var radius:Number = m._height/2;
var sum_x:Number = 0;
var sum_y:Number = 0;
var collisions:Number = 0;
var normal_x:Number = 0;
var normal_y:Number = 0;
var vector_length = 0;
var go:Boolean = false;
var im_drawing:Boolean = false;
var friction:Number = 1.0;
var vector_length = m._height/2;
var speed:Object = {x:0, y:1, g:0.15};
t.lineStyle(10, 0x000000, 50);
r.gotoAndStop(2);
c.gotoAndStop(3);
_root.onMouseDown = function() {
if (!im_drawing) {
t.moveTo(_xmouse, _ymouse);
im_drawing = true;
t.onEnterFrame = function() {
linesDrawn = t.lineTo(_xmouse, _ymouse);
};
}
};
_root.onMouseUp = function() {
im_drawing = false;
t.onEnterFrame = function() {
};
};
g.onRelease = function() {
go = true;
};
r.onRelease = function() {
go = false;
m._x = 125;
m._y = 15;
};
c.onRelease = function() {
t.clear();
t.lineStyle(10, 0x000000, 50);
};
_root.onEnterFrame = function() {
sum_x = 0;
sum_y = 0;
collisions = 0;
if (go) {
for (var i:Number = 1; i<360; i += precision) {
var angle = (i*precision)*(Math.PI/180);
var spot_x = m._x+radius*(Math.sin(angle));
var spot_y = m._y-radius*(Math.cos(angle));
if (t.hitTest(spot_x, spot_y, true)) {
collisions++;
sum_x += spot_x;
sum_y += spot_y;
}
}
if (collisions>0) {
var total_speed = Math.sqrt(Math.pow(speed.x, 2)+Math.pow(speed.y, 2));
var spot_x = -((sum_x/collisions)-m._x);
var spot_y = -((sum_y/collisions)-m._y);
var hitspot_x = sum_x/collisions;
var hitspot_y = sum_y/collisions;
var tangent_x = -spot_y;
var tangent_y = spot_x;
var angle = Math.atan(tangent_y/tangent_x)/(Math.PI/180);
m._rotation = angle;
var vector_length = Math.sqrt(spot_x*spot_x+spot_y*spot_y);
_root.subtract_x = spot_x/vector_length;
_root.subtract_y = spot_y/vector_length;
var sumX = ((tangent_x))/vector_length;
var sumY = ((tangent_y))/vector_length;
f = m.createEmptyMovieClip("f", 5);
f.lineStyle(1, 0x00FF00);
f.moveTo(0, 0);
f.lineTo(spot_x*20, spot_y*20);
f.moveTo(0, 0);
f.lineTo(tangent_x*20, tangent_y*20);
var normalizer = Math.sqrt(sumX*sumX+sumY*sumY);
sumX /= normalizer;
sumY /= normalizer;
sumX *= total_speed;
sumY *= total_speed;
var sumXspeed = sumX;
var sumYspeed = sumY;
speed.x = sumXspeed;
speed.y = sumYspeed;
m._x += subtract_x;
m._y += subtract_y;
}
else {
old_x = m._x;
old_y = m._y;
}
speed.y += speed.g;
speed.x *= friction;
m._x += speed.x;
m._y += speed.y;
}
if (!go) {
speed.x = 0;
speed.y = 0;
}
};
And there are the source codes.
I think Kevin is very close to the solution, let's us help him with feedback.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.