Godot 4 源码分析 - 练手 - 和谐共生

今天看到一个微信视频,和谐共生,大概效果如下

https://live.csdn.net/v/306826

研究这么长时间的Godot,今天试试能否实现上述效果

粗看一下,这个效果实现分几步:

1. 画圆,并确定多个圆的位置规律

2. 动点,并确定各动点的运动规律

3. 综合调试

1. 画圆

这个应该是比较简单的,直观感觉是要画出来。直接新建一个工程draw,根场景为Node2D,名为Circle,绑定脚本circle.gd。下来就直接处理circle.gd

从学习理解来看,应该是重写_draw()函数,直接调用draw_circle函数试试

func _draw():	
	draw_circle(Vector2(300, 300), 150, Color.WHITE);

结果出来了,好象不难。 

但好象是需要画空心圆,但看Godot源码中,draw_circle函数居然只有三个参数,分别为中心点坐标、半径长度、颜色。

void CanvasItem::draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color) {
	ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");

	RenderingServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color);
}

 跟踪源码canvas_item_add_circle

void RendererCanvasCull::canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) {
	Item *canvas_item = canvas_item_owner.get_or_null(p_item);
	ERR_FAIL_COND(!canvas_item);

	Item::CommandPolygon *circle = canvas_item->alloc_command<Item::CommandPolygon>();
	ERR_FAIL_COND(!circle);

	circle->primitive = RS::PRIMITIVE_TRIANGLES;

	Vector<int> indices;
	Vector<Vector2> points;

	static const int circle_points = 64;

	points.resize(circle_points);
	Vector2 *points_ptr = points.ptrw();
	const real_t circle_point_step = Math_TAU / circle_points;

	for (int i = 0; i < circle_points; i++) {
		float angle = i * circle_point_step;
		points_ptr[i].x = Math::cos(angle) * p_radius;
		points_ptr[i].y = Math::sin(angle) * p_radius;
		points_ptr[i] += p_pos;
	}

	indices.resize((circle_points - 2) * 3);
	int *indices_ptr = indices.ptrw();

	for (int i = 0; i < circle_points - 2; i++) {
		indices_ptr[i * 3 + 0] = 0;
		indices_ptr[i * 3 + 1] = i + 1;
		indices_ptr[i * 3 + 2] = i + 2;
	}

	Vector<Color> color;
	color.push_back(p_color);
	circle->polygon.create(indices, points, color);
}

搞明白了,画圆其实就是在内部用多边形实现。感觉

circle->primitive = RS::PRIMITIVE_TRIANGLES;

 是控制效果的,直接看定义:

	enum PrimitiveType {
		PRIMITIVE_POINTS,
		PRIMITIVE_LINES,
		PRIMITIVE_LINE_STRIP,
		PRIMITIVE_TRIANGLES,
		PRIMITIVE_TRIANGLE_STRIP,
		PRIMITIVE_MAX,
	};

那就挨个测试一下,前5个分别对应以下效果

感觉都不是想要的效果。再看这些枚举量,感觉有点熟悉,好象在OpenGL中有类似的定义。难道Godot没有画空心圆的方法。

当然不是,一种方法是画一个实心圆,然后再以小一点的半径再用背景色画一个实心圆,最终感觉是一个空心圆。比如

	draw_circle(Vector2(300, 300), 150, Color.WHITE);
	draw_circle(Vector2(300, 300), 149, Color.BLACK);

结果 

为了更像一些,先把背景也画成黑色

	draw_rect(Rect2(0, 0, get_viewport_rect().size.x, get_viewport_rect().size.y), Color.BLACK)
	draw_circle(Vector2(300, 300), 150, Color.WHITE);
	draw_circle(Vector2(300, 300), 149, Color.BLACK);

得到结果,感觉是个空心圆了。

 

但这种方式是伪空心圆,画两个部分重叠的圆,就发现问题了

	draw_rect(Rect2(0, 0, get_viewport_rect().size.x, get_viewport_rect().size.y), Color.BLACK)
	draw_circle(Vector2(300, 300), 150, Color.WHITE);
	draw_circle(Vector2(300, 300), 149, Color.BLACK);
	draw_circle(Vector2(450, 300), 150, Color.WHITE);
	draw_circle(Vector2(450, 300), 149, Color.BLACK);

 所以这种方法行不通。

研究一下源码,画图的各API函数

	void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, real_t p_dash = 2.0, bool p_aligned = true);
	void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
	void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
	void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0, bool p_antialiased = false);
	void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
	void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0);
	void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0);
	void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = -1.0);
	void draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color);
	void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
	void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
	void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
	void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0, double p_scale = 1.0);
	void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1));
	void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
	void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>());
	void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
	void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());

	void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
	void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture);

	void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
	void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;

	void draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = 1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
	void draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;

	void draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
	void draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;

	void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0));
	void draw_set_transform_matrix(const Transform2D &p_matrix);
	void draw_animation_slice(double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset = 0);
	void draw_end_animation();

感觉draw_arc可能有用,试下

	draw_rect(Rect2(0, 0, get_viewport_rect().size.x, get_viewport_rect().size.y), Color.BLACK)
	draw_arc(Vector2(300, 300), 150, 0, PI * 2, 200, Color.WHITE, -1, true);
	draw_arc(Vector2(450, 300), 150, 0, PI * 2, 200, Color.WHITE, -1, true);

结果

可行。

那就统一改造。因为可能画实心圆,也可能画空心圆,所以实现函数

func drawCircle(pos, radius, color, line_width = -1, filled = false):
	if filled :
		draw_circle(pos, radius, color);
	else:
		draw_arc(pos, radius, 0, PI * 2, 200, color, line_width, true);	

 所以,简单修改一下,就可以画出空心圆+实心点

	draw_rect(Rect2(0, 0, get_viewport_rect().size.x, get_viewport_rect().size.y), Color.BLACK)
	drawCircle(Vector2(300, 300), radius, lineColor);
	drawCircle(Vector2(450, 300), 2, Color.WHITE, -1, true);

2. 动点

点得动起来,才能看得出效果

从原理角度来说,动点是沿圆周逆时针转动,其与圆心连线有一个角度theta,该角度受时间t控制。当然,最精确的控制时间的地方在_physics_process函数中,可以定义一个全局角度变量deltaAngle = 0,在_physics_process中更新deltaAngle并强制刷新

func _physics_process(delta):
	deltaAngle -= 0.125 / PI;
	queue_redraw();

func _draw():	
	draw_rect(Rect2(0, 0, get_viewport_rect().size.x, get_viewport_rect().size.y), Color.BLACK)
	drawCircle(Vector2(300, 300), radius, lineColor);
	drawCircle(Vector2(300 + cos(deltaAngle) * 150, 300 + sin(deltaAngle) * 150), 2, Color.WHITE, -1, true);

现在就动起来了 

3. 分布规律

下来就是要看圆位置与动点角度的规律。这个过程就不分析了,反正我是简单分析后就直接提出规律,写代码测试验证即可。

为更好演示,加上级数控制

func _draw():	
	draw_rect(Rect2(0, 0, get_viewport_rect().size.x, get_viewport_rect().size.y), Color.BLACK)	
	drawCircleAtAngle(0, PI + deltaAngle);
	drawCircleAtAngle(PI, PI + deltaAngle - PI);
	
	var step = 2;
	while step <= pow(2, level):
		for i in step:
			drawCircleAtAngle(PI / step + i * 2 * PI / step, PI + deltaAngle - PI / step - i * 2 * PI / step);
		step *= 2;
	pass

然后就是最终效果

http://42.192.128.33:1880/hx.mp4

达到初步目标。

猜你喜欢

转载自blog.csdn.net/drgraph/article/details/131366992
今日推荐