性能面板
每帧采样帧率、静态内存和节点数量,画进三张实时图表,同时一波节点不断被创建和移除。
上方的编辑器正在运行这个项目。改一行代码,它就会重新加载。
用到的 Godot 类
PerformanceControl._drawCanvasItem.draw_polyline
代码
scripts/main.gd
extends Node
const CHURN: int = 40
const MONITORS: Array = [
{monitor = Performance.TIME_FPS, label = 'frames per second', format = '%.0f', scale = 1.0, tint = '#8eef97'},
{monitor = Performance.MEMORY_STATIC, label = 'static memory', format = '%.2f MB', scale = 1.0 / 1048576.0, tint = '#f3d48d'},
{monitor = Performance.OBJECT_NODE_COUNT, label = 'nodes in the tree', format = '%.0f', scale = 1.0, tint = '#8da5f3'},
]
var rows: Array = []
var ballast: Node
var elapsed: float = 0.0
var manual: int = 0
var status: Label
func _ready() -> void:
var column: VBoxContainer = _root()
var title: Label = Label.new()
title.text = 'Performance, read while it happens'
title.add_theme_font_size_override('font_size', 28)
column.add_child(title)
var hint: Label = Label.new()
hint.text = 'Performance.get_monitor returns a single float. One sample per frame is already a graph.'
hint.add_theme_color_override('font_color', Color('#8a90a0'))
column.add_child(hint)
var graph_script: GDScript = Get.script('graph')
for entry in MONITORS:
column.add_child(_row(entry, graph_script))
ballast = Node.new()
add_child(ballast)
var button: Button = Button.new()
button.text = 'add 400 more nodes'
button.pressed.connect(_pile_up)
column.add_child(button)
status = Label.new()
status.add_theme_color_override('font_color', Color('#8eef97'))
status.text = 'a wave of nodes is being added and removed on its own'
column.add_child(status)
print('the node count breathes by itself, the button piles 400 more on top')
func _process(delta: float) -> void:
elapsed += delta
_churn()
for row in rows:
var value: float = Performance.get_monitor(row.entry.monitor) * row.entry.scale
row.value.text = row.entry.format % value
row.graph.push(value)
func _churn() -> void:
var target: int = manual + int(500.0 + 460.0 * sin(elapsed * 3.4))
var have: int = ballast.get_child_count()
if have < target:
for i in mini(CHURN, target - have):
ballast.add_child(Node.new())
elif have > target:
for i in mini(CHURN, have - target):
var spare: Node = ballast.get_child(0)
ballast.remove_child(spare)
spare.free()
func _pile_up() -> void:
manual += 400
status.text = '%d extra nodes pinned on top of the wave' % manual
func _row(entry: Dictionary, graph_script: GDScript) -> Control:
var line: HBoxContainer = HBoxContainer.new()
line.add_theme_constant_override('separation', 20)
var caption: VBoxContainer = VBoxContainer.new()
caption.custom_minimum_size = Vector2(220, 0)
line.add_child(caption)
var name_label: Label = Label.new()
name_label.text = entry.label
name_label.add_theme_color_override('font_color', Color('#8a90a0'))
caption.add_child(name_label)
var value: Label = Label.new()
value.text = '0'
value.add_theme_font_size_override('font_size', 30)
value.add_theme_color_override('font_color', Color(entry.tint))
caption.add_child(value)
var graph: Control = Control.new()
graph.set_script(graph_script)
graph.custom_minimum_size = Vector2(880, 108)
line.add_child(graph)
graph.setup(Color(entry.tint))
rows.append({entry = entry, value = value, graph = graph})
return line
func _root() -> VBoxContainer:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var margin: MarginContainer = MarginContainer.new()
for side in ['margin_left', 'margin_right', 'margin_top', 'margin_bottom']:
margin.add_theme_constant_override(side, 40)
add_child(margin)
margin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var column: VBoxContainer = VBoxContainer.new()
column.add_theme_constant_override('separation', 12)
margin.add_child(column)
return columnscripts/graph.gd
extends Control
const CAP: int = 90
var samples: PackedFloat32Array = PackedFloat32Array()
var tint: Color = Color('#8da5f3')
func setup(color: Color) -> void:
tint = color
func push(value: float) -> void:
samples.append(value)
if samples.size() > CAP:
samples.remove_at(0)
queue_redraw()
func _draw() -> void:
draw_rect(Rect2(Vector2.ZERO, size), Color('#12141a'))
draw_line(Vector2(0, size.y * 0.5), Vector2(size.x, size.y * 0.5), Color('#2a2f3d'), 1.0)
if samples.size() < 2:
return
var low: float = samples[0]
var high: float = samples[0]
for value in samples:
low = minf(low, value)
high = maxf(high, value)
var span: float = high - low
# a monitor that never moves would divide by zero and pin the line to the floor
if span < 0.001:
low -= 0.5
span = 1.0
var points: PackedVector2Array = PackedVector2Array()
for i in samples.size():
var x: float = size.x * float(i) / float(CAP - 1)
var y: float = size.y - 6.0 - (samples[i] - low) / span * (size.y - 12.0)
points.append(Vector2(x, y))
draw_polyline(points, tint, 2.0, true)