【Godot4.2】myPoint类 - 基于旋转和移动的点求取方式

概述

记得很久以前(大约17年前)有个用指令绘图的软件(不是LOGO,而是它的一个模仿者,我找半天实在找不到。),基于移动和旋转来绘制折线。每次移动和旋转都是基于当前位置和方向,就像一个人在走路一样。这是一种有别于向量旋转的方式。

所以我就突发奇想,想要自己实现这种独特的绘图方法。

myPoint类

刚开始我想要用编写函数来实现,但发现必须在函数外使用全局变量来记录当前位置和方向。所以直接编写了一个自定义类myPoint

通过它你可以定义一个起始点,然后通过其旋转函数rot和移动函数move,来获取新的点。

最后通过points属性获取所有顶点,然后使用CanvasItem绘图函数绘制和显示。

# ========================================================
# 名称:myPoint
# 类型:自定义类
# 简介:通过记录当前位置和方向进行旋转和移动,获取多个点,可用于绘制
# 多边形、折线
# 作者:巽星石
# Godot版本:v4.2.1.stable.official [b09f793f5]
# 创建时间:20244615:10:13
# 最后修改时间:202441413:39:06
# ========================================================

class_name myPoint

var dir = Vector2.RIGHT:         # 当前方向
	set(val):
		dir = val
		
var start_angle:float:           # 起始角度(与X轴夹角),决定初始方向
	set(val):
		start_angle = val
		dir = dir.rotated(deg_to_rad(val)) # 旋转dir

var pos:= Vector2()               # 当前位置
var _start_pos:= Vector2()       # 初始位置(不会随着移动发生变化)
var points:PackedVector2Array    # 点集合

# 获取初始位置
func get_start_pos() -> Vector2:
	return Vector2(_start_pos)     # 返回副本(避免修改)

# 初始化
func _init(x:float = 0.0,y:float = 0.0) -> void:
	pos = Vector2(x,y)          # 构造初始位置
	points.append(pos)          # 添加初始点
	_start_pos = pos            # 记录初始位置

# 旋转
func rot(deg:float) -> void:
	dir = dir.normalized().rotated(deg_to_rad(deg))

# 移动
func move(length:float) -> void:
	var newpos = pos + dir * length  # 计算新点
	points.append(newpos)              # 添加新点
	pos = newpos                      # 更新位置

# 执行路径,每个Vector2的x表示旋转度数,y表示移动距离
func do_path(path:PackedVector2Array) -> void:
	for do in path:
		rot(do.x)  # 旋转
		move(do.y) # 移动

# 以字符串形式执行路径
func do_path_with_str(path:String) -> void:
	var dos = path.split(" ",false)
	for do in dos:
		var ds = do.split(",",false)
		rot(float(ds[0]))  # 旋转
		move(float(ds[1])) # 移动

绘制简单旗帜

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	
	p.move(100)    
	p.rot(90)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(120)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(90)
	p.move(100)
	draw_polyline(p.points,Color.AQUAMARINE,2)

上面代码绘制的图形如下:
image.png

  • var p = myPoint.new(400,200)定义了初始位置(400,200)
  • 默认方向是Vector2.RIGHT,所以第一个p.move(100),是向右移动100像素,也就是(400,200)+(100,0)=(500,100)
  • 第一个p.rot(90),则是在Vector2.RIGHT基础上顺时针旋转90°,也就变成了Vector2.DOWN。依次类推…

image.png
通过简单修改可以获得另一个图形:

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	
	p.rot(-30)
	p.move(100/cos(deg_to_rad(30)))
	p.rot(30)
	p.rot(90)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(120)
	p.move(200)
	p.rot(30)
	p.move(200)
	p.rot(120)
	p.move(100/cos(deg_to_rad(30)))
	draw_polyline(p.points,Color.AQUAMARINE,2)

image.png
可以看到,这种方式绘制图形更考验你在平面空间中行走的想象能力。也需要你灵活的计算各种夹角、以及使用三角函数来计算对边和邻边投影等。

但是它的优点是可以只用旋转和移动两种操作,得到一些需要用向量加减法和向量旋转做半天的图形。

用向量表示旋转和移动操作

实际上,因为只有旋转和移动两种操作,所以完全可以用一个Vector2来存储每次的旋转角度移动距离。也就是Vector2(angle,length)

然后用一个PackedVector2Array来记录执行的所有操作。

extends Node2D

# 用于myPoint执行的路径
var path:PackedVector2Array = [
		Vector2(0,100),    # 旋转0度,向前移动100像素
		Vector2(90,200),   # 旋转90度,向前移动200像素
		Vector2(30,200),   # 旋转30度,向前移动200像素
		Vector2(120,200),  # 旋转120度,向前移动200像素
		Vector2(30,200),   # 旋转30度,向前移动200像素
		Vector2(90,100),   # 旋转90度,向前移动100像素
	]

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	p.do_path(path)                  # 执行路径
	# 绘制获得的曲线
	draw_polyline(p.points,Color.AQUAMARINE,2)

绘制的效果是一样的。
path数组中的单个Vector2可以看做是一次旋转和移动操作的复合:

  • Vector2(45,100),表示当前方向顺时针旋转45度,然后向前移动100像素
  • 如果要单单表示移动,只需要将旋转设为0,比如Vector2(0,100)只进行移动
  • 而单单表示旋转也类似,只需要将移动设为0,比如Vector2(45,0)只进行旋转

折线描述字符串

我们可以进一步将PackedVector2Array简化为一个字符串,通过解析字符串来还原操作。

extends Node2D

# 用于myPoint执行的路径
var path = "0,100 90,200 30,200 120,200 30,200 90,100"	

func _draw() -> void:
	var p = myPoint.new(400,200)     # 起始位置
	p.do_path_with_str(path)                  # 执行路径
	# 绘制获得的曲线
	draw_polyline(p.points,Color.AQUAMARINE,2)

这种形式的好处是,极致压缩,但又存储了关键信息,可以用于在文件中存储和还原图形。

其他绘制案例

绘制正多边形

你可以使用循环来执行多次的旋转和移动操作,从而获得一些特殊图形,比如正多边形。

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 3   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(360.0/steps)
		p.move(100)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

在这里插入图片描述

  • 图中绿色点为实例化myPoint时定义的起始点。
  • 通过将每次行走的线段绘制成向量,可以看到图形基于起始点行走的方向和过程。

绘制螺旋线

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(180.0/steps)
		p.move(10 + 2.0 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(30)
		p.move(5 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(90)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(60)
		p.move(5 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(120)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 60   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(150)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,300)
	var steps := 50   # 步幅
	
	# 旋转和移动
	for i in range(steps):
		p.rot(180)
		p.move(10 * i)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

绘制方波

extends Node2D

func _draw() -> void:
	var p = myPoint.new(10,200)
	var steps := 10   # 重复次数
	var length:= 20.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.move(length)
		p.rot(-90)
		p.move(length)
		p.rot(90)
		p.move(length)
		p.rot(90)
		p.move(length)
		p.rot(-90)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

齿轮

# 暂时有Bug!

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 60  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.move(length)
		p.rot(-90 + 360.0/steps)
		p.move(length)
		p.rot(90 + 360.0/steps)
		p.move(length)
		p.rot(90 + 360.0/steps)
		p.move(length)
		p.rot(-90 + 360.0/steps)
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

三角波

extends Node2D

func _draw() -> void:
	var p = myPoint.new(10,200)
	var steps := 10  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(60)
		p.move(length)
		p.rot(-120)
		p.move(length)
		p.rot(60)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(60)
		p.move(length)
		p.rot(-120 + 360.0/steps)
		p.move(length)
		p.rot(60)
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

锯齿波

extends Node2D

func _draw() -> void:
	var p = myPoint.new(10,200)
	var steps := 10  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(-45)
		p.move(length/sin(deg_to_rad(45)))
		p.rot(135)
		p.move(length)
		p.rot(-90)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

圆锯

extends Node2D

func _draw() -> void:
	var p = myPoint.new(400,200)
	var steps := 20  # 重复次数
	var length:= 40.0 # 移动距离
	# 旋转和移动
	for i in range(steps):
		p.rot(-45)
		p.move(length/sin(deg_to_rad(45)))
		p.rot(135 + 360.0/steps)
		p.move(length)
		p.rot(-90)
	
	myCanvas.draw_vectors(self,p.points)          # 绘制向量
	myCanvas.draw_point(self,p.get_start_pos())   # 绘制起始点

image.png

总结

  • myPoint类基本上是开辟了另一种几何图形点求取方式,可以很好的作为简单向量旋转和加减法求点形式的补充
  • 通过简单的旋转和移动函数可以绘制很多特殊几何图形,而且也可以通过进一步编写函数来参数化的快速获取图形。
  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

巽星石

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

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

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

打赏作者

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

抵扣说明:

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

余额充值