I'm building an application in three.js, however I'm having real problems with performance. This part of the application is based upon the Voxel Painter example. In my version, the user clicks on a cell to begin placement, drags the cursor to where they wish to end placement, and clicks to end.
function onDocumentMouseMove(event) {
//set up mouse and raycaster
event.preventDefault();
mouse.set((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);
raycaster.setFromCamera(mouse, camera);
switch (buildMode) {
case buildModes.CORRIDOR:
scene.add(rollOverFloor);
var intersects = raycaster.intersectObjects(gridObject);
if (intersects.length > 0) {
var intersect = intersects[0];
if (beginPlace == true) {
//store the intersection position
var endPlace = new THREE.Vector3(0, 0, 0);
endPlace.copy(intersect.point).add(intersect.face.normal);
endPlace.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
endPlace.set(endPlace.x, 0, endPlace.z);
corridorDrag(endPlace);
}
//if user hasn't begun to place the wall
else {
//show temporary wall on grid
rollOverFloor.position.copy(intersect.point).add(intersect.face.normal);
rollOverFloor.position.divideScalar(step).floor().multiplyScalar(step).addScalar(step / step);
rollOverFloor.position.set(rollOverFloor.position.x, 0, rollOverFloor.position.z);
}
}
break;
}
render();
}
The code above is called when the user moves the mouse (there are many buildmodes in the main application, but I have not included them here). This function simply gets a start and end point, the corridorDrag() function fills in the cells between the start and end points:
function corridorDrag(endPlace) {
deleteFromScene(stateType.CORRIDOR_DRAG);
var startPoint = startPlace;
var endPoint = endPlace;
var zIntersect = new THREE.Vector3(startPoint.x, 0, endPoint.z);
var xIntersect = new THREE.Vector3(endPoint.x, 0, startPoint.z);
var differenceZ = Math.abs(startPlace.z - zIntersect.z);
var differenceX = Math.abs(startPlace.x - xIntersect.x);
var mergedGeometry = new THREE.Geometry();
for (var i = 0; i <= (differenceZ / step); i++) {
for (var j = 0; j <= (differenceX / step); j++) {
var x = startPlace.x;
var y = startPlace.y;
var z = startPlace.z;
if (endPoint.x <= (startPlace.x )) {
if (endPoint.z <= (startPlace.z)) {
x = x - (step * j);
z = z - (step * i);
}
else if (endPoint.z >= (startPlace.z)) {
x = x - (step * j);
z = z + (step * i);
}
} else if (endPoint.x >= (startPlace.x)) {
if (endPoint.z <= (startPlace.z)) {
x = x + (step * j);
z = z - (step * i);
}
else if (endPoint.z >= (startPlace.z)) {
x = x + (step * j);
z = z + (step * i);
}
}
floorGeometry.translate(x, y, z);
mergedGeometry.merge(floorGeometry);
floorGeometry.translate(-x, -y, -z);
}
}
var voxel = new THREE.Mesh(mergedGeometry, tempMaterial);
voxel.state = stateType.CORRIDOR_DRAG;
scene.add(voxel);
tempObjects.push(voxel);
}
Firstly, the deleteFromScene() function removes all current highlighted cells from the scene (see below). The code then (I believe), should create a number of meshes, depending on the start and end points, and add them to the scene.
function deleteFromScene(state) {
tempObjects = [];
var i = scene.children.length;
while (i--) {
if (scene.children[i].state != undefined)
if (scene.children[i].state == state)
scene.children.splice(i, 1);
}
}
As I said, it is very, very slow. It also appears to be adding an obscene amount of vertices to the renderer, as seen in the WebGLRenderer stats window. I have no idea why it's adding so many vertices, but I'm assuming that's why it's rendering so slowly.
The application can be viewed here - the problem can be seen by clicking on one cell, dragging the cursor to the other end of the grid, and observing the time taken to fill in the cells.
Thank you in advance, this really is a last resort.
A few years ago Twitter put out an update. In this update they had just introduced infinite scrolling and on the day of its release the update was crashing users browsers. Twitter engineers did some investigating and found that the crashes were the result of the scroll event firing hundreds of times a second.
Mouse events can fire many MANY times a second and can cause your code to execute too often, which slows down the browser and (in many cases) crashes it. The solution for Twitter (and hopefully you) was simple: Poll your event.
Inside your mousemove event handler check that it has been some number of milliseconds since the last move event.
var lastMove = Date.now();
function onDocumentMouseMove(event) {
if (Date.now() - lastMove < 31) { // 32 frames a second
return;
} else {
lastMove = Date.now();
}
// your code here
}
I hope that helps!
Related
Hello I don't really understand javascript well because I only use python typically. and I'm doing this javascript project and I want to make it where the input is not dragging and instead it is left and right. So like when you move the lock all your have to do is press the arrow keys. Any help would greatly be very very nice. Here is the codepen I'm using this from https://codepen.io/MSFTserver/pen/aJGBXp
// make dial draggable
Draggable.create(".dial", {
type:"rotation",
throwProps:true
});
// values 40 or above will be set to 0
const combo = [20, 5, 30],
findCombo = function(comboArr){
let dial = $(".dial"),
dialTrans = dial.css("transform"),
ticks = 40,
tickAngle = 360 / ticks,
numOffset = 0.5, // how far red arrow can be from number
// break down matrix value of dial transform and get angle
matrixVal = dialTrans.split('(')[1].split(')')[0].split(','),
cos1 = matrixVal[0],
sin = matrixVal[1],
negSin = matrixVal[2],
cos2 = matrixVal[3],
angle = Math.round(Math.atan2(sin, cos1) * (180 / Math.PI)) * -1;
// convert negative angles to positive
if (angle < 0) {
angle += 360;
}
// check numbers found, stop loop if at first number not found
for (let i = 0; i < comboArr.length; ++i) {
if (!$(".num" + (i + 1)).hasClass("found")) {
if (angle > (comboArr[i] - numOffset) * tickAngle &&
angle < (comboArr[i] + numOffset) * tickAngle) {
// make numbers green when found
$(".num" + (i + 1)).addClass("found");
// on unlock
if (i == comboArr.length - 1) {
$(".shackle").addClass("unlocked");
$(".top").addClass("pivot1");
$(".inner").addClass("pivot2");
$(".left").addClass("moveRight");
$(".dentL, .dentR").addClass("moveLeft");
// then lock again
setTimeout(function() {
$(".shackle").removeClass("unlocked");
$(".top").removeClass("pivot1");
$(".inner").removeClass("pivot2");
$(".left").removeClass("moveRight");
$(".dentL, .dentR").removeClass("moveLeft");
$("body").attr("style","background: black;");
$(".sitelocker").attr("style","display:none;");
$(".mainsitebody").removeAttr("style");
for (let j = 0; j < combo.length; ++j) {
$(".num" + (j + 1)).removeClass("found");
}
}, 2400);
}
}
break;
}
}
};
// dial interaction (mouse)
$(".dial").on("click",function(){
findCombo(combo);
});
// dial interaction (touch)
$(".dial").on("touchend",function(){
findCombo(combo);
});
I am coding a lil js game for a university project.
I have a 2d map and I can move my player with arrows. Enemies are spawned every 5 seconds and they are guided by the function:
enemy.updatePosition = function() {
if(enemy.isAttacking === false) {
var diffX = Math.floor(player.x - enemy.x);
var diffY = Math.floor(player.y - enemy.y);
//security distance by player --> superEnemy type 1 uses arrows
var distance = getDistanceBetweenEntities(player, enemy);
var gap = 20;
enemy.pressingRight = diffX > gap;
enemy.pressingLeft = diffX < -gap;
enemy.pressingDown = diffY > gap;
enemy.pressingUp = diffY < -gap;
enemy.isStopped = false;
if(enemy.speedX < 0)
enemy.speedX = - enemy.speedX;
if(enemy.speedY < 0)
enemy.speedY = - enemy.speedY;
//bumpers check if hitting a wall or end of map
var rightBumper = {x:enemy.x + 15, y:enemy.y};
var leftBumper = {x:enemy.x - 15, y:enemy.y};
var upBumper = {x:enemy.x, y:enemy.y - 25};
var downBumper = {x:enemy.x, y:enemy.y + 20};
if(currentMap.isPositionWall(rightBumper)) {
enemy.x -= 1;
} else {
if(enemy.pressingRight)
enemy.x += enemy.speedX;
}
if(currentMap.isPositionWall(leftBumper)) {
enemy.x += 1;
} else {
if(enemy.pressingLeft)
enemy.x -= enemy.speedX;
}
if(currentMap.isPositionWall(downBumper)) {
enemy.y -= 1;
} else {
if(enemy.pressingDown)
enemy.y += enemy.speedY;
}
if(currentMap.isPositionWall(upBumper)) {
enemy.y += 1;
} else {
if(enemy.pressingUp)
enemy.y -= enemy.speedY;
}
//set position again if the center of the draw
//of enemy goes out of map's limits
if(enemy.x < enemy.width/2)
enemy.x = enemy.width/2;
if(enemy.x > currentMap.width - enemy.width/2)
enemy.x = currentMap.width - enemy.width/2;
if(enemy.y < enemy.height/2)
enemy.y = enemy.height/2;
if(enemy.y > currentMap.height - enemy.height/2)
enemy.y = currentMap.height - enemy.height/2;
}
}
}
So my enemies follow the player with the values of diffX and diffY. Each enemy has it own speedX and speedY, something like:
var random = 1 + Math.random()*7; //from 1 to 8
enemy.speedX = random;
enemy.speedY = random;
The result is that enemies start to overlap, expecially when they are performing an attack(x and y don't changes during attack). Is there a simple way to avoid that without checking a lot of collision? Thanks everyone
There are more options for you, but here is one simple collision detection.
First you will need to make every enemy unique like giving everyone of them a unique name. This doesn't need to be complicated just like enemy1, enemy2, .... enemy223. You can do it at the point where you spawn the enemy, like this:
enemy['name'] = 'enemy' + i++;
so you can access it like this:
enemy.name;
Important: you should write some kind position that updates everytime the 'enemy' changes position or every tick.
enemy['position'] = enemy.x+','+enemy.y;
make an array into that you can write the positions of every enemy. I know this is not the best option but it's simple and will work for now.
var pstns = [];
After that write every enemy into the array (just do it at spawn). I would like to mention that the following is not good practice.
var pstnsObj = {};
pstnsObj[enemy.name] = enemy.position;
pstns.push(pstnsObj);
Next you need to update the position in the array every tick with every enemy. This is only one example you can do it multiple ways or even automate this process.
function updatePstns(id, position){
pstns[id][Object.keys(pstns[id])[0]] = position;
//just in case:
return pstns;
}
//updating first enemy:
updatePstns(0, enemy.position);
now for the collision:
function checkCollision(){
var count = 0;
pstns.forEach(function(e){
for(i=0; i<pstns.length; i++){
if(pstns[e][Object.keys(pstns[e])[0]] == pstns[i][Object.keys(pstns[i])[0]]){
count++;
}
}
if(count > 1){
console.log('enemy ' + pstns[e] + 'collides with ' + count + 'enemies');
}
});
}
I am trying to create an analytical program which keeps track of user mouse movement on a website and stores the data in a DB. Here is where I am stuck:
Assuming the mouse is always starting at the middle of the screen, and the user is instructed to move it to a particular element, how do I determine the efficiency and accuracy of that movement. I need to keep in mind the duration from start of hovering till the click, but I want to also include the hovering path of the mouse.
A perfect score would be a perfect line from Point A to Point B in x seconds, how do I determine the score of a curved path in 2x seconds, or an instance where the path goes in the wrong direction before proceeding to Point B? Are there any algorithms in existence?
Thanks for your help!
Here is a JSFiddle that I created. Click on the START box and then click on the FINISH box. Hopefully this will help you get started.
var start = false;
var start_time,end_time;
var points = [];
$("#start").click(function() {
start = true;
points = [];
start_time = Date.now();
});
$("#finish").click(function() {
start = false;
distance = travelledDistance();
time = (Date.now() - start_time)/1000;
var center_x_start = $("#start").offset().left + $("#start").width() / 2;
var center_y_start = $("#start").offset().top + $("#start").height() / 2;
var center_x_finish = $("#finish").offset().left + $("#finish").width() / 2;
var center_y_finish = $("#finish").offset().top + $("#finish").height() / 2;
var straight_distance = Math.round(Math.sqrt(Math.pow(center_x_finish - center_x_start, 2) + Math.pow(center_y_finish - center_y_start, 2)));
$("#time").text(+time+"s");
$("#distance").text(distance+"px");
$("#straight_distance").text(straight_distance+"px");
});
$(document).mousemove(function( event ) {
if(!start)
return;
points.push(event.pageX + "," + event.pageY);
});
function travelledDistance(){
var distance = 0;
for (i = 0; i < points.length - 1; i++) {
start_point = points[i].split(",");
end_point = points[i+1].split(",");
distance += Math.round(Math.sqrt(Math.pow(end_point[0] - start_point[0], 2) + Math.pow(end_point[1] - start_point[1], 2)));
}
return distance;
}
UPDATE
I made a new version here. Now you can drag the targets to check the different results.
Background: Over the last week I've been working on a game that is essentially multi-directional Tron, using Canvas and JavaScript. I opted not to clear the Canvas every frame so that my little line segments leave a trail. For collision detection, I use this function:
// 8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1; //points just outside the circumference
var counter = 0;
var pixelData;
for (var i = 0; i < 16; i += 2) {
//collisionPixels[] is an array of 8 (x, y) offsets, spaced evenly around the center of the circle
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
pixelData = context.getImageData(x,y,1,1).data; //pixel data at each point
if (pixelData[3] != 0) {
counter++;
}
}
if (counter > 4) {
this.collision();
}
The purpose here is to get the alpha values of 8 pixels around the brushpoint's surface; alpha values of 0 are just on the background. If the number of colliding pixels, out of the total 8, is greater than 4 (this is including the trail behind the player) then I call the collision() method. This function actually works really well (and this IS inside a function, so these declarations are local).
The problem is that context.getImageData() skyrockets my memory usage, and after 3 or 4 games tanks the framerate. Cutting just that line out and assigning pixelData some other value makes everything run very smoothly, even while doing the other computations.
How do I fix this memory leak? And, if there's a less convoluted way to do collision detection of this type, what is it?
EDIT: at request, here is my loop:
function loop() {
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
requestAnimationFrame(loop);
}
EDIT 2: So I tried Patrick's UInt8ClampedArrays idea:
//8 sensors for collision testing, positioned evenly around the brush point
var detectionRadius = this.width / 2 + 1;
var counter = 0;
for (var i = 0; i < 16; i += 2) {
var x = this.x + collisionPixels[i] * detectionRadius;
var y = this.y + collisionPixels[i + 1] * detectionRadius;
//translate into UInt8ClampedArray for data
var index = (y * canvas.width + x) * 4 + 3; //+3 so we're at the alpha index
if (canvasArray[index] != 0) {
counter++;
}
}
And, at the top of my loop I added a new global variable, updated once per frame:
var canvasArray = context.getImageData(0,0,canvas.width,canvas.height).data;
Hope I did that right. It works, but the memory and framerate still get worse each round you play. Going to upload some heap snapshots.
EDIT 3:
Snapshot 1: https://drive.google.com/open?id=0B-8p3yyYzRjeY2pEa2Z5QlgxRUk&authuser=0
Snapshot 2: https://drive.google.com/open?id=0B-8p3yyYzRjeV2pJb1NyazY3OWc&authuser=0
Snapshot 1 is after the first game, 2 is after the second.
EDIT 4: Tried capping the framerate:
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
//lastUpdate = now;
if (delta > interval) {
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
}
}
}
Where
interval = 1000 / fps;
It delays the eventual performance hit, but memory is still climbing with this option.
EDIT 5: While I'm sure there must be a better way, I found a solution that works reasonably well. Capping the framerate around 30 actually worked in terms of long-term performance, but I hated the way the game looked at 30 FPS.. so I built a loop that had an uncapped framerate for all updating and rendering EXCEPT for collision handling, which I updated at 30 FPS.
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
Thanks for the answers.. a lot of your ideas found their way into the final(?) product, and I appreciate that. Closing this thread.
Is there some point at which you could call context.getImageData(0, 0, context.canvas.width, context.canvas.height).data so that you can use that single UInt8ClampedArray instead of however many you're using? Also when you're done with the image data (the ImageData that is, not the TypedArray inside it), you could try calling delete on it, though I'm not certain if that will deallocate the memory.
While I'm sure there must be a better way, I found a solution that works reasonably well. Capping the framerate around 30 actually worked in terms of long-term performance, but I hated the way the game looked at 30 FPS.. so I built a loop that had an uncapped framerate for all updating and rendering EXCEPT for collision handling, which I updated at 30 FPS.
//separate update cycle for collision detection
var collisionFPS = 30;
var lastCollisionUpdate;
var collisionInterval = 1000 / collisionFPS;
var canvasData;
function loop() {
requestAnimationFrame(loop);
now = Date.now();
delta = now - lastUpdate;
lastUpdate = now;
if (!paused) {
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
players[i].update(delta);
players[i].render();
}
}
if (now - lastCollisionUpdate > collisionInterval) {
canvasData = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
for (var i = 0; i < numPlayers; i++) {
if (players[i].alive) {
if (players[i].detectCollisions()) {
players[i].collision();
}
}
}
lastCollisionUpdate = now;
}
canvasData = null;
}
}
Might not be the best solution, but it's consistent.
I'm looking for a way to move a div from an array of position with javascript/jquery.
I have trying to do it with jquery.animate but he moved the div with a pause at each iteration of my array.
That could be something like move the div from 0,0 to 120px,230px passing by the 23px,35px;45px,50px etc...
That is for moving an game character on a Tile map
So as requested, some bit of code
First you have a global timer that call a function at short interval to see if it have any action to execute.
In this loop a routine look if some mobile tiles are waiting of any mouvement.
Mobiles are declared as Object class and have a sub function that do the deplacement like that
setPos:function(coord){
var pos = jQuery("#"+this.id).position();
var x = (coord[0] - 32 + this.screenOffX + this.xOffset) - pos.left;
var y =(coord[1] + this.yOffset) - pos.top;
//this.stopAnimation();
//this.startAnimation(this.walkingAnimation);
jQuery("#"+this.id).animate({
left: '+='+ x,
top: '+='+ y
}, 33, function() {
// Animation complete.
});
},
That is a bit messy cause i trying a lot of thing to do the smooth movement that i'm looking for.
so setPos is calling in another place like that
stepMobile:function(mobile){
var wp;/*TEST*/
mobile.changeState("idle");
var ind = mobile.getWayPointIndex();
while(ind < (mobile.getWayPoints()).length - 1){
if (ind < (mobile.getWayPoints()).length - 1) {
wp = (mobile.getWayPoints())[ind + 1];
if (getTime() > wp.time) {
mobile.setWayPointIndex(ind + 1);
ind = ind +1;
}
}
wp = (mobile.getWayPoints())[ind];
var x;
var y = 0;
var z;
x = this.tileWidth * (wp.getTile()).getCol();
z = this.tileHeight * (wp.getTile()).getRow();
var elapsed = getTime() - wp.getTime();
console.log(elapsed);
if (ind == (mobile.getWayPoints()).length - 1) {
console.log('checkForOnStopEvent()');
} else {
//x += 1 * mobile.getWalkSpeed() * mobile.getCosAngle();
//z += 1 * mobile.getWalkSpeed() * mobile.getSinAngle();
}
var coord = this.mapToScreen(x, y, -z);
mobile.setPos(coord);
ind = mobile.getWayPointIndex();
}
},
Again lot of junk code here cause i literally burned my brain but i didn't get any good result.
And you have that global function that run this function over all mobiles waiting for deplacement.