[Godot] Make a reusable rotating node

Godot 3.2.3

First make the base class of this "rotation controller"

"""=====================================================================
				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

"Rotation node" inherits this node and writes a script

"""=====================================================================
				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, by setting the node to be controlled in the property panel, setting the properties of the controlled node, you can use it directly, without writing any other code! As long as these two files are put into any project, they can be used directly.

Note : This node can only control 2D type nodes.

Guess you like

Origin blog.csdn.net/qq_37280924/article/details/108379212