feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s
checkpoint / checkpoint (push) Successful in 6m32s
This commit is contained in:
@@ -16,21 +16,136 @@ extends Node3D
|
||||
|
||||
const ScriptBehaviorClass := preload("res://scripts/scripting/script_behavior.gd")
|
||||
const ScriptInterpreterNodeClass := preload("res://scripts/scripting/script_interpreter.gd")
|
||||
const ObjectMotionComponentClass := preload("res://scripts/components/object_motion_component.gd")
|
||||
|
||||
## The world object data this node represents.
|
||||
var world_object: WorldObject = null
|
||||
|
||||
## The loaded mesh instance (or placeholder).
|
||||
var _mesh_instance: MeshInstance3D = null
|
||||
var _anim_player: AnimationPlayer = null
|
||||
var _motion_comp: Node = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if world_object == null:
|
||||
return
|
||||
_load_mesh()
|
||||
_setup_animation_and_motion()
|
||||
_setup_behavior()
|
||||
|
||||
|
||||
func _setup_animation_and_motion() -> void:
|
||||
if world_object == null or not world_object.properties is Dictionary:
|
||||
return
|
||||
|
||||
# 1. Animation playback setup
|
||||
if _anim_player != null:
|
||||
var anim_cfg = world_object.properties.get("animation", {})
|
||||
var clip: String = ""
|
||||
var autoplay: bool = true
|
||||
var loop: bool = true
|
||||
var speed: float = 1.0
|
||||
if anim_cfg is Dictionary and not anim_cfg.is_empty():
|
||||
clip = str(anim_cfg.get("clip", anim_cfg.get("name", "")))
|
||||
autoplay = bool(anim_cfg.get("autoplay", true))
|
||||
loop = bool(anim_cfg.get("loop", true))
|
||||
speed = float(anim_cfg.get("speed", 1.0))
|
||||
elif world_object.properties.has("active_animation"):
|
||||
clip = str(world_object.properties["active_animation"])
|
||||
|
||||
if clip == "" and not _anim_player.get_animation_list().is_empty():
|
||||
clip = _anim_player.get_animation_list()[0]
|
||||
|
||||
if clip != "" and _anim_player.has_animation(clip):
|
||||
var anim := _anim_player.get_animation(clip)
|
||||
if anim != null:
|
||||
anim.loop_mode = Animation.LOOP_LINEAR if loop else Animation.LOOP_NONE
|
||||
_anim_player.speed_scale = speed
|
||||
if autoplay:
|
||||
_anim_player.play(clip)
|
||||
|
||||
# 2. Motion Component setup
|
||||
var motion_cfg = world_object.properties.get("motion", null)
|
||||
if motion_cfg == null and world_object.properties.has("motion_type"):
|
||||
motion_cfg = {
|
||||
"motion_type": world_object.properties.get("motion_type"),
|
||||
"trigger_mode": world_object.properties.get("trigger_mode", "always"),
|
||||
"rotation_speed": world_object.properties.get("rotation_speed", 45.0),
|
||||
"bob_amplitude": world_object.properties.get("bob_amplitude", 0.5),
|
||||
"bob_frequency": world_object.properties.get("bob_frequency", 1.0),
|
||||
"waypoints": world_object.properties.get("waypoints", []),
|
||||
"move_speed": world_object.properties.get("move_speed", 2.0),
|
||||
}
|
||||
|
||||
if motion_cfg is Dictionary and not motion_cfg.is_empty() and str(motion_cfg.get("motion_type", "none")).to_lower() != "none":
|
||||
if _motion_comp == null:
|
||||
_motion_comp = ObjectMotionComponentClass.new()
|
||||
_motion_comp.name = "ObjectMotionComponent"
|
||||
add_child(_motion_comp)
|
||||
_motion_comp.setup(self, motion_cfg)
|
||||
elif _motion_comp != null:
|
||||
_motion_comp.queue_free()
|
||||
_motion_comp = null
|
||||
|
||||
|
||||
func get_animation_player() -> AnimationPlayer:
|
||||
return _anim_player
|
||||
|
||||
|
||||
func get_available_animations() -> Array[String]:
|
||||
if _anim_player != null:
|
||||
var res: Array[String] = []
|
||||
for anim in _anim_player.get_animation_list():
|
||||
res.append(str(anim))
|
||||
return res
|
||||
return []
|
||||
|
||||
|
||||
func play_animation(clip_name: String, speed: float = 1.0, loop: bool = true) -> void:
|
||||
if _anim_player != null and _anim_player.has_animation(clip_name):
|
||||
var anim := _anim_player.get_animation(clip_name)
|
||||
if anim != null:
|
||||
anim.loop_mode = Animation.LOOP_LINEAR if loop else Animation.LOOP_NONE
|
||||
_anim_player.speed_scale = speed
|
||||
_anim_player.play(clip_name)
|
||||
|
||||
|
||||
func stop_animation() -> void:
|
||||
if _anim_player != null:
|
||||
_anim_player.stop()
|
||||
|
||||
|
||||
func get_motion_component() -> Node:
|
||||
return _motion_comp
|
||||
|
||||
|
||||
func update_animation(anim_config: Dictionary) -> void:
|
||||
if world_object != null:
|
||||
world_object.properties["animation"] = anim_config
|
||||
_setup_animation_and_motion()
|
||||
|
||||
|
||||
func update_motion(motion_config: Dictionary) -> void:
|
||||
if world_object != null:
|
||||
world_object.properties["motion"] = motion_config
|
||||
_setup_animation_and_motion()
|
||||
|
||||
|
||||
func trigger_motion(action: String = "toggle") -> void:
|
||||
if _motion_comp == null:
|
||||
return
|
||||
match action:
|
||||
"start":
|
||||
_motion_comp.start_motion()
|
||||
"stop":
|
||||
_motion_comp.stop_motion()
|
||||
"toggle":
|
||||
_motion_comp.toggle_motion()
|
||||
"interact":
|
||||
_motion_comp.on_interact()
|
||||
|
||||
|
||||
func _setup_behavior() -> void:
|
||||
if world_object == null or not world_object.properties is Dictionary:
|
||||
return
|
||||
@@ -63,11 +178,42 @@ func _load_mesh() -> void:
|
||||
_mesh_instance = asset_node as MeshInstance3D
|
||||
else:
|
||||
_mesh_instance = asset_node.find_child("*", true, false) as MeshInstance3D
|
||||
return
|
||||
else:
|
||||
# 2. Build a stylized, recognizable 3D representation based on catalog ID / asset ID
|
||||
var cat_id := _get_catalog_id()
|
||||
_build_procedural_object(cat_id)
|
||||
|
||||
# 2. Build a stylized, recognizable 3D representation based on catalog ID / asset ID
|
||||
var cat_id := _get_catalog_id()
|
||||
_build_procedural_object(cat_id)
|
||||
# Scan for AnimationPlayer
|
||||
_anim_player = find_child("*", true, false) as AnimationPlayer
|
||||
if _anim_player == null:
|
||||
var players = find_children("*", "AnimationPlayer", true, false)
|
||||
if not players.is_empty():
|
||||
_anim_player = players[0] as AnimationPlayer
|
||||
if _anim_player != null and world_object != null and world_object.properties is Dictionary:
|
||||
world_object.properties["available_animations"] = Array(_anim_player.get_animation_list())
|
||||
|
||||
# Ensure collision body exists for physics / platform ride
|
||||
_ensure_collision()
|
||||
|
||||
|
||||
func _ensure_collision() -> void:
|
||||
var sb := find_child("collision", true, false) as StaticBody3D
|
||||
if sb == null:
|
||||
sb = find_child("*", true, false) as StaticBody3D
|
||||
if sb == null:
|
||||
sb = _create_static_collision()
|
||||
var col_shape := CollisionShape3D.new()
|
||||
var box := BoxShape3D.new()
|
||||
if _mesh_instance != null and _mesh_instance.mesh != null:
|
||||
var aabb := _mesh_instance.mesh.get_aabb()
|
||||
box.size = Vector3(maxf(0.2, aabb.size.x), maxf(0.2, aabb.size.y), maxf(0.2, aabb.size.z))
|
||||
col_shape.position = aabb.get_center()
|
||||
else:
|
||||
box.size = Vector3(1.0, 1.0, 1.0)
|
||||
col_shape.position = Vector3(0, 0.5, 0)
|
||||
col_shape.shape = box
|
||||
sb.add_child(col_shape)
|
||||
add_child(sb)
|
||||
|
||||
|
||||
func _get_catalog_id() -> String:
|
||||
|
||||
Reference in New Issue
Block a user