Add multiple times image dropping down the screen (video game js) - javascript

So I am building a video game where some fireball drop down the screen. However, there is only one image crossing the screen at a time. I would like that the image is actually multiplied an number of times. To get an idea of what I am saying, here is an image:
But what I would like to do is instead of only having one image (fireball) going down the screen, I would like to have a bunch of images dropping down the screen.
Here is the code for the fireball:
//Fireball script
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
let fireball = {x: fFireball(fireballElement.offsetWidth), y: 0}
const fireLoop = function() {
fireball.y += 2; fireballElement.style.top = fireball.y + 'px'
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth)
fireballElement.style.left = fireball.x + 'px'; fireball.y = 0
}
}
fireballElement.style.left = fireball.x + 'px'
let fireInterval = setInterval(fireLoop, 1000 / 100)
And the image:
<img src="Photo/fireball.png" id="fireball">
Thanks!

Use document.createElement()
Demo : https://jsfiddle.net/hexzero/ukh1dpwn/
I wrote down several comments in the code below to help you understand it better. If you have any additional question don't hesitate to ask.
const fireballArray = [];
// You can add any additional attributes you need for your fire balls here like ids and class names.
function generateFireBallWithAttributes(el, attrs) {
for (var key in attrs) {
el.setAttribute(key, attrs[key]);
}
return el;
}
function createFireBalls(amount){
for (let i = 0; i <= amount; i++) {
fireballArray.push( // create an image element
generateFireBallWithAttributes(document.createElement("img"), {
src: "Photo/fireball.png",
width: "32",
height: "32",
})
);
}}
createFireBalls(10)
fireballArray.forEach((fireballElement) => {
// Just add the id of the game body here, so that you could append your fire balls
document.getElementById("game-body").appendChild(fireballElement);
const fireball = { x: fFireball(fireballElement.offsetWidth), y: 0 };
const fireLoop = function () {
fireball.y += 2;
fireballElement.style.top = fireball.y + "px";
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth);
fireballElement.style.left = fireball.x + "px";
fireball.y = 0;
}
};
fireballElement.style.left = fireball.x + "px";
// I've randomised the interval
// you may want to implement your own version to get more control
let fireInterval = setInterval(fireLoop, 1000 / ((Math.random() * (125 - 75)) + 75));
});
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset));
}
img {
position: absolute;
}
I got inspired by this post Setting multiple attributes for an element at once with JavaScript, when adding extra attributes. If you like it please show them some love.

You can simply create a new <img> element for each bullet
I created a snippet doing it the way you were doing it. However, it would be more efficient if you had a single loop that updates all of the images at once.
const create = (x, y, vx, vy) => {
const object = {
x,
y,
vx,
vy,
el: document.createElement("img")
}
object.el.src = "http://placehold.it/50x50";
object.el.style.left = `${object.x}px`;
object.el.style.top = `${object.y}px`;
document.body.appendChild(object.el);
const intervalID = setInterval(() => {
object.x += object.vx;
object.y += object.vy;
object.el.style.left = `${object.x}px`;
object.el.style.top = `${object.y}px`;
if (object.y > window.innerHeight) {
document.body.removeChild(object.el);
clearInterval(intervalID)
}
}, 20);
}
setInterval(() => {
const randX = Math.floor(Math.random() * (window.innerWidth - 50));
create(randX, -50, 0, 2);
}, 500);
body {
margin: 0;
overflow: hidden;
}
img {
position: absolute;
}

Related

Random movement of circles created by the script

I have a function that craeates divs with a circle.
Now they are all created and appear at the beginning of the page and go further in order.
Next, I need each circle to appear in a random place. I did this.
Now I need all of them to move randomly across the entire page, I have difficulties with this.
Here is an example of how everything works for one element that is already on the page.
https://jsfiddle.net/quej8wko/
But when I add this code, all my created circles don't move.
I get an error:
"message": "Uncaught TypeError: Cannot set properties of null (setting 'willChange')",
This is probably due to the fact that initially there are no circles on the page. How can I connect the code so that all created circles move?
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createDiv(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
clip-path: circle(50%);
height: 40px;
width: 40px;
margin: 20px;
position: absolute;
}
I have modified the snippet which works as you expected.
There was a mistake where you were initializing and creating the object instance only once and none of the div elements that you created inside the setInterval function never got Instantiated.
I think you are just starting out with JavaScript with this sample project.
Below are few suggestions:
Learn to debug the code. You should be using dev tools by making use of debugger statement where it takes you to the source code to analyze the variable scope and stack during the runtime. console.log also helps in few situations.
I could see a lot of confusing naming convention (You have named the create div parameter as id but creating a div class using that id)
Try using ES6 features (class syntax is really good when writing OOP in JS although it's just a syntactic sugar for prototype)
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createAndInitializeDivObject(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
var x = new RandomObjectMover(document.querySelector(`.${id}`), window);
x.start();
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createAndInitializeDivObject(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
width: 35px;
height: 35px;
border-radius: 35px;
background-color: #ffffff;
border: 3px solid purple;
position: absolute;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="circle"></div>
<script src="app.js"></script>
</body>
</html>

Phaser 3 (Game framework): collider callback is called, but somethimes object still passes through other object, instead of colliding

I'm working on a small flappy-bird-like-game demo. Everthing seems fine, but I have a small problem/question.
I setup a collider function, and the callback works as expected, when the "two" objects collide, but there is a strange behavior:
the white-square (the bird) can fly through the obstacles, when coming from the side
but cannot passthrough when coming from below or on above
BUT the callback is execute always.
blue arrow marks where the square passes through
green arrows mark where the square doesn't passthrough
I'm not sure if this is, because I'm using rectangles (they sometimes) cause problems, or because of my nested physics setup. I even tried to replaced the white rectangel with a sprite, but this still produces the same result/error.
For my demo: I could probablly just destroy the object and restart the level on collision, but I still would like to understand why this is happening? And how I can prevent this, inconsistant behavior.
I'm probably missing something, but couldn't find it, and I don't want to rebuild the application again.
So my question is: why is this happening? And How can I prevent this?
Here is the code:
const width = 400;
const height = 200;
const spacing = width / 4;
const levelSpeed = width / 4;
const flySpeed = width / 4;
var GameScene = {
create (){
let player = this.add.rectangle(width / 4, height / 2, 20, 20, 0xffffff);
this.physics.add.existing(player);
this.input.keyboard.on('keydown-SPACE', (event) => {
if(player.body.velocity.y >= -flySpeed/2){
player.body.setVelocityY(-flySpeed);
}
});
player.body.onWorldBounds = true;
player.body.setCollideWorldBounds(true );
this.physics.world.on("worldbounds", function (body) {
console.info('GAME OVER');
player.y = height / 2;
player.body.setVelocity(0);
});
this.pipes = [];
for(let idx = 1; idx <= 10; idx++) {
let obstacle = this.createObstacle(spacing * idx, Phaser.Math.Between(-height/3, 0));
this.add.existing(obstacle);
this.pipes.push(obstacle);
this.physics.add.collider(obstacle.list[0], player)
this.physics.add.collider(obstacle.list[1], player, _ => console.info(2))
}
},
update(){
this.pipes.forEach((item) => {
if(item.x <= 0){
item.body.x = spacing * 10;
}
})
},
extend: {
createObstacle (x, y){
let topPipe = (new Phaser.GameObjects.Rectangle(this, 0, 0 , 20 , height / 2 ,0xff0000)).setOrigin(0);
let bottomPipe = (new Phaser.GameObjects.Rectangle(this, 0, height/2 + 75, 20 , height / 2 ,0xff0000)).setOrigin(0);
this.physics.add.existing(topPipe);
this.physics.add.existing(bottomPipe);
topPipe.body.setImmovable(true);
topPipe.body.allowGravity = false;
bottomPipe.body.setImmovable(true);
bottomPipe.body.allowGravity = false;
let obstacle = new Phaser.GameObjects.Container(this, x, y, [
topPipe,
bottomPipe
]);
this.physics.add.existing(obstacle);
obstacle.body.velocity.x = - levelSpeed;
obstacle.body.allowGravity = false;
return obstacle;
}
}
};
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width,
height,
scene: [GameScene],
physics: {
default: 'arcade',
arcade: {
gravity: { y: flySpeed },
debug: true
},
}
};
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
Currently I just can assume, that the physics objects don't seem to work correct, when physics objects are nested.
Maybe I'm wrong, but since I rewrote the code again without nested physics - objects and it seems to work, I think my assumption Is correct. I shouldn't have tried to over engineer my code.
If someone has more insides, please let me know/share. I still not 100% sure, if this is the real reason, for the strange behavior.
Here the rewriten code:
const width = 400;
const height = 200;
const spacing = width / 4;
const levelSpeed = width / 4;
const flySpeed = width / 4;
var GameScene = {
create (){
let player = this.add.rectangle(width / 4, height / 2, 20, 20, 0xffffff);
this.physics.add.existing(player);
this.input.keyboard.on('keydown-SPACE', (event) => {
if(player.body.velocity.y >= -flySpeed/2){
player.body.setVelocityY(-flySpeed);
}
});
player.body.onWorldBounds = true;
player.body.setCollideWorldBounds(true );
this.physics.world.on("worldbounds", function (body) {
console.info('GAME OVER');
player.x = width / 4;
player.y = height / 2;
player.body.setVelocity(0);
});
this.pipes = [];
for(let idx = 1; idx <= 10; idx++) {
let obstacle = this.createObstacle(spacing * idx, Phaser.Math.Between(-height/3, 0));
this.add.existing(obstacle[0]);
this.add.existing(obstacle[1]);
this.pipes.push(...obstacle);
this.physics.add.collider(obstacle[0], player)
this.physics.add.collider(obstacle[1], player, _ => console.info(2))
}
},
update(){
this.pipes.forEach((item) => {
if(item.x <= 0){
item.body.x = spacing * 10;
}
item.body.velocity.x = - levelSpeed;
})
},
extend: {
createObstacle (x, y){
let topPipe = (new Phaser.GameObjects.Rectangle(this, x, -20 , 20 , height / 2 ,0xff0000)).setOrigin(0);
let bottomPipe = (new Phaser.GameObjects.Rectangle(this, x, height/2 + 75, 20 , height / 2 ,0xff0000)).setOrigin(0);
this.physics.add.existing(topPipe);
this.physics.add.existing(bottomPipe);
topPipe.body.setImmovable(true);
topPipe.body.allowGravity = false;
topPipe.body.velocity.x = - levelSpeed;
bottomPipe.body.setImmovable(true);
bottomPipe.body.allowGravity = false;
bottomPipe.body.velocity.x = - levelSpeed;
return [topPipe, bottomPipe];
}
}
};
var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width,
height,
scene: [GameScene],
physics: {
default: 'arcade',
arcade: {
gravity: { y: flySpeed },
debug: true
},
}
};
var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

I wanna repeat function infinite but i couldn't

I wanna do some effect to my background so i tought i can add some shapes flying around but i do only 1 shape i cant loop or anything
i tried call function more than a one time but it doesnt help
function animasyon(a) {
window.onload=function(){
var id = setInterval(anim,5);
$('body').append('<div class=shape></div>');
$('body').append('<div class=shape></div>');
var kutu = document.getElementsByClassName("shape");
var pos = 0;
var x = window.innerWidth;
var y = window.innerHeight;
var borderSize = Math.floor(Math.random() * 3 ) + 1;
var ySize = Math.floor(Math.random() * y ) + 1;
var Size = Math.floor(Math.random() * 30 ) + 5;
var yon = Math.floor(Math.random() *2)+1;
var dolu = Math.floor(Math.random() *2)+1;
if (ySize > 50) { ySize-=20; }
function anim(){
if (pos == x) {
clearInterval(id);
document.getElementById("shape").remove();
}else{
pos++;
kutu[a].style.position = "absolute";
kutu[a].style.border = "solid rgb(119,38,53) "+borderSize+"px";
kutu[a].style.left = pos+"px";
kutu[a].style.width = Size+"px";
kutu[a].style.height = Size+"px";
if (yon == 1) { ySize-=0.2; } else { ySize+=0.2; }
if (dolu==1) {kutu[a].style.background = "rgb(119,38,53)";}
if (kutu[a].offsetTop < 0 || kutu[a].offsetTop > y-30) {document.getElementById("shape").remove();}
kutu[a].style.top = ySize+"px";
}
}
}
}
animasyon(0);
Try Calling 'anim' function in this way
setInterval(function(){anim()},5);
Your problem is that you are assigning window.onload function a value inside the function animasyon
window.onload holds only 1 function. If you call animation function more than once, then the last one will overwrite the first one.
Edit: You need to separate your animation logic from the page load logic. They are completely separate things.
Here is an example of adding multiple objects and animating each separately.
// HTML
<div id="container">
<div id="ball"></div>
<div id="ball2"></div>
<div id="ball3"></div>
<div id="ball4"></div>
</div>
// CSS
#ball, #ball2, #ball3, #ball4 {
background: red;
border: 1px solid #FAFDFA;
display: inline-block;
width: 1em;
height: 1em;
border-radius: 2em;
position: absolute;
}
#ball2 {
background: blue;
}
#ball3 {
background: green;
}
#ball4 {
background: purple;
}
#container {
width: 512px;
height: 512px;
background: black;
position: relative;
}
// JS
const container = document.getElementById('container');
const stageWidth = 512;
const stageHeight = 512;
const makeFall = (elementId) => {
// this function makes an enlement fall in random direction
const animationSpeed = 4;
// your onload function
const ball = document.getElementById(elementId);
let directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
let directionY = Math.random() * animationSpeed;
const setRandomStart = () => {
ball.style.top = '10px';
ball.style.left = (Math.random() * (stageWidth / 2)) + 'px';
directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
directionY = Math.random() * animationSpeed;
}
setRandomStart();
let animationInterval = setInterval(() => {
let px = parseFloat(ball.style.left);
let py = parseFloat(ball.style.top);
px += directionX;
py += directionY;
if (px > stageWidth - 20 || py > stageHeight - 20 || px < -20) {
setRandomStart();
} else {
ball.style.left = px + 'px';
ball.style.top = py + 'px';
}
}, 48);
}
// In Your onload function you can add the elements and then animate
makeFall('ball');
makeFall('ball2');
makeFall('ball3');
makeFall('ball4');
https://jsfiddle.net/753oL8re/4/

Display Absolute positioned images inside a div

i am trying to absolute position images at random positions inside a div. i'm not sure how to get the calculations right for 'top' and 'left' but the images on occasions display outside of the div. i want to also prevent the overlapping of the images.
Any ideas would help
(function() {
//array of links to the images
var images = ["http://via.placeholder.com/150/800",
"http://via.placeholder.com/150/080",
"http://via.placeholder.com/150/008",
"http://via.placeholder.com/150/880"
];
//function to calculate a random integer
var getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
//function to get a top and left value position for each image
var pos = function() {
var wrapWidth = document.getElementById("wrap");
wrapWidth = $("#wrap").width();
wrapHeight = $("#wrap").height();
// Image Position
var xPos = getRandomInt(0, wrapWidth - 150);
var yPos = getRandomInt(0, wrapHeight - 150);
return {
x: xPos + "px",
y: yPos + "px"
}
}
var displayImages = function(images) {
var elementArray = [];
for (var i = 0; i < images.length; i++) {
var src = images[i];
elementArray[i] = '<img class="imagePos" style="top:' + pos().x + '; left:' + pos().y + ' " src="' + src + ' "/>';
}
console.log(elementArray);
elementArray.forEach(function(element) {
console.log(element);
$("#wrap").append(element);
});
}
displayImages(images);
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="IntTree">
<div id="header">
<h1>Interactive Tree</h1>
</div>
<div id="wrap">
</div>
</div>
I'm assuming that you have some css resembling this:
img {
position: absolute;
width:150;
height:150;
}
Regarding your first issue, you appear to have your x and y assignments backwards in the bit where you're adding the elems to the array. Also, you're making 2 times as many calls to pos() as needed.
That line should be:
let position = pos();
elementArray[i] = '<img class="imagePos" style="top:'+position.y+'; left:'+position.x+' " src="'+src+' "/>';
For the second issue, you need to check for each image whether any of the corners overlap a different image. The easy way to achieve this by adding an array to track the positions you've already used, and comparing against the items in the array for subsequent position calculations.
(function (){
//array of links to the images
var images = ["http://via.placeholder.com/150/800",
"http://via.placeholder.com/150/080",
"http://via.placeholder.com/150/008",
"http://via.placeholder.com/150/880"
];
//function to calculate a random integer
var getRandomInt = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// array to track previous positions of images
var positions = [];
//function to get a top and left value position for each image
var pos = function (){
var wrapWidth = $("#wrap").width();
var wrapHeight = $("#wrap").height();
// Image Position
var xPos = getRandomInt(0, wrapWidth - 150);
var yPos = getRandomInt(0, wrapHeight - 150);
var overlapX = true;
var overlapY = true;
while(overlapX && overlapY) {
overlapX = false;
overlapY = false;
for(var i = 0; i < positions.length; i++) {
// check if x coord is inside previously placed image
if ( (xPos > positions[i].x && xPos < positions[i].x+150) ||
(xPos+150 > positions[i].x && (xPos+150) < positions[i].x+150) ){
overlapX = true;
}
// check if y coord is inside previously placed image
if( (yPos > positions[i].y && yPos < positions[i].y+150) ||
(yPos+150 > positions[i].y && yPos+150 < positions[i].y+150) ) {
overlapY = true;
}
}
if (overlapX) {
xPos = getRandomInt(0, wrapWidth - 150);
}
if (overlapY) {
yPos = getRandomInt(0, wrapHeight - 150);
}
}
positions.push({x:xPos,y:yPos});
return {
x: xPos + "px",
y: yPos + "px"
}
}
var displayImages = function(images){
var elementArray = [];
for (var i = 0; i < images.length; i++) {
var src = images[i];
let position = pos();
elementArray[i] = '<img class="imagePos" style="top:'+position.y+'; left:'+position.x+' " src="'+src+' "/>';
}
elementArray.forEach(function(element) {
$("#wrap").append(element);
});
}
displayImages(images);
})();

Creating a class of Crafty JS entity (class of a class?)

I am trying to create a class which creates a Crafty entity with specific properties. So far, the functions within the class do not run because 'this' refers to the window object
$(document).ready(function () {
Crafty.init(window.innerWidth, window.innerHeight);
var player = new controller(37,38,39,40);
player.d.color("red").attr({
w: 50,
h: 50,
x: 0,
y: 0
});
// Jump Height = velocity ^ 2 / gravity * 2
// Terminal Velocity = push * (1 / viscosity)
var gravity = 1;
var viscosity = 0.5;
var frame = (1 / 20);
var distanceMultiplier = 10; //pixels per meter
var timeMultiplier = 20; //relative to actual time
var keystart = [];
var keyboard = [];
function controller (controls) {
this.d = Crafty.e();
this.d.addComponent("2D, Canvas, Color, Collision");
this.d.collision();
this.d.mass = 1;
this.d.a = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0,
resistance : 0
};
this.d.a.push = 0;
this.d.v = {
extradistance : 0,
velocity : 0,
acceleration : 0,
force : 0
};
this.d.jumping = true;
this.d.onHit("Collision", function () {
var a = this.d.hit("Collision");
if (a) {
for (var b in a) {
this.d.x = this.d.x - a[b].normal.x * a[b].overlap;
this.d.y = this.d.y - a[b].normal.y * a[b].overlap;
if (a[b].normal.y < -0.5) {
this.d.jumping = false;
}
if (Math.abs(a[b].normal.x) < 0.2) {
this.d.v.velocity = this.d.v.velocity * a[b].normal.y * 0.2;
}
if (Math.abs(a[b].normal.y) < 0.2) {
this.d.a.velocity = this.d.a.velocity * a[b].normal.x * 0.2;
}
}
return;
}
});
this.d.physics = function () {
if (keyboard[arguments[1]] && !this.jumping) {
this.v.velocity = 5;
this.jumping = true;
}
if (keyboard[arguments[1]] && this.jumping) {
var now = new Date();
if (now.getTime() - keystart[arguments[1]].getTime() < 500) {
this.v.velocity = 5;
}
}
if (keyboard[arguments[0]] && keyboard[arguments[2]]) {
this.a.velocity = 0;
} else {
if (keyboard[arguments[0]]) {
this.a.velocity = -3;
}
if (keyboard[arguments[2]]) {
this.a.velocity = 3;
}
}
if (keyboard[arguments[3]]) {
this.v.velocity = -5;
}
this.a.force = this.a.push - this.a.resistance;
this.a.acceleration = this.a.force / this.mass;
this.a.velocity = this.a.velocity + (this.a.acceleration * frame);
this.a.extradistance = (this.a.velocity * frame);
this.a.resistance = this.a.velocity * viscosity;
this.attr({
x: (this.x + (this.a.extradistance * distanceMultiplier))
});
this.v.force = gravity * this.mass;
this.v.acceleration = this.v.force / this.mass;
this.v.velocity = this.v.velocity - (this.v.acceleration * frame);
this.v.extradistance = (this.v.velocity * frame);
this.attr({
y: (this.y - (this.v.extradistance * distanceMultiplier))
});
setTimeout(this.physics, (frame * 1000) / timeMultiplier);
};
this.d.listen = function(){ document.body.addEventListener("keydown", function (code) {
var then = new Date();
if (!keyboard[code.keyCode] && !this.jumping && code.keyCode == arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
if (!keyboard[code.keyCode] && code.keyCode != arguments[1]) { //only if not yet pressed it will ignore everything until keyup
keyboard[code.keyCode] = true; //start movement
keystart[code.keyCode] = then; //set time
}
});
};
}
player.d.physics();
player.d.listen();
document.body.addEventListener("keyup", function (code) {
keyboard[code.keyCode] = false;
});
});
In trying to put the functions as prototypes of the class, I run into a problem.
Crafty.init(500,500);
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
If an object is defined in the constructor, I cannot use it to add a prototype to.
Generally in Crafty, we favor composition. That is, you extend an entity by adding more components to it. You can have kind of a hierarchy by having one component automatically add others during init.
I haven't looked through all of your example code, because there's a lot! But consider the second block:
function block () {
block.d = Crafty.e("2D, Color, Canvas");
block.d.color("red");
block.d.attr({x:0,y:0,h:50,w:50});
}
block.d.prototype.green = function() {
this.color("green");
}
var block1 = new block();
block1.d.color();
You're trying to combine Crafty's way of doing things (an entity component system) with classes in a way that's not very idiomatic. Better to do this:
// Define a new component with Crafty.c(), rather than creating a class
Crafty.c("Block", {
// On init, add the correct components and setup the color and dimensions
init: function() {
this.requires("2D, Color, Canvas")
.color("red")
.attr({x:0,y:0,h:50,w:50});
},
// method for changing color
green: function() {
this.color("green");
}
});
// Create an entity with Crafty.e()
block1 = Crafty.e("Block");
// It's not easy being green!
block1.green();

Categories