├── README.md ├── demo └── Fluxsystem_v0.1.unitypackage └── src ├── Cell.cs ├── Cell.cs.meta ├── Flux.cs ├── Flux.cs.meta ├── LookTarget.cs.meta ├── Spwan.cs.meta ├── flow.cs └── flow.cs.meta /README.md: -------------------------------------------------------------------------------- 1 | # Fluxsystem_Unity 2 | 3 | This is a simple Vectorfield(aka flowfield) for Unity. 4 | ![alt text](http://gilpark.com/wp-content/uploads/2016/06/fluxsystem0.png "Fluxsystem_Unity") 5 | 6 | It is still WIP and supports only 2D, which means only **X** and **Z** positions of the object are updated on the field. 7 | 3D Vectorfield will soon be implemented. 8 | More information about [vectorfield / goal based path finding](http://gamedevelopment.tutsplus.com/tutorials/understanding-goal-based-vector-field-pathfinding--gamedev-9007) 9 | 10 | The system consists of 3 parts: 11 | * ***Fluxsystem class*** is the main class that initiates & calculates the field. 12 | * ***Cell class*** is holding infomation of its & neighbors' location in the array. 13 | * ***flow class*** is for looking up vector in the cell in the field and updates object's position. 14 | 15 | [![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/7XekR9gT9Rk/0.jpg)](http://www.youtube.com/watch?v=7XekR9gT9Rk) 16 | 17 | -------------------------------------------------------------------------------- /demo/Fluxsystem_v0.1.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilpark/Fluxsystem_Unity/eb7ea6d884c1b9024002b84acf443d72dae422c9/demo/Fluxsystem_v0.1.unitypackage -------------------------------------------------------------------------------- /src/Cell.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class Cell : MonoBehaviour { 5 | public Vector3 direction, size; 6 | public bool isPassable = true; 7 | public bool isGoal = false; 8 | public bool ready = false; 9 | public bool block = false; 10 | public int[] neighbors = new int[4]; 11 | public int id, cols, rows; 12 | public float cost; 13 | 14 | //public int north,east,south,west; 15 | void Start () { 16 | gameObject.tag = "flux"; 17 | } 18 | 19 | public void reset(){ 20 | cost = 0; 21 | isGoal = false; 22 | isPassable = true; 23 | } 24 | 25 | // Update is called once per frame 26 | void Update () { 27 | ready = true; 28 | } 29 | 30 | void OnTriggerEnter(Collider other){ 31 | 32 | if(other.tag =="block"){ 33 | block = true; 34 | isPassable = false; 35 | } 36 | 37 | if(other.tag =="goal"){ 38 | isGoal = true; 39 | cost = 0; 40 | } 41 | } 42 | 43 | 44 | void OnDrawGizmos(){ 45 | Gizmos.matrix = transform.localToWorldMatrix; 46 | BoxCollider box = gameObject.GetComponent(); 47 | if(Flux.flux.GetComponent().debug){ 48 | Gizmos.color = new Color(0,(float)(cost * 0.1),0,0.8F); 49 | Gizmos.DrawCube(box.center, size); 50 | Gizmos.color = new Color((float)(cost * 0.1),(float)(cost * 0.1),0,0.8F); 51 | Gizmos.DrawLine(box.center, box.center+direction); 52 | } 53 | //Debug.DrawLine(box.center, box.center+direction,Color.red); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Cell.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 28482b1df0f7443369ec009a610dc95a 3 | timeCreated: 1465703071 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /src/Flux.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | public class Flux : MonoBehaviour { 5 | 6 | public static GameObject flux; 7 | public int _columns =1; 8 | public int _rows = 1; 9 | public bool debug = false; 10 | Cell[] tiles,temp_tiles; 11 | //GameObject Goal = new GameObject("GOAL"); 12 | private List visitList; 13 | private bool run = false; 14 | 15 | //private float cell_width,cell_height,cell_depth; 16 | 17 | /* 18 | Pre-drawing the feild 19 | */ 20 | 21 | void _drawTiles(){ 22 | //_temp_cells = new List(); 23 | float cell_width = (float) (_columns * 0.1); 24 | float cell_height = (float) (_rows * 0.1); 25 | float feild_width = cell_width * _columns; 26 | float feild_height = cell_height * _rows; 27 | 28 | 29 | for (int id =0; id < _columns*_rows; id++){ 30 | int _x = id%_columns; 31 | int _z = id/_columns; 32 | 33 | float cell_x = Vector3.zero.x + (_x * cell_width); 34 | float cell_z = Vector3.zero.z + (_z * cell_height); 35 | _drawRect(cell_width, cell_height, cell_x, cell_z); 36 | } 37 | _drawBoundary(feild_width, feild_height, (float)(cell_width* 0.5)); 38 | } 39 | 40 | void _drawBoundary(float width, float height, float depth){ 41 | Gizmos.color = new Color(0.9F,0.9F,0.0F,1.0F); 42 | //down 43 | Gizmos.DrawLine(new Vector3(0,-depth,0), new Vector3(width,-depth,0)); 44 | Gizmos.DrawLine(new Vector3(width,-depth,0), new Vector3(width,-depth,height)); 45 | Gizmos.DrawLine(new Vector3(width,-depth,height), new Vector3(0,-depth, height)); 46 | Gizmos.DrawLine(new Vector3(0,-depth, height), new Vector3(0,-depth,0)); 47 | 48 | //up 49 | Gizmos.DrawLine(new Vector3(0,depth,0), new Vector3(width,depth,0)); 50 | Gizmos.DrawLine(new Vector3(width,depth,0), new Vector3(width,depth,height)); 51 | Gizmos.DrawLine(new Vector3(width,depth,height), new Vector3(0,depth, height)); 52 | Gizmos.DrawLine(new Vector3(0,depth, height), new Vector3(0,depth,0)); 53 | } 54 | 55 | void _drawRect(float w, float h, float x, float z){ 56 | //float x = Vector3.zero.x; 57 | float y = Vector3.zero.y; 58 | //float z = Vector3.zero.z; 59 | Gizmos.color = new Color(0.0F,0.8F,0.8F,0.2F); 60 | Gizmos.DrawLine(new Vector3(x, y, z), new Vector3(x + w, y, z)); 61 | Gizmos.DrawLine(new Vector3(x + w, y, z), new Vector3(x + w, y, z + h)); 62 | Gizmos.DrawLine(new Vector3(x + w, y, z + h), new Vector3(x, y, z + h)); 63 | Gizmos.DrawLine(new Vector3(x, y, z + h), new Vector3(x, y, z)); 64 | } 65 | 66 | /* 67 | Setup/initialization the feild 68 | */ 69 | 70 | void _initFeild(){ 71 | 72 | tiles = new Cell[_columns * _rows]; //each cells 73 | //temp_tiles = new Cell[_columns * _rows]; 74 | visitList = new List(); 75 | 76 | for(int id=0; id < _columns * _rows; id++){ 77 | int _x = id%_columns; 78 | //int _y = 0; // for the later update in case of making 3d vector field 79 | int _z = id/_columns; 80 | 81 | float cell_width = (float)(_columns * 0.1 * transform.localScale.x); //x 82 | float cell_height = (float) (_rows * 0.1 * transform.localScale.z); //z 83 | float cell_depth = (float) (_columns * 0.1 * transform.localScale.y); //y 84 | 85 | float cell_x = Vector3.zero.x + (_x * cell_width); 86 | float cell_y = Vector3.zero.y; // for the later update in case of making 3d vector field 87 | float cell_z = Vector3.zero.z + (_z * cell_height); 88 | 89 | Vector3 cell_vec3 = new Vector3(cell_x,cell_y,cell_z); 90 | Vector3 cell_size = new Vector3(cell_width, cell_depth, cell_height); 91 | 92 | _createCells(_x, _z,id, cell_vec3, cell_size); //creating cells and adding to array 93 | 94 | } 95 | } 96 | 97 | /* 98 | Setup/initialization each cells 99 | */ 100 | void _createCells(int x, int y,int id, Vector3 pos, Vector3 size ){ 101 | 102 | GameObject tile = new GameObject(id+"|" + x + "|"+y); 103 | tile.tag = "flux"; 104 | 105 | tile.transform.parent = Flux.flux.transform; 106 | tile.transform.localPosition = new Vector3(0,0,0); 107 | 108 | BoxCollider tile_Collider = tile.AddComponent(); 109 | Rigidbody tile_rigidbody = tile.AddComponent(); 110 | tile_rigidbody.useGravity = false; 111 | tile_Collider.center = new Vector3(pos.x+(float)(size.x*0.5), pos.y, pos.z+(float)(size.z*0.5)); 112 | tile_Collider.size = size; 113 | tile_Collider.isTrigger = true; 114 | 115 | tile.AddComponent(); 116 | 117 | Cell cell = tile.GetComponent(); 118 | tiles[id] = cell; 119 | cell.size = size; 120 | cell.id = id; 121 | cell.cols = _columns; 122 | cell.rows = _rows; 123 | _setNeighbors(id, cell); 124 | //copy tiles to temp 125 | //temp_tiles[id] = tiles[id]; 126 | 127 | } 128 | 129 | void _setNeighbors(int id, Cell c){ 130 | int column = id%_columns; 131 | int row = id/_columns; 132 | if (column == 0) { 133 | c.neighbors[1] = row * _columns + column+1; 134 | c.neighbors[3] = -1; //left 135 | } 136 | if (column == _columns-1) { 137 | c.neighbors[1] = -1; //if right one is out of boundary 138 | c.neighbors[3] = row * _columns + column-1; 139 | } 140 | if (row == 0 ) { 141 | c.neighbors[2] = -1; //if upper one is out of boundary 142 | c.neighbors[0] = (row+1) * _columns + column; 143 | } 144 | if (row == _rows-1) { 145 | c.neighbors[2] = (row-1) * _columns + column; 146 | c.neighbors[0] = -1; //down 147 | } 148 | if (column > 0 && column < _columns-1) { // if they aer in the boundary 149 | c.neighbors[1] = row * _columns + column+1; 150 | c.neighbors[3] = row * _columns + column-1; 151 | } 152 | if (row > 0 && row < _rows-1) { 153 | c.neighbors[2] = (row-1) * _columns + column; 154 | c.neighbors[0] = (row+1) * _columns + column; 155 | } 156 | } 157 | 158 | 159 | //@ todo flux(id) --> reset the field --> for moving goals; 160 | void _Flux(int id){ //this is called when Goal-cell is triggered 161 | 162 | visitList.Add(id); 163 | while (visitList.Count != 0) { 164 | int front = visitList[0]; //get the first one, we are always evalutating the first one 165 | Cell target = tiles[front]; 166 | 167 | _visitNeighbors(target); //Negative val 168 | 169 | 170 | visitList.RemoveAt(0); 171 | } 172 | visitList.Clear();//clear it every cycle; 173 | 174 | 175 | } 176 | void _visitNeighbors(Cell center) { 177 | 178 | center.isPassable = false; 179 | 180 | int step = 1; 181 | for (int i = 0; i < center.neighbors.Length; i++) { 182 | 183 | if (center.neighbors[i] < 0 ) continue; // check the boundary, OoB marked as -1 184 | //Cell t = temp_tiles[center.neighbors[i]]; 185 | Cell t = tiles[center.neighbors[i]]; 186 | if(t.block){ 187 | center.neighbors[i] = -1; 188 | // t.GetComponent ().constraints = RigidbodyConstraints.FreezeAll; 189 | // t.GetComponent ().isTrigger = false; 190 | 191 | } 192 | if (!t.isPassable || t.isGoal)continue; 193 | t.cost = center.cost + step; 194 | if (t.isPassable) { 195 | t.isPassable=false; 196 | visitList.Add(t.id);// add it self to the visitied list to find its neighbors 197 | } 198 | } 199 | } 200 | 201 | void _calculateDirection(Cell center) { 202 | //Cell center = (Cell) _center.GetComponent(typeof (Cell)); 203 | float X, Y; 204 | float N=0, E=0, S=0, W = 0; 205 | if (center.neighbors[0]>0){N = tiles[center.neighbors[0]].cost;} 206 | else if(center.neighbors[0]<0){N = center.cost + 1;} 207 | 208 | if (center.neighbors[1]>0){E = tiles[center.neighbors[1]].cost;} 209 | else if(center.neighbors[1]<0){E = center.cost + 1;} 210 | 211 | if (center.neighbors[2]>0){S = tiles[center.neighbors[2]].cost;} 212 | else if(center.neighbors[2]<0){S = center.cost + 1;} 213 | 214 | if (center.neighbors[3]>0){W = tiles[center.neighbors[3]].cost;} 215 | else if(center.neighbors[3]<0){W = center.cost + 1;} 216 | 217 | X = W-E; 218 | Y = S-N; 219 | 220 | //center.direction.set((-Y+X/2), (X+Y/2)); 221 | //center.direction.set((-Y+X/2+X/2), (X+Y/2+Y/2)); 222 | //check this article if interested 223 | //http://www.math.uic.edu/coursepages/math210/labpages/lab7 224 | 225 | if(center.block){ 226 | center.direction = new Vector3(0,0,0); 227 | }else center.direction = new Vector3(X,0,Y); 228 | } 229 | 230 | 231 | void Start () { 232 | flux = gameObject; 233 | _initFeild();//initialize feild 234 | StartCoroutine(WaitAndRun()); 235 | } 236 | public IEnumerator WaitAndRun() { 237 | 238 | yield return new WaitForSeconds(3); //wait till all cells are ready 239 | 240 | print(" is WaitedAndRun " + Time.time); 241 | for(int id = 0; id < tiles.Length; id++){ 242 | 243 | if(tiles[id].isGoal){ 244 | _Flux(id); 245 | print("id : "+id +"is processing"); 246 | } 247 | 248 | } 249 | foreach (Cell t in tiles){//calculate directions 250 | _calculateDirection(t); 251 | } 252 | run = true; 253 | } 254 | void Update () { 255 | 256 | } 257 | void OnDrawGizmos() { 258 | Gizmos.matrix = transform.localToWorldMatrix; 259 | if(!run){ 260 | 261 | _drawTiles(); 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/Flux.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3db5e9782d504f7cb9bcddd1a28343e 3 | timeCreated: 1465703071 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /src/LookTarget.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec81ac2acc2774472ada088a5079cecd 3 | timeCreated: 1465791460 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /src/Spwan.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 082b56680ee7f4cd981ba82030e26ec3 3 | timeCreated: 1465703071 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /src/flow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class flow : MonoBehaviour { 5 | Vector3 location; 6 | Vector3 acceleration = new Vector3(0,0,0); 7 | Vector3 velocity = new Vector3(0,0,0); 8 | 9 | public float maxforce; // Maximum steering force 10 | public float maxspeed; // Maximum speed 11 | public bool dead = false; 12 | public int ageMax = 300; 13 | int age = 0; 14 | public bool aging = false; 15 | private Rigidbody rb; 16 | // Use this for initialization 17 | void Start () { 18 | 19 | rb = gameObject.GetComponent (); 20 | //location = transform.position; 21 | location = rb.position; 22 | age = ageMax ; 23 | } 24 | 25 | // Update is called once per frame 26 | void Update () { 27 | velocity = velocity + acceleration; 28 | velocity =Vector3.ClampMagnitude(velocity, maxspeed); 29 | 30 | //transform.position = transform.position + velocity; 31 | rb.MovePosition(rb.position + velocity); 32 | acceleration = acceleration * 0; 33 | if (aging) { 34 | age--; 35 | if (age < 0) 36 | dead = true; 37 | } 38 | 39 | } 40 | void follow(Cell cell){ 41 | Vector3 desired = cell.direction; 42 | desired = desired * maxspeed; 43 | Vector3 steer = desired - velocity; 44 | steer = Vector3.ClampMagnitude(steer,maxforce); 45 | applyForce(steer); 46 | //print("on the flow! - dsrd :" + desired + " steer : "+steer); 47 | } 48 | void applyForce(Vector3 force){ 49 | acceleration = acceleration + force; 50 | //print("acceleration :" + acceleration ); 51 | } 52 | void OnTriggerEnter(Collider other){ 53 | 54 | if(other.tag =="flux"){ 55 | Cell c = other.GetComponent(); 56 | follow(c); 57 | if (dead) { 58 | Destroy (gameObject); 59 | Destroy (this); 60 | } 61 | } 62 | 63 | if(other.tag =="destroy"){ 64 | aging = true; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/flow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ce0a26bd380642b89d68ef505888c3c 3 | timeCreated: 1465703071 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | --------------------------------------------------------------------------------