I am creating a simple game with HTML, CSS, and JavaScript (jQuery). There is main ship, where all of the particles (bullets) originate from. They are each just divs. Then, enemy divs are places randomly throughout the screen.
I am looking for an efficient way to test if each particle is hitting a particular enemy. I have something that starts to work out fine, but gets bogged down incredibly fast. I am new to js, so my code is pretty messy and probably inefficient in many other ways. Any suggestions would be greatly appreciated!
Here is my section that creates enemies and tests for hitting:
var createEnemy = function(){
var xRandom = Math.floor(Math.random() * (containerW-50));
var yRandom = Math.floor(Math.random() * (containerH-50));
var newEnemy = $('<div class="enemy"></div>');
$(newEnemy).css({'left':xRandom,'top':yRandom}).appendTo('#container').fadeTo(200, .7);
var hitTest = setInterval(function(){
var enemy = $(newEnemy);
var particle = $('.particle');
var enemyT = enemy.offset().top;
var enemyB = enemy.height()+enemyT;
var enemyL = enemy.offset().left;
var enemyR = enemy.width()+enemyL;
var particleT = particle.offset().top;
var particleB = particle.height();
var particleL = particle.offset().left;
var particleR = particle.width();
if(particleT >= enemyT-particleB && particleT <= enemyB && particleL >= enemyL-particleR && particleL <= enemyR){
enemy.hide();
var removeEnemy = setTimeout(function(){
newEnemy.remove();
clearInterval(hitTest, 0);
},500);
}
}, 20);
}
var enemyInt = setInterval(createEnemy, 1000);
Is getting something like this to run smoothly in a browser realistic? Does my code just need some changes? You will probably need more context so:
EDIT 1/12/2012: Game Link Removed // Not Relevant
NOTE: This works best in Chrome and Safari at the moment.
EDIT 3/22/2011: Changed enemy fadeOut() to hide() so that you can see exactly when an enemy disappears (it is sometimes delayed). The hitTest only seems to trigger when you click on the actual enemy, so if it passes through, it is not being triggered.Also, I forgot to clearInterval on hitTest. This seemed to boost performance dramatically, but still isn't quite there.
If you want the best performance, drop jQuery and use native JavaScript.
If that isn't an option, profile the slowest parts and use native DOM there, e.g.
var newEnemy = $('<div class="enemy"></div>');
...becomes...
var newEnemy = document.createElement('div');
newEnemy.className = 'enemy';
Related
The difference between the following two spheres - in terms of how their gradient colors were applied, comes down to one statement:
sphereGeometry = sphereGeometry.toNonIndexed();
Being that I really like the smoother look that .toNonIndexed() gives us, I tried applying it to some of the imported “.glb” models available on the THREE.js GIT - but it’s not working.
For example, here’s what happens when I use the horse model available here: https://github.com/mrdoob/three.js/blob/master/examples/models/gltf/Horse.glb
It basically completely ignore my colors and defaults to red and black for some reason.
But when I comment out the .toNonIndexed() line, it gives me the colors I asked for - except you definitely see the triangles, which is the look I'm trying to avoid:
Here's my code for loading the object:
function loadAny3DModel() {
loader.load("./Horse.glb", function(theHorse) {
console.log("===>'theHorse' has arrived!!!\n");
var horseScene = theHorse.scene;
horseMesh = horseScene.children[0];
var horseGeometry = horseMesh.geometry;
let horseMat = horseMesh.material;
var horseVertexPositionsArray = horseGeometry.attributes.position;
// Here's the command that isn't working:
// horseGeometry = horseGeometry.toNonIndexed();
// horseVertexPositionsArray = horseGeometry.attributes.position;
let theColor = new THREE.Color();
let colorsArray = [];
for(let i = 0; i < horseVertexPositionsArray.count; i++) {
let randC1 = "purple";
let randC2 = "white";
let chosenColor = i % 2 == 0 ? randC1 : randC2;
theColor.set(chosenColor);
colorsArray.push(theColor.r, theColor.g, theColor.b);
}
horseGeometry.setAttribute("color", new THREE.Float32BufferAttribute(colorsArray, 3));
horseMat.vertexColors = true;
render();
scene.add(horseScene);
}
}
What should I be doing to get the smoother gradients going?
=====================================================================
UPDATE:
Here is a very rough idea of what I'm trying to do: extend a gradient over an entire model, as opposed to every single triangle that is forming the model. (Compare this image to the one above.)
If you comment in the following line...
horseGeometry = horseGeometry.toNonIndexed();
...it means you create a new (!) geometry. As long as you don't assign the geometry back to Mesh.geometry, this code won't have any effect. So the fix is to add the following line after using toNonIndexed():
horseMesh.geometry = horseGeometry;
I want to link the change letter.linewidth = 10 with a control in dat.GUI.
Here is the code for the full letter variable:
var letter = two.interpret(document.querySelector('.assets svg'));
letter.linewidth = 10;
letter.cap = letter.join = 'round';
letter.noFill().stroke = '#333';
To add an element to dat.GUI it says in the docs "The property must be public, i.e. defined by this.prop = value", though when I add this. in front of letter.linewidth it breaks the functionality of two.js and does not interpret the SVG.
I'm kinda' new to JavaScript and having a tough time figuring this one out.
Any help would be greatly appreciated.
So after lots of playing around, I found the fix.
Here is the code to draw the SVG through two.js:
var letter = two.interpret(document.querySelector('.assets svg'));
letter.linewidth = 100;
letter.cap = letter.join = 'round';
letter.noFill().stroke = '#272727';
letter.scale = 1;
I was calling the letter wrong with dat.GUI. Here is my code for dat.GUI:
window.onload = function() {
var gui = new dat.GUI();
gui.add(letter, 'linewidth', 1, 100);
}
I don't know if this will be useful for anyone, but hey, hopefully this will help if somebody is running into the same problem.
Hello and thanks for looking up.
I have decided to learn Javascript by trial and error and Googling the stuff that I need. So far so good, but when I tried something a bit tricky, a slider, things got tough.
I dont really have good knowledge about syntax in Javascript and how expressions should be written in functions but I am trying.
So here we have a simple 4 images in a div with 2 arrows slider. When we click the arrow we want the counter to decrease by 1, we want to be sure it doesnt equal 0 and if it does we change it to 4, and when this is done we give the respective image ( img[counter] ) a z index of 50 for example. But it doesnt work. For some reason.
Here is a fiddle of the whole thing and hope I dont trouble you guys too much with this.
https://jsfiddle.net/wu2Lysrv/3/
function slideEngine() {
var img1 = document.querySelector("#img1");
var img2 = document.querySelector("#img2");
var img3 = document.querySelector("#img3");
var img4 = document.querySelector("#img4");
var counter = 1;
function slideL() {
counter--;
if (counter == 0) {
counter = 4;
}
img[counter].setAttribute("z-index", "50");
}
}
I would understand if someone goes aggro, since I bet this piece of code over there is very very wrong. Still I would like to learn how do I do it right.
The slideL function lives inside slideEngine, so your html doesn't have direct access to it.
One potential fix is to simply remove the wrapping function, since it doesn't seem to be used anywhere. However, there are other issues with the code that will prevent it from working. I've attempted to fix some below.
var img1 = document.querySelector("#img1");
var img2 = document.querySelector("#img2");
var img3 = document.querySelector("#img3");
var img4 = document.querySelector("#img4");
var counter = 1;
function slideL() {
counter--;
if (counter == 0) {
counter = 4;
}
window['img'+counter].style.zIndex = 50;
}
I didn't tes this, but try it out.
Working on chess board using html and javascript. I am trying to add a function so that the user can flip the perspective of the board and running into a glitch that I understand, but can't figure out how to solve. Code as below....
function flipBoard(){
document.getElementById("a8").id = "h1";
document.getElementById("a7").id = "h2";
document.getElementById("a6").id = "h3";
document.getElementById("a5").id = "h4";
document.getElementById("a4").id = "h5";
document.getElementById("a3").id = "h6";
document.getElementById("a2").id = "h7";
document.getElementById("a1").id = "h8";
document.getElementById("h8").id = "a1";
document.getElementById("h7").id = "a2";
document.getElementById("h6").id = "a3";
document.getElementById("h5").id = "a4";
document.getElementById("h4").id = "a5";
document.getElementById("h3").id = "a6";
document.getElementById("h2").id = "a7";
document.getElementById("h1").id = "a8";
}
So.... I thought this would work just fine, but discovered that this wreaks havoc on my board by position half white, half black pieces on both sides of the board. The problem of course is that after the original "a1" square is renamed to "h8", there are now TWO "h8" squares, and at the end of the code it switches both back to "a1".
I have no idea how to get the id names to switch at the same time, otherwise I'd have to add a whole lot of code switching the id names to some third name as a place holder before switching them over to the desired name. Possible, but tedious and I feel there has to be a simpler way to do this.
Any ideas?
You can reduce the repetition of your current code by generating the id values programmatically in a loop:
function flipBoard(){
var a, h, aVal, hVal, aEl, hEl;
for(a = 8, h = 1; a > 0; --a, ++h) {
aVal = 'a' + a;
hVal = 'h' + h;
// Store off the current values
aEl = document.getElementById(aVal);
hEl = document.getElementById(hVal);
// swap them
aEl.id = hVal;
hEl.id = aVal;
}
}
demo fiddle
Save the reference to the element in variable. For example:
function flip(){
var aEight = document.getElementById("a8"),
hOne = document.getElementById("h1");
aEight.id = "h1";
hOne.id = "a8";
}
By separating out the steps of finding your elements and changing their ids, you'll easily be able to keep track of the flipped elements.
I know a few questions have been asked like this one before, such as this: Check FPS in JS? - which did work to some degree, I was able to find out how long each loop took to complete.
What I am looking for though is something more readable and controllable. I want to be able to set the refresh rate for the FPS counter to make it slow so it is human readable or as fast as the application can run, so I can use it on some kind of speedometer.
Anyway so here is the code I have right now:
var lastLoop = new Date().getTime();
function updateStage()
{
clearCanvas();
updateStageObjects();
drawStageObjects();
var thisLoop = new Date().getTime();
var fps = (thisLoop - lastLoop);
$('#details').html(fps);
lastLoop = thisLoop;
iteration = setTimeout(updateStage, 1);
}
Am I right to be setting the setTimeout function to a speed of 1 millisecond? I was thinking this will just make it loop as fast as it possibly can.
Should I count every 100 frames or so, find out how many milliseconds it took to run 100 frames then make a calculation to find out how many frames it would have done if the milliseconds were 1000? What would this calculation be?
To make the result more accurate I am guessing I need to display averages as one frame can vary a significant amount, how should I do this?
Any tips are greatly appreciated.
Thanks.
Note that the faster you update your output, the more you will affect your measurement. Although minimal, I try to update my fps output once per second or less unless it's necessary to go faster.
I like to have a low-pass filter on my results so that a temporary hiccup doesn't affect the values too strongly. This is easier to compute and write than a moving average, and doesn't have the problem of an overall average where your 'current' readings are affected by total performance over the entire run (e.g. anomalous readings during startup).
Put together, here's how I usually measure FPS:
var fps = 0, now, lastUpdate = (new Date)*1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
if (now!=lastUpdate){
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now;
}
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
Ive tried something out,
If you change the
lastUpdate = now
to
lastUpdate = now * 1 - 1;
Your NaN problem is solved! This is also used where the lastUpdate is defined. Probably because it is not able to convert the date to unix timestamp.
The new result will be:
var fps = 0, now, lastUpdate = (new Date)*1 - 1;
// The higher this value, the less the FPS will be affected by quick changes
// Setting this to 1 will show you the FPS of the last sampled frame only
var fpsFilter = 50;
function drawFrame(){
// ... draw the frame ...
var thisFrameFPS = 1000 / ((now=new Date) - lastUpdate);
fps += (thisFrameFPS - fps) / fpsFilter;
lastUpdate = now * 1 - 1;
setTimeout( drawFrame, 1 );
}
var fpsOut = document.getElementById('fps');
setInterval(function(){
fpsOut.innerHTML = fps.toFixed(1) + "fps";
}, 1000);
I've taken the solution(s) posted and enhanced them a little. Have a look here - http://jsfiddle.net/ync3S/
I fixed that NaN error by using Date.now() instead of constructing a new date object each time and trying to reference it. This also prevents some garbage collection necessity.
I neatened up the variable and function names a bit and added some extra commenting - not necessary but nice to have.
I included some drawing code for testing.
I added fpsDesired as a test var for the engine loop.
I started fpsAverage at fpsDesired so with the fpsFilter it doesn't work up from 0 to the real FPS, rather starting at the desired FPS and adjusting from there.
Drawing now blocks incase it already was drawing, and this can be used for pausing and other control functions.
The main block is as follows:
var fpsFilter = 1; // the low pass filter to apply to the FPS average
var fpsDesired = 25; // your desired FPS, also works as a max
var fpsAverage = fpsDesired;
var timeCurrent, timeLast = Date.now();
var drawing = false;
function fpsUpdate() {
fpsOutput.innerHTML = fpsAverage.toFixed(2);
}
function frameDraw() {
if(drawing) { return; } else { drawing = true; }
timeCurrent = Date.now();
var fpsThisFrame = 1000 / (timeCurrent - timeLast);
if(timeCurrent > timeLast) {
fpsAverage += (fpsThisFrame - fpsAverage) / fpsFilter;
timeLast = timeCurrent;
}
drawing = false;
}
setInterval(fpsUpdate, 1000);
fpsUpdate();
setInterval(frameDraw, 1000 / fpsDesired);
frameDraw();
Going to have a tinker and see if I can come up with something smoother, as this thread is near the top in Google results.
Let's see what we can all come up with as a team, and I think it's always neat to not use 3rd party libraries, making the code portable for anyone :)
-Platima
Just set a interval that is resetting the fps counter every second.
var fpsOut, fpsCount;
var draw = function () {
fpsCount++;
..Draw To Canvas..
..Get the fps value: fpsOut
requestAnimationFrame(draw);
};
setInterval(function () {
fpsOut = fpsCount;
fpsCount = 0;
}, 1000);
draw();
If you want real-time updates, consider making it loop again and again in real time. To make it affect the performance less, only update the controlled variable, in this case, the FPS. You can have optional Frame Latency, which I will put here, just in case. Just copy, paste and tweak the code to your needs.
Take note that a single frame lasts for 16.66 miliseconds.
setInterval(function(){var latencybase1 = parseFloat(new Date().getTime());
var latencybase2 = parseFloat(new Date().getTime());
var latency = latencybase2-latencybase1;
var fps = Math.round(1000/latency);
if (latency<16.66)
{document.getElementById("FPS").innerHTML = fps+"
FPS";}
else {document.getElementById("FPS").innerHTML = ""+fps+" FPS";}
document.getElementById("Latency").innerHTML = latency+" ms";}, 0);