Trying to put together a small game for practice and I came across a issue. The point is to catch the goblin but I don't know how to make the game detect that player has hit the goblin.
So far you can only detect touched if your position Y and X matches with the enemies axis. How can I either increase the width for both or a better way of detecting touch.
try it out here: https://jsfiddle.net/acbkk7cs/4/
Style:
#map{
margin: 0 auto;
height: 510px;
width: 510px;
background-image:url(background.png);
}
.character{
background-image:url(character.png);
z-index:1;
position: relative;
top: 150px;
left: 150px;
height: 32px;
width: 33px;
}
.monster{
background-image:url(monster.png);
position: relative;
height: 32px;
width: 30px;
}
Javascript:
$(document).ready(function(){
var char = {
player : $(".character"),
x: 150,
y: 150
};
var monster = {
npc : $(".monster"),
x: 100,
y: 100
};
var keypush = {};
$(document).on("keydown keyup", function(e){
var keyN = e.which;
keypush[keyN] = e.type === "keydown";
});
function moveCharacter(){
if (keypush[37]) char.x -=2;
if (keypush[38]) char.y -=2;
if (keypush[39]) char.x +=2;
if (keypush[40]) char.y +=2;
char.player.css({transform:"translate3D("+ char.x +"px, "+ char.y +"px, 0)"});
monster.npc.css({transform:"translate3D("+ monster.x +"px, "+ monster.y +"px, 0)"});
var playerPosX = char.player.position().top;
var monsterPosX = monster.npc.position().top;
var playerPosY = char.player.position().left;
var monsterPosY = monster.npc.position().left;
if ( playerPosX === monsterPosX
&& playerPosY === monsterPosY
){
document.getElementById("pos").innerHTML="Touched";
} else {
document.getElementById("pos").innerHTML="Off";
}
//backend logs
console.log(char.x);
}
(function engine(){
moveCharacter();
window.requestAnimationFrame(engine);
}());
});
HTML:
<div id = "map">
<div class = "character">
</div>
<div class = "monster">
<p id = "pos" style = "color:yellow;font- weight:bold;position:relative;left:5px;font-size:20px;">Position: </p>
</div>
</div>
The easiest and fastest way is to calculate the distance between the character and the monster: fiddle using circle collision.
var dx = playerPosX - monsterPosX,
dy = playerPosY - monsterPosY,
touchDistance = 30;
if ( Math.sqrt( dx * dx + dy * dy ) < touchDistance )
document.getElementById("pos").innerHTML="Touched";
else
document.getElementById("pos").innerHTML="Off";
Here, I eyeballed the touchDistance, but it's actual value should be the sum of the radius of the player sprite and the radius of the monster sprite. This works best if they are circles:
/**
* (ax,ay) and (bx,by) are the center positions, and ar, br their radii.
*/
function circleCollision( ax, ay, ar, bx, by, br ) {
return Math.sqrt( (ax-bx)*(ax-bx) + (ay-by)*(ay-by) ) <= ar + br;
}
A second common way to determine collision is to use 'bounding boxes', or in this case, rectangles/squares: Fiddle using rectangle collision.
function rectangleCollision( pLeft, pTop, pRight, pBottom, mLeft, mTop, mRight, mBottom ) {
return
( (pLeft >= mLeft && pLeft < mRight ) || (pRight >= mLeft && pRight < mRight ) )
&& ( (pBottom >= mTop && pBottom < mBottom) || (pTop >= mTop && pTop < mBottom) )
}
Related
Im working on a project game and I want to see if a div "rope" hits one of my pictures class='Fish1'.
I managed to get it to work by using document.GetElementsById("fish") but when I change it to document.getElementsByClassName('fish1') it gives me an error saying:
TypeError: Cannot read properties of undefined (reading
'getBoundingClientRect') at checkCollision
How can i fix this error? Also how can I check the height during the transition of a div and have it give me the height that it is at that point in the transition?
I've tried this:
function checkCollision(rope, fishy) {
var line = rope;
var fishy = document.getElementsByClassName('fish1');
var ropeRect = line.getBoundingClientRect();
for (var i = 0; i < fishy.length; i++) {
var fishyRect = fishy[i].getBoundingClientRect(i);
}
return (ropeRect.right >= fishyRect.left &&
ropeRect.left <= fishyRect.right) &&
(ropeRect.bottom >= fishyRect.top &&
ropeRect.top <= fishyRect.bottom);
}
And this is what worked for the id only:
function checkCollision(line, fishy) {
var line = rope;
var fishy = document.getElementById('fish');
var lineRect = line.getBoundingClientRect();
var fishyRect = fishy.getBoundingClientRect();
return (lineRect.right >= fishyRect.left &&
lineRect.left <= fishyRect.right) &&
(lineRect.bottom >= fishyRect.top &&
lineRect.top <= fishyRect.bottom);
}
Checking collision:
You can check if two elements overlap by using conditions that compare their x,y coords also involving their width, height:
//returns true if obj1 and obj2 collided, otherwise false
function checkCollision(obj1, obj2) {
const rect1 = obj1.getBoundingClientRect();
const rect2 = obj2.getBoundingClientRect();
return (rect1.x + rect1.width >= rect2.x && rect1.x <= rect2.x + rect2.width) &&
(rect1.y + rect1.height >= rect2.y && rect1.y <= rect2.y + rect2.height)
}
Moving the elements:
Once you have the function, you can call it inside your loop after changing the position of the elements at each iteration.
You didn't share any other detail apart the not working checkCollision function so I have no idea how did you perform the motion or probably you didn't at all yet.
Here I used Window.requestAnimationFrame()
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
The window.requestAnimationFrame() method tells the browser that you
wish to perform an animation and requests that the browser calls a
specified function to update an animation before the next repaint. The
method takes a callback as an argument to be invoked before the
repaint.
Here in this example, I animate a group of .fish elements over a sin wave crossing a line in the middle. Every time an element overlaps with the rope, it calls the function collisionOccurred printing on console its id property.
Since you have the exact element partecipating to the event you are free to fetch anything about it like for example the exact position where it is at in that instant (or the height you were expecting to know).
Later I also added the events fishEnter and fishLeave that fire respectively when the element enters and leaves the rope area.
const container = document.querySelector('body');
const rope = document.getElementById('rope');
const fishes = document.getElementsByClassName('fish');
let start = null;
let containerWidth, containerHeight;
beginAnimation();
//begins the fishes animation
function beginAnimation() {
window.requestAnimationFrame(moveFish);
}
//returns the x,y coords of a sin function
function getSin(millisecondsPast, cycleDuration = 2000) {
//ranging 0-1 in the span of cycleDuration (ms)
const progress = (millisecondsPast % cycleDuration) / cycleDuration;
//=~6.28
const twoPi = 2 * Math.PI;
//x ranging from 0-6.28 in the span of cycleDuration
const x = twoPi * progress;
//sin(x) (radius=1)
const y = Math.sin(x);
return {
//[0-1] (in the span of cycleDuration ms)
x: progress,
//[-1,+1]
y: y
}
}
//moves all the fishes following a sin wave
function moveFish(timestamp) {
const containerWidth = parseInt(container.offsetWidth);
const containerHeight = parseInt(container.offsetHeight);
//keep track of the animation progress
if (!start) start = timestamp;
let progress = timestamp - start;
//for all the fishes
for (fish of fishes) {
//calculate the x and y of a sin wave
//spread across the container width in the 0-2pi space
const duration = parseInt(fish.dataset.duration);
const vertical = parseInt(fish.dataset.verticalspan);
const sin = getSin(progress, duration);
let x = sin.x * containerWidth;
let y = sin.y * (vertical/2) + (containerHeight / 2);
//change the position of the current fish
fish.style.left = `${x}px`;
fish.style.top = `${y}px`;
const didcollide = checkCollision(rope, fish);
const overlapping = (fish.dataset.overlapping == 'true') ? true : false;
//calls fishEnter if the fish entered in the space of the rope
if (!overlapping && didcollide) {
fish.dataset.overlapping = 'true';
fishEnter(fish);
}
//calls fishLeave if the fish left the space of the rope
else if (overlapping && !didcollide) {
fish.dataset.overlapping = 'false';
fishLeave(fish);
}
//calls collisionOccurred if the fish collided with the rope
if (didcollide)
collisionOccurred(fish);
}
//render next iteration
window.requestAnimationFrame(moveFish);
}
//returns true if obj1 and obj2 collided, otherwise false
function checkCollision(obj1, obj2) {
const rect1 = obj1.getBoundingClientRect();
const rect2 = obj2.getBoundingClientRect();
const didcollide =
(rect1.x + rect1.width >= rect2.x && rect1.x <= rect2.x + rect2.width) &&
(rect1.y + rect1.height >= rect2.y && rect1.y <= rect2.y + rect2.height);
return didcollide;
}
//gets fired when the fish enters in the space of the rope
function fishEnter(target) {
target.classList.add('crossing');
console.log(`The fish id: ${target.id} entered in the rope space!`);
}
//gets fired when the fish leaves in the space of the rope
function fishLeave(target) {
target.classList.remove('crossing');
console.log(`The fish id: ${target.id} left the rope space!`);
}
//gets fired when the collision event occurs
function collisionOccurred(target) {
//console.log(`The fish id: ${target.id} crossed the rope!`);
}
*{
box-sizing: border-box;
}
body {
position: relative;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
}
#rope {
--size: 80px;
--border: 5px;
position: absolute;
top: calc(50% + var(--border) - var(--size) / 2) ;
width: 100%;
height: var(--size);
background: brown;
display: flex;
justify-content: center;
align-items: center;
font-weight: 600;
outline: solid var(--border) darkorange;
color: darkorange;
font-size: 2rem;
}
.fish {
position: absolute;
width: 80px;
line-height: 2rem;
background: blue;
font-weight: 600;
outline: solid darkblue;
text-align: center;
font-size: 1.5rem;
}
.fish::after {
content: attr(id);
color: white;
}
button {
padding: 1rem;
cursor: pointer;
}
.crossing{
background: yellow;
}
.crossing::after{
color: black !important;
}
<div id="rope">ROPE AREA</div>
<div id="fish1" class="fish" data-duration="5000" data-verticalspan="200"></div>
<div id="fish2" class="fish" data-duration="8000" data-verticalspan="300"></div>
<div id="fish3" class="fish" data-duration="10000" data-verticalspan="100"></div>
<div id="fish4" class="fish" data-duration="3000" data-verticalspan="500"></div>
I need to make it so that it is possible to connect the dice only with straight lines. But to do this, I need each subsequent dice to have a common "top" or "left" coordinate with the previous dice and at the same time a partial random is preserved.
Here is an example:
Here is my code:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Игральные кости</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="icon" type="image/png" href="./dice.png">
</head>
<body>
<div id="main-container">
<div id="square-container">
</div>
<canvas id="overlay"></canvas>
</div>
</body>
<script src="./script.js"></script>
</html>
html,
body {
margin: 0;
padding: 0;
}
#main-container {
width: 100%;
margin: 10px
}
#square-container {
width: 700px;
height: 600px;
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
background: #dddddd;
border: 5px solid #6b6b6b;
border-radius: 5px;
z-index: 1;
}
#overlay {
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
position: absolute;
top: 0;
left: 0;
}
.js-is-target {
position: absolute;
width: 56px;
height: 56px;
/*background: blue;*/
border-radius: 30px;
}
.square {
top: 13px;
left: 13px;
position: relative;
width: 30px;
height: 30px;
background: red;
background-size: contain;
border-radius: 5px;
z-index: 10;
}
.active-target {
box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.5);
}
//links to images of dice
var imgArr = [
'./dotsOnDice/dice1.jpg',
'./dotsOnDice/dice2.jpg',
'./dotsOnDice/dice3.jpg',
'./dotsOnDice/dice4.jpg',
'./dotsOnDice/dice5.jpg',
'./dotsOnDice/dice6.jpg'
]
//random number from 3 to 6
var randomAmount = Math.floor(Math.random() * (6 - 3 + 1)) + 3;
//limit for recursion
var limitDice = 0;
function addDice() {
// if the limit is equal to a random number, the recursion function is interrupted
if (limitDice === randomAmount) {
return
}
//random number from 2 to 5
let randomAmountImg = Math.floor(Math.random() * (5 - 2 + 1)) + 2;
//random numbers for dice coordinates
let position = {
top: Math.random() * 520,
left: Math.random() * 600
}
//saved previous coordinates for comparison
let positionAfter = {
top: 0,
left: 0
}
//creating an id that we press later
let idSquare = 'idSquare-' + limitDice;
//subtract the previous coordinates from the current coordinates
let top = position.top - positionAfter.top;
let left = position.left - positionAfter.left;
//we compare the positions of the previous coordinates to avoid a large number of overlaps
if (top > 50 || left > 50) {
positionAfter.top = position.top;
positionAfter.left = position.left;
//adding dice to the playing field
let square = $('<div class="js-is-target"><div id="' + idSquare + '" class="square"></div></div>');
square.appendTo('#square-container').css({ left: position.left + 'px', top: position.top + 'px' });
if (limitDice === 0) {
$('#idSquare-0').css('background-image', 'url(' + imgArr[randomAmountImg] + ')');
} else if (limitDice === 1) {
$('#idSquare-1').css('background-image', 'url(' + imgArr[randomAmountImg] + ')');
} else if (limitDice === 2) {
$('#idSquare-2').css('background-image', 'url(' + imgArr[randomAmountImg] + ')');
} else if (limitDice === 3) {
$('#idSquare-3').css('background-image', 'url(' + imgArr[randomAmountImg] + ')');
} else if (limitDice === 4) {
$('#idSquare-4').css('background-image', 'url(' + imgArr[randomAmountImg] + ')');
} else if (limitDice === 5) {
$('#idSquare-5').css('background-image', 'url(' + imgArr[randomAmountImg] + ')');
}
limitDice += 1;
}
addDice();
}
addDice();
//here we store the canvas field
const canvas = document.getElementById('overlay');
//here we set the format in 2D for canvas
const ctx = canvas.getContext('2d');
//here we store the width and height of the canvas field. We set the height and width in HTML
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
//in the variable we store the element that we clicked on
let targetEl = null;
//in the variable we store the coordinates of the center of the element we clicked on
let targetCenter = null;
//we check whether you clicked on the mouse or not
var down = false;
//coordinates of the pressed cursor mouseCoords
var mouseCoords = {
x: 0,
y: 0
}
//coordinates of the starting point
var centerCoords = {
x: 760,
y: 280
}
//в этой функции мы вычисляем где центр у прямоугольника
function getCenter(el) {
//if not true, the function terminates and returns null
if (!el) return null;
//we save the coordinates in the rect constant using the getBoundingClientRect method
const rect = el.getBoundingClientRect();
//returning the x and y coordinates. Here we calculate the center of the rectangle
return {
x: rect.left + rect.width / 2 + window.scrollX,
y: rect.top + rect.height / 2 + window.scrollY,
}
}
//connection points
function joinPoints(ctx, from, to) {
//dotted line size
const dashSize = 10;
//draw a dotted line and write the size in the arguments
ctx.setLineDash([dashSize]);
//setting the gradient
var stroke = ctx.createLinearGradient(from.x, from.y, to.x, to.y);
//setting the color and position
stroke.addColorStop(0, 'blue');
stroke.addColorStop(1, 'red');
//passing the colors to the strokeStyle property to set the color
ctx.strokeStyle = stroke;
//line thickness
ctx.lineWidth = 5;
//draw a contour (line)
ctx.beginPath();
//moves the contour point to the specified coordinates without drawing a line. Starting point
ctx.moveTo(from.x, from.y);
//adds a new contour point and creates a line to this point from the last specified point. End point
ctx.lineTo(to.x, to.y);
//draw a line
ctx.stroke();
}
//when you click the mouse
$(document).mousedown(function (e) {
down = true;
mouseCoords.x = e.pageX;
mouseCoords.y = e.pageY;
});
//when moving the mouse
$(document).mousemove(function (event) {
if (down) {
mouseCoords.x = event.pageX;
mouseCoords.y = event.pageY;
console.log(mouseCoords.x + ' ' + mouseCoords.y);
}
});
//when we release the mouse button
$(document).mouseup(function (e) {
down = false;
});
$(".js-is-target").hover(function (e) {
if (down) {
down = false;
//we transmit data about which js-is-target we clicked on
targetEl = e.target;
//we write down where the center of the object we clicked on is. We calculate using the getCenter() function
targetCenter = getCenter(targetEl);
//changing the coordinates of the starting point
centerCoords.x = targetCenter.x;
centerCoords.y = targetCenter.y;
}
});
//first we count, then we draw a line from ctx.lineTo to ctx.moveTo
function drwaDot() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = 'red';
ctx.moveTo(mouseCoords.x, mouseCoords.y);
ctx.lineTo(centerCoords.x, centerCoords.y);
ctx.stroke();
};
//the drawing interval of our line
var intervalId = setInterval(function () {
if (down) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
joinPoints(ctx, { x: centerCoords.x, y: centerCoords.y }, mouseCoords);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}, 10);
jsfiddle link
under the comment "//we compare the positions of the previous coordinates to avoid a large number of overlaps" changed the condition:
if(top < 10 || left < 10){
dice spawn code
}
That's what came out of it:
I am trying to create a function for my game of pong for the ball to move around within a frame. When I call the function I am using a function called setStyle() to change the position of the ball every 100 milliseconds.
After I saw that it did not work I console logged the left and top positions to find that the function was getting called but the amount of pixels it was located at was the same.
//Javascript
function ballMove(){
var x = 5;
var y = 5;
var maxHeight = getStyle('frame', 'top') + getStyle('frame', 'height');
var maxWidth = getStyle('frame', 'left') + getStyle('frame', 'width');
var ballLeft = getStyle('ball', 'left');
var ballTop = getStyle('ball', 'top');
var minLeft = getStyle('frame', 'left');
var minTop = getStyle('frame', 'top');
if(ballTop >= minTop){
setStyle('ball', 'top', (ballTop + y) + "px");
}
else if(ballTop <= maxHeight){
setStyle('ball', 'top', (ballTop - y) + "px");
}
if(ballLeft >= minLeft){
setStyle('ball', 'left', (ballLeft + x) + "px");
}
else if(ballLeft <= maxWidth){
setStyle('ball', 'left', (ballLeft - x) + "px");
}
console.log(ballLeft);
console.log(ballTop);
}
function startIntervals(){
setInterval(ballMove, 100);
console.log("starting...");
}
Here is my css
#ball{
position: relative;
background-color: white;
height: 10px;
width: 10px;
left: 50px;
}
and the html
<body onload="startIntervals();">
<div id="main-div">
<div id="title">Pong</div>
<div id="frame">
<div id="paddlewasd">
</div>
<div id="paddlearrow">
</div>
<div id="ball">
</div>
</div>
</div>
I expected the ball to move but instead the function gets called and the left and top positions stay the same.My code
Although it depends fairly significantly on the rest of the code that wasn't posted the following implementations of getStyle and setStyle along with a css defintion of #frame allows the ball to move diagonally 5pixels right and down every 100 milliseconds
function getStyle(id, style) {
var element = document.getElementById(id);
var computedStyle = window.getComputedStyle(element);
var selectedStyle = computedStyle[style];
// Check if the style value matches a 'pixel' string format, if it does extract the numeric part and parse it as an integer
var pixelTokens = /([\d]*)px$/.exec(selectedStyle);
if (pixelTokens) {
return parseInt(pixelTokens[1]);
}
return selectedStyle;
}
function setStyle(id, style, value) {
var element = document.getElementById(id);
return element.style[style] = value;
}
function ballMove() {
var x = 5;
var y = 5;
var maxHeight = getStyle('frame', 'top') + getStyle('frame', 'height');
var maxWidth = getStyle('frame', 'left') + getStyle('frame', 'width');
var ballLeft = getStyle('ball', 'left');
var ballTop = getStyle('ball', 'top');
var minLeft = getStyle('frame', 'left');
var minTop = getStyle('frame', 'top');
if (ballTop >= minTop) {
setStyle('ball', 'top', (ballTop + y) + "px");
} else if (ballTop <= maxHeight) {
setStyle('ball', 'top', (ballTop - y) + "px");
}
if (ballLeft >= minLeft) {
setStyle('ball', 'left', (ballLeft + x) + "px");
} else if (ballLeft <= maxWidth) {
setStyle('ball', 'left', (ballLeft - x) + "px");
}
console.log(ballLeft);
console.log(ballTop);
}
function startIntervals() {
setInterval(ballMove, 100);
console.log("starting...");
}
#ball {
position: relative;
background-color: white;
height: 10px;
width: 10px;
left: 50px;
}
#frame {
width: 300px;
height: 300px;
top: 0px;
left: 0px;
background-color: green;
}
<body onload="startIntervals();">
<div id="main-div">
<div id="title">Pong</div>
<div id="frame">
<div id="paddlewasd">
</div>
<div id="paddlearrow">
</div>
<div id="ball">
</div>
</div>
</div>
</body>
The Goal :
The idea is to create an element grid (image gallery for exemple) that would infinitely loop on itself scrolling on two axes.
There should be no holes nor too much randomness (avoid having the same element randomly falling aside from itself). And this no matter how many element there is in the first place (it seems easy to infinite loop through a grid of 16 (4*4) elements, not that much over 17 (17*1). (My guess is that any prime number of elements is by definition a pain to make a grid of).
So I actually found a wonderful working exemple :
http://www.benstockley.com/
It's actually really close (probably better) than what I was imagining. Now it's using canvas and i tried looking at the javascript and it's a 30000 minified lines long script so I really can't read any core logic behind it.
Math side / Problem solving :
This is the logic and theory behind the problem, the math involved and the mindset.
How the program should process the list of elements so we have no holes, infinite grid, best repartion of the elements over all the axes.
My guess is that it somehow has to be procedural. I'm not sure if we should create grids or loop through the list on every axes (kind of like sudoku ? i don't know);
Pratical side / UI / UX :
Any advice on the technologies involved, pieces of code. I'm guessing it classic DOM is out of the way and that somehow canvas or 2D webgl will be mandatory. But I would love to hear any advice on this side.
Besides all the elements grid processing. The UI and UX involved in exploring a 2D infinite or vast layout in DOM or renderer is somehow not classical. The best technologies or advice on doing this are welcome.
Exemples :
I would welcome any working exemple that somewhat share an aspect of this problem.
I've got a fiddle that's set up to arrange your 2d grid.
It functions by using horizontal and vertical "step sizes". So, moving one step right in the grid advances the horizontal step size in the list. Moving one step down advances the vertical step size in the list (and they accumulate).
We allow the advances in the list to loop back to zero when the end is reached.
It likely makes sense to use a horizontal step size of 1 (so a row of your grid will maintain your list order). For the vertical step size, you want an integer that shares no common divisors with the list length. Though it's no guarantee, I used the (rounded) square root of the list length as something that will work in lots of cases.
I'll reproduce the fiddle here:
var list = ['red','green','blue','cyan','orange','yellow','pink'];
var hstep = 1;
var vstep = Math.ceil(Math.sqrt(list.length));
function getListItem(x,y) {
var index = x * hstep + y * vstep;
return list[index % list.length];
}
var elementSize = 30;
var gutterSize = 10;
function getOffset(x,y) {
return [10 + (elementSize + gutterSize) * x, 10 + (elementSize + gutterSize) * y];
}
var frame = $('.frame');
function drawElement(x,y) {
var listItem = getListItem(x,y);
var offsets = getOffset(x,y);
var element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': listItem
});
frame.append(element);
}
function drawElements() {
var x = 0, y = 0;
while (10 + (elementSize + gutterSize) * x < frame.width()) {
while (10 + (elementSize + gutterSize) * y < frame.height()) {
drawElement(x,y);
y++;
}
y = 0;
x++;
}
}
drawElements();
.frame {
border: 2px solid black;
margin: 40px auto;
height: 300px;
width: 300px;
position: relative;
overflow: hidden;
}
.frame .element {
position: absolute;
width: 30px;
height: 30px;
}
.buttons {
position: absolute;
top: 0px;
width: 100%;
}
.buttons button {
position: absolute;
width: 30px;
height: 30px;
padding: 5px;
}
button.up {top: 0px; left: 46%;}
button.down {top: 355px; left: 46%;}
button.left {top: 160px; left: 15px;}
button.right {top: 160px; right: 15px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
</div>
<div class="buttons">
<button class="up">↑</button>
<button class="down">↓</button>
<button class="left">←</button>
<button class="right">→</button>
</div>
You can see I've left some simple buttons to implement movement, but they are not functional yet. If you wanted to continue implementation along the lines of what I've done here, you could render your elements to a certain range beyond the visible frame, then implement some sort of animated repositioning. The renderElements function here only renders what is visible, so you can use something like that and not get stuck in rendering infinite elements, even though there's no theoretical limit to how far you can "scroll".
#arbuthnott I edited your code to implement the exploration via decrementing relativeX and relativeY variables. Also I inserted an "origin" div (1x1 px, overflow visible). This DOM element will represent the X and Y origin. I'm not sure it's essential but it's really convenient.
Now my function currently remove all elements and reinsert all elements on each update (every 500ms for now).
The idear would be to find a way to compare which elements I need versus which one already exists.
Maybe storing existing elements into an array, and compare the array with the "query" array. Than see just the elements that are missing.
This is the idear, not sure about the implementation (I suck at handling arrays).
https://jsfiddle.net/bnv6mumd/64/
var sources = ['red','green','blue','cyan','orange','yellow','pink','purple'];
var frame = $('.frame'),
origin = $('.origin');
var fWidth = 600,
fHeight = 300,
srcTotal = sources.length,
srcSquare = Math.ceil(Math.sqrt(srcTotal)),
rX = 0,
rY = 0;
var gridSize = 30,
gutterSize = 5,
elementSize = gridSize - gutterSize;
function getSourceItem(x,y) {
var index = x + y * srcSquare;
return sources[Math.abs(index) % srcTotal];
}
function getOffset(x,y) {
return [gridSize * x,gridSize * y];
}
function drawElement(x,y) {
var sourceItem = getSourceItem(x,y);
var offsets = getOffset(x,y);
var element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': sourceItem,
});
origin.append(element);
}
function init() {
var x = 0, y = 0;
while ( gridSize * x < fWidth) {
while ( gridSize * y < fHeight) {
drawElement(x,y);
y++;
}
y = 0;
x++;
}
}
function updateElements() {
origin.empty();
var x = -Math.trunc(rX / gridSize) -1, y = - Math.trunc(rY / gridSize) -1;
while ( gridSize * x + rX < fWidth) {
while ( gridSize * y + rY < fHeight) {
drawElement(x,y);
y++;
}
y = -Math.ceil(rY / gridSize);
x++;
}
}
function animate() {
rX -= 5;
rY -= 5;
origin.css({left: rX, top: rY})
updateElements();
console.log("relative X : " + rX + " | relative Y : " + rY);
}
setInterval(animate, 500)
init();
.frame {
border: 2px solid black;
margin: 40px auto;
height: 300px;
width: 600px;
position: relative;
overflow: hidden;
}
.origin {
height: 1px;
width: 1px;
position: absolute;
overflow: visible;
}
.frame .element {
position: absolute;
width: 25px;
height: 25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame">
<div class="origin" style="top:0;left:0;"></div>
</div>
This is my final snippet version (i will start to work on real implementation specific to my case now).
I think I optimized in a decent way DOM operations, code structure etc (I am very well open to suggestions though).
I now only update the elements that needs to be updated (click near the frame to show overflow)
https://jsfiddle.net/bnv6mumd/81/
var sources = ['red', 'green', 'blue', 'cyan', 'orange', 'yellow', 'pink', 'purple'];
var frame = $('.frame'),
origin = $('.origin');
var srcTotal = sources.length,
srcSquare = Math.round(Math.sqrt(srcTotal)),
fWidth = 200,
fHeight = 200,
cellSize = 50,
gutterSize = 20,
gridSize = [Math.floor(fWidth / cellSize) + 1, Math.floor(fHeight / cellSize) + 1],
aX = 0, // Absolute/Applied Coordinates
aY = 0,
rX = 0, // Relative/frame Coordinates
rY = 0;
function getSrcItem(x, y) {
var index = x + y * srcSquare;
return sources[Math.abs(index) % srcTotal];
}
function getOffset(x, y) {
return [cellSize * x, cellSize * y];
}
function getY() {
return Math.floor(-rY / cellSize);
}
function getX() {
return Math.floor(-rX / cellSize);
}
function drawElement(x, y) {
var srcItem = getSrcItem(x, y),
offsets = getOffset(x, y),
element = $('<div></div>').addClass('element').css({
left: offsets[0] + 'px',
top: offsets[1] + 'px',
'background-color': srcItem,
}).attr({
"X": x,
"Y": y
});
origin.append(element);
}
function drawCol(x, y) {
var maxY = y + gridSize[1];
while (y <= maxY + 1) {
drawElement(x - 1, y - 1);
y++;
}
}
function drawLign(x, y) {
var maxX = x + gridSize[0];
while (x <= maxX + 1) {
drawElement(x - 1, y - 1);
x++;
}
}
function drawGrid() {
origin.empty();
var x = getX(),
y = getY(),
maxX = x + gridSize[0],
maxY = y + gridSize[1];
while (y <= maxY + 1) {
drawLign(x, y);
x = getX();
y++;
}
}
function updateX(x, y, diffX, diffY) {
if (Math.sign(diffX) == -1) {
drawCol(aX - 1, y);
$('[x=' + (aX + gridSize[0]) + ']').remove();
aX--;
} else if (Math.sign(diffY) == 1) {
drawCol(aX + gridSize[0] + 2, y);
$('[x=' + (aX - 1) + ']').remove();
aX++;
}
}
function updateY(x, y, diffX, diffY) {
if (Math.sign(diffY) == -1) {
drawLign(x, aY - 1);
$('[y=' + (aY + gridSize[0]) + ']').remove();
aY--;
} else if (Math.sign(diffY) == 1) {
drawLign(x, aY + gridSize[0] + 2);
$('[y=' + (aY - 1) + ']').remove();
aY++;
}
}
function animate() {
rX += 1;
rY += 1;
origin.css({
left: rX,
top: rY
});
var x = getX(),
y = getY(),
diffX = x - aX,
diffY = y - aY;
if (diffX) {
updateX(x, y, diffX, diffY)
};
if (diffY) {
updateY(x, y, diffX, diffY)
};
requestAnimationFrame(animate);
}
$('body').click(function() {
$(frame).toggleClass("overflow");
})
drawGrid();
animate();
.frame {
border: 2px solid black;
margin: 100px auto;
height: 200px;
width: 200px;
position: relative;
}
.overflow{
overflow:hidden;
}
.origin {
height: 1px;
width: 1px;
position: absolute;
overflow: visible;
}
.frame .element {
position: absolute;
width: 30px;
height: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="frame overflow">
<div class="origin" style="top:0;left:0;"></div>
</div>
To see a working example simply copy code into notepad++ and run in chrome as a .html file, I have had trouble getting a working example in snippet or code pen, I would have given a link to those websites if I could get it working in them.
The QUESTION is; once I fire the laser once it behaves exactly the way I want it to. It increments with lzxR++; until it hits boarder of the game arena BUT if I hit the space bar WHILST the laser is moving the code iterates again and tries to display the laser in two places at once which looks bad and very choppy, so how can I get it to work so the if I hit the space bar a second time even whilst the laser was mid incrementation - it STOPS the incrementing and simply shoots a fresh new laser without trying to increment multiple lasers at once???
below is the Code:
<html>
<head>
<style>
#blueCanvas {
position: absolute;
background-color: black;
width: 932px;
height: 512px;
border: 1px solid black;
top: 20px;
left: 20px;
}
#blueBall {
position: relative;
background-color: white;
border: 1px solid blue;
width: 10px;
height: 10px;
border-radius: 100%;
top: 0px;
left: 0px;
}
#laser {
position: absolute;
background-color: white;
border: 1px solid blue;
width: 10px;
height: 1px;
top: 10px;
left: 10px;
}
#pixelTrackerTop {
position: absolute;
top: 530px;
left: 20px;
}
#pixelTrackerLeft {
position: absolute;
top: 550px;
left: 20px;
}
</style>
<title>Portfolio</title>
<script src="https://ajax.googleapis.com/
ajax/libs/jquery/1.12.4/jquery.min.js">
</script>
<SCRIPT LANGUAGE="JavaScript" type="text/javascript">
document.addEventListener("keydown", keyBoardInput);
var topY = 0;
var leftX = 0;
var lzrY = 0;
var lzrX = 0;
function moveUp() {
var Y = document.getElementById("blueBall");
topY = topY -= 1;
Y.style.top = topY;
masterTrack();
if (topY < 1) {
topY = 0;
Y.style.top = topY;
};
stopUp = setTimeout("moveUp()", 1)
/**allows for progression of speed with additional key strokes**/
topStop();
stopConflictYup();
console.log('moveUp');
};
function moveDown() {
var Y = document.getElementById("blueBall");
topY = topY += 1;
Y.style.top = topY;
masterTrack();
if (topY > 500) {
topY = 500;
Y.style.top = topY;
};
stopDown = setTimeout("moveDown()", 1)
/**allows for progression of speed with additional key strokes**/
topStop();
stopConflictYdown();
console.log('moveDown');
};
function moveLeft() {
var X = document.getElementById("blueBall");
leftX = leftX -= 1;
X.style.left = leftX;
masterTrack();
if (leftX < 1) {
leftX = 0;
Y.style.leftX = leftX;
};
stopLeft = setTimeout("moveLeft()", 1)
/**allows for progression of speed with additional key strokes**/
leftStop();
stopConflictXleft();
console.log('moveLeft');
};
function moveRight() {
var X = document.getElementById("blueBall");
leftX = leftX += 1;
X.style.left = leftX;
masterTrack();
if (leftX > 920) {
leftX = 920;
Y.style.leftX = leftX;
};
stopRight = setTimeout("moveRight()", 1)
/**allows for progression of speed with additional key strokes**/
leftStop();
stopConflictXright();
console.log('moveRight');
};
function masterTrack() {
var pxY = topY;
var pxX = leftX;
document.getElementById('pixelTrackerTop').innerHTML =
'Top position is ' + pxY;
document.getElementById('pixelTrackerLeft').innerHTML =
'Left position is ' + pxX;
};
function topStop() {
if (topY <= 0) {
clearTimeout(stopUp);
console.log('stopUp activated');
};
if (topY >= 500) {
clearTimeout(stopDown);
console.log('stopDown activated');
};
};
function leftStop() {
if (leftX <= 0) {
clearTimeout(stopLeft);
console.log('stopLeft activated');
};
if (leftX >= 920) {
clearTimeout(stopRight);
console.log('stopRight activated');
};
};
function stopConflictYup() {
clearTimeout(stopDown);
};
function stopConflictYdown() {
clearTimeout(stopUp);
};
function stopConflictXleft() {
clearTimeout(stopRight);
};
function stopConflictXright() {
clearTimeout(stopLeft);
};
function shootLaser() {
var l = document.getElementById("laser");
var lzrY = topY;
var lzrX = leftX;
fireLaser();
function fireLaser() {
l.style.left = lzrX; /**initial x pos **/
l.style.top = topY; /**initial y pos **/
var move = setInterval(moveLaser, 1);
/**continue to increment laser unless IF is met**/
function moveLaser() { /**CALL and start the interval**/
var bcrb = document.getElementById("blueCanvas").style.left;
if (lzrX > bcrb + 920) {
/**if the X axis of the laser goes beyond the
blueCanvas 0 point by 920 then stop incrementing the laser on its X
axis**/
clearInterval(move);
/**if statement was found true so stop increment of laser**/
} else {
lzrX++;
l.style.left = lzrX;
};
};
};
};
function keyBoardInput() {
var i = event.keyCode;
if (i == 32) {
shootLaser();
};
if (i == 38) {
if (topY > 0) {
moveUp();
};
};
if (i == 40) {
if (topY < 500) {
moveDown();
};
};
if (i == 37) {
if (leftX > 0) {
moveLeft();
};
};
if (i == 39) {
if (leftX < 920) {
moveRight();
};
};
};
/**
!! gradual progression of opacity is overall
!! being able to speed up element is best done with setTimout
!! setInterval is constant regards to visual speed
!! NEXT STEP IS ARRAYS OR CLASSES
IN ORDER TO SHOOT MULITPLE OF SAME ELEMENT? MAYBEE?
var l = document.getElementById("laser");
lzrX = lzrX += 1;
l.style.left = lzrX;
lzrY = topY += 1;
l.style.top = lzrY;
**/
</SCRIPT>
</head>
<div id="blueCanvas">
<div id="laser"></div>
<div id="blueBall">
</div>
</div>
<p id="pixelTrackerTop">Top position is 0</p>
<br>
<p id="pixelTrackerLeft">Left position is 0</p>
</body>
</html>
Solved the problem with using a variable called "g" and incrementing it once the laser is shot!
var g = 0;
function keyBoardInput() {
var i = event.keyCode;
if (i == 32) {
if (g < 1) {
shootLaser();
g++;
};
};