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

Read all posts about "" game

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.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers