添加了基础的关卡编辑器的功能
This commit is contained in:
parent
80bf92faf6
commit
e3eeded054
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,7 @@
|
|||
extends AnimatableBody2D
|
||||
|
||||
func door_open() -> void:
|
||||
pass
|
||||
|
||||
func door_close() -> void:
|
||||
pass
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://byntg0bbp8nar
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://5ia817pm76x1"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://byntg0bbp8nar" path="res://_props/_prefabs/door.gd" id="1_gt1uq"]
|
||||
[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="2_f05xd"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_g1m3x"]
|
||||
size = Vector2(20, 128)
|
||||
|
||||
[node name="Door" type="AnimatableBody2D"]
|
||||
script = ExtResource("1_gt1uq")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.16, 1)
|
||||
texture = ExtResource("2_f05xd")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("RectangleShape2D_g1m3x")
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://bonrls3iuhdqb"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c25ea2nypjah7" path="res://_props/tools/player_trigger_volumn.gd" id="1_qrafk"]
|
||||
[ext_resource type="Script" uid="uid://c25ea2nypjah7" path="res://_props/_prefabs/player_trigger_volumn.gd" id="1_qrafk"]
|
||||
|
||||
[node name="PlayerTriggerVolumn" type="Area2D"]
|
||||
editor_description = "此类在检测到玩家时会发出signal,可以与其他的需要检测玩家的scene组合使用。
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cmjwk4gr1nfns"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://hme2aiy2gff7" path="res://_props/tools/rock.gd" id="1_60pcp"]
|
||||
[ext_resource type="Script" uid="uid://hme2aiy2gff7" path="res://_props/_prefabs/rock.gd" id="1_60pcp"]
|
||||
|
||||
[node name="rock" type="CharacterBody2D"]
|
||||
collision_layer = 4
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
extends Node2D
|
||||
|
||||
##标记位
|
||||
@onready var door_open_poser: Marker2D = %DoorOpenPoser
|
||||
@onready var door_close_poser: Marker2D = %DoorClosePoser
|
||||
@onready var door: AnimatableBody2D = %Door
|
||||
|
||||
var _move_tween: Tween # 用来管理当前 tween
|
||||
|
||||
##门的状态,默认为0(关闭),可以切换为1(开启)
|
||||
var door_state_id: int = 0
|
||||
##门移动动画结束广播,输出0为关闭,输出1为开启
|
||||
signal door_move_finished(int)
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().create_timer(.2).timeout
|
||||
$PropComponent.change_state(1,true)
|
||||
|
||||
##开门
|
||||
func door_open() -> void:
|
||||
door_move(door_open_poser)
|
||||
|
||||
##关门
|
||||
func door_close() -> void:
|
||||
door_move(door_close_poser)
|
||||
|
||||
##门的移动
|
||||
func door_move(poser: Node2D, duration := 0.4) -> void:
|
||||
if not poser or not door:
|
||||
return
|
||||
|
||||
# 如果上一次 tween 还在,先杀掉(非常重要)
|
||||
if _move_tween and _move_tween.is_running():
|
||||
_move_tween.kill()
|
||||
|
||||
var target_pos: Vector2 = poser.global_position
|
||||
|
||||
_move_tween = create_tween()
|
||||
_move_tween.set_trans(Tween.TRANS_SINE)
|
||||
_move_tween.set_ease(Tween.EASE_IN_OUT)
|
||||
|
||||
_move_tween.tween_property(
|
||||
door,
|
||||
"global_position",
|
||||
target_pos,
|
||||
duration
|
||||
)
|
||||
|
||||
##移动结束的返回信号
|
||||
_move_tween.finished.connect(
|
||||
Callable(self, "_on_door_move_finished").bind(poser)
|
||||
)
|
||||
|
||||
##直接重置door的位置到某个poser
|
||||
func reset_door(poser: Node2D)-> void:
|
||||
if not is_node_ready():
|
||||
await ready
|
||||
|
||||
if not poser: return
|
||||
door.global_position = poser.global_position
|
||||
|
||||
##直接重置door的位置到某个poser的id,id可以见上面
|
||||
func reset_door_state_id(id:int = 0)-> void:
|
||||
if not is_node_ready():
|
||||
await ready
|
||||
|
||||
if id != 0 and id != 1: return
|
||||
if door_state_id == id: return
|
||||
|
||||
var n = door_open_poser if id == 1 else door_close_poser
|
||||
door_state_id = id
|
||||
reset_door(n)
|
||||
|
||||
##门开关结束的广播
|
||||
func _on_door_move_finished(poser: Node2D) -> void:
|
||||
if poser == door_open_poser:
|
||||
door_move_finished.emit(1)
|
||||
|
||||
if poser == door_close_poser:
|
||||
door_move_finished.emit(0)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cup8c7j4e6xpf
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://b5nx4dntm0gyn"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://5ia817pm76x1" path="res://_props/_prefabs/door.tscn" id="1_3ghu3"]
|
||||
[ext_resource type="Script" uid="uid://cup8c7j4e6xpf" path="res://_props/door_manager/event_trigger_door.gd" id="1_iprsb"]
|
||||
|
||||
[node name="EventTriggerDoor" type="Node2D"]
|
||||
script = ExtResource("1_iprsb")
|
||||
|
||||
[node name="Door" parent="." instance=ExtResource("1_3ghu3")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="DoorClosePoser" type="Marker2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="DoorOpenPoser" type="Marker2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
position = Vector2(0, -90)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dm28i1f2r18b8
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
extends ReedTransition
|
||||
|
||||
func execute(from: ReedPropState, to: ReedPropState, ctx: Dictionary) -> bool:
|
||||
super.execute(from,to,ctx)
|
||||
to._owner.call
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://csbvukpwc7g6p
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://knrcnoedxvm6"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bonrls3iuhdqb" path="res://_props/tools/player_trigger_volumn.tscn" id="1_mvp6g"]
|
||||
[ext_resource type="PackedScene" uid="uid://cmjwk4gr1nfns" path="res://_props/tools/rock.tscn" id="1_nh18e"]
|
||||
[ext_resource type="PackedScene" uid="uid://bonrls3iuhdqb" path="res://_props/_prefabs/player_trigger_volumn.tscn" id="1_mvp6g"]
|
||||
[ext_resource type="PackedScene" uid="uid://cmjwk4gr1nfns" path="res://_props/_prefabs/rock.tscn" id="1_nh18e"]
|
||||
[ext_resource type="Script" uid="uid://b8yl6l3tlam86" path="res://_props/trigger_fall_rock/trigger_fall_rock.gd" id="1_vv0hj"]
|
||||
[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="2_xilvp"]
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
class_name ActManager extends Node
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dsgl7lbyjsiif
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
'''此类为Prop的管理组件类,任何的Prop,其至少需要在此类的最上层的子层添加该组件
|
||||
该管理器,在添加入Tree时,会自动的添加一个States节点作为其所有state的root,
|
||||
并且添加一个默认的state作为其子节点。
|
||||
'''
|
||||
|
||||
@tool
|
||||
@icon("res://addons/reedscene/prop/icon/prop_icon.svg")
|
||||
class_name PropComponent extends Node
|
||||
|
||||
## ==============================
|
||||
## Export
|
||||
## ==============================
|
||||
|
||||
##此物件的描述ID
|
||||
@export var prop_id: int
|
||||
|
||||
##初始的默认state_id
|
||||
@export var initial_state_id: int = 0
|
||||
|
||||
##是否需要输出错误
|
||||
@export var debug_log := false
|
||||
|
||||
##是否等待
|
||||
@export var should_wait_owner_ready :bool = true
|
||||
|
||||
##状态切换的信号
|
||||
signal state_changed(from_state: int, to_state: int, ctx: Dictionary)
|
||||
|
||||
##state的根节点,所有的state都需要连在根节点上
|
||||
var _states_root: Node
|
||||
var _state_map: Dictionary = {}
|
||||
var _current: ReedPropState = null
|
||||
|
||||
const states_root_name := "States"
|
||||
const DEFAULT_STATE_ID := 0
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
_editor_ensure_states_root()
|
||||
|
||||
func _ready() -> void:
|
||||
_build_state_cache()
|
||||
|
||||
if not Engine.is_editor_hint():
|
||||
if should_wait_owner_ready: await owner.ready
|
||||
|
||||
_init_states()
|
||||
if _current == null and initial_state_id >= 0:
|
||||
change_state(initial_state_id, false, {
|
||||
"reason": "INIT",
|
||||
"instant": true
|
||||
})
|
||||
|
||||
##映射State到stateID
|
||||
func _build_state_cache() -> void:
|
||||
_state_map.clear()
|
||||
|
||||
_states_root = get_node_or_null(states_root_name)
|
||||
if _states_root == null:
|
||||
push_warning("[PropComponent:%s] States root not found" % prop_id)
|
||||
return
|
||||
|
||||
for c in _states_root.get_children():
|
||||
if c is ReedPropState:
|
||||
var s := c as ReedPropState
|
||||
if _state_map.has(s.state_id):
|
||||
push_error("[PropComponent:%s] Duplicate state_id: %d" % [prop_id, s.state_id])
|
||||
continue
|
||||
_state_map[s.state_id] = s
|
||||
|
||||
if debug_log:
|
||||
print("[PropComponent:%s] States:", _state_map.keys())
|
||||
|
||||
##检查当前是否存在当前Id的State
|
||||
func has_state(state_id: int) -> bool:
|
||||
return _state_map.has(state_id)
|
||||
|
||||
##获取当前的状态ID
|
||||
func get_current_state_id() -> int:
|
||||
return _current.state_id if _current else -1
|
||||
|
||||
##初始化状态
|
||||
func _init_states() -> void:
|
||||
var owner := get_parent()
|
||||
if owner == null:
|
||||
push_error("[PropComponent:%s] Owner node not found." % prop_id)
|
||||
return
|
||||
|
||||
for s in _state_map.values():
|
||||
var ok :bool = s.init(owner, self)
|
||||
if not ok:
|
||||
push_error("[PropComponent:%s] State init failed: %s"
|
||||
% [prop_id, s.name])
|
||||
|
||||
## 切换状态,如果 use_trans 为 true,则会优先检查 next state 下是否存在可用的 Transition
|
||||
func change_state(state_id: int, use_trans: bool, ctx: Dictionary = {}) -> bool:
|
||||
if not _state_map.has(state_id):
|
||||
push_warning("[PropComponent:%s] State not found: %d" % [prop_id, state_id])
|
||||
return false
|
||||
|
||||
var next: ReedPropState = _state_map[state_id]
|
||||
|
||||
if _current == next:
|
||||
return true
|
||||
|
||||
if not next.can_enter(_current, ctx):
|
||||
if debug_log:
|
||||
print("[PropComponent:%s] can_enter rejected: %d" % [prop_id, state_id])
|
||||
return false
|
||||
|
||||
var from_state_id := get_current_state_id()
|
||||
var prev := _current
|
||||
|
||||
# ---------- EXIT ----------
|
||||
if _current:
|
||||
_current.on_exit(next, ctx)
|
||||
|
||||
# ---------- SWITCH CURRENT ----------
|
||||
_current = next
|
||||
|
||||
# ---------- TRANSITION CHECK ----------
|
||||
var transition_handled := false
|
||||
|
||||
if use_trans:
|
||||
for child in _current.get_children():
|
||||
if child is ReedTransition:
|
||||
if child.can_trigger(prev, ctx):
|
||||
if debug_log:
|
||||
print("[PropComponent:%s] Transition triggered: %s"
|
||||
% [prop_id, child.name])
|
||||
|
||||
transition_handled = child.execute(prev, _current, ctx)
|
||||
if transition_handled:
|
||||
break
|
||||
|
||||
# ---------- ENTER ----------
|
||||
if not transition_handled:
|
||||
_current.on_enter(prev, ctx)
|
||||
else:
|
||||
if debug_log:
|
||||
print("[PropComponent:%s] StateEnter skipped by Transition: %d"
|
||||
% [prop_id, state_id])
|
||||
|
||||
if debug_log:
|
||||
print("[PropComponent:%s] State %d -> %d | ctx=%s"
|
||||
% [prop_id, from_state_id, state_id, ctx])
|
||||
|
||||
return true
|
||||
|
||||
## ==============================
|
||||
## Act Sync Entry
|
||||
## ==============================
|
||||
func sync_to_state(state_id: int, act_id: int, instant := false) -> bool:
|
||||
var ctx := {
|
||||
"reason": "ACT_SYNC",
|
||||
"act_id": act_id,
|
||||
"instant": instant
|
||||
}
|
||||
return change_state(state_id, false ,ctx)
|
||||
|
||||
## ==============================
|
||||
## Editor Tool
|
||||
## ==============================
|
||||
func _editor_ensure_states_root() -> void:
|
||||
var states := get_node_or_null(states_root_name)
|
||||
if states != null:
|
||||
return
|
||||
|
||||
states = Node.new()
|
||||
states.name = states_root_name
|
||||
add_child(states)
|
||||
|
||||
# ⚠️ 关键:让它成为 Scene 的一部分
|
||||
states.owner = get_tree().edited_scene_root
|
||||
|
||||
if debug_log:
|
||||
print("[PropComponent] Created States root")
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://b4menkyub4ce7
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
@tool
|
||||
@icon("res://addons/reedscene/prop/icon/prop_state_icon.svg")
|
||||
class_name ReedPropState extends Node
|
||||
|
||||
##状态编号,默认为-1,-1是不可用编号,请修改
|
||||
@export var state_id: int = -1
|
||||
##该State的信息是否需要被存入Save中
|
||||
@export var save_game: bool = false
|
||||
|
||||
##预装的一些Effect,可以用来对State进行一些初始化
|
||||
@export var effects: Array[ReedPropEffect] = []
|
||||
|
||||
##State的拥有者
|
||||
var _owner
|
||||
##State的prop comp
|
||||
var _owner_prop_comp
|
||||
|
||||
## 是否已经初始化
|
||||
var _inited: bool = false
|
||||
|
||||
##初始化State
|
||||
func init(owner: Node, prop_comp: Node) -> bool:
|
||||
# 防止重复初始化
|
||||
if _inited:
|
||||
push_warning("ReedPropState [%s] already initialized." % name)
|
||||
return false
|
||||
|
||||
if owner == null:
|
||||
push_error("ReedPropState [%s] init failed: owner is null." % name)
|
||||
return false
|
||||
|
||||
if prop_comp == null:
|
||||
push_error("ReedPropState [%s] init failed: prop_comp is null." % name)
|
||||
return false
|
||||
|
||||
_owner = owner
|
||||
_owner_prop_comp = prop_comp
|
||||
_inited = true
|
||||
return true
|
||||
|
||||
##是否可以进入此状态,默认为真
|
||||
func can_enter(_current: ReedPropState, _ctx: Dictionary) -> bool:
|
||||
return true
|
||||
|
||||
##State进入
|
||||
func on_enter(_from: ReedPropState, _ctx: Dictionary) -> void:
|
||||
if not _inited:
|
||||
push_error("ReedPropState [%s] entered before init!" % name)
|
||||
return
|
||||
|
||||
for e in effects:
|
||||
e.apply(_owner, _owner_prop_comp, _ctx)
|
||||
|
||||
##State退出
|
||||
func on_exit(_to: ReedPropState, _ctx: Dictionary) -> void:
|
||||
pass
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://7lml6d1t5xtq
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
''' 此Resource定义了一系列的Prop可能需要的预设的简单Effect,如果有更复杂的需求,应该重写State的OnEnter
|
||||
'''
|
||||
@tool
|
||||
@icon("uid://cw1yyc4oeph85")
|
||||
extends Resource
|
||||
class_name ReedPropEffect
|
||||
|
||||
enum TargetType {
|
||||
OWNER,
|
||||
NODE_PATH
|
||||
}
|
||||
|
||||
enum EffectType {
|
||||
SET_VALUE,
|
||||
CALL_FUNC
|
||||
}
|
||||
|
||||
##作用目标的类型,默认为PropComp的拥有者
|
||||
@export var target_type: TargetType = TargetType.OWNER
|
||||
##如果选择了NodePath,则会读取此字段,默认不读取
|
||||
@export var target_path: NodePath
|
||||
##作用的具体效果类别,默认为设置某个数值
|
||||
@export var effect_type: EffectType = EffectType.SET_VALUE
|
||||
|
||||
## SetValue 用
|
||||
@export var property_name: StringName
|
||||
@export var value: Variant
|
||||
|
||||
## CallFunc 用
|
||||
@export var func_name: StringName
|
||||
@export var func_args: Array[Variant] = []
|
||||
|
||||
## 是否在退出 state 时恢复
|
||||
@export var restore_on_exit: bool = false
|
||||
|
||||
## 运行时缓存
|
||||
var _cached_old_value: Variant
|
||||
|
||||
##应用效果
|
||||
func apply(owner: Node, prop_comp: Node, ctx: Dictionary) -> void:
|
||||
var target := _resolve_target(owner, prop_comp)
|
||||
if target == null:
|
||||
return
|
||||
|
||||
match effect_type:
|
||||
EffectType.SET_VALUE:
|
||||
# 你也可以加 has_property 检查,但 Godot 没有统一 API,这里先直接 set
|
||||
target.set(property_name, value)
|
||||
|
||||
EffectType.CALL_FUNC:
|
||||
if target.has_method(func_name):
|
||||
target.callv(func_name, func_args)
|
||||
|
||||
##获取到组件或对象
|
||||
func _resolve_target(owner: Node, prop_comp: Node) -> Object:
|
||||
match target_type:
|
||||
TargetType.OWNER:
|
||||
return owner
|
||||
TargetType.NODE_PATH:
|
||||
if owner:
|
||||
return owner.get_node_or_null(target_path)
|
||||
return null
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cdvgq0xqdbagk
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
@tool
|
||||
@icon("res://addons/reedscene/prop/icon/prop_trans_icon.svg")
|
||||
|
||||
extends Node
|
||||
class_name ReedTransition
|
||||
|
||||
@export var from_state_id: int = -1
|
||||
@export var effects: Array[ReedPropEffect] = []
|
||||
|
||||
## runtime only
|
||||
@export var runtime_only: bool = true
|
||||
|
||||
##Transtion能否被触发
|
||||
func can_trigger(from: ReedPropState, ctx: Dictionary) -> bool:
|
||||
if from == null:
|
||||
return false
|
||||
|
||||
if from_state_id != -1 and from.state_id != from_state_id:
|
||||
return false
|
||||
|
||||
if runtime_only and ctx.get("is_load", false):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
##执行Transition的逻辑
|
||||
func execute(from: ReedPropState, to: ReedPropState, ctx: Dictionary) -> bool:
|
||||
# Effect 不需要知道 from/to,但 Transition 需要用 to 拿目标对象
|
||||
var owner : Node= to._owner
|
||||
var prop_comp : Node= to._owner_prop_comp
|
||||
|
||||
for e in effects:
|
||||
e.apply(owner, prop_comp, ctx)
|
||||
|
||||
return true
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://jeybblac0kg2
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767081680409" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5027" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M430.08 922.624H102.4V152.576h327.68V51.2H512v972.8H430.08v-101.376z m409.6-80.9984V233.6256h-245.76V152.4736H921.6v770.1504h-327.68v-81.1008h245.76z" fill="#d4237a" p-id="5028"></path></svg>
|
||||
|
After Width: | Height: | Size: 524 B |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cw1yyc4oeph85"
|
||||
path="res://.godot/imported/prop_effect_icon.svg-8b989d7af0a50d984a55fda3d5d49014.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/reedscene/prop/icon/prop_effect_icon.svg"
|
||||
dest_files=["res://.godot/imported/prop_effect_icon.svg-8b989d7af0a50d984a55fda3d5d49014.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767068468929" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12087" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M934.39999999 287.28888856c-4.266667-12.80000001-12.80000001-21.33333302-25.59999999-25.6l-366.93333299-196.266666c-17.06666702-8.533333-38.4-8.533333-55.466667 0L115.2 261.68888856C98.133333 270.22222256 85.33333301 291.55555554 85.33333301 312.88888856v383.99999999c0 21.33333302 12.80000001 42.666667 29.86666699 51.20000001l366.93333299 196.26666703c12.80000001 4.266667 25.60000001 8.533333 38.40000001 4.26666699 4.266667 0 12.80000001-4.266667 17.06666699-4.26666699l366.93333302-196.26666703c21.33333302-8.533333 34.13333299-29.86666701 34.13333402-51.20000001V312.88888856c0-8.533333-4.266667-17.06666702-4.26666702-25.6z m-413.86666699 217.60000001V867.55555557l-8.533333 8.53333299-349.866667-187.73333301V321.42222255L512 133.68888855l349.866667 187.733334-341.333334 183.46666602z" p-id="12088" fill="#d4237a"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dsxo2ukbb0g45"
|
||||
path="res://.godot/imported/prop_icon.svg-4813df6b5b1ef2c0b427ea393d13fa6f.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/reedscene/prop/icon/prop_icon.svg"
|
||||
dest_files=["res://.godot/imported/prop_icon.svg-4813df6b5b1ef2c0b427ea393d13fa6f.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767068679688" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22120" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M493.511 213.902l333.284 325.803c18.403 18.005 18.602 46.933 0.483 64.74L498.631 925.581a47.388 47.388 0 0 1-66.275-0.455L99.27 599.381a45.284 45.284 0 0 1-9.557-52.11l-1.963-1.991h2.958a45.75 45.75 0 0 1 8.079-10.61L416.37 224.142l-88.86-86.926a29.98 29.98 0 0 1-0.342-43.065 31.516 31.516 0 0 1 44.117 0.313l122.226 119.438z m78.393 162.361L460.8 267.663 176.555 545.336h568.433L571.876 376.292v-0.057z m286.464 501.874c-44.174 0.114-80.071-34.73-80.327-77.881 0-28.672 26.766-77.568 80.27-146.83 53.561 69.262 80.356 118.158 80.356 146.83 0 43.008-35.983 77.88-80.299 77.88z" p-id="22121" fill="#d4237a"></path></svg>
|
||||
|
After Width: | Height: | Size: 954 B |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dursmjgc7ug8f"
|
||||
path="res://.godot/imported/prop_state_icon.svg-c74620cdc425f868535286f7ce62d08d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/reedscene/prop/icon/prop_state_icon.svg"
|
||||
dest_files=["res://.godot/imported/prop_state_icon.svg-c74620cdc425f868535286f7ce62d08d.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767081802741" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10458" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M303.307286 9.731785L194.095037 118.944034c-11.894403 11.894403-11.894403 32.439282 0 45.414995L541.195354 511.459345 194.095037 859.640971c-11.894403 11.894403-11.894403 32.439282 0 45.414995L303.307286 1014.268215c11.894403 11.894403 32.439282 11.894403 45.414995 0l366.563886-366.563886c1.081309-1.081309 3.243928-2.162619 4.325237-3.243928l110.293559-110.293558c6.487856-6.487856 9.731785-14.057022 9.731785-22.707498s-3.243928-16.219641-9.731785-22.707497L719.611404 379.539599c-1.081309-1.081309-2.162619-2.162619-4.325237-3.243928L348.722281 9.731785C335.746568-3.243928 316.282999-3.243928 303.307286 9.731785z" p-id="10459" fill="#d4237a"></path></svg>
|
||||
|
After Width: | Height: | Size: 995 B |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c2hji2jfaditq"
|
||||
path="res://.godot/imported/prop_trans_icon.svg-427c5d6c6dfbea98cf99a23374b74151.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/reedscene/prop/icon/prop_trans_icon.svg"
|
||||
dest_files=["res://.godot/imported/prop_trans_icon.svg-427c5d6c6dfbea98cf99a23374b74151.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
|
|
@ -1,22 +1,2 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
func _enable_plugin() -> void:
|
||||
# Add autoloads here.
|
||||
pass
|
||||
|
||||
|
||||
func _disable_plugin() -> void:
|
||||
# Remove autoloads here.
|
||||
pass
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
# Initialization of the plugin goes here.
|
||||
pass
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
# Clean-up of the plugin goes here.
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -25,4 +25,127 @@
|
|||
对于Scene,Scene通过PropComponent上的ID,通过ID获取到PropComponent所挂载的组件,这也是为什么我强制要求
|
||||
PropComponent必须是处于Prop的最上层子节点集。
|
||||
'''
|
||||
|
||||
class_name ReedScene extends Node2D
|
||||
|
||||
## ==============================
|
||||
## Export Config
|
||||
## ==============================
|
||||
@export var props_root_path: NodePath = ^"Props"
|
||||
@export var auto_attach_prop_component := true
|
||||
@export var debug_log := true
|
||||
|
||||
## ==============================
|
||||
## Internal State
|
||||
## ==============================
|
||||
|
||||
var _act_manager: ActManager
|
||||
var _props_root: Node
|
||||
var _prop_map: Dictionary = {} # prop_id -> PropComponent
|
||||
|
||||
## ==============================
|
||||
## Lifecycle
|
||||
## ==============================
|
||||
func _enter_tree() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
_editor_auto_attach_prop_components()
|
||||
|
||||
## ==============================
|
||||
## Resolve References
|
||||
## ==============================
|
||||
func _resolve_act_manager() -> void:
|
||||
_act_manager = get_node_or_null("ActManager")
|
||||
if _act_manager == null:
|
||||
push_error("[ReedScene] ActManager not found.")
|
||||
return
|
||||
|
||||
func _resolve_props_root() -> void:
|
||||
_props_root = get_node_or_null(props_root_path)
|
||||
if _props_root == null:
|
||||
push_error("[ReedScene] Props root not found: %s" % props_root_path)
|
||||
|
||||
## ==============================
|
||||
## Prop Collection
|
||||
## ==============================
|
||||
|
||||
func _collect_props() -> void:
|
||||
_prop_map.clear()
|
||||
|
||||
if _props_root == null:
|
||||
return
|
||||
|
||||
for prop in _props_root.get_children():
|
||||
if not prop is Node:
|
||||
continue
|
||||
|
||||
var prop_comp := _find_prop_component(prop)
|
||||
if prop_comp == null:
|
||||
continue
|
||||
|
||||
var prop_id := prop_comp.prop_id
|
||||
if prop_id < 0:
|
||||
push_warning("[ReedScene] Prop has invaild ID: %s" % prop.name)
|
||||
continue
|
||||
|
||||
if _prop_map.has(prop_id):
|
||||
push_error("[ReedScene] Duplicate Prop ID: %s" % prop_id)
|
||||
continue
|
||||
|
||||
_prop_map[prop_id] = prop_comp
|
||||
|
||||
if debug_log:
|
||||
print("[ReedScene] Registered Prop:", prop_id)
|
||||
|
||||
func _find_prop_component(prop: Node) -> PropComponent:
|
||||
for child in prop.get_children():
|
||||
if child is PropComponent:
|
||||
return child
|
||||
return null
|
||||
|
||||
## ==============================
|
||||
## Act Binding
|
||||
## ==============================
|
||||
func _bind_act_events() -> void:
|
||||
if _act_manager == null:
|
||||
return
|
||||
|
||||
_act_manager.act_changed.connect(_on_act_changed)
|
||||
|
||||
|
||||
func _on_act_changed(from_act: StringName, to_act: StringName) -> void:
|
||||
if debug_log:
|
||||
print("[ReedScene] Act changed:", from_act, "->", to_act)
|
||||
|
||||
for prop_comp in _prop_map.values():
|
||||
prop_comp.on_act_changed(from_act, to_act)
|
||||
|
||||
## ==============================
|
||||
## Editor Helpers
|
||||
## ==============================
|
||||
func _editor_auto_attach_prop_components() -> void:
|
||||
if not auto_attach_prop_component:
|
||||
return
|
||||
|
||||
var props_root := get_node_or_null(props_root_path)
|
||||
if props_root == null:
|
||||
return
|
||||
|
||||
for prop in props_root.get_children():
|
||||
if not prop is Node:
|
||||
continue
|
||||
|
||||
if _find_prop_component(prop) != null:
|
||||
continue
|
||||
|
||||
var comp := PropComponent.new()
|
||||
prop.add_child(comp)
|
||||
comp.owner = get_tree().edited_scene_root
|
||||
|
||||
if debug_log:
|
||||
print("[ReedScene][Editor] Auto attached PropComponent to:", prop.name)
|
||||
|
||||
func _ready() -> void:
|
||||
_resolve_act_manager()
|
||||
_resolve_props_root()
|
||||
_collect_props()
|
||||
_bind_act_events()
|
||||
|
|
|
|||
23
update.md
23
update.md
|
|
@ -27,7 +27,10 @@ v0.2.2
|
|||
- 添加了落石的基础类,可以实现,开始抖动,结束抖动,移动,结束移动,移动到目标点等一系列操作
|
||||
- 添加了一个落石的基础案例,可以在此案例的基础上对落石功能进行拓展和修改
|
||||
|
||||
v0.2.3
|
||||
|
||||
- 基础的关卡编辑器,主要明确了几个概念:Prop Scene,Act,目前已经有了一个基础的展示功能性质的Demo
|
||||
- 分离了Player和控制器,现在Player是Avatar,控制器是PlayerController,控制器向玩家发送指令,玩家接收指令以驱动功能。
|
||||
|
||||
## 更新计划
|
||||
|
||||
|
|
@ -59,17 +62,17 @@ V0.2
|
|||
主要是实现蔚蓝的地图相关的功能,比如穿过一个入口之后时间缓速并切换场景,等
|
||||
|
||||
- [x] Phantom Camera 插件接入
|
||||
- [ ] 相机功能
|
||||
- [ ] 相机追随
|
||||
- [ ] 相机震动
|
||||
- [ ] ~~相机功能~~
|
||||
- [x] ~~相机追随~~
|
||||
- [ ] ~~相机震动~~
|
||||
- [ ] 地图功能
|
||||
- [ ] 地图美术功能
|
||||
- [ ] 地图物件功能
|
||||
- [ ] 落石
|
||||
- [ ] 移动跟随平台
|
||||
- [ ] 玩家死亡和重生的逻辑
|
||||
- [ ] Room功能:一个Level由n个Room组成,Room存在一个Entrance,玩家进入Entrance会让Entrance向上传输消息给Room并让镜头切换到对应的Room
|
||||
- [ ] entrance的对应不同的复活点,从不同的entrance进入会让玩家死亡后在不同的复活点复活
|
||||
- [ ] ~~地图美术功能~~
|
||||
- [ ] ~~地图物件功能~~
|
||||
- [ ] 关卡编辑器
|
||||
- [x] 根据state的物件性质加载
|
||||
- [ ] 物件性质预设集Act
|
||||
- [ ] 场景管理器SceneManager
|
||||
- [ ] 玩家死亡和重生点
|
||||
|
||||
|
||||
V0.3
|
||||
|
|
|
|||
Loading…
Reference in New Issue