The idea is such that when toggle build mode, then you cast a ray that hits the world object, if result then place instnace (child) at the result.position.

At the moment i have a player node where i place the preview block inside the previewNode.

Player has a script with functions for block preview:

func new_block_logic():
	if is_preview_block:
		# place prieview block in player node
		var blocks = $PreviewBlock.get_children()
		if blocks.is_empty():
			# if there are no preview blocks in the node
			var newWall:Node3D = wall_block.instantiate()
			var rid_wall = get_rid_from_node(newWall)
			var result = get_ray_results(rid_wall)
			
			if result:
				snap_block(result,newWall)
				newWall.top_level=true
				$PreviewBlock.add_child(newWall)

			else:
				remove_preview_block()
		else:
			var newWall:Node3D = 	blocks[0]
			
			var rid_wall = get_rid_from_node(newWall)
			var result = get_ray_results(rid_wall)
			
			if result:
				snap_block(result, newWall)
			else:
				remove_preview_block()
	else:
		# delete prieview block
		remove_preview_block()
	pass
	
func snap_block(result, block):

	if result.collider.is_in_group("j_block"):
		pass
	else:
		block.position = result.position
	pass

func get_ray_results(rid_wall):
	var space_state = get_world_3d().direct_space_state
	var cam = cameraNode
	var mousepos = get_viewport().get_mouse_position()

	var origin = cam.project_ray_origin(mousepos)
	var RAY_LENGTH = 1000.0
	var end = origin + cam.project_ray_normal(mousepos) * RAY_LENGTH
	var query = PhysicsRayQueryParameters3D.create(origin, end)
	query.collide_with_areas = true

	if rid_wall:
		
		query.exclude = [self, rid_wall]
	else:
		query.exclude = [self]
		
	
	return space_state.intersect_ray(query)
func get_rid_from_node(node:Node3D):
	var col = node.find_child("StaticBody3D",true) #.get_node("CollisionShape3D")
	if col: 
		var rid = col.get_rid()
		return rid

As you can see in new_block_logic() if the ray hit gets results then i get new instance of block scene and then i set that block position to the result position and i set the top_level to true and then i add it as child to the preview node, but when i run the game, and activate the build mode, the block appears to be added at default coordinate and when the next frame comes the position is set to the correct position.

How to can i fix this so that the block i want to add is set to the right position before i add it to scene??

Video in godot 4.1.3

video in godot 4.2 rc2

To me it seems like the set top level to true is not working 🙁 on the frame 0 when you add child to node.

  • kuligs2 The code is complicated because its code

    No, your posted code snippet looks too messy and complicated for what it's supposed to do. Because of that it's easy to lose track of what's happening, which precisely is the case here. You forgot to actually update the position of newly added block:

    Instead of just:

    $PreviewBlock.add_child(newWall)

    Do:

    $PreviewBlock.add_child(newWall)
    snap_block(result, newWall)

    In general, the whole thing should be rewritten to avoid ugly nested if/elses and duplication of parts of code. For example, this needlessly appears twice in a relatively short function:

    var rid_wall = get_rid_from_node(newWall)
    var result = get_ray_results(rid_wall)

    Doing things like this is an open invitation to bugs.

try global_position instead of position

    award

    Setting global position is throwing this error. And it still is jittery, It still when added as child is placed at local position then next frame its in correct position.

    The only way to make the node appear without jittery position switcherooney, is to set visibility to false before you add child, then you cycle to the next frame with another function and check if block exist in the node you added it and if it exist then you set visible to true.

    But this feels so hacky, it does not feel right.

    func new_block_logic():
    	if is_preview_block:
    		# place prieview block in player node
    		var blocks = $PreviewBlock.get_children()
    		if blocks.is_empty():
    			# if there are no preview blocks in the node
    			var newWall:Node3D = wall_block.instantiate()
    			var rid_wall = get_rid_from_node(newWall)
    			var result = get_ray_results(rid_wall)
    			
    			if result:
    				newWall.top_level=true
    				newWall.visible = false # <----------------- here
    				snap_block(result,newWall)
    				$PreviewBlock.add_child(newWall)
    			else:
    				remove_preview_block()
    		else:
    			var newWall:Node3D = 	blocks[0]
    			
    			var rid_wall = get_rid_from_node(newWall)
    			var result = get_ray_results(rid_wall)
    			
    			if result:
    				snap_block(result, newWall)
    				newWall.visible = true # <----------------- here
    			else:
    				remove_preview_block()
    	else:
    		# delete prieview block
    		remove_preview_block()
    	pass
    func _physics_process(delta):
    	move_and_slide()
    	new_block_logic()

    I had problems with setting positions on instantiated objects too.
    The problem was setting the position while the object wasn't a child of anything.

    In snap_block you want to set the position, but the newWall has no parent yet.
    I believe the error message !is_inside_tree() shows up only when you try to set global position of unparented objects, but the problem occurs also for setting local positions I guess.

    I guess you want to freely position the newWall and when placing it add it as a child to the preview block?
    Maybe parent the newWall for placement on the scene tree get_tree().root.add_child(newWall) (or any other node) and then use reparent($PreviewBlock) .
    Or set position after adding it as child.

      trizZzle exactly as you described, but this is kinda stupid, that you cant set the position of instanced object before adding it to the parent. If i add it to the root then it will still spawn somewhere in the world and then after the next frame it will be moved to the correct position. What if there is another player moving about the same coordinates, then that player will collide with the object, every time someone adds it to the world.

      I just asked my friend (chatgtp):

      If you want to ensure that the position change is applied immediately, you can call instance.update_transform() after setting the position:

      func newthing():
          var instance = something.instantiate()
          get_tree().root.add_child(instance)
          instance.position = Vector3.ZERO
          instance.update_transform()  # Force an immediate update of the node's transform

      This will force an immediate update of the node's transformation, ensuring that the change in position is applied before the next frame is rendered. Keep in mind that forcing an immediate update can have performance implications, so use it judiciously depending on your specific use case.

      EDIT: I didn't know that positions where updated in the next physics process frame. Good to know.

        It's force_update_transform()

          kuligs2 You're probably right. But often the wrong answer pointed me in the right direction. In this example: There should be a function for this, but maybe it got renamed.

            xyz but unfortunately its still not working

            • xyz replied to this.

              trizZzle it would be nice if godot had the method/function/property preview when you start typing, but unfortunately it shows some of avaliable functions, but not all.

              kuligs2 but unfortunately its still not working

              I didn't expect it to. I just wrote the correct function name.

              Your code looks too complicated. There may be bugs, including ones in parts of code you didn't show.
              If you already have the preview block on the position why just not use it? Or place the block from mousedown event handler.
              Anyway, it'd be best to make a minimal reproduction project and post it for inspection 🙂

                xyz The code is complicated because its code, not a hello world example oneliner. Why do you say im not using the block? Im placing it at the mouse coordinates in the world. The problem is that when i set the position in the world and add it to parent (add_child) the position is not the one that i set/mouse pointer. Its the parent node position, which in my case is a node inside a player instnace.

                Here is the project as is. Run it, press start, then use middle mouse button to activate the building mode then use right mouse button to place the block.
                https://share.apefront.lv/s/4NiGqN6WPEARFzm

                • xyz replied to this.

                  kuligs2 The code is complicated because its code

                  No, your posted code snippet looks too messy and complicated for what it's supposed to do. Because of that it's easy to lose track of what's happening, which precisely is the case here. You forgot to actually update the position of newly added block:

                  Instead of just:

                  $PreviewBlock.add_child(newWall)

                  Do:

                  $PreviewBlock.add_child(newWall)
                  snap_block(result, newWall)

                  In general, the whole thing should be rewritten to avoid ugly nested if/elses and duplication of parts of code. For example, this needlessly appears twice in a relatively short function:

                  var rid_wall = get_rid_from_node(newWall)
                  var result = get_ray_results(rid_wall)

                  Doing things like this is an open invitation to bugs.

                    xyz i appreciate you giving me your attention and time for helping. The code is what it is, its not a game, its not an art piece, its my way to learn how to do things.

                    When it comes to sequence of events, i would understand that before i show an object in the world, i would set its location, then add object to the world, this makes sense,

                    But apparently godot is ass backwards where you have to add object to world then set its position.

                    Either way it works, changing the 🇦

                    snap_block(result,newWall)
                    $PreviewBlock.add_child(newWall)

                    to

                    $PreviewBlock.add_child(newWall)
                    snap_block(result,newWall)

                    also fyi, if i add the block to world node not player node then the first code works too. So idk..

                    And please explain what you meant with:

                    For example, this needlessly appears twice in a relatively short function:

                    var rid_wall = get_rid_from_node(newWall)
                    var result = get_ray_results(rid_wall)

                    These are different functions that return different values.

                    Given that godot is ass backwards and wont show me values when i put line breaks and try to debug what kind of value i get from methods/properties, i have to define vars pretty much each line to see what kind of value it is.

                    thanks~!

                    • xyz replied to this.

                      kuligs2 You just don't have full understanding of how realtime graphics (including this engine) works. So exercising some restraint before passing the final judgement may be in order 😉

                      The code tidiness is not for winning awards, it primarily serves you, the developer, so you don't end up tangled in unwieldy mess (and puzzling bugs such as this one 🙂)

                      The order of setting the position vs parenting the node is irrelevant as long as the proper position is set before the current frame finishes processing. You didn't do so but instead you updated the position as late as the next frame so you got one frame drawn with non-updated position.

                      As for what I meant about code duplication; your function has two separate blocks of code that do exactly the same thing. It's a sign that control flow can and should be improved.

                      I'd write this whole function as follows, eliminating most of repetition and making it almost twice as short:

                      func new_block_logic():
                      	if not is_preview_block:
                      		remove_preview_block()
                      		return
                      		
                      	var blocks = $PreviewBlock.get_children()
                      	var wall = wall_block.instantiate() if blocks.is_empty() else blocks[0]
                      	if blocks.is_empty():
                      		wall.top_level = true
                      		$PreviewBlock.add_child(wall)
                      
                      	var raycast_result = get_ray_results(get_rid_from_node(wall))
                      	if raycast_result: snap_block(raycast_result, wall)
                      	else: remove_preview_block()

                      The whole thing could architecturally be made even simpler if you don't instantiate the cursor block all the time but instead just keep it alive and hide/show it as needed.