Create a flash draw game like Line Rider or others – part 4

Read all posts about "" game

March 31st update: part 5 released

Here we go with the 4th part. Read steps 1, 2 and 3 and you’re ready to go.

In the last example in step 3 we had our bouncing ball but some of you noticed that sometimes the ball appears to be “stuck” in the ground, making some fake bounces.

Let’s understand why.

In the next movie, I created another movieclip instanced as “last_hit”.

Let’s see what happens.

onClipEvent (load) {
	yspeed = 0;
	xspeed = 0;
	gravity = 0.2;
	radius = 5;
	friction = 0.90;
	precision = 360;
	bounces = 0;
}
onClipEvent (enterFrame) {
	if (_root.go == true) {
		collisions = 0;
		sum_x = 0;
		sum_y = 0;
		yspeed = yspeed+gravity;
		for (x=1; x0) {
			_root.last_hit._x = _x;
			_root.last_hit._y = _y;
			bounces++;
			_root.collisions.text = "Bounces: "+bounces;
			ball_dir = Math.atan(yspeed/(xspeed*-1))/(Math.PI/180);
			if ((xspeed*-1)<0) {
				ball_dir += 180;
			}
			if ((xspeed*-1)>=0 && yspeed<0) {
				ball_dir += 360;
			}
			spot_x = sum_x/collisions;
			spot_y = sum_y/collisions;
			x_cat = spot_x-_x;
			y_cat = spot_y-_y;
			ball_coll = Math.atan(y_cat/x_cat)/(Math.PI/180);
			if (x_cat<0) {
				ball_coll += 180;
			}
			if (x_cat>=0 && y_cat<0) {
				ball_coll += 360;
			}
			ground_rotation = ball_coll-90;
			if (ground_rotation<0) {
				ground_rotation += 180;
			}
			bounce_angle = 180-ball_dir-2*(ground_rotation);
			if (bounce_angle<0) {
				bounce_angle += 360;
			}
			speed = Math.sqrt((yspeed*yspeed)+(xspeed*xspeed));
			xspeed = speed*Math.cos(bounce_angle*Math.PI/180)*friction;
			yspeed = (speed*Math.sin(bounce_angle*Math.PI/180))*-1*friction;
		}
		_y = _y+yspeed;
		_x = _x+xspeed;
	}
}

On lines 26-27 I assign to last_hit _x and _y the position where we checked the collision between the ball and the ground

As you can see, sometimes the black ball (last_hit) seems to "sink" in the ground, and that's when an error may occur. If the ball sinks, and after the first bouncing frame is still inside the ground, the script will believe the ball encountered another wall and make it bounce again. Look at this picture:

Fake bounce

If the ball sinks too much into the ground, line in position 2, during the bounce may collide again with the ground causing a fake bounce.

There are several ways to fix this issue, we could check the collision again and verify if there is a fake bounce.

Remember, though, that we are already doing a quite CPU expansive task, so determining twice (or more) in a frame all fake bounces may cause the game to slow down too much.

I found a way that roughly approximates the right way to avoid fake bounces and that works well, specially if you consider we are creating a game and not an accurate simulation.

I just "remember" the last _x and _y position where there wasn't any collision and set the collision point at that point.

The physics works in the same way, but the bounce won't start from the ground but from the last spot the ball was sighted before it touched the wall.

onClipEvent (load) {
	yspeed = 0;
	xspeed = 0;
	gravity = 0.2;
	radius = 5;
	friction = 0.90;
	precision = 360;
	bounces = 0;
}
onClipEvent (enterFrame) {
	if (_root.go == true) {
		collisions = 0;
		sum_x = 0;
		sum_y = 0;
		yspeed = yspeed+gravity;
		for (x=1; x0) {
			_root.last_hit._x = old_x;
			_root.last_hit._y = old_y;
			bounces++;
			_root.collisions.text = "Bounces: "+bounces;
			ball_dir = Math.atan(yspeed/(xspeed*-1))/(Math.PI/180);
			if ((xspeed*-1)<0) {
				ball_dir += 180;
			}
			if ((xspeed*-1)>=0 && yspeed<0) {
				ball_dir += 360;
			}
			spot_x = sum_x/collisions;
			spot_y = sum_y/collisions;
			x_cat = spot_x-_x;
			y_cat = spot_y-_y;
			ball_coll = Math.atan(y_cat/x_cat)/(Math.PI/180);
			if (x_cat<0) {
				ball_coll += 180;
			}
			if (x_cat>=0 && y_cat<0) {
				ball_coll += 360;
			}
			ground_rotation = ball_coll-90;
			if (ground_rotation<0) {
				ground_rotation += 180;
			}
			bounce_angle = 180-ball_dir-2*(ground_rotation);
			if (bounce_angle<0) {
				bounce_angle += 360;
			}
			speed = Math.sqrt((yspeed*yspeed)+(xspeed*xspeed));
			xspeed = speed*Math.cos(bounce_angle*Math.PI/180)*friction;
			yspeed = (speed*Math.sin(bounce_angle*Math.PI/180))*-1*friction;
			_x = old_x;
			_y = old_y;
		}
		else{
			old_x = _x;
			old_y = _y;
		}
		_y = _y+yspeed;
		_x = _x+xspeed;
	}
}

Lines 62-65 assign the actual _x and _y position to a couple of variables called old_x and old_y in case the ball didn't hit the terrain, while lines 59-60 set the _x and _y ball position to old_x and old_y. Now the ball is not on the ground so we cannot have any fake bounce.

You will notice sometimes there are about 2 or 3 pixels of error, but this can be tolerated in a game where fun is more important than simulation.

Ok, let's resume the drawing

This is a draw game but it's since part 1 we aren't drawing. To put all together, I created a GO! button that will release the ball and added the drawing capability.

To do this, I changed the actions in frame 1

go = false;
createEmptyMovieClip("terrain", 1);
terrain.lineStyle(10, 0xdd00dd, 100);
imdrawing = false;
onMouseDown = function () {
    if (imdrawing == false) {
        terrain.moveTo(_xmouse, _ymouse);
        imdrawing = true;
    }
    if (imdrawing == true) {
        onEnterFrame = function () {
            terrain.lineTo(_xmouse, _ymouse);
        };
    }
};
onMouseUp = function () {
    onEnterFrame = function () {
        imdrawing = false;
    };
};

To start drawing as seen in tutorial 1, and put this actionscript in the button

on (release) {
	go = true;
}

to release the ball.

Now I want to stop drawing when the "GO!" button is pressed (it would be too easy if you could draw once in game, you have to foresee the ball bounces!), and I want a "RESET!" button to start drawing again.

To reset the stage, simply add a rest button with this actionscript:

on (release) {
	_root.ball._x = 27;
	_root.ball._y = 47;
	_root.ball.xspeed = 0;
	_root.ball.yspeed = 0;
	_root.go = false;
	_root.terrain.clear();
	_root.terrain.lineStyle(10, 0xdd00dd, 100);
}

Lines 2-3: Reset ball position

Lines 4-5: Reset ball speed

Line 6: The go variable is set to false (the ball is not moving)

Lines 7-8: Clear the stage and start drawing again

Finally, I want to stop drawing when the ball is running... and of course I want...

The exit

I created another movieclip instanced as exit

First, let's see how to prevent the player from drawing once the ball is going.

The actions on frame 1 are:

go = false;
createEmptyMovieClip("terrain", 1);
terrain.lineStyle(10, 0xdd00dd, 100);
imdrawing = false;
onMouseDown = function () {
	if (!go) {
		if (imdrawing == false) {
			terrain.moveTo(_xmouse, _ymouse);
			imdrawing = true;
		}
		if (imdrawing == true) {
			onEnterFrame = function () {
				terrain.lineTo(_xmouse, _ymouse);
			};
		}
	}
};
onMouseUp = function () {
	onEnterFrame = function () {
		imdrawing = false;
	};
};

Line 6 Simply checks the go variable and let the player draw only if go is false

About the exit, the ball actionscript will be:

onClipEvent (load) {
	yspeed = 0;
	xspeed = 0;
	gravity = 0.2;
	radius = 5;
	friction = 0.90;
	precision = 360;
	bounces = 0;
}
onClipEvent (enterFrame) {
	if (_root.go == true) {
		if (_root.exit.hitTest(_x, _y, true)) {
			xspeed = 0;
			yspeed = 0;
		} else {
			collisions = 0;
			sum_x = 0;
			sum_y = 0;
			yspeed = yspeed+gravity;
			for (x=1; x0) {
				_root.last_hit._x = old_x;
				_root.last_hit._y = old_y;
				bounces++;
				_root.collisions.text = "Bounces: "+bounces;
				ball_dir = Math.atan(yspeed/(xspeed*-1))/(Math.PI/180);
				if ((xspeed*-1)<0) {
					ball_dir += 180;
				}
				if ((xspeed*-1)>=0 && yspeed<0) {
					ball_dir += 360;
				}
				spot_x = sum_x/collisions;
				spot_y = sum_y/collisions;
				x_cat = spot_x-_x;
				y_cat = spot_y-_y;
				ball_coll = Math.atan(y_cat/x_cat)/(Math.PI/180);
				if (x_cat<0) {
					ball_coll += 180;
				}
				if (x_cat>=0 && y_cat<0) {
					ball_coll += 360;
				}
				ground_rotation = ball_coll-90;
				if (ground_rotation<0) {
					ground_rotation += 180;
				}
				bounce_angle = 180-ball_dir-2*(ground_rotation);
				if (bounce_angle<0) {
					bounce_angle += 360;
				}
				speed = Math.sqrt((yspeed*yspeed)+(xspeed*xspeed));
				xspeed = speed*Math.cos(bounce_angle*Math.PI/180)*friction;
				yspeed = (speed*Math.sin(bounce_angle*Math.PI/180))*-1*friction;
				_x = old_x;
				_y = old_y;
			} else {
				old_x = _x;
				old_y = _y;
			}
			_y = _y+yspeed;
			_x = _x+xspeed;
		}
	}
}

Lines 12-14 stop the ball if it touches the exit.

Now you should be ready to create your first ball drawing game... why don't you send me your works? I will publish on this site.

The tutorial is not over of course... we still have to see how to advance levels, how to change the ball with a car or a guy, and so on.

Meanwhile, download source codes and give me feedback, then proceed to part 5

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

215 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
// Stairs
// 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