How to add/remove objects with time in Three.js - javascript

I want to add and remove objects with a period. For example, i have an array which includes 50 objects. I want to add the object with a period while removing the previous one, like creating an object stream. First i tried with setTimeout and setInterval functions but they didn't work (both inside and outside render function). Then i tried this;
function render(){
controls.update(clock.getDelta());
renderer.render( scene, camera);
i = i+1;
if (i % 2 == 0){
if (i % 300 == 0){
remove(lights);
}
else
{ scene.add(lights[(i/2)]);
}
}
}
It works but it does not start adding process with first object. I also tried getElapsedtime() instead of iterating i, but this time it only adds the first object. Is there any more effective time controlled method that i can use for this?
Thanks a lot.

Something like this would work:
var spotOn = true;
window.setInterval(function(){spot()},milliseconds);
function spot() {
var i;
if(spotOn) {
for(i = 0; i < lights.length; ++i) {
scene.add(lights[i]);
}
} else {
for(i = 0; i < lights.length; ++i) {
scene.remove(lights[i]);
}
}
spotOn = !spotOn;
}
function render(){
renderer.render( scene, camera);
}

Related

React Native staggered render

With this code how would I make the render staggered? I want it to render each element one by one rather than just rendering all at once. I've tried setTimeout but don't know how to implement it or whether its even the right way to do it.
renderSelected() {
var temp=[];
for (var i = 0; i < 11; i++) {
temp.push(this.selected(i))
}
return temp;
}
selected(number) {
return (<View key={number} style={styles.normal} >
<Text>{number}</Text>
</View>);
}
Update based on the answer but it still doesn't work. The code in the answer was too different since this is React Native.
renderLater(i) {
TimerMixin.setTimeout(() => {
this.selected(i);
}, 100);
}
renderSelected() {
var temp=[];
for (var i = 0; i < 11; i++) {
temp.push(this.renderLater(i))
}
return temp;
}
selected(number) {
return (<View key={number} style={styles.normal} >
<Text>{number}</Text>
</View>);
}
Based on the code, the problem is that you return temp which actually contains nothing, since renderLater returns nothing.
A solution is to create an element with a state, and depending on the state your render one or more elements. This is similar to the timer element on the reactjs page, where the state is updated every second triggering a new rendering. Instead of changing the ticker every second, you can increase a counter and in renderSelected() display all the elements up to that counter. There is no renderLater, just a this.setState() called regularly triggering a new rendering with a different number of elements.
var MyClass = React.createClass({
getInitialState: function() {
return {counter: 0};
},
tick: function() {
if (this.state.counter >= 10) return;
this.setState({counter: this.state.counter + 1});
},
componentDidMount: function() {
this.interval = setInterval(() => {this.tick();}, 50);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
var temp=[];
for (var i = 0; i <= 10 && i <= this.state.counter; i++) {
temp.push(this.selected(i))
}
return temp;
},
selected: function(number) {
return (<View key={number} style={styles.normal} >
<Text>{number}</Text>
</View>);
}
});
ReactDOM.render(<MyClass />, mountNode);
Live demo
You can also instead create all the elements separately from the start with each an empty render() function in the beginning and have them display something when enough time is elapsed. For that, you can still use the x.setState() function for each separate element to trigger a new rendering. This avoids erasing and redrawing already drawn elements at each tick.
Old answer:
You can do a global queue for delayed stuff.
var queue = [];
/* executes all element that have their delay elapsed */
function readQueue() {
var now = (new Date()).getTime();
for (var i = 0; i < queue.length; i++) {
if (queue[i][0] <= now) {
queue[i][1]();
} else {
if(i != 0) queue = queue.slice(i);
return;
}
}
queue = [];
}
/* Delay is in milliseconds, callback is the function to render the element */
function addQueue(delay, callback) {
var absoluteTime = (new Date()).getTime() + delay;
for (var i = 0; i < queue.length; i++) {
if (absoluteTime < queue[i][0]) {
queue.splice(i, 0, [absoluteTime, callback]);
return;
}
}
queue.push_back([absoluteTime, callback]);
}
var queueTimer = setInterval(readQueue, 10); //0.01s granularity
With that queue, if you want to render something some elements every 50ms later, then you can just do:
function renderElementLater(time, index) {
/* render */
addQueue(time, function(){ReactDOM.render(selected(index), mountNode);}):
}
for (var i = 0; i <= 10; i++) {
renderElementLater(50*i, i);
}
You can change the granularity (when the queue is read and checked) to something even less than every 10ms for finer control.
Although, I don't see what's the problem with the setTimeout. You could just do:
function renderElementLater(time, index) {
/* render */
setTimeout(function(){ReactDOM.render(selected(index), mountNode);}, time):
}
for (var i = 0; i <= 10; i++) {
renderElementLater(50*i, i);
}
Maybe your problem came that if you want to use the value of a variable that changes in a callback created in a loop, then it needs to be enclosed by another function (like I did by creating the new function renderElementLater instead of directly putting function code there).

Optimize looping through 2 arrays javascript canvas game

I'm working on my first javascript canvas game, and I wonder is there a better way for comparing collisons between objects in 2 arrays. For example i have an array with rockets, and array with enemies, the code is working, but i think when arrays length becomes much larger it will have effect on the performance. Example 100 rockets through 100 enemies is 10000 iterations per frame
for (i in rockets){
rockets[i].x+=projectile_speed;
for (j in enemies){
if(collision(rockets[i], enemies[j])){
enemies[j].health-=5;
sound_hit[hit_counter-1].play();
hit_counter--;
if (hit_counter==0){
hit_counter=5;
}
rockets.splice(i,1);
if (enemies[j].health <= 0) {
score += enemies[j].score;
sound_explode[Math.floor(Math.random()*25)].play();
enemies[j].isDead = true;
}
} else if(rockets[i].x >= width){
rockets.splice(i,1);
}
}
}
If you want to test every rocket on every player its not really possible to do differently, without knowing more about position of players and rockets.
If you keep the collision function fast, this should though be no problem at all.
I can only think of two easy improvements on this:
when a collision is found use continue since looping over the rest of players should not be necessary (unless players is allowed to collide)
instead of splice'ing the rockets array multiple times, build a new one, excluding all "dead" rockets.
You should also consider using forEach, map and filter to make the code a bit easier to read:
rockets = rockets.filter(function(rocket) {
rocket.x+=projectile_speed;
if(rocket.x >= width) {
return false;
}
var enemy = enemies.find(function(enemy) { return collision(rocket, enemy) });
if(enemy) {
enemy.health-=5;
sound_hit[--hit_counter].play();
if (hit_counter==0){
hit_counter=5;
}
if (enemy.health <= 0) {
score += enemy.score;
sound_explode[Math.floor(Math.random()*25)].play();
enemy.isDead = true;
}
return false;
}
return true;
});
What you could try to do is to reduce the number of tests by grouping the enemies and rockets, so that you only have to test the elements in the same group.
Here is a simple implementation to show what I mean, this only partitions in X-direction, because your rockets only seem to travel horizontally:
var groupWidth = 100; // do some experiments to find a suitable value
var rocketGroups = [];
var enemyGroups = [];
// initalize groups, not shown (array of array of rocket/enemy),
// but here are some other function examples...
function addToGroups(element, groups) {
groups[element.x / groupWidth].push(element);
}
function move(element, groups, distance) {
if (element.x / groupWidth != (element.x + distance) / groupWidth) {
// remove element from the old group and put it in the new one
}
element.x += distance;
}
// Note: this is only to show the idea, see comments about length
function checkCollisions() {
var i,j,k;
for (i = 0; i < rocketGroups.length; i++) {
for (j = 0; j < rocketGroups[i].length; j++) {
for (k = 0; k < enemyGroups[i].length; k++) {
// only compares elements in the same group
checkPossibleCollision(rocketGroups[i], enemyGroups[i], j, k);
}
}
}
}

Threejs remove all together object from scene

I tried to make a function to remove all object from scene in one shoot but it removes only one object for invocation.
GeometryModel.prototype.clearScene = function(scene) {
var i;
for(i=0; i < scene.children.length; i++){
obj = scene.children[i];
scene.remove(obj);
}
}
another solution I tried and that works is this:
scene.children={};
but I am not sure if it is correct.
You have to do the opposite:
for( var i = scene.children.length - 1; i >= 0; i--) {
obj = scene.children[i];
scene.remove(obj);
}
because in each iteration the .children array changes once you do a .remove() from the start and the indexing of that array changes.
If you want to understand it better, unroll the for loop and follow what the index into the array is.
You can accomplish that with while :
while (object.children.length)
{
object.remove(object.children[0]);
}
Explanations :
object.children.length return true if object.children.length is not 0, if it's equal to 0 it return false.
So you just have to remove the first child element as long as object has children.
A preferred method is using the scene's traverse function. All objects have this function and will do a depth-first search through the parent's children.
Here's a snippet from Mr. Doob himself.
scene.traverse( function ( object ) {
if ( object instanceof THREE.Mesh ) {
var geometry = object.geometry;
var matrixWorld = object.matrixWorld;
...
}
});
And here's a bit from r82's source:
traverse: function ( callback ) {
callback( this );
var children = this.children;
for ( var i = 0, l = children.length; i < l; i ++ ) {
children[ i ].traverse( callback );
}
}
You can also use traverseVisible in your case:
scene.traverseVisible(function(child) {
if (child.type !== 'Scene') {
scene.remove(child);
}
});
The existing answer is good, I just want to provide a fuller response for those running into this same issue. When I'm using hot module reloading with Three.js, I often want to recreate all objects other than the plane and camera. To do that I do the following:
export const reload = (/* updatedDependencies */) => {
console.info('Canceling the run loop...')
cancelAnimationFrame(runLoopIdentifier) // the return value of `requestAnimationFrame`, stored earlier
console.info('Removing all children...')
for (let i = scene.children.length - 1; i >= 0 ; i--) {
let child = scene.children[ i ];
if ( child !== plane && child !== camera ) { // plane & camera are stored earlier
scene.remove(child);
}
}
while (renderer.domElement.lastChild) // `renderer` is stored earlier
renderer.domElement.removeChild(renderer.domElement.lastChild)
document.removeEventListener( 'DOMContentLoaded', onDOMLoad )
conicalDendriteTreeSegments = require('./plantae/conical-dendrite-trees').default
initializeScene() // re-add all your objects
runLoopIdentifier = startRenderRunLoop() // render on each animation frame
console.info('Reload complete.')
}
This solution seems to work better than traversing (according to the documentation of Three.js it is not recommended to use the traverse function to make scene graph changes). Also do not forget to dispose geometries in order to release the memory. It releases first the nodes that are deep in the graph so as to avoid memory leaks.
// Remove all objects
removeObjectsWithChildren(obj){
if(obj.children.length > 0){
for (var x = obj.children.length - 1; x>=0; x--){
removeObjectsWithChildren( obj.children[x]);
}
}
if (obj.geometry) {
obj.geometry.dispose();
}
if (obj.material) {
if (obj.material.length) {
for (let i = 0; i < obj.material.length; ++i) {
if (obj.material[i].map) obj.material[i].map.dispose();
if (obj.material[i].lightMap) obj.material[i].lightMap.dispose();
if (obj.material[i].bumpMap) obj.material[i].bumpMap.dispose();
if (obj.material[i].normalMap) obj.material[i].normalMap.dispose();
if (obj.material[i].specularMap) obj.material[i].specularMap.dispose();
if (obj.material[i].envMap) obj.material[i].envMap.dispose();
obj.material[i].dispose()
}
}
else {
if (obj.material.map) obj.material.map.dispose();
if (obj.material.lightMap) obj.material.lightMap.dispose();
if (obj.material.bumpMap) obj.material.bumpMap.dispose();
if (obj.material.normalMap) obj.material.normalMap.dispose();
if (obj.material.specularMap) obj.material.specularMap.dispose();
if (obj.material.envMap) obj.material.envMap.dispose();
obj.material.dispose();
}
}
obj.removeFromParent();
return true;
}

Adding a random class to element from an array that duplicates using jQuery

I'm creating a matching game and I'm trying to add a class from an array to match against.
The code I have below creates the classes I need, then randomizes them.
My problem is in the randomizeDeck function. I'm trying to add each of the classes to the specified element twice. When I console.log the code the classes gets added to the first six elements but not the last six, which I need it to do so that I have the classes to match against in the matching game I'm creating.
var cardDeck = new Array();
function createDeck() {
for (i = 1; i <= 6; i++) {
cardDeck.push("card-" + i);
}
}
createDeck();
var randDeck = cardDeck.sort(randOrd);
function randomizeDeck() {
card.each(function(i){
$(this).addClass(randDeck[i]);
});
}
randomizeDeck();
I think your createDeck function needs to create 12 classes instead of 6. Just push each one twice:
function createDeck() {
for (i = 1; i <= 6; i++) {
cardDeck.push("card-" + i);
cardDeck.push("card-" + i);
}
}
Then you'll have an array of 12 classes (2 each of 6 unique classes), which will be randomized and assigned to the 12 cards.
I suggest a separate variable to keep track of the index, rather that the each index. Once you've gone through the pack once, it might be a good idea to shuffle the deck again so the order is different on the second pass. YMMV.
function sortCards(randOrd) {
randDeck = cardDeck.sort(randOrd);
}
function randomizeDeck() {
var count = 0;
cards.each(function(i) {
if (i === 6) { count = 0; sortCards(randOrd); }
$(this).addClass(randDeck[count]);
count++;
});
}
Your randomizeDeck() function can be rewritten to use the same array of class names twice:
function randomizeDeck() {
card.each(function(i){
if(i < 6)
$(this).addClass(randDeck[i])
else
$(this).addClass(randDeck[i-6]);
});
}
Note: I would rewrite the variable card as $cards so that you know it's a jQuery object and in this case a collection of them. Otherwise, its hard to tell it apart from any other javascript var.
Try something like this - it's tested now updated
SEE THIS FIDDLE
http://jsfiddle.net/8XBM2/1/
var cardDeck = new Array();
function createDeck() {
for (i = 1; i <= 6; i++) {
cardDeck.push("card-" + i);
}
}
createDeck();
var randDeck = cardDeck.sort();
alert(randDeck);
function randomizeDeck() {
var x = 0;
$('div').each(function(i){
if ( i > 5) {
$(this).addClass(randDeck[x]);
x++;
} else {
$(this).addClass(randDeck[i]);
}
});
}
randomizeDeck();

creating a timer using setInterval that can clean up after itself?

I want to use setInterval to animate a couple things. First I'd like to be able to specify a series of page elements, and have them set their background color, which will gradually fade out. Once the color returns to normal the timer is no longer necessary.
So I've got
function setFadeColor(nodes) {
var x = 256;
var itvlH = setInterval(function () {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
// would like to call
clearInterval(itvlH);
// but itvlH isn't in scope...?
}
},50);
}
Further complicating the situation is I'd want to be able to have multiple instances of this going on. I'm thinking maybe I'll push the live interval handlers into an array and clean them up as they "go dead" but how will I know when they do? Only inside the interval closure do I actually know when it has finished.
What would help is if there was a way to get the handle to the interval from within the closure.
Or I could do something like this?
function intRun() {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
// now I can access an array containing all handles to intervals
// but how do I know which one is ME?
clearInterval(itvlH);
}
}
var handlers = [];
function setFadeColor(nodes) {
var x = 256;
handlers.push(setInterval(intRun,50);
}
Your first example will work fine and dandy ^_^
function setFadeColor(nodes) {
var x = 256;
var itvlH = setInterval(function () {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
clearInterval(itvlH);
// itvlH IS in scope!
}
},50);
}
Did you test it at all?
I've used code like your first block, and it works fine. Also this jsFiddle works as well.
I think you could use a little trick to store the handler. Make an object first. Then set the handler as a property, and later access the object's property. Like so:
function setFadeColor(nodes) {
var x = 256;
var obj = {};
// store the handler as a property of the object which will be captured in the closure scope
obj.itvlH = setInterval(function () {
for (i in nodes) {
nodes[i].style.background-color = "rgb(0,"+(--x)+",0);";
}
if (x <= 0) {
// would like to call
clearInterval(obj.itvlH);
// but itvlH isn't in scope...?
}
},50);
}
You can write helper function like so:
function createDisposableTimerInterval(closure, delay) {
var cancelToken = {};
var handler = setInterval(function() {
if (cancelToken.cancelled) {
clearInterval(handler);
} else {
closure(cancelToken);
}
}, delay);
return handler;
}
// Example:
var i = 0;
createDisposableTimerInterval(function(token) {
if (i < 10) {
console.log(i++);
} else {
// Don't need that timer anymore
token.cancelled = true;
}
}, 2000);

Categories