完整项目地址:https://gitee.com/wagio_admin/godot_demo1
视频教程:https://www.bilibili.com/video/BV1RT1mYkEV5

Player节点代码

 class_name Player

extends CharacterBody2D

const SPEED = 150.0
const ARROW_SCENE = preload("res://Arrow.tscn")  # 预加载箭矢场景

enum Action {
	Idle,    # 站立
	Running, # 奔跑
	Arrow,   # 射箭
}

@onready var animation_player: AnimationPlayer = $AnimationPlayer

var player_direction = Vector2.DOWN #默认站立朝向
var attacking = false  # 攻击状态


func _physics_process(delta: float) -> void:
	var direction_x = Input.get_axis("ui_left", "ui_right")
	var direction_y = Input.get_axis("ui_up", "ui_down")
	velocity.x = direction_x * SPEED
	velocity.y = direction_y * SPEED
	# 归一化速度
	if velocity.length() > SPEED:
		velocity = velocity.normalized() * SPEED
	# 处理站立和奔跑动画
	if !attacking:
		if velocity != Vector2.ZERO:
			if direction_y != 0:
				player_direction = Vector2.DOWN if direction_y > 0 else Vector2.UP
				change_animation(Action.Running) # 启动跑步动画
			if direction_x != 0:
				player_direction = Vector2.RIGHT if direction_x > 0 else Vector2.LEFT
				change_animation(Action.Running) # 启动跑步动画
		else:
			change_animation(Action.Idle) # 启动站立
	else:
		# 攻击时,减慢速度
		velocity = velocity * 0.4
	move_and_slide()
	# 处理射箭输入
	if Input.is_action_just_pressed("ui_select"):
		shoot_arrow()

func shoot_arrow() -> void:
	if attacking:
		return  # 如果正在攻击,忽略输入
	# 添加箭矢
	var arrow_instance = ARROW_SCENE.instantiate()
	get_tree().root.add_child(arrow_instance)  # 将箭矢添加到场景中
	# 计算射箭方向
	var mouse_position = get_global_mouse_position()  # 鼠标位置
	var direction = (mouse_position - position).normalized()  # 计算方向向量
	var offset = Vector2.ZERO # 偏移量(使弓箭出手位置更加自然) 
	# 计算玩家的方向
	if abs(direction.x) > abs(direction.y):
		player_direction.y = 0  # 保留 x,将 y 设置为 0
		player_direction.x = direction.x
		offset = Vector2(16, -5) if direction.x > 0 else Vector2(-16, -5) # 具体值可以根据实际情况调整
	else:
		player_direction.y = direction.y  
		player_direction.x = 0  # 保留 y,将 x 设置为 0
		offset = Vector2(0, 10) if direction.y > 0 else Vector2(0, -20)  # 具体值可以根据实际情况调整
	player_direction = player_direction.normalized()
	var arrow_start_point = position + offset  # 玩家位置 + 偏移量(使弓箭出手位置更加自然)
	var arrow_impact_point = mouse_position  # 弓箭的落点
	var distance = mouse_position.distance_to(arrow_start_point)  # 计算距离
	if distance > 150:
		# 计算落点在 arrow_start_point 到 mouse_position 线段上,150像素的位置
		arrow_impact_point = arrow_start_point + direction * 150  # 落点为 arrow_start_point 加上方向向量乘以 150
	change_animation(Action.Arrow) # 启动射箭动画
	arrow_instance.shoot(arrow_start_point, arrow_impact_point)  # 启动箭矢
	attacking = true  # 设置攻击状态
	# 设定攻击动画结束后重置攻击状态
	# 假设攻击动画持续时间为 0.2 秒,你可以根据实际情况调整(如果游戏设计中不同的弓箭射速不一样,可以调整这里)
	await get_tree().create_timer(0.2).timeout
	attacking = false  # 重置攻击状态
	
# 切换动画效果
func change_animation(action:Action):
	# 判断站立动画
	if action == Action.Idle:
		if player_direction == Vector2.DOWN:
			animation_player.play("idle_down")
		elif player_direction == Vector2.UP:
			animation_player.play("idle_up")
		elif player_direction == Vector2.RIGHT:
			animation_player.play("idle_right")
		elif player_direction == Vector2.LEFT:
			animation_player.play("idle_left")
	# 判断跑步动画
	elif action == Action.Running:
		if player_direction == Vector2.DOWN:
			animation_player.play("running_down")
		elif player_direction == Vector2.UP:
			animation_player.play("running_up")
		elif player_direction == Vector2.RIGHT:
			animation_player.play("running_right")
		elif player_direction == Vector2.LEFT:
			animation_player.play("running_left")
	# 判断射箭动画
	elif action == Action.Arrow:
		if player_direction == Vector2.DOWN:
			animation_player.play("arrow_down")
		elif player_direction == Vector2.UP:
			animation_player.play("arrow_up")
		elif player_direction == Vector2.RIGHT:
			animation_player.play("arrow_right")
		elif player_direction == Vector2.LEFT:
			animation_player.play("arrow_left")

Arrow 节点代码

extends Node2D

const SPEED = 200.0  # 箭矢飞行速度
const ARC_HEIGHT = 20.0  # 控制抛物线拱形高度

@onready var sprite_2d: Sprite2D = $Sprite2D

var start_position: Vector2  # 箭矢起始位置
var target_position: Vector2  # 箭矢目标位置
var moving: bool = false  # 是否正在移动
var time_passed: float = 0.0  # 飞行时间累积
var flight_duration: float  # 箭矢总飞行时间

# 发射箭矢,设置起始和目标位置
func shoot(arrow_start_point: Vector2, arrow_impact_point: Vector2) -> void:
	position = arrow_start_point  # 设置初始位置
	start_position = arrow_start_point  # 记录起始位置
	target_position = arrow_impact_point  # 记录目标位置
	flight_duration = start_position.distance_to(target_position) / SPEED  # 计算飞行总时长
	time_passed = 0.0  # 重置飞行时间
	moving = true  # 标记箭矢开始移动


# 在每帧更新位置和角度
func _process(delta: float) -> void:
	if not moving:
		return  # 不移动时直接返回
	time_passed = min(time_passed + delta, flight_duration)  # 累加经过时间,限制最大值为总飞行时间
	var t = time_passed / flight_duration  # 计算归一化时间 t (0 到 1)
	# x 坐标线性插值
	var x = lerp(start_position.x, target_position.x, t)  
	# y 坐标在线性插值的基础上,结合抛物线偏移
	var y = lerp(start_position.y, target_position.y, t) - ARC_HEIGHT * 4 * t * (1 - t)  
	position = Vector2(x, y)  # 更新箭矢当前位置
	# 计算角度:箭头对齐运动方向
	if t < 1.0:
		var next_position = Vector2(
			lerp(start_position.x, target_position.x, t + delta / flight_duration),
			lerp(start_position.y, target_position.y, t + delta / flight_duration) - ARC_HEIGHT * 4 * (t + delta / flight_duration) * (1 - (t + delta / flight_duration))
		)  # 计算下一帧位置
		sprite_2d.rotation = (next_position - position).angle()  # 设置箭矢角度朝向为曲线的切线方向
	# 到达目标后释放箭矢实例
	if t >= 1.0:
		queue_free()

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐