I'm making a 2D isometric game where the tile map is held in a 2D array. Here's what a 16*16 map looks like just so you get the idea:
Before the game loop begins the size and contents of the map array must be initialized and I have noticed that this is one of the biggest bottlenecks when loading the program. If the map is 5000*5000 it can take about 7 seconds on my relatively high end computer (which i figure seems pretty slow).
Is this more a problem with the speed of javascript or is my code doing something wildly inefficient?
The array which holds the tile contents is initialized like so:
// Create Array to represent tile contents
mapArray = new Array(mapWidth);
for (var i = 0; i < mapWidth; i++) {
mapArray[i] = new Array(mapHeight);
for (var j = 0; j < mapHeight; j++) {
mapArray[i][j] = new Tile("water", 0, false);
}
}
and the objects which that array hold is the tile class below:
// Tile class
function Tile(type, height, mouseOver) {
// Add object properties like this
this.type = type;
this.height = height;
this.mouseOver = mouseOver;
}
If you're registering individual event listeners to each tile, that could certainly slow down your program. Consider cutting down the number of listeners by only listening to mouseover/mousemove events on the container, and then deriving the tile from the (x, y) coordinates of the mouse.
Related
I am pretty new to JavaScript and PIXI. I am making a little game that requires the player to navigate and grab a key (move onto the same space as the key). This takes them to the next level. My problem is that whenever a new level is generated, it somehow uses the x and y coordinates for the key of the previous level to skew the logic. Because of this, the first level works fine, but every level afterwards is messed up.
The levels get skewed up and to the left by the respective values of the previous key. I have a 2d array in the background which holds values of where and where not the player can (holds values for things like walls, turrets, and the key). I randomly generate the key coordinates and for example set grid[9][12] = 3; for the key space. The next level visually looks perfect, with the walls and key generating, and the player being in the top left. However because of the skew, the top left of the visuals is really at [max_X - 9][max_Y - 12] and the player can go off screen to invisibly access the rest of the maze. So I can then phase through walls because the logic has the walls in a different place than the visuals, and the actual key space usually ends up off screen.
Here is my code that generates a new goal (key) object
function createGoal(scene) {
while (true) {
let x = getRandomInt(APP_WIDTH);
let y = getRandomInt(APP_HEIGHT);
//if space is not already occupied, populate
if (grid[x][y] == 0) {
console.log(x);
console.log(y);
goal = null;
goal = new Goal(TILE_WIDTH, x, y);
scene.addChild(goal);
grid[x][y] = 3;
break;
}
}
}
And here is the grid initialization:
const grid = new Array(APP_WIDTH);
for (let i = 0; i < APP_WIDTH; i++) {
grid[i] = new Array(APP_HEIGHT);
for (let j = 0; j < APP_HEIGHT; j++) {
grid[i][j] = 0;
}
}
Any help would be greatly appreciated.
I am working with 3D color LUTs (color lookup tables) in javascript and I was wondering is there a way to combine two or more 3D LUTs to export them in one single file.
Let me explain:
I get .cube (3D color lookup file). I parse it and store parsed color values into an array and apply it to existing image. After that I apply new 3D LUT onto existing (changed) image, and I apply new LUT once more. So now I have original image with 3 different 3D LUTs applied onto each other.
Now, I can successfully export every 3D LUT in separate file and download it, but I don't know how to combine them into a single .cube file.
I believe I need some algorithm for "combining" different LUTs into one file?
This is example how photoshop does it:
LUT1:
0.024536 0.000183 0.000244
0.049103 0.000336 0.000458
LUT2:
0.041260 0.021149 0.009125
0.067230 0.023804 0.009125
COMBINED LUT (result):
0.035034 0.020660 0.009308
0.054810 0.022766 0.009430
Thank you!
After some research I have found a solution.
Essentially, I needed to pipe the output of the first LUT into the input of the second LUT. This requires to have an interpolation function in-program (not just a 3D LUT shader).
Process goes something like this:
Create a new identity LUT of a chosen size (default LUT with no changes)
Iterate through every point of that 3D LUT and pipe the identity color of each point through the first LUT's ColorFromColor, and then through the second LUT's ColorFromColor. Store the final value in the new LUT.
Function looks something like this:
function mapColorsFast(out, image, clut, clutMix){
let od = out.data,
id = image.data,
w = out.width,
h = out.height,
cd = clut.data,
cl = Math.floor(Math.pow(clut.width, 1/3)+0.001),
cs = cl*cl,
cs1 = cs-1;
var x0 = 1 - clutMix, x1 = clutMix;
for(var y = 0; y < h; y++) {
for(var x = 0; x < w; x++) {
let i = (y*w+x)*4,
r = id[i]/255*cs1,
g = id[i+1]/255*cs1,
b = id[i+2]/255*cs1,
a = id[i+3]/255,
ci = (dither(b)*cs*cs+dither(g)*cs+dither(r))*4;
od[i] = id[i]*x0 + x1*cd[ci];
od[i+1] = id[i+1]*x0 + x1*cd[ci+1];
od[i+2] = id[i+2]*x0 + x1*cd[ci+2];
od[i+3] = a*255;
}
}
}
Function accepts few arguments:
out - buffer into which the result is written
image - a buffer containing the image in imageData format
clut - color LUT that we're applying to the image
clutMix - affecting the strength of the effect (0-1)
In this case, we needed to create identity LUT, save it as image and pass it as image argument to the function, and then apply new LUT onto it. Then we pipe the result again into same function and apply new LUT onto it. We do that for every LUT we want to mix with other LUTs.
I found this function on https://github.com/jwagner/analog-film-emulator/blob/master/src/image-processing.js - Javascript Film Emulation project.
There's a lot of interesting material to find if you're working with canvas 2d image processing, and there's also a working example included: https://29a.ch/film-emulator/
Hope it will help someone in the future!
EDIT : I have rephrased my question to help users with the same problem.
I have a three.js scene on which I have added some spheres.
I want to move the camera towards a specific direction until all the objects (which are randomly positioned inside the scene) are "fitting exactly" the user's screen.
I have found the answer to my problem!
1. I move the camera (zooming to the desired direction) inside a loop, and in every repeat I create a new frustum using the camera's matrix
2. I check if any of my spheres intersects with a plane of the frustum. If it does, that means that part of one of my objects is outside the frustum so I break the loop and move the camera to its last position.
The above might also works for any object (not only spheres) because every object has a boundingSphere that can be calculated (it might not be very precise the result though).
It also works when zooming out, you 'd just have to move the camera from the object until none of the has a negative distance from all the planes (negative distance means object is "outside" the plane of the frustum).
Code (only for zooming out - r72) :
var finished = false;
var camLookingAt = /* calc. */ ;
while( finished === false ){
var toDirection= camera.position.clone().sub(camLookingAt.clone());
toDirection.setLength(vec.length() - 1); // reduce length for zooming out
camera.position.set(toDirection.x, toDirection.y, toDirection.z);
camera.updateMatrix(); // make sure camera's local matrix is updated
camera.updateMatrixWorld(); // make sure camera's world matrix is updated
var frustum = new THREE.Frustum();
frustum.setFromMatrix( new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) );
for (var j = frustum.planes.length - 1; j >= 0; j--) {
var p = frustum.planes[j];
for (var i = myMeshSpheres.length - 1; i >= 0; i--) {
var sphere = new THREE.Sphere(myMeshSpheres[0].position.clone(), myMeshSpheres[0].radius);
if( p.distanceToSphere(sphere) < 1 ){ // if is negative means part of sphere is outside plane/frustum
finished = true;
}
}
}
This will be a "strange" question, because my code works. I just want to check if it can be tweaked because I dont have programming experience.
I wrote a script that gets a stream of kinect joints and plots them on a three js scene on an coordinates system with a webgl renderer.
At first I create a set of spheres, based on Stemkoski's examples and then add the event listener for the json skeleton that arrives in the update function, so I can update the spheres coordinates and have it moving.
The only problem that this creates is that now I cannot rotate the coordinates system with my mouse (while I could when I was testing it with static spheres or with pulsing spheres, as in Stemkoski's example).
So the first question is how can I correct this problem.
The second, more complex question is this : Is this a correct practice? I may not be a developer but I have learned that when my gut says this is awkward or bad practice, it probably is.. Any help and suggestions, appreciated.
Oh, and I have implemented this for only one skeleton in the scene. I will try to make it for more skeletons but I ll wait for your input here in case this all could be simpler and better written.
Here's the relevant code, the initialization of the sphere/joints and the update function:
WholeSkeleton = new THREE.Object3D();
particleAttributes = { startSize: [], startPosition: [] };
var totalParticles = 20; //=num.skeleton.joints //add later variable number
var radiusRange = 50;
for (var i = 0; i < totalParticles; i++) { // INITIALIZE THE SKELETON JOINTS POSITION
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(255,0,0)' });
var jointSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
//initialize positions
jointSphere.position.set(0, 0, 0);
WholeSkeleton.add(jointSphere);
// add variable qualities to arrays, if they need to be accessed later
particleAttributes.startPosition.push(jointSphere.position.clone());
}
scene.add(WholeSkeleton);
}
function animate() {
requestAnimationFrame(animate);
render();
update();
}
function update() {
document.addEventListener("skeletonEvent", function (e) {
jsonObject = e.detail.SkeletonSourceData;
// for (var i = 0; i < jsonObject.Skeletons.length; i++) {
for (var j = 0; j < jsonObject.Skeletons[0].Joints.length; j++) { //only for skeleton 0.
var incoming=jsonObject.Skeletons[0].Joints[j];
var joint = WholeSkeleton.children[j];
joint.position.x = (particleAttributes.startPosition[j].x + incoming.Position.X)*30;
joint.position.y = (particleAttributes.startPosition[j].y + incoming.Position.Y)*30;
joint.position.z = (particleAttributes.startPosition[j].z + incoming.Position.Z)*10;
}
// }
});
}
--
I just started playing around with pixi and have drawn multiple rectangles from an array with pixel coordinates like this:
var rectangle = [....];
....
var stage = new PIXI.Stage();
var renderer = PIXI.autoDetectRenderer(wrapper.getWidth(), wrapper.getHeight(), { transparent: true });
....
var graphics = new PIXI.Graphics();
graphics.interactive = true;
graphics.on("mouseover", function(e) {
this.alpha = 0.5;
}).on("mouseout", function() {
this.alpha = 1;
});
graphics.beginFill(0xFFFFFF);
graphics.lineStyle(2, 0x000000);
for (var i = 0; i < rectangle.length; i++) {
graphics.drawRect(rectangle[i][0], rectangle[i][1], 10, 10);
}
graphics.endFill();
stage.addChild(graphics);
renderer.render(stage);
The events are triggered but the object I get by "e" or "this" inside the callback is the object for all graphics. I want to get that single "mouseovered" rectangles object I can see in the graphicsData, but there is no id or anything to identify it by. How can I do this?
Performance is of essence as I'm going to render 20k+ rectangles or circles.
Without drawing each rectangle onto it's own PIXI.Graphics object you won't be able to get individual mouseover events. This is because as far as PIXI is concerned the Graphics object is a single bitmap image.
I would suggest performing your own hit tests inside the mouseover function to detect which rectangle the cursor is over.
If you are using PIXI.Rectangles you can take advantage of the built in Rectangle.Contains function to check if a point (in this case the mouse position) is inside the bounds.