Platforming game implementing simple phyics and pixel collision over bitmap file.
var Bouncy = function (div_play_area, div_ball, div_level, div_output, canvas_ball) {
var level_array = [];
var background_offset = 0;
var collision_degree_increment = 3;
var ball_image = new Image();
var fps;
var gravity;
var ball_diameter;
var ball_bounce;
var ball_friction;
var jump_velocity;
var PlayArea = document.getElementById(div_play_area);
var BallDiv = document.createElement('div');
var CanvasBall = document.createElement('canvas');
var Level = document.createElement('div');
var Output = document.getElementById(div_output);
Ball.appendChild(CanvasBall);
PlayArea.appendChild(BallDiv);
PlayArea.appendChild(Level);
var ctx;
var Ball = {
pos: {x: 0, y: 0},
vel: {x: 0, y: 0, rot: 0},
acc: {x: 0, y: 0}
};
var Ymin;
var Ymax;
this.init = function (map_name, init_fps, init_gravity, init_ball_diameter, init_ball_bounce, init_ball_friction, init_jump_velocity) {
//Setup instance
fps = init_fps;
gravity = init_gravity;
ball_diameter = init_ball_diameter;
ball_bounce = init_ball_bounce;
ball_friction = init_ball_friction;
jump_velocity = init_jump_velocity;
//Setup Canvas
ball_image.src = './img/ball.png';
ball_image.onload = function () {
CanvasBall.setAttribute('width', ball_image.width);
CanvasBall.setAttribute('height', ball_image.height);
CanvasBall.style.width = CanvasBall.style.height = ball_diameter + 'px';
ctx = CanvasBall.getContext('2d');
ctx.drawImage(ball_image, 0, 0);
}
Ymin = PlayArea.clientHeight;
Ymax = PlayArea.clientHeight;
//Set ball
Ball.pos = {x: 50, y: 20};
Ball.vel = {x: 0, y: 0, rot: 0};
Ball.acc = {x: 0, y: 0};
//Set Physics
Ball.acc.y = gravity;
document.onkeydown = keyHandle;
document.onkeyup = keyHandle;
//Get level_array
Level.style.backgroundImage = 'url("./maps/' + map_name + '/background.png")';
getMap(map_name, gameLoop);
};
var gameLoop = function () {
resolvePhysics();
checkCollision();
setPosition(Ball, Math.round(Ball.pos.x), Math.round(Ball.pos.y));
rotateBall();
//ballStats();
};
var getMap = function (mapfile, callback) {
var ajaxRequest = new XMLHttpRequest();
var response;
ajaxRequest.onreadystatechange = function () {
if (ajaxRequest.readyState == 4) {
level_array = JSON.parse(ajaxRequest.responseText);
setInterval(callback, Math.round(1000 / fps));
}
}
ajaxRequest.open("GET", "/load/" + mapfile, true);
ajaxRequest.send(null);
}
var rotateBall = function () {
ball_circum = ball_diameter * Math.PI;
Ball.vel.rot = Math.round(Ball.vel.x) / ball_circum * 360;
ctx.translate(ball_image.height / 2, ball_image.width / 2);
ctx.rotate(Ball.vel.rot * Math.PI / 180);
ctx.translate( - ball_image.height / 2, - ball_image.width / 2);
ctx.drawImage(ball_image, 0, 0);
};
this.drawMap = function () {
for (iy = 0; iy < 10; iy++) {
for (ix = 0; ix < 100; ix++) {
if (level_array[iy].charAt(ix) == '1') {
drawPixel(ix, iy);
}
}
}
};
var ballStats = function () {
Output.innerHTML = 'Ball Pos X: ' + Ball.pos.x +
' Ball Pos Y: ' + Ball.pos.y +
' MinY: ' + Ymin + ' ';
Output.innerHTML += 'Ball Vel X: ' + Ball.vel.x +
' Ball Vel Y: ' + Ball.vel.y + ' ';
Output.innerHTML += 'Ball Nxt X: ' + (Ball.pos.x + Ball.vel.x) +
' Ball Nxt Y: ' + (Ball.pos.y - Ball.vel.y) + ' ';
Output.innerHTML += 'angle : ' + calcAngle(Ball.vel.x, Ball.vel.y) + ' ';
};
//Collision Math Functions-------------------------
var checkCollision = function () {
var velocityMagnitude = calcMagnitude(Ball.vel.x, Ball.vel.y);
var velocityUnit = new Object();
if (velocityMagnitude != 0) {
velocityUnit.x = Ball.vel.x / velocityMagnitude;
velocityUnit.y = Ball.vel.y / velocityMagnitude;
} else {
velocityUnit.x = 0;
velocityUnit.y = 0;
}
var surfNormal = new Object();
surfNormal.x = 0;
surfNormal.y = 0;
var checkX = 0;
var checkY = 0;
var colReflection;
var finalCoord = new Object();
finalCoord.x = Ball.pos.x;
finalCoord.y = Ball.pos.y;
finalCoord.colX = 0;
finalCoord.colY = 0;
finalCoord.numberOfCollisions = 0;
finalCoord.xcol = false;
finalCoord.ycol = false;
var velAngle = calcAngle(Ball.vel.x, Ball.vel.y);
for (i = 0; i <= Math.round(velocityMagnitude); i++) {
for (degrees = -90; degrees <= 90; degrees += collision_degree_increment) {
var checkAngle = velAngle + degrees;
if (checkAngle < 0) {
checkAngle += 360;
} else if (checkAngle >= 360) {
checkAngle -= 360;
}
var circleX = Math.round(getCircleCoordAtAngle(checkAngle, ball_diameter / 2, 1, 1).x);
var circleY = Math.round(getCircleCoordAtAngle(checkAngle, ball_diameter / 2, 1, 1).y);
checkX = Math.round(Ball.pos.x + (ball_diameter / 2) + (velocityUnit.x * i) + circleX);
checkY = Math.round(Ball.pos.y + (ball_diameter / 2) - (velocityUnit.y * i) - circleY);
if (level_array[checkY][checkX] == '1' || checkX < 1 || checkX == level_array[0].length || checkY == Ymax || checkY <= 1) {
finalCoord.colX += checkX;
finalCoord.colY += checkY;
finalCoord.numberOfCollisions++;
finalCoord.ycol = true;
} else if (degrees == 90 && level_array[Math.round(Ball.pos.y + ball_diameter) + 1][Math.round(Ball.pos.x + ball_diameter / 2)] != '1') {
Ymin = Ymax;
}
}
if (finalCoord.ycol == true) {
Ymin = Ball.pos.y;
finalCoord.colX = Math.round(finalCoord.colX / finalCoord.numberOfCollisions);
finalCoord.colY = Math.round(finalCoord.colY / finalCoord.numberOfCollisions);
surfNormal.x = Math.round(Ball.pos.x + Ball.clientWidth / 2) - finalCoord.colX;
surfNormal.y = finalCoord.colY - Math.round(Ball.pos.y + Ball.clientWidth / 2);
Ball.vel.x = (colReflection.x * (velocityMagnitude));
Ball.vel.y = (colReflection.y * (velocityMagnitude));
var xMod = ( (1-ball_bounce) * Ball.vel.x) * Math.abs(surfNormal.x / calcMagnitude(surfNormal.x, surfNormal.y) );
var yMod = ( (1-ball_bounce) * Ball.vel.y) * Math.abs(surfNormal.y / calcMagnitude(surfNormal.x, surfNormal.y) );
Ball.vel.x -= xMod;
Ball.vel.y -= yMod; break;
} else {
finalCoord.x = Math.round(Ball.pos.x + (velocityUnit.x * i));
finalCoord.y = Math.round(Ball.pos.y - (velocityUnit.y * i));
}
}
//Move the viewport across the map
if(finalCoord.x - background_offset > PlayArea.clientWidth - 100 && Ball.vel.x > 0 && finalCoord.x + 100 < level_array[0].length) {
background_offset = finalCoord.x - (PlayArea.clientWidth - 100);
Level.style.backgroundPosition = -1 * background_offset + "px";
}
if (finalCoord.x - background_offset < 100 && Ball.vel.x < 0 && finalCoord.x - 100 > 0) {
background_offset = finalCoord.x - 100;
Level.style.backgroundPosition = -1 * background_offset + "px";
}
//Set final positions
Ball.pos.x = finalCoord.x;
Ball.pos.y = finalCoord.y;
}
var resolvePhysics = function () {
Ball.vel.y += Ball.acc.y;
Ball.vel.x += (Math.abs(Ball.vel.x) < 15) ? Ball.acc.x : 0;
if (Ball.pos.y == Ymin) {
Ball.vel.y = (Math.abs(Ball.vel.y - Ball.acc.y) <= 1) ? 0 : Ball.vel.y;
Ball.vel.x = (Math.abs(Ball.vel.x) <= 1) ? 0 : ball_friction * Ball.vel.x;
}
};
var setPosition = function (Element, posX, posY) {
Element.style.left = posX - background_offset + "px";
Element.style.top = posY + "px";
};
var keyHandle = function (e) {
switch (e.type) {
case 'keydown':
if (e.keyCode == 37) {
Ball.acc.x = -2;
}
if (e.keyCode == 39) {
Ball.acc.x = 2;
}
if (e.keyCode == 38) {
Ball.vel.y -= jump_velocity;
}
break;
case 'keyup':
if (e.keyCode == 37) {
Ball.acc.x = 0;
}
if (e.keyCode == 39) {
Ball.acc.x = 0;
}
break;
};
if (e.keyCode == 33) {
ball_diameter += 10;
CanvasBall.style.width = ball_diameter + 'px';
CanvasBall.style.height = ball_diameter + 'px';
}
if (e.keyCode == 34) {
ball_diameter -= 10;
CanvasBall.style.width = ball_diameter + 'px';
CanvasBall.style.height = ball_diameter + 'px';
}
}
var resizeRefresh = function () {
Ball.pos.x = boxX;
Ball.pos.y = boxY;
};
return this;
};
};