Object3D loses it's children when two or more are pushed into array

Hey, I’m making this recreation of The Binding of Isaac with Three.js for a uni project, I just came up with a problem that I don’t understand at all:

Basically, in the game there are pedestals with items floating above them that you can take when touching the pedestal, the way I’ve made that is by making a genItemPedestal function (read below for all the relevant code) in which I create a pedestal geometry, add the item geometry as a child using .add() and then send the pedestal geometry as a return value. I use that function to assign the result to a variable and push that variable into a “pedestals” array that will be used later for multiple things, the problem here is that one of the things is updating the pedestal item’s y position to make a floating effect, for that I need access to the children, the thing is when I add one pedestal everything works fine, but when I add a second one, the function that updates the pedestals says that it cannot access a pedestal’s children because it’s undefined.
I dont get it, help ;-;.

Also, as stated earlier, I’m still a student, which means I’m still learning and I’m definitely not the greatest coder ever so don’t expect high quality super-professional code.

Here’s the code:

item geometry generation------

let godhead = function() {
	let godhead_geometry = new THREE.ConeGeometry( 0.4, 0, 3 );
	let godhead_material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
	let godhead = new THREE.Mesh( godhead_geometry, godhead_material );

	let eye_pupil_geometry = new THREE.CircleGeometry( 0.07, 8 );
	let eye_material = new THREE.MeshBasicMaterial( { color: 0x000000 } );
	let eye = new THREE.Mesh( eye_pupil_geometry, eye_material );

	let eye_border_geometry = new THREE.RingGeometry( 0.15, 0.18, 8, 1, Math.PI / 6, 2 * Math.PI / 3 );
	let eye_top = new THREE.Mesh( eye_border_geometry, eye_material );
	let eye_bottom = new THREE.Mesh( eye_border_geometry, eye_material );

	eye.add( eye_top, eye_bottom );
	eye_top.rotateZ( Math.PI );
	eye_top.position.y = 0.09;
	eye_bottom.position.y = -0.09;
	eye.rotateX( Math.PI / 2 );

	let godhead_corners_geometry = new THREE.ConeGeometry( 0.15, 0.025, 3 );
	let red_corner_material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
	let blue_corner_material = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
	let yellow_corner_material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
	let red_corner = new THREE.Mesh( godhead_corners_geometry, red_corner_material );
	let blue_corner = new THREE.Mesh( godhead_corners_geometry, blue_corner_material );
	let yellow_corner = new THREE.Mesh( godhead_corners_geometry, yellow_corner_material );

	godhead.add( red_corner, blue_corner, yellow_corner, eye );
	red_corner.position.set( 0, 0, 0.28 );
	blue_corner.position.set( -0.25, 0, -0.13 );
	yellow_corner.position.set( 0.25, 0, -0.13 );
	eye.position.set( 0, -0.01, 0.02 );

	godhead.rotateX( -15 * Math.PI / 16 );
	godhead.rotateY( Math.PI );

	return godhead;
}
items.push( godhead() );

pedestal generation here------

function genItemPedestal( item_id ) {
	let pedestal_geometry = new THREE.SphereGeometry( 0.35, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2 );
	let pedestal_material = new THREE.MeshStandardMaterial( { color: 0x665533 } );
	let pedestal = new THREE.Mesh( pedestal_geometry, pedestal_material );
	let item = new THREE.Mesh();
	scene.add(pedestal);

	item = items[ item_id ];

	pedestal.add( item );
	item.position.y = 1.5;

	pedestal.userData = { id: item_id, taken: false, float_position: 0 };

	return pedestal;
}

pedestal update function------

function updatePedestals() {
	for ( let i = 0; i < pedestals.length; i++) {
		if ( !pedestals[i].userData.taken ) {
			pedestals[i].userData.float_position += 0.05;
			pedestals[i].children[0].position.y = 1.5 + Math.sin(pedestals[i].userData.float_position) / 5;
		}
	}
}

initialization of the pedestals and addition to the pedestals list------

let pedestal1 = genItemPedestal( Math.floor( Math.random() * items.length ) );
pedestal1.position.set( 0, 0.5, -2 );
pedestals.push( pedestal1 );

let pedestal2 = genItemPedestal( Math.floor( Math.random() * items.length ) );
pedestal2.position.set( 0, 0.5, 2 );
pedestals.push( pedestal2 );

I think that’s all the relevant code, if you need more details feel free to ask.

If children[0] is undefined, it means that you have added that object to another object (do not use random, otherwise it may happen).

I suggest you to have shared geometries and materials, if they are identical.

mesh1.add(child); // now mesh1.children[0] === child;
mesh2.add(child); // now mesh1.children[0] === undefined
// because child has been added to mesh2 and removed from mesh1

Thanks for your answer, but I solved the problem in the meantime.

After further testing I have concluded that I am a stinky, stoopid, unknowledgeable noob, because this:

items.push( godhead() );

is not adding the function to the list, but the result of the list, so when I call:

let item = items[ item_id ];

in genItemPedestal it’s not calling the function godhead and assigning the result to item, but taking the element added to the array items and moving it into the variable, which is easily solved by modifying the code to make a clone of it instead of moving it, like this:

let item = items[ item_id ].clone( true );

And those 15 characters have costed me three hours of my life, I love coding :).

1 Like

Calm down ahaha

You could also use a class instead of a function (in a separate js to make it clean), what do you say?

const godhead_geometry = new THREE.ConeGeometry( 0.4, 0, 3 );
const godhead_material = new THREE.MeshBasicMaterial( { color: 0xffffff } );

class GodHead extends Mesh {
  constructor() {
    super(godhead_geometry, godhead_material);

    // other stuff

    this.rotateX( -15 * Math.PI / 16 );
    this.rotateY( Math.PI );
  }
}

const godhead = new GodHead();
1 Like

I actually intend on doing that (using a second file, idk much about classes in js and I’m not a fan of copying code that I don’t understand, especially if its for a uni project xd) but there’s still tons of stuff that I want to add, Isaac has tons upon tons of content, and this is not the only project I need to work on, I have two other projects (that I haven’t even started working on yet, cuz I’m a very responsible student and haven’t been procrastinating one bit), one of those is due in less than two weeks and the other one and the three.js one are due in three weeks, so I’ll add the secondary file later down the line if I have time, which I don’t think I will.

Still, thanks for your answer again, if I had more time it’d help more than it does now xd.

ahaha good luck then! :slight_smile: