Godot 3.2.3
首先将这个『旋转控制器』的基类做出来
"""=====================================================================
ControllerBase
========================================================================
基础控制器
========================================================================
@datetime: 2020-8-29 00:01
@Godot version: 3.2.3
@author: [email protected]
@version: 1.0
====================================================================="""
class_name ControllerBase extends Node2D
"""面板属性"""
export (bool) var enable = true setget set_enable # 当前节点是否可用
export (NodePath) var control_node_path = @'../' setget set_control_node_path # 控制的角色节点路径(默认父节点)
##==============================
## setget 方法
##==============================
func set_enable(value) -> void:
enable = value
set_physics_process(is_enable())
func set_control_node_path(value) -> void:
# 做这一步验证是为了防止在通过 PackedScene.instance() 实例化的时候 name 值不一致的错误
if is_inside_tree():
if get_node(value) == get_parent():
control_node_path = @'../'
return
control_node_path = value
##==============================
## 内置方法
##==============================
func _ready() -> void:
set_physics_process(is_enable())
##==============================
## 自定义方法
##==============================
func init() -> void:
pass
func set_control(node: Node) -> void:
control_node_path = get_path_to(node)
func get_control() -> Node2D:
"""获取控制的节点"""
if control_node_path == @'../':
return get_parent() as Node2D
return get_node(control_node_path) as Node2D
func has_control() -> bool:
"""返回是否存在控制的节点"""
return get_control() != null
func get_rotation_velocity(sub_half_pi: bool = false) -> Vector2:
"""返回控制的节点旋转弧度的向量"""
if sub_half_pi:
return Vector2(1, 0).rotated(get_control().global_rotation - PI/2)
return Vector2(1, 0).rotated(get_control().global_rotation)
func is_enable() -> bool:
"""返回节点是否是可用状态"""
return enable
「旋转节点」继承这个节点,并编写脚本
"""=====================================================================
Steering
========================================================================
[转向节点]
控制节点的转向行为
========================================================================
@datetime: 2020-8-23 20:12
@Godot version: 3.2.3
@author: [email protected]
@version: 1.0
====================================================================="""
class_name Steering extends ControllerBase
"""枚举"""
# 控制角色旋转的方式
enum CONTROL_MODE {
NONE,
MANUAL, # 手动按下按键控制旋转
CODE, # 通过代码传入目标点到 set_target_pos 进行旋转
MOUSE_POS, # 旋转到鼠标的方向
}
# 旋转的方式
enum TURNING_MODE {
NORMAL, # 普通的按照速度旋转
IMMENDIATLY # 立即旋转到目标点
}
"""面板属性"""
# 控制方式
export (CONTROL_MODE) var control_mode = CONTROL_MODE.MANUAL setget set_control_mode
export (String) var input_key_left = 'ui_left' # 控制转向的按键
export (String) var input_key_right = 'ui_right'
# 旋转方式
export (TURNING_MODE) var turning_mode = TURNING_MODE.NORMAL setget set_turning_mode
export (float) var angular_speed = 90 setget set_angular_speed # 旋转速度(每秒旋转角度)
"""私有变量"""
var _target_pos := Vector2(0, 0) setget set_target_pos # 旋转到的目标位置
var _rotate_speed : float # 旋转速度
var _direction: float = 1 # 旋转方向(1为顺时针,-1逆时针)
var current_control_method: FuncRef
var current_turn_method: FuncRef # 执行的转向方法
onready var control_method := {
# 控制节点的方式
CONTROL_MODE.MANUAL: funcref(self, 'get_input_rot'),
CONTROL_MODE.CODE: funcref(self, 'code_turn'),
CONTROL_MODE.MOUSE_POS: funcref(self, 'turn_to_mouse_pos'),
}
onready var turn_method := {
# 调用执行的旋转方法
TURNING_MODE.NORMAL: funcref(self, 'turn_to'),
TURNING_MODE.IMMENDIATLY: funcref(self, 'turn_to_immediately'),
}
var _dir_vel: Vector2 # 自身到目标点的方向向量
var _dot: float # 自身到目标位置的点积
##==============================
## setget方法
##==============================
func set_target_pos(value: Vector2) -> void:
"""设置旋转到的目标位置"""
_target_pos = value
# 计算自身到目标点的方向向量
_dir_vel = _target_pos.direction_to(get_control().global_position)
# 计算自身到目标位置的点积
_dot = _dir_vel.dot(get_rotation_velocity(true))
# 计算向左还是向右旋转
_direction = sign(_dot)
# 如果刚好垂直正对着或者背对着目标方向,则随机设置一个方向
if _direction == 0:
_direction = [-1, 1][randi() % 2]
func set_control_mode(value) -> void:
control_mode = value
# 更改控制方式时重新初始化
if is_inside_tree():
init()
func set_turning_mode(value) -> void:
turning_mode = value
if is_inside_tree():
current_turn_method = turn_method[turning_mode]
func set_angular_speed(value) -> void:
angular_speed = value
_rotate_speed = angular_speed / ProjectSettings.get('physics/common/physics_fps')
##==============================
## 内置方法
##==============================
func _ready() -> void:
if not is_enable():
# print("--> ", name, ' 当前节点状态不可用')
set_physics_process(false)
return
# 代码控制时,不开启线程
if control_mode == CONTROL_MODE.CODE:
set_physics_process(false)
# 初始化
init()
func _physics_process(delta: float) -> void:
current_control_method.call_func()
##==============================
## Skill 方法
##==============================
func init() -> void:
"""初始化"""
.init()
# 如果没有控制节点,则报错
assert ( get_control() != null )
# 计算每帧的旋转速度,节省资源
_rotate_speed = angular_speed / ProjectSettings.get('physics/common/physics_fps')
# 控制方式
current_control_method = control_method[control_mode]
# 旋转的方法
current_turn_method = turn_method[turning_mode]
func get_control_rotate() -> float:
"""获取控制节点的旋转弧度"""
return get_control().global_rotation
# @Override
func is_enable() -> bool:
"""当前节点是否是可用的"""
return enable && control_mode != CONTROL_MODE.NONE
##==============================
## 不同类型的旋转方法
##==============================
#>>> 控制节点的方式
func get_input_rot() -> void:
"""按键控制旋转"""
var dir := Input.get_action_strength(input_key_right) - Input.get_action_strength(input_key_left)
get_control().rotation_degrees += dir * _rotate_speed
func turn_to_mouse_pos() -> void:
"""旋转到鼠标位置"""
# 调用设置目标位置
set_target_pos(get_control().get_global_mouse_position())
current_turn_method.call_func()
func code_turn(pos: Vector2) -> void:
"""代码控制旋转 (外部用代码旋转时调用这个方法)"""
set_target_pos(pos)
current_turn_method.call_func()
#>>> 旋转的方式
func turn_to():
"""旋转到目标位置"""
if abs(_dot) >= 0.1:
get_control().global_rotation_degrees += _direction * _rotate_speed
func turn_to_immediately():
"""旋转到目标位置(立即)"""
get_control().look_at(_target_pos)
OK,通过设置属性面板中要操控的节点,设置一下控制的节点的属性,就可以直接用了,无需再编写其他任何代码!只要将这两个文件放入任意项目中,都可直接使用。
注意:这个节点只能控制 2D 类型的节点。