Files
my-world/World/birds.gd
T
aibrashandClaude Sonnet 5.5 d759cf6a54 Лес на MultiMesh, киберпанк-птицы у башни, левитация обелиска
- Деревья: упрощённая модель, MultiMesh, коллизия стволов
- Птицы кружат вокруг Tower_01 (шейдерные крылья, свечение)
- Обелиск увеличен, поднят и плавно левитирует

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 09:29:15 +05:00

117 lines
4.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@tool
extends Node3D
# Киберпанк-птицы кружат вокруг цели. Один MultiMesh, взмахи крыльев считает шейдер.
@export var target_path: NodePath
var target: Node3D
@export var count := 12
@export var min_radius := 55.0
@export var max_radius := 100.0
@export var min_height := 15.0
@export var max_height := 150.0
@export var speed_range := Vector2(0.15, 0.35) # рад/с вокруг цели
@export var bird_scale := 1.6
@export var seed_value := 7
const SHADER := """
shader_type spatial;
render_mode cull_disabled, unshaded, fog_disabled;
uniform float flap_speed = 9.0;
uniform float flap_amp = 0.9;
varying vec3 col;
void vertex() {
float ph = INSTANCE_CUSTOM.x;
// COLOR.r — вес крыла (0 у тела, 1 на кончике)
VERTEX.y += sin(TIME * flap_speed * (0.8 + INSTANCE_CUSTOM.y * 0.4) + ph) * flap_amp * COLOR.r;
VERTEX.z += COLOR.r * 0.25 * cos(TIME * flap_speed + ph);
col = INSTANCE_CUSTOM.z > 0.5 ? vec3(1.0, 0.1, 0.8) : vec3(0.0, 0.95, 1.0);
}
void fragment() {
// Светлее к кончикам крыльев — HDR для свечения
ALBEDO = col * (5.0 + 3.0 * COLOR.r);
}
"""
var _mm: MultiMesh
var _orbit: Array[Dictionary] = []
func _ready() -> void:
target = get_node_or_null(target_path) as Node3D
_build()
func _build() -> void:
var rng := RandomNumberGenerator.new()
rng.seed = seed_value
var mat := ShaderMaterial.new()
var sh := Shader.new()
sh.code = SHADER
mat.shader = sh
_mm = MultiMesh.new()
_mm.transform_format = MultiMesh.TRANSFORM_3D
_mm.use_custom_data = true
_mm.mesh = _make_mesh()
_mm.mesh.surface_set_material(0, mat)
_mm.instance_count = count
for i in count:
_orbit.append({
"r": rng.randf_range(min_radius, max_radius),
"h": rng.randf_range(min_height, max_height),
"w": rng.randf_range(speed_range.x, speed_range.y) * (1.0 if rng.randf() > 0.3 else -1.0),
"a": rng.randf() * TAU,
"bob": rng.randf() * TAU,
})
_mm.set_instance_custom_data(i, Color(rng.randf() * TAU, rng.randf(), 1.0 if rng.randf() < 0.3 else 0.0, 0.0))
var mmi := MultiMeshInstance3D.new()
mmi.name = "Flock"
mmi.multimesh = _mm
mmi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
mmi.custom_aabb = AABB(Vector3(-400, -50, -400), Vector3(800, 400, 800))
mmi.top_level = true # координаты глобальные
add_child(mmi)
_update(0.0)
# Угловатая птица: тело-ромб, хвост и два крыла-«клинка» (вес крыла в цвете вершины)
func _make_mesh() -> ArrayMesh:
var st := SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
var tris := [
# [вершина, вес крыла]
# тело (носом в -Z)
[Vector3(0, 0, -1.2), 0.0], [Vector3(-0.25, 0, 0.3), 0.0], [Vector3(0.25, 0, 0.3), 0.0],
[Vector3(0, 0.2, 0.0), 0.0], [Vector3(-0.25, 0, 0.3), 0.0], [Vector3(0, 0, -1.2), 0.0],
[Vector3(0, 0.2, 0.0), 0.0], [Vector3(0, 0, -1.2), 0.0], [Vector3(0.25, 0, 0.3), 0.0],
# хвост
[Vector3(-0.2, 0, 0.3), 0.0], [Vector3(0, 0, 1.4), 0.0], [Vector3(0.2, 0, 0.3), 0.0],
# левое крыло
[Vector3(-0.2, 0, -0.5), 0.0], [Vector3(-2.6, 0, 0.5), 1.0], [Vector3(-0.2, 0, 0.4), 0.0],
# правое крыло
[Vector3(0.2, 0, -0.5), 0.0], [Vector3(0.2, 0, 0.4), 0.0], [Vector3(2.6, 0, 0.5), 1.0],
]
for t in tris:
st.set_color(Color(t[1], 0, 0))
st.add_vertex(t[0])
return st.commit()
func _process(delta: float) -> void:
_update(delta)
func _update(delta: float) -> void:
if _mm == null or target == null:
return
var c := target.global_position
var t := Time.get_ticks_msec() * 0.001
for i in _orbit.size():
var o: Dictionary = _orbit[i]
o["a"] += o["w"] * delta
var a: float = o["a"]
var pos := c + Vector3(cos(a) * o["r"], o["h"] + sin(t * 0.7 + o["bob"]) * 3.0, sin(a) * o["r"])
# Летим по касательной, лёгкий крен в сторону поворота
var dir := Vector3(-sin(a), 0, cos(a)) * signf(o["w"])
var b := Basis.looking_at(dir, Vector3.UP)
b = b.rotated(dir, -0.35 * signf(o["w"])).scaled(Vector3.ONE * bird_scale)
_mm.set_instance_transform(i, Transform3D(b, pos))