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);
Related
I'm writing a collision function to help the object stop moving in a certain direction when facing a block/wall. But it seems that the function has been overlapped because I have called this function multiple times in the draw function of the javascript. How do I prevent this from happening if I want to draw an entire level for the game? For reference, I'm recreating the Bounce game on Nokia.
let cX = 150, cY = 250, size = 50; // circle
let velY = 0;
let gravity = 0.3;
let blockX = new Array (999);
let blockY = new Array (999);
function draw() {
background(230);
velY += gravity; // ball falls faster with time
cY += velY;
if (cX - size/2 < 0) // ball touches left wall
{
cX = 0 + size/2;
}
if (cX + size/2 > width) // ball touches right wall
{
cX = width - size/2;
}
// creates object
for (i = 0; i < 13; i++)
{
blockX[i] = size * i;
blockY[i] = size * 7;
square(blockX[i], blockY[i], size);
collision();
}
for (i = 0; i < 4; i++)
{
blockX[i] = size * 7;
blockY[i] = size * (i+3);
square(blockX[i], blockY[i], size);
collision();
}
for (i = 0; i < 6; i++)
{
blockX[i] = 0;
blockY[i] = size * (i+1);
square(blockX[i], blockY[i], size);
collision();
}
if (keyIsDown(LEFT_ARROW)) // move left
{
cX += -3;
}
if (keyIsDown(RIGHT_ARROW)) // move right
{
cX += 3;
}
if (keyIsDown(UP_ARROW) && cY + size/2 == blockY[i]) // ball touches ground
{
velY = -12;
}
}
function collision()
{
if (cY + size/2 > blockY[i] && cY < blockY[i] && blockX[i]-5 <= cX && cX <= blockX[i]+5 + size) // ball touches top of block
{
cY = blockY[i] - size/2;
velY = -0.4 * velY;
}
if (cX - size/2 < blockX[i] + size && cX - size/2 > blockX[i] + size/2 && blockY[i] <= cY && cY <= blockY[i] + size) // ball touches right of block
{
cX = blockX[i] + size * 3/2;
}
if (cY - size/2 < blockY[i] + size && cY > blockY[i] + size/2 && blockX[i]-5 <= cX && cX <= blockX[i]+5 + size) // ball touches bottom of block
{
cY = blockY[i] + size * 3/2;
}
if (cX + size/2 > blockX[i] && cX + size/2 < blockX[i] + size/2 && blockY[i] <= cY && cY <= blockY[i] + size) // ball touches left of block
{
cX = blockX[i] - size/2;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js"></script>
The occurring problem is that the ball can't jump if it's standing on a higher block than the floor.
I have the first moving object that moves down the screen by y axis, I have another object that is positioned 400 pixels below the first moving object, but I want the first moving object to go around the object that is stationary which is 400 pixels down.
How can I achieve this? I tried this:
var tX = target.x;
var tY = target.y;
var magnitude = 0.2;
dx = tX + player.x;
dy = tY + player.y;
angle = Math.atan(dy, dx);
player.x = player.x - magnitude * Math.cos(angle);
player.y = player.y - magnitude * Math.sin(angle);
What's happening is the first moving object is just bouncing off the object that's 400 pixels below but I need the first moving object to go around this below 400 pixels object. how can this be achieved?
More code:
var resArrayPlayerHome;
function nearestToPlayer(player) {
resArrayPlayerHome = arrayPlayerHome.filter(player2 => player2.shirtNumber != player.shirtNumber);
var numObj = findClosest2({ x: player.x, y: player.y });
const result = resArrayPlayerHome.filter(player => player.shirtNumber == numObj.shirtNumber);
return result;
}
const findClosest2 = (target) => resArrayPlayerHome
.reduce((a, item) => getDist(item, target) < getDist(a, target) ? item : a);
var nearPlayer = nearestToPlayer(player);
for (var j = 0; j < arrayPlayerHome.length; j++) {
topY = arrayPlayerHome[j].y - 15;
bottomY = arrayPlayerHome[j].y + 15;
var lengthTop = topY + bottomY;
leftX = arrayPlayerHome[j].x - 15;
rightX = arrayPlayerHome[j].x + 15;
var lengthLeft = leftX + rightX;
if (arrayPlayerHome[j].shirtNumber == nearPlayer[0].shirtNumber && (((player.y - 30) <= arrayPlayerHome[j].y && (player.y + 30) >= arrayPlayerHome[j].y) && ((player.x - 30) <= arrayPlayerHome[j].x && (player.x + 30) >= arrayPlayerHome[j].x))) {
//this.angle1 = Math.atan2(target.y - player.y, target.x - player.x);
//var tX = arrayPlayerHome[j].x - player.x;
//var tY = arrayPlayerHome[j].y - player.y;
var magnitude = 0.2;
dx = arrayPlayerHome[j].x - player.x;
dy = arrayPlayerHome[j].y - player.y;
let dist = Math.hypot(dx, dy);
dx /= dist;
dy /= dist;
if (dx != 0 || dy != 0) {
if (
player.x >= arrayPlayerHome[j].x + 30 ||
player.x <= arrayPlayerHome[j].x - 30 ||
player.y >= arrayPlayerHome[j].y + 30 ||
player.y <= arrayPlayerHome[j].y - 30
) {for (var x = 0; x < 4; x++) {
for (var y = 0; y < 4; y++) {
var tX = target.x;
var tY = target.y;
var magnitude = 0.2;
dx = tX + player.x;
dy = tY + player.y;
angle = Math.atan(dy, dx);
player.x = player.x - magnitude * Math.cos(angle);
player.y = player.y - magnitude * Math.sin(angle);
}
}
}else{
console.log("ssssss");
for (var x = 0; x < 4; x++) {
for (var y = 0; y < 4; y++) {
var tX = target.x;
var tY = target.y;
var magnitude = 0.2;
dx = tX + player.x;
dy = tY + player.y;
angle = Math.atan(dy, dx);
player.x = player.x - magnitude * Math.cos(angle);
player.y = player.y - magnitude * Math.sin(angle);
}
}
}
}
}
}
I'm coding a maze generator using the backtracking algorithm and p5.js library, but when I was coding a loop to set random positions in the maze, I noticed an error in javascript: "maze[(x - 2)] is undefined", but this variable is declared! I tried to read about data structures, but I couldn't find a solution for this problem.
What is happening?
Can someone help me?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "utf-8">
<title>Maze</title>
</head>
<body>
<script src = "https://cdn.jsdelivr.net/npm/p5#1.4.0/lib/p5.js"></script>
<script>
var width = 720;
var height = 400;
var x, y;
var maze = Array(39).fill(Array(71).fill(0));
function setup()
{
createCanvas(720, 400);
}
function draw()
{
//inicial position
x = 1;
y = 1;
maze[x][y] = 1;
//grid
strokeWeight(1);
stroke('BLACK');
for (x = 0; x <= 720; x += 10) line(x, 0, x, 400);
for (y = 0; y <= 400; y += 10) line(0, y, 720, y);
found = 0;
do
{
direction = Math.floor(Math.random() * 4);
if (direction == 0 && (y != width - 2 && maze[x][y + 2] == 0))
{
x2 = x;
y2 = y;
y += 2;
maze[x][y] = 1;
maze[x][y-1] = 1;
found = 1;
}
else if (direction == 1 && (x > 1 && maze[x - 2][y] == 0))
{
x2 = x;
y2 = y;
x -= 2;
maze[x][y] = 1;
maze[x + 1][y] = 1;
found = 1;
}
else if (direction == 2 && (y > 1 && maze[x][y - 2] == 0))
{
x2 = x;
y2 = y;
y -= 2;
maze[x][y] = 1;
maze[x][y + 1] = 1;
found = 1;
}
else if (direction == 3 && (x != height - 2 && maze[x + 2][y] == 0))
{
x2 = x;
y2 = y;
x += 2;
maze[x][y] = 1;
maze[x - 1][y] = 1;
found = 1;
}
} while (found == 0);
noLoop();
}
</script>
</body>
</html>
There a few issues with this code.
Lets start with why you getting that error. The issue is that you are reusing global x and y variables in for loop that draw lines, when the loop is done, it set these variables larger than your maze array.
This brings us to second issue: maze array is smaller than width/height of the canvas.
The code is not 100% working so I don't exactly understand what it supposed to do, but it seems x and y should not exceed width/height, however you have a condition y != width - 2 which will be true even if y is bigger than width (btw, shouldn't it be height instead??)
And finally, through out the code you have multiple places with 720 and 400, instead of using width and height variables. This is a bad practice.
const width = 720;
const height = 400;
var x, y;
var maze = Array(width+1).fill(Array(height+1).fill(0));
function setup()
{
createCanvas(width, height);
}
function draw()
{
//inicial position
x = 1;
y = 1;
maze[x][y] = 1;
//grid
strokeWeight(1);
stroke('BLACK');
for (let x = 0; x <= width; x += 10) line(x, 0, x, height);
for (let y = 0; y <= height; y += 10) line(0, y, width, y);
found = 0;
do
{
direction = Math.floor(Math.random() * 4);
if (direction == 0 && (y <= height - 2 && maze[x][y + 2] == 0))
{
x2 = x;
y2 = y;
y += 2;
maze[x][y] = 1;
maze[x][y-1] = 1;
found = 1;
}
else if (direction == 1 && (x > 1 && maze[x - 2][y] == 0))
{
x2 = x;
y2 = y;
x -= 2;
maze[x][y] = 1;
maze[x + 1][y] = 1;
found = 1;
}
else if (direction == 2 && (y > 1 && maze[x][y - 2] == 0))
{
x2 = x;
y2 = y;
y -= 2;
maze[x][y] = 1;
maze[x][y + 1] = 1;
found = 1;
}
else if (direction == 3 && (x <= width - 2 && maze[x + 2][y] == 0))
{
x2 = x;
y2 = y;
x += 2;
maze[x][y] = 1;
maze[x - 1][y] = 1;
found = 1;
}
} while (found == 0);
noLoop();
}
<script src = "https://cdn.jsdelivr.net/npm/p5#1.4.0/lib/p5.js"></script>
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;
}
};
I am working with a javascript animation that shows ripples in water in html canvas.
This is the javascript code and the jfiddle link
(function(){
var canvas = document.getElementById('c'),
/** #type {CanvasRenderingContext2D} */
ctx = canvas.getContext('2d'),
width = 400,
height = 400,
half_width = width >> 1,
half_height = height >> 1,
size = width * (height + 2) * 2,
delay = 30,
oldind = width,
newind = width * (height + 3),
riprad = 3,
ripplemap = [],
last_map = [],
ripple,
texture,
line_width = 20,
step = line_width * 2,
count = height / line_width;
canvas.width = width;
canvas.height = height;
with (ctx) {
fillStyle = '#a2ddf8';
fillRect(0, 0, width, height);
fillStyle = '#07b';
save();
rotate(-0.785);
for (var i = 0; i < count; i++) {
fillRect(-width, i * step, width * 3, line_width);
}
restore();
}
texture = ctx.getImageData(0, 0, width, height);
ripple = ctx.getImageData(0, 0, width, height);
for (var i = 0; i < size; i++) {
last_map[i] = ripplemap[i] = 0;
}
/**
* Main loop
*/
function run() {
newframe();
ctx.putImageData(ripple, 0, 0);
}
/**
* Disturb water at specified point
*/
function disturb(dx, dy) {
dx <<= 0;
dy <<= 0;
for (var j = dy - riprad; j < dy + riprad; j++) {
for (var k = dx - riprad; k < dx + riprad; k++) {
ripplemap[oldind + (j * width) + k] += 128;
}
}
}
/**
* Generates new ripples
*/
function newframe() {
var a, b, data, cur_pixel, new_pixel, old_data;
var t = oldind; oldind = newind; newind = t;
var i = 0;
// create local copies of variables to decrease
// scope lookup time in Firefox
var _width = width,
_height = height,
_ripplemap = ripplemap,
_last_map = last_map,
_rd = ripple.data,
_td = texture.data,
_half_width = half_width,
_half_height = half_height;
for (var y = 0; y < _height; y++) {
for (var x = 0; x < _width; x++) {
var _newind = newind + i, _mapind = oldind + i;
data = (
_ripplemap[_mapind - _width] +
_ripplemap[_mapind + _width] +
_ripplemap[_mapind - 1] +
_ripplemap[_mapind + 1]) >> 1;
data -= _ripplemap[_newind];
data -= data >> 5;
_ripplemap[_newind] = data;
//where data=0 then still, where data>0 then wave
data = 1024 - data;
old_data = _last_map[i];
_last_map[i] = data;
if (old_data != data) {
//offsets
a = (((x - _half_width) * data / 1024) << 0) + _half_width;
b = (((y - _half_height) * data / 1024) << 0) + _half_height;
//bounds check
if (a >= _width) a = _width - 1;
if (a < 0) a = 0;
if (b >= _height) b = _height - 1;
if (b < 0) b = 0;
new_pixel = (a + (b * _width)) * 4;
cur_pixel = i * 4;
_rd[cur_pixel] = _td[new_pixel];
_rd[cur_pixel + 1] = _td[new_pixel + 1];
_rd[cur_pixel + 2] = _td[new_pixel + 2];
}
++i;
}
}
}
canvas.onmousemove = function(/* Event */ evt) {
disturb(evt.offsetX || evt.layerX, evt.offsetY || evt.layerY);
};
setInterval(run, delay);
// generate random ripples
var rnd = Math.random;
setInterval(function() {
disturb(rnd() * width, rnd() * height);
}, 700);
})();
The issue is the ripple spills over from one side of the canvas and animates on the opposite side, I want to keep them from moving into the opposite sides.
Thanks!
Stopping waves at edges
To stop the propagation of the waves across the edges you need to limit the function that adds the disturbance so that it does not write past the edges.
So change...
function disturb(dx, dy) {
dx <<= 0;
dy <<= 0;
for (var y = dy - riprad; y < dy + riprad; y++) {
for (var x = dx - riprad; x < dx + riprad; x++) {
ripplemap[oldind + (y * width) + x] += 128;
}
}
}
to...
function disturb(dx, dy) {
dx <<= 0;
dy <<= 0;
for (var y = dy - riprad; y < dy + riprad; y++) {
for (var x = dx - riprad; x < dx + riprad; x++) {
// don't go past the edges.
if(y >= 0 && y < height && x >= 0 && x < width){
ripplemap[oldind + (y * width) + x] += 128;
}
}
}
}
And you also need to change the wave propagation that is in the function newFrame. The propagation is controlled by the value in data. A value of zero means no wave and no propagation. So test if the pixel is on the edge. If the pixel is an edge pixel then just stop the wave by setting data to zero. To save CPU cycles I have added to vars h, w that are height - 1 and width - 1 so you don't have to perform the subtraction twice for every pixel.
Change the code in the function newFrame from...
for (var y = 0; y < _height; y++) {
for (var x = 0; x < _width; x++) {
var _newind = newind + i, _mapind = oldind + i;
data = (
_ripplemap[_mapind - _width] +
_ripplemap[_mapind + _width] +
_ripplemap[_mapind - 1] +
_ripplemap[_mapind + 1]) >> 1;
}
To...
var w = _width - 1; // to save having to subtract 1 for each pixel
var h = _height - 1; // dito
for (var y = 0; y < _height; y++) {
for (var x = 0; x < _width; x++) {
var _newind = newind + i, _mapind = oldind + i;
// is the pixel on the edge
if(y === 0 || x === 0 || y === h || x === w){
data = 0; // yes edge pixel so stop propagation.
}else{
// not on the edge so just do as befor.
data = (
_ripplemap[_mapind - _width] +
_ripplemap[_mapind + _width] +
_ripplemap[_mapind - 1] +
_ripplemap[_mapind + 1]) >> 1;
}
This is not perfect as it will dampen the waves bouncing from the sides but you don't have much CPU time so it is a good solution.
Some notes.
Use requestAnimationFrame rather than setInterval(run, delay) to time the animation as you will cause some devices to crash the page with the code you currently have, if the device can not handle the CPU load. requestAnimationFrame will stop this happening.
Change the run function to
function run() {
newframe();
ctx.putImageData(ripple, 0, 0);
requestAnimationFrame(run);
}
At bottom of code remove setInterval(run,delay); and add
requestAnimationFrame(run);
And change the second setInterval to
var drops = function() {
disturb(rnd() * width, rnd() * height);
setTimeout(drops,700)
};
drops();
NEVER use setInterval, especially for this type of CPU intensive code. You will cause many machines to crash the page. Us setTimeout or requestAnimationFrame instead.
Also remove the with statement where you create the texture.