feat: update vfx
This commit is contained in:
+138
@@ -1919,6 +1919,144 @@ func sync_grid_item(x: int, y: int, z: int, item: int):
|
||||
if enhanced_gridmap.has_method("update_grid_data"):
|
||||
enhanced_gridmap.update_grid_data()
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func play_freeze_floor_vfx(center_x: int, center_z: int, radius: int, duration: float) -> void:
|
||||
# Spawns a flat freeze-floor AnimatedSprite3D that covers the (2*radius+1)^2
|
||||
# frozen area, centered on the given grid cell and sitting just above the
|
||||
# floor surface. The template under vfxController/AnimatedSprite3D (hidden) is
|
||||
# laid flat and sized for ~1 tile; we duplicate it, then size it to the area
|
||||
# from its measured footprint. Runs on every peer.
|
||||
var vfx_root := get_node_or_null("vfxController")
|
||||
if not vfx_root:
|
||||
return
|
||||
var template := vfx_root.get_node_or_null("AnimatedSprite3D") as AnimatedSprite3D
|
||||
if not template:
|
||||
return
|
||||
|
||||
var enhanced_gridmap = $EnhancedGridMap
|
||||
if not enhanced_gridmap:
|
||||
return
|
||||
var cell: Vector3 = enhanced_gridmap.cell_size
|
||||
|
||||
var vfx := template.duplicate() as AnimatedSprite3D
|
||||
|
||||
# --- Scale to cover the whole area -------------------------------------
|
||||
# The template's world footprint = its local sprite size (texture * pixel_size,
|
||||
# from the AABB) times each basis axis length. Scale each flat axis (X = width,
|
||||
# Y = depth, since the sprite is rotated to lie flat) so it spans the area.
|
||||
# AREA_SIZE_FACTOR oversizes slightly so the visible art (which has transparent
|
||||
# padding inside the frame) actually fills the (2*radius+1)-tile region — tune
|
||||
# this if it reads a touch small or large.
|
||||
const AREA_SIZE_FACTOR := 1.25
|
||||
var aabb := template.get_aabb()
|
||||
var b := template.transform.basis
|
||||
var base_w: float = absf(aabb.size.x) * b.x.length()
|
||||
var base_d: float = absf(aabb.size.y) * b.y.length()
|
||||
var want_w: float = float(2 * radius + 1) * cell.x * AREA_SIZE_FACTOR
|
||||
var want_d: float = float(2 * radius + 1) * cell.z * AREA_SIZE_FACTOR
|
||||
if base_w > 0.0001:
|
||||
b.x = b.x * (want_w / base_w)
|
||||
if base_d > 0.0001:
|
||||
b.y = b.y * (want_d / base_d)
|
||||
|
||||
# --- World center of the freeze area -----------------------------------
|
||||
# X/Z use the same convention players use (grid_to_world): cell center =
|
||||
# index*size + half a cell. Y is taken from the template's own position in the
|
||||
# scene, so the height can be tuned in the editor (move vfxController/
|
||||
# AnimatedSprite3D up/down) without touching code. vfxController/Main sit at
|
||||
# the origin, so this local point is also the world point.
|
||||
var world_center := Vector3(
|
||||
center_x * cell.x + cell.x * 0.5,
|
||||
template.position.y,
|
||||
center_z * cell.z + cell.z * 0.5
|
||||
)
|
||||
|
||||
# Assign basis + origin together in one shot so nothing overwrites the position.
|
||||
vfx.transform = Transform3D(b, world_center)
|
||||
vfx.visible = true
|
||||
vfx_root.add_child(vfx)
|
||||
vfx.play()
|
||||
|
||||
print("[FreezeVFX] center cell=(%d,%d) -> world=%s, scale=(%.2f x %.2f)" % [center_x, center_z, world_center, want_w, want_d])
|
||||
|
||||
await get_tree().create_timer(duration).timeout
|
||||
|
||||
if is_instance_valid(vfx):
|
||||
vfx.queue_free()
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func play_block_floor_vfx(cells: PackedVector2Array, duration: float) -> void:
|
||||
# Spawns one box_block VFX per blocked cell (each covers 1x1), autoplaying its
|
||||
# wall animation. The template under vfxController/box_block (hidden) is sized
|
||||
# for one cell; we duplicate it per cell, keeping its Y/scale from the scene so
|
||||
# height can be tuned in the editor. Runs on every peer.
|
||||
var vfx_root := get_node_or_null("vfxController")
|
||||
if not vfx_root:
|
||||
return
|
||||
var template := vfx_root.get_node_or_null("box_block")
|
||||
if not template:
|
||||
return
|
||||
|
||||
var enhanced_gridmap = $EnhancedGridMap
|
||||
if not enhanced_gridmap:
|
||||
return
|
||||
var cell: Vector3 = enhanced_gridmap.cell_size
|
||||
|
||||
var spawned: Array = []
|
||||
for c in cells:
|
||||
# DUPLICATE_USE_INSTANTIATION so the instanced glb subtree (incl. its
|
||||
# AnimationPlayer) is recreated in the copy rather than dropped.
|
||||
var box := template.duplicate(DUPLICATE_USE_INSTANTIATION)
|
||||
# X/Z = cell center (same convention as players), Y/scale kept from template.
|
||||
var pos := Vector3(
|
||||
int(c.x) * cell.x + cell.x * 0.5,
|
||||
template.position.y,
|
||||
int(c.y) * cell.z + cell.z * 0.5
|
||||
)
|
||||
box.transform = Transform3D(template.transform.basis, pos)
|
||||
box.visible = true
|
||||
vfx_root.add_child(box)
|
||||
_autoplay_vfx_animation(box)
|
||||
spawned.append(box)
|
||||
|
||||
await get_tree().create_timer(duration).timeout
|
||||
|
||||
for box in spawned:
|
||||
if is_instance_valid(box):
|
||||
box.queue_free()
|
||||
|
||||
func _autoplay_vfx_animation(node: Node) -> void:
|
||||
# Find the AnimationPlayer Godot creates for an imported glb/scene and play its
|
||||
# first clip once (one-shot) when the VFX is spawned.
|
||||
var anim_player := node.get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||
if not anim_player:
|
||||
for child in node.get_children():
|
||||
if child is AnimationPlayer:
|
||||
anim_player = child
|
||||
break
|
||||
if not anim_player:
|
||||
return
|
||||
var list := anim_player.get_animation_list()
|
||||
if list.is_empty():
|
||||
return
|
||||
var anim_name: String = list[0]
|
||||
var anim := anim_player.get_animation(anim_name)
|
||||
if anim:
|
||||
anim.loop_mode = Animation.LOOP_NONE
|
||||
anim_player.play(anim_name)
|
||||
|
||||
func play_playerboard_scatter_vfx() -> void:
|
||||
# One-shot "scatter" animation over the local player's board UI. Triggered for
|
||||
# the player whose tiles were scattered (caught outside the safe zone in
|
||||
# Stop n Go). The clip is already non-looping in the SpriteFrames.
|
||||
var vfx := get_node_or_null("PlayerBoardUI/AnimatedSprite2D") as AnimatedSprite2D
|
||||
if not vfx:
|
||||
return
|
||||
vfx.visible = true
|
||||
vfx.play("scatter")
|
||||
await vfx.animation_finished
|
||||
vfx.visible = false
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func sync_grid_items_batch(data: Array):
|
||||
# data is an array of dictionaries: [{x: int, y: int, z: int, item: int}, ...]
|
||||
|
||||
Reference in New Issue
Block a user