Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Line Rider game, and Flash.

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

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.