追逐与逃离

逃跑者躲开鼠标,追逐者跟着逃跑者。两边都用 direction_to,只有符号不同。

上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。

用到的 Godot 类

Node2DLine2DVector2

代码

scripts/main.gd
extends Node

const RUNNER_SPEED: float = 340.0
const CHASER_SPEED: float = 250.0
const FLEE_RANGE: float = 340.0
const MARGIN: float = 100.0
const DRIFT_SPEED: float = 0.9

var runner: Node2D
var chaser: Node2D
var pointer: Polygon2D
var link: Line2D
var target: Vector2
var center: Vector2
var span: Vector2
var limit: Vector2
var drifting: bool = true
var elapsed: float = 0.0


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size
	center = view * 0.5
	span = view * 0.3
	limit = view - Vector2(MARGIN, MARGIN)
	target = center

	link = Line2D.new()
	link.width = 3.0
	link.default_color = Color('#2a2f3d')
	add_child(link)

	pointer = _make_circle(11.0, Color('#f3d48d'))
	pointer.position = target
	add_child(pointer)

	runner = _make_actor('runner', Color('#8eef97'), 26.0)
	runner.position = center + Vector2(200, 60)
	add_child(runner)

	chaser = _make_actor('chaser', Color('#fc7f7f'), 30.0)
	chaser.position = center - Vector2(320, 120)
	add_child(chaser)

	_add_label('every move is direction_to: the runner flees the mouse and the chaser, the chaser follows the runner', Vector2(40, 40))

	print('move the mouse, the green shape runs away from it')


func _input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		target = event.position
		drifting = false


func _process(delta: float) -> void:
	elapsed += delta

	if drifting:
		target = center + Vector2(cos(elapsed * DRIFT_SPEED), sin(elapsed * DRIFT_SPEED * 1.4)) * span

	pointer.position = target

	var flee: Vector2 = Vector2.ZERO

	if target.distance_to(runner.position) < FLEE_RANGE:
		flee += target.direction_to(runner.position)

	if chaser.position.distance_to(runner.position) < FLEE_RANGE:
		flee += chaser.position.direction_to(runner.position)

	if flee == Vector2.ZERO:
		flee = runner.position.direction_to(center)

	runner.position += flee.normalized() * RUNNER_SPEED * delta
	runner.position = runner.position.clamp(Vector2(MARGIN, MARGIN), limit)
	chaser.position += chaser.position.direction_to(runner.position) * CHASER_SPEED * delta
	link.points = PackedVector2Array([chaser.position, runner.position])


func _make_actor(text: String, color: Color, radius: float) -> Node2D:
	var actor: Node2D = Node2D.new()
	actor.add_child(_make_circle(radius, color))

	var label: Label = Label.new()
	label.text = text
	label.position = Vector2(-30, radius + 10)
	label.add_theme_color_override('font_color', color)
	label.add_theme_font_size_override('font_size', 20)
	actor.add_child(label)

	return actor


func _add_label(text: String, at: Vector2) -> void:
	var label: Label = Label.new()
	label.text = text
	label.position = at
	label.add_theme_color_override('font_color', Color('#999999'))
	label.add_theme_font_size_override('font_size', 20)
	add_child(label)


func _make_circle(radius: float, color: Color) -> Polygon2D:
	var points: PackedVector2Array = PackedVector2Array()

	for i in 28:
		points.append(Vector2(cos(TAU * i / 28.0), sin(TAU * i / 28.0)) * radius)

	var circle: Polygon2D = Polygon2D.new()
	circle.polygon = points
	circle.color = color

	return circle


func _add_background() -> void:
	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

更多运动示例