Create a Flash game like Deflection - part 3
Multipart tutorial: available parts 1, 2, 3
In this 3rd part we are going to create a complete game with exit and two levels.
Read parts 1 and 2 if you haven't done it already.
I'll also fix a bug reported by Questo:
I was playing around and if you right click while making a line it sotps the ball but the ball keeps moving when you put lines on it. it’s pretty cool.
It's not a difficult task since the game engine is already working... I just created a makelevel function that handles level creation with a switch
ACTIONSCRIPT:
-
// create game object
-
game = {gravity:0, dragging:false, clip:_root.game_mc, stageW:500, stageH:400, maxV:20};
-
// starting level
-
level = 1;
-
// function that prepares the level
-
function makelevel(level) {
-
// flag that states if we can draw line or not
-
can_draw = false;
-
// flag that states if the player drew a line
-
player_drew = false;
-
// next movie depth for the current drawing line
-
nextLineDepth = 0;
-
// create the exit
-
_root.attachMovie("exit", "exit", 1);
-
// create the movieclip that will contain maze lines
-
_root.createEmptyMovieClip("lines", 2);
-
lines.lineStyle(1, 0xff0000);
-
// create a movie which represents a line drawn by mouse
-
_root.createEmptyMovieClip("mouseLine", 3);
-
// create array for vectors of lines drawn by mouse
-
game.mouseV = new Array();
-
// create the ball
-
_root.attachMovie("ball", "ball", 4);
-
game.myOb = {clip:ball, airf:1, b:1, f:1, r:10, lastTime:getTimer()};
-
// drawing and placing assets according to level number
-
switch (level) {
-
case 1 :
-
exit._x = 100;
-
exit._y = 300;
-
game.myOb.p0 = {x:20, y:80};
-
// vectors x/y components
-
game.myOb.vx = 0;
-
game.myOb.vy = 12;
-
// create first vector
-
// point p0 is its starting point in the coordinates x/y
-
// point p1 is its end point in the coordinates x/y
-
game.v = new Array();
-
game.v[0] = {p0:{x:50, y:40}, p1:{x:450, y:40}};
-
game.v[1] = {p0:{x:50, y:40}, p1:{x:50, y:360}};
-
game.v[2] = {p0:{x:50, y:360}, p1:{x:450, y:360}};
-
game.v[3] = {p0:{x:100, y:80}, p1:{x:400, y:320}};
-
break;
-
case 2 :
-
exit._x = 100;
-
exit._y = 350;
-
game.myOb.p0 = {x:250, y:250};
-
// vectors x/y components
-
game.myOb.vx = 12;
-
game.myOb.vy = 0;
-
// create first vector
-
// point p0 is its starting point in the coordinates x/y
-
// point p1 is its end point in the coordinates x/y
-
game.v = new Array();
-
game.v[0] = {p0:{x:200, y:0}, p1:{x:200, y:120}};
-
game.v[1] = {p0:{x:200, y:150}, p1:{x:200, y:400}};
-
game.v[2] = {p0:{x:300, y:0}, p1:{x:300, y:400}};
-
game.v[3] = {p0:{x:0, y:300}, p1:{x:200, y:300}};
-
game.v[4] = {p0:{x:200, y:150}, p1:{x:300, y:150}};
-
break;
-
}
-
// draw and calculate all parameters for the wall vectors
-
for (x=0; x<game.v.length; x++) {
-
lines.moveTo(game.v[x].p0.x, game.v[x].p0.y);
-
lines.lineTo(game.v[x].p1.x, game.v[x].p1.y);
-
updateVector(game.v[x], true);
-
}
-
// flag to see if I pressed the right mouse button
-
right_click = false;
-
}
-
// phyisically making the level
-
makelevel(level);
-
_root.onEnterFrame = function() {
-
// checking right mouse button
-
if (Key.isDown(2)) {
-
right_click = true;
-
}
-
_root.runMe();
-
// checking the distance from the exit
-
dist_x = ball._x-exit._x;
-
dist_y = ball._y-exit._y;
-
distance = dist_x*dist_x+dist_y*dist_y;
-
// arbitrary distance, I don't want the ball to just touch the exit, I want the ball to be a bit inside the exit
-
if (distance<256) {
-
// level up...
-
level++;
-
makelevel(level);
-
}
-
};
-
// function to draw the points, lines and show text
-
function drawAll(v) {
-
// place ob mc
-
v.clip._x = v.p1.x;
-
v.clip._y = v.p1.y;
-
}
-
// main function
-
function runMe() {
-
// start to calculate movement
-
var ob = game.myOb;
-
// add air resistance
-
ob.vx *= ob.airf;
-
ob.vy *= ob.airf;
-
// dont let it go over max speed
-
if (ob.vx>game.maxV) {
-
ob.vx = game.maxV;
-
} else if (ob.vx<-game.maxV) {
-
ob.vx = -game.maxV;
-
}
-
if (ob.vy>game.maxV) {
-
ob.vy = game.maxV;
-
} else if (ob.vy<-game.maxV) {
-
ob.vy = -game.maxV;
-
}
-
// update the vector parameters
-
updateObject(ob);
-
// check the walls for collisions
-
for (x=0; x<game.v.length; x++) {
-
var w = game.v[x];
-
var v = findIntersection(ob, w);
-
v = updateVector(v, false);
-
var pen = ob.r-v.len;
-
// if we have hit the wall
-
if (pen>=0) {
-
// move object away from the wall
-
ob.p1.x += v.dx*pen;
-
ob.p1.y += v.dy*pen;
-
// change movement, bounce off from the normal of v
-
var vbounce = {dx:v.lx, dy:v.ly, lx:v.dx, ly:v.dy, b:1, f:1};
-
var vb = bounce(ob, vbounce);
-
ob.vx = vb.vx;
-
ob.vy = vb.vy;
-
}
-
}
-
/********************************************************************/
-
// CODE ADDED BY SRDJAN SUSNIC
-
// check the lines created by mouse for collisions
-
x = 0;
-
while (x<game.mouseV.length) {
-
var w = game.mouseV[x];
-
var v = findIntersection(ob, w);
-
v = updateVector(v, false);
-
var pen = ob.r-v.len;
-
// if we have hit the wall
-
if (pen>=0) {
-
// move object away from the wall
-
ob.p1.x += v.dx*pen;
-
ob.p1.y += v.dy*pen;
-
// change movement, bounce off from the normal of v
-
var vbounce = {dx:v.lx, dy:v.ly, lx:v.dx, ly:v.dy, b:1, f:1};
-
var vb = bounce(ob, vbounce);
-
ob.vx = vb.vx;
-
ob.vy = vb.vy;
-
// remove the clip and vector array of the collided line
-
game.mouseV[x].lineClip.removeMovieClip();
-
game.mouseV.splice(x, 1);
-
} else {
-
x++;
-
}
-
}
-
// END OF CODE ADDED BY SRDJAN SUSNIC
-
/********************************************************************/
-
// reset object to other side if gone out of stage
-
if (ob.p1.x>game.stageW+ob.r) {
-
ob.p1.x = -ob.r;
-
} else if (ob.p1.x<-ob.r) {
-
ob.p1.x = game.stageW+ob.r;
-
}
-
if (ob.p1.y>game.stageH+ob.r) {
-
ob.p1.y = -ob.r;
-
} else if (ob.p1.y<-ob.r) {
-
ob.p1.y = game.stageH+ob.r;
-
}
-
// draw it
-
drawAll(ob);
-
// make end point equal to starting point for next cycle
-
ob.p0 = ob.p1;
-
// save the movement without time
-
ob.vx = ob.vx/ob.timeFrame;
-
ob.vy = ob.vy/ob.timeFrame;
-
}
-
// function to find all parameters for the vector
-
function updateVector(v, frompoints) {
-
// x and y components
-
if (frompoints) {
-
v.vx = v.p1.x-v.p0.x;
-
v.vy = v.p1.y-v.p0.y;
-
} else {
-
v.p1.x = v.p0.x+v.vx;
-
v.p1.y = v.p0.y+v.vy;
-
}
-
// length of vector
-
v.len = Math.sqrt(v.vx*v.vx+v.vy*v.vy);
-
// normalized unti-sized components
-
if (v.len>0) {
-
v.dx = v.vx/v.len;
-
v.dy = v.vy/v.len;
-
} else {
-
v.dx = 0;
-
v.dy = 0;
-
}
-
// right hand normal
-
v.rx = -v.dy;
-
v.ry = v.dx;
-
// left hand normal
-
v.lx = v.dy;
-
v.ly = -v.dx;
-
return v;
-
}
-
function updateObject(v) {
-
// find time passed from last update
-
var thisTime = getTimer();
-
var time = (thisTime-v.lastTime)/100;
-
// we use time, not frames to move so multiply movement vector with time passed
-
v.vx *= time;
-
v.vy *= time;
-
// add gravity, also based on time
-
v.vy = v.vy+time*game.gravity;
-
v.p1 = {};
-
// find end point coordinates
-
v.p1.x = v.p0.x+v.vx;
-
v.p1.y = v.p0.y+v.vy;
-
// length of vector
-
v.len = Math.sqrt(v.vx*v.vx+v.vy*v.vy);
-
// normalized unti-sized components
-
v.dx = v.vx/v.len;
-
v.dy = v.vy/v.len;
-
// right hand normal
-
v.rx = -v.vy;
-
v.ry = v.vx;
-
// left hand normal
-
v.lx = v.vy;
-
v.ly = -v.vx;
-
// save the current time
-
v.lastTime = thisTime;
-
// save time passed
-
v.timeFrame = time;
-
}
-
// find intersection point of 2 vectors
-
function findIntersection(v1, v2) {
-
// vector between center of ball and starting point of wall
-
var v3 = {};
-
v3.vx = v1.p1.x-v2.p0.x;
-
v3.vy = v1.p1.y-v2.p0.y;
-
// check if we have hit starting point
-
var dp = v3.vx*v2.dx+v3.vy*v2.dy;
-
if (dp<0) {
-
// hits starting point
-
var v = v3;
-
} else {
-
var v4 = {};
-
v4.vx = v1.p1.x-v2.p1.x;
-
v4.vy = v1.p1.y-v2.p1.y;
-
// check if we have hit side or endpoint
-
var dp = v4.vx*v2.dx+v4.vy*v2.dy;
-
if (dp>0) {
-
// hits ending point
-
var v = v4;
-
} else {
-
// it hits the wall
-
// project this vector on the normal of the wall
-
var v = projectVector(v3, v2.lx, v2.ly);
-
}
-
}
-
return v;
-
}
-
// find new vector bouncing from v2
-
function bounce(v1, v2) {
-
// projection of v1 on v2
-
var proj1 = projectVector(v1, v2.dx, v2.dy);
-
// projection of v1 on v2 normal
-
var proj2 = projectVector(v1, v2.lx, v2.ly);
-
var proj = {};
-
// reverse projection on v2 normal
-
proj2.len = Math.sqrt(proj2.vx*proj2.vx+proj2.vy*proj2.vy);
-
proj2.vx = v2.lx*proj2.len;
-
proj2.vy = v2.ly*proj2.len;
-
// add the projections
-
proj.vx = v1.f*v2.f*proj1.vx+v1.b*v2.b*proj2.vx;
-
proj.vy = v1.f*v2.f*proj1.vy+v1.b*v2.b*proj2.vy;
-
return proj;
-
}
-
// project vector v1 on unit-sized vector dx/dy
-
function projectVector(v1, dx, dy) {
-
// find dot product
-
var dp = v1.vx*dx+v1.vy*dy;
-
var proj = {};
-
// projection components
-
proj.vx = dp*dx;
-
proj.vy = dp*dy;
-
return proj;
-
}
-
/********************************************************************/
-
// CODE ADDED BY SRDJAN SUSNIC
-
// declare starting point of the current drawing line
-
var lineX1, lineY1;
-
// declare ending point of the current drawing line
-
var lineX2, lineY2;
-
// when we click...
-
onMouseDown = function () {
-
// get starting point of line according to current mouse position
-
lineX1 = _root._xmouse;
-
lineY1 = _root._ymouse;
-
// set drawing flag so we can draw a line when mouse is moved
-
can_draw = true;
-
};
-
// when we move mouse...
-
onMouseMove = function () {
-
if (can_draw) {
-
// the player is drawing!
-
player_drew = true;
-
// get ending point of line according to current mouse position
-
lineX2 = _root._xmouse;
-
lineY2 = _root._ymouse;
-
// clear the previous line drawn by mouse
-
mouseLine.clear();
-
// set the drawing style
-
mouseLine.lineStyle(2, 0x00dd00);
-
// move the pen to the starting position
-
mouseLine.moveTo(lineX1, lineY1);
-
// draw the line to the ending position
-
mouseLine.lineTo(lineX2, lineY2);
-
}
-
};
-
// when we release...
-
onMouseUp = function () {
-
if (player_drew and !right_click) {
-
// clear the line drawn by mouse
-
mouseLine.clear();
-
// create a new movie clip which contains the line previously drawn by mouse
-
var tmp = mouseLine.createEmptyMovieClip("newLine", 10+nextLineDepth);
-
tmp.lineStyle(5, 0x0000ff);
-
tmp.moveTo(lineX1, lineY1);
-
tmp.lineTo(lineX2, lineY2);
-
// increase movie depth for the next line
-
nextLineDepth++;
-
// add the parameters of previously created line to the end of line vectors array
-
// lineClip parameter is used to store previously created line movie clip
-
// so we can remove it when ball bounces from it
-
index = game.mouseV.length;
-
game.mouseV[index] = {p0:{x:lineX1, y:lineY1}, p1:{x:lineX2, y:lineY2}, b:1, f:1, lineClip:tmp};