From a question I posted, a gentleman suggested that I ask on the forums. http://answers.unity3d.com/questions/882719/barnes-hut.html Ok I found http://beltoforion.de/barnes-hut/barnes-hut_en.html which actually is quite helpful. I am reading up on quad trees and im just wondering if unity has such a data structure?
Ok I honestly at this point have no idea how to implement this. I understand how it works however coding it seems to be a major problem (the quadtree). The force of gravity and center of mass part is very simple which I already done but brute forcing is causing it to be pretty inefficient (O(N^2))
Well if I have 1000 stars that is at most about 10000 planets and 50000 moons therefore, 61000 objects. This number would be much smaller of course but Barnes-hut should be able to easily handle that amount. I think the confusion comes from the quad tree itself but I can't find any code or good articles that I can study. They all seem to be conceptual or I'm not a good google searcher... I don't want any exact implementation just some detailed articles or tutorials so I can learn the material by filling in the blank (for example pseudo code)
http://www.flipcode.com/archives/Octree_Implementation.shtml You can easily convert this to c# Does it have to work in real time? I suspect even with Barnes-hut it would be possible in real time for 61000 particles.
Ok I looked at a java implementation and rewrote the BarnesHut to work with my main NBody gravity manager for all of my objects. I get performance issues for a few seconds due to probably loading the tree and stack-overflow error at x>~700 objects(mono-develop points to the insert function of the BarnesHutTree class when overflow happens). I think the tree's recursion might never end so its quickly using up all memory. And Yes..Real Time Im going to try out the link TiG, Thanks! Spoiler: Code Code (CSharp): using UnityEngine; using System.Collections; using BHTree; namespace BHTree { public class BarnesHutTree { public double Theta = 0.1; public Body body; public Quad quad; public BarnesHutTree NW; public BarnesHutTree NE; public BarnesHutTree SW; public BarnesHutTree SE; public BarnesHutTree(Quad quad) { this.quad = quad; this.body = null; this.NW = null; this.NE = null; this.SW = null; this.SE = null; } public void insert(Body b) { if(this.body == null) { body = b; return; } if(! isExternal()) { body = body.plus(b); putBody(b); } else { NW = new BarnesHutTree(quad.NW()); NE = new BarnesHutTree(quad.NE()); SE = new BarnesHutTree(quad.SE()); SW = new BarnesHutTree(quad.SW()); // recursively insert both this body and Body b into the appropriate quadrant putBody(this.body); putBody(b); // update the center-of-mass and total mass body = body.plus(b); } } public void putBody(Body b) { if (b.inCheck(quad.NW())) NW.insert(b); else if (b.inCheck(quad.NE())) NE.insert(b); else if (b.inCheck(quad.SE())) SE.insert(b); else if (b.inCheck(quad.SW())) SW.insert(b); } public bool isExternal() { // a node is external iff all four children are null return (NW == null && NE == null && SW == null && SE == null); } public void updateForce(Body b) { if (body == null || b.Equals(body)) return; // if the current node is external, update net force acting on b if (isExternal()) b.addForce(body); // for internal nodes else { // width of region represented by internal node double s = quad.getLength(); // distance between Body b and this node's center-of-mass double d = body.distanceTo(b); // compare ratio (s / d) to threshold value Theta if ((s / d) < Theta) b.addForce(body); // b is far away // recurse on each of current node's children else { NW.updateForce(b); NE.updateForce(b); SW.updateForce(b); SE.updateForce(b); } } } } public class Quad { public double xmid; public double ymid; public double length; public Quad(double xmid, double ymid, double length) { this.xmid = xmid; this.ymid = ymid; this.length = length; } public double getLength() { return length; } public bool contains(double x, double y) { double halfLen = this.length / 2.0; return (x <= this.xmid + halfLen && x >= this.xmid - halfLen && y <= this.ymid + halfLen && y >= this.ymid - halfLen); } public Quad NW() { double x = this.xmid - this.length / 4.0; double y = this.ymid + this.length / 4.0; double len = this.length / 2.0; Quad NW = new Quad(x, y, len); return NW; } public Quad NE() { double x = this.xmid + this.length / 4.0; double y = this.ymid + this.length / 4.0; double len = this.length / 2.0; Quad NE = new Quad(x, y, len); return NE; } public Quad SW() { double x = this.xmid - this.length / 4.0; double y = this.ymid - this.length / 4.0; double len = this.length / 2.0; Quad SW = new Quad(x, y, len); return SW; } public Quad SE() { double x = this.xmid + this.length / 4.0; double y = this.ymid - this.length / 4.0; double len = this.length / 2.0; Quad SE = new Quad(x, y, len); return SE; } } public class Body { public double G; public double rx, ry; // position public double fx, fy; // force public double mass; // mass public Body(double rx, double ry, double mass, double G) { this.rx = rx; this.ry = ry; this.mass = mass; this.G = G; } public void update(double rx, double ry) { this.rx = rx; this.ry = ry; } public double distanceTo(Body b) { double dx = rx - b.rx; double dy = ry - b.ry; return Mathf.Sqrt((float)(dx*dx + dy*dy)); } public void resetForce() { fx = 0.0; fy = 0.0; } public void addForce(Body b) { if(distanceTo(b) > 0.75f) { Body a = this; double dx = b.rx - a.rx; double dy = b.ry - a.ry; double dist = Mathf.Sqrt((float)(dx*dx + dy*dy)); double F = (G * a.mass * b.mass) / (dist*dist); a.fx += F * dx / dist; a.fy += F * dy / dist; } } public bool inCheck(Quad q) { return q.contains(this.rx, this.ry); } public Body plus(Body b) { Body a = this; double m = a.mass + b.mass; double x = (a.rx * a.mass + b.rx * b.mass) / m; double y = (a.ry * a.mass + b.ry * b.mass) / m; return new Body(x, y, m, a.G); } } } Spoiler: More Code Code (CSharp): using UnityEngine; using System.Collections; using System.Collections.Generic; using BHTree; public class NBody : MonoBehaviour { public GameObject planetPrefab01; public double dt = 0.1; public double radius = 1000; public int N = 5; public double G = 1; private Body[] nBodies; private GameObject[] pBodies; void Start() { Initialize(); } void FixedUpdate() { Quad quad = new Quad(0, 0, radius * 2); BarnesHutTree tree = new BarnesHutTree(quad); pBodies = GameObject.FindGameObjectsWithTag("Celestial Object"); for(int i = 0; i < N; i++) { if(nBodies[i].inCheck(quad)) { tree.insert(nBodies[i]); } } for(int i = 0; i < N; i++) { nBodies[i].resetForce(); tree.updateForce(nBodies[i]); nBodies[i].update(pBodies[i].transform.position.x, pBodies[i].transform.position.y); } for(int i = 0; i < N; i++) { Vector2 force = new Vector2((float)nBodies[i].fx, (float)nBodies[i].fy); pBodies[i].rigidbody2D.AddForce(force); } } void Initialize() { List<int> places = new List<int>(); bool check = true; for(int i = 0; i < N; i++) { check = true; while(check) { int x = Random.Range(0,(int)radius); int y = Random.Range(0,(int)radius); if(!places.Contains(x + y)) { places.Add(x + y); Instantiate(planetPrefab01, new Vector3(x, y) , Quaternion.identity); check = false; } } } pBodies = GameObject.FindGameObjectsWithTag("Celestial Object"); nBodies = new Body[N]; for(int i = 0; i < N; i++) { double px = pBodies[i].transform.position.x; double py = pBodies[i].transform.position.y; double mass = pBodies[i].rigidbody2D.mass; nBodies[i] = new Body(px, py, mass, G); } } }