Create a flash draw game like Line Rider or others – A different approach – Part 2

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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 (distance<size && colliding) {
		collisionReact();
		colliding = false;
	}
	if (distance>size) {
		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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 5.00 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 12 comments

  1. Create a flash draw game like Line Rider or others - A different approach at Emanuele Feronato

    on April 21, 2007 at 5:49 pm

    [...] April 21st update: part 2 released [...]

  2. abhilash

    on April 22, 2007 at 7:05 am

    cool and nice

  3. Timothy

    on April 22, 2007 at 8:19 am

    This is great!

    CAN YOU PLEASE MAKE A TUTORIAL ON HOW TO MAKE A SIMPLE DEFENCE GAME?

  4. mousey

    on April 22, 2007 at 10:34 am

    Much better congrats!

  5. Peh

    on April 22, 2007 at 11:36 am

    COOL!
    Keep up with your good work!!!

  6. abhilash

    on April 25, 2007 at 10:57 am

    i understood thing but i could not understand how to make buttons and rider

  7. zak

    on April 28, 2007 at 2:43 am

    wow this is great stuff!!!!

  8. Callum

    on April 28, 2007 at 8:58 pm

    How do I remove the green lines :S

  9. adam

    on May 25, 2007 at 1:42 pm

    i do not understand, the flash actionscript does not work on my computer

  10. Jesuson

    on June 13, 2007 at 3:28 pm

    I have been testing it and it chash a lot of times. I would change next things:
    1. For the collision response, to calculate the out velocity I would use

    vx1=-cosa*w.vx sina*w.vy;
    vy1=(-cosa*w.vy-sina*w.vx)*bounce;

    w.vx = -cosa*vx1-sina*vy1; //ball X velocity
    w.vy = -cosa*vy1 sina*vx1; //ball Y velocity

    Now it has got aceleration and velocity won’t be infinite.

    2. There is some problems when you test the perimeter of the ball for collisions. The first is you don’t do it in order. For do it in order change
    px=w._x w.radius*Math.sin(i*360/precision);
    py=w._y-w.radius*Math.cos(i*360/precision);
    by
    px=w._x w.radius*Math.sin(i*Math.PI/precision);
    py=w._y-w.radius*Math.cos(i*Math.PI/precision);

    The second is, what happend if there are diferent points of collisions? for expample if ball collide with floor and wall, you don’t must take it like a single collision, becouse the response won’t be good.
    For fix it I take all the collisions points what are together (For detect than it´s together you have to detect them in order like I said)and put it in a collision group array, and then I calculate diferents responses with diferent arrays.

    Here you can see what I’m speaking about:
    Bad method –> http://denvish.net/ulf/130607/55530_collisions4.fla
    Good method –> http://denvish.net/ulf/130607/55602_collisions5.fla

    Look what happend in two cases when the ball hit the wall.
    note: use up/down to acelerate/brake.

  11. Create a flash draw game like Line Rider or others - A different approach - Part 3 at Emanuele Feronato

    on June 15, 2007 at 2:42 pm

    [...] Maybe some of you did not notice it, but there was a real pearl in a comment of Create a flash draw game like Line Rider or others – A different approach – Part 2 post. [...]

  12. bad

    on June 8, 2011 at 7:02 pm

    how do i create the ball and the hittest to stop my guy from falling through the lines?