Tips for using the Godot game engine

Blender

Backface culling = material -> viewport display -> settings -> backface culling checkbox.

If collision is messed up, check the normals, apply scale (maybe all transforms), and recalc normals in Blender.

If animations messed up - reset all bone positions to rest before exporting from blender

GDScript

Casting:

  var button := node as Button
    if button:
      #it's this type! No need for comparisons or type checks
      
lerp_angle() for smoothly transistioning between angles

Hold alt for multi-caret editing

'"property" in node' to see if node.property exists

Uninitialized Strings are "" NOT null

Do NOT save Callables in a Resource

Events.gd - a script with only signal declarations in it. Make it autoload so its a global singleton. Then hook up global events through it, very handy.

Multiplayer

@rpc annotations:
@rpc("call_local", "any_peer", "reliable") to call method on all peers from any peer.
@rpc("call_remote", "any_peer", "reliable") - rpc_id(1, "methodname") for any peer to call a server method

MultiplayerSynchronizer node to replicate it's variables from the authority to all others.
Might need to make a specific node/script just to hold vars for it, it has trouble directly accessing vars on some nodes, like CharacterBody3D.
MultiplayerSpawner node to replicate all children of a specified node from the authority to all others.
Has mysteriously failed on me, not sure if buggy or I don't know how to use it.

set_multiplayer_authority(name) where your name is set to your peer_id from the authority. Then each player can control their character, still need synchros
rpc's cannot return values, but you can do it with an extra rpc call:
client - player.gd
Globals.main.level.rpc_id(1, "find_spawn")
    
server - level.gd
@rpc("call_local", "any_peer")
func find_spawn(team_num := 0):
  var point := Vector3.INF
  ...
  var caller := multiplayer.get_remote_sender_id()
  Globals.main.players[caller].rpc_id(caller, "set_respawn_point", point)

client - player.gd
@rpc("call_local", "any_peer")
func set_respawn_point(point):
	global_position = point
    

if you get tons of error spam about sync_state, try looking for MultiplayerSynchronizer's with no properties assigned.

You can calculate the distance from object2's plane (via their up-vector) by using dot-product:
var distance := abs(object2.global_transform.y.dot(object1.global_position))

Misc

Use Area3D's to control audio buses, to get different reverb or effects for different areas.

Right click a scene -> view owners to see what uses that scene

For anisotropic filtering - need to enable on each material - set texture_filter as linear w/ mipmaps anisotropic

If your gui isn't expanding as expected, check container sizing and the 'expand' checkbox

Timer.start(time) - timer overwrites the earlier wait_time when called for all future calls too

Can add Timers to the scene to save on setup code and remembering to add_child() them

Custom values for instantiated scenes - Right click on node -> check Editable Children

Can add any data to any node using metadata in the editor or code

Unique Names - right click node in editor -> check % scene unique name then use in code like '%myNode'

Get 3d node's screen position - 'cam.unproject_position(node.get_global_position)'

Callable .bind() args are added after .call() args

Keep physics fps at or above fps otherwise there's a lotta jitter

Tweens work on bools and lots of other types, but not always in the expected way

linear_to_db() for volume sliders

get_instance_id() and instance_from_id() to get global references to any node.

gui in 3d -
 add a sub Viewport to a Sprite3D.
 give it a viewport texture in subviewport
 turn transparent BG on
 trim off bottom and right pixels with the sprite3d's region
 if using a meshinstance - material has to be local to scenes

Vector.distance_to_squared() is faster than Vector.distance_to() so use it when you can

Triplanar mapping and displacements maps can't work together

Small things ignoring collisions? Turn on continuous CD on the physics object

Physics never sleep? might have to write own code to stop it at low vels

'F' in editor to zoom to select node

To move a rigid body via code, need to use _integrate_forces(state) and modify the state var:

  func _integrate_forces(state):
    if reset:
      reset = false
      state.transform = Transform2D(0.0, reset_pos)
      linear_velocity = Vector2.ZERO
      angular_velocity = randf_range(-4.0, 4.0)

If your subviewport looks blurry, change the texture filtering on the SubViewportContainer -> CanvasItem -> Texture -> Filter -> set to Nearest. Might need to turn on Stretch at the top too. Also might need code to resize the subviewport to the container's size on size_changed signal.

Overlapping Areas that have scripts with body_entered can be screwy. Separate them a bit.

Debug -> Keep debug server open checkbox to listen and attach to other godot instances that run

Can set a constant color on a reflection probe for colored ambient light in an area

Mobile = lightmapgi and reflection probes, no fancier effects.

Faking GI here

To make a 3d thing invisible but still cast a shadow (like a fps player's head for example), just set each mesh.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_SHADOWS_ONLY in code or find it in the editor. Alpha materials don't work.

Godot XR Tools - premade code and scenes to help get started on vr games faster.

if mouse clicks aren't getting through to 3d, check for ui nodes eating mouse input. Check their Mouse -> Filter and try changing it to Pass.