How would I handle sideways collisions in two dimensions? - javascript

I've been experimenting with the html element.
I'm trying to make a 2d sidescroller game and I've been having issues with collisions on the sides of objects.
Walking on the top of objects works just fine and hitting the bottom of objects also works but I cannot get colliding with the side of an object to work. I've made dozens of attempts which all end up introducing dozens of new bugs while also not fixing the problem.
Here's the code
<html>
<canvas id="canvas" width="200" height="200" style="border-style:solid; border-width:1px"></canvas>
<script>if (typeof module === 'object') { window.module = module; module = undefined; }</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>if (window.module) module = window.module;</script>
<span id="x"></span>
<span id="y"></span>
<br>
<br>
<span id="test"></span>
<script>
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
function clear() {
ctx.fillStyle = "#FFFFFF"
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = "#000000"
}
let keys = {};
$(document).keydown(function (e) { //Records keystrokes
keys[e.which] = true;
})
$(document).keyup(function (e) {
delete keys[e.which];
});
let worldx = 10;
const x = 90;
let y = 0;
let yVel = 0;
let grounded = false;
let objects = { //I'm representing the game world as an array of boxes
"boxes": [
{ "width": 50, "height": 10, "x": 45, "y": 100 },
{ "width": 75, "height": 20, "x": 60, "y": 150 },
{ "width": 10, "height": 100, "x": 125, "y": 40 }
]
}
let id = window.setInterval(function () { //Lock it at ~60fps
clear();
$("span").empty();
ctx.fillRect(x, y, 10, 20) //Player character
yVel += 9.8 * 0.016 // force of gravity
y += yVel
for (let check = 0; check != objects.boxes.length; check++) { //Loop through objects
if (y >= objects.boxes[check].y - 20 && y <= objects.boxes[check].y + objects.boxes[check].height && x < objects.boxes[check].x + worldx + objects.boxes[check].width && x >= objects.boxes[check].x - 10 + worldx) { //Check if there is a collision.
yVel = 0;
//TODO add something here that will handle side-on collisions
if (y <= objects.boxes[check].y + objects.boxes[check].height / 2) {
y = objects.boxes[check].y - 20
grounded = true
}
else {
y = objects.boxes[check].y + objects.boxes[check].height
}
}
ctx.fillRect(objects.boxes[check].x + worldx, objects.boxes[check].y, objects.boxes[check].width, objects.boxes[check].height) // draw object
}
$("#x").append(Math.floor(x - worldx) + ", ")
$("#y").append(Math.floor(y))
for (i in keys) {
if (!keys.hasOwnProperty(i)) { continue; }
if (i == 32 && grounded) { //Space bar
yVel -= 5;
grounded = false;
}
if (i == 65) { //A
worldx += 3;
}
if (i == 68) { //D
worldx -= 3;
}
}
}, 16)
</script>
</html>
The TODO comment is where I check for collisions.

Here is a good start for you(this code is from scratch, yours is a little tricky for me to understand):
var px = 0;//player's x and y
var py = 0;
var velX = 0;
var velY = 0;
var grounded = false;
var pw = 20;//player's width
var ph = 20;//player's height
var block = function(x, y, w, h) {
if (px + pw > x && px < x + w && py + ph > y && py + ph < y + 5 && velY > 0) {//Top
velY = 0;
grounded = true;
py = y - ph;
}
if (px + pw > x && px < x + w && py < y + h && py > y + h - 5 && velY < 0) {//bottom
velY = 2;//bonk
py = y + h;
}
if (px + pw > x && px < x + 5 && py + ph > y && py < y + h && velX > 0) {//left
velX = 0;
px = x - pw;
}
if (px > x + w - 5 && px < x + w && py + ph > y && py < y + h && velX < 0) {//right
velX = 0;
px = x + w;
}
};

Related

how to fix bugs in snake game?

I have been making a snake game for a few days to improve my js skills.
And I released the first beta test of my mini-game. And you actually know that there are always a lot of bugs in beta tests. And I noticed them. First I notice that if the snake.x position < 0, then the snake appears from a different corner, but in front of one block, and not from the zero coordinate. This also applies to snake.y<0.
here is the code:
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
var box = 10;
//Snake
var snake = [];
var dir = "right";
var maxCell = 10;
var can = canvas.getBoundingClientRect();
var px = Math.floor(canvas.width / 2 / 10) * 10;
var py = Math.floor(canvas.height / 2 / 10) * 10;
//Apple
var ax = Math.floor((Math.random() * ~~(canvas.width / box)) / 10) * 10;
var ay = Math.floor((Math.random() * ~~(canvas.height / box)) / 10) * 10;
var loop = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
Elems();
}, 1000 / 40);
document.addEventListener("keydown", function (e) {
if (e.keyCode === 37 && dir !== "right") {
dir = "left";
//console.log('left')
} else if (e.keyCode === 38 && dir !== "down") {
dir = "up";
//console.log('up')
} else if (e.keyCode === 39 && dir !== "left") {
dir = "right";
//console.log('right')
} else if (e.keyCode === 40 && dir !== "up") {
dir = "down";
//console.log('down')
}
});
function direction() {
if (dir == "right") {
px += box;
} else if (dir == "left") {
px -= box;
} else if (dir == "up") {
py -= box;
} else if (dir == "down") {
py += box;
}
}
//Closure )))
function Elems() {
function Apple() {
ctx.fillStyle = "green";
ctx.fillRect(ax, ay, box, box);
}
//! Spawn snake
function Snake() {
direction();
var head = {
x: px,
y: py,
};
snake.unshift(head);
//-------------BUG---------------
if (px >= canvas.width) {
px = 0;
}
if (px + box < 0) {
px = canvas.width;
}
if (py >= canvas.height) {
py = 0;
}
if (py + box < 0) {
py = canvas.height;
}
//-------------------------------
snake.forEach(function (elem, index) {
ctx.fillStyle = `red`;
ctx.fillRect(elem.x, elem.y, box, box);
});
if (head.x == ax && head.y == ay) {
maxCell += 1;
ax = Math.floor(Math.random() * ~~(canvas.width / box)) * 10;
ay = Math.floor(Math.random() * ~~(canvas.height / box)) * 10;
}
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
maxCell = 10;
px = Math.floor(canvas.width / 2 / 10) * 10;
py = Math.floor(canvas.height / 2 / 10) * 10;
clearInterval(loop);
alert("Game Over:(")
}
}
if (snake.length < maxCell) {
snake.push({ x: px, y: py });
}
snake.pop();
}
Snake();
Apple();
}
I tried changing the canvas dimensions and box value. But it didn't help x((((
You'd have to rearrange your code a good bit to get it working right.
The Snake function should not happen in a loop, the loop related stuff inside of Snake should be inside the Elems function instead. And the loop being set should happen last.
The following is a basic rework of your code with an added method, and I've included the grid that angel.bonev suggested so it works like a traditional snake game now and with the apple getting eaten to increase the snake size. The testForPointInArea method makes it such that the snake does not have to be exactly over the apple spot, but just overlapping.
Also the opposite direction restrictions in the keydown listener were removed;
&& dir !== "right" && dir !== "down" && dir !== "left" && dir !== "up"
If the snake is a small size or not big enough to bank corners,
then game over would not happen with the above backwards restrictions.
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
var box = 10;
//Snake body Array
var snake = [];
var dir = "right";
var maxCell = 2;
var can = canvas.getBoundingClientRect();
var grid = {
x: Math.floor(canvas.width / box),
y: Math.floor(canvas.height / box)
};
var px = Math.floor(grid.x / 2) * 10;
var py = Math.floor(grid.y / 2) * 10;
//Apple cords
var ax = Math.floor( Math.random() * grid.y ) * box;
var ay = Math.floor( Math.random() * grid.x ) * box;
var theSnake;
document.addEventListener("keydown", function(e) {
e.preventDefault();
if (e.keyCode === 37 ) {
dir = "left";
//console.log('left')
} else if (e.keyCode === 38 ) {
dir = "up";
//console.log('up')
} else if (e.keyCode === 39) {
dir = "right";
//console.log('right')
} else if (e.keyCode === 40 ) {
dir = "down";
//console.log('down')
}
});
function direction() {
if (dir == "right") {
px += 2;
} else if (dir == "left") {
px -= 2;
} else if (dir == "up") {
py -= 2;
} else if (dir == "down") {
py += 2;
}
}
function Apple() {
ctx.fillStyle = "green";
ctx.fillRect(ax, ay, box, box);
}
function testForPointInArea(p, left,top,right,bottom) {
return Boolean(!(p.x < left || p.x > right || p.y < top || p.y > bottom));
}
function Elems() {
direction();
theSnake.head.x = px; theSnake.head.y = py;
snake.unshift({x:theSnake.head.x, y:theSnake.head.y});
for (let i = 1; i < snake.length; i++) {
if (theSnake.head.x === snake[i].x && theSnake.head.y === snake[i].y) {
maxCell = 2;
px = Math.floor(grid.x / 2) * 10;
py = Math.floor(grid.y / 2) * 10;
snake = [];
//clearInterval(loop);
alert("Game Over :(");
return;
}
}
if (snake.length < maxCell) {
snake.push({ x: px, y: py });
}
if (px >= canvas.width) {
px = 0;
}
if (px + box < 0) {
px = canvas.width;
}
if (py >= canvas.height) {
py = 0;
}
if (py + box < 0) {
py = canvas.height;
}
theSnake.drawSnake();
Apple();
snake.pop();
if ( testForPointInArea(theSnake.head, ax - 3, ay - 3, ax + box + 3, ay + box + 3) ) {
maxCell += 1;
ax = Math.floor( Math.random() * grid.y ) * box;
ay = Math.floor( Math.random() * grid.x ) * box;
Apple();
}
}
//Snake Class
function Snake() {
this.head = {
x: px,
y: py,
};
this.drawSnake = function() {
snake.forEach(function (elem, index) {
ctx.fillStyle = "red";
ctx.fillRect(elem.x, elem.y, box, box);
});
};
}
theSnake = new Snake();
var loop = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
Elems();
}, 1000 / 40);
<canvas id="game" width="150px" height="150px"> </canvas>

Can't detect collision between a rectangle and a circle in p5.js

var rectx = 287.5;
var recty = 460;
var rectx2 = 287.5
var recty2 = 0
var rectwidth = 100
var rectheight = 25
//ball vars
var xBall = Math.floor(Math.random() * 300) + 50;
var yBall = 50;
var diameter = 75;
var xBallChange = 5;
var yBallChange = 5;
//Misc
var started = false;
var score = 0;
var score2 = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(0);
//main commands
xBall += xBallChange;
yBall += yBallChange;
if (xBall < diameter/2 || xBall > windowWidth - 0.5*diameter) {
xBallChange *= -1;
}
if (yBall < diameter/2 || yBall > windowHeight - diameter) {
yBallChange *= -1;
}
if ((xBall > rectx && xBall < rectx + rectwidth) && (yBall + (diameter/2) >= recty)) {
xBallChange *= -1;
yBallChange *= -1;
score++;
}
//Player 1
fill (0,255,255);
noStroke();
rect(rectx,recty,rectwidth,rectheight);
//Player 2
fill(0,255,255)
noStroke()
rect(rectx2,recty2,rectwidth,rectheight);
//The ball
fill (255,255,0);
noStroke();
ellipse(xBall,yBall,75,75)
// The partition
fill(148,0,211)
noStroke()
rect(0,257,750,1)
}
function keyPressed() {
//Player 1 Input
if (keyCode === LEFT_ARROW) {
rectx -= 50;
} else if (keyCode === RIGHT_ARROW) {
rectx += 50;
}
//Player 2 Input
if (keyCode === 65) {
rectx2 -= 50;
} else if (keyCode === 68) {
rectx2 += 50;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
This is my code. I want the circle/ball to reflect off the second rectangle placed on top. Please help me with the appropriate commands required for this program. I have tried many possibilities and have got the first rectangle to work as I want(the circle/ball reflects off the rectangle). I am trying to make the pong game for two people to play locally. THank you for helping me.
If the ball hits the paddle, then only invert the y component of the direction vector of the ball to reflect the ball on the inner side of the paddle. Correct the position of the ball so that it touches the paddle:
if (xBall > rectx && xBall < rectx + rectwidth && yBall + diameter/2 >= recty) {
yBallChange *= -1;
yBall = recty - diameter/2;
score++;
}
This is only an approximation of course, the reflection at the corners is incorrect and the ball does not bounce on the left or right edge of the paddle, but it is a good starting point.
Implement a similar algorithm for the 2nd paddle:
if (xBall > rectx2 && xBall < rectx2 + rectwidth && yBall - diameter/2 < recty2+rectheight) {
yBallChange *= -1;
yBall = recty2 + rectheight + diameter/2;
score2++;
}
Complete example:
//Rect vars
var rectx = 287.5;
var recty = 460;
var rectx2 = 287.5
var recty2 = 0
var rectwidth = 100
var rectheight = 25
//ball vars
var xBall = Math.floor(Math.random() * 300) + 50;
var yBall = 50;
var diameter = 75;
var xBallChange = 5;
var yBallChange = 5;
//Misc
var started = false;
var score = 0;
var score2 = 0;
function setup() {
createCanvas(750, 485);
}
function draw() {
background(0);
//main commands
xBall += xBallChange;
yBall += yBallChange;
if (xBall < diameter/2 || xBall > width - diameter/2) {
xBallChange *= -1;
}
if (yBall < diameter/2 || yBall > height - diameter/2) {
yBallChange *= -1;
}
if (xBall > rectx && xBall < rectx + rectwidth && yBall + diameter/2 >= recty) {
yBallChange *= -1;
yBall = recty - diameter/2;
score++;
}
if (xBall > rectx2 && xBall < rectx2 + rectwidth && yBall - diameter/2 < recty2+rectheight) {
yBallChange *= -1;
yBall = recty2 + rectheight + diameter/2;
score2++;
}
//Player 1
fill (0,255,255);
noStroke();
rect(rectx,recty,rectwidth,rectheight);
//Player 2
fill(0,255,255)
noStroke()
rect(rectx2,recty2,rectwidth,rectheight);
//The ball
fill (255,255,0);
noStroke();
ellipse(xBall,yBall,75,75)
// The partition
fill(148,0,211)
noStroke()
rect(0,257,750,1)
}
function keyPressed() {
//Player 1 Input
if (keyCode === LEFT_ARROW) {
rectx -= 50;
} else if (keyCode === RIGHT_ARROW) {
rectx += 50;
}
//Player 2 Input
if (keyCode === 65) {
rectx2 -= 50;
} else if (keyCode === 68) {
rectx2 += 50;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

Why are my Raycaster Sprites blinking and how can I fix it?

My raycaster's sprites keep blinking, and I think it has to do with ZBuffer.
Using this for reference: https://lodev.org/cgtutor/index.html
I have done plenty of research and can't find any JS raycasters that answer my problem. By sprites, I mean billboarded images, and when you move they blink like an atari game that is being pushed to its limits.
Here is the project on JSFiddle: https://jsfiddle.net/Vakore/bsvx1m26/
Here is where I am using ZBuffer:
for (var stripe = drawStartX; stripe < drawEndX; stripe++) {
var texX =
floor((256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texWidth) / spriteWidth) / 256;
// the conditions in the if are:
// 1) it's in front of camera plane so you don't see things behind you
// 2) it's on the screen (left)
// 3) it's on the screen (right)
// 4) ZBuffer, with perpendicular distance
var ruffer = 0;
if (sprite[i][2] == "barrel") {
ruffer = 576 - 64;
}
if (sprite[i][2] == "pillar") {
ruffer = 576;
}
if (sprite[i][2] == "greenlight") {
ruffer = 576 + 64;
}
if (
transformY > 0 &&
stripe > 0 &&
stripe < w &&
transformY < ZBuffer[stripe]
) {
can.drawImage(
document.getElementById(sprite[i][2]),
texX + ruffer, 0, 1, texHeight, stripe, drawStartY, 1, drawEndY - drawStartY
);
}
}
ZBuffer was declared and assigned an array value earlier in the code.
var keys = {
"a": false,
"s": false,
"d": false,
"A": false,
"S": false,
"D": false,
"W": false,
"Z": false,
"X": false,
"C": false,
"ENTER": false
};
document.addEventListener("keydown", function(e) {
e.preventDefault();
if (e.keyCode === 16)
keys["SHIFT"] = true;
if (e.keyCode === 37)
keys["a"] = true;
if (e.keyCode === 39)
keys["d"] = true;
if (e.keyCode === 65)
keys["A"] = true;
if (e.keyCode === 68)
keys["D"] = true;
if (e.keyCode === 83)
keys["S"] = true;
if (e.keyCode === 87)
keys["W"] = true;
if (e.keyCode === 40)
keys["S"] = true;
if (e.keyCode === 38)
keys["W"] = true;
if (e.keyCode === 67)
keys["C"] = true;
if (e.keyCode === 88)
keys["X"] = true;
if (e.keyCode === 90)
keys["Z"] = true;
if (e.keyCode === 13)
keys["ENTER"] = true;
})
document.addEventListener("keyup", function(e) {
e.preventDefault();
if (e.keyCode === 16)
keys["SHIFT"] = false;
if (e.keyCode === 37)
keys["a"] = false;
if (e.keyCode === 39)
keys["d"] = false;
if (e.keyCode === 65)
keys["A"] = false;
if (e.keyCode === 68)
keys["D"] = false;
if (e.keyCode === 83)
keys["S"] = false;
if (e.keyCode === 87)
keys["W"] = false;
if (e.keyCode === 40)
keys["S"] = false;
if (e.keyCode === 38)
keys["W"] = false;
if (e.keyCode === 67)
keys["C"] = false;
if (e.keyCode === 88)
keys["X"] = false;
if (e.keyCode === 90)
keys["Z"] = false;
})
/*
Basic functions(ease of access)
*/
var canvas = document.getElementById("canva");
var can = canvas.getContext("2d");
canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock;
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock;
canvas.onclick = function() {
canvas.requestPointerLock();
clicked = true;
};
var mouseX = 0;
var mouseMove = function(e) {
mouseX += e.movementX;
};
var rect = function(x, y, w, h) {
can.fillRect(x, y, w, h);
};
var arc = function(x, y, r, start, stop) {
can.beginPath();
can.arc(x, y, r, start, stop);
can.fill();
};
var circle = function(x, y, r) {
arc(x, y, r, 0, 360);
};
var fill = function(r, g, b, a) {
if (a === undefined) {
a = 1;
}
can.fillStyle = "rgb(" + r + "," + g + "," + b + "," + a + ")";
};
var font = function(siz) {
can.font = siz;
};
var textAlign = function(ali) {
can.textAlign = ali;
};
var text = function(txt, x, y, w, h) {
can.fillText(txt, x, y, w, h);
};
var quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
can.beginPath();
can.moveTo(x1, y1);
can.lineTo(x2, y2);
can.lineTo(x3, y3);
can.lineTo(x4, y4);
can.closePath();
can.fill();
};
var random = function(min, max) {
return round(Math.random() * (max - min)) + min;
};
var translate = function(x, y) {
can.save();
can.translate(x, y);
};
var scale = function(w, h) {
can.scale(w, h);
}
var trestore = function() {
can.restore();
};
var floor = function(num) {
return Math.floor(num);
};
var sqrt = function(num) {
return Math.sqrt(num);
};
var abs = function(num) {
return Math.abs(num);
};
var color = function(r, g, b, a) {
return [r, g, b, a];
};
var dist = function() {
return true;
};
var cos = function(num) {
return Math.cos(num);
}
var sin = function(num) {
return Math.sin(num);
}
var dist = function(x1, y1, x2, y2) {
dx = x1 - x2;
dy = y1 - y2;
return Math.sqrt(dx * dx + dy * dy);
};
//Begin the raycaster
var scaler = 3; //This reduces the resolution so that it can display large rooms without lagging
var w = 640 / scaler;
var h = 480 / scaler;
var texWidth = 64;
var texHeight = 64;
var rotSpeed = 0.1;
var moveSpeed = 0.1;
var worldMap = [
"1111111111111111111111111111111111111111",
"1001000001234567800000000000000000000001",
"1001000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1001000000000000000000000000000000000001",
"1001000000000000000000000000222200000001",
"1001000000000000000000000000200200000001",
"1111000000000000000000000000200200000001",
"1000000000000000000000000033300333000001",
"8008000000000000000000000000000000000001",
"8000800000000000000000000033300333000001",
"8000080000000000000000000000300300000001",
"8000008000000000000000000000333300000001",
"8888888000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1666006660000000000000000000000000000001",
"1000000060000000000000000000000000000001",
"1000000060000000000000000000000000000001",
"1000000060000000000000000000000000000001",
"1000000060000000000000000000000000000001",
"1666666660000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000000000000000000000000000001",
"1000000040444444444444400000000000000001",
"1000000040000000000000400000000000000001",
"1000000040000000000000400000000000000001",
"1000000040000000000000400000000000000001",
"1000000044444444444444400000000000000001",
"1000000000000000000000000000000000000001",
"1000000000000007775777000000000000000001",
"1000000000000007000007000000000000000001",
"1000000000000005000005000000000000000001",
"1000000000000007000007000000000000000001",
"1000000000000000000007000000000000000001",
"1111111111111117775777111111111111111111",
];
try {
var ZBuffer = [];
var sprite = [
[5, 5, "barrel", 0], //Y and X are reversed
[6.5, 2.5, "pillar", 1],
[6.5, 1.5, "pillar", 1.5],
[8, 6, "greenlight", 2],
];
var posX = 2,
posY = 2,
dirX = -1,
dirY = 0,
planeX = 0,
planeY = 0.66; //The 2d raycaster version of the camera plane
} catch (e) {
alert(e);
}
var castRays = function() {
for (var x = 0; x < w; x++) {
var cameraX = 2 * x / (w) - 1; //x-coordinate in camera space
var rayDirX = dirX + planeX * cameraX;
var rayDirY = dirY + planeY * cameraX;
//which box of the map we're in
var mapX = floor(posX);
var mapY = floor(posY);
//length of ray from current position to next x or y-side
var sideDistX;
var sideDistY;
//length of ray from one x or y-side to next x or y-side
var deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
var deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
var perpWallDist = 0;
//what direction to step in x or y-direction (either +1 or -1)
var stepX = 0;
var stepY = 0;
var hit = 0; //was there a wall hit?
var side = 0; //was a NS or a EW wall hit?
//calculate step and initial sideDist
if (rayDirX < 0) {
stepX = -1;
sideDistX = (posX - mapX) * deltaDistX;
} else {
stepX = 1;
sideDistX = (mapX + 1.0 - posX) * deltaDistX;
}
if (rayDirY < 0) {
stepY = -1;
sideDistY = (posY - mapY) * deltaDistY;
} else {
stepY = 1;
sideDistY = (mapY + 1.0 - posY) * deltaDistY;
}
while (hit == 0) {
//jump to next map square, OR in x-direction, OR in y-direction
if (sideDistX < sideDistY) {
sideDistX += deltaDistX;
mapX += stepX;
side = 0;
} else {
sideDistY += deltaDistY;
mapY += stepY;
side = 1;
}
//Check if ray has hit a wall
if (worldMap[mapX][mapY] == 0) {}
if (worldMap[mapX][mapY] != 0) {
hit = 1;
}
} //end of while loop
//Calculate distance of perpendicular ray (Euclidean distance will give fisheye effect!)
if (side == 0) {
perpWallDist = (mapX - posX + (1 - stepX) / 2) / rayDirX;
} else {
perpWallDist = (mapY - posY + (1 - stepY) / 2) / rayDirY;
}
//Calculate height of line to draw on screen
var lineHeight = abs(floor(h / perpWallDist));
//calculate lowest and highest pixel to fill in current stripe
var drawStart = -lineHeight / 2 + h / 2;
//if(drawStart < 0) {drawStart = 0;}
var drawEnd = lineHeight / 2 + h / 2;
//if(drawEnd >= h) {drawEnd = h - 1;}
//texturing calculations
//var texNum = worldMap[mapX][mapY] - 1; //1 subtracted from it so that texture 0 can be used!
//calculate value of wallX
var wallX = 0; //where exactly the wall was hit
if (side == 0) {
wallX = posY + perpWallDist * rayDirY;
} else {
wallX = posX + perpWallDist * rayDirX;
}
wallX -= floor((wallX));
//x coordinate on the texture
var texX = floor(wallX * (texWidth));
if (side == 0 && rayDirX > 0) {
texX = texWidth - texX - 1;
}
if (side == 1 && rayDirY < 0) {
texX = texWidth - texX - 1;
}
//Untextured variant
/*var currentColor = color(255, 255, 255);
if (worldMap[mapX][mapY] == "1") {currentColor = color(150, 150, 150);}
if (worldMap[mapX][mapY] == "2") {currentColor = color(255, 255, 255);}
if (worldMap[mapX][mapY] == "3") {currentColor = color(255,0,0);}
if (worldMap[mapX][mapY] == "4") {currentColor = color(0,0,255);}
if (worldMap[mapX][mapY] == "5") {currentColor = color(0,255,0);}
if (worldMap[mapX][mapY] == "6") {currentColor = color(255,0,255);}
if (worldMap[mapX][mapY]=="7") {currentColor = color(0,255,255);}
if (side === 1) {currentColor = [currentColor[0] - 40, currentColor[1] - 40, currentColor[2] - 40];}
//if (dist(mapX, mapY, px, pz) > 10) {continue;}
fill(currentColor[0],currentColor[1],currentColor[2]);
rect(x, drawStart, 1, drawEnd - drawStart);*/
var currentImg = "bluestone";
if (worldMap[mapX][mapY] == "1") {
currentImg = "bluestone";
}
if (worldMap[mapX][mapY] == "2") {
currentImg = "greystone";
}
if (worldMap[mapX][mapY] == "3") {
currentImg = "wood";
}
if (worldMap[mapX][mapY] == "4") {
currentImg = "colorstone";
}
if (worldMap[mapX][mapY] == "5") {
currentImg = "eagle";
}
if (worldMap[mapX][mapY] == "6") {
currentImg = "mossy";
}
if (worldMap[mapX][mapY] == "7") {
currentImg = "redbrick";
}
if (worldMap[mapX][mapY] == "8") {
currentImg = "purplestone";
}
//if (worldMap[mapX][mapY] =="a") {currentImg = "barrel";}
//if (dist(mapX, mapY, posX, posY) > 20) {continue;}
can.drawImage(document.getElementById(currentImg), texX + ((worldMap[mapX][mapY] - 1) * 64), 0, 1, texHeight, x, drawStart, 1, drawEnd - drawStart);
//fill(255, 0, 0);rect(x, drawStart, 1, drawEnd - drawStart);
if (side === 1) {
fill(0, 0, 0, 0.3);
rect(x, drawStart, 1, drawEnd - drawStart);
}
//Set the ZBuffer
ZBuffer[x] = perpWallDist;
//FLOOR CASTING(not using till figure out how to do without lag)
var floorXWall = 0,
floorYWall = 0;
if (side == 0 && rayDirX > 0) {
floorXWall = mapX;
floorYWall = mapY + wallX;
} else if (side == 0 && rayDirX < 0) {
floorXWall = mapX + 1.0;
floorYWall = mapY + wallX;
} else if (side == 1 && rayDirY > 0) {
floorXWall = mapX + wallX;
floorYWall = mapY;
} else {
floorXWall = mapX + wallX;
floorYWall = mapY + 1.0;
}
var distWall, distPlayer, currentDist;
distWall = perpWallDist;
distPlayer = 0.0;
for (var y = drawEnd + 1; y < h; y++) {
//if (dist(mapX, mapY, posX, posY) > 5) {continue;}
currentDist = h / (2.0 * y - h); //you could make a small lookup table for this instead
var weight = (currentDist - distPlayer) / (distWall - distPlayer);
var currentFloorX = weight * floorXWall + (1.0 - weight) * posX;
var currentFloorY = weight * floorYWall + (1.0 - weight) * posY;
var floorTexX, floorTexY;
floorTexX = floor(currentFloorX * texWidth) % texWidth;
floorTexY = floor(currentFloorY * texHeight) % texHeight;
//OPTIMIZE FOR LESS LAG!!!!!!!!!! (search
//FLOOR
//can.drawImage(document.getElementById("greystone"), floorTexX, floorTexY, texWidth, texHeight, x, y, texWidth, texHeight);
//CEILING
//can.drawImage(document.getElementById("wood"), floorTexX, floorTexY, texWidth, 1, x, h - y, texWidth, 1);
} //End of the 'y' loop
} //end the loop
//SPRITE CASTING
//Sort sprites
sprite.sort(function(a, b) {
return b[3] - a[3];
}); //Sort the depth of each sprite
//Draw sprites
for (var i = 0; i < sprite.length; i++) {
var spriteX = sprite[i][0] - posX;
var spriteY = sprite[i][1] - posY;
sprite[i][3] = abs((posX - sprite[i][0]) - (posY - sprite[i][1]));
var invDet = 1.0 / (planeX * dirY - dirX * planeY);
var transformX = invDet * (dirY * spriteX - dirX * spriteY);
var transformY = invDet * (-planeY * spriteX + planeX * spriteY); //this is actually the depth inside the screen, that what Z is in 3D(Thanks lodev.org for explanation!)
var spriteScreenX = floor((w / 2) * (1 + transformX / transformY));
//calculate height of the sprite on screen
var spriteHeight = abs(floor(h / (transformY))); //using "transformY" instead of the real distance prevents fisheye
//calculate lowest and highest pixel to fill in current stripe
var drawStartY = -spriteHeight / 2 + h / 2;
//if (drawStartY < 0) {drawStartY = 0;} Don't need THIS
var drawEndY = spriteHeight / 2 + h / 2;
//if (drawEndY >= h) {drawEndY = h - 1;} Or THIS
//calculate width of the sprite
var spriteWidth = abs(floor(h / (transformY)));
var drawStartX = -spriteWidth / 2 + spriteScreenX;
//if (drawStartX < 0) {drawStartX = 0;} Nor this
var drawEndX = spriteWidth / 2 + spriteScreenX;
//if (drawEndX >= w) {drawEndX = w - 1;} Nor dis
//loop through every vertical stripe of the sprite on screen
for (var stripe = drawStartX; stripe < drawEndX; stripe++) {
var texX = floor(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texWidth / spriteWidth) / 256;
//the conditions in the if are:
//1) it's in front of camera plane so you don't see things behind you
//2) it's on the screen (left)
//3) it's on the screen (right)
//4) ZBuffer, with perpendicular distance
var ruffer = 0;
if (sprite[i][2] == "barrel") {
ruffer = 576 - 64;
}
if (sprite[i][2] == "pillar") {
ruffer = 576;
}
if (sprite[i][2] == "greenlight") {
ruffer = 576 + 64;
}
if (transformY > 0 && stripe > 0 && stripe < w && transformY < ZBuffer[stripe]) {
can.drawImage(document.getElementById(sprite[i][2]), texX + ruffer, 0, 1, texHeight, stripe, drawStartY, 1, drawEndY - drawStartY);
}
}
} //End of 'i' loop
//Bottom of dat
if (keys["W"]) {
if (worldMap[floor(posX + dirX * moveSpeed)][floor(posY)] == false) {
posX += dirX * moveSpeed
};
if (worldMap[floor(posX)][floor(posY + dirY * moveSpeed)] == false) {
posY += dirY * moveSpeed
};
}
//move backwards if no wall behind you
if (keys["S"]) {
if (worldMap[floor(posX - dirX * moveSpeed)][floor(posY)] == false) {
posX -= dirX * moveSpeed;
}
if (worldMap[floor(posX)][floor(posY - dirY * moveSpeed)] == false) {
posY -= dirY * moveSpeed;
}
}
if (keys["D"]) {
if (worldMap[floor(posX + planeX * moveSpeed)][floor(posY)] == false) {
posX += planeX * moveSpeed
};
if (worldMap[floor(posX)][floor(posY + planeY * moveSpeed)] == false) {
posY += planeY * moveSpeed
};
}
if (keys["A"]) {
if (worldMap[floor(posX - planeX * moveSpeed)][floor(posY)] == false) {
posX -= planeX * moveSpeed;
}
if (worldMap[floor(posX)][floor(posY - planeY * moveSpeed)] == false) {
posY -= planeY * moveSpeed;
}
}
//rotate to the right
mouseX = -mouseX / 75;
var oldDirX = dirX;
dirX = dirX * cos(mouseX) - dirY * sin(mouseX);
dirY = oldDirX * sin(mouseX) + dirY * cos(mouseX);
var oldPlaneX = planeX;
planeX = planeX * cos(mouseX) - planeY * sin(mouseX);
planeY = oldPlaneX * sin(mouseX) + planeY * cos(mouseX);
mouseX = 0;
if (keys["d"]) {
//both camera direction and camera plane must be rotated
var oldDirX = dirX;
dirX = dirX * cos(-rotSpeed) - dirY * sin(-rotSpeed);
dirY = oldDirX * sin(-rotSpeed) + dirY * cos(-rotSpeed);
var oldPlaneX = planeX;
planeX = planeX * cos(-rotSpeed) - planeY * sin(-rotSpeed);
planeY = oldPlaneX * sin(-rotSpeed) + planeY * cos(-rotSpeed);
}
//rotate to the left
if (keys["a"]) {
//both camera direction and camera plane must be rotated
var oldDirX = dirX;
dirX = dirX * cos(rotSpeed) - dirY * sin(rotSpeed);
dirY = oldDirX * sin(rotSpeed) + dirY * cos(rotSpeed);
var oldPlaneX = planeX;
planeX = planeX * cos(rotSpeed) - planeY * sin(rotSpeed);
planeY = oldPlaneX * sin(rotSpeed) + planeY * cos(rotSpeed);
}
};
var run = function() {
try {
if (document.pointerLockElement === canvas || document.mozPointerLockElement === canvas) {
document.addEventListener("mousemove", mouseMove, false);
} else {
document.removeEventListener("mousemove", mouseMove, false);
} //Mouse stuff
translate(0, 0);
scale(scaler, scaler);
fill(40, 40, 40);
rect(0, 0, w, h / 2);
fill(80, 80, 80);
rect(0, h / 2, w, h / 2);
if (keys["C"]) {
rotSpeed = 0.04;
} else {
rotSpeed = 0.1;
}
/*if (keys["W"]) {fill(255, 0, 0);}
if (keys["A"]) {fill(0, 255, 0);}
if (keys["S"]) {fill(0, 0, 255);}
if (keys["D"]) {fill(255, 0, 255);}
if (keys["X"]) {fill(255, 255, 0);}
if (keys["Z"]) {fill(0, 255, 255);}*/
castRays();
trestore();
requestAnimationFrame(run);
} catch (e) {
alert(e);
}
};
run();
<html>
<title>Jailbreak</title>
<div class="center">
<canvas width="640" height="480" id="canva"></canvas>
<br>
<img id="barrel" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="pillar" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="greenlight" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="bluestone" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="greystone" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="wood" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="colorstone" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="eagle" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="mossy" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="redbrick" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
<img id="purplestone" src="https://lodev.org/cgtutor/images/wolftexturesobj.gif">
</div>
</html>
UPDATE: I tried changing
if (
transformY > 0 &&
stripe > 0 &&
stripe < w &&
transformY < ZBuffer[stripe]//Pay attention to this line
) {
to:
if (
transformY > 0 &&
stripe > 0 &&
stripe < w &&
transformY > ZBuffer[stripe]//Now look
) {
Which basically reverses the check to see if it is behind a wall. Surprisingly, the blinking continues.
The answer was pretty simple. It was because stripe is not an even value, and if you were to have an array like this: var thisArray = [1, 2, 3, 4]; and you were to try and grab var money = thisArray[1.5]; the value undefined would be returned.
So I changed this:
if (
transformY > 0 &&
stripe > 0 &&
stripe < w &&
transformY < ZBuffer[stripe]
) {
To this:
if (
transformY > 0 &&
stripe > 0 &&
stripe < w &&
transformY < ZBuffer[Math.round(stripe)]
) {
Math.floor also works, since it also returns an integer value.

mouseover events with canvas

I was wondering if it was possible to have mouseover events with multiple squares on a canvas
this is my code right now: http://jsfiddle.net/2j3u9f7m/
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var Enemy = function (x, y, velx, vely) {
this.x = x;
this.y = y;
this.velx = 0;
this.vely = 0;
}
Enemy.prototype.update = function () {
var tx = 650 - this.x;
var ty = 250 - this.y;
var dist = Math.sqrt(tx * tx + ty * ty);
this.velx = (tx / dist);
this.vely = (ty / dist);
if (dist > 0) {
this.x += this.velx;
this.y += this.vely;
}
};
Enemy.prototype.render = function () {
context.fillStyle = '#000000';
context.beginPath();
context.rect(this.x, this.y, 25, 25);
context.fill();
context.closePath();
};
var enemies = [];
for (var i = 0; i < 10; i++) {
// random numbers from 0 (inclusive) to 100 (exclusive) for example:
var randomX = Math.random() * 896;
var randomY = Math.random() * 1303;
console.log(randomX);
console.log(randomY);
if (randomX > 100 && randomX < 1200) {
if (randomX % 2 == 0) {
randomX = 140;
} else {
randomX = 1281;
}
}
if (randomY > 100 && randomY < 75) {
if (randomY % 2 == 0) {
randomY = 15;
} else {
randomY = 560;
}
}
var enemy = new Enemy(randomX, randomY, 0, 0);
enemies.push(enemy);
}
for (var i = 0; i < 15; i++) {
// random numbers from 0 (inclusive) to 100 (exclusive) for example:
var randomX = Math.random() * 200;
var randomY = Math.random() * 403;
console.log(randomX);
console.log(randomY);
if (randomX > 100 && randomX < 1200) {
if (randomX % 2 == 0) {
randomX = 140;
} else {
randomX = 1281;
}
}
if (randomY > 100 && randomY < 75) {
if (randomY % 2 == 0) {
randomY = 15;
} else {
randomY = 560;
}
}
var enemy = new Enemy(randomX, randomY, 0, 0);
enemies.push(enemy);
}
function render() {
context.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < enemies.length; i++) {
var one = enemies[i];
one.update();
one.render();
}
requestAnimationFrame(render);
}
render();
What I want to do is to have a mouseover event for each square; is there a way to do this without using a library?
You can extend your Enemy object doing a region check like this:
Enemy.prototype.isOnEnemy = function(x, y) {
return (x >= this.x && x < this.x + 25 && // 25 = width
y >= this.y && y < this.y + 25); // 25 = height
};
If the provided (x,y) position is inside the rectangle (here assuming width and height of 25) it will return true.
Then add a mousemove event listener to the canvas. Inside adjust the mouse position, then feed the muse position to each enemy object to check:
context.canvas.onmousemove = function(e) {
var rect = this.getBoundingClientRect(), // correct mouse position
x = e.clientX - rect.left,
y = e.clientY - rect.top,
i = 0;
for(; i < enemies.length; i++) { // check each enemy
if (enemies[i].isOnEnemy(x, y)) { // is inside?
console.log("AAAARG...", i); // some action...
}
}
};
Modified fiddle (see console for hits).

How to draw rectangle spiral?

I'm trying to draw a one pixel width line going form the canvas center and evolving with the canvas width/height ratio as it's drawn.
var x = 0;
var y = 0;
var dx = 0;
var dy = -1;
var width = 200;
var height = 40;
//var i = width * height;
var counter = 0;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
setInterval(function(){
//for (i = Math.pow(Math.max(width, height), 2); i>0; i--) {
if ((-width/2 < x <= width/2) && (-height/2 < y <= height/2)) {
console.log("[ " + x + " , " + y + " ]");
ctx.fillStyle = "#FF0000";
ctx.fillRect(width/2 + x, height/2 - y,1,1);
}
if (x === y || (x < 0 && x === -y) || (x > 0 && x === 1-y) || ( -width/2 > x > width/2 ) || ( -height/2 > y > height/2 ) ) {
// change direction
var tempdx = dx;
dx = -dy;
dy = tempdx;
}
counter += 1;
//alert (counter);
x += dx;
y += dy;
}, 1);
I want the spiral to evolve as such:
I'd like to be able to get the ratio between height and width on the equation, so I don't need to calculate the coordinates for points outside the canvas. Also, the purpose is for it to adjust the spiral drawing to the canvas proportions.
Any help would be appreciated.
A friend helped me handling a proper solution. I only have a 1 pixel offset to solve where I need to move all the drawing to the left by one pixel.
Here's the fiddle for the solution achieved: http://jsfiddle.net/hitbyatruck/c4Kd6/
And the Javascript code below:
var width = 150;
var height = 50;
var x = -(width - height)/2;
var y = 0;
var dx = 1;
var dy = 0;
var x_limit = (width - height)/2;
var y_limit = 0;
var counter = 0;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
setInterval(function(){
if ((-width/2 < x && x <= width/2) && (-height/2 < y && y <= height/2)) {
console.log("[ " + x + " , " + y + " ]");
ctx.fillStyle = "#FF0000";
ctx.fillRect(width/2 + x, height/2 - y,1,1);
}
if( dx > 0 ){//Dir right
if(x > x_limit){
dx = 0;
dy = 1;
}
}
else if( dy > 0 ){ //Dir up
if(y > y_limit){
dx = -1;
dy = 0;
}
}
else if(dx < 0){ //Dir left
if(x < (-1 * x_limit)){
dx = 0;
dy = -1;
}
}
else if(dy < 0) { //Dir down
if(y < (-1 * y_limit)){
dx = 1;
dy = 0;
x_limit += 1;
y_limit += 1;
}
}
counter += 1;
//alert (counter);
x += dx;
y += dy;
}, 1);
I nearly crashed my browser trying this. Here, have some code before I hurt myself!
It computes y=f(x) for the diagonal, and y2=f(x) for the antidiagonal, then checks if we're above or below the diagonals when needed.
var x = 0;
var y = 0;
var dx = 0;
var dy = -1;
var width = 200;
var height = 40;
//var i = width * height;
var counter = 0;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
function diag1(x) {
return x*height/width;
}
function diag2(x) {
return -1/diag(x);
}
setInterval(function(){
//for (i = Math.pow(Math.max(width, height), 2); i>0; i--) {
if ((-width/2 < x && x <= width/2) && (-height/2 < y && y <= height/2)) {
console.log("[ " + x + " , " + y + " ]");
ctx.fillStyle = "#FF0000";
ctx.fillRect(width/2 + x, height/2 - y,1,1);
}
if (dx == 0) {
if (dy == 1) {
// moving up
if (y >= diag1(x)) {
// then move left
dy = 0;
dx = -1;
}
}
else {
// moving down
if (y <= diag2(x)) {
// then move right
dy = 0;
dx = 1;
}
}
}
else {
if (dx == 1) {
// moving right
if (y <= diag1(x)) {
// then move up
dy = 1;
dx = 0;
}
}
else {
// moving left
if (y <= diag2(x)) {
// then move down
dy = -1;
dx = 0;
}
}
}
counter += 1;
//alert (counter);
x += dx;
y += dy;
}, 1);

Categories