Pesquisar este blog

godot mato que amassa quando pisa e vento tbm usa o material em shader

 @tool

extends MultiMeshInstance3D


@export var clump_center: NodePath:

set(v):

clump_center = v

_request_scatter()


@export var clump_radius: int = 10:

set(v):

clump_radius = v

_request_scatter()


@export_range(1, 50) var clump_angulate: int = 1:

set(v):

clump_angulate = v

_request_scatter()


@export_range(0, 200) var thickness: int = 10:

set(v):

thickness = v

_request_scatter()


@export var meadowing: bool = false:

set(v):

meadowing = v

_request_scatter()


@export var meadow_radius: int = 0:

set(v):

meadow_radius = v

_request_scatter()


@export_range(0, 10) var blade_width: float = 1.0:

set(v):

blade_width = v

_request_scatter()


@export_range(0, 10) var blade_height: float = 1.0:

set(v):

blade_height = v

_request_scatter()


@export_range(0, 100) var amount_randomize: int = 3:

set(v):

amount_randomize = v

_request_scatter()


@export var y_offset: float = 0.0:

set(v):

y_offset = v

_request_scatter()


@export var randomize_hori: bool = true:

set(v):

randomize_hori = v

_request_scatter()


@export var randomize_vert: bool = true:

set(v):

randomize_vert = v

_request_scatter()


@export var skip_instances: Array[int] = []

@export var ground_collision_mask: int = 1

@export var max_instances: int = 20000

@export var player_node: Node3D


var _last_center_position: Vector3

var _needs_update := false

var _material_cache: ShaderMaterial


func _ready() -> void:

if not multimesh:

multimesh = MultiMesh.new()

multimesh.transform_format = MultiMesh.TRANSFORM_3D

_cache_material()

if clump_center != NodePath():

var c = get_node_or_null(clump_center)

if c:

_last_center_position = c.global_transform.origin

if Engine.is_editor_hint():

set_process(true)

_request_scatter()


func _process(_delta: float) -> void:

if Engine.is_editor_hint():

if not is_visible_in_tree():

return

if _needs_update:

scatter()

_needs_update = false

_update_shader_player()


# Checa mudança de posição do centro de forma otimizada

if clump_center != NodePath():

var c = get_node_or_null(clump_center)

if c and c.global_transform.origin != _last_center_position:

_last_center_position = c.global_transform.origin

_request_scatter()


func _request_scatter() -> void:

if Engine.is_editor_hint():

_needs_update = true


func scatter() -> void:

if not multimesh:

return

var center_node = get_node_or_null(clump_center)

if not center_node:

return

var center = center_node.global_transform.origin

var count = min(thickness * thickness, max_instances)

multimesh.instance_count = count

var space_state = get_world_3d().direct_space_state

var current_index := 0

for i in range(count):

if i in skip_instances:

continue

var angle = randf() * TAU

var dist = randf() * clump_radius

if meadowing:

dist += meadow_radius

var x = center.x + cos(angle) * dist

var z = center.z + sin(angle) * dist

# Raycast direto otimizado

var query = PhysicsRayQueryParameters3D.create(

Vector3(x, 500, z),

Vector3(x, -500, z),

ground_collision_mask

)

query.collide_with_areas = false

var hit = space_state.intersect_ray(query)

if hit.is_empty():

continue

var t = Transform3D()

if randomize_hori:

t = t.rotated(Vector3.UP, randf_range(-sqrt(amount_randomize), sqrt(amount_randomize)))

if randomize_vert:

t = t.rotated(Vector3.LEFT, randf_range(-0.2, 0.2))

t = t.rotated(Vector3.FORWARD, randf_range(-0.2, 0.2))

var scale_rand = randf_range(0.8, 1.2)

t = t.scaled(Vector3(blade_width * scale_rand, blade_height * scale_rand, blade_width * scale_rand))

t.origin = Vector3(x, hit.position.y + y_offset, z)

multimesh.set_instance_transform(current_index, t)

current_index += 1

multimesh.instance_count = current_index


func _cache_material() -> void:

if material_override is ShaderMaterial:

_material_cache = material_override

elif multimesh and multimesh.mesh:

var mat = multimesh.mesh.surface_get_material(0)

if mat is ShaderMaterial:

_material_cache = mat


func _update_shader_player() -> void:

if not player_node:

return

if not _material_cache:

_cache_material()

if _material_cache:

_material_cache.set_shader_parameter("player_pos", player_node.global_transform.origin)









codigo do material shder dos matos:


shader_type spatial;
render_mode cull_disabled; // Útil para grama 2D/planos para não sumir ao olhar por trás

uniform vec3 player_pos;
uniform float radius : hint_range(0.1, 10.0) = 2.0;
uniform float bend_amount : hint_range(0.0, 5.0) = 2.0;
uniform float height_influence : hint_range(0.0, 5.0) = 1.0;
uniform float wind_strength : hint_range(0.0, 1.0) = 0.10;
uniform float wind_speed : hint_range(0.0, 10.0) = 2.0;
uniform float wind_scale : hint_range(0.1, 10.0) = 2.0;
uniform vec4 grass_color : source_color = vec4(0.2, 0.8, 0.3, 1.0);
uniform float color_variation : hint_range(0.0, 0.5) = 0.15;

varying vec3 v_world_pos;

void vertex() {
    v_world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    float height_mask = clamp(VERTEX.y * height_influence, 0.0, 1.0);

    // 🌬️ VENTO OTIMIZADO
    float wind_phase = TIME * wind_speed + (v_world_pos.x * 0.7 + v_world_pos.z * 0.5) * wind_scale;
    float wind_wave = sin(wind_phase);
    
    VERTEX.x += wind_wave * wind_strength * height_mask;
    VERTEX.z += cos(wind_phase * 0.8) * wind_strength * height_mask;

    // 👣 INTERAÇÃO COM O PLAYER
    vec2 to_player = v_world_pos.xz - player_pos.xz;
    float dist = length(to_player);
    
    if (dist < radius) {
        float t = dist / radius;
        float strength = smoothstep(1.0, 0.0, t);
        vec2 dir = to_player / max(dist, 0.0001); // Evita divisão por zero
        
        VERTEX.xz += dir * bend_amount * strength * height_mask;
        VERTEX.y -= bend_amount * 0.6 * strength * height_mask;
    }
}

void fragment() {
    // Ruído procedural leve para variação de cor sem texturas extras
    float noise = fract(sin(dot(v_world_pos.xz, vec2(12.9898, 78.233))) * 43758.5453);
    vec3 col = grass_color.rgb + (noise - 0.5) * color_variation;
    
    ALBEDO = col;
    ALPHA = grass_color.a;
    ALPHA_SCISSOR_THRESHOLD = 0.5; // Caso use texturas com transparência
}

godot inimigo seguindo playr usando o navegacao3d

 extends CharacterBody3D


@export var player: Node3D

@export var velocidade_perseguir: float = 4.0

@export var velocidade_vagar: float = 2.0


# Distância mínima para começar a perseguir

@export var distancia_deteccao: float = 12.0


# Configurações do modo vagar

@export var raio_vagar: float = 8.0

@export var tempo_mudar_destino: float = 4.0


@onready var nav_agent: NavigationAgent3D = $NavigationAgent3D


var tempo_restante: float = 0.0

var ponto_inicial: Vector3

var gravidade: float = ProjectSettings.get_setting("physics/3d/default_gravity")


func _ready() -> void:

# Salva a posição inicial como referência para vagar na área em volta

ponto_inicial = global_position


func _physics_process(delta: float) -> void:

# Aplica a gravidade

if not is_on_floor():

velocity.y -= gravidade * delta


# Se o caminho ainda não terminou, move o inimigo

if not nav_agent.is_navigation_finished():

var proxima_posicao: Vector3 = nav_agent.get_next_path_position()

var direcao: Vector3 = (proxima_posicao - global_position)

direcao.y = 0

direcao = direcao.normalized()


if direcao != Vector3.ZERO:

look_at(global_position + direcao, Vector3.UP)

# Ajusta a velocidade conforme o estado

var vel_atual: float = velocidade_vagar

if player and global_position.distance_to(player.global_position) <= distancia_deteccao:

vel_atual = velocidade_perseguir

velocity.x = direcao.x * vel_atual

velocity.z = direcao.z * vel_atual

else:

# Para o movimento horizontal se chegou ao destino

velocity.x = move_toward(velocity.x, 0, velocidade_vagar)

velocity.z = move_toward(velocity.z, 0, velocidade_vagar)


move_and_slide()


# Lógica de atualização de destino

_atualizar_destino(delta)


func _atualizar_destino(delta: float) -> void:

if player and global_position.distance_to(player.global_position) <= distancia_deteccao:

# --- PERSEGUIR PLAYER ---

nav_agent.target_position = player.global_position

else:

# --- VAGAR ALEATORIAMENTE ---

tempo_restante -= delta

if tempo_restante <= 0.0 or nav_agent.is_navigation_finished():

tempo_restante = tempo_mudar_destino

_definir_novo_destino_aleatorio()


func _definir_novo_destino_aleatorio() -> void:

# Gera um ponto aleatório em volta da posição inicial

var offset = Vector3(

randf_range(-raio_vagar, raio_vagar),

0,

randf_range(-raio_vagar, raio_vagar)

)

var destino_aleatorio = ponto_inicial + offset

# Obtém o ponto navegável mais próximo do destino gerado

var mapa_nav = get_world_3d().navigation_map

var ponto_valido = NavigationServer3D.map_get_closest_point(mapa_nav, destino_aleatorio)

nav_agent.target_position = ponto_valido


gerador de terreno godot com colisor , arvore e textura por bioma

 @tool

extends MeshInstance3D


# ==============================================================================

# PROPRIEDADES DO INSPECTOR

# ==============================================================================


@export_category("Configurações do Terreno")

@export var terrain_size: Vector2 = Vector2(40, 40):

set(value):

terrain_size = value

if is_node_ready(): _generate_terrain()


@export var subdivision: int = 40:

set(value):

subdivision = clamp(value, 2, 250)

if is_node_ready(): _generate_terrain()


@export var height_scale: float = 8.0:

set(value):

height_scale = value

if is_node_ready(): _generate_terrain()


@export var generate_collision: bool = true:

set(value):

generate_collision = value

if is_node_ready(): _update_collision()


@export_category("Configuração das Bordas (Suavização e Profundidade)")

@export var enable_edge_falloff: bool = true:

set(value):

enable_edge_falloff = value

if is_node_ready(): _generate_terrain()


@export_range(0.05, 0.5, 0.01) var edge_falloff_margin: float = 0.2:

set(value):

edge_falloff_margin = value

if is_node_ready(): _generate_terrain()


@export var edge_target_height: float = -2.0: # Altura/Profundidade final das bordas (Valores negativos afundam o terreno)

set(value):

edge_target_height = value

if is_node_ready(): _generate_terrain()


@export var edge_falloff_radial: bool = false:

set(value):

edge_falloff_radial = value

if is_node_ready(): _generate_terrain()


@export_category("Configurações do Ruído Base")

@export var noise_seed: int = 1337:

set(value):

noise_seed = value

_update_noise_seeds()

if is_node_ready(): _generate_terrain()


@export var noise_frequency: float = 0.04:

set(value):

noise_frequency = value

if noise: noise.frequency = value

if is_node_ready(): _generate_terrain()


@export var gerar_novo_terreno_aleatorio: bool = false:

set(value):

if value:

noise_seed = randi()

_update_noise_seeds()

if is_node_ready(): _generate_terrain()

gerar_novo_terreno_aleatorio = false


@export_category("Texturas dos Biomas")

@export var texture_sand: Texture2D:

set(value): texture_sand = value; _update_shader_parameters()

@export var texture_grass: Texture2D:

set(value): texture_grass = value; _update_shader_parameters()

@export var texture_rock: Texture2D:

set(value): texture_rock = value; _update_shader_parameters()

@export var texture_snow: Texture2D:

set(value): texture_snow = value; _update_shader_parameters()

@export var texture_trail: Texture2D:

set(value): texture_trail = value; _update_shader_parameters()


@export var texture_scale: float = 10.0:

set(value): texture_scale = value; _update_shader_parameters()


@export_category("Configuração de Trilhas (Caminhos Conectados)")

@export var enable_trails: bool = true:

set(value):

enable_trails = value

if is_node_ready(): _generate_terrain()


@export var trail_frequency: float = 0.03:

set(value):

trail_frequency = value

if trail_noise: trail_noise.frequency = value

if is_node_ready(): _generate_terrain()


@export var trail_width: float = 0.06:

set(value):

trail_width = value

if is_node_ready(): _generate_terrain()


@export var trail_warp_strength: float = 12.0:

set(value):

trail_warp_strength = value

if is_node_ready(): _generate_terrain()


@export_category("Configuração de Árvores (Bioma Grama)")

@export var spawn_trees: bool = true:

set(value):

spawn_trees = value

if is_node_ready(): _generate_trees()


@export var tree_mesh: Mesh:

set(value):

tree_mesh = value

_update_tree_mesh()


@export var tree_density: float = 0.2:

set(value):

tree_density = value

if is_node_ready(): _generate_trees()


# ==============================================================================

# VARIÁVEIS INTERNAS E RUÍDOS

# ==============================================================================


var noise: FastNoiseLite = FastNoiseLite.new()

var trail_noise: FastNoiseLite = FastNoiseLite.new()

var warp_noise: FastNoiseLite = FastNoiseLite.new()


var tree_multimesh_inst: MultiMeshInstance3D

var static_body: StaticBody3D

var collision_shape: CollisionShape3D


# ==============================================================================

# CICLO DE VIDA E INICIALIZAÇÃO

# ==============================================================================


func _ready() -> void:

_init_noises()

_setup_tree_multimesh()

_generate_terrain()


func _init_noises() -> void:

noise.noise_type = FastNoiseLite.TYPE_SIMPLEX

trail_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX

warp_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX

warp_noise.frequency = 0.015

_update_noise_seeds()


func _update_noise_seeds() -> void:

if noise:

noise.seed = noise_seed

noise.frequency = noise_frequency

if trail_noise:

trail_noise.seed = noise_seed + 123

trail_noise.frequency = trail_frequency

if warp_noise:

warp_noise.seed = noise_seed + 456


# ==============================================================================

# CÁLCULOS PROCEDURAIS (ALTURA, BORDAS E TRILHAS)

# ==============================================================================


func _get_trail_weight(x: float, z: float) -> float:

if not enable_trails: return 0.0

var warp_x = warp_noise.get_noise_2d(x, z) * trail_warp_strength

var warp_z = warp_noise.get_noise_2d(x + 500.0, z + 500.0) * trail_warp_strength

var raw_trail = abs(trail_noise.get_noise_2d(x + warp_x, z + warp_z))

if raw_trail < trail_width:

return smoothstep(trail_width, 0.0, raw_trail)

return 0.0


func _get_height_at(x: float, z: float, trail_weight: float) -> float:

var n_val = noise.get_noise_2d(x, z)

var h = n_val * height_scale


# Aplicação da suavização e profundidade nas bordas

if enable_edge_falloff:

var half_x = terrain_size.x * 0.5

var half_z = terrain_size.y * 0.5

var nx = abs(x) / half_x

var nz = abs(z) / half_z

var dist: float

if edge_falloff_radial:

dist = sqrt(nx * nx + nz * nz)

else:

dist = max(nx, nz)

var start_margin = clamp(1.0 - edge_falloff_margin, 0.0, 0.99)

# falloff varia de 1.0 (centro) até 0.0 (extremidade da borda)

var falloff = 1.0 - smoothstep(start_margin, 1.0, dist)

# Interpola da altura original até a altura alvo da borda

h = lerp(edge_target_height, h, falloff)


return h - (trail_weight * 0.4)


# ==============================================================================

# GERAÇÃO DA MALHA DO TERRENO

# ==============================================================================


func _generate_terrain() -> void:

_init_noises()


var st = SurfaceTool.new()

st.begin(Mesh.PRIMITIVE_TRIANGLES)


var half_x = terrain_size.x / 2.0

var half_z = terrain_size.y / 2.0

var step_x = terrain_size.x / float(subdivision)

var step_z = terrain_size.y / float(subdivision)


for z in range(subdivision):

for x in range(subdivision):

var x0 = -half_x + x * step_x

var z0 = -half_z + z * step_z

var x1 = x0 + step_x

var z1 = z0 + step_z

var tw0 = _get_trail_weight(x0, z0)

var tw1 = _get_trail_weight(x1, z0)

var tw2 = _get_trail_weight(x0, z1)

var tw3 = _get_trail_weight(x1, z1)


var v0 = Vector3(x0, _get_height_at(x0, z0, tw0), z0)

var v1 = Vector3(x1, _get_height_at(x1, z0, tw1), z0)

var v2 = Vector3(x0, _get_height_at(x0, z1, tw2), z1)

var v3 = Vector3(x1, _get_height_at(x1, z1, tw3), z1)


var u0 = float(x) / subdivision

var u1 = float(x + 1) / subdivision

var w0 = float(z) / subdivision

var w1 = float(z + 1) / subdivision


# Triângulo 1

st.set_color(Color(tw0, 0, 0, 1)); st.set_uv(Vector2(u0, w0)); st.add_vertex(v0)

st.set_color(Color(tw1, 0, 0, 1)); st.set_uv(Vector2(u1, w0)); st.add_vertex(v1)

st.set_color(Color(tw2, 0, 0, 1)); st.set_uv(Vector2(u0, w1)); st.add_vertex(v2)


# Triângulo 2

st.set_color(Color(tw1, 0, 0, 1)); st.set_uv(Vector2(u1, w0)); st.add_vertex(v1)

st.set_color(Color(tw3, 0, 0, 1)); st.set_uv(Vector2(u1, w1)); st.add_vertex(v3)

st.set_color(Color(tw2, 0, 0, 1)); st.set_uv(Vector2(u0, w1)); st.add_vertex(v2)


st.generate_normals()

self.mesh = st.commit()

_setup_terrain_shader()

_generate_trees()

_update_collision()


# ==============================================================================

# COLISÃO FÍSICA

# ==============================================================================


func _update_collision() -> void:

static_body = get_node_or_null("TerrainCollider") as StaticBody3D

if not generate_collision or not self.mesh:

if static_body: static_body.queue_free()

return


if not static_body:

static_body = StaticBody3D.new()

static_body.name = "TerrainCollider"

add_child(static_body)

collision_shape = CollisionShape3D.new()

collision_shape.name = "Shape"

static_body.add_child(collision_shape)

else:

collision_shape = static_body.get_node_or_null("Shape") as CollisionShape3D


if collision_shape and self.mesh:

collision_shape.shape = self.mesh.create_trimesh_shape()


# ==============================================================================

# VEGETAÇÃO (MULTIMESH)

# ==============================================================================


func _setup_tree_multimesh() -> void:

tree_multimesh_inst = get_node_or_null("TreeMultiMesh") as MultiMeshInstance3D

if not tree_multimesh_inst:

tree_multimesh_inst = MultiMeshInstance3D.new()

tree_multimesh_inst.name = "TreeMultiMesh"

add_child(tree_multimesh_inst)

if Engine.is_editor_hint() and get_tree():

tree_multimesh_inst.owner = get_tree().edited_scene_root

if not tree_multimesh_inst.multimesh:

var mm = MultiMesh.new()

mm.transform_format = MultiMesh.TRANSFORM_3D

tree_multimesh_inst.multimesh = mm

_update_tree_mesh()


func _update_tree_mesh() -> void:

if not tree_multimesh_inst or not tree_multimesh_inst.multimesh:

return

if tree_mesh:

tree_multimesh_inst.multimesh.mesh = tree_mesh

else:

var default_cyl = CylinderMesh.new()

default_cyl.top_radius = 0.1

default_cyl.bottom_radius = 0.3

default_cyl.height = 1.5

tree_multimesh_inst.multimesh.mesh = default_cyl


func _generate_trees() -> void:

_setup_tree_multimesh()

if not spawn_trees or not tree_multimesh_inst.multimesh:

if tree_multimesh_inst and tree_multimesh_inst.multimesh:

tree_multimesh_inst.multimesh.instance_count = 0

return


var valid_transforms: Array[Transform3D] = []

var step_x = terrain_size.x / float(subdivision)

var step_z = terrain_size.y / float(subdivision)

var rng = RandomNumberGenerator.new()

rng.seed = noise_seed + 999

var start_x = -terrain_size.x / 2.0

var start_z = -terrain_size.y / 2.0

for x in range(subdivision + 1):

for z in range(subdivision + 1):

var px = start_x + (x * step_x) + rng.randf_range(-step_x * 0.3, step_x * 0.3)

var pz = start_z + (z * step_z) + rng.randf_range(-step_z * 0.3, step_z * 0.3)

var tw = _get_trail_weight(px, pz)

var py = _get_height_at(px, pz, tw)

var norm_val = (py / height_scale) * 0.5 + 0.5

# Só instancia se estiver fora do caminho e na altitude da grama

if tw == 0.0 and norm_val >= 0.25 and norm_val <= 0.55:

if rng.randf() <= tree_density:

var t = Transform3D()

t = t.rotated_local(Vector3.UP, rng.randf_range(0, TAU))

var sc = rng.randf_range(0.7, 1.3)

t = t.scaled_local(Vector3(sc, sc, sc))

t.origin = Vector3(px, py, pz)

valid_transforms.append(t)


var mm = tree_multimesh_inst.multimesh

mm.instance_count = valid_transforms.size()

for i in range(valid_transforms.size()):

mm.set_instance_transform(i, valid_transforms[i])


# ==============================================================================

# SHADER DO TERRENO

# ==============================================================================


func _setup_terrain_shader() -> void:

var mat = self.material_override as ShaderMaterial

if not mat:

mat = ShaderMaterial.new()

var shader = Shader.new()

shader.code = """

shader_type spatial;

render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx;


uniform sampler2D tex_sand : source_color, filter_linear_mipmap, hint_default_white;

uniform sampler2D tex_grass : source_color, filter_linear_mipmap, hint_default_white;

uniform sampler2D tex_rock : source_color, filter_linear_mipmap, hint_default_white;

uniform sampler2D tex_snow : source_color, filter_linear_mipmap, hint_default_white;

uniform sampler2D tex_trail : source_color, filter_linear_mipmap, hint_default_white;

uniform float uv_scale = 10.0;

uniform float max_height = 8.0;

uniform bool enable_trails = true;


varying float height_factor;

varying float slope;


void vertex() {

height_factor = (VERTEX.y / max_height) * 0.5 + 0.5;

slope = NORMAL.y;

}


void fragment() {

vec2 base_uv = UV * uv_scale;

vec4 col_sand = texture(tex_sand, base_uv);

vec4 col_grass = texture(tex_grass, base_uv);

vec4 col_rock = texture(tex_rock, base_uv);

vec4 col_snow = texture(tex_snow, base_uv);

vec4 col_trail = texture(tex_trail, base_uv);

vec4 final_color = col_sand;

float blend_grass = smoothstep(0.2, 0.35, height_factor);

final_color = mix(final_color, col_grass, blend_grass);

float blend_snow = smoothstep(0.75, 0.85, height_factor);

final_color = mix(final_color, col_snow, blend_snow);


float rock_blend = 1.0 - smoothstep(0.65, 0.85, slope);

final_color = mix(final_color, col_rock, rock_blend);


if (enable_trails && COLOR.r > 0.0) {

if (height_factor < 0.75 && height_factor > 0.15) {

final_color = mix(final_color, col_trail, COLOR.r);

}

}

ALBEDO = final_color.rgb;

ROUGHNESS = 0.8;

}

"""

mat.shader = shader

self.material_override = mat

_update_shader_parameters()


func _update_shader_parameters() -> void:

var mat = self.material_override as ShaderMaterial

if not mat: return

if texture_sand: mat.set_shader_parameter("tex_sand", texture_sand)

if texture_grass: mat.set_shader_parameter("tex_grass", texture_grass)

if texture_rock: mat.set_shader_parameter("tex_rock", texture_rock)

if texture_snow: mat.set_shader_parameter("tex_snow", texture_snow)

if texture_trail: mat.set_shader_parameter("tex_trail", texture_trail)

mat.set_shader_parameter("uv_scale", texture_scale)

mat.set_shader_parameter("max_height", height_scale)

mat.set_shader_parameter("enable_trails", enable_trails)