{"id":602,"date":"2025-02-17T10:48:12","date_gmt":"2025-02-17T15:48:12","guid":{"rendered":"https:\/\/willkolb.com\/?p=602"},"modified":"2025-02-17T10:48:12","modified_gmt":"2025-02-17T15:48:12","slug":"gadot-bro-a-k-a-wonder-wonder-woman-game-engine-a-k-a-side-project-to-a-side-project","status":"publish","type":"post","link":"https:\/\/willkolb.com\/?p=602","title":{"rendered":"Gadot Bro a.k.a. Wonder Wonder Woman game engine a.k.a. Side project to a side project"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9-1024x576.png\" alt=\"\" class=\"wp-image-603\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9-1024x576.png 1024w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9-300x169.png 300w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9-768x432.png 768w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9-1536x864.png 1536w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9-500x281.png 500w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-9.png 1919w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>I started messing with Gadot (<a href=\"https:\/\/godotengine.org\/\">https:\/\/godotengine.org\/<\/a>) to fulfill the free-form open source activist in me (also I have a friend making stuff in Gadot). <\/p>\n\n\n\n<p>So I started messing with the 3d side but the goal is for a more metroidvania type game. So I moved into understanding the 2d asset pipelines a bit better. The way I want to handle this is to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Make a spritesheet(s) with four different animations: Idle, walk, jump, land<\/li>\n\n\n\n<li>Make a character using the sprite-sheet with simple controls<\/li>\n\n\n\n<li>Stretch goal- Add landing and walking smoke effects<\/li>\n<\/ul>\n\n\n\n<p>Making spritesheets has gotten so easily since I was making simple games in XNA (15ish years ago damn&#8230;) now you can do everything in browser. I&#8217;m using <a href=\"https:\/\/www.piskelapp.com\">https:\/\/www.piskelapp.com<\/a> which is very fast and reliable for dumping a .png for a sprite sheet.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"504\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10-1024x504.png\" alt=\"\" class=\"wp-image-604\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10-1024x504.png 1024w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10-300x148.png 300w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10-768x378.png 768w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10-1536x756.png 1536w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10-500x246.png 500w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-10.png 1911w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>I&#8217;m going with a stick figure. I&#8217;m cool with jank walking but as long as you get four frames I you&#8217;re good (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Walk_cycle\">https:\/\/en.wikipedia.org\/wiki\/Walk_cycle<\/a>). My finalized sprite sheet: <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"96\" height=\"128\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/Guy_Basic1.png\" alt=\"\" class=\"wp-image-605\"\/><\/figure>\n\n\n\n<p>First three two frames are idle (just a simple bob). The next four are the jump\/land cycle, the rest are walking (which I can invert for each direction in gadot). <\/p>\n\n\n\n<p>Gadot has a REALLY good sprite sheet importer: <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"603\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-11-1024x603.png\" alt=\"\" class=\"wp-image-606\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-11-1024x603.png 1024w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-11-300x177.png 300w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-11-768x452.png 768w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-11-500x294.png 500w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-11.png 1253w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>It lets you chop, then select which frames to import. In that way you can keep 100% of the sprites in one huge sprite-sheet and not worry about flipping between files at runtime (which in reality isn&#8217;t a big issue but I imagine you&#8217;re going to hit performance on low ram platforms with 1000+ pngs loaded in memory).<\/p>\n\n\n\n<p>The animation editor once you get frames in place is SUPER easy and lets you quickly edit and test spritesheets as needed: <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"304\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-12-1024x304.png\" alt=\"\" class=\"wp-image-607\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-12-1024x304.png 1024w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-12-300x89.png 300w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-12-768x228.png 768w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-12-500x148.png 500w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-12.png 1153w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>All of these get stored inside of your &#8220;AnimatedSprite2d&#8221; instance that can be selected via sprite frames property: <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"363\" height=\"564\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-13.png\" alt=\"\" class=\"wp-image-608\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-13.png 363w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-13-193x300.png 193w\" sizes=\"auto, (max-width: 363px) 100vw, 363px\" \/><\/figure>\n\n\n\n<p>Now the scripting in gadot mirrors unity (and unreal kinda) where everything has a setup and loop function that you edit to handle internal logic to your &#8220;Nodes&#8221; (which are called actor and gameobject in unreal and unity respectively). Gadot doesn&#8217;t have a nice state machine editor like unity or unreal so you need to program it up yourself. The design of the simple character is actually expecting you to do this so there&#8217;s there&#8217;s state and utility funcitons in both the character and sprite classes that let you do this easily:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>extends CharacterBody2D\n\nconst SPEED = 300.0\nconst JUMP_VELOCITY = -400.0\n@onready var _animated_sprite = $AnimatedSprite2D\n#AnimationStateMachine\nvar startJump = false\nvar isFalling = false\nvar endJump = false\nvar isWalking = false\n\nfunc _physics_process(delta: float) -> void:\n\t# Add the gravity.\n\tif not is_on_floor():\n\t\tvelocity += get_gravity() * delta\n\t\tstartJump = false\n\t\tisFalling = true\n\telif isFalling and is_on_floor():\n\t\tendJump = true\n\t\tisFalling = false\n\telif endJump and is_on_floor():\n\t\tendJump = false\n\telse:\n\t\tendJump = false\n\t\tisFalling = false\n\t\t\n\t# Handle jump.\n\tif Input.is_action_just_pressed(\"ui_accept\") and is_on_floor():\n\t\tvelocity.y = JUMP_VELOCITY\n\t\tstartJump = true\n\n\t# Get the input direction and handle the movement\/deceleration.\n\t# As good practice, you should replace UI actions with custom gameplay actions.\n\tvar direction := Input.get_axis(\"ui_left\", \"ui_right\")\n\tif direction:\n\t\tvelocity.x = direction * SPEED\n\t\tif(absf(velocity.x) > 0):\n\t\t\tisWalking = true\n\t\telse:\n\t\t\tisWalking = false\n\telse:\n\t\tvelocity.x = move_toward(velocity.x, 0, SPEED)\n\t\tif(absf(velocity.x) > 0):\n\t\t\tisWalking = true\n\t\telse:\n\t\t\tisWalking = false\n\tmove_and_slide()\n\thandleAnimationStateMachine()\n\nfunc handleAnimationStateMachine():\n\tif startJump:\n\t\t_animated_sprite.play(\"Jump_start\")\n\telif isFalling and _animated_sprite.animation_finished:\n\t\t_animated_sprite.play(\"Jump_loop\")\n\t\tstartJump = false\n\telif endJump and  _animated_sprite.animation_finished:\n\t\t_animated_sprite.play(\"Jump_end\")\n\telif isWalking and _animated_sprite.animation_finished:\n\t\t_animated_sprite.play(\"Walk\")\n\telse:\n\t\t_animated_sprite.play(\"default\")\n\t\tendJump = false\n\t\tisWalking = false<\/code><\/pre>\n\n\n\n<p>If you have no idea what this is doing essentially its: <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"747\" height=\"597\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-14.png\" alt=\"\" class=\"wp-image-609\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-14.png 747w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-14-300x240.png 300w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-14-375x300.png 375w\" sizes=\"auto, (max-width: 747px) 100vw, 747px\" \/><\/figure>\n\n\n\n<p>Final result: <\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"1080\" style=\"aspect-ratio: 1920 \/ 1080;\" width=\"1920\" controls src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/2025-02-17_10-46-10.mp4\"><\/video><\/figure>\n\n\n\n<p>So I&#8217;m pretty confident here that if someone threw me a bunch of sprites\/2d art I could go ahead and make a game. Gadot has the same issues as unity where the structure is much less defined than Unreal. Therefore its crazy easy to prototype but scaling up will take more developer discipline to prevent Node spanning bugs and state issues. In my head the rough hierarchical structure of any 2d sidescroller would be:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"835\" height=\"546\" src=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-16.png\" alt=\"\" class=\"wp-image-611\" srcset=\"https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-16.png 835w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-16-300x196.png 300w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-16-768x502.png 768w, https:\/\/willkolb.com\/wp-content\/uploads\/2025\/02\/image-16-459x300.png 459w\" sizes=\"auto, (max-width: 835px) 100vw, 835px\" \/><\/figure>\n\n\n\n<p>The way to read this is that the controller points to the controlled. So the game master controls everything the scene master controls characters and cut-scenes etc. With this structure way you don&#8217;t get confused with race conditions etc. If you&#8217;re a lower level object that wants to initiate a high level event you&#8217;ll be forced to request up the chain (i.e. an npc that the player presses use on will send a request to the scene controller to start a cut-scene, which will then send a request to the game controller which then can trigger the cut-scene player). It seems confusing and over-engineered but if you consider the alternative you would need everyone to align to some other kind of mental model, which in my opinion can be crazy painful to handle and leads to a BUNCH of crunch. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>I started messing with Gadot (https:\/\/godotengine.org\/) to fulfill the free-form open source activist in me (also I have a friend making stuff in Gadot). So I started messing with the 3d side but the goal is for a more metroidvania type game. So I moved into understanding the 2d asset pipelines a bit better. The [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":607,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,7],"tags":[],"class_list":["post-602","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gadot","category-gamedev"],"_links":{"self":[{"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/posts\/602","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/willkolb.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=602"}],"version-history":[{"count":1,"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/posts\/602\/revisions"}],"predecessor-version":[{"id":613,"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/posts\/602\/revisions\/613"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/willkolb.com\/index.php?rest_route=\/wp\/v2\/media\/607"}],"wp:attachment":[{"href":"https:\/\/willkolb.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=602"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/willkolb.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=602"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/willkolb.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=602"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}