How to make a hole in mesh between player and the camera?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Skipperro

Hi,

I have a 3D isometric scene where player can sometimes disappear behind a wall. I would like to dynamically make a hole in a wall that stand between player and the camera or even better - make this wall half-transparent, to have a “see through walls” effect.

Kind of like here:

How can I do this in Godot?

Don’t have a definitive answer but if it was for me I would look to do this with a Shader.

BraindeadBZH | 2019-08-01 18:50

:bust_in_silhouette: Reply From: johnygames

OK here’s one way to do this:

For a start, you need a way to recognize when your character moves behind an obstacle, or a way to figure out what lies between the camera and the character. Then, it is a matter of changing the material of the obstacle into a semi-transparent material.

One way you can acquire the obstacle is by casting a ray. I suggest you look it up by yourself, because as far as I know, there are multiple ways to work with rays in Godot. Choose the one that best fits your needs.
Take a look at the following sources:

  1. Ray-casting — Godot Engine (3.1) documentation in English
  2. https://www.youtube.com/watch?v=pIOmaSNpCmE
  3. and an older one: https://www.youtube.com/watch?v=y6yyDJy0uVE

Having said that, I have tested the following minimal example and it works: I have attached a script to the Camera node. This script casts a ray from the position of the Camera to 1000 units in the z direction of the Camera (again, there are other ways of casting rays that are more efficient, check the docs). If said ray detects a collision object (such as an Area or RigidBody), then it acquires the collider’s MeshInstance using the collider method. Afterwards, The script sets the collider’s MeshInstance’s material into another semi-transparent material using the set_surface_material(0, mat) method.

extends Camera

onready var wall = get_node("/root/Spatial/Invisible_wall/Invisible_wall_mesh") # our obstacle
onready var mat = preload("res://Materials/orange.tres")# our second, transparent material

func _ready():
	pass
	
	
func _physics_process(delta):
	var space_state = get_world().direct_space_state
	var result = space_state.intersect_ray(translation, Vector3(translation.x, translation.y, translation.z-1000)) # Cast a ray from camera
	if result: # if collision is found
		print(result.collider) 
		result.collider.get_node("Invisible_wall_mesh").set_surface_material(0, mat)
		print(mat) #get the collider's MeshInstance and change its material

A few notes:

  1. Raycasting works with Area and Rigidbody nodes
  2. I have hardcoded everything in the example, but you can avoid that easily
  3. A more efficient way to do this might be to attach the script on the colliders instead of the Camera node. That way you can set the logic in such a way that once the ray no longer collides with your obstacles, your obstacles can have their previous material.

I hope that helps!