Análisis del código fuente de Godot 4 - Práctica - Coexistencia armoniosa

Vi un video de WeChat hoy, simbiosis armoniosa, el efecto aproximado es el siguiente

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

Después de estudiar a Godot durante tanto tiempo, intente ver si el efecto anterior se puede lograr hoy.

De un vistazo, este efecto se logra en varios pasos:

1. Dibuja un círculo y determina la regularidad de las posiciones de múltiples círculos.

2. Puntos en movimiento y determinación de la ley de movimiento de cada punto en movimiento.

3. Depuración completa

1. Dibuja un círculo

Esto debería ser relativamente simple, y la sensación intuitiva es dibujarlo. Cree directamente un nuevo dibujo de proyecto, la escena raíz es Node2D, llamada Circle, y vincule el script circle.gd. Baja y trata directamente con circle.gd

Desde la perspectiva del aprendizaje y la comprensión, debería ser reescribir la función _draw() e intentar llamar a la función draw_circle directamente

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

El resultado salió, no parece difícil. 

Pero parece que necesita dibujar un círculo hueco, pero mirando el código fuente de Godot, la función dibujar_círculo tiene solo tres parámetros, que son las coordenadas del punto central, la longitud del radio y el color.

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);
}

 Fuente de seguimiento 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);
}

Lo descubrí, dibujar un círculo en realidad está usando polígonos internamente. Sentir

circle->primitive = RS::PRIMITIVE_TRIANGLES;

 Controla el efecto, mira directamente la definición:

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

Luego pruebe uno por uno, los primeros 5 corresponden respectivamente a los siguientes efectos

No se siente como el efecto deseado. Mirando estas enumeraciones nuevamente, me siento un poco familiar, como si hubiera una definición similar en OpenGL. Godot no tiene una manera de dibujar círculos huecos.

Por supuesto que no, una forma es dibujar un círculo sólido y luego dibujar un círculo sólido con un radio más pequeño y usar el color de fondo, y finalmente sentirse como un círculo hueco. Por ejemplo

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

resultado 

Para que sea más parecido, primero dibuja el fondo en negro.

	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);

Cuando se obtiene el resultado, se siente como un círculo hueco.

 

Pero este método es un círculo pseudo-hueco. Si dibuja dos círculos parcialmente superpuestos, encontrará el problema.

	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);

 Así que este enfoque no funcionará.

Estudie el código fuente, las diversas funciones API de dibujo.

	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();

Siento que draw_arc puede ser útil, pruébalo

	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);

resultado

factible.

Luego unifica la transformación. Debido a que es posible dibujar un círculo sólido, también es posible dibujar un círculo hueco, así que implemente la función

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);	

 Por lo tanto, con una simple modificación, puedes dibujar un círculo hueco + punto sólido

	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. Punto de movimiento

Muévelo para ver el efecto.

Desde un punto de vista teórico, el punto en movimiento gira en sentido contrario a las agujas del reloj a lo largo de la circunferencia, y hay un ángulo theta entre él y el centro del círculo, que está controlado por el tiempo t. Por supuesto, el control de tiempo más preciso está en la función _physics_process, puede definir una variable de ángulo global deltaAngle = 0, actualizar deltaAngle en _physics_process y forzar la actualización

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);

Muévete ahora 

3. Ley de distribución

El siguiente paso es observar la ley de la posición del círculo y el ángulo del punto en movimiento. Este proceso no será analizado, de todos modos, directamente propondré las reglas después de un análisis simple, y escribiré códigos para probar y verificar.

Para una mejor presentación, agregue control de nivel

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

y luego el efecto final

http://42.192.128.33:1880/hx.mp4

llegar al objetivo inicial.

Supongo que te gusta

Origin blog.csdn.net/drgraph/article/details/131366992
Recomendado
Clasificación