【Godot】做一个可复用的旋转节点

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 类型的节点。

猜你喜欢

转载自blog.csdn.net/qq_37280924/article/details/108379212