{"id":37,"date":"2025-06-27T19:16:07","date_gmt":"2025-06-27T19:16:07","guid":{"rendered":"https:\/\/blog.metu.edu.tr\/e264325\/?p=37"},"modified":"2025-06-27T20:01:03","modified_gmt":"2025-06-27T20:01:03","slug":"blade-master","status":"publish","type":"post","link":"https:\/\/blog.metu.edu.tr\/e264325\/2025\/06\/27\/blade-master\/","title":{"rendered":"Blade Master"},"content":{"rendered":"\n<p>Hello dear graphics enjoyers! In this blogpost, my friend Murat and I will show you how to build &amp; run the project, what user controls we implemented and some implementation details. Hope you have good time playing Blade Master!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to run:<\/h2>\n\n\n\n<p>Create a project directory and unzip <code>blade-master.zip<\/code> there.<\/p>\n\n\n\n<p>On inek machines, execute the following commands in the project directory:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-small-font-size\"><code>make<\/code><\/li>\n\n\n\n<li class=\"has-small-font-size\"><code>LD_LIBRARY_PATH=vendor\/assimp\/bin .\/bin\/blade_master<\/code><\/li>\n<\/ul>\n\n\n\n<p>On other ubuntu machines, replace the contents of <code>Makefile<\/code> with the contents of <code>makefile_for_other_ubuntu_machines.txt<\/code> and then run the following commands:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"has-small-font-size\"><code>sudo apt install libassimp-dev<\/code><\/li>\n\n\n\n<li class=\"has-small-font-size\"><code>make<\/code><\/li>\n\n\n\n<li class=\"has-small-font-size\"><code>.\/bin\/blade_master<\/code><\/li>\n<\/ul>\n\n\n\n<p>The difference is that we&#8217;ve provided binaries for inek machines but they may not necessarily work on other ubuntu machines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">All controls:<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Enter<\/strong> &#8211; start the game<\/li>\n\n\n\n<li><strong>Space<\/strong> &#8211; stop the physics<\/li>\n\n\n\n<li><strong>M<\/strong> &#8211; switch between modes<\/li>\n\n\n\n<li><strong>A\/D<\/strong> &#8211; control the horizontal position of the sword<\/li>\n\n\n\n<li><strong>Mouse X\/Y<\/strong> &#8211; control the sword&#8217;s roll and vertical position, respectively<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">More on Blade Control and Animation<\/h2>\n\n\n\n<p>A slice is only as satisfying as its animation. We implemented an animation system to give the sword a sense of weight and motion.<\/p>\n\n\n\n<p><strong>Player Control:<\/strong> The control scheme is simple and direct. The player&#8217;s camera is fixed, looking straight ahead.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>A\/D Keys:<\/strong> Control the horizontal position of the sword.<\/li>\n\n\n\n<li><strong>Mouse X\/Y:<\/strong> Control the sword&#8217;s roll and vertical position, respectively.<\/li>\n<\/ul>\n\n\n\n<p><strong>The Slice Animation:<\/strong> When the left mouse button is clicked, we trigger a pre-scripted animation that takes over the sword&#8217;s transform for a fraction of a second. We found that a simple linear thrust felt robotic. To make it look natural, we combined three motions, all driven by a smooth sine wave for easing:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Forward Thrust:<\/strong> The sword lunges forward into the scene.<\/li>\n\n\n\n<li><strong>Downward Dip:<\/strong> It follows a slight arc, dipping down as it thrusts, simulating the motion of a real arm.<\/li>\n\n\n\n<li><strong>Pivoted Pitch:<\/strong> This was the most crucial detail. Instead of the whole sword rotating, we made the blade &#8220;lean forward&#8221; by rotating it around a pivot point located at the handle. This was achieved by finding the pivot&#8217;s coordinate in the model&#8217;s local space and applying a <code>Translate -&gt; Rotate -&gt; Inverse Translate<\/code> transformation sequence. This gives the impression that the handle stays stable while the blade does the cutting work.<\/li>\n<\/ol>\n\n\n\n<p>The actual slice logic is triggered at the very peak of this animation, ensuring the cut happens at the moment of maximum extension.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding the juice<\/h2>\n\n\n\n<p><strong>CPU Logic:<\/strong> The CPU is responsible for the particle physics. Each frame, it loops through active particles, applies gravity to their velocity, updates their position, and decreases their lifetime.<\/p>\n\n\n\n<p><strong>GPU Rendering:<\/strong> We upload the position, color, and size of every active particle to a buffer in a single batch. Then we call instanced rendering (<code>glDrawArraysInstanced<\/code>) to draw all active particles efficiently in one call, with each instance using its own position, color, and size.<\/p>\n\n\n\n<p>The number of particles at each moment is not higher than 15000. Therefore, we decided to not use compute shaders and stick to a simpler solution.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Basic Physics<\/h2>\n\n\n\n<p>Once an object is sliced into two new pieces, they shouldn&#8217;t just hang in the air. We needed a physics system to make them react realistically. We built a simple but effective system from the ground up.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>RigidBody Component:<\/strong> We created a <code>RigidBody<\/code> class that can be attached to any <code>GameObject<\/code>. It stores physical properties like mass, velocity (linear and angular), and accumulators for force and torque.<\/li>\n\n\n\n<li><strong>PhysicsEngine:<\/strong> This central system manages a list of all active rigid bodies. In its <code>update(deltaTime)<\/code> loop, it performs two key steps:\n<ol class=\"wp-block-list\">\n<li>It applies a constant downward gravitational force to every object.<\/li>\n\n\n\n<li>It updates each object&#8217;s position based on its velocity, and its rotation based on its angular velocity.<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li><strong>Applying Slice Forces:<\/strong> The most important part was making the cut feel impactful. When the <code>SliceManager<\/code> successfully creates two new pieces, it immediately applies two types of forces to their new <code>RigidBody<\/code> components:\n<ol class=\"wp-block-list\">\n<li><strong>Separation Force:<\/strong> A force is applied along the plane&#8217;s normal, pushing the two halves directly away from each other.<\/li>\n\n\n\n<li><strong>Follow-Through Force:<\/strong> A larger force is applied in the direction of the sword&#8217;s slice (the blade&#8217;s forward-facing &#8220;blue axis&#8221;). This gives the pieces momentum and makes it look like the sword&#8217;s energy was transferred to them.<\/li>\n\n\n\n<li><strong>Torque:<\/strong> A small, random torque is also applied to each piece to give them a natural-looking tumble as they fly apart.<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Gameplay Structure &#8211; Game Modes and Spawning<\/h2>\n\n\n\n<p>An <code>enum<\/code> was introduced to manage the current state (<code>FRUIT_MODE<\/code> or <code>DUMMY_MODE<\/code>). Pressing the &#8216;M&#8217; key now switches between these modes, cleans up old objects and prepares the new environment.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fruit Mode:<\/strong> every 5 seconds, a fruit (randomly chosen between watermelon, apple and banana) is thrown into the scene.<\/li>\n\n\n\n<li><strong>Dummy Mode:<\/strong> a static humanoid model in the centre of the scene.<\/li>\n<\/ul>\n\n\n\n<p>Also, independent from the game mode, you can stop the physics of the objects (so that they stay static) and try to slice a fruit\/dummy in several directions at once.<\/p>\n\n\n\n<p>Please see our demo video below:<\/p>\n\n\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"lyte-wrapper fourthree\" style=\"width:420px;max-width:100%;margin:5px;\"><div class=\"lyMe\" id=\"WYL_gvOsugcHEjA\"><div id=\"lyte_gvOsugcHEjA\" data-src=\"\/\/i.ytimg.com\/vi\/gvOsugcHEjA\/hqdefault.jpg\" class=\"pL\"><div class=\"tC\"><div class=\"tT\"><\/div><\/div><div class=\"play\"><\/div><div class=\"ctrl\"><div class=\"Lctrl\"><\/div><div class=\"Rctrl\"><\/div><\/div><\/div><noscript><a href=\"https:\/\/youtu.be\/gvOsugcHEjA\" rel=\"nofollow\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i.ytimg.com\/vi\/gvOsugcHEjA\/0.jpg\" alt=\"YouTube video thumbnail\" width=\"420\" height=\"295\" \/><br \/>Watch this video on YouTube<\/a><\/noscript><\/div><\/div><div class=\"lL\" style=\"max-width:100%;width:420px;margin:5px;\"><\/div><figcaption><\/figcaption><\/figure>\n\n\n<p>Murat \u015eeng\u00fcl &amp; Omar Afandi<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello dear graphics enjoyers! In this blogpost, my friend Murat and I will show you how to build &amp; run the project, what user controls we implemented and some implementation details. Hope you have good time playing Blade Master! How to run: Create a project directory and unzip blade-master.zip there. On inek machines, execute the [&hellip;]<\/p>\n","protected":false},"author":9370,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1],"tags":[],"class_list":["post-37","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/posts\/37","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/users\/9370"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/comments?post=37"}],"version-history":[{"count":0,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/posts\/37\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/media?parent=37"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/categories?post=37"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/tags?post=37"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}