This commit is contained in:
DCFApixels 2024-11-18 17:42:52 +08:00
parent 50a5fb3b30
commit 487357a9de
4 changed files with 136 additions and 44 deletions

View File

@ -1,5 +1,5 @@
<p align="center">
<img width="400" src="https://github.com/user-attachments/assets/e2ae19e1-b121-46a2-94bc-eabf7378071b">
<img width="400" src="https://github.com/user-attachments/assets/4c1aaeea-7283-4980-b447-a3bc7e54aeb7">
</p>
<p align="center">
@ -34,3 +34,40 @@
</table>
</br>
Реализация связи сущностей в виде графа, где дугами выступают связывающие сущности.
> [!WARNING]
> Проект в стадии разработки. API может меняться.
# Оглавление
- [Установка](#установка)
- [Инициализация](#инициализация)
</br>
# Установка
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Окружение
Обязательные требования:
+ Зависимость: [DragonECS](https://github.com/DCFApixels/DragonECS)
+ Минимальная версия C# 7.3;
Опционально:
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.
Протестировано:
+ **Unity:** Минимальная версия 2020.1.0;
## Установка для Unity
* ### Unity-модуль
Поддерживается установка в виде Unity-модуля в при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) или ручного добавления в `Packages/manifest.json`:
```
https://github.com/DCFApixels/DragonECS-Graphs.git
```
* ### В виде исходников
Пакет так же может быть добавлен в проект в виде исходников.
</br>
# Инициализация

View File

@ -34,3 +34,36 @@
</table>
</br>
Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text
> [!WARNING]
> The project is a work in progress, API may change.
>
> While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS-Graphs/blob/main/README-RU.md).
</br>
# Installation
Versioning semantics - [Open](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Environment
Requirements:
+ Dependency: [DragonECS](https://github.com/DCFApixels/DragonECS)
+ Minimum version of C# 7.3;
Optional:
+ Game engines with C#: Unity, Godot, MonoGame, etc.
Tested with:
+ **Unity:** Minimum version 2020.1.0;
## Unity Installation
* ### Unity Package
The package can be installed as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) or manually adding it to `Packages/manifest.json`:
```
https://github.com/DCFApixels/DragonECS-Graphs.git
```
* ### Source Code
The package can also be added to the project as source code.
</br>

View File

@ -90,11 +90,11 @@ namespace DCFApixels.DragonECS.Graphs.Internal
if (Mask.IsEmpty || _versionsChecker.CheckAndNext() == false)
{
_filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities);
//Подготовка массивов
if (_sourceEntities.Length < _filteredAllEntitiesCount * 2)
if (_sourceEntities.Length < _graph.World.Capacity * 2)
{
_sourceEntities = new int[_filteredAllEntitiesCount * 2];
_sourceEntities = new int[_graph.World.Capacity * 2];
}
//Подготовка массивов
}
@ -103,9 +103,9 @@ namespace DCFApixels.DragonECS.Graphs.Internal
_currentFilteredEntitiesCount = _filteredAllEntitiesCount;
//Подготовка массивов
if (_targetWorldCapacity < World.Capacity)
if (_targetWorldCapacity < _graph.World.Capacity)
{
_targetWorldCapacity = World.Capacity;
_targetWorldCapacity = _graph.World.Capacity;
_linkedListSourceHeads = new LinkedListHead[_targetWorldCapacity];
//_startEntities = new int[_targetWorldCapacity];
}

View File

@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using TValue = System.Int32;
namespace DCFApixels.DragonECS.Graphs.Internal
@ -10,12 +11,13 @@ namespace DCFApixels.DragonECS.Graphs.Internal
public const int MIN_CAPACITY_BITS_OFFSET = 4;
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
private const int MAX_CHAIN_LENGTH = 5;
private const int CHAIN_LENGTH_THRESHOLD = 5;
private const float CHAIN_LENGTH_THRESHOLD_CAPCITY_THRESHOLD = 0.7f;
private UnsafeArray<Basket> _buckets;
private UnsafeArray<Entry> _entries;
private int _capacity;
private int _count_Threshold;
private int _count;
@ -54,7 +56,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
_freeList = 0;
_freeCount = 0;
_capacity = minCapacity;
SetCapacity(minCapacity);
}
#endregion
@ -69,7 +71,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
Throw.ArgumentException("Has(x, y) is true");
}
#endif
int targetBucket = key.yHash & _modBitMask;
int targetBucket = key.YHash & _modBitMask;
AddInternal(key, targetBucket, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -80,7 +82,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
{
return false;
}
int targetBucket = key.yHash & _modBitMask;
int targetBucket = key.YHash & _modBitMask;
AddInternal(key, targetBucket, value);
return true;
}
@ -88,7 +90,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
public void Set(int x, int y, TValue value)
{
Key key = Key.FromXY(x, y);
int targetBucket = key.yHash & _modBitMask;
int targetBucket = key.YHash & _modBitMask;
for (int i = _buckets[targetBucket].index; i >= 0; i = _entries[i].next)
{
@ -109,9 +111,11 @@ namespace DCFApixels.DragonECS.Graphs.Internal
if (_count == _capacity)
{
Resize();
targetBucket = key.yHash & _modBitMask;
// обновляем под новое значение _modBitMask
targetBucket = key.YHash & _modBitMask;
}
index = _count++;
index = Interlocked.Increment(ref _count);
//index = _count++;
}
else
{
@ -130,10 +134,13 @@ namespace DCFApixels.DragonECS.Graphs.Internal
entry.next = basket.index;
entry.key = key;
entry.value = value;
basket.count++;
Interlocked.Increment(ref basket.count);
//basket.count++;
basket.index = index;
if (basket.count >= MAX_CHAIN_LENGTH && Count / Capacity >= 0.7f)
if (basket.count >= CHAIN_LENGTH_THRESHOLD &&
_count > _count_Threshold)
//_count / _capacity >= CHAIN_LENGTH_THRESHOLD_CAPCITY_THRESHOLD)
{
Resize();
}
@ -145,7 +152,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
private int FindEntry(int x, int y)
{
Key key = Key.FromXY(x, y);
for (int i = _buckets[key.yHash & _modBitMask].index; i >= 0; i = _entries[i].next)
for (int i = _buckets[key.YHash & _modBitMask].index; i >= 0; i = _entries[i].next)
{
if (_entries[i].key == key)
{
@ -157,7 +164,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindEntry(Key key)
{
for (int i = _buckets[key.yHash & _modBitMask].index; i >= 0; i = _entries[i].next)
for (int i = _buckets[key.YHash & _modBitMask].index; i >= 0; i = _entries[i].next)
{
if (_entries[i].key == key)
{
@ -202,7 +209,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
public bool TryDel(int x, int y)
{
Key key = Key.FromXY(x, y);
int targetBucket = key.yHash & _modBitMask;
int targetBucket = key.YHash & _modBitMask;
ref Basket basket = ref _buckets[targetBucket];
int last = -1;
@ -269,10 +276,10 @@ namespace DCFApixels.DragonECS.Graphs.Internal
UnsafeArray<Entry> newEntries = UnsafeArray<Entry>.Resize(_entries, newSize);
for (int i = 0; i < _count; i++)
{
if (newEntries[i].key.x >= 0)
if (newEntries[i].key.X >= 0)
{
ref Entry entry = ref newEntries[i];
ref Basket basket = ref newBuckets[entry.key.yHash & _modBitMask];
ref Basket basket = ref newBuckets[entry.key.YHash & _modBitMask];
entry.next = basket.index;
basket.index = i;
basket.count++;
@ -282,7 +289,13 @@ namespace DCFApixels.DragonECS.Graphs.Internal
_buckets = newBuckets;
_entries = newEntries;
SetCapacity(newSize);
}
private void SetCapacity(int newSize)
{
_capacity = newSize;
_count_Threshold = (int)(_count_Threshold * CHAIN_LENGTH_THRESHOLD_CAPCITY_THRESHOLD);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -301,7 +314,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal
public int next; // Index of next entry, -1 if last
public Key key;
public TValue value;
public override string ToString() { return key.x == 0 ? "NULL" : $"{key} {value}"; }
public override string ToString() { return key.X == 0 ? "NULL" : $"{key} {value}"; }
}
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
@ -311,54 +324,63 @@ namespace DCFApixels.DragonECS.Graphs.Internal
public int index;
public int count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Basket(int index, int length)
public Basket(int index, int count)
{
this.index = index;
this.count = length;
this.count = count;
}
public override string ToString() { return index < 0 ? "NULL" : $"{index} {count}"; }
}
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)]
[StructLayout(LayoutKind.Explicit, Pack = 4, Size = 8)]
public readonly struct Key : IEquatable<Key>
{
public static readonly Key Null = new Key(-1, 0);
public readonly int x;
public readonly int yHash;
[FieldOffset(0)]
public readonly long Full;
[FieldOffset(0)]
public readonly int X;
[FieldOffset(4)]
public readonly int YHash;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Key(int x, int yHash)
private Key(int x, int yHash) : this()
{
this.x = x;
this.yHash = yHash;
this.X = x;
this.YHash = yHash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Key FromXY(int x, int y)
public static unsafe Key FromXY(int x, int y)
{
unchecked
{
return new Key(x, x ^ y ^ XXX(y));
return new Key(x, x ^ y ^ Mixing(y));
}
}
private static int XXX(int x)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Mixing(int x)
{
x *= 3571;
x ^= x << 13;
x ^= x >> 17;
return x;
unchecked
{
x *= 3571;
x ^= x << 13;
x ^= x >> 17;
return x;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool EqualsInFind(Key a, Key b) { return a.x == b.x; }
internal static bool EqualsInFind(Key a, Key b) { return a.X == b.X; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Key a, Key b) { return a.x == b.x && a.yHash == b.yHash; }
public static bool operator ==(Key a, Key b) { return a.X == b.X && a.YHash == b.YHash; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Key a, Key b) { return a.x != b.x || a.yHash != b.yHash; }
public static bool operator !=(Key a, Key b) { return a.X != b.X || a.YHash != b.YHash; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() { return yHash; }
public override int GetHashCode() { return YHash; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Key other) { return this == other; }
public override bool Equals(object obj) { return obj is Key && Equals((Key)obj); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString() { return $"({x}, {yHash})"; }
public override string ToString() { return $"({X}, {YHash})"; }
}
#endregion
}