{"id":1306,"date":"2012-10-22T17:12:46","date_gmt":"2012-10-22T14:12:46","guid":{"rendered":"http:\/\/unitycoder.com\/blog\/?p=1306"},"modified":"2014-06-21T17:09:17","modified_gmt":"2014-06-21T14:09:17","slug":"qix-prototype","status":"publish","type":"post","link":"https:\/\/unitycoder.com\/blog\/2012\/10\/22\/qix-prototype\/","title":{"rendered":"Qix Prototype"},"content":{"rendered":"<p><a title=\"start webplayer demo\" href=\"http:\/\/unitycoder.com\/upload\/demos\/Qix_prototype_unity\/\" target=\"_blank\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1307\" data-permalink=\"https:\/\/unitycoder.com\/blog\/2012\/10\/22\/qix-prototype\/qix_prototype_game_unity\/\" data-orig-file=\"https:\/\/i0.wp.com\/unitycoder.com\/blog\/wp-content\/uploads\/2012\/10\/qix_prototype_game_unity.jpg?fit=680%2C500&amp;ssl=1\" data-orig-size=\"680,500\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;}\" data-image-title=\"qix_prototype_game_unity\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/unitycoder.com\/blog\/wp-content\/uploads\/2012\/10\/qix_prototype_game_unity.jpg?fit=680%2C500&amp;ssl=1\" class=\"alignnone size-full wp-image-1307\" title=\"qix_prototype_game_unity\" src=\"https:\/\/i0.wp.com\/unitycoder.com\/blog\/wp-content\/uploads\/2012\/10\/qix_prototype_game_unity.jpg?resize=680%2C500\" alt=\"\" width=\"680\" height=\"500\" srcset=\"https:\/\/i0.wp.com\/unitycoder.com\/blog\/wp-content\/uploads\/2012\/10\/qix_prototype_game_unity.jpg?w=680&amp;ssl=1 680w, https:\/\/i0.wp.com\/unitycoder.com\/blog\/wp-content\/uploads\/2012\/10\/qix_prototype_game_unity.jpg?resize=300%2C220&amp;ssl=1 300w\" sizes=\"auto, (max-width: 680px) 100vw, 680px\" \/><\/a><\/p>\n<p>Qix test :\u00a0<a title=\"http:\/\/en.wikipedia.org\/wiki\/Qix\" href=\"http:\/\/en.wikipedia.org\/wiki\/Qix\" target=\"_blank\">Qix (wikipedia)<\/a>, See gameplay video on <a title=\"http:\/\/www.youtube.com\/watch?v=fyEzhntx9dY\" href=\"http:\/\/www.youtube.com\/watch?v=fyEzhntx9dY\" target=\"_blank\">c64 (youtube<\/a>)<\/p>\n<p>Using the same <a title=\"http:\/\/unitycoder.com\/blog\/2012\/10\/10\/flood-fill-algorithm\/\" href=\"http:\/\/unitycoder.com\/blog\/2012\/10\/10\/flood-fill-algorithm\/\" target=\"_blank\">c# floodfill<\/a> from here (comments section).<br \/>\nDiagonal movement not allowed (using part of <a title=\"http:\/\/wiki.unity3d.com\/index.php?title=GridMove\" href=\"http:\/\/wiki.unity3d.com\/index.php?title=GridMove\" target=\"_blank\">this script from unifywiki<\/a>)<\/p>\n<p>Still missing collisions, enemies etc.<\/p>\n<p>Just 1 bigger problem to think about:<br \/>\n&#8211; How do I get the position to apply floodfill to?<br \/>\n&#8211; Which side to fill (or fill both and check which side the enemy is.. still need to get some pixel position to &#8220;drop&#8221; the flood fill to..)<\/p>\n<p><strong>Webplayer:<\/strong><br \/>\n<a title=\"http:\/\/unitycoder.com\/upload\/demos\/Qix_prototype_unity\/\" href=\"http:\/\/unitycoder.com\/upload\/demos\/Qix_prototype_unity\/\" target=\"_blank\">http:\/\/unitycoder.com\/upload\/demos\/Qix_prototype_unity\/<\/a> (v1.0 : doesnt fill correctly)<\/p>\n<p><strong>Source: (javascript)<\/strong><\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\r\n#pragma strict\r\n\r\npublic var target:Transform;\r\nprivate var tex:Texture2D;\r\nprivate var texsize:int=64;\r\n\r\nprivate var f:FloodFiller = new FloodFiller();\r\n\r\nprivate var MAP_SIZE:int = texsize; \/\/ only powers of two are supported\r\nprivate var map:byte&#x5B;] = new byte&#x5B;MAP_SIZE * MAP_SIZE];\r\n\r\nprivate var speed : float = 20.0;\r\n\r\nprivate var pos:Vector3;\r\nprivate var oldpos:Vector3;\r\nprivate var gridpos:Vector3;\r\n\r\nprivate var drawing:boolean = false;\r\nprivate var startPos:Vector3; \/\/ we started drawing from here\r\n\r\nfunction Start ()\r\n{\r\ntex = new Texture2D (texsize, texsize);\r\ntarget.renderer.material.mainTexture = tex;\r\ntarget.renderer.material.mainTexture.filterMode = FilterMode.Point;\r\n\r\nfor (var y:int=0;y&lt;texsize;y++)\r\nfor (var x:int=0;x&lt;texsize;x++)\r\n{\r\nmap&#x5B;texsize * x + y] = 0;\r\ntex.SetPixel(x,y, new Color(0,0,0.2,1));\r\n\r\nif (x==0 || y== 0 || x==texsize-1 || y==texsize-1)\r\n{\r\nmap&#x5B;texsize * x + y] = 33; \/\/ border\r\ntex.SetPixel(x,y, Color.gray);\r\n}\r\n}\r\n\r\ntex.Apply();\r\n\r\npos = transform.position;\r\ngridpos = pos;\r\noldpos = -pos;\r\n\r\n}\r\n\r\nfunction Update ()\r\n{\r\n\/\/ get movements\r\nvar moveX : float = Input.GetAxisRaw (&quot;Horizontal&quot;) * speed;\r\nvar moveY : float = Input.GetAxisRaw (&quot;Vertical&quot;) * speed;\r\n\r\n\/\/ limit diagonal\r\nif (Mathf.Abs(moveX) &gt; Mathf.Abs(moveY))\u00a0\u00a0 \u00a0moveY = 0.0;\u00a0\u00a0 \u00a0else\u00a0\u00a0 \u00a0moveX = 0.0;\r\n\r\n\/\/ move\r\ntransform.Translate(Vector3(moveX,0,moveY) * Time.deltaTime, Space.World);\r\n\r\n\/\/ get gridpos\r\ngridpos= new Vector3( Mathf.RoundToInt(transform.position.x) , 0 , Mathf.RoundToInt(transform.position.z));\r\n\r\n\/\/ limit on walls\r\ntransform.position.x = Mathf.Clamp(transform.position.x,0,texsize-1);\r\ntransform.position.z = Mathf.Clamp(transform.position.z,0,texsize-1);\r\n\r\n\/\/ we have moved?\r\nif (gridpos!=oldpos)\r\n{\r\n\r\n\/\/ we are in empty spot\r\nif (map&#x5B;texsize * gridpos.x + gridpos.z]==0)\r\n{\r\nvar hit : RaycastHit;\r\nvar fwd = transform.TransformDirection (-Vector3.up);\r\nif (Physics.Raycast (transform.position+Vector3(0,0.5,0), fwd, hit))\r\n{\r\nvar pixelUV:Vector2 = hit.textureCoord;\r\npixelUV.x *= texsize;\r\npixelUV.y *= texsize;\r\n\/\/print (pixelUV);\r\n\r\n\/\/ TODO: separate maps?\r\nmap&#x5B;texsize * Mathf.RoundToInt(pixelUV.x) + Mathf.RoundToInt(pixelUV.y)] = 10;\r\n\r\ntex.SetPixel(Mathf.RoundToInt(pixelUV.x), Mathf.RoundToInt(pixelUV.y), Color.green);\r\ntex.Apply();\r\n\r\nif (!drawing) startPos = gridpos;\r\ndrawing = true;\r\n\r\n\r\n}\r\n}else{ \/\/ we are in pre-filled area\r\n\r\n\/\/ TODO: if neighbour cells are already painted, we should floodfill (because we entered 1x1 hole?)\r\n\r\n\/\/ we had been drawing before coming here\r\nif (drawing)\r\n{\r\ndrawing = false;\r\n\r\nvar paintpos:Vector3 = oldpos;\r\n\r\nDebug.DrawLine (startPos, gridpos, Color.red,10);\r\n\r\nif (startPos.z&lt;gridpos.z) paintpos.z -= 1;\r\n\r\n\/\/if (gridpos.x) paintpos.y = gridpos.y+1;\r\n\/\/if (startPos.y&lt;gridPos.y) paintpos.y = gridPos.y+1;\r\n\r\n\r\nprint (&quot;paintpos:&quot;+paintpos+&quot; gridpos:&quot;+gridpos+&quot; oldpos:&quot;+oldpos);\r\nf.FloodFill(map, paintpos.x, paintpos.z,10, 99);\r\ncheckAreas();\r\n}\r\n}\r\n\r\noldpos = gridpos;\r\n\r\n}\r\n\r\n\r\n\/*\r\nif (Input.GetMouseButtonUp (0))\r\n{\r\n\/\/\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0print (&quot;fill&quot;);\r\nf.FloodFill(map, 10, 10, 10, 99);\r\ncheckAreas();\r\n}\r\n*\/\r\n\r\n\r\n}\r\n\r\n\r\n\r\nfunction checkAreas()\r\n{\r\nvar found:boolean=false;\r\nvar countArea:int=0;\r\n\r\nfor (var y:int=0;y&lt;texsize;y++)\r\nfor (var x:int=0;x&lt;texsize;x++)\r\n{\r\nvar val = map&#x5B;texsize * x + y];\r\n\r\n\/\/ its still empty, so its inside building (or its wall?)\r\nif (val==0)\r\n{\r\nmap&#x5B;texsize * x + y] = 0;\r\n\/\/tex.SetPixel(x,y,new Color(0,1,0,1));\r\n\r\nfound=true;\r\ncountArea++;\r\n}\r\n\r\n\/\/it was filled, clean it up\r\nif (val==99)\r\n{\r\nmap&#x5B;texsize * x + y] = 0;\r\ntex.SetPixel(x,y,new Color(0,0,1,1));\r\n}\r\n}\r\n\r\nprint (&quot;countArea:&quot;+countArea);\r\ntex.Apply();\r\n\r\n}\r\n<\/pre>\n<p>FloodFiller.cs (put in Plugins\/ folder)<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\r\nusing System;\r\nusing System.Diagnostics;\r\nusing System.Linq;\r\n\r\n\/\/ orig: http:\/\/pastebin.com\/KHD8axSL\r\n\r\n\/\/namespace FloodFill\r\n\/\/{\r\n\/\/internal class Program\r\npublic class FloodFiller\r\n{\r\n\/\/\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0private const int MAP_SIZE = 1024; \/\/ only powers of two are supported\r\n\r\n\/*\r\nprivate static void Main()\r\n{\r\nvar map = new byte&#x5B;MAP_SIZE * MAP_SIZE];\r\n\r\nvar stopwatch = Stopwatch.StartNew();\r\n{\r\nFloodFill(map, startX: 123, startY: 123, fromValue: 0, toValue: 1);\r\n}\r\nstopwatch.Stop();\r\n\r\nConsole.WriteLine(stopwatch.ElapsedMilliseconds);\r\n\r\n\/\/ Test results\r\nif (map.Any(item =&gt; item == 0))\r\n{\r\nConsole.WriteLine(&quot;Error&quot;);\r\n}\r\nelse\r\n{\r\nConsole.WriteLine(&quot;Ok&quot;);\r\n}\r\n\r\nConsole.ReadLine();\r\n}\r\n*\/\r\n\r\npublic void FloodFill(byte&#x5B;] map, int startX, int startY, byte fromValue, byte toValue)\r\n{\r\nint shift = (int) Math.Round(Math.Log(map.Length, 4)); \/\/ if the array's length is (2^x * 2^x), then shift = x\r\nint startIndex = startX + (startY &lt;&lt; shift);\r\n\r\nif (map&#x5B;startIndex] &gt;= fromValue)\r\n{\r\nreturn;\r\n}\r\n\r\n\r\n\r\n\/\/ initialize flood fill\r\nint size = 1 &lt;&lt; shift;\r\nint sizeMinusOne = size - 1;\r\nint xMask = size - 1;\r\nint minIndexForVerticalCheck = size;\r\nint maxIndexForVerticalCheck = map.Length - size - 1;\r\n\r\n\/\/ initialize queue\r\nint capacity = size * 2;\r\nint mask = capacity - 1;\r\nuint tail = 0;\r\nuint head = 0;\r\nvar queue = new int&#x5B;capacity];\r\n\r\nmap&#x5B;startIndex] = toValue;\r\nqueue&#x5B;tail++ &amp; mask] = startIndex;\r\n\r\nwhile (tail - head &gt; 0)\r\n{\r\nint index = queue&#x5B;head++ &amp; mask];\r\nint x = index &amp; xMask;\r\n\r\n\/\/if (x &gt; 0 &amp;&amp; map&#x5B;index - 1] == fromValue)\r\nif (x &gt; 0 &amp;&amp; map&#x5B;index - 1] &lt; fromValue)\r\n{\r\nmap&#x5B;index - 1] = toValue;\r\nqueue&#x5B;tail++ &amp; mask] = index - 1;\r\n}\r\n\/\/if (x &lt; sizeMinusOne &amp;&amp; map&#x5B;index + 1] == fromValue)\r\nif (x &lt; sizeMinusOne &amp;&amp; map&#x5B;index + 1] &lt; fromValue)\r\n{\r\nmap&#x5B;index + 1] = toValue;\r\nqueue&#x5B;tail++ &amp; mask] = index + 1;\r\n}\r\n\/\/if (index &gt;= minIndexForVerticalCheck &amp;&amp; map&#x5B;index - size] == fromValue)\r\nif (index &gt;= minIndexForVerticalCheck &amp;&amp; map&#x5B;index - size] &lt; fromValue)\r\n{\r\nmap&#x5B;index - size] = toValue;\r\nqueue&#x5B;tail++ &amp; mask] = index - size;\r\n}\r\n\/\/if (index &lt;= maxIndexForVerticalCheck &amp;&amp; map&#x5B;index + size] == fromValue)\r\nif (index &lt;= maxIndexForVerticalCheck &amp;&amp; map&#x5B;index + size] &lt; fromValue)\r\n{\r\nmap&#x5B;index + size] = toValue;\r\nqueue&#x5B;tail++ &amp; mask] = index + size;\r\n}\r\n}\r\n\r\n\r\n\r\n\r\n}\r\n}\r\n\/\/}\r\n\r\n&amp;nbsp;\r\n\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Qix test :\u00a0Qix (wikipedia), See gameplay video on c64 (youtube) Using the same c# floodfill from here (comments section). Diagonal movement not allowed (using part of this script from unifywiki) Still missing collisions, enemies etc. Just 1 bigger problem to think about: &#8211; How do [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1307,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4,3],"tags":[331,350,349,324,122,347,348],"class_list":["post-1306","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-demos","category-unity3d","tag-area","tag-c64","tag-connect","tag-flood-fill","tag-lines","tag-qix","tag-remake"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/unitycoder.com\/blog\/wp-content\/uploads\/2012\/10\/qix_prototype_game_unity.jpg?fit=680%2C500&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/p1KTaT-l4","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/posts\/1306","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/comments?post=1306"}],"version-history":[{"count":7,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/posts\/1306\/revisions"}],"predecessor-version":[{"id":3018,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/posts\/1306\/revisions\/3018"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/media\/1307"}],"wp:attachment":[{"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/media?parent=1306"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/categories?post=1306"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unitycoder.com\/blog\/wp-json\/wp\/v2\/tags?post=1306"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}