Related
* I can't figure out why I get the following error on my console: "Cannot read property 'style' of null" on line 216(near the bottom, I have put a comment) because of something also on lines 190, 200, 213? I think the problem is about the laser variable... Please help. I would really appreciate if someone could help me as soon as possible.*
'''
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>test</title>
<div>
<h1 style="text-align:center; color:purple;" id="pixelator">
<big>
<i>
<b>
Pixelator
</b>
</i>
</big>
</h1>
<button type="button" id="button1" onclick="Instructions()">Instructions for game</button>
<button type="button" id="button2" onclick="YouTube()">YouTube</button>
<h3 id="Instructions1">
Use the keys WASD to controll player one(blue), Q to
shoot left and E to shoot right. Use the arrow keys to controll player two(),
1 on the numberpad to shoot left and two to shoot right.
</h3>
<button type="button" id="Close" onclick="close1()">Close</button>
</div>
<img src="C:/Users/Tania/Documents/Website/Screenshots/Nice.jpg" alt="Image not found" id="Pic">
</head>
<body style="background-color:green">
<style>
button {
background-color: #4169E1;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 30%;
height: 45px;
}
#hero {
background:#ff0000;
width:20px;
height:50px;
position:absolute;
}
#player {
background:#ff0000;
width:20px;
height:50px;
position:absolute;
}
#Pic {
width: 150px;
}
#laser {
width: 30;
height: 3;
background:#ff0000
position:absolute;
}
</style>
<div id="player"></div>
<div id="hero"></div>
<div id="laser"></div>
<script type="text/javascript">
function YouTube(){
window.open("https://www.youtube.com/channel/UCe9MhUIA6wwwchVBzypCBnA");
}
document.getElementById("Instructions1").style.display = "none";
document.getElementById("Close").style.display = "none";
function Instructions() {
document.getElementById("Instructions1").style.display = "block";
document.getElementById("Close").style.display = "block";
}
function close1() {
document.getElementById("Instructions1").style.display = "none";
document.getElementById("Close").style.display = "none";
}
var LEFT_KEY = 65;
var UP_KEY = 87;
var RIGHT_KEY = 68;
var DOWN_KEY = 83;
var LEFT_ARROW = 37;
var RIGHT_ARROW = 39;
var SPACE_KEY = 32;
var DOWN_ARROW = 40;
var UP_ARROW = 38;
var Q_KEY = 81;
var E_KEY = 69;
var HERO_MOVEMENT = 10;
var lastLoopRun = 0;
var controller = new Object();
var player = new Object();
player.element = 'player'
player.x = 650;
player.y =460;
var hero = new Object();
hero.element = 'hero';
hero.x = 250;
hero.y = 460;
var laser = new Object();
laser.x = 0;
laser.y = -120;
function ensureBounds(sprite, ignoreY) {
if (sprite.x < 20) {
sprite.x = 20;
}
if (!ignoreY && sprite.y < 20) {
sprite.y = 20;
}
if (sprite.x + sprite.w > 1910) {
sprite.x = 1910 - sprite.w;
}
if (!ignoreY && sprite.y + sprite.h > 940) {
sprite.y = 940 - sprite.h;
}
}
function createSprite(element, x, y, w, h) {
var result=new Object();
result.element=element;
result.x=x;
result.y=y;
result.w=w;
result.h=h;
return result;
}
function toggleKey(keyCode, isPressed) {
if (keyCode == DOWN_KEY) {
controller.down = isPressed;
}
if (keyCode == LEFT_KEY) {
controller.left = isPressed;
}
if (keyCode == RIGHT_KEY) {
controller.right = isPressed;
}
if (keyCode == UP_KEY) {
controller.up = isPressed;
}
if (keyCode == SPACE_KEY) {
controller.space = isPressed;
}
if (keyCode == LEFT_ARROW) {
controller.leftarrow = isPressed;
}
if (keyCode == RIGHT_ARROW) {
controller.rightarrow = isPressed;
}
if (keyCode == UP_ARROW) {
controller.rightarrow = isPressed
}
if (keyCode == DOWN_ARROW) {
controller.downarrow = isPressed
}
if (keyCode == Q_KEY) {
controller.qkey = isPressed
}
if (keyCode == E_KEY) {
controller.ekey = isPressed
}
}
function handleControls() {
if (controller.down) {
hero.y += HERO_MOVEMENT
}
if (controller.up) {
hero.y -= HERO_MOVEMENT
}
if (controller.left) {
hero.x -= HERO_MOVEMENT;
}
if (controller.right) {
hero.x += HERO_MOVEMENT;
}
if (controller.qkey && laser.x <= -120) {
laser.x=hero.x-20;
laser.y=hero.y+15;
}
if (controller.ekey && laser.x <= -120) {
laser.x=hero.x+20;
laser.y=hero.y+10;
}
ensureBounds(hero);
}
function showSprites() {
setPosition(hero);
setPosition(laser);
setPosition(player)
}
function updatePositions() {
laser.x-= 90
}
function loop() {
if (new Date().getTime() - lastLoopRun > 40) {
updatePositions();
handleControls();
showSprites();
lastLoopRun = new Date().getTime();
}
setTimeout('loop();', 2);
}
document.onkeydown = function(evt) {
toggleKey(evt.keyCode, true);
};
document.onkeyup = function(evt) {
toggleKey(evt.keyCode, false);
};
loop();
function setPosition(sprite) {
var e = document.getElementById(sprite.element)
//***LINE 216-*** e.style.left = sprite.x + 'px';
e.style.top = sprite.y + 'px';
}
createSprite('laser', 0, -120, 2, 50)
</script>
</body>
</html>
'''
Your laser object doesn't have the laser.element property set, if you add laser.element='laser'; after the laster.x and laser.y inicializations (line 104) this error should be fixed
I've been trying to make a game where a character shoots a laser, but for some reason when you press Q, which is the firing key, the laser doesn't shoot, which is weird because I have tried this code before and it worked, the only thing I changed is I have added another player to the game, and it doesn't give me any error messages
'''
#hero {
background: #ff0000;
width: 20px;
height: 50px;
position: absolute;
}
#player {
background: #ff0000;
width: 20px;
height: 50px;
position: absolute;
}
#Pic {
width: 150px;
}
#laser {
width: 30;
height: 3;
background: #ff0000 position:absolute;
}
#plaser {
width: 30;
height: 3;
background: #ff0000 position:absolute;
}
</style>
<div id="player"></div>
<div id="hero"></div>
<div id="laser"></div>
<div id="plaser"></div>
<script type="text/javascript">
var LEFT_KEY = 65;
var UP_KEY = 87;
var RIGHT_KEY = 68;
var DOWN_KEY = 83;
var LEFT_ARROW = 37;
var RIGHT_ARROW = 39;
var SPACE_KEY = 32;
var DOWN_ARROW = 40;
var UP_ARROW = 38;
var Q_KEY = 81;
var E_KEY = 69;
var HERO_MOVEMENT = 10;
var ONE_KEY = 97;
var TWO_KEY = 98;
var lastLoopRun = 0;
var controller = new Object();
var player = new Object();
player.element = 'player'
player.x = 1450;
player.y = 460;
var hero = new Object();
hero.element = 'hero';
hero.x = 250;
hero.y = 460;
var laser = new Object();
laser.element = 'laser'
laser.x = 0;
laser.y = -120;
laser.w = 30;
laser.h = 3;
var plaser = new Object();
plaser.element = 'plaser'
plaser.x = 0;
plaser.y = -130;
plaser.w = 30;
plaser.h = 3;
function ensureBounds(sprite, ignoreY) {
if (sprite.x < 20) {
sprite.x = 20;
}
if (!ignoreY && sprite.y < 20) {
sprite.y = 20;
}
if (sprite.x + sprite.w > 1910) {
sprite.x = 1910 - sprite.w;
}
if (!ignoreY && sprite.y + sprite.h > 940) {
sprite.y = 940 - sprite.h;
}
}
function createSprite(element, x, y, w, h) {
var result = new Object();
result.element = element;
result.x = x;
result.y = y;
result.w = w;
result.h = h;
return result;
}
function toggleKey(keyCode, isPressed) {
if (keyCode == DOWN_KEY) {
controller.down = isPressed;
}
if (keyCode == LEFT_KEY) {
controller.left = isPressed;
}
if (keyCode == RIGHT_KEY) {
controller.right = isPressed;
}
if (keyCode == UP_KEY) {
controller.up = isPressed;
}
if (keyCode == SPACE_KEY) {
controller.space = isPressed;
}
if (keyCode == LEFT_ARROW) {
controller.leftarrow = isPressed;
}
if (keyCode == RIGHT_ARROW) {
controller.rightarrow = isPressed;
}
if (keyCode == UP_ARROW) {
controller.uparrow = isPressed
}
if (keyCode == DOWN_ARROW) {
controller.downarrow = isPressed
}
if (keyCode == Q_KEY) {
controller.qkey = isPressed
}
if (keyCode == E_KEY) {
controller.ekey = isPressed
}
if (keyCode == ONE_KEY) {
controller.onekey = isPressed
}
if (keyCode == TWO_KEY) {
controller.twokey = isPressed
}
}
function handleControls() {
if (controller.down) {
hero.y += HERO_MOVEMENT
}
if (controller.up) {
hero.y -= HERO_MOVEMENT
}
if (controller.left) {
hero.x -= HERO_MOVEMENT;
}
if (controller.right) {
hero.x += HERO_MOVEMENT;
}
if (controller.qkey && laser.x <= -120) {
laser.x = hero.x - 20;
laser.y = hero.y + 15;
}
if (controller.space && laser.x <= -120) {
laser.x = hero.x + 20;
laser.y = hero.y + 10;
}
if (controller.onekey && plaser.x <= -120) {
plaser.x = player.x - 20;
plaser.y = player.y + 15;
}
if (controller.twokey) {
}
if (controller.uparrow) {
player.y -= HERO_MOVEMENT
}
if (controller.downarrow) {
player.y += HERO_MOVEMENT
}
if (controller.leftarrow) {
player.x -= HERO_MOVEMENT;
}
if (controller.rightarrow) {
player.x += HERO_MOVEMENT;
}
ensureBounds(hero);
ensureBounds(player)
}
function showSprites() {
setPosition(hero);
setPosition(laser);
setPosition(player)
setPosition(plaser)
}
function updatePositions() {
laser.x -= 90
plaser.x -= 90
}
function loop() {
if (new Date().getTime() - lastLoopRun > 40) {
updatePositions();
handleControls();
showSprites();
lastLoopRun = new Date().getTime();
}
setTimeout('loop();', 2);
}
document.onkeydown = function(evt) {
toggleKey(evt.keyCode, true);
};
document.onkeyup = function(evt) {
toggleKey(evt.keyCode, false);
};
loop();
function setPosition(sprite) {
var e = document.getElementById(sprite.element)
e.style.left = sprite.x + 'px';
e.style.top = sprite.y + 'px';
}
createSprite('laser', 0, -120, 2, 50)
createSprite('plaser', 0, -120, 2, 50)
</script>
</body>
</html>
'''
I have another question about game creation, that I already reached out for.
As I continued developing the game and tried to implement point counter I have encountered another issue.
It seems that sometimes the points are being counter instead of +1 to +2 or even +3.
I am suspecting incorrect placement of the code-part expo += 1 as it would (probably) get looped 2 (or multiple) times instead of once, but frankly, I am not sure.
The code can be found here or below:
var LEVELS = [
[" x x",
" xx x",
" xxx x x",
" xx!xx x ox",
" x!!!x x xx",
" xx!xx x x",
" x xvx x x",
" x xx x",
" x x x",
" x x x",
" x x xx",
" x x",
" x # xxxxx o x",
" xxxxxx xxxxxxxxx xxxxxxxxxxxxx",
" x x ",
" x!!!!!x ",
" x!!!!!x ",
" xxxxxxx ",
" "],
];
var life = 3;
var expo = 0;
document.getElementById("life").innerHTML = ("Lives left: " + life);
document.getElementById("expo").innerHTML = ("Points: " + expo);
function Vector(x, y) {
this.x = x; this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
Vector.prototype.times = function(scale) {
return new Vector(this.x * scale, this.y * scale);
};
// Note: uppercase words are used that means constructor are values
var actorchars = {
"#": Player,
"o": Coin,
"=": Lava,
"|": Lava,
"v": Lava,
"#": Lava
};
function Player(pos) {
this.pos = pos.plus(new Vector(0, -.5));
this.size = new Vector(.5, 1);
this.speed = new Vector(0, 0);
}
Player.prototype.type = "player";
function Lava(pos, ch) {
this.pos = pos;
this.size = new Vector(1, 1);
if (ch === "=")
this.speed = new Vector(2, 0);
else if (ch === '|')
this.speed = new Vector(0, 2);
else if (ch === 'v'){
this.speed = new Vector(0, 5); // new Vector(0, 3);
this.repeatPos = pos;
} else if (ch === '#'){
this.speed = new Vector(0, 10);
}
}
Lava.prototype.type = "lava"
//Lava.prototype.type = "Lava";
function Coin(pos) {
this.basePos = this.pos = pos;
this.size = new Vector(.6, .6);
// take a look back
this.wobble = Math.random() * Math.PI * 2;
}
Coin.prototype.type = "coin";
Level.prototype.isFinished = function() {
return this.status !== null && this.finishDelay < 0;
};
function Level(plan) {
this.width = plan[0].length;
this.height = plan.length;
this.grid = [];
this.actors = [];
for (var y = 0; y < this.height; y++) {
var line = plan[y], gridLine = [];
for (var x = 0; x < this.width; x++) {
var ch = line[x], fieldType = null;
var Actor = actorchars[ch];
if (Actor)
this.actors.push(new Actor(new Vector(x, y), ch));
else if (ch === "x")
fieldType = "wall";
else if (ch === "!")
fieldType = "lava";
else if (ch === "|")
fieldType = "lava";
else if (ch === "=")
fieldType = "lava";
else if (ch === "#")
fieldType = "lava";
else if (ch === "v"){
fieldType = "lava";
console.log(fieldType);
}
gridLine.push(fieldType);
}
this.grid.push(gridLine);
}
this.player = this.actors.filter(function(actor) {
return actor.type === "player";
})[0];
this.status = this.finishDelay = null;
}
function element(name, className) {
var elem = document.createElement(name);
if(className) elem.className = className;
return elem;
}
function DOMDisplay(parent, level) {
this.wrap = parent.appendChild(element("div", "game"));
this.level = level;
this.wrap.appendChild(this.drawBackground());
this.actorLayer = null;
this.drawFrame();
}
var scale = 15;
DOMDisplay.prototype.drawBackground = function() {
var table = element("table", "background");
table.style.width = this.level.width * scale + "px";
table.style.height = this.level.height * scale + "px";
this.level.grid.forEach(function(row) {
var rowElement = table.appendChild(element("tr"));
rowElement.style.height = scale + "px";
row.forEach(function(type) {
rowElement.appendChild(element("td", type));
});
});
return table;
};
DOMDisplay.prototype.drawActors = function() {
var wrap = element("div");
this.level.actors.forEach(function(actor) {
var rect = wrap.appendChild(element("div", "actor " + actor.type));
rect.style.width = actor.size.x * scale + "px";
rect.style.height = actor.size.y * scale + "px";
rect.style.left = actor.pos.x * scale + "px";
rect.style.top = actor.pos.y * scale + "px";
});
return wrap;
};
DOMDisplay.prototype.drawFrame = function() {
if (this.actorLayer)
this.wrap.removeChild(this.actorLayer);
this.actorLayer = this.wrap.appendChild(this.drawActors());
this.wrap.className = "game " + (this.level.status || "");
this.scrollPlayerIntoView();
};
// clear it later
DOMDisplay.prototype.scrollPlayerIntoView = function() {
var width = this.wrap.clientWidth;
var height = this.wrap.clientHeight;
var margin = width / 3;
// The viewport
var left = this.wrap.scrollLeft, right = left + width;
var top = this.wrap.scrollTop, bottom = top + height;
var player = this.level.player;
var center = player.pos.plus(player.size.times(0.5))
.times(scale);
if (center.x < left + margin)
this.wrap.scrollLeft = center.x - margin;
else if (center.x > right - margin)
this.wrap.scrollLeft = center.x + margin - width;
if (center.y < top + margin)
this.wrap.scrollTop = center.y - margin;
else if (center.y > bottom - margin)
this.wrap.scrollTop = center.y + margin - height;
};
DOMDisplay.prototype.clear = function() {
this.wrap.parentNode.removeChild(this.wrap);
};
Level.prototype.obstacleAt = function(pos, size) {
var xStart = Math.floor(pos.x);
var xEnd = Math.ceil(pos.x + size.x);
var yStart = Math.floor(pos.y);
var yEnd = Math.ceil(pos.y + size.y);
if (xStart < 0 || xEnd > this.width || yStart < 0)
return "wall";
if (yEnd > this.height)
return "lava";
for (var y = yStart; y < yEnd; y++) {
for (var x = xStart; x < xEnd; x++) {
var fieldType = this.grid[y][x];
if (fieldType) return fieldType;
}
}
};
Level.prototype.actorAt = function(actor) {
for (var i = 0; i < this.actors.length; i++) {
var other = this.actors[i];
if (other != actor &&
actor.pos.x + actor.size.x > other.pos.x &&
actor.pos.x < other.pos.x + other.size.x &&
actor.pos.y + actor.size.y > other.pos.y &&
actor.pos.y < other.pos.y + other.size.y)
return other;
}
};
var maxStep = 0.05;
Level.prototype.animate = function(step, keys) {
if (this.status !== null)
this.finishDelay -= step;
while (step > 0) {
var thisStep = Math.min(step, maxStep);
this.actors.forEach(function(actor) {
actor.act(thisStep, this, keys);
}, this);
step -= thisStep;
}
};
Lava.prototype.act = function(step, level) {
var newPos = this.pos.plus(this.speed.times(step));
if (!level.obstacleAt(newPos, this.size))
this.pos = newPos;
else if (this.repeatPos)
this.pos = this.repeatPos;
else
this.speed = this.speed.times(-1);
};
var wobbleSpeed = 8, wobbleDist = 0.07;
Coin.prototype.act = function(step) {
this.wobble += step * wobbleSpeed;
var wobblePos = Math.sin(this.wobble) * wobbleDist;
this.pos = this.basePos.plus(new Vector(0, wobblePos));
};
var playerXSpeed = 10;
Player.prototype.moveX = function(step, level, keys) {
this.speed.x = 0;
if (keys.left) this.speed.x -= playerXSpeed;
if (keys.right) this.speed.x += playerXSpeed;
var motion = new Vector(this.speed.x * step, 0);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle)
level.playerTouched(obstacle);
else
this.pos = newPos;
};
var gravity = 30;
var jumpSpeed = 17;
Player.prototype.moveY = function(step, level, keys) {
this.speed.y += step * gravity;
var motion = new Vector(0, this.speed.y * step);
var newPos = this.pos.plus(motion);
var obstacle = level.obstacleAt(newPos, this.size);
if (obstacle) {
level.playerTouched(obstacle);
if (keys.up && this.speed.y > 0)
this.speed.y = -jumpSpeed;
else
this.speed.y = 0;
} else {
this.pos = newPos;
}
};
Player.prototype.act = function(step, level, keys) {
this.moveX(step, level, keys);
this.moveY(step, level, keys);
var otherActor = level.actorAt(this);
if (otherActor)
level.playerTouched(otherActor.type, otherActor);
// Losing animation
if (level.status == "lost") {
this.pos.y += step;
this.size.y -= step;
}
};
Level.prototype.playerTouched = function(type, actor) {
//if (type == "lava" || type == "Lava" && this.status === null) { //DOESN'T SEEM TO WORK, FIND OUT WHY MASS DAMAGE
if (type == "lava" && this.status === null) {
this.status = "lost";
life -= 1;
console.log(life);
expo = 0;
document.getElementById("expo").innerHTML = ("Points: " + expo);
document.getElementById("life").innerHTML = ("Lives left: " + life);
if(life < 0) {
sessionStorage.setItem("reloading", "true");
document.location.reload();
}
this.finishDelay = 1;
} else if (type == "coin") {
this.actors = this.actors.filter(function(other) {
return other != actor;
});
if (!this.actors.some(function(actor) {
console.log("coin picked up")
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
return actor.type == "coin";
})) {
life += 1;
document.getElementById("life").innerHTML = ("Lives left: " + life);
this.status = "won";
this.finishDelay = 1;
}
}
};
var arrowCodes = {37: "left", 38: "up", 39: "right"};
function trackKeys(codes) {
var pressed = Object.create(null);
function handler(event) {
if (codes.hasOwnProperty(event.keyCode)) {
var down = event.type == "keydown";
pressed[codes[event.keyCode]] = down;
event.preventDefault();
}
}
addEventListener("keydown", handler);
addEventListener("keyup", handler);
return pressed;
}
function runAnimation(frameFunc) {
var lastTime = null;
function frame(time) {
var stop = false;
if (lastTime !== null) {
var timeStep = Math.min(time - lastTime, 100) / 1000;
stop = frameFunc(timeStep) === false;
}
lastTime = time;
if (!stop)
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
var arrows = trackKeys(arrowCodes);
function runLevel(level, Display, andThen) {
var display = new Display(document.body, level);
runAnimation(function(step) {
level.animate(step, arrows);
display.drawFrame(step);
if (level.isFinished()) {
display.clear();
if (andThen)
andThen(level.status);
return false;
}
});
}
var lives = function() {
ctx.font = "20px Courier";
ctx.fontFamily = "monospace";
ctx.fillStyle = "#666";
ctx.textAlign = "left";
ctx.textBaseline = "top";
ctx.fillText("Lives left: " + life, 10, 10);
};
function runGame(plans, Display) {
function startLevel(n) {
runLevel(new Level(plans[n]), Display, function(status) {
if (status == "lost") {
startLevel(n);
} else if (n < plans.length - 1)
startLevel(n + 1);
else
alert("You win!");
});
}
startLevel(0);
}
function restart() {
sessionStorage.setItem("reloading", "true");
document.location.reload();
}
runGame(LEVELS, DOMDisplay);
body {
background: #222;
}
h2 {
color: #666;
font-family: monospace;
text-align: center;
}
.background {
table-layout: fixed;
border-spacing: 0;
}
.background td {
padding: 0;
}
.lava, .actor {
background: #e55;
}
.wall {
background: #444;
border: solid 3px #333;
box-sizing: content-box;
}
.actor {
position: absolute;
}
.coin {
background: #e2e838;
border-radius: 50%;
}
.player {
background: #335699;
box-shadow: none;
}
.lost .player {
background: #a04040;
}
.won .player {
background: green;
}
.game {
position: relative;
overflow: hidden;
}
#life, #expo {
font = 20px;
font-family: monospace;
color: #666;
text-align: left;
baseline: top;
margin-left: 30px;
font-weight: bold;
}
input {
margin-left: 30px;
float: right;
position: relative;
top: -30px;
}
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>game</title>
</head>
<body>
<h2>Simple JavaScript Game</h2>
<div id="life"></div>
<div id="expo"></div>
<div>
<input type="button" onclick="restart()" value="Restart"/>
</div>
</body>
</html>
I have already tried some options and quite interesting, was that if I removed all "lava sources" as variants or/and all collisions of those, the counter would work as it should.
Another level, that might get some ideas about my findings, found here.
I will really appreciate any help, Thanks
EDIT::
I would like to mess with the existing code as minimal as possible, if not, I will have to scrap the code and redo most of it (at least that's the only option I saw).
OK, so it seems that what fixed my issue was incorrect placement of the following sentences:
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
The placement of code before:
else if (type == "coin") {
this.actors = this.actors.filter(function(other) {
return other != actor;
});
if (!this.actors.some(function(actor) {
console.log("coin picked up")
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
return actor.type == "coin";
})) {
life += 1;
document.getElementById("life").innerHTML = ("Lives left: " + life);
this.status = "won";
this.finishDelay = 1;
}
The correct placement of the code:
else if (type == "coin") {
expo += 1;
document.getElementById("expo").innerHTML = ("Points: " + expo);
this.actors = this.actors.filter(function(other) {
return other != actor;
});
if (!this.actors.some(function(actor) {
console.log("coin picked up")
return actor.type == "coin";
})) {
life += 1;
document.getElementById("life").innerHTML = ("Lives left: " + life);
this.status = "won";
this.finishDelay = 1;
}
I'm building a program that runs through an matrix (nxn) avoiding collisions with certain obstacles. I'm having trouble implementing a generic algorithm that works for all possible collision situations, the ultimate goal is to go through all the points of the matrix.
The algorithm I built is looping and can not complete the matrix.
Note: The red square can move in any direction (horizontal, vertical and diagonal movements), but only one cell(square) at a time.
var WALL = 0;
var started = false;
var gridSize = 20;
class Agent {
constructor(x, y, charge, cap, distance) {
this.x = x;
this.y = y;
this.charge = charge;
this.cap = cap;
this.distance = distance;
}
}
$(function() {
var $grid = $("#search_grid");
var opts = {
gridSize: 20
};
var grid = new GraphSearch($grid, opts);
//Initializes the agent
$("#btnInit").click(function() {
if (!started) {
var agent = new Agent(0, 0, 100, 50, 0);
agent.initialize();
started = true;
}
});
});
//Initializes the matrix
function GraphSearch($graph, options) {
this.$graph = $graph;
this.opts = options;
this.initialize();
}
//Initializes the matrix
GraphSearch.prototype.initialize = function() {
this.grid = [];
$graph = this.$graph;
$graph.empty();
var cellWidth = ($graph.width() / this.opts.gridSize) - 2,
cellHeight = ($graph.height() / this.opts.gridSize) - 2,
lineHeight = (this.opts.gridSize >= 30 ? "9.px" : ($graph.height() / this.opts.gridSize) - 10 + "px"),
fontSize = (this.opts.gridSize >= 30 ? "10px" : "20px");
$cellTemplate = $("<span />").addClass("grid_item").width(cellWidth).height(cellHeight).css("line-height", lineHeight).css("font-size", fontSize);
for (var x = 0; x < this.opts.gridSize; x++) {
var $row = $("<div class='row' />");
for (var y = 0; y < this.opts.gridSize; y++) {
var id = "cell_" + x + "_" + y,
$cell = $cellTemplate.clone();
$cell.attr("id", id).attr("x", x).attr("y", y);
$row.append($cell);
var isWall = addWall(x, y, this.opts.gridSize);
if (isWall === 1) {
$cell.addClass("wall");
} else {
$cell.addClass('weight1');
}
}
$graph.append($row);
//Fix for stackoverflow snippet
if ($(window).width() < 700) {
$("#search_grid").css("width", "320px");
$("#main").css("width", "38%");
} else {
$("#search_grid").css("width", "300px");
$("#main").css("width", "20%");
}
}
};
//Where will be wall in the matrix
addWall = function(x, y, size) {
var limitPointLeftUp = [2, 3];
var limitPointRightUp = [2, size - 4];
var limitPointLeftDown = [size - 4, 2];
var limitPointRightDown = [size - 4, size - 4];
if ((x == 2 && y == 2) || (x == 2 && y == size - 3)) {
return 1;
}
if ((x == size - 3 && y == 2) || (x == size - 3 && y == size - 3)) {
return 1;
}
if (x >= 2 && (y == 3 && x >= limitPointLeftUp[0] && x <= limitPointLeftDown[0] + 1)) {
return 1;
}
if (x >= 2 && (y == size - 4 && x >= limitPointRightUp[0] && x <= limitPointRightDown[0] + 1)) {
return 1;
}
if ((x == 1 && y == 5) || (x == 9 && y == 17) || (x == 6 && y == 0) || (x == 9 && y == 7) || (x == 15 && y == 0) || (x == 15 && y == 2) || (x == 18 && y == 15)) {
return 1;
}
}
//Initializes the agent
Agent.prototype.initialize = function() {
var agent = this;
var lastDir = "right";
var tryTo = "";
var trying = false;
var right = true;
var up = false;
var down = false;
var left = false;
var timerId = 0;
//Simulates agent movement [Here is my problem]
timerId = setInterval(function() {
RemoveAgent();
var cell = $("#search_grid .row .grid_item[x=" + agent.x + "][y=" + agent.y + "]");
cell.css("background-color", "#e2e2e2");
cell.addClass("agent");
//start direction: right
if (right) {
lastDir = "right";
if (tryTo == "down" && trying) {
if (EmptySqm(agent.x + 1, agent.y)) {
trying = false;
right = false;
down = true;
agent.x++;
}
} else if (tryTo == "up" && trying) {
if (EmptySqm(agent.x - 1, agent.y)) {
trying = false;
right = false;
up = true;
agent.x--;
}
}
if (right) {
//check if is valid sqm
if (ValidSqm(agent.x, agent.y + 1)) {
//go right if empty
if (EmptySqm(agent.x, agent.y + 1)) {
agent.y++;
} else {
right = false;
//check up sqm
if (EmptySqm(agent.x - 1, agent.y)) {
up = true;
trying = true;
}
//check down
else if (EmptySqm(agent.x + 1, agent.y)) {
down = true;
trying = true;
}
}
} else {
agent.x++;
right = false;
left = true;
}
}
//left direction
} else if (left) {
lastDir = "left";
if (tryTo == "down" && trying) {
if (EmptySqm(agent.x + 1, agent.y)) {
trying = false;
left = false;
down = true;
agent.x++;
}
} else if (tryTo == "up" && trying) {
if (EmptySqm(agent.x - 1, agent.y)) {
trying = false;
left = false;
up = true;
agent.x--;
}
}
if (left) {
if (ValidSqm(agent.x, agent.y - 1)) {
if (EmptySqm(agent.x, agent.y - 1)) {
agent.y--;
} else {
left = false;
if (EmptySqm(agent.x + 1, agent.y)) {
down = true;
trying = true;
} else if (EmptySqm(agent.x - 1, agent.y)) {
up = true;
trying = true;
}
}
} else {
agent.x++;
right = true;
left = false;
}
}
//up direction
} else if (up) {
tryTo = "down";
if (lastDir == "left") {
if (EmptySqm(agent.x, agent.y - 1)) {
up = false;
left = true;
agent.y--;
}
} else if (lastDir == "right") {
if (EmptySqm(agent.x, agent.y + 1)) {
up = false;
right = true;
agent.y++;
}
}
if (up) {
if (ValidSqm(agent.x - 1, agent.y)) {
if (EmptySqm(agent.x - 1, agent.y)) {
agent.x--;
} else {
up = false;
//check left sqm
if (EmptySqm(agent.x, agent.y - 1)) {
left = true;
agent.y--;
}
//check right sqm
else if (EmptySqm(agent.x, agent.y + 1)) {
right = true;
agent.y++;
}
//check down sqm
else if (EmptySqm(agent.x + 1, agent.y)) {
down = true;
agent.x++;
}
}
} else {
agent.x++;
up = false;
down = true;
}
}
//down direction
} else if (down) {
tryTo = "up";
if (lastDir == "left") {
if (EmptySqm(agent.x, agent.y - 1)) {
down = false;
left = true;
agent.y--;
}
} else if (lastDir == "right") {
if (EmptySqm(agent.x, agent.y + 1)) {
down = false;
right = true;
agent.y++;
}
}
if (down) {
if (ValidSqm(agent.x + 1, agent.y)) {
if (EmptySqm(agent.x + 1, agent.y)) {
agent.x++;
} else {
down = false;
//check left sqm
if (EmptySqm(agent.x, agent.y - 1)) {
left = true;
agent.y--;
}
//check right sqm
else if (EmptySqm(agent.x, agent.y + 1)) {
right = true;
agent.y++;
}
//check up sqm
else if (EmptySqm(agent.x - 1, agent.y)) {
up = true;
agent.x--;
}
}
} else {
agent.x--;
up = true;
down = false;
}
}
}
}, 100);
var stopInterval = function() {
clearInterval(timerId);
};
};
EmptySqm = function(x, y) {
var bNotWall = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("wall");
return bNotWall;
}
RemoveAgent = function() {
$("#search_grid .row .grid_item").removeClass("agent");
}
ValidSqm = function(x, y) {
return ((x >= 0 && x < gridSize) && (y >= 0 && y < gridSize));
}
html,
body {
height: 100%;
margin: 0;
}
.buttons {
float: right;
position: relative;
right: 10px;
top: 10px;
}
.buttons a {
text-decoration: none;
}
#content {
margin: 0 auto;
width: 98%;
text-align: center;
}
#controls {
text-align: center;
margin-bottom: 25px;
padding: 5px;
}
#search_grid {
width: 320px;
height: 300px;
position: relative;
}
#main {
margin: auto;
width: 20%;
}
.grid_item {
display: block;
border: 1px solid #bbb;
float: left;
line-height: 12px;
font-size: 10px;
}
.grid_item.wall {
background-color: #000000;
}
.grid_item.weight1 {
background-color: #ffffff;
}
.agent {
text-align: center;
color: grey;
font-size: 20px;
background-color: red !important;
color: blue;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="content">
<input type="button" id="btnInit" value="Start" /><br><br>
<div id="main">
<div id="search_grid">Loading...</div>
</div>
</div>
<div id="footer"></div>
</body>
I solved my problem with the help of the a* algorithm, more specifically this implementation, the deviation of the obstacles is done with the move method, which returns the path to a certain cell
path = grid.move(currentCell, endCell);
var agentSpeed = 10;
var WALL = 0;
var started = false;
var gridSize = 20;
var x = 0;
var y = 0;
var runsSameLine = false;
class Agent {
constructor(x, y, charge, cap, distance) {
this.x = x;
this.y = y;
this.charge = charge;
this.cap = cap;
this.distance = distance;
}
}
$(function() {
var $grid = $("#search_grid");
var opts = {
gridSize: gridSize
};
var grid = new GraphSearch($grid, opts, astar.search);
//Initializes the agent
$("#btnInit").click(function() {
if (!started) {
var agent = new Agent(0, 0, 100, 50, 0);
agent.initialize();
started = true;
}
});
});
//Initializes the matrix
function GraphSearch($graph, options, implementation) {
this.$graph = $graph;
this.search = implementation;
this.opts = options;
this.initialize();
}
var grid;
GraphSearch.prototype.move = function($start, $end) {
var end = this.nodeFromElement($end);
if ($end.hasClass("wall")) {
return;
}
var start = this.nodeFromElement($start);
var path = this.search(this.graph.nodes, start, end, true);
if (!path || path.length == 0) {
//this.animateNoPath();
} else {
return path;
}
};
GraphSearch.prototype.nodeFromElement = function($cell) {
return this.graph.nodes[parseInt($cell.attr("x"))][parseInt($cell.attr("y"))];
};
//Initializes the matrix
GraphSearch.prototype.initialize = function() {
this.grid = [];
var self = this,
nodes = [],
$graph = this.$graph;
$graph.empty();
var cellWidth = ($graph.width() / this.opts.gridSize) - 2,
cellHeight = ($graph.height() / this.opts.gridSize) - 2,
lineHeight = (this.opts.gridSize >= 30 ? "9.px" : ($graph.height() / this.opts.gridSize) - 10 + "px"),
fontSize = (this.opts.gridSize >= 30 ? "10px" : "20px");
$cellTemplate = $("<span />").addClass("grid_item").width(cellWidth).height(cellHeight).css("line-height", lineHeight).css("font-size", fontSize);
for (var x = 0; x < this.opts.gridSize; x++) {
var $row = $("<div class='row' />");
nodeRow = [],
gridRow = [];
for (var y = 0; y < this.opts.gridSize; y++) {
var id = "cell_" + x + "_" + y,
$cell = $cellTemplate.clone();
$cell.attr("id", id).attr("x", x).attr("y", y);
$row.append($cell);
gridRow.push($cell);
var isWall = addWall(x, y, this.opts.gridSize);
if (isWall === 1) {
$cell.addClass("wall");
nodeRow.push(1);
} else {
$cell.addClass('weight1');
nodeRow.push(0);
}
}
$graph.append($row);
this.grid.push(gridRow);
nodes.push(nodeRow);
//Fix for stackoverflow snippet
if ($(window).width() < 700) {
$("#search_grid").css("width", "320px");
$("#main").css("width", "38%");
} else {
$("#search_grid").css("width", "300px");
$("#main").css("width", "20%");
}
}
this.graph = new Graph(nodes);
this.$cells = $graph.find(".grid_item");
grid = this;
};
//Where will be wall in the matrix
addWall = function(x, y, size) {
var limitPointLeftUp = [2, 3];
var limitPointRightUp = [2, size - 4];
var limitPointLeftDown = [size - 4, 2];
var limitPointRightDown = [size - 4, size - 4];
if ((x == 2 && y == 2) || (x == 2 && y == size - 3)) {
return 1;
}
if ((x == size - 3 && y == 2) || (x == size - 3 && y == size - 3)) {
return 1;
}
if (x >= 2 && (y == 3 && x >= limitPointLeftUp[0] && x <= limitPointLeftDown[0] + 1)) {
return 1;
}
if (x >= 2 && (y == size - 4 && x >= limitPointRightUp[0] && x <= limitPointRightDown[0] + 1)) {
return 1;
}
if ((x == 1 && y == 5) || (x == 9 && y == 17) || (x == 6 && y == 0) || (x == 9 && y == 7) || (x == 15 && y == 0) || (x == 15 && y == 2) || (x == 18 && y == 15)) {
return 1;
}
}
//Initializes the agent
Agent.prototype.initialize = function() {
var agent = this;
var goToLeft = false;
var goToRight = true;
var rightLimit = gridSize - 1;
var leftLimit = 0;
var lastPos = 0;
var path = [];
var completedPath = true;
timerId = setInterval(function() {
agent.x = x;
agent.y = y;
currentCell = $("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]");
currentCell.css("background-color", "#e2e2e2");
if (agent.x == gridSize - 1 && agent.y == 0) {
stopInterval(timerId);
return false;
}
if (goToRight && y == rightLimit) {
if (runsSameLine) {
goToLeft = true;
goToRight = false;
runsSameLine = false;
} else {
if (FreeCell((x + 1), y)) {
endCell = $("#search_grid .row .grid_item[x=" + (x + 1) + "][y=" + y + "]");
x++;
goToLeft = true;
goToRight = false;
} else {
endCell = FindNextFreeCell(x, y, "limDir");
goToLeft = true;
goToRight = false;
}
}
} else if (goToLeft && y == leftLimit) {
if (runsSameLine) {
goToLeft = false;
goToRight = true;
runsSameLine = false;
} else {
if (FreeCell((x + 1), y)) {
endCell = $("#search_grid .row .grid_item[x=" + (x + 1) + "][y=" + y + "]");
x++;
goToLeft = false;
goToRight = true;
} else {
endCell = FindNextFreeCell(x, y, "limEsq");
goToLeft = false;
goToRight = true;
}
}
} else if (goToRight) {
if (FreeCell(x, (y + 1))) {
endCell = $("#search_grid .row .grid_item[x=" + x + "][y=" + (y + 1) + "]");
y++;
} else {
endCell = FindNextFreeCell(x, y, "dir");
}
} else if (goToLeft) {
if (FreeCell(x, (y - 1))) {
endCell = $("#search_grid .row .grid_item[x=" + x + "][y=" + (y - 1) + "]");
y--;
} else {
endCell = FindNextFreeCell(x, y, "esq");
}
}
if (completedPath) {
path = grid.move(currentCell, endCell);
}
if (path) {
if (lastPos == path.length - 1) {
completedPath = true;
}
if (path.length > 1 && lastPos < path.length && lastPos != path.length - 1) {
x = path[lastPos].x;
y = path[lastPos].y;
lastPos++;
completedPath = false;
} else if (completedPath) {
x = path[lastPos].x;
y = path[lastPos].y;
lastPos = 0;
}
}
grid.$cells.removeClass("agent");
$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").addClass("agent");
}, agentSpeed);
var stopInterval = function() {
clearInterval(timerId);
};
};
FindNextFreeCell = function(x, y, dir) {
if (dir == "limDir") {
if (x != gridSize) {
for (var y = y; y >= 0; y--) {
if (FreeCell((x + 1), y)) {
return getCell((x + 1), y);
}
}
}
} else if (dir == "limEsq") {
if (x != gridSize) {
for (var y = y; y <= gridSize; y++) {
if (FreeCell((x + 1), y)) {
return getCell((x + 1), y);
}
}
}
} else if (dir == "dir") {
for (var y = y; y < gridSize - 1; y++) {
if (FreeCell(x, (y + 1))) {
return getCell(x, (y + 1));
}
}
for (var x = x; x <= gridSize - 1; x++) {
if (FreeCell((x + 1), y)) {
runsSameLine = true;
return getCell((x + 1), y);
}
}
} else if (dir == "esq") {
for (var y = y; y > 0; y--) {
if (FreeCell(x, (y - 1))) {
return getCell(x, (y - 1));
}
}
for (var x = x; x <= gridSize - 1; x++) {
if (FreeCell((x + 1), y)) {
runsSameLine = true;
return getCell((x + 1), y);
}
}
}
}
EmptySqm = function(x, y) {
var bNotWall = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("wall");
return bNotWall;
}
getCell = function(x, y) {
return $("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]");
}
FreeCell = function(x, y) {
var bNaoTemParede = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("wall");
var bNaoTemLixeira = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("lixeira");
var bNaoTemRecarga = !$("#search_grid .row .grid_item[x=" + x + "][y=" + y + "]").hasClass("pontoRecarga");
return bNaoTemParede && bNaoTemLixeira && bNaoTemRecarga;
}
ValidSqm = function(x, y) {
return ((x >= 0 && x < gridSize) && (y >= 0 && y < gridSize));
}
// javascript-astar
// http://github.com/bgrins/javascript-astar
// Freely distributable under the MIT License.
// Implements the astar search algorithm in javascript using a binary heap.
var astar = {
init: function(grid) {
for (var x = 0, xl = grid.length; x < xl; x++) {
for (var y = 0, yl = grid[x].length; y < yl; y++) {
var node = grid[x][y];
node.f = 0;
node.g = 0;
node.h = 0;
node.cost = node.type;
node.visited = false;
node.closed = false;
node.parent = null;
}
}
},
heap: function() {
return new BinaryHeap(function(node) {
return node.f;
});
},
search: function(grid, start, end, diagonal, heuristic) {
astar.init(grid);
heuristic = heuristic || astar.manhattan;
diagonal = !!diagonal;
var openHeap = astar.heap();
openHeap.push(start);
while (openHeap.size() > 0) {
// Grab the lowest f(x) to process next. Heap keeps this sorted for us.
var currentNode = openHeap.pop();
// End case -- result has been found, return the traced path.
if (currentNode === end) {
var curr = currentNode;
var ret = [];
while (curr.parent) {
ret.push(curr);
curr = curr.parent;
}
return ret.reverse();
}
// Normal case -- move currentNode from open to closed, process each of its neighbors.
currentNode.closed = true;
// Find all neighbors for the current node. Optionally find diagonal neighbors as well (false by default).
var neighbors = astar.neighbors(grid, currentNode, diagonal);
for (var i = 0, il = neighbors.length; i < il; i++) {
var neighbor = neighbors[i];
if (neighbor.closed || neighbor.isWall() || $("#search_grid .row .grid_item[x=" + neighbor.x + "][y=" + neighbor.y + "]").hasClass("pontoRecarga") || $("#search_grid .row .grid_item[x=" + neighbor.x + "][y=" + neighbor.y + "]").hasClass("lixeira")) {
// Not a valid node to process, skip to next neighbor.
continue;
}
// The g score is the shortest distance from start to current node.
// We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
var gScore = currentNode.g + neighbor.cost;
var beenVisited = neighbor.visited;
if (!beenVisited || gScore < neighbor.g) {
// Found an optimal (so far) path to this node. Take score for node to see how good it is.
neighbor.visited = true;
neighbor.parent = currentNode;
neighbor.h = neighbor.h || heuristic(neighbor.pos, end.pos);
neighbor.g = gScore;
neighbor.f = neighbor.g + neighbor.h;
if (!beenVisited) {
// Pushing to heap will put it in proper place based on the 'f' value.
openHeap.push(neighbor);
} else {
// Already seen the node, but since it has been rescored we need to reorder it in the heap
openHeap.rescoreElement(neighbor);
}
}
}
}
// No result was found - empty array signifies failure to find path.
return [];
},
manhattan: function(pos0, pos1) {
// See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
var d1 = Math.abs(pos1.x - pos0.x);
var d2 = Math.abs(pos1.y - pos0.y);
return d1 + d2;
},
neighbors: function(grid, node, diagonals) {
var ret = [];
var x = node.x;
var y = node.y;
// West
if (grid[x - 1] && grid[x - 1][y]) {
ret.push(grid[x - 1][y]);
}
// East
if (grid[x + 1] && grid[x + 1][y]) {
ret.push(grid[x + 1][y]);
}
// South
if (grid[x] && grid[x][y - 1]) {
ret.push(grid[x][y - 1]);
}
// North
if (grid[x] && grid[x][y + 1]) {
ret.push(grid[x][y + 1]);
}
if (diagonals) {
// Southwest
if (grid[x - 1] && grid[x - 1][y - 1]) {
ret.push(grid[x - 1][y - 1]);
}
// Southeast
if (grid[x + 1] && grid[x + 1][y - 1]) {
ret.push(grid[x + 1][y - 1]);
}
// Northwest
if (grid[x - 1] && grid[x - 1][y + 1]) {
ret.push(grid[x - 1][y + 1]);
}
// Northeast
if (grid[x + 1] && grid[x + 1][y + 1]) {
ret.push(grid[x + 1][y + 1]);
}
}
return ret;
}
};
// javascript-astar
// http://github.com/bgrins/javascript-astar
// Freely distributable under the MIT License.
// Includes Binary Heap (with modifications) from Marijn Haverbeke.
// http://eloquentjavascript.net/appendix2.html
var GraphNodeType = {
OPEN: 0,
WALL: 1
};
// Creates a Graph class used in the astar search algorithm.
function Graph(grid) {
var nodes = [];
for (var x = 0; x < grid.length; x++) {
nodes[x] = [];
for (var y = 0, row = grid[x]; y < row.length; y++) {
nodes[x][y] = new GraphNode(x, y, row[y]);
}
}
this.input = grid;
this.nodes = nodes;
}
Graph.prototype.toString = function() {
var graphString = "\n";
var nodes = this.nodes;
var rowDebug, row, y, l;
for (var x = 0, len = nodes.length; x < len; x++) {
rowDebug = "";
row = nodes[x];
for (y = 0, l = row.length; y < l; y++) {
rowDebug += row[y].type + " ";
}
graphString = graphString + rowDebug + "\n";
}
return graphString;
};
function GraphNode(x, y, type) {
this.data = {};
this.x = x;
this.y = y;
this.pos = {
x: x,
y: y
};
this.type = type;
}
GraphNode.prototype.toString = function() {
return "[" + this.x + " " + this.y + "]";
};
GraphNode.prototype.isWall = function() {
return this.type === GraphNodeType.WALL;
};
function BinaryHeap(scoreFunction) {
this.content = [];
this.scoreFunction = scoreFunction;
}
BinaryHeap.prototype = {
push: function(element) {
// Add the new element to the end of the array.
this.content.push(element);
// Allow it to sink down.
this.sinkDown(this.content.length - 1);
},
pop: function() {
// Store the first element so we can return it later.
var result = this.content[0];
// Get the element at the end of the array.
var end = this.content.pop();
// If there are any elements left, put the end element at the
// start, and let it bubble up.
if (this.content.length > 0) {
this.content[0] = end;
this.bubbleUp(0);
}
return result;
},
remove: function(node) {
var i = this.content.indexOf(node);
// When it is found, the process seen in 'pop' is repeated
// to fill up the hole.
var end = this.content.pop();
if (i !== this.content.length - 1) {
this.content[i] = end;
if (this.scoreFunction(end) < this.scoreFunction(node)) {
this.sinkDown(i);
} else {
this.bubbleUp(i);
}
}
},
size: function() {
return this.content.length;
},
rescoreElement: function(node) {
this.sinkDown(this.content.indexOf(node));
},
sinkDown: function(n) {
// Fetch the element that has to be sunk.
var element = this.content[n];
// When at 0, an element can not sink any further.
while (n > 0) {
// Compute the parent element's index, and fetch it.
var parentN = ((n + 1) >> 1) - 1,
parent = this.content[parentN];
// Swap the elements if the parent is greater.
if (this.scoreFunction(element) < this.scoreFunction(parent)) {
this.content[parentN] = element;
this.content[n] = parent;
// Update 'n' to continue at the new position.
n = parentN;
}
// Found a parent that is less, no need to sink any further.
else {
break;
}
}
},
bubbleUp: function(n) {
// Look up the target element and its score.
var length = this.content.length,
element = this.content[n],
elemScore = this.scoreFunction(element);
while (true) {
// Compute the indices of the child elements.
var child2N = (n + 1) << 1,
child1N = child2N - 1;
// This is used to store the new position of the element,
// if any.
var swap = null;
var child1Score;
// If the first child exists (is inside the array)...
if (child1N < length) {
// Look it up and compute its score.
var child1 = this.content[child1N];
child1Score = this.scoreFunction(child1);
// If the score is less than our element's, we need to swap.
if (child1Score < elemScore) {
swap = child1N;
}
}
// Do the same checks for the other child.
if (child2N < length) {
var child2 = this.content[child2N],
child2Score = this.scoreFunction(child2);
if (child2Score < (swap === null ? elemScore : child1Score)) {
swap = child2N;
}
}
// If the element needs to be moved, swap it, and continue.
if (swap !== null) {
this.content[n] = this.content[swap];
this.content[swap] = element;
n = swap;
}
// Otherwise, we are done.
else {
break;
}
}
}
};
html,
body {
height: 100%;
margin: 0;
}
.buttons {
float: right;
position: relative;
right: 10px;
top: 10px;
}
.buttons a {
text-decoration: none;
}
#content {
margin: 0 auto;
width: 98%;
text-align: center;
}
#controls {
text-align: center;
margin-bottom: 25px;
padding: 5px;
}
#search_grid {
width: 300px;
height: 300px;
position: relative;
}
#main {
margin: auto;
width: 20%;
}
.grid_item {
display: block;
border: 1px solid #bbb;
float: left;
line-height: 12px;
font-size: 10px;
}
.grid_item.wall {
background-color: #000000;
}
.grid_item.weight1 {
background-color: #ffffff;
}
.agent {
text-align: center;
color: grey;
font-size: 20px;
background-color: red !important;
color: blue;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="content">
<input type="button" id="btnInit" value="Start" /><br><br>
<div id="main">
<div id="search_grid">Loading...</div>
</div>
</div>
<div id="footer"></div>
</body>
I have another question. When I move the main player image Left or Right it moves great except for when you are moving right and then you hurry and press the left key while the right key is still down, the image stops for a second and then it decides to move left. Vise-Versa.
Main.js
var getPlatF1POSX = 0;
var getPlatF1POSY = 0;
var addVar = 0;
var addVar2 = 0;
var addVar3 = 0;
function loadGame() {
document.getElementsByTagName("DIV")[4].style.visibility = "visible";
addEventListener('mousemove', getData, false);
addEventListener('keydown', movePlayer, false);
addEventListener('keyup', stopPlayer, false);
movePlat();
moveP();
document.getElementById("player").style.left = xPos + "px";
document.getElementById("player").style.top = yPos + "px";
}
function getData(gData) {
}
var thisThis = 1;
var moveBlock1 = 350;
var stLandT = 0;
var gPos = "";
var rightPos = false;
var leftPos = false;
function movePlat() {
}
function movePlayer(mPlayer) {
switch (mPlayer.keyCode) {
case 39: // RIGHT
if (stLandT == 0 && gPos == "" && rightPos == false) {
setThis = setTimeout(landT, 500);
thisSet = setTimeout(moveLand, 30);
stLandT = 1;
}
gPos = "RIGHT";
rightPos = true;
leftPos = false;
break;
case 37: // LEFT
if (stLandT == 0 && gPos == "" && leftPos == false) {
setThis = setTimeout(landT, 500);
thisSet = setTimeout(moveLand, 30);
stLandT = 1;
}
gPos = "LEFT";
rightPos = false;
leftPos = true;
break;
case 38: // UP
break;
case 40: // DOWN
break;
}
}
function stopPlayer(sPlayer) {
switch (sPlayer.keyCode) {
case 39:
clearTimeout(setThis);
clearTimeout(thisSet);
stLandT = 0;
gPos = "";
rightPos = false;
leftPos = false;
break;
case 37:
clearTimeout(setThis);
clearTimeout(thisSet);
stLandT = 0;
gPos = "";
rightPos = false;
leftPos = false;
break;
}
}
Move Land And Player
var cTAdd = 0;
var setThis = 1;
var GAPlayer = 3;
function landT() {
setThis = setTimeout(landT, 500);
if (xPos >= 500) {
cTAdd = Math.floor(Math.random() * 100 + 1);
var block00 = document.createElement("img");
if (cTAdd > 0 && cTAdd < 25) {
block00.src = "images/sep2.png";
}
if (cTAdd > 25 && cTAdd < 50) {
block00.src = "images/sep1.png";
}
if (cTAdd > 50 && cTAdd < 100) {
block00.src = "images/platform00.png";
}
document.getElementById("land01").appendChild(block00);
var block01 = document.createElement("img");
var getB = block01.getBoundingClientRect();
if (cTAdd > 0 && cTAdd < 25) {
block01.src = "images/platform00.png";
}
if (cTAdd > 25 && cTAdd < 50) {
block01.src = "images/sep2.png";
}
if (cTAdd > 50 && cTAdd < 100) {
block01.src = "images/sep1.png";
}
document.getElementById("land00").appendChild(block01);
GAPlayer = GAPlayer + 2;
}
}
var thisSet = 1;
var cPlayer = 0;
var moveSpeed = 5;
var xPos = 50;
var yPos = 300;
function moveLand() {
thisSet = setTimeout(moveLand, 30);
if (xPos >= 500) {
moveBlock1 = moveBlock1 - 10;
document.getElementById("land00").style.left = moveBlock1 + "px";
document.getElementById("land01").style.left = moveBlock1 + "px";
}
cPlayer++;
if (cPlayer >= 4)
cPlayer = 0;
document.images[GAPlayer].src = gPlayer[cPlayer].src;
}
function moveP() {
var setThis = setTimeout(moveP, 10);
if (leftPos == false) {
xPos = xPos + moveSpeed;
}
if (rightPos == false) {
xPos = xPos - moveSpeed;
}
document.getElementById("player").style.left = xPos + "px";
document.getElementById("player").style.top = yPos + "px";
if (xPos >= 500) {
xPos = 500;
}
if (xPos <= 50) {
xPos = 50;
}
}
This is because you stop your player no matter what key is up. You should store what key down is last pressed, than on key up you need to check if last key is released.
It was hard to me to debug your code, so I made it in jQuery (and had same troubles as you had):
var game = {
settings: {
moveSpeed: 5, // 5 milliseconds, 200fps
moveBy: 2 // 2 pixels
},
land: null,
landWidth: null,
landLeft: null,
viewport: null,
viewportWidth: null,
landMinLeft: null,
init: function() {
game.land = $('.land');
game.landWidth = game.land.width();
game.landLeft = game.land.position().left;
game.viewport = $('.viewport');
game.viewportWidth = game.viewport.width();
game.landMinLeft = -(game.landWidth-game.viewportWidth);
},
movingInterval: null,
lastKey: null,
keyDown: function (e) {
switch (e.keyCode) {
case 39: // RIGHT
game.lastKey = e.keyCode;
clearInterval( game.movingInterval );
game.movingInterval = setInterval( function() {
game.move('right');
}, game.settings.moveSpeed);
break;
case 37: // LEFT
game.lastKey = e.keyCode;
clearInterval( game.movingInterval );
game.movingInterval = setInterval( function() {
game.move('left');
}, game.settings.moveSpeed);
break;
}
},
keyUp: function(e) {
if( e.keyCode==game.lastKey ) {
game.stopMoving();
};
},
move: function( direction ) {
switch( direction ) {
case 'left':
var newLeft = game.land.position().left+game.settings.moveBy;
if( newLeft>0 ) newLeft=0;
game.land.css({
'left': newLeft+'px'
});
break;
case 'right':
var newLeft = game.land.position().left-game.settings.moveBy;
if( newLeft<game.landMinLeft ) newLeft=game.landMinLeft;
game.land.css({
'left': newLeft+'px'
});
break;
};
},
stopMoving: function() {
clearInterval( game.movingInterval );
}
};
game.init();
$(window).on('keydown', game.keyDown);
$(window).on('keyup', game.keyUp);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body, html, .viewport {
width: 100%;
height: 100%;
}
.viewport {
position: relative;
overflow: hidden;
}
.land {
position: absolute;
left: 0;
top: 0;
width: 2300px;
height: 200px;
background: url('//dummyimage.com/2300x400/000/fff&text=Mario+is+great!+Mario+is+our+hero!+We+love+you+mario!') no-repeat center center;
background-size: cover;
will-change: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="viewport">
<div class="land"></div>
</div>
Also on Playground.
This is how you do it in Javascript/HTML5
theGame.js
var getPlatF1POSX = 0;
var getPlatF1POSY = 0;
var addVar = 0;
var addVar2 = 0;
var addVar3 = 0;
var thisThis = 1;
var moveBlock1 = 350;
var stLandT = 0;
var moveRight = false;
var moveLeft = false;
var movePL = 0;
var movePR = 0;
//////////////////////////////////////////////////////////
//
// LOAD PLATFORMS/SET KEY UP AND DOWN/SET PLAYER POS
function loadGame() {
document.getElementsByTagName("DIV")[4].style.visibility = "visible";
addEventListener('mousemove', getData, false);
addEventListener('keydown', movePlayer, false);
addEventListener('keyup', stopPlayer, false);
moveP();
document.getElementById("player").style.left = xPos + "px";
document.getElementById("player").style.top = yPos + "px";
}
function getData(gData) {
}
//////////////////////////////////////////////////////////
//
// KEY DOWN TO MOVE PLAYER
function movePlayer(mPlayer) {
switch (mPlayer.keyCode) {
case 39: // RIGHT
if (stLandT == 0) {
setThis = setTimeout(landT, 500);
thisSet = setTimeout(moveLand, 30);
stLandT = 1;
}
movePL = 0;
movePR = 1;
break;
case 37: // LEFT
if (stLandT == 0) {
setThis = setTimeout(landT, 500);
thisSet = setTimeout(moveLand, 30);
stLandT = 1;
}
movePL = 1;
movePR = 0;
break;
case 38: // UP
break;
case 40: // DOWN
break;
}
}
//////////////////////////////////////////////////////////
//
// KEY UP TO STOP PLAYER/VOID STOP AND GO GLITCH
function stopPlayer(sPlayer) {
if (sPlayer.keyCode == 39) {
clearTimeout(setThis);
clearTimeout(thisSet);
stLandT = 0;
movePR = 0;
}
if (sPlayer.keyCode == 37) {
clearTimeout(setThis);
clearTimeout(thisSet);
stLandT = 0;
movePL = 0;
}
}
landThis.js/ MOVE PLAYER AND PLATFORMS
var cTAdd = 0;
var setThis = 1;
var GAPlayer = 3;
//////////////////////////////////////////////////////////
//
// SHOW PLATFORMS TO MOVE
function landT() {
setThis = setTimeout(landT, 500);
if (xPos >= 500) {
cTAdd = Math.floor(Math.random() * 100 + 1);
var block00 = document.createElement("img");
if (cTAdd > 0 && cTAdd < 25) {
block00.src = "images/sep2.png";
}
if (cTAdd > 25 && cTAdd < 50) {
block00.src = "images/sep1.png";
}
if (cTAdd > 50 && cTAdd < 100) {
block00.src = "images/platform00.png";
}
document.getElementById("land01").appendChild(block00);
var block01 = document.createElement("img");
var getB = block01.getBoundingClientRect();
if (cTAdd > 0 && cTAdd < 25) {
block01.src = "images/platform00.png";
}
if (cTAdd > 25 && cTAdd < 50) {
block01.src = "images/sep2.png";
}
if (cTAdd > 50 && cTAdd < 100) {
block01.src = "images/sep1.png";
}
document.getElementById("land00").appendChild(block01);
GAPlayer = GAPlayer + 2;
}
}
//////////////////////////////////////////////////////////
//
// MOVE PLATFORMS
var thisSet = 1;
var cPlayer = 0;
var moveSpeed = 5;
var xPos = 50;
var yPos = 300;
function moveLand() {
thisSet = setTimeout(moveLand, 30);
if (xPos >= 500) {
moveBlock1 = moveBlock1 - 10;
document.getElementById("land00").style.left = moveBlock1 + "px";
document.getElementById("land01").style.left = moveBlock1 + "px";
}
}
//////////////////////////////////////////////////////////
//
// MOVE PLAYER
var setP = 1;
function moveP() {
setP = setTimeout(moveP, 10);
if (movePR == 1) {
xPos = xPos + moveSpeed;
cPlayer++;
if (cPlayer >= 4)
cPlayer = 0;
document.images[GAPlayer].src = gPlayer[cPlayer].src;
}
if (movePL == 1) {
xPos = xPos - moveSpeed;
cPlayer++;
if (cPlayer >= 4)
cPlayer = 0;
document.images[GAPlayer].src = gPlayer[cPlayer].src;
}
document.getElementById("player").style.left = xPos + "px";
document.getElementById("player").style.top = yPos + "px";
if (xPos >= 500) {
xPos = 500;
}
if (xPos <= 50) {
xPos = 50;
}
}