【Godot4.2】CanvasItem绘图函数全解析 - 1.绘制简单图形

本文详细介绍了Godot中的CanvasItem如何通过各种函数绘制圆形、实心圆、空心圆、圆弧、扇形、线段、多边形以及椭圆,包括绘制技巧和方法。
摘要由CSDN通过智能技术生成

概述

本节通过实例来简单讲解CanvasItem绘图函数中用于图形绘制的一些函数。
其他内容可以查看系列相关文章。

系列目录

绘制圆

绘制实心圆

圆是一种非常简单的几何图形,可以通过圆形和半径来定义一个圆。

  • 使用draw_circle()方法可以绘制指定填充颜色的实心圆。
extends Node2D

# 绘制虚函数
func _draw() -> void:
	# 以(200,200)为圆心,绘制一个半径为100的橘黄色圆
	draw_circle(Vector2(200,200),100,Color.ORANGE)

绘制效果如下:
image.png

绘制空心圆

  • CanvasItem并没有直接提供绘制空心圆的函数,相反我们可以用圆弧绘制函数draw_arc()来绘制一个360度闭合的圆弧来间接绘制空心圆
extends Node2D

# 绘制虚函数
func _draw() -> void:
	# 以(200,200)为圆心,绘制一个半径为100的橘黄色圆圈
	draw_arc(Vector2(200,200),100,0,TAU,100,Color.ORANGE,2,true)

绘制效果如下:
image.png

绘制带边线的圆

  • 通过结合实心圆和空心圆的绘制,可以实现带有边线的实心圆效果
  • 也就是说可以绘制指定了边线颜色和填充颜色的圆。
extends Node2D

# 绘制虚函数
func _draw() -> void:
	# 实心圆
	draw_circle(Vector2(200,200),100,Color.ORANGE)
	# 圆圈
	draw_arc(Vector2(200,200),100,0,TAU,100,Color.ORANGE_RED,2,true)

可以看到:

  • 我们使用了draw_circle()draw_arc()函数,分别绘制了相同圆心和半径的实心圆空心圆,并利用了绘图的先后顺序和特性:先绘制的图形会被后绘制的遮盖
  • 先绘制实心圆,后绘制空心圆,则空心圆覆盖在空心圆之上

绘制效果如下:
image.png

绘制圆弧和扇形

上面绘制空心圆时我们已经提到了圆弧绘制函数draw_arc()

  • 圆弧是圆的一部分,所以依然需要指定一个圆心和半径
  • 通过设定起始角度和终止角度,可以限定绘制圆上的那一部分
extends Node2D

# 绘制虚函数
func _draw() -> void:
	# 绘制圆弧
	draw_arc(Vector2(200,200),100,0,PI/2.0,100,Color.ORANGE_RED,2,true)

上面代码的含义就是:

  • (200,200)为圆心,以100像素为半径
  • 起始角度为0,结束角度为PI/2.0,也就是90°
  • 边线宽度为2,颜色为Color.ORANGE_RED(橘红色)
  • 抗锯齿为true,绘制一条弧线

绘制效果如下:
image.png

绘制空心扇形

通过将弧线起止点与圆心相连,可以获得空心的扇形。
我们可以使用draw_line()绘制圆心到弧线起始点和终止点的连线。

extends Node2D

var start_angle:= 0.0    # 起始角度
var end_angle:= PI/2.0   # 终止角度
var c:= Vector2(200,200) # 圆心
var r:= 100.0            # 半径
var border_color := Color.ORANGE_RED    # 边线颜色
var border_width := 2.0                 # 边线颜色
var antialiased := true                 # 抗锯齿
var point_count := 100                  # 绘制的点数目


# 绘制虚函数
func _draw() -> void:
	# 绘制圆弧
	draw_arc(c,r,start_angle,end_angle,point_count,border_color,border_width,antialiased)
	
	# 求圆弧起始和终止点的坐标
	var p1 = c + Vector2.RIGHT.rotated(start_angle) * r
	var p2 = c + Vector2.RIGHT.rotated(end_angle) * r
	
	# 绘制圆心到圆弧起始和终止点的连线
	draw_line(c,p1,border_color,border_width,antialiased)
	draw_line(c,p2,border_color,border_width,antialiased)

绘制效果:
image.png

绘制线段

绘制实线和虚线

  • draw_line()用于绘制两点之间的线段
  • draw_dashed_line()则用于绘制两点之间的虚线
extends Node2D

var p1:= Vector2(200,200)    # 起点
var p2:= Vector2(300,200)    # 终点
var border_color := Color.WHITE         # 边线颜色
var border_width := 2.0                 # 边线宽度
var antialiased := true                 # 抗锯齿

# 绘制虚函数
func _draw() -> void:
	# 绘制线段
	draw_line(p1,p2,border_color,border_width,antialiased)
	# 间距
	var d := Vector2(0,100)
	# 绘制虚线线段
	draw_dashed_line(p1 + d,p2 + d,border_color,border_width)

绘制效果:
image.png

  • draw_line()draw_dashed_line()都要指定两个点作为线段的起点和终点
  • 都需要设定边线的绘制宽度border_width和绘制颜色border_color

绘制不同长短和间距的虚线

通过设定draw_dashed_line()dash参数(默认2.0),可以创建不同长度和间距的虚线。

extends Node2D

var p1:= Vector2(200,200)    # 起点
var p2:= Vector2(300,200)    # 终点
var border_color := Color.WHITE         # 边线颜色
var border_width := 1.0                 # 边线宽度
var antialiased := true                 # 抗锯齿

# 绘制虚函数
func _draw() -> void:
	# 间距
	var d := Vector2(0,20)
	for i in range(20):
		# 绘制虚线线段
		draw_dashed_line(p1 + d * i,p2 + d * i,border_color,border_width,i)

不同长度和间距的虚线效果:
image.png
不使用对齐:

  • 通过将aligned参数设为false,可以绘制出与线段终点不对齐的虚线
extends Node2D

var p1:= Vector2(200,200)    # 起点
var p2:= Vector2(300,200)    # 终点
var border_color := Color.WHITE         # 边线颜色
var border_width := 1.0                 # 边线宽度
var antialiased := true                 # 抗锯齿

# 绘制虚函数
func _draw() -> void:
	# 间距
	var d := Vector2(0,20)
	for i in range(20):
		# 绘制虚线线段
		draw_dashed_line(p1 + d * i,p2 + d * i,border_color,border_width,i,false)

绘制效果:
image.png

绘制多条线段

  • draw_multiline()可以用于同时绘制多条线段
  • 需要传入一个包含所有线段起始点和终点PackedVector2Array,其中按顺序,每两个Vector2构成一条线段。
extends Node2D

var p1 = Vector2(20,20)
var p2 = p1 + Vector2.RIGHT * 100
var margin := 20.0   # 线段间的间隔距离

var lines:PackedVector2Array

func _ready() -> void:
	var h = Vector2.DOWN * margin
	
	for i in range(10):
		lines.append_array([p1 + h * i,p2+h * i])  # 添加线段
		
func _draw() -> void:
	draw_multiline(lines,Color.AQUA,1)       # 同时绘制多条线段

绘制效果:
image.png

绘制折线

折线(Polyline)是一种带有转折的连续线段。至少需要一个“拐弯”或由三个点定义。(经典巽式非严谨定义

使用笨办法

  • 可以使用draw_line()进行首尾相连的绘制来得到折线
  • 也可以使用draw_multiline()构造多条线段来绘制折线
extends Node2D


var border_color := Color.WHITE         # 边线颜色
var border_width := 1.0                 # 边线宽度
var antialiased := true                 # 抗锯齿

# 绘制虚函数
func _draw() -> void:
	var p1:= Vector2(200,200)    # 点1
	var p2:= Vector2(300,200)    # 点2
	var p3:= Vector2(200,100)    # 点3
	# 绘制虚线线段
	draw_line(p1,p2,border_color,border_width)
	draw_line(p2,p3,border_color,border_width)

上面的代码:

  • 定义3个点p1p2p3
  • 然后使用draw_line()分别绘制线段(p1,p2)(p2,p3)

绘制效果:
image.png

使用折线绘制函数

  • CanvasItem提供了更好的折线绘制函数draw_polyline()draw_polyline_colors()
  • 你只需要将折线的顶点按顺序存入一个PackedVector2Array并传入折线绘制函数,函数就会自动绘制出该折线
extends Node2D


var border_color := Color.WHITE         # 边线颜色
var border_width := 1.0                 # 边线宽度
var antialiased := false                # 抗锯齿

# 绘制虚函数
func _draw() -> void:
	# 折线的顶点
	var points:PackedVector2Array = [Vector2(200,200),Vector2(300,200), Vector2(200,100)]
	# 绘制虚线线段
	draw_polyline(points,border_color,border_width,antialiased)

绘制效果:
image.png

绘制渐变折线

  • draw_polyline()只能指定一种颜色。所以只能绘制带一种颜色的折线。
  • draw_polyline_colors()可以指定多个颜色对应于相应顺序的顶点,然后绘制时每两个顶点之间的线段会因为顶点颜色而插值,显示出颜色渐变的效果
  • draw_polyline(points,border_color,border_width,antialiased)与draw_polyline_colors(points,[border_color],border_width,antialiased)等价
extends Node2D

var colors:PackedColorArray = [Color.AQUA,Color.BROWN,Color.ORANGE]  # 顶点颜色集合
var border_width := 1.0                 # 边线宽度
var antialiased := false                # 抗锯齿


# 绘制虚函数
func _draw() -> void:
	# 折线的顶点
	var points:PackedVector2Array = [Vector2(200,200),Vector2(300,200), Vector2(200,100)]
	# 绘制渐变折线
	draw_polyline_colors(points,colors,border_width,antialiased)

image.png
这里,定义了三个颜色对应到折线的3个顶点,用draw_polyline_colors()绘制时,会在内部通过颜色插值来实现渐变。

绘制多边形

多边形(Polygon)是指由3个及以上顶点构成的封闭图形。(依旧是巽式非严谨定义)

  • draw_colored_polygon()可用于绘制填充某种颜色的实心多边形
  • 至少需要指定平面上3个点才能构成多边形。
extends Node2D

# 绘制虚函数
func _draw() -> void:
	# 折线的顶点
	var points:PackedVector2Array = [Vector2(200,200),Vector2(300,200), Vector2(200,100)]
	# 绘制渐变多边形
	draw_colored_polygon(points,Color.AQUA)

上面的代码:

  • 定义了包含三个点的数组points
  • 然后直接传递给draw_colored_polygon()函数,然后指定使用Color.AQUA(海水色)来填充整个多边形

绘制效果如下:
image.png

绘制渐变填充多边形

  • draw_polyline_colors()一样,多边形也有指定顶点颜色的渐变版本draw_polygon()
  • 通过按顺序指定每个顶点的颜色,可以实现整个多边形的颜色插值,从而实现渐变多边形
extends Node2D

var colors:PackedColorArray = [Color.AQUA,Color.BROWN,Color.ORANGE]  # 顶点颜色集合
var border_width := 1.0                 # 边线宽度
var antialiased := false                # 抗锯齿


# 绘制虚函数
func _draw() -> void:
	# 折线的顶点
	var points:PackedVector2Array = [Vector2(200,200),Vector2(300,200), Vector2(200,100)]
	# 绘制渐变多边形
	draw_polygon(points,colors)

上面的代码中:

  • 依旧是定义了三个顶点,存储与数组points
  • 然后还定义了一个颜色数组colors,来存储三个顶点的颜色
  • 最后调用draw_polygon(),并传入pointscolors

绘制效果如下:
image.png

  • draw_polygon(points,[Color.BLUE])等价于draw_colored_polygon(points,Color.BLUE),将只会以一种颜色填充

绘制四边形和矩形

通过指定4个点,可以定义四边形或矩形。

extends Node2D

var colors:PackedColorArray = [Color.AQUA,Color.BROWN,Color.ORANGE,Color.RED]  # 顶点颜色集合
var border_width := 1.0                 # 边线宽度
var antialiased := false                # 抗锯齿


# 绘制虚函数
func _draw() -> void:
	# 矩形的顶点
	var points:PackedVector2Array = [Vector2(200,200),Vector2(300,200), Vector2(300,300),Vector2(200,300)]
	# 绘制渐变多边形
	draw_polygon(points,colors)

image.png
同样你可以使用draw_colored_polygon()绘制单色填充版本。

通过向量旋转获取正多边形

正多边形可以看做在圆上的等分点按顺序相连构成的图形。

具体可以参考我之前的文章:

这里以正多边形顶点求取为例,编写如下函数:

# 正多边形点求取函数
func regular_polygon(center:Vector2,r:float,edges:=3,strat_angle:float=0,close:=false):
	var points:PackedVector2Array = []
	var right = Vector2.RIGHT
	# 利用角度插值和向量旋转获取正多边形顶点
	for i in range(edges):
		var deg = lerp(0,360,float(i)/float(edges))
		var p = right.rotated(deg_to_rad(deg)) * r
		points.append(p)
	
	if close: # 闭合 - 用于draw_polyline
		points.append(points[0])
	# 变换
	var T:= Transform2D().rotated(strat_angle).translated(center)
	return T * points

绘制测试:

extends Node2D

# 顶点颜色集合
var colors:PackedColorArray = [Color.AQUA,Color.BROWN,Color.ORANGE,Color.RED]
var border_width := 1.0                 # 边线宽度
var antialiased := false                # 抗锯齿


func _draw() -> void:
	var regular = regular_polygon(Vector2(200,200),100,3,0,true)
	# 绘制空心正多边形
	draw_polyline(regular,colors[0])

上面代码:

  • 调用自定义多边形顶点求取函数regular_polygon()
  • (200,200)为圆心,以100像素为半径,起始角度为0(与X轴正方向夹角为0),求正三角形

绘制效果:
image.png
通过修改edges参数,可以绘制不同边数的正多边形:
edges = 6

使用正多边形函数绘制圆

通过绘制有很多边的正多边形,可以绘制出近似的圆。

func _draw() -> void:
	var regular = regular_polygon(Vector2(200,200),100, 2 * 100 * PI,0,true)
	# 绘制空心圆
	draw_polyline(regular,colors[0])

上面的代码:

  • 绘制半径r200,边数为2 * r * PI的正多边形

其绘制效果完全可以看做是一个圆:
image.png

绘制椭圆

椭圆也是很常见的几何图形,但是通过数学公式和原始定义去求椭圆是比较愚蠢的做法。
实际上椭圆可以看做是圆拉伸后的图形,这种理解在几何和数学上也是被承认的。
所以我们实际上只需要构造一个拉伸的线性变换,并将其施加到绘制的圆上就可以了。
基于上面的思想,编写椭圆求取函数如下:

# 椭圆求取函数
func ellipse(center:Vector2,r1:float,r2:float,rot:=0.0,close:=false):
	# 先求取半径为r1的圆
	var circle = regular_polygon(Vector2(),r1, r1 * 2 * PI,0,close)
	# 构造变换
	var t := Transform2D().scaled(Vector2(1,r2/r1)).rotated(rot).translated(center)
	return t * circle

测试代码:

extends Node2D

var c = Vector2(500,300)  # 圆心
var r1 = 200  # 水平半径
var r2 = 150  # 垂直半径
var rot = deg_to_rad(0)  # 旋转角度
var border_width := 1.0  # 边线宽度


func _draw() -> void:
	# 求椭圆
	var ell = ellipse(c,r1,r2,rot,true)
	# 绘制原始的圆
	draw_circle(c,r1,Color.DARK_GRAY)
	# 绘制原始圆心
	draw_circle(c,2,Color.AQUA)
	# 绘制椭圆
	draw_polyline(ell,Color.AQUA,border_width)

绘制效果:
rot = deg_to_rad(0)“)rot = deg_to_rad(45)”)

  • 此处,为了验证缩放变换和旋转变换的正确性,所以在绘制椭圆之前绘制了以横向半径r1为半径的圆。

完整代码

因为椭圆求取函数ellipse是基于正多边形求取函数regular_polygon的,所以完整代码如下:

extends Node2D

var c = Vector2(500,300)  # 圆心
var r1 = 200  # 水平半径
var r2 = 150  # 垂直半径
var rot = deg_to_rad(0)  # 旋转角度
var border_width := 1.0  # 边线宽度


func _draw() -> void:
	# 求椭圆
	var ell = ellipse(c,r1,r2,rot,true)
	# 绘制原始的圆
	draw_circle(c,r1,Color.DARK_GRAY)
	# 绘制原始圆心
	draw_circle(c,2,Color.AQUA)
	# 绘制椭圆
	draw_polyline(ell,Color.AQUA,border_width)

# 椭圆求取函数
func ellipse(center:Vector2,r1:float,r2:float,rot:=0.0,close:=false):
	# 先求取半径为r1的圆
	var circle = regular_polygon(Vector2(),r1, r1 * 2 * PI,0,close)
	# 构造变换
	var t := Transform2D().scaled(Vector2(1,r2/r1)).rotated(rot).translated(center)
	return t * circle
	
	
	# 正多边形点求取函数
func regular_polygon(center:Vector2,r:float,edges:=3,strat_angle:float=0,close:=false):
	var points:PackedVector2Array = []
	var right = Vector2.RIGHT
	# 利用角度插值和向量旋转获取正多边形顶点
	for i in range(edges):
		var deg = lerp(0,360,float(i)/float(edges))
		var p = right.rotated(deg_to_rad(deg)) * r
		points.append(p)
	
	if close: # 闭合 - 用于draw_polyline
		points.append(points[0])
	# 变换
	var T:= Transform2D().rotated(strat_angle).translated(center)
	return T * points

总结

  • 31
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巽星石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值