Some time ago I wrote this fun grid that is offset to look like a hexagonal grid. I added a feature to check if any two points are touching. I thought it was brilliant until I finally found a bug and am not sure how to correct it.
If you press (1,4) and (2,5) you will see that they are "touching" but they shouldn't be. What logic am I missing in the function isTouchingHex?
Found bugs so far..
(1,4) & (2,5)
(2,2) & (3,3)
(0,0) & (1,1)
CodePen
var $point1 = $('#point1');
var $point2 = $('#point2');
var $dx = $('#dx');
var $dy = $('#dy');
var $isTouching = $('#isTouching');
var $table = $('#hexTable');
var table = [];
var rows = 10;
var cols = 5;
var point1 = null;
var point2 = null;
var clickedCount = 0;
for (let y = 0; y < rows; y++) {
var $tr = $('<div class="hexagonRow"></div>')
var row = [];
for (let x = 0; x < cols; x++) {
var $col = $('<div class="hexagon"><div class="hexagonText">' + x + ',' + y + '</div></div>');
$col.click(function() {
if (clickPoint(x, y))
$(this).toggleClass('clicked');
});
$tr.append($col);
row.push(y);
}
$table.append($tr);
table.push(row);
}
function clickPoint(x, y) {
var clicked = true;
if (point1 && point1[0] === x && point1[1] === y) {
point1 = null;
clickedCount--;
} else if (point2 && point2[0] === x && point2[1] === y) {
point2 = null;
clickedCount--;
} else if (!point1 && clickedCount < 3) {
point1 = [x, y];
clickedCount++;
} else if (!point2 && clickedCount < 3) {
point2 = [x, y];
clickedCount++;
} else {
clicked = false;
}
updateDisplay();
return clicked;
}
function updateDisplay() {
if (point1)
$point1.html(point1[0] + ',' + point1[1])
else
$point1.html('');
if (point2)
$point2.html(point2[0] + ',' + point2[1])
else
$point2.html('');
if (point1 && point2) {
var touching = isTouchingHex(point1[0], point1[1], point2[0], point2[1]) ? 'true' : 'false';
$isTouching.html(touching)
} else {
$isTouching.html('');
}
}
function isTouchingHex(x1, y1, x2, y2) {
var deltaX = x1 - x2;
var deltaY = y1 - y2;
$dx.html(deltaX);
$dy.html(deltaY);
// check same x
if (deltaX === 0) {
switch (deltaY) {
// check bottom left
case -1:
// check bottom
case -2:
// check top right
case 1:
// check above
case 2:
return true;
break;
}
} else if (deltaX === 1) {
switch (deltaY) {
// check bottom left
case -1:
// check top left
case 1:
return true;
break;
}
}else if (deltaX === -1) {
switch (deltaY) {
// check bottom right
case -1:
return true;
break;
}
}
return false;
}
Related
I'm creating a game where the user wanders around a cemetery and collects stories from different graves. It's a classic top-down game. I'm building a script where if the user walks into a grave their movement stops, but I'm having trouble setting up collisions. I am using jQuery. Here is what I have so far:
var position = -1;
var $char = $('#char');
var keyCode = null;
var fired = false;
var $stones = $('.stones div');
var collision = null;
document.onkeydown = function(e) {
keyCode = e.which || e.keyCode;
if (!fired) {
position = -1;
fired = true;
switch (keyCode) {
case 38: position = 0; break; //up
case 40: position = 1; break; //down
case 37: position = 2; break; //left
case 39: position = 3; break; //right
}
walking();
stepping = setInterval(walking,125);
}
};
document.onkeyup = function(e) {
//standing
clearInterval(stepping);
stepping = 0;
fired = false;
};
function walking() {
$stones.each(function() { //check all the stones...
collision = collision($(this), $char, position); ...for collisions
if (collision) { //if any, then break loop
return false;
}
});
if (!collision) { //check if there was a collision
//if no collision, keep walking x direction
}
function collision($el, $charEl, position) {
var $el = $el[0].getBoundingClientRect();
var $charEl = $charEl[0].getBoundingClientRect();
var elBottom = parseInt($el.bottom);
var elRight = parseInt($el.right);
var elLeft = parseInt($el.left);
var elTop = parseInt($el.top);
var charBottom = parseInt($charEl.bottom);
var charRight = parseInt($charEl.right);
var charLeft = parseInt($charEl.left);
var charTop = parseInt($charEl.top);
//this is where I'm stuck
}
}
I've tried various different codes, but nothing seems to work. I keep having an issue where if I'm going forward and then I bump into a headstone and I turn around, I'm stuck. Here's an example code of what I mean:
if (position == 0 &&
!(elTop > charBottom ||
elBottom < charTop ||
elRight < charLeft + 1 ||
elLeft > charRight - 1)
) {
return true;
}
if (position == 1 &&
!(elTop > charBottom ||
elBottom < charTop ||
elRight < charLeft + 1 ||
elLeft > charRight - 1)
) {
return true;
}
return false;
I have looked this question and this question and this question and so far I'm not having any luck. Can somebody help me with the logic or supply an example code of what I need to do?
Thank you.
Your game is looking good man!
I recently wrote some collision detection and had the exact same problem. The issue is that once your coordinates are true of the case of the collision then they will always be true on any other movement.
You need to store the previous position your character was in and revert back to it OR perform the check before you change your characters coordinates.
I managed to find the following solution, thanks to stwitz' about idea, as well as this script: https://magently.com/blog/detecting-a-jquery-collision-part-iv/
var position = -1;
var $char = $('#char');
var keyCode = null;
var fired = false;
var stepSize = 32;
var $stones = $('.stones div');
//new
var cancelTop = cancelRight = cancelLeft = cancelBottom = false;
var charEl = $char[0].getBoundingClientRect();
var charLeft = parseInt(charEl.left);
var charRight = parseInt(charEl.right);
var charTop = parseInt(charEl.top);
var charBottom = parseInt(charEl.bottom);
function walking() {
if (position == 0 && !cancelTop) {
//if moving up & is safe to move up
} else if (position == 1 && !cancelBottom) {
//if moving down & is safe to move down
} else if (position == 2 && !cancelLeft) {
//if moving left and is safe to move left
} else if (position == 3 && !cancelRight) {
//if moving right and is safe to move right
}
cancelTop = cancelRight = cancelLeft = cancelBottom = false; //mark all as safe until we check
$stones.each(function() {
collision($(this));
});
}
document.onkeydown = function(e) {
keyCode = e.which || e.keyCode;
if (!fired) {
position = -1;
fired = true;
switch (keyCode) {
case 38: position = 0; break; //up
case 40: position = 1; break; //down
case 37: position = 2; break; //left
case 39: position = 3; break; //right
}
walking();
stepping = setInterval(walking,125);
}
};
document.onkeyup = function(e) {
//standing
clearInterval(stepping);
stepping = 0;
fired = false;
};
function collision($el) {
var el = $el[0].getBoundingClientRect();
var elBottom = parseInt(el.bottom);
var elRight = parseInt(el.right);
var elLeft = parseInt(el.left);
var elTop = parseInt(el.top);
if (
(elRight == charLeft) &&
(elBottom - stepSize >= charBottom && charBottom >= elTop + stepSize)
) {
cancelLeft = true;
return true;
}
if (
(elLeft == charRight) &&
(elBottom - stepSize >= charBottom && charBottom >= elTop + stepSize)
) {
cancelRight = true;
return true;
}
if (
(elTop + stepSize > charBottom) &&
(elTop <= charBottom) &&
(elLeft < charRight) &&
(elRight > charLeft)
)
{
cancelBottom = true;
return true;
}
if (
(elBottom - stepSize < charTop) &&
(elBottom >= charTop) &&
(elLeft < charRight) &&
(elRight > charLeft)
)
{
cancelTop = true;
return true;
}
return false;
}
We are using createJS and right now I am struggling with a hit test.
I get this error:
"ss.js:203 Uncaught TypeError: Cannot read property 'x' of undefined
at hitTest (ss.js:203)
at doCollisionChecking (ss.js:215)
at heartBeat (ss.js:238)
at Function.b._dispatchEvent (createjs-2015.11.26.min.js:12)
at Function.b.dispatchEvent (createjs-2015.11.26.min.js:12)
at Function.a._tick (createjs-2015.11.26.min.js:12)
at a._handleTimeout (createjs-2015.11.26.min.js:12)"
I think the problem has to the with the 2 objects x position, but one is a player controlled character and the other object have random x value.
All the hit test example i found always consist of a static object and a moving, but this time they are both moving and i have no idea what to do.
var stage, hero, queue, circle, coin;
var coins = [];
var Score, tekst1, tekst2;
var speed = 3;
var keys = {
u: false,
d: false,
l: false,
r: false
};
var settings = {
heroSpeed: 15
};
function preload() {
"use strict";
stage = new createjs.Stage("ss");
queue = new createjs.LoadQueue(true);
queue.installPlugin(createjs.Sound);
queue.loadManifest([
{
id: 'Vacuum',
src: "img/Vacuum.png"
},
{
id: 'Dust',
src: "img/dust.png"
},
{
id: 'Pickup',
src: "sounds/pickup.mp3"
},
{
id: 'Suger',
src: "sounds/suger.wav"
},
]);
queue.addEventListener('progress', function () {
console.log("hi mom, preloading");
});
queue.addEventListener('complete', setup);
}
function setup() {
"use strict";
window.addEventListener('keyup', fingerUp);
window.addEventListener('keydown', fingerDown);
circle = new createjs.Bitmap("img/Vacuum.png");
circle.width = 40;
circle.height = 90;
stage.addChild(circle);
circle.y = 570;
circle.x = 460;
Score = new createjs.Text("0", "25px Impact", "white");
Score.x = 900;
Score.y = 680;
Score.textBaseline = "alphabetic";
stage.addChild(Score);
tekst1 = new createjs.Text("Score", "25px Impact", "white");
tekst1.x = 740;
tekst1.y = 680;
tekst1.textBaseline = "alphabetic";
stage.addChild(tekst1);
tekst2 = new createjs.Text("Bombs fallin", "40px Impact", "white");
tekst2.x = 10;
tekst2.y = 50;
tekst2.textBaseline = "alphabetic";
stage.addChild(tekst2);
createjs.Ticker.setFPS(30);
createjs.Ticker.addEventListener('tick', heartBeat)
}
function addCoins() {
coin = new createjs.Bitmap("img/dust.png");
coin.x = Math.random() * 900;
coin.width = 36;
coin.height = 50;
coins.push(coin);
stage.addChild(coin);
}
function moveCoins() {
for (var i = 0; i < coins.length; i++) {
coins[i].y += speed;
}
for (var j = 0; j < coins.length; j++) {
if (coins[j].y > 650) {
console.log("hejsa");
stage.removeChild(coins[j]);
coins.splice(j, 1);
}
}
}
function maybeAddCoin() {
var rand = Math.random() * 500;
if (rand < 5) {
addCoins();
}
}
function fingerUp(e) {
"use strict";
//createjs.Sound.stop("Suger")
switch (e.keyCode) {
case 37:
keys.l = false;
break;
case 38:
keys.u = false;
break;
case 39:
keys.r = false;
break;
case 40:
keys.d = false;
break;
}
}
function fingerDown(e) {
"use strict";
switch (e.keyCode) {
case 37:
keys.l = true;
break;
case 38:
keys.u = true;
break;
case 39:
keys.r = true;
break;
case 40:
keys.d = true;
break;
}
}
function moveSlime() {
"use strict";
if (keys.l) {
circle.x -= settings.heroSpeed;
if (circle.x < 0) {
circle.x = 0;
}
if (circle.currentDirection != "left") {
circle.currentDirection = "left";
//createjs.Sound.play("Suger");
keys.u = false;
keys.r = false;
keys.d = false;
}
}
if (keys.r) {
circle.x += settings.heroSpeed;
if (circle.x > 960) {
circle.x = 960;
}
if (circle.currentDirection != "right") {
circle.currentDirection = "right";
//createjs.Sound.play("Suger")
keys.u = false;
keys.l = false;
keys.d = false;
}
}
}
function hitTest(rect1, rect2) {
if (rect1.x >= rect2.x + rect2.width || rect1.x + rect1.width <= rect2.x ||
rect1.y >= rect2.y + rect2.height || rect1.y + rect1.height <= rect2.y)
{
return false;
}
return true;
}
function doCollisionChecking() {
for (var k = coins.length - 1; k >= 0; k--) {
if (hitTest(circle, coin[k])) {
console.log("ramt");
}
}
}
function scoreTimer() {
//Score.text = parseInt(Score.text + 10);
}
function heartBeat(e) {
"use strict";
doCollisionChecking()
maybeAddCoin()
//addCoins()
moveCoins()
scoreTimer()
moveSlime()
stage.update(e);
}
window.addEventListener('load', preload);
Clearly one of your elements is undefined (either circle or coins[k]). I would start with figuring out which one.
Open your debugger.
Turn on "Pause on Exceptions" and re-run your code. When the error happens, your debugger will pause and you can inspect your code
Determine what is undefined. This should shed some light on what is causing the error
One important thing I noticed is that you are looking for rect.width when collision checking. EaselJS elements don't have a width property, so you should instead use getBounds(), which will work with Bitmaps once they are loaded.
// Example
var bounds = rect.getBounds();
var w = bounds.width, h = bounds.height;
Hope that helps!
Here's the problem:
function doCollisionChecking() {
for (var k = coins.length - 1; k >= 0; k--) {
if (hitTest(circle,
coin[k] // your array is coins, not coin
)) {
console.log("ramt");
}
}
}
It might help you in the future to pass arguments through the function instead of relying on global objects. They help you by keeping modifications to your data on tight track. With global variables, anything can modify coins from anywhere and you won't be able to tell what function it is if you have 50+ different functions editing that variable.
I am trying to perform calculator operation, depending on the input values and the operator the result should be displayed. I am struck with validation of dividing by 0 and I am not able to display the result in another function. Please someone could help me!
function calCaulation(e) {
var x = document.getElementById("first").value;
var y = document.getElementById("second").value;
var z = document.getElementById("oper").value;
var a = "";
if ((isNaN(x) || x == "") || (isNaN(y) || y == "")) {
a = "Sum:Please enter the valid Number";
}
if (z == "/" && y == 0) {
a = "Divide By Zero Error";
}
return a;
}
else {
var x = parseFloat(document.getElementById("first").value);
var y = parseFloat(document.getElementById("second").value);
switch (z) {
case ("+"):
a = "Sum:" + ((x + y).toFixed(2));
break;
case ("-"):
a = "Sub:" + ((x - y).toFixed(2));
break;
case ("*"):
a = "Mul:" + ((x * y).toFixed(2));
break;
case ("/"):
a = "Div:" + ((x / y).toFixed(2));
break;
default:
a = "Invalid Operator";
}
return a;
}
}
You have a bracket and else clause too much.
See here for a working jsfiddle.
https://jsfiddle.net/r2aq88tu/4/
edit: also, your return statement after the error checks would leave the function before the real code would execute, I moved the return a inside each if to make it work.
Your new code:
function calCaulation(e) {
var x = document.getElementById("first").value;
var y = document.getElementById("second").value;
var z = document.getElementById("oper").value;
var a = "";
if ((isNaN(x) || x == "") || (isNaN(y) || y == "")) {
a = "Sum:Please enter the valid Number";
return a;
}
if (z == "/" && y == 0) {
a = "Divide By Zero Error";
return a;
}
var x = parseFloat(x);
var y = parseFloat(y);
switch (z) {
case ("+"):
a = "Sum:" + ((x + y).toFixed(2));
break;
case ("-"):
a = "Sub:" + ((x - y).toFixed(2));
break;
case ("*"):
a = "Mul:" + ((x * y).toFixed(2));
break;
case ("/"):
a = "Div:" + ((x / y).toFixed(2));
break;
default:
a = "Invalid Operator";
}
return a;
}
I'm trying to create a "spawn point" for a div. I have made it work and I have a working collision detector for it. There are two things I wanted to ask regarding my code.
How do I get my code to work with more than one player (window.i). - At the moment, after an hour of fiddling, I've only broken my code. This whole area screws up the collision detector, I have more than one player showing at times, but I'm unable to move.
How do I make it so that it detects the contact before it happens - I've tried working with the "tank's" margin and subtracting it's width, so that before it makes contact it calls an event, but it has been unsuccessful and completely stopped the collision function working.
I'm sorry that it's asking a lot, I really do understand that, but the issues come into eachother and rebound off so I thought it was best I put it all into one question rather than 2 separate ones an hour apart.
function animate() {
var tank = document.createElement("div");
tank.id= "tank";
tank.style.marginLeft="0px";
tank.style.marginTop="0px";
tank.style.height="10px";
tank.style.width="10px";
document.body.appendChild(tank);
x = parseInt(tank.style.marginLeft);
y = parseInt(tank.style.marginTop);
document.onkeydown = function () {
e = window.event;
if (e.keyCode == '37') {
if (x > 0) {
if (collisionDetector() == false) {
x = x - 10;
tank.style.marginLeft = x + "px";
} else {
alert();
}
}
} else if (e.keyCode == '39') {
if (x < 790) {
if (collisionDetector() == false) {
x = x + 10;
tank.style.marginLeft = x + "px";
} else {
alert();
}
}
} else if (e.keyCode == '38') {
if (y > 0) {
if (collisionDetector() == false) {
y = y - 10;
tank.style.marginTop = y + "px";
} else {
alert();
}
}
} else if (e.keyCode == '40') {
if (y < 490) {
if (collisionDetector() == false) {
y = y + 10;
tank.style.marginTop = y + "px";
} else {
alert();
}
}
}
}
}
window.lives = 3;
function playerSpawn() {
window.i = 1;
while (i > 0) {
var player = document.createElement("div");
randMarL = Math.ceil(Math.random()*80)*10;
randMarT = Math.ceil(Math.random()*50)*10;
player.id = "player";
player.style.marginLeft= randMarL + "px";
player.style.marginTop= randMarT + "px";
player.style.height="10px";
player.style.width="10px";
document.body.appendChild(player);
i--;
}
}
function collisionDetector() {
x1 = tank.style.marginLeft;
x2 = player.style.marginLeft;
y1 = tank.style.marginTop;
y2 = player.style.marginTop;
if ((x1 == x2 && y1 == y2)) {
return true;
} else {
return false;
}
}
When I attempt to move the div (tank) to the right ONLY in the first "movement command", and only in that direction, I come across in issue whereby my div shoots off a few thousand pixels to the right, way off of the screen region. Was hoping someone would assist me to see why this is.
function animate() {
var tank = document.getElementById("tank");
tank.style.marginLeft="360px";
tank.style.marginTop="440px";
window.xpos = tank.style.marginLeft;
window.ypos = tank.style.marginTop;
window.x = xpos.replace("px","");
window.y = ypos.replace("px","");
document.onkeydown = checkKey;
function checkKey(e) {
e = e || window.event;
if (e.keyCode == '37') {
if (x > 0) {
x = x - 20;
tank.style.marginLeft = x + "px";
}
} else if (e.keyCode == '39') {
if (x < 70) {
x = x + 20;
tank.style.marginLeft = x + "px";
}
} else if (e.keyCode == '38') {
if (y > 0) {
y = y - 20;
tank.style.marginTop = y + "px";
}
} else if (e.keyCode == '40') {
if (y < 440) {
y = y + 20;
tank.style.marginTop = y + "px";
}
}
}
checkKey(e);
}
window.lives = 3;
function destroy() {
if (lives != 0) {
alert("Life Lost!");
lives--;
window.collision == false;
animate();
} else {
alert("Try Again!");
}
}
window.collision = true;
function state() {
if (collision == false) {
window.state = 1;
} else if (collision == true) {
window.state = 0;
}
return state;
}
state();
if (state == 1) {
animate();
} else {
destroy();
}
You think you are doing a math operation but what you really are doing a string concatenation. In Javascript "360"-20 equals 340 because in this case the string is converted to a number and then an arithmetic subtraction is performed with both numeric values, however a different set of rules apply for the plus operator: in this case "360"+20 yields "36020" because the number is converted to a string and then both strings are concatenated.
Do this:
window.x = Number(xpos.replace("px",""));
window.y = Number(ypos.replace("px",""));