22
2012
Qix Prototype
Qix test : Qix (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:
– How do I get the position to apply floodfill to?
– Which side to fill (or fill both and check which side the enemy is.. still need to get some pixel position to “drop” the flood fill to..)
Webplayer:
http://unitycoder.com/upload/demos/Qix_prototype_unity/ (v1.0 : doesnt fill correctly)
Source: (javascript)
#pragma strict
public var target:Transform;
private var tex:Texture2D;
private var texsize:int=64;
private var f:FloodFiller = new FloodFiller();
private var MAP_SIZE:int = texsize; // only powers of two are supported
private var map:byte[] = new byte[MAP_SIZE * MAP_SIZE];
private var speed : float = 20.0;
private var pos:Vector3;
private var oldpos:Vector3;
private var gridpos:Vector3;
private var drawing:boolean = false;
private var startPos:Vector3; // we started drawing from here
function Start ()
{
tex = new Texture2D (texsize, texsize);
target.renderer.material.mainTexture = tex;
target.renderer.material.mainTexture.filterMode = FilterMode.Point;
for (var y:int=0;y<texsize;y++)
for (var x:int=0;x<texsize;x++)
{
map[texsize * x + y] = 0;
tex.SetPixel(x,y, new Color(0,0,0.2,1));
if (x==0 || y== 0 || x==texsize-1 || y==texsize-1)
{
map[texsize * x + y] = 33; // border
tex.SetPixel(x,y, Color.gray);
}
}
tex.Apply();
pos = transform.position;
gridpos = pos;
oldpos = -pos;
}
function Update ()
{
// get movements
var moveX : float = Input.GetAxisRaw ("Horizontal") * speed;
var moveY : float = Input.GetAxisRaw ("Vertical") * speed;
// limit diagonal
if (Mathf.Abs(moveX) > Mathf.Abs(moveY)) moveY = 0.0; else moveX = 0.0;
// move
transform.Translate(Vector3(moveX,0,moveY) * Time.deltaTime, Space.World);
// get gridpos
gridpos= new Vector3( Mathf.RoundToInt(transform.position.x) , 0 , Mathf.RoundToInt(transform.position.z));
// limit on walls
transform.position.x = Mathf.Clamp(transform.position.x,0,texsize-1);
transform.position.z = Mathf.Clamp(transform.position.z,0,texsize-1);
// we have moved?
if (gridpos!=oldpos)
{
// we are in empty spot
if (map[texsize * gridpos.x + gridpos.z]==0)
{
var hit : RaycastHit;
var fwd = transform.TransformDirection (-Vector3.up);
if (Physics.Raycast (transform.position+Vector3(0,0.5,0), fwd, hit))
{
var pixelUV:Vector2 = hit.textureCoord;
pixelUV.x *= texsize;
pixelUV.y *= texsize;
//print (pixelUV);
// TODO: separate maps?
map[texsize * Mathf.RoundToInt(pixelUV.x) + Mathf.RoundToInt(pixelUV.y)] = 10;
tex.SetPixel(Mathf.RoundToInt(pixelUV.x), Mathf.RoundToInt(pixelUV.y), Color.green);
tex.Apply();
if (!drawing) startPos = gridpos;
drawing = true;
}
}else{ // we are in pre-filled area
// TODO: if neighbour cells are already painted, we should floodfill (because we entered 1x1 hole?)
// we had been drawing before coming here
if (drawing)
{
drawing = false;
var paintpos:Vector3 = oldpos;
Debug.DrawLine (startPos, gridpos, Color.red,10);
if (startPos.z<gridpos.z) paintpos.z -= 1;
//if (gridpos.x) paintpos.y = gridpos.y+1;
//if (startPos.y<gridPos.y) paintpos.y = gridPos.y+1;
print ("paintpos:"+paintpos+" gridpos:"+gridpos+" oldpos:"+oldpos);
f.FloodFill(map, paintpos.x, paintpos.z,10, 99);
checkAreas();
}
}
oldpos = gridpos;
}
/*
if (Input.GetMouseButtonUp (0))
{
// print ("fill");
f.FloodFill(map, 10, 10, 10, 99);
checkAreas();
}
*/
}
function checkAreas()
{
var found:boolean=false;
var countArea:int=0;
for (var y:int=0;y<texsize;y++)
for (var x:int=0;x<texsize;x++)
{
var val = map[texsize * x + y];
// its still empty, so its inside building (or its wall?)
if (val==0)
{
map[texsize * x + y] = 0;
//tex.SetPixel(x,y,new Color(0,1,0,1));
found=true;
countArea++;
}
//it was filled, clean it up
if (val==99)
{
map[texsize * x + y] = 0;
tex.SetPixel(x,y,new Color(0,0,1,1));
}
}
print ("countArea:"+countArea);
tex.Apply();
}
FloodFiller.cs (put in Plugins/ folder)
using System;
using System.Diagnostics;
using System.Linq;
// orig: http://pastebin.com/KHD8axSL
//namespace FloodFill
//{
//internal class Program
public class FloodFiller
{
// private const int MAP_SIZE = 1024; // only powers of two are supported
/*
private static void Main()
{
var map = new byte[MAP_SIZE * MAP_SIZE];
var stopwatch = Stopwatch.StartNew();
{
FloodFill(map, startX: 123, startY: 123, fromValue: 0, toValue: 1);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
// Test results
if (map.Any(item => item == 0))
{
Console.WriteLine("Error");
}
else
{
Console.WriteLine("Ok");
}
Console.ReadLine();
}
*/
public void FloodFill(byte[] map, int startX, int startY, byte fromValue, byte toValue)
{
int shift = (int) Math.Round(Math.Log(map.Length, 4)); // if the array's length is (2^x * 2^x), then shift = x
int startIndex = startX + (startY << shift);
if (map[startIndex] >= fromValue)
{
return;
}
// initialize flood fill
int size = 1 << shift;
int sizeMinusOne = size - 1;
int xMask = size - 1;
int minIndexForVerticalCheck = size;
int maxIndexForVerticalCheck = map.Length - size - 1;
// initialize queue
int capacity = size * 2;
int mask = capacity - 1;
uint tail = 0;
uint head = 0;
var queue = new int[capacity];
map[startIndex] = toValue;
queue[tail++ & mask] = startIndex;
while (tail - head > 0)
{
int index = queue[head++ & mask];
int x = index & xMask;
//if (x > 0 && map[index - 1] == fromValue)
if (x > 0 && map[index - 1] < fromValue)
{
map[index - 1] = toValue;
queue[tail++ & mask] = index - 1;
}
//if (x < sizeMinusOne && map[index + 1] == fromValue)
if (x < sizeMinusOne && map[index + 1] < fromValue)
{
map[index + 1] = toValue;
queue[tail++ & mask] = index + 1;
}
//if (index >= minIndexForVerticalCheck && map[index - size] == fromValue)
if (index >= minIndexForVerticalCheck && map[index - size] < fromValue)
{
map[index - size] = toValue;
queue[tail++ & mask] = index - size;
}
//if (index <= maxIndexForVerticalCheck && map[index + size] == fromValue)
if (index <= maxIndexForVerticalCheck && map[index + size] < fromValue)
{
map[index + size] = toValue;
queue[tail++ & mask] = index + size;
}
}
}
}
//}
Related Posts
5 Comments + Add Comment
Leave a comment
Recent posts
- LudumDare59 : Signal
- Unity Editor: Tree Generator
- Leaf/Foliage Generator Tools (Runs in Browser)
- Testing Unity AI Beta
- Ways to Support UnityCoder Development
- Using UI Slider to Create 5-Star Rating Element
- Game Music Library For Unity (editor plugin)
- Fontastic : Easily Test Fonts in Unity Editor!
- GeoTiff Importer & Terrain Generator for Unity
- Create Baked DropShadow for UI images
- .JP2 Ortho Image Converter to PNG/JPG/TIFF
- Convert LAS/LAZ/PLY pointclouds to GLTF (GLB) Point Meshes (standalone converter)
Recent Comments
- on Sprite Sheet Flip Book Shader
- on Sprite Sheet Flip Book Shader
- on [Asset Store] PolygonCollider2D Optimizer
- on Trajectory Test Scene 2.0
- on Vector3 maths for dummies!
- on UnityHub 3.6.0: Remove Version Control & Cloud Dashboard columns
- on Using RenderDoc with Unity (graphics debugger)
- on UI Scroll View automatic Content height
Coin:
CUgDSbRqFcAumDSAcdKDvuXsw26VdkJe8C8WGUQHBAGS
An article by













Hi! This looks great!
I’m trying to make a game qix-like too. I have been working about a script I found and it works ok in corona, but I’m new in unity and I’m having problems adapting to c# and all the other unity stuff. Maybe I can help you if you share youre code. My code is based on this:
http://luis.peralta.pt/sandbox/js/flood.html
Thanks!
John
Flood fill stuff from here:
http://unitycoder.com/blog/2012/10/10/flood-fill-algorithm/
(check comments, also that Bitmap Drawing API (free) might be easy to use)
Or which part you are having problems with?
* added those old webplayer demo scripts, would need to remake them..
Thank you for the info and code!
Basically I have problems with the basics of unity I guess. How many GameObjects I need to make this code run? I understand I need one that use mesh with renderer no? What is the target on your code?
Thank you again!
script is attached to player gameobject (cube in the webplayer demo, no collider on it),
target is the plane (used CreatePlane from unity to make “Plane1x1W64L64HBL”)
Ok, I runned! It works well but fills the largest area, it should be the tinniest. I’m doing my investigations on the code. It’s a bit complicated because usually I use a 2 dimensional array and you are using a byte array. The bitwise operations are harder to understand, but I supose It’s a decision made for improving performance…
Thanks!