【Godot4.2】圆环绘制思路以及函数

概述

在Godot的CanvasItem中没有办法直接绘制圆环,利用Geometry2D进行两个圆的布尔运算也无法实现。所以只能另想办法。

这个问题其实想了有一段时间了,脑海中也早就有了解决方案,以及料想到可能存在的问题。昨天因为QQ群群友的提问,于是就直接试验了一下,结果就有了本文。

两种思路

对于在Godot中绘制圆环,我自己想到两种办法。办法1是将圆环理解为若干梯形围绕原点环形阵列:
将圆环理解为梯形的环形阵列

  • 这种思路的好处是,通过求一个小梯形,然后旋转变换,就可以绘制出圆环的形状。
  • 而且这种方式可以很方便的用颜色插值做出渐变色环效果。

另一种思路是将整个圆环做成一个留有一条极小缝隙的多边形:
将圆环理解为留有一条缝的多边形

  • 这种方法获得的圆环一体性强,可以直接作为单个多边形绘制,顶点数目也相较第一种方式少
  • 但缺点是可能是做颜色渐变效果可能会有些不方便

两种方法各有优缺点吧。本文就介绍这两种思路下的圆环点求解和绘制思路。

整体圆环点求取函数

我首先实现了第2种方法的点求取函数,代码如下:

# 圆环点求取函数
func ring(r1:float,r2:float,edges:int = 3,start_angle:=0.0,end_engle:=360.0) -> PackedVector2Array:
	var points:PackedVector2Array   # 圆环的顶点
	var v1 = (Vector2.RIGHT * r1).rotated(deg_to_rad(start_angle))
	var v2 = (Vector2.RIGHT * r2).rotated(deg_to_rad(start_angle))
	var ang = (end_engle - start_angle)/float(edges)  # 单次旋转角度
	var arc1:PackedVector2Array     # 圆弧1
	var arc2:PackedVector2Array     # 圆弧2
	# 通过向量旋转求不同半径的两条圆弧顶点
	for i in range(edges+1):
		arc1.append(v1.rotated(deg_to_rad( ang * i))) 
		arc2.append(v2.rotated(deg_to_rad( ang * i)))
	
	var close_circle = fposmod((end_engle - start_angle),360.0) == 0 # 圆弧闭合形成圆
	
	if close_circle:
		arc1.set(arc1.size()-1,arc1[arc1.size()-1] + Vector2.UP * 0.01)
	# 顺时针添加圆弧1的顶点
	points.append_array(arc1)
	
	arc2.reverse()
	if close_circle:
		arc2.set(arc2.size()-1,arc2[arc2.size()-1] - Vector2.UP * 0.01)
	# 逆时针添加圆弧1的顶点
	points.append_array(arc2)
	return points
  • 这就是利用简单的向量旋转在半径不同的两个外接圆上求多边形顶点的思路。
  • 只不过将内部圆上求得的顶点顺序翻转,从而获得正确的多边形顶点顺序。

测试代码:

extends Node2D


func _draw() -> void:
	draw_set_transform(Vector2(300,400))  # 设定绘图原点
	# 求一个大圆半径100,小圆半径50,细分边数为100的圆环顶点
	var ring = ring(100,50,100)
	# 以绘制多边形形式绘制圆环
	draw_colored_polygon(ring,Color.AQUAMARINE)

绘制效果:
image.png
其他边数下的效果:
在这里插入图片描述

设定起始和终止角度

弧线是圆的一部分,所以上面的函数不仅仅可以求圆环,也可以求圆环的一部分。

你只需要设定好圆弧的起始和终止角度即可。

extends Node2D

func _draw() -> void:
    # 设定绘图原点
	draw_set_transform(Vector2(300,400))  
	# 绘制圆环的一部分
	var ring = ring(100,50,6,45,90)
	# 以绘制多边形形式绘制圆环
	draw_colored_polygon(ring,Color.AQUAMARINE)
  • 45°到90°,6条边完成

绘制效果:

image.png

extends Node2D

func _draw() -> void:
    # 设定绘图原点
	draw_set_transform(Vector2(300,400))  
	# 绘制圆环的一部分
	var ring = ring(100,50,6,45,180)
	# 以绘制多边形形式绘制圆环
	draw_colored_polygon(ring,Color.AQUAMARINE)
  • 45°到180°,6条边完成

绘制效果:

image.png

extends Node2D

func _draw() -> void:
    # 设定绘图原点
	draw_set_transform(Vector2(300,400))  
	# 绘制圆环的一部分
	var ring = ring(100,50,6,45,360)
	# 以绘制多边形形式绘制圆环
	draw_colored_polygon(ring,Color.AQUAMARINE)
  • 45°到360°,6条边完成

绘制效果:
image.png

梯形旋转变换法

点求取函数如下:

# 圆环点求取函数
func ring2(r1:float,r2:float,edges:int = 3,start_angle:=0.0,end_engle:=360.0) -> Array[PackedVector2Array]:
	var ts:Array[PackedVector2Array] # 所有梯形的集合
	var v1 = (Vector2.RIGHT * r1).rotated(deg_to_rad(start_angle))
	var v2 = (Vector2.RIGHT * r2).rotated(deg_to_rad(start_angle))
	var ang = (end_engle - start_angle)/float(edges)  # 单次旋转角度
	print(ang)
	# 第1个梯形
	var t:PackedVector2Array
	t.append(v1)
	t.append(v1.rotated(deg_to_rad(ang)))
	t.append(v2.rotated(deg_to_rad(ang)))
	t.append(v2)
	#t.append(v1)
	
	var rot = Transform2D().rotated(deg_to_rad(ang))   # 旋转变换
	# 通过旋转第一个梯形,获取其他的梯形
	for i in range(edges):
		ts.append(t)
		t = rot * t
	return ts
  • 代码中求取了第一个梯形的所有顶点,然后构造了一个旋转变换,最后通过顺次应用到第一个梯形来获得其他位置的梯形。
  • 函数直接返回包含所有梯形的数组。在绘制时,我们可以遍历取出每个单独梯形的顶点集合,来进行绘制。

测试代码:

extends Node2D


func _draw() -> void:
	draw_set_transform(Vector2(300,400))  # 设定绘图原点
	# 绘制圆环的一部分
	var ring = ring2(100,50,100)
	#print(ring)
	# 以绘制梯形形式绘制圆环
	for i in range(ring.size()):
		draw_colored_polygon(ring[i],Color.AQUAMARINE.lerp(Color.BROWN,i/float(ring.size())))

各种参数下绘制的效果:

在这里插入图片描述

绘制同心环

通过插值一个长度范围,获得不同的半径,我们可以绘制多个同心环。

extends Node2D

var c1:= Color.RED
var c2:= Color.YELLOW_GREEN
var steps:= 30  # 步幅
var edges:= 20  # 边数
var max_r:= 100.0  # 最大半径
var min_r:= 0.0  # 最小半径


func _draw() -> void:
	c1 = Color.AQUAMARINE
	c2 = Color.BROWN
	draw_set_transform(Vector2(300,400))  # 设定绘图原点
	for w in range(0,steps+1):
		c1 = c1.darkened(w/float(steps) * 0.1)
		c2 = c2.darkened(w/float(steps) * 0.1)
		var r2 = lerp(min_r,max_r,w/float(steps))
		var r1 = lerp(min_r,max_r,(w+1)/float(steps))
		
		# 绘制圆环的一部分
		var ring = ring2(r1,r2,edges)
		#print(ring)
		# 以绘制梯形形式绘制圆环
		for i in range(ring.size()):
			draw_colored_polygon(ring[i],c1.lerp(c2,i/float(ring.size())))

各种参数下的效果:

extends Node2D

var c1:= Color.RED
var c2:= Color.YELLOW_GREEN
var steps:= 5  # 步幅
var edges:= 6  # 边数
var max_r:= 100.0  # 最大半径
var min_r:= 0.0  # 最小半径

image.png

extends Node2D

var c1:= Color.RED
var c2:= Color.YELLOW_GREEN
var steps:= 30  # 步幅
var edges:= 6  # 边数
var max_r:= 100.0  # 最大半径
var min_r:= 0.0  # 最小半径

image.png

extends Node2D

var c1:= Color.RED
var c2:= Color.YELLOW_GREEN
var steps:= 5  # 步幅
var edges:= 100  # 边数
var max_r:= 100.0  # 最大半径
var min_r:= 0.0  # 最小半径

image.png

extends Node2D

var c1:= Color.RED
var c2:= Color.YELLOW_GREEN
var steps:= 100  # 步幅
var edges:= 100  # 边数
var max_r:= 100.0  # 最大半径
var min_r:= 0.0  # 最小半径

同时减小c1、c2每次变暗的系数为0.01

image.png

总结

  • 本文介绍和总结了在Godot求2D圆环图形顶点的两种思路和具体的实现办法,并测试了在不同参数设定下的效果。
  • 可以看到,在求取一些特殊图形顶点时,我们必须要避开Godot的不足(比如无法绘制带孔洞的多边形)而另辟蹊径。
  • 我们可以通过构造带有肉眼无法分辨的间隙的多边形,来绘制带有孔洞的图形,即可以骗过Godot正确实现三角化,也可以在视觉上骗过使用者和观看者。
  • 通过将特殊图形分解为小的图形,再通过某些变换来拼凑,也是一种很好的思路。
  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
基于MySQL+Vue.js开发集成实时聊天系统的动态项目管理web端软件源码+答辩PPT+使用说明.zip 【优质项目推荐】 1.项目代码功能经验证ok,确保稳定可靠运行。欢迎下载使用!在使用过程中,如有问题或建议,请及时私信沟通,帮助解答。 2.项目主要针对各个计算机相关专业,包括计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师或企业员工使用。 3.项目具有丰富的拓展空间,不仅可作为入门进阶,也可直接作为毕设、课程设计、大作业、项目初期立项演示等用途。 4.如果基础还行,或热爱钻研,可基于此项目进行二次开发,DIY其他不同功能。 基于MySQL+Vue.js开发集成实时聊天系统的动态项目管理web端软件源码+答辩PPT+使用说明.zip 部署前端服务 1. 打包前端文件生成 dist 文件夹 ```js // genal-chat-client npm i npm run build ``` 1. 将 dist 下所有文件放到 nginx 下的 html 文件夹中 2. 配置 nginx 的 gzip (提高传输速度)和请求级别(注意只是新增下面的代码,不是替换整个nginx文件) ```js 记得重启一下nginx #### 数据库配置 1. 安装 mysql 2. 设置 mysql 账号密码 3. 创建名为 `chat` 的数据库 4. 配置后端 `app.module.ts` 中的 mysql 账号密码 部署后端服务 1. 安装 pm2 ```js // genal-chat-server npm i pm2 -g ``` 2. 生成 dist 文件 ```js // genal-chat-server npm i npm run build ``` 3. 使用 pm2 运行 ```js // genal-chat-server npm run pm2 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

巽星石

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

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

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

打赏作者

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

抵扣说明:

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

余额充值