Added Arch Persitance and Relationships, styackable items, hot fixed Tags.containsAll. AutoList in Ordered!DList
This commit is contained in:
@@ -12,8 +12,8 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Export] public int Width { get; set; } = default;
|
||||
public IVoxelGridRegistry Value() => Grid;
|
||||
[Dependency] protected IVoxelGridRegistry Grid => this.DependOn<IVoxelGridRegistry>();
|
||||
public IVoxelGridRegistry Value() => FoodFactoryApi.GridRegistry;
|
||||
[Dependency] protected IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
|
||||
private VoxelGuid _guid = default;
|
||||
public GridTransform3D VoxelTransform
|
||||
{
|
||||
@@ -62,13 +62,13 @@ public partial class ConveyorBeltStraight : Node3D, IProvide<IVoxelGridRegistry>
|
||||
}
|
||||
public void OnResolved()
|
||||
{
|
||||
_guid = Grid.Register(this, VoxelTransform.Origin);
|
||||
_guid = FoodFactoryApi.GridRegistry.Register(this, VoxelTransform.Origin);
|
||||
this.Provide();
|
||||
|
||||
}
|
||||
public override void _ExitTree()
|
||||
{
|
||||
Grid.UnRegister(_guid);
|
||||
FoodFactoryApi.GridRegistry.UnRegister(_guid);
|
||||
base._ExitTree();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[gd_scene format=3 uid="uid://c4h7mwnfrdesg"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dgyuh6un1qoj4" path="res://src/Conveyors/ConveyorBeltStraight/ConveyorBeltStraight.cs" id="1_mn8ro"]
|
||||
[ext_resource type="Script" uid="uid://j24fuotdwwx4" path="res://src/VoxelGrid/TestItemConveyor.cs" id="2_1vr1d"]
|
||||
[ext_resource type="Script" uid="uid://j24fuotdwwx4" path="res://src/Conveyors/TestItemConveyor.cs" id="2_1vr1d"]
|
||||
[ext_resource type="PackedScene" uid="uid://bapdy1c6rb4qr" path="res://assets/kenney_conveyor-kit/Models/GLB format/conveyor-sides.glb" id="2_mn8ro"]
|
||||
[ext_resource type="PackedScene" uid="uid://ccuhhy2oa8xvm" path="res://assets/kenney_conveyor-kit/Models/GLB format/arrow-basic.glb" id="3_1vr1d"]
|
||||
[ext_resource type="Script" uid="uid://bjuntmf2sjynp" path="res://src/VoxelGrid/ConveyorItemRender.cs" id="4_g7n1f"]
|
||||
[ext_resource type="Script" uid="uid://bjuntmf2sjynp" path="res://src/Conveyors/ConveyorItemRender.cs" id="4_g7n1f"]
|
||||
|
||||
[sub_resource type="Curve3D" id="Curve3D_x5qdr"]
|
||||
_data = {
|
||||
|
||||
@@ -3,10 +3,14 @@ namespace FoodFactory.Conveyors;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Arch.Core;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.Sync.Primitives;
|
||||
using CommunityToolkit.HighPerformance.Helpers;
|
||||
using FoodFactory.Items;
|
||||
using Godot;
|
||||
using SJK.Math;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class ConveyorItemRender : Node
|
||||
@@ -15,48 +19,69 @@ public partial class ConveyorItemRender : Node
|
||||
[Export] protected Path3D Path3D { get; set; } = default!;
|
||||
[Export] protected TestItemConveyor ItemConveyor { get; set; } = default!;
|
||||
[Chickensoft.AutoInject.Dependency] protected IItemRenderer Items => this.DependOn<IItemRenderer>();
|
||||
[Chickensoft.AutoInject.Dependency] protected IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
|
||||
// private Chickensoft.Sync.Primitives.AutoList<ConveyorSlice>.Binding _binding = default!;
|
||||
public override async void _Ready()
|
||||
private AutoList<Ordered1DList<IBeltItem>.Entry>.Binding _binding = default!;
|
||||
public void OnResolved()
|
||||
{
|
||||
base._Ready();
|
||||
if (!ItemConveyor.IsNodeReady())
|
||||
{
|
||||
await ToSignal(ItemConveyor, Node.SignalName.Ready);
|
||||
}
|
||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
|
||||
ItemConveyor.GetChildren().OfType<Timer>().First().Timeout += () => offset = 0;
|
||||
return;
|
||||
// _binding = ItemConveyor.Items.Items.Bind();
|
||||
// binding.OnRemove(callback =>
|
||||
// {
|
||||
// Items.Remove(callback.Item);
|
||||
// // GD.PrintS(callback.Item, callback.BeltT);
|
||||
// // if (itemsRenders.TryGetValue(callback.Item, out var node))
|
||||
// // {
|
||||
// // GD.PrintS(callback.Item, callback.BeltT,node);
|
||||
// // itemsRenders.Remove(callback.Item);
|
||||
// // node.QueueFree();
|
||||
// // }
|
||||
// });
|
||||
// _binding.OnAdd((i, v) => Items.UpdateTransform(i.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(i.BeltT)));
|
||||
// _binding.OnUpdate((a, b) => Items.UpdateTransform(a.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(b.BeltT).Translated(-Path3D.Curve.SampleBakedWithRotation(b.BeltT).Basis.X * b.LaneSpan.Start)));
|
||||
|
||||
}
|
||||
float offset = 0;
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
offset += (float)delta;
|
||||
base._Process(delta);
|
||||
var e = ItemConveyor.EnumerateTowardStart();
|
||||
while (e.MoveNext())
|
||||
_binding = ItemConveyor.Items.Values.Bind().OnUpdate((old, updated) =>
|
||||
{
|
||||
Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position + (ItemConveyor.SignedSpeed * offset)));
|
||||
}
|
||||
if (old.Position == updated.Position)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Items.UpdateTransform(updated.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(updated.Position), (float)delta);
|
||||
|
||||
});
|
||||
// FoodFactoryApi.TickManger.GameTick += _ =>Process(_.Delta);// offset = 0;
|
||||
FoodFactoryApi.TickManger.GameTick += _ => delta = _.Delta;// offset = 0;
|
||||
}
|
||||
// public override async void _Ready()
|
||||
// {
|
||||
// base._Ready();
|
||||
// if (!ItemConveyor.IsNodeReady())
|
||||
// {
|
||||
// await ToSignal(ItemConveyor, Node.SignalName.Ready);
|
||||
// }
|
||||
// await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
|
||||
// // ItemConveyor.GetChildren().OfType<Timer>().First().Timeout += () => offset = 0;
|
||||
// return;
|
||||
// // _binding = ItemConveyor.Items.Items.Bind();
|
||||
// // binding.OnRemove(callback =>
|
||||
// // {
|
||||
// // Items.Remove(callback.Item);
|
||||
// // // GD.PrintS(callback.Item, callback.BeltT);
|
||||
// // // if (itemsRenders.TryGetValue(callback.Item, out var node))
|
||||
// // // {
|
||||
// // // GD.PrintS(callback.Item, callback.BeltT,node);
|
||||
// // // itemsRenders.Remove(callback.Item);
|
||||
// // // node.QueueFree();
|
||||
// // // }
|
||||
// // });
|
||||
// // _binding.OnAdd((i, v) => Items.UpdateTransform(i.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(i.BeltT)));
|
||||
// // _binding.OnUpdate((a, b) => Items.UpdateTransform(a.Item, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(b.BeltT).Translated(-Path3D.Curve.SampleBakedWithRotation(b.BeltT).Basis.X * b.LaneSpan.Start)));
|
||||
|
||||
// }
|
||||
float offset = 0;
|
||||
double delta = 0;
|
||||
|
||||
// public void Process(double delta)
|
||||
// {
|
||||
// offset += (float)delta;
|
||||
// base._Process(delta);
|
||||
// var e = ItemConveyor.EnumerateTowardStart();
|
||||
// while (e.MoveNext())
|
||||
// {
|
||||
// // Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position + (ItemConveyor.SignedSpeed * offset)));
|
||||
// Items.UpdateTransform(e.Current.Value, Path3D.GlobalTransform * Path3D.Curve.SampleBakedWithRotation(e.Current.Position), (float)delta);
|
||||
// }
|
||||
// }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
// _binding.Dispose();
|
||||
_binding.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +93,58 @@ public interface IItemRenderer
|
||||
void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time);
|
||||
void Remove(IBeltItem beltItem);
|
||||
}
|
||||
public partial class ItemRenderBuffered : Node3D, IItemRenderer
|
||||
{
|
||||
private record struct ItemEntry(IBeltItem Item, Transform3D PreviousTransform, Transform3D NewTransform, float Elapsed, float Duration);
|
||||
private List<ItemEntry> _current = [];
|
||||
private List<ItemEntry> _next = [];
|
||||
private Dictionary<IBeltItem, Node3D> _visuals = [];
|
||||
public void Remove(IBeltItem beltItem) => throw new System.NotImplementedException();
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform) => throw new System.NotImplementedException();
|
||||
public void UpdateTransform(IBeltItem beltItem, Transform3D newTransform, float time)
|
||||
{
|
||||
var index = _current.FindIndex(f => f.Item == beltItem);
|
||||
if (index >= 0)
|
||||
{
|
||||
_current[index] = _current[index] with { PreviousTransform = _current[index].NewTransform, NewTransform = newTransform, Elapsed = 0, Duration = time };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_visuals.TryGetValue(beltItem, out var node3D))
|
||||
{
|
||||
_visuals[beltItem] = node3D = beltItem.CreateItemVisual();
|
||||
node3D.Transform = newTransform;
|
||||
AddChild(node3D);
|
||||
beltItem.Disposed += _ => node3D.QueueFree();
|
||||
}
|
||||
// if (node3D.Transform.Origin.DistanceTo(newTransform.Origin) < .00001f)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
_current.Add(new(beltItem, node3D.Transform, newTransform, 0, time));
|
||||
}
|
||||
}
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
_next.Clear();
|
||||
GD.Print(_current.Count);
|
||||
for (int i = 0; i < _current.Count; i++)
|
||||
{
|
||||
var entry = _current[i];
|
||||
if (!IsInstanceValid(_visuals[entry.Item])){
|
||||
_visuals.Remove(entry.Item);
|
||||
continue;
|
||||
}
|
||||
entry.Elapsed = (float)(entry.Elapsed + delta);
|
||||
_visuals[entry.Item].Transform = entry.PreviousTransform.InterpolateWith(entry.NewTransform, entry.Elapsed / entry.Duration);
|
||||
if ((entry.Elapsed / entry.Duration) < 1f)
|
||||
{
|
||||
_next.Add(entry);
|
||||
}
|
||||
}
|
||||
(_current, _next) = (_next, _current);
|
||||
}
|
||||
}
|
||||
public partial class ItemRenderSimple : Node3D, IItemRenderer
|
||||
{
|
||||
private Dictionary<IBeltItem, Node3D> _items = [];
|
||||
|
||||
@@ -25,6 +25,7 @@ public interface IMovementConveyor
|
||||
float SignedSpeed { get; set; }
|
||||
bool IsReversed { get; set; }
|
||||
float Length { get; set; }
|
||||
bool HasItems();
|
||||
// float GetAvailableTravel(ItemConveyor.BeltDirection beltDirection, LaneSpanT itemSpan, float maxDistance);
|
||||
// IBeltSlotProfile GetPortFacingStart();
|
||||
// IBeltSlotProfile GetPortFacingEnd();
|
||||
@@ -41,25 +42,6 @@ public interface IMovementConveyor
|
||||
BeltObstacle GetDistanceToNextItem(BeltDirection beltDirection, float itemBeltT, float maxDistToCheck, LaneSpan laneSpan, HashSet<IMovementConveyor>? visted = null);
|
||||
ConveyorPort CreatePort(BeltPortProfile profile, BeltT beltT, LaneSpan laneSpan);
|
||||
}
|
||||
public readonly struct ConveyorItemHandle
|
||||
{
|
||||
private readonly Action _remove;
|
||||
private readonly Action<ConveyorSlice> _replace;
|
||||
public int Index { get; }
|
||||
public ConveyorSlice Slice { get; }
|
||||
public IBeltItem Item => Slice.Item;
|
||||
public LaneSpan Span => Slice.LaneSpan;
|
||||
public float BeltT => Slice.BeltT;
|
||||
public ConveyorItemHandle(int index, ConveyorSlice slice, Action remove, Action<ConveyorSlice> replace)
|
||||
{
|
||||
Index = index;
|
||||
Slice = slice;
|
||||
_remove = remove;
|
||||
_replace = replace;
|
||||
}
|
||||
public void Remove() => _remove();
|
||||
public void Replace(ConveyorSlice slice) => _replace(slice);
|
||||
}
|
||||
public interface IBeltMovement
|
||||
{
|
||||
void AdvanceBelt(IMovementConveyor conveyor, float delta);
|
||||
@@ -112,7 +94,7 @@ public sealed class IndividualMovement : IBeltMovement
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerator.SortAll();
|
||||
// enumerator.SortAll();
|
||||
}
|
||||
}
|
||||
public sealed class StrictMovement : IBeltMovement
|
||||
@@ -208,9 +190,78 @@ public sealed class StrictMovement : IBeltMovement
|
||||
// }
|
||||
// }
|
||||
|
||||
public void AdvanceBelt(IMovementConveyor conveyor, float delta) => throw new NotImplementedException();
|
||||
}
|
||||
public void AdvanceBelt(IMovementConveyor conveyor, float delta)
|
||||
{
|
||||
|
||||
// GD.Print(conveyor.Items);
|
||||
if (conveyor.GetBeltDirection() == BeltDirection.NotMoving)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!conveyor.HasItems())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// GD.Print("hello");
|
||||
var towardStart = conveyor.GetBeltDirection() == BeltDirection.TowardStart;
|
||||
float maxMove = conveyor.SpeedMagnitude * delta;
|
||||
var enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
BeltObstacle freeSpace = default!;
|
||||
float lastPos = 0;
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
lastPos = enumerator.Current.Position;
|
||||
freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),lastPos,maxMove,LaneSpan.One,[]);
|
||||
}
|
||||
var move = MathF.Min(maxMove, freeSpace.Distance);
|
||||
if (lastPos > conveyor.Length || lastPos < 0)
|
||||
{
|
||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
while (enumerator.MoveNext() && (enumerator.Current.Position >= conveyor.Length || enumerator.Current.Position <= 0))
|
||||
{
|
||||
if (towardStart ? enumerator.Current.Position <= 0 : enumerator.Current.Position >= conveyor.Length)
|
||||
{
|
||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, LaneSpan.One, 0))
|
||||
{
|
||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||
// GD.Print(itemRef.Item);
|
||||
enumerator.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// var freeSpace = conveyor.GetDistanceToNextItem(conveyor.GetBeltDirection(),conveyor.GetLastItemPosition(),maxMove,LaneSpan.One,[]);
|
||||
if (freeSpace.Distance <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
enumerator.MoveAllBy(MathF.Min(maxMove, freeSpace.Distance));
|
||||
}
|
||||
enumerator = !towardStart ? conveyor.EnumerateTowardStart() : conveyor.EnumerateTowardEnd();
|
||||
while (enumerator.MoveNext() && (enumerator.Current.Position >= conveyor.Length || enumerator.Current.Position <= 0))
|
||||
{
|
||||
if (towardStart ? enumerator.Current.Position <= 0 : enumerator.Current.Position >= conveyor.Length)
|
||||
{
|
||||
var facing = conveyor.GetPortFacing(towardStart ? conveyor.StartPort : conveyor.EndPort);
|
||||
// GD.Print(facing.HasValue(out var slot2),slot2);// , slot2.CanAccept(new TestItem(),LaneSpan.One,0));
|
||||
if (facing.HasValue(out var slot) && slot.TryInsert(enumerator.Current.Value, LaneSpan.One, 0))
|
||||
{
|
||||
// GD.Print(space is PortBeltObstacle port?port.LaneSpan:itemRef.Value.LaneSpan);
|
||||
// GD.PrintS(itemRef.Value.LaneSpan,itemRef.Value.LaneSpan);
|
||||
// GD.Print(itemRef.Item);
|
||||
enumerator.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct ConveyorSlice(IBeltItem item, float beltT = 0)
|
||||
@@ -258,7 +309,12 @@ public record BeltTOffset(float T) : BeltT();
|
||||
|
||||
public static class ConveyorExtensions
|
||||
{
|
||||
|
||||
public static BeltDirection Reverse(this BeltDirection beltDirection) => beltDirection switch
|
||||
{
|
||||
BeltDirection.TowardStart => BeltDirection.TowardEnd,
|
||||
BeltDirection.TowardEnd => BeltDirection.TowardStart,
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
public static IOption<IBeltPort> GetPortFacing(this IBeltPort slot, IVoxelGridRegistry gridRegistry)
|
||||
{
|
||||
var toCheck = new List<Vector3I>();
|
||||
|
||||
@@ -19,10 +19,12 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
[Dependency] public IBeltMovement MovementSystem => this.DependOn<IBeltMovement>(() => new IndividualMovement());
|
||||
[Dependency] public IVoxelGridRegistry GridRegistry => this.DependOn<IVoxelGridRegistry>();
|
||||
[Dependency] public IFoodFactoryApi FoodFactoryApi => this.DependOn<IFoodFactoryApi>();
|
||||
// private readonly AutoList<ConveyorSlice> _items = [];
|
||||
// public IAutoList<ConveyorSlice> Items => _items;
|
||||
// public readonly Sorted1DList<ConveyorSlice> Items = new(pos => pos.BeltT);
|
||||
private readonly Ordered1DList<IBeltItem> _items = new();
|
||||
public Ordered1DList<IBeltItem> Items => _items;
|
||||
// private AutoList<ConveyorSlice>.Binding _itemsBinding;
|
||||
public IBeltPort StartPort { get; set; }
|
||||
// public IBeltSlotProfile StartPort => new ItemConveyor.ConveyorPort() {
|
||||
@@ -52,7 +54,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
public IList<IBeltPort> OtherPorts = [];
|
||||
// [Export] public Vector3I Position { get; set; } = default!;
|
||||
//Speed per unit time
|
||||
private readonly AutoValue<float> _speed = new(1);
|
||||
private readonly AutoValue<float> _speed = new(.5f);
|
||||
public IAutoValue<float> SpeedValue => _speed;
|
||||
|
||||
public float SignedSpeed
|
||||
@@ -82,9 +84,9 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
{
|
||||
return;
|
||||
}
|
||||
var timer = new Timer() { WaitTime = .05f, Autostart = true };
|
||||
AddChild(timer);
|
||||
timer.Timeout += () => MovementSystem.AdvanceBelt(this, (float)timer.WaitTime);
|
||||
// var timer = new Timer() { WaitTime = .05f, Autostart = true };
|
||||
// AddChild(timer);
|
||||
FoodFactoryApi.TickManger.GameTick += args => MovementSystem.AdvanceBelt(this, (float)args.Delta);
|
||||
if (StartPort is null || EndPort is null)
|
||||
{
|
||||
throw new Exception();
|
||||
@@ -127,13 +129,15 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
}
|
||||
|
||||
// 2️⃣ No local item → try crossing into adjacent conveyor
|
||||
return GetDistanceAcrossPort(
|
||||
var recursiveDistance = GetDistanceAcrossPort(
|
||||
towardStart,
|
||||
itemBeltT,
|
||||
maxDistToCheck,
|
||||
laneSpan,
|
||||
visited
|
||||
);
|
||||
|
||||
return recursiveDistance;// with { Distance = recursiveDistance.Distance + Length };
|
||||
}
|
||||
//ChatGPT Assisted
|
||||
private IOption<ConveyorSlice> FindNextLocalItem(
|
||||
@@ -412,6 +416,7 @@ public partial class TestItemConveyor : Node, IMovementConveyor
|
||||
|
||||
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardEnd() => _items.EnumerateTowardEnd(-1);
|
||||
public Ordered1DList<IBeltItem>.Enumerator EnumerateTowardStart() => _items.EnumerateTowardStart(_items.Count);
|
||||
public bool HasItems() => _items.Count > 0;
|
||||
}
|
||||
//TODO Should liklely account for max search distance where the conveyorm may be needed to know
|
||||
public record class BeltObstacle(float Distance);
|
||||
|
||||
Reference in New Issue
Block a user