Cute Animation with Primitive Shapes
I wanted to share the animation process for creating Harley the Hare, the main character from my latest game Hops and Harmony
Harley, along with all the characters in the game are just primitive shapes drawn onto the screen
You can especially see this in the aptly named Sheepa below when a light is cast from above
Let's draw Harley the Hare!
I used the Godot Engine but you can use any engine or tool as long as there is a way to do custom drawing in 2D
Start with a basic circle
export var body_radius = 40 func _draw(): var screen_center = get_viewport().size / 2 draw_circle(screen_center, body_radius, Color.pink)
Convert to 2 semicircles
Drawing a circle is great and all but what we really want is 2 adjustable semicircles. We can use this arc function to help us define our own custom method for creating semicircles supported by custom parameters such as radius, angle, and curviness. The white circles are the points that the circular arc function generates (the nb_points parameter determines how many points each arc should take)
export var body_radius = 40 func _draw(): var screen_center = get_viewport().size / 2 draw_circular_arc(screen_center, body_radius, 0, 180, Color.pink, 1.0, 6) draw_circular_arc(Vector2(screen_center.x - 20, screen_center.y), body_radius, 180, 360, Color.pink, 1.0, 6) # func draw_circular_arc(center, radius, angle_from, angle_to, color, curviness, nb_points): var points_arc = PoolVector2Array() for i in range(nb_points + 1): var angle_point = deg2rad(angle_from + i * (angle_to-angle_from) / nb_points - 90) var next_point = Vector2(cos(angle_point) * 1 / curviness, \ sin(angle_point) * curviness) * radius points_arc.push_back(center + next_point) draw_circle(center + next_point, 5, Color.white) draw_colored_polygon(points_arc, color)
By modifying the curviness parameter, we can alter the curviness of the circle
Update curviness dynamically every frame
I created curviness, curviness_rate, and is_curving_up variables to facilitate the curviness variable oscillating from 0.8 - 1.2
It's breathing!
var curviness = 1.0 var curviness_rate = .1 var is_curving_up = true func _process(delta): if is_curving_up: curviness += curviness_rate * delta else: curviness -= curviness_rate * delta if curviness < .9: is_curving_up = true elif curviness > 1.1: is_curving_up = false update()
Add the head
For the head, we'll just bob it up and down using the same curviness parameter
func draw_head(): var screen_center = get_viewport().size / 2 var head_offset_y = 50 * curviness var head_center = Vector2(screen_center.x, screen_center.y - head_offset_y) draw_circular_arc(head_center, head_radius, \ 0, 180, Color('c06c84'), 1.0, 8) draw_circular_arc(head_center, head_radius, \ 180, 360, Color('6c5b7b'), 1.0, 8)
Add eyes and nose
The eyes and nose are just circles with offsets. Use the head center position as a base for drawing the eyes and nose
func draw_face(head_vec, head_offset_x): var eye_height := 5.0 var eye_between_width := 9.0 var eye_radius := 3.5 var left_eye = Vector2(head_vec.x - eye_between_width + head_offset_x, head_vec.y - eye_height) var right_eye = Vector2(head_vec.x + eye_between_width + head_offset_x, head_vec.y - eye_height) draw_circle_custom(left_eye, eye_radius, eye_color) draw_circle_custom(right_eye, eye_radius, eye_color) var nose_radius := 4.0 var nose_offset_y := 2.0 draw_circle_custom(Vector2(head_vec.x + head_offset_x, head_vec.y + nose_offset_y), nose_radius, pink_color)
Since we have a good understanding of the semicircles, let's go back to just pink
Add left/right offsets based on direction
var face_offset_x = 0 func draw_head(): var screen_center = get_viewport().size / 2 var head_offset_y = 50 * curviness var head_center = Vector2(screen_center.x + face_offset_x / 2, screen_center.y - head_offset_y) draw_circular_arc(head_center, head_radius, \ 0, 180, primary_color, 1.0, 8) draw_circular_arc(head_center, head_radius, \ 180, 360, primary_color, 1.0, 8) if is_walking_right(): face_offset_x = 5 if is_walking_left(): face_offset_x = -5 draw_face(head_center, face_offset_x)
Add ears
There are circles for the outer ear, smaller circles for the pink inner ear, and a primitive shape from the radius of the outer ear down to a little above the eyes
That's it!
To finish up, just add a collider for physics and stretch the body whenever you jump/land. I added some little particle effects on landing as well
For more devlogs, updates, and tutorials feel free to follow me on itch.io or twitter
Cheers!
---
You can check out Hops and Harmony at https://pyrecraft.itch.io/hops-and-harmony
Get Hops and Harmony
Hops and Harmony
when in doubt, hop about
Status | Released |
Author | Jon Topielski |
Genre | Adventure, Interactive Fiction, Platformer, Visual Novel |
Tags | 2D, Atmospheric, Casual, Cute, Singleplayer |
Comments
Log in with itch.io to leave a comment.
how would one follow this in game maker?
Hi - looks like there is a way to draw primitives in gamemaker: https://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/drawing/primitives/draw_primitive_begin.html
You can try and translate my godot code to gamemaker code
An alternative to the drawing primitive approach altogether would be using sprites + scaling the sprite x/y values to stretch the circle upward and sideways
Let me know if you have any questions about these approaches