Created
June 8, 2025 07:18
-
-
Save adammyhre/b94637dc8ee5e272b05375c61da6d161 to your computer and use it in GitHub Desktop.
Unity Memory Arena
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Unity.Collections; | |
using Unity.Collections.LowLevel.Unsafe; | |
public unsafe class ArenaAllocator : IDisposable { | |
byte* buffer; // 'buffer' is a pointer to a byte (i.e., byte*) | |
int offset; | |
readonly int capacity; | |
public ArenaAllocator(int sizeInBytes) { | |
buffer = (byte*)UnsafeUtility.Malloc(sizeInBytes, 16, Allocator.Persistent); | |
offset = 0; | |
capacity = sizeInBytes; | |
} | |
public T* Alloc<T>(int count = 1) where T : unmanaged { | |
int size = UnsafeUtility.SizeOf<T>() * count; | |
if (offset + size > capacity) throw new Exception("Arena overflow"); | |
T* ptr = (T*)(buffer + offset); | |
offset += size; | |
return ptr; | |
} | |
public void Reset() => offset = 0; | |
public void Dispose() { | |
if (buffer != null) { | |
UnsafeUtility.Free(buffer, Allocator.Persistent); | |
buffer = null; | |
} | |
offset = 0; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public unsafe struct CraftNode { | |
public ItemType outputType; | |
public int amountNeeded; | |
public int amountAvailable; | |
public CraftNode** subIngredients; | |
public int subCount; | |
} | |
public unsafe class CraftSimulator { | |
readonly RecipeBook book; | |
readonly Inventory inventory; | |
public CraftSimulator(RecipeBook book, Inventory inventory) { | |
this.book = book; | |
this.inventory = inventory; | |
} | |
public CraftNode* SimulateCraft(ArenaAllocator arena, ItemType item, int amountNeeded) { | |
CraftNode* node = arena.Alloc<CraftNode>(); | |
node->outputType = item; | |
node->amountNeeded = amountNeeded; | |
if (book.TryGetRecipe(item, out var recipe)) { | |
node->subCount = recipe.ingredients.Length; | |
node->subIngredients = (CraftNode**)arena.Alloc<byte>(sizeof(CraftNode*) * node->subCount); | |
int maxCraftable = int.MaxValue; | |
for (int i = 0; i < recipe.ingredients.Length; i++) { | |
var ingredient = recipe.ingredients[i]; | |
int requiredAmount = ingredient.count * amountNeeded; | |
CraftNode* sub = SimulateCraft(arena, ingredient.type, requiredAmount); | |
node->subIngredients[i] = sub; | |
int possible = sub->amountAvailable / ingredient.count; | |
if (possible < maxCraftable) maxCraftable = possible; | |
} | |
node->amountAvailable = maxCraftable; | |
} | |
else { | |
node->subCount = 0; | |
node->subIngredients = null; | |
node->amountAvailable = inventory.GetCount(item); | |
} | |
return node; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Unity.Collections; | |
using Unity.Collections.LowLevel.Unsafe; | |
using UnityEngine; | |
public unsafe class Example : MonoBehaviour { | |
RecipeBook book; | |
Inventory inventory; | |
ArenaAllocator arena; | |
CraftSimulator simulator; | |
void Start() { | |
book = new RecipeBook(); | |
inventory = new Inventory(); | |
arena = new ArenaAllocator(UnsafeUtility.SizeOf<CraftNode>() * 10); | |
simulator = new CraftSimulator(book, inventory); | |
book.Add(new Recipe(ItemType.IronIngot, new[] { | |
new Ingredient { type = ItemType.IronOre, count = 2 } | |
})); | |
book.Add(new Recipe(ItemType.IronSword, new[] { | |
new Ingredient { type = ItemType.IronIngot, count = 3 }, | |
new Ingredient { type = ItemType.Stick, count = 1 } | |
})); | |
inventory.Add(ItemType.IronOre, 10); | |
inventory.Add(ItemType.Stick, 3); | |
CraftNode* root = simulator.SimulateCraft(arena, ItemType.IronSword, 1); | |
Debug.Log($"Can craft {root->outputType}: {root->amountAvailable}/{root->amountNeeded}"); | |
for (int i = 0; i < root->subCount; i++) { | |
CraftNode* sub = root->subIngredients[i]; | |
Debug.Log($" -> {sub->outputType}: {sub->amountAvailable}/{sub->amountNeeded}"); | |
} | |
arena.Reset(); | |
} | |
void OnDestroy() => arena.Dispose(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
public enum ItemType { IronOre, IronIngot, IronSword, Wood, Stick } | |
public struct Ingredient { | |
public ItemType type; | |
public int count; | |
} | |
public class RecipeBook { | |
readonly Dictionary<ItemType, Recipe> recipes = new(); | |
public void Add(Recipe recipe) => recipes[recipe.output] = recipe; | |
public bool TryGetRecipe(ItemType type, out Recipe recipe) => recipes.TryGetValue(type, out recipe); | |
} | |
public class Recipe { | |
public ItemType output; | |
public Ingredient[] ingredients; | |
public Recipe(ItemType output, Ingredient[] ingredients) { | |
this.output = output; | |
this.ingredients = ingredients; | |
} | |
} | |
public class Inventory { | |
readonly Dictionary<ItemType, int> stock = new(); | |
public void Add(ItemType type, int count) { | |
stock.TryAdd(type, 0); | |
stock[type] += count; | |
} | |
public int GetCount(ItemType type) => stock.GetValueOrDefault(type, 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment