move performance with quad tree

This commit is contained in:
2016-12-26 02:26:39 +07:00
parent 864727a50d
commit 3e9c6a8377
6 changed files with 242 additions and 143 deletions

View File

@@ -108,7 +108,7 @@ Prefab:
objectReference: {fileID: 0}
- target: {fileID: 417424, guid: 185575a05f87743c0b2ddb83dd39c6cd, type: 2}
propertyPath: m_LocalPosition.z
value: 1
value: 1.2
objectReference: {fileID: 0}
- target: {fileID: 417424, guid: 185575a05f87743c0b2ddb83dd39c6cd, type: 2}
propertyPath: m_LocalRotation.x
@@ -240,9 +240,11 @@ MonoBehaviour:
_startDepth: 1
_showIsoBounds: 0
_showScreenBounds: 0
_showDepends: 1
_snapByCells: 1
_snapByObjects: 1
_showDepends: 1
_showQuadTree: 0
_showScreenGrid: 0
--- !u!1001 &769235453
Prefab:
m_ObjectHideFlags: 0
@@ -424,7 +426,7 @@ Prefab:
objectReference: {fileID: 0}
- target: {fileID: 417424, guid: 185575a05f87743c0b2ddb83dd39c6cd, type: 2}
propertyPath: m_LocalPosition.z
value: 1.2
value: 1
objectReference: {fileID: 0}
- target: {fileID: 417424, guid: 185575a05f87743c0b2ddb83dd39c6cd, type: 2}
propertyPath: m_LocalRotation.x

View File

@@ -15,7 +15,11 @@ namespace IsoTools.Internal {
void GetMinMaxCells (T item, ref IsoPoint2 min, ref IsoPoint2 max);
}
public interface ILookUpper {
public interface IBoundsLookUpper {
void LookUp(IsoRect bounds);
}
public interface IContentLookUpper {
void LookUp(IsoList<T> items);
}
@@ -109,13 +113,35 @@ namespace IsoTools.Internal {
_gridNumPosCount = IsoPoint2.zero;
}
public void LookUpCells(
IsoPoint2 min_cell, IsoPoint2 max_cell, ILookUpper look_upper)
public void VisitAllBounds(IBoundsLookUpper look_upper) {
if ( look_upper == null ) {
throw new System.ArgumentNullException("look_upper");
}
for ( int y = 0, ye = _gridNumPosCount.y; y < ye; ++y ) {
for ( int x = 0, xe = _gridNumPosCount.x; x < xe; ++x ) {
var cell = GetCell(x, y);
if ( cell.Items.Count > 0 ) {
var rect = new IsoRect(
x * _gridCellSize,
y * _gridCellSize,
x * _gridCellSize + _gridCellSize,
y * _gridCellSize + _gridCellSize);
rect.Translate(
_gridMinNumPos.x * _gridCellSize,
_gridMinNumPos.y * _gridCellSize);
look_upper.LookUp(rect);
}
}}
}
public void VisitItemsByCells(
IsoPoint2 min_cell, IsoPoint2 max_cell,
IContentLookUpper look_upper)
{
if ( min_cell.x < 0 || min_cell.y < 0 ) {
throw new System.ArgumentOutOfRangeException("min_cell");
}
if ( min_cell.y >= _gridNumPosCount.x || min_cell.y >= _gridNumPosCount.y ) {
if ( max_cell.x > _gridNumPosCount.x || max_cell.y > _gridNumPosCount.y ) {
throw new System.ArgumentOutOfRangeException("max_cell");
}
if ( look_upper == null ) {

View File

@@ -19,39 +19,7 @@ namespace IsoTools.Internal {
// ---------------------------------------------------------------------
//
// ItemPool
//
// ---------------------------------------------------------------------
class Item {
public Node Owner = null;
public IsoRect Bounds = IsoRect.zero;
public T Content = default(T);
public Item Init(Node owner, IsoRect bounds, T content) {
Owner = owner;
Bounds = bounds;
Content = content;
return this;
}
public Item Clear() {
return Init(null, IsoRect.zero, default(T));
}
}
class ItemPool : IsoPool<Item> {
public ItemPool(int capacity) : base(capacity) {
}
public override Item CreateItem() {
return new Item();
}
}
// ---------------------------------------------------------------------
//
// NodePool
// Node
//
// ---------------------------------------------------------------------
@@ -61,26 +29,47 @@ namespace IsoTools.Internal {
public Node[] Nodes = new Node[4];
public IsoAssocList<Item> Items = new IsoAssocList<Item>(MinChildCountPerNode);
public Node Parent = null;
public IsoRect Bounds = IsoRect.zero;
public IsoRect SelfBounds = IsoRect.zero;
public IsoRect[] NodeBounds = new IsoRect[4];
public Node Init(Node parent, IsoRect bounds) {
Parent = parent;
Bounds = bounds;
Parent = parent;
SelfBounds = bounds;
return FillNodeBounds();
}
public Node Clear(IsoIPool<Node> node_pool, IsoIPool<Item> item_pool) {
public Node Clear(
IsoIPool<Node> node_pool, IsoIPool<Item> item_pool)
{
ClearNodes(node_pool, item_pool);
ClearItems(item_pool);
return Init(null, IsoRect.zero);
}
public bool CleanUpNodes(
IsoIPool<Node> node_pool, IsoIPool<Item> item_pool)
{
var has_any_busy_nodes = false;
for ( int i = 0, e = Nodes.Length; i < e; ++i ) {
var node = Nodes[i];
if ( node != null ) {
if ( node.CleanUpNodes(node_pool, item_pool) ) {
node_pool.Release(
node.Clear(node_pool, item_pool));
Nodes[i] = null;
} else {
has_any_busy_nodes = true;
}
}
}
return !has_any_busy_nodes && Items.Count == 0;
}
public bool AddItem(
IsoRect bounds, T content, out Item item,
IsoIPool<Node> node_pool, IsoIPool<Item> item_pool)
{
if ( !Bounds.Contains(bounds) ) {
if ( !SelfBounds.Contains(bounds) ) {
item = null;
return false;
}
@@ -112,12 +101,14 @@ namespace IsoTools.Internal {
return true;
}
public void RemoveItem(Item item) {
Items.Remove(item);
public void RemoveItem(Item item, IsoIPool<Item> item_pool) {
if ( Items.Remove(item) ) {
item_pool.Release(item.Clear());
}
}
public void VisitAllBounds(IBoundsLookUpper look_upper) {
look_upper.LookUp(Bounds);
look_upper.LookUp(SelfBounds);
for ( int i = 0, e = Nodes.Length; i < e; ++i ) {
if ( Nodes[i] != null ) {
Nodes[i].VisitAllBounds(look_upper);
@@ -125,8 +116,10 @@ namespace IsoTools.Internal {
}
}
public void VisitItemsByBounds(IsoRect bounds, IContentLookUpper look_upper) {
if ( Bounds.Overlaps(bounds) ) {
public void VisitItemsByBounds(
IsoRect bounds, IContentLookUpper look_upper)
{
if ( bounds.Overlaps(SelfBounds) ) {
for ( int i = 0, e = Items.Count; i < e; ++i ) {
var item = Items[i];
if ( bounds.Overlaps(item.Bounds) ) {
@@ -147,8 +140,8 @@ namespace IsoTools.Internal {
//
Node FillNodeBounds() {
var size = Bounds.size * 0.5f;
var center = Bounds.center;
var size = SelfBounds.size * 0.5f;
var center = SelfBounds.center;
{ // LT
var rect = new IsoRect(center - size, center);
NodeBounds[0] = rect;
@@ -174,8 +167,7 @@ namespace IsoTools.Internal {
for ( int i = 0, e = Nodes.Length; i < e; ++i ) {
var node = Nodes[i];
if ( node != null ) {
node_pool.Release(
node.Clear(node_pool, item_pool));
node_pool.Release(node.Clear(node_pool, item_pool));
}
}
System.Array.Clear(Nodes, 0, Nodes.Length);
@@ -184,13 +176,41 @@ namespace IsoTools.Internal {
void ClearItems(IsoIPool<Item> item_pool) {
for ( int i = 0, e = Items.Count; i < e; ++i ) {
var item = Items[i];
item_pool.Release(
item.Clear());
item_pool.Release(item.Clear());
}
Items.Clear();
}
}
// ---------------------------------------------------------------------
//
// Item
//
// ---------------------------------------------------------------------
class Item {
public Node Owner = null;
public IsoRect Bounds = IsoRect.zero;
public T Content = default(T);
public Item Init(Node owner, IsoRect bounds, T content) {
Owner = owner;
Bounds = bounds;
Content = content;
return this;
}
public Item Clear() {
return Init(null, IsoRect.zero, default(T));
}
}
// ---------------------------------------------------------------------
//
// Pools
//
// ---------------------------------------------------------------------
class NodePool : IsoPool<Node> {
public NodePool(int capacity) : base(capacity) {
}
@@ -200,6 +220,15 @@ namespace IsoTools.Internal {
}
}
class ItemPool : IsoPool<Item> {
public ItemPool(int capacity) : base(capacity) {
}
public override Item CreateItem() {
return new Item();
}
}
// ---------------------------------------------------------------------
//
// Members
@@ -225,44 +254,67 @@ namespace IsoTools.Internal {
}
public void AddItem(IsoRect bounds, T content) {
if ( bounds.x.size > 0.0f && bounds.y.size > 0.0f ) {
if ( _allItems.ContainsKey(content) ) {
MoveItem(bounds, content);
} else {
if ( _rootNode == null ) {
var initial_bounds = new IsoRect(
bounds.center - bounds.size * 2.0f,
bounds.center + bounds.size * 2.0f);
_rootNode = _nodePool.Take().Init(null, initial_bounds);
}
Item item;
while ( !_rootNode.AddItem(bounds, content, out item, _nodePool, _itemPool) ) {
GrowUp(
bounds.center.x < _rootNode.Bounds.center.x,
bounds.center.y < _rootNode.Bounds.center.y);
}
_allItems.Add(content, item);
if ( _allItems.ContainsKey(content) ) {
MoveItem(bounds, content);
} else if ( bounds.x.size > 0.0f && bounds.y.size > 0.0f ) {
if ( _rootNode == null ) {
var initial_side = IsoUtils.Vec2From(
IsoUtils.Vec2MaxF(bounds.size));
var initial_bounds = new IsoRect(
bounds.center - initial_side * 2.0f,
bounds.center + initial_side * 2.0f);
_rootNode = _nodePool.Take().Init(null, initial_bounds);
}
Item item;
while ( !_rootNode.AddItem(bounds, content, out item, _nodePool, _itemPool) ) {
GrowUp(
bounds.center.x < _rootNode.SelfBounds.center.x,
bounds.center.y < _rootNode.SelfBounds.center.y);
}
_allItems.Add(content, item);
_rootNode.CleanUpNodes(_nodePool, _itemPool);
}
}
public bool RemoveItem(T content) {
public void RemoveItem(T content) {
Item item;
if ( _allItems.TryGetValue(content, out item) ) {
if ( item.Owner != null ) {
item.Owner.RemoveItem(item);
}
_allItems.Remove(content);
return true;
} else {
return false;
var item_node = item.Owner;
item_node.RemoveItem(item, _itemPool);
if ( item_node.Items.Count == 0 ) {
BackwardNodeCleanUp(item_node);
}
}
}
public void MoveItem(IsoRect bounds, T content) {
//TODO implme
RemoveItem(content);
AddItem(bounds, content);
Item item;
if ( _allItems.TryGetValue(content, out item) ) {
var item_node = item.Owner;
if ( item_node.SelfBounds.Contains(bounds) && item_node.Items.Count <= MinChildCountPerNode ) {
item.Bounds = bounds;
} else {
item_node.RemoveItem(item, _itemPool);
if ( item_node.Items.Count == 0 ) {
item_node = BackwardNodeCleanUp(item_node) ?? _rootNode;
}
while ( item_node != null ) {
Item new_item;
if ( item_node.SelfBounds.Contains(bounds) ) {
if ( item_node.AddItem(bounds, content, out new_item, _nodePool, _itemPool) ) {
_allItems[content] = new_item;
return;
}
}
item_node = item_node.Parent;
}
_allItems.Remove(content);
AddItem(bounds, content);
}
} else {
AddItem(bounds, content);
}
}
public void Clear() {
@@ -299,11 +351,11 @@ namespace IsoTools.Internal {
// ---------------------------------------------------------------------
void GrowUp(bool left, bool top) {
var new_root_bounds = _rootNode.Bounds;
var new_root_bounds = _rootNode.SelfBounds;
new_root_bounds.Translate(
left ? -_rootNode.Bounds.size.x : 0.0f,
top ? -_rootNode.Bounds.size.y : 0.0f);
new_root_bounds.Resize(_rootNode.Bounds.size * 2.0f);
left ? -new_root_bounds.size.x : 0.0f,
top ? -new_root_bounds.size.y : 0.0f);
new_root_bounds.Resize(new_root_bounds.size * 2.0f);
var new_root = _nodePool.Take().Init(null, new_root_bounds);
if ( left ) {
if ( top ) {
@@ -321,5 +373,12 @@ namespace IsoTools.Internal {
_rootNode.Parent = new_root;
_rootNode = new_root;
}
Node BackwardNodeCleanUp(Node node) {
while ( node != null && node.CleanUpNodes(_nodePool, _itemPool) ) {
node = node.Parent;
}
return node;
}
}
}

View File

@@ -7,16 +7,18 @@ using UnityEngine.Profiling;
namespace IsoTools.Internal {
public class IsoScreenSolver {
Vector2 _minIsoXY = Vector2.zero;
IsoAssocList<IsoObject> _oldVisibles = new IsoAssocList<IsoObject>();
IsoAssocList<IsoObject> _curVisibles = new IsoAssocList<IsoObject>();
Vector2 _minIsoXY = Vector2.zero;
IsoAssocList<IsoObject> _oldVisibles = new IsoAssocList<IsoObject>();
IsoAssocList<IsoObject> _curVisibles = new IsoAssocList<IsoObject>();
IsoQuadTree<IsoObject> _quadTree = new IsoQuadTree<IsoObject>(47);
IsoGrid<IsoObject> _screenGrid = new IsoGrid<IsoObject>(new IsoGridItemAdapter(), 47);
IsoQuadTree<IsoObject> _quadTree = new IsoQuadTree<IsoObject>(47);
IsoGrid<IsoObject> _screenGrid = new IsoGrid<IsoObject>(new IsoSGItemAdapter(), 47);
IsoQTBoundsLookUpper _qtBoundsLU = new IsoQTBoundsLookUpper();
IsoQTContentLookUpper _qtContentLU = new IsoQTContentLookUpper();
IsoGridLookUpper _screenGridLU = new IsoGridLookUpper();
IsoQTBoundsLookUpper _qtBoundsLU = new IsoQTBoundsLookUpper();
IsoQTContentLookUpper _qtContentLU = new IsoQTContentLookUpper();
IsoSGBoundsLookUpper _sgBoundsLU = new IsoSGBoundsLookUpper();
IsoSGContentLookUpper _sgContentLU = new IsoSGContentLookUpper();
// ---------------------------------------------------------------------
//
@@ -27,7 +29,10 @@ namespace IsoTools.Internal {
class IsoQTBoundsLookUpper : IsoQuadTree<IsoObject>.IBoundsLookUpper {
public void LookUp(IsoRect bounds) {
#if UNITY_EDITOR
IsoUtils.DrawRect(bounds, Color.blue);
IsoUtils.DrawSolidRect(
bounds,
IsoUtils.ColorChangeA(Color.red, 0.05f),
Color.red);
#endif
}
}
@@ -64,11 +69,11 @@ namespace IsoTools.Internal {
// ---------------------------------------------------------------------
//
// IsoGridItemAdapter
// IsoSGItemAdapter
//
// ---------------------------------------------------------------------
class IsoGridItemAdapter : IsoGrid<IsoObject>.IItemAdapter {
class IsoSGItemAdapter : IsoGrid<IsoObject>.IItemAdapter {
public IsoRect GetBounds(IsoObject item) {
return item.Internal.ScreenBounds;
}
@@ -86,11 +91,28 @@ namespace IsoTools.Internal {
// ---------------------------------------------------------------------
//
// IsoGridLookUpper
// IsoSGBoundsLookUpper
//
// ---------------------------------------------------------------------
class IsoGridLookUpper : IsoGrid<IsoObject>.ILookUpper {
class IsoSGBoundsLookUpper : IsoGrid<IsoObject>.IBoundsLookUpper {
public void LookUp(IsoRect bounds) {
#if UNITY_EDITOR
IsoUtils.DrawSolidRect(
bounds,
IsoUtils.ColorChangeA(Color.green, 0.1f),
Color.green);
#endif
}
}
// ---------------------------------------------------------------------
//
// IsoSGContentLookUpper
//
// ---------------------------------------------------------------------
class IsoSGContentLookUpper : IsoGrid<IsoObject>.IContentLookUpper {
IsoObject _isoObject;
public void LookUp(IsoList<IsoObject> items) {
@@ -102,7 +124,7 @@ namespace IsoTools.Internal {
IsoScreenSolver screen_solver, IsoObject iso_object)
{
_isoObject = iso_object;
screen_solver._screenGrid.LookUpCells(
screen_solver._screenGrid.VisitItemsByCells(
iso_object.Internal.MinGridCell,
iso_object.Internal.MaxGridCell,
this);
@@ -138,6 +160,7 @@ namespace IsoTools.Internal {
_quadTree.AddItem(
iso_object.Internal.ScreenBounds,
iso_object);
_minIsoXY = IsoUtils.Vec2Min(_minIsoXY, iso_object.position);
}
public void OnRemoveInstance(IsoObject iso_object) {
@@ -151,6 +174,7 @@ namespace IsoTools.Internal {
_quadTree.MoveItem(
iso_object.Internal.ScreenBounds,
iso_object);
_minIsoXY = IsoUtils.Vec2Min(_minIsoXY, iso_object.position);
if ( !iso_object.Internal.Dirty && _curVisibles.Contains(iso_object) ) {
iso_object.Internal.Dirty = true;
return true;
@@ -160,21 +184,12 @@ namespace IsoTools.Internal {
#if UNITY_EDITOR
public void OnDrawGizmos(IsoWorld iso_world) {
_quadTree.VisitAllBounds(_qtBoundsLU);
/*
for ( int y = 0, ye = (int)_sectorsNumPosCount.y; y < ye; ++y ) {
for ( int x = 0, xe = (int)_sectorsNumPosCount.x; x < xe; ++x ) {
var sector = FindSector((float)x, (float)y);
if ( sector != null && sector.objects.Count > 0 ) {
var rect = new IsoRect(
(x * _sectorsSize),
(y * _sectorsSize),
(x * _sectorsSize) + _sectorsSize,
(y * _sectorsSize) + _sectorsSize);
rect.Translate(_sectorsMinNumPos * _sectorsSize);
IsoUtils.DrawRect(rect, Color.blue);
}
}}*/
if ( iso_world.isShowQuadTree ) {
_quadTree.VisitAllBounds(_qtBoundsLU);
}
if ( iso_world.isShowScreenGrid ) {
_screenGrid.VisitAllBounds(_sgBoundsLU);
}
}
#endif
@@ -205,7 +220,7 @@ namespace IsoTools.Internal {
public void SetupIsoObjectDepends(IsoObject iso_object) {
ClearIsoObjectDepends(iso_object);
_screenGridLU.LookUpForDepends(this, iso_object);
_sgContentLU.LookUpForDepends(this, iso_object);
}
public void ClearIsoObjectDepends(IsoObject iso_object) {
@@ -237,16 +252,8 @@ namespace IsoTools.Internal {
void ProcessAllInstances(IsoAssocList<IsoObject> instances) {
if ( instances.Count > 0 ) {
_minIsoXY.Set(float.MaxValue, float.MaxValue);
for ( int i = 0, e = instances.Count; i < e; ++i ) {
var iso_object = instances[i];
var object_pos = iso_object.position;
if ( _minIsoXY.x > object_pos.x ) {
_minIsoXY.x = object_pos.x;
}
if ( _minIsoXY.y > object_pos.y ) {
_minIsoXY.y = object_pos.y;
}
if ( !IsoUtils.Vec2Approximately(
iso_object.Internal.LastTrans,
iso_object.Internal.Transform.position) )
@@ -254,8 +261,6 @@ namespace IsoTools.Internal {
iso_object.FixIsoPosition();
}
}
} else {
_minIsoXY.Set(0.0f, 0.0f);
}
}

View File

@@ -542,6 +542,13 @@ namespace IsoTools.Internal {
Handles.DrawLine(point2, point3);
Handles.DrawLine(point3, point0);
}
public static void DrawSolidRect(IsoRect rect, Color face_color, Color outline_color) {
Handles.DrawSolidRectangleWithOutline(
new Rect(rect.x.min, rect.y.min, rect.size.x, rect.size.y),
face_color,
outline_color);
}
#endif
}
}

View File

@@ -297,11 +297,6 @@ namespace IsoTools {
get { return _showScreenBounds; }
set { _showScreenBounds = value; }
}
[SerializeField] bool _showDepends = false;
public bool isShowDepends {
get { return _showDepends; }
set { _showDepends = value; }
}
[SerializeField] bool _snapByCells = true;
public bool isSnapByCells {
get { return _snapByCells; }
@@ -312,6 +307,22 @@ namespace IsoTools {
get { return _snapByObjects; }
set { _snapByObjects = value; }
}
[Header("Development Only")]
[SerializeField] bool _showDepends = false;
public bool isShowDepends {
get { return _showDepends; }
set { _showDepends = value; }
}
[SerializeField] bool _showQuadTree = false;
public bool isShowQuadTree {
get { return _showQuadTree; }
set { _showQuadTree = value; }
}
[SerializeField] bool _showScreenGrid = false;
public bool isShowScreenGrid {
get { return _showScreenGrid; }
set { _showScreenGrid = value; }
}
#endif
// ---------------------------------------------------------------------
@@ -441,17 +452,6 @@ namespace IsoTools {
_screenSolver.OnDrawGizmos(this);
_sortingSolver.OnDrawGizmos();
}
/* QuadTree WIP
void OnDrawGizmos() {
var qt = new IsoQuadTree<IsoObject>(_objects.Count);
for ( int i = 0, e = _objects.Count; i < e; ++i ) {
qt.Insert(_objects[i].Internal.ScreenRect, _objects[i]);
}
qt.VisitAllBounds(rect => {
IsoUtils.DrawRect(rect, Color.green);
});
}*/
#endif
}
}