#archived-dots
1 messages Β· Page 266 of 1
they exposed a very easy to implement editor for fields
who's crazy enough to hijack a block of memory via dynamic buffers to use for other things
yeah! you are not supposed to do all those crazy things!
I'm kind of disappointed that they didn't. having hashmaps on entities is a very general use case I feel like
sure, to abuse the DB is kind of unexpected ^^
but then again, it's really not
{
UnityEngine.Debug.Log("buffer catched");
Property = CreateInstance(typeof(DynamicBufferProperty<>));
}``` kind of unexpected that the instance figures out the correct TElement for the DynamicBufferProperty
i need to get the types of TKey and TValue too. good thing that is possible with type.GetInterfaces() and then GetGenericArguments()
Ok, that thing annoys me way too much right now. There's another abstraction I've found that registers the type. I'm pretty sure it's easier to write a custom inspector for this outside of the dots debugger. This all makes little sense when having to edit the core package anyway.
stupid question, but since entities and other libraries related to dots are all in preview mode... is it more safe to build a game normally instead ?
Yea Unity officially does not recommend users to go into production using preview packages
Okay, so I am about to attempt to create a value type-only AStar implementation based on CodeMonkey's tutorial, but adapted to fit a node graph rather than a grid. I'm not 100% sure what I am doing, so could someone look at this and tell me if I'm at least on the right track?
https://pastebin.com/faqhvxCF
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
also: How would I go about working with Node Data deserialized from disk? I'm thinking somehow put it into a default list kept in persistent memory, and then during the job I clone it into a new list and mutate the data inside?
thank you
right track although I'd not think about jobs too much right now and if you do, a jobs job is to calculate an array of path finding requests and not just one
astar can be translated pretty much 1:1 into burst
What about deserialization
multithreading 1 astar request itself wouldn't make much sense
node data can live in a hashmap or one dimensional grid array
ok, I just say this because you have set the PathFindJob up for only 1 request
What. where
public struct PathFindJob : IJob
{
public float3 start;
public float3 end;```
This could be arrays and an IJobFor. In general, use IJobFor because that's newer and more flexible (can be single or multithreaded)
what does this error mean?
Cannot modify the return value of 'NativeArray<Node>.this[int]' because it is not a variable
Context:
public struct Node
{
public float3 position;
public int index;
public int lastNodeInPath;
//TODO: World
public int g; //distance from start point
public int h; //distance to end point
public int f;
public void updateF()
{
f = g + h;
}
}
//...
NativeArray<Node> nodeGraph = new NativeArray<Node>();
//...
int workingNode = getLeastF(nodeGraph, openList); //get least F in open list
NativeList<int> validConnections = getValidConnections(connectionsSource, workingNode); //find all open connections
//Set parent nodes
foreach (int item in validConnections)
{
nodeGraph[item].lastNodeInPath = workingNode;
}
you can't directly change it. var tmp = nodeGraph[item]; tmp.lastNodeInPath = workingNode; nodeGraph[item] = tmp
right. I forgot about taking out and setting in stuff
Alrighty, I've made a first draft of the AStar implementation. I haven't tested it because I would need to create a graph somehow and I have to make dinner. Can someone please do a once-over to make sure I'm not doing anything stupid/will crash my computer?
https://pastebin.com/Y5UyLwmg
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
Going to bed now. Haven't found any obvious errors although getClosestNode will be a performance hog. either hash the position to a proper index or use a hashmap. for testing it's okay to leave it like that.
I donβt know how I would do that but okay
This is a problem a K tree or whatever itβs called could solve but I donβt know how to make those yet
K-d tree I mean
i ended up using pointers to nodes to modify them in an array
if you're comfortable with unsafe stuff it can be quite handy since you avoid all the copying of struct data
ideally no
spikes are much more noticeable to a player than low fps
e.g. i'd rather solid 60fps than 90fps with spikes
IJobEntityBatch does it support parallel scheduling?
yes
everything code gens into IJobEntityBatch
entities foreach, IJjobEntity are just IJobEntityBatch under the hood
no I mean
I do use IJobEntityBatch rn
already
since I need shared comp index
I did it all as Schedule
now I want to transform into Parallel
no, I want to make my job run in parallel through chunks
either i'm confused or that is exactly what ScheduleParallel does
you can just use chunkindex
batchindex*
yes
if you happen to need entityInQueryIndex there's also a IJobEntityBatchWithIndex for that
left out by default implementation for performance
Does NativeMultiHashMap supports parallel writing?
when you do AsParallelWriter it does
no
all right, it's all parallel now
But I can't tell if perfomance good or bad, hm
1651 entities
AI agents
with choice between 2 tasks: hug player, run around (if no players, or if player is too close)
each task schedules at least 3 jobs per update
also each of them has LineRenderer xD
all right, I think it's good
almost all perfomance is on ManagedComponents
which only required for presentation
meaning if they are outside of player system
they can be disabled
looks cool
oh
I did name them kek
Is there any way to reference Editor in Runtime?
#if UNITY_EDITOR
World.GetExistingSystem<SelectionTool>();
#endif
I don't want to force PC render selection texture twice
kinda just want to pass Entity to it
maybe reflection?
dont know what you're asking
SelectionTOol is system from Editor folder
I want to do Editor code
to select entity
in Runtime system
make selection tool a runtime tool then?
SelectionTOol is system from Editor folder
then why is it in editor folder
well, it's empty basically
if I simply do code for EntitySelection in editor
same thing
ah nvm, looks like I needed to reference editor in assembly def
Hmm, when I disable System, does it run OnStopRunning?
yes, it does
are you trying to use entityInQueryIndex in IJobEntity?
yeah, and I use attribute
private void Execute(Entity e, [EntityInQueryIndex] int entityInQueryIndex, ref MoveTarget moveTarget,
in FollowShip ship)
{
It didn't cause trouble before
that's what is curious
i'd just give unity a restart then reimport the the assembly if it's still an issue
those c# compiler version errors usually just need a restart
I tried it all
even full reimport
kek
Microsoft (R) Visual C# Compiler version 3.8.0-dev.20527.1 (53dc6556)
Copyright (C) Microsoft Corporation. All rights reserved.
error SGJE0003: The parameter 'entityInQueryIndex' of type int will be ignored.
Assets\Runtime\HybridGO\CompanionLinkDisableSystem.cs(15,28): info SP0001: Diagnostic 'CS0282: There is no defined ordering between fields in multiple declarations of partial struct 'CompanionLinkDisableSystem.SetModeJob'. To specify an ordering, all instance fields must be in the same declaration.' was programmatically suppressed by a DiagnosticSuppressor with suppression ID 'SPDC0282' and justification 'Some DOTS types utilize codegen requiring the type to be partial.'
Assets\Runtime\TestAI\FollowShip.cs(23,32): info SP0001: Diagnostic 'CS0282: There is no defined ordering between fields in multiple declarations of partial struct 'FollowShipSystem.FollowShipJob'. To specify an ordering, all instance fields must be in the same declaration.' was programmatically suppressed by a DiagnosticSuppressor with suppression ID 'SPDC0282' and justification 'Some DOTS types utilize codegen requiring the type to be partial.'
Assets\Runtime\SpaceshipMovement\MovementLineSystem.cs(83,32): info SP0001: Diagnostic 'CS0282: There is no defined ordering between fields in multiple declarations of partial struct 'MovementLineSystem.SetLineRendererJob'. To specify an ordering, all instance fields must be in the same declaration.' was programmatically suppressed by a DiagnosticSuppressor with suppression ID 'SPDC0282' and justification 'Some DOTS types utilize codegen requiring the type to be partial.'
[EntityInQueryIndex]
Is that attribute only required in IJobEntity?
nothing else?
yay
I found it
I did miss one attribute on sketchy job
@rotund token How do you store your graph nodes?
I'd like to use graphs for a few systems, and ideally store their data in blobs, but I have no idea how to store it, since in normal OOP I would just use inheritance
i use inheritance π
just my own unmanaged version of it
(not too dissimilar to how physics stores their colliders for example)
So from looking at it on a surface level, the collider is a struct that stores it's own "true" type in an enum, and the interface methods are implemented by casting the collider base struct to a pointer of the relevant type and calling the actual method (thus getting the proper memory offset for the correct type)?
e.g. Collider.MemorySize
How / where are you storing the struct though? Since you can't store a Collider struct, as the size would be incorrect when compared to the "true" size.
Basically, If the graph lives on a scriptable object, it's easy enough to just use an interface type while working with nodes, but how are they stored in the final blob?
im not using blobs but my own memory storage
(blobs had some restrictions in 0.17 that made it painful but it should be quite doable in 0.50 hopefully again)
How would you store them though? What is the type used for e.g. a BlobArray? Just use a struct type that is as large as the maximum subtype?
Or just use a byte array and somehow reinterpret the nodes from that?
Guess I'll take a look at the unity physics implementation. Thanks for the tips!
i learn a lot from that
but yeah its just stored as a pointer in a blob asset reference
the blob asset reference is a collider type
but its allocated as a larger size
Yeah, I just saw that. The BlobAssetReference<T> Create(void* ptr, int length) method seems useful for storing generic data, and it's nice that the actual reference can "remember" any arbitrary type with the generic parameter
How do I convert the blob asset reference to the true type? Is UnsafeUtility.AsRef<MyTrueType>(blobAssetReference.GetUnsafePtr()) the best way? Since that seems to be what .Value does
probably my last update on this rollback side project
https://youtu.be/LST1DBzHImY
took Unity's boids demo, cranked the amount from 50k to 250k and added rollback
Any spikes are basically my poor gpu unable to keep up π
Very cool. It also looks incredibly satisfying with them just starting out as spheres and then beginning to move π
How large is each frame snapshot for 250k entities?
i haven't checked but it'd be large especially since i turned off compression for this quick demo
18MB uncompressed
hmmm
Feels bad IJobEntity is not IJob
I kind of want to use some general Interface
for my jobs
I'm trying to reuse some code, but since it's all struct/interface based, it's impossible sadkek
That's a weird saying
They're completely different things
I know
it's just that I am going to have exactly this same thing in every my System
and I need to do Schedule
for them
and since jobs are interfaces
I can't really do it
and have to rewrite same thing
Make your system generic, use IJobEntityBatch?
@rotund token Do ChangeFilters now always cause syncpoints or are there conditions? do you know where i can find it in the code?
Basically all filters will cause a sync point atm
Its not a hard sync point on everything
Just on any component the job depends on (maybe only the filter)
jobs are extremely simple mostly
using batch is a waste
[BurstCompile]
private partial struct SetWeightJob : IJobEntity
{
void Execute(ref DynamicBuffer<ActionWeight> actions)
{
actions.Add(new ActionWeight() {type = ComponentType.ReadOnly<DoWorkTag>(), weight = 0f});
}
}
[BurstCompile]
private partial struct ClearTaskJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter buffer;
void Execute([EntityInQueryIndex] int entityInQueryIndex, Entity e)
{
buffer.RemoveComponent<DoWorkTag>(entityInQueryIndex, e);
buffer.RemoveComponent<HasJob>(entityInQueryIndex, e);
}
}
private SetWeightJob _setWeightJob;
private EntityQuery _setWeightQuery;
private ClearTaskJob _clearTaskJob;
private EntityQuery _clearTaskQuery;
Here one example
What do you mean by waste? Like effort?
ah,perfect! Can totally work with that.
like, my concern is writing too much code
Using Batch will result writing even more code xD
when working with classes you reuse a lot of code
with jobs it seems
impossible
Well that's what I'm saying use the same job in every system
but it's fine
I can't
unless maybe if it's generic
but even in this case
it can't always be same
That's literally what I said
You can totally reuse same job and pass custom logic to each with generics
I know, but the point
eh, I'm just complaining
Still, requires too much abstract templates
kek
I already have like 5
and I only have 2 total actions
lol
btw, is that a thing for Rider to move "errors window" the same way as in VS?
so it shows all error/hints/warnings as full messages?
that brings me up to a question what are the ways people are using to reuse code in jobs in general?
like I was using a struct before but I have to remember to create a declare it before the job every time or a static method I guess thats a bit better
static helper functions/helper structs with methods defined on them
yeah, a lot of in ecs are simple math after all
ok cool, thats pretty much what I'm doing
Is there a perfomance difference between ForEach and IJobEntity?
if code is literalyl same
there shouldnt be because both are running through codegen and end up beeing an IJobEntityBatch
They're under com.unity.entities/Unity.Entities.Editor/ScriptTemplates/
Thanks, the package manager resets the change but I can create my own script templates menuItem to show my templates.
The package manager resets it because you haven't made a local copy
I would recommend you just make your own templates though
It will be a mess to share the project if I have a local copy, having my own templates will be better, again thanks for the information.
ahem, these errors are so missleading
protected override void OnStopRunning()
{
if (!Enabled)
{
_clearTaskJob.buffer = bufferSystem.CreateCommandBuffer().AsParallelWriter();
Dependency = _clearTaskJob.ScheduleParallel(_clearTaskQuery, Dependency);
bufferSystem.AddJobHandleForProducer(Dependency);
}
}
I simply added this
meanwhile I run this same job, in OnUpdate
meanwhile this job literally has nothing to do with LTWs
kek
It's not completing the dependencies it requires
private partial struct ClearTaskJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter buffer;
void Execute([EntityInQueryIndex] int entityInQueryIndex, Entity e)
{
buffer.RemoveComponent<DoWorkTag>(entityInQueryIndex, e);
buffer.RemoveComponent<HasJob>(entityInQueryIndex, e);
}
}
There it is, kek
OnStopRunning does not force the system to complete required dependencies, that's probably why it is complaining
hey does anyone know how to use "dont destroy onload" in dots ?
for my networking game
=)) i use netcode package so there is entity and it 's be destroyed when change scene
@pine plaza you should probably have an extra subscene where you have all the stuff you dont want to unload
hmm
what would be better solution
run a job that literally just removes component through buffer
or simply move some new system to sync point
and remove all components through entity manager?
if you can batch the command the second option is better
wdym batch the command?
when you can use the entityquery to remove the components
[UpdateInGroup(typeof(EndSyncPointTickSimulationGroup))]
private partial class ClearSystem : SystemBase
{
protected override void OnCreate()
{
_query = GetEntityQuery(typeof(HasJob), typeof(DoWorkTag), ComponentType.Exclude<MoveTarget>());
_types = new ComponentTypes(typeof(HasJob), typeof(DoWorkTag));
}
private EntityQuery _query;
private ComponentTypes _types;
protected override void OnUpdate() { EntityManager.RemoveComponent(_query, _types); }
}
protected override void UpdateWithBuffer(EntityCommandBuffer.ParallelWriter buffer)
{
Dependency = _setWeightJob.ScheduleParallel(_setWeightQuery, Dependency);
Dependency = Entities
.WithAll<DoWorkTag>()
.WithNone<MoveTarget>()
.ForEach((int entityInQueryIndex, Entity entity, in LocalToWorld translation) =>
{
//code
})
.ScheduleParallel(Dependency);
// _clearTaskJob.buffer = buffer;
// Dependency = _clearTaskJob.ScheduleParallel(_clearTaskQuery, Dependency);
}
here it is, anyway
commented part in bottom
is one I changed for new system
above
order looks like this
idk. seems to me like you remove the HasJobTag every frame?
nah, I only remove it
when Task is done
when MoveTarget doesn't exist
which is only added when this Task is assigned
HasJob tag just means that agent is busy with work and doesn't look for it
So that means youd need to do that for every action your agent can do? if you had a AttackTarget Component youd have to define another query that removes HasJob when it the entity had no AttackTarget?
You probably should work with less tagging
HasJob is common between all
it's protected component inherited
from abstract system
it just means
that job searcher doesn't need to look for job
yes but my point is there are many types of jobs it could have. not only MoveTarget
MoveTarget is not really job
it's just condition
for work* to be over
once MoveTarget is removed - means target reached point
it's pretty simple atm
since I just want to get on track
yes but you have other jobs with other conditions
of pattern
yeah
and every condition for each will be different
doesn't mean it will even require tags
it can be any code
but agent will look for new work only when HasJob is absent
so wait is that in your utility ai or is it just your logic to execute decided actions?
and it's up to work system to decide when that happens
MoveTarget is internal logic for agent to simply move to some position
not really job tag
each work has it's own tag though
What i am basically asking is if you coupled your Action Logic to your AI Decision Logic
Sry super tired. might be a bit unclear
eh, not sure what that means
I can simply show code
private partial struct DetermineBestActionJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter buffer;
void Execute([EntityInQueryIndex] int entityInQueryIndex, Entity e, ref DynamicBuffer<ActionWeight> actions)
{
float highestWeight = -1f;
int ind = -1;
for (int i = 0; i < actions.Length; i++)
{
var action = actions[i];
if (highestWeight < action.weight)
{
highestWeight = action.weight;
ind = i;
}
}
if (ind == -1) return;
buffer.AddComponent(entityInQueryIndex, e, actions[ind].type);
buffer.AddComponent<HasJob>(entityInQueryIndex, e);
actions.Clear();
}
}
protected override void OnCreate()
{
base.OnCreate();
_findTaskJob = new DetermineBestActionJob();
_findTaskQuery = GetEntityQuery(ComponentType.Exclude<HasJob>(), ComponentType.ReadOnly<ActionWeight>());
}
private DetermineBestActionJob _findTaskJob;
private EntityQuery _findTaskQuery;
protected override void UpdateWithBuffer(EntityCommandBuffer.ParallelWriter buffer)
{
_findTaskJob.buffer = buffer;
_findTaskJob.ScheduleParallel(_findTaskQuery);
}
}
that's it
this is how I determine which work for agent to do
to add behaviour to agent you simply add components to him
if agent has no HasJob all it's possible actions write to ActionWeight buffer it's worth value
and then tag of that exact action and HasJob are assigned to agent
which starts Action logic
and cleaning job that looks for condition to remove HasJob and action tag is waiting for it to trigger
once that happens -> go to step 1
ok i get it now. very similar to what i did
i just dont tag AIs that have completed their task. my tasks are always interuptable if something more important is dedided
I don't tag them either, I simply remove tags
i think this shouldnt be a major performance impact no matter how you remove/add that tag. probalby for easier maintainability you should do it directly in the system that decides that the job is complete
same thing lol
one thing about it
is to not use HasJob tag
but instead let decider jobs rn every update
but that will question perfomance
yes thats what i do. decide every frame and if there are better actions you switch
makes for more responsive AI
But you can do both. have actions that need to be finished and ones that can be "overruled"
or maybe have interruptable timer
What i additionaly have is a "momentumFactor" that get multiplied onto the Score and that factor decays over time
as in, some timer which will decide when job can be checked for interruption
for every action
yeah
I guess we think the same way about it kek
so that when a new decision would be decided in the next frame it wont overrule the previous one because of the additional score
some form of hysteresis
Adding / removing tags can be a bigger performance bottle neck than just running the system every frame
If you have a lot of actors and it is slowing down the game, only update actors every second update
so 50% this frame, 50% next frame, repeat
And scale to actor count appropriately
how i did this is to deifne how many chunks per frame should be updated
I think I'll have around thousand or two of agents
Really depends on how heavy the workload for them is, whether or not that will fit in a single frame
adding/removing tags only happens about once in several thousand updates
That's definitely fine, I didn't read into completely how you are doing it, I just wanted to mention it
you wont find a diffrence in actual gameplay. since you wont have many agents in the same frame change states
so batching those commands wont be that helpful
just remove the components in commandbuffers so you can do it directly in your ActionLogic
It's fine, the garbage collector will only run every several thousand updates. π
private partial struct SetWeightJob : IJobEntityBatch
{
[ReadOnly] public NativeMultiHashMap<int, TargetData> hashMap;
[ReadOnly] public SharedComponentTypeHandle<InSystemComponent> inSystemTypeHandle;
[ReadOnly] public ComponentTypeHandle<LocalToWorld> ltwTypeHandle;
public BufferTypeHandle<ActionWeight> bufferTypeHandle;
public void Execute(ArchetypeChunk batchInChunk, int batchIndex)
{
int systemID = batchInChunk.GetSharedComponentIndex(inSystemTypeHandle);
var buffers = batchInChunk.GetBufferAccessor(bufferTypeHandle);
var ltws = batchInChunk.GetNativeArray(ltwTypeHandle);
for (int i = batchIndex; i < batchInChunk.Count; i++)
{
TargetData targetData;
NativeMultiHashMapIterator<int> iterator;
if (hashMap.TryGetFirstValue(systemID, out targetData, out iterator))
{
float3 pos = ltws[i].Position;
pos.z = 0;
do
{
float3 curTarPos = targetData.position;
curTarPos.z = 0;
if (math.distance(pos, curTarPos) > 2f)
{
buffers[i]
.Add(new ActionWeight() {type = ComponentType.ReadOnly<DoWorkTag>(), weight = 1f});
return;
}
} while (hashMap.TryGetNextValue(out targetData, ref iterator));
}
}
}
}
Hmmmmmmmmmmm
smth VERY weird with this system
ooooh
I'm dum
very dum
nvm any of that
I do struggle regarding on point tho:
How do I move that return to continue of for loop?
since I also need loop for hashMap iteration
u mean u want to break instead of return?
where you return right now, you want to break the do/while but continue the for?
If I have a job that is scheduled with WithStructuralChange, but there are 0 entities in that query, does it still cause a sync point?
Good question, can't remember
I'd usually early out with an is empty ignore filter anyway to avoid it
hmmm
Are structural changes in ECB performed in any order they were scheduled?
nvm, I don't think it matters anyway
shame there's no burstable navmesh :/
do you mean building it?
because you can query unity navmesh from burst jobs
i have my own wrap of recast though
doesn't perform as well as i'd like for local updates - it's fine just not as good as i'd like
not sure what magic unit made to theirs
yeah, actually just querying. oh that works. have to look into it
they used navmesh from burst jobs in their first austin demo
cool, thanks! then I even have source code
if you look at NavMeshQuery
it's just a pointer
wrote a how to way back in 19 on how to build it
and talked about it a bit here in 21: https://forum.unity.com/threads/any-tips-on-coding-a-dots-navmesh.1031752/#post-6680677
hmm, I noticed that IJobEntity in System debugger shows a lot of fake queries
with fake entities
well, not really fake
but it counts same entities several times
is there any kind of way to avoid that?
screenshot?
kind of confused unless you are talking about multiple queries being generated for a single IJobEntity
seems to imply you have 937 huggers
π€
so what's your second sphere query doing
yes
i'm confused about your problem
your job query is only returning the 937 entities you seem to want
but you have a second query in your system that is returning every entity with LocalToWorld
I meant this
the one above is totally different system, kek
_doWorkQuery = GetEntityQuery(ComponentType.Exclude<GetToPoint>(), ComponentType.ReadOnly<DoWorkTag>());
I only schedule it with query
seems like IJobEntity always includes it's own query
even if you don't use it
i guess Entities.ForEach didn't allow you to use your own query so it's an oversight
that's not ideal
I still find it odd that you need to explicitly set read/write not just on the job but also in a query. Weird redundancy.
oh wow, just found out my exposed CDFE was missing AddReaderWriter and not setting dependencies. how did this work out for so long without any errors I wonder ... π
they actually do very different things though
Where can I find examples on generic jobs?
I got to the situation, where I got 2 jobs that look literally equal
lol
allthough...
not really equal
[ReadOnly] public ComponentDataFromEntity<LocalToWorld> translations;
public EntityCommandBuffer.ParallelWriter buffer;
private void Execute([EntityInQueryIndex] int entityInQueryIndex, Entity e, ref Translation translation,
ref Rotation rotation,
in FollowShip followShip, in ShipSpeed speed, in Maneuverability maneuverability)
{
float3 targetPosition = translations[followShip.entity].Position;
MovementJobUtility.CalculateDelta(targetPosition,
translation.Value,
out float3 delta,
out float deltaSpeed);
if (deltaSpeed < 1f)
{
buffer.RemoveComponent<FollowShip>(entityInQueryIndex, e);
buffer.RemoveComponent(entityInQueryIndex, e, followShip.tagsToRemove);
}
else
{
deltaSpeed = speed.value;
}
MovementJobUtility.CalculateShipMovement(ref translation,
ref rotation,
delta.Normalized(),
deltaSpeed,
maneuverability.value);
}
Entities.ForEach(
(int entityInQueryIndex, Entity e, ref Translation translation, ref Rotation rotation,
in GetToPoint target, in ShipSpeed speed, in Maneuverability maneuverability) =>
{
MovementJobUtility.CalculateDelta(target.value,
translation.Value,
out float3 delta,
out float deltaSpeed);
if (deltaSpeed < 1f)
{
buffer.RemoveComponent<GetToPoint>(entityInQueryIndex, e);
buffer.RemoveComponent(entityInQueryIndex, e, target.tagsToRemove);
}
else
{
deltaSpeed = speed.value;
}
MovementJobUtility.CalculateShipMovement(ref translation,
ref rotation,
delta.Normalized(),
deltaSpeed,
maneuverability.value);
})
.ScheduleParallel();
not sure if I can turn it into one
at the very least create a helper struct for the identical code
ShipUtility or something
does unity plan to add a stopwatch feature for jobs to get timings - profiler is okay but its kind've annoying some times to navigate π
probably not but you can add your own if you really want
though i'd just use profiler
but to quote myself
https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
[DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
private static extern void GetSystemTimePreciseAsFileTime(out long filetime);
Or other platform specific methods or you can just write your own little native library to provide all the functionality you need.```
Is all of that just your generic movement code for ships? Why is there a difference between GetToPoint and FollowShip? isnt FollowShip just GetToPoint where the Point is the ship?
ngl
I hate the way I did it
want to redo it completely
Can't figure out yet how
I need some kind of system that can make any sorts of tasks and action
including chained
from simple: get to point
to: do a system jump to other star system, land on planet, do X thing
ah i see. the issue with profiler even deep profiler - its not showing all the jobs which is odd
I did figure out that I can add logic on end to add/remove components, which will let me chain events
But I just don't see yet how it would work
oh i found them they just absolutely tiny =/
had to zoom in so far just to actually see them properly
well it says 0.001ms but im thinking that might just be the smallest number it will show π
ive never seen anything with a smaller number
1us is about the smallest precision you're ever going to get
is that because of profiler performance reasons or a C# limitation ?
computer/os limitations
oh lol
well that is stupidly fast for a path finder im almost believing i must've done something wrong for it to be that fast
at that time it's not doing anything
for me that all sounds like one action. GoToTarget. How you get there is your pathfinding logic. Your pathfinding needs to account for Stargates but does your Decision to go to a diffrent planet need to know?
personally my AI has no knowledge of pathfinding except to request either a static or dynamic path
requests a path to X and then the pathfinding / movement system/s take over
and just feed back any data the AI might need to make decisions
so you suggest just make one complex action of moving?
Unavailable/AtDestination/Processing/InProgress/etc
yep thats how i do it too
i have to make a decision now on whether to use managed objects and create temporary arrays of unmanaged data to do jobs or keep everything unmanaged
I only have different Actions for Movement when it comes to fighting vs move to job. Since when fighting i want them to use diffrent speed etc.
rn I want to make 2 basic actions:
Get to Point
Follow Ship
first is controlled by pepega AI which just moves entity to next point of array (next quadrant)
Second is making entity to follow player ship, until it's too close -> and do pepega AI if it is
so, I guess you suggest to make FollowShip action just correct GetToPoint target value?
Well if you can keep everything unmanaged without it destroying your maintainability then its an easy choice
well i played around with pointers so i could update structs without worrying about defensive copies etc. which is a big plus, but things can go wrong with pointers if i screw it up very quickly
yes. and then when the ship gets close enough to the other ship your Decision to follow should just get 0 so it goes into Idle Decision
I'm not sure how chaining will work
So hugger AI wants every hugger to go and hug player once, but only if it's too far (meaning if it's close, action weight will be low)
In previous scenario
I'd simply add FollowShip component with entity field of player
and all moving logic is in here
once it reaches player -> FollowShip along with HasJob gets removed, and agent returns to AI pool
The great part about utility AI is that you dont need to define chaining explicitly
then what would be the way to achieve that behaviour?
so lets say you have an Agent that should gather resources.
You dont need to make that one single decision with an Actionchain.
the Actionchain looks like this:
- GoToTarget (Resource)
- FillBackpack with resource
- GoToTarget (ResourceDepot)
- EmptyBackpack
now your decisionlogic can decide when to execute what. at any point in time it doesnt need to know that you decided to GatherResources but only needs to execute its step until another Action automatically takes over.
So your first Action is taken when resources are low.
Second Action is taken when backpack is empty and you are close to resource.
Third Action is taken when backpack is full
Forth Action is taken when Backpack is full and close to Depot.
Now if anything ingame would happen that would fill the backpack the agent will move to the ResourceDepot.
it wouldnt have to first gather resources to drop them afterwards.
or outside sources like being attacked might cause it to interrupt and start fighting
and when it's done, it'll just naturally go back to moving to resource depot
ok, I see the point
no its Utility AI. if set up correctly it can do actions that seem like planning without actual plans
This is how GoToAttackPosition is defined in my AI. you see the responsecurve of the first Axis? that just tells the agent to not go closer if close enough and then attack takes over
what is that?
ah i see. im stilling learning GOAP have not heard of utility ai will read up on the differences
this is how attack then looks like
When close to a Zombie and if healthpoints are high it attacks.
if health would be low i have another Consideration that triggers a Flee
what is that? how did you make it look like this?
kinda looks like ScriptableObject
for OOP
yes i author the whole AI with scriptable objects and Graphs in the editor.
why are scriptable objects OOP? they're just data
WDYM? Its just a Scriptable Object which i convert into data my ConsiderationSystem gets to run its logic.
ah
sir, could you explain how exactly do you chain those considerations into one action?
As in architecture
I do get how to make consideration of one
but combine 2 (or more) dynamically, is smth different
@rustic rain Not sure if i understand your question. The Consideration with the highest UtilityScore gets Chosen as the Decision. The Action that is defined in the Decision is executed.
Your Considerations should be set up in such a way that they make sense even if not used in a chain of actions. A chain of Actions is something emergent of the System of all of those considerations combined.
I mean, considerations
so far all my considerations
are single job hard coded
unique per action
but from what I see
you somehow managed to do it in a dynamic way
where you add several consideration to any action
or am I wrong?
hmm maybe the terminology is a bit confusing in my case because considerations dont have to be coupled to Actions but can be. I assign an Action to a consideration not the other way around. So i can assign the GoToTarget Action to different Considerations that result in diffrent Targets to go to.
i think you confuse the Axis with considerations? Axis are the Input to the Consideration. Each Axis gets multiplied with one another to get the UtilityScore of the Consideration
consideration is 0..1 of Action weight?
axis is input of consideration?
in that case, I meant axis, yes
What you refer to as Action weight i refer to as Utility Score if that makes it a little bit clearer
all right
yes. its like the Infinite Axis Architecture
I get how it works on paper, but I don't see it in ECS
Yes making it generic in ECS was the hardest part.
i scrapped like 3-4 designs cause i always ran into another roadblock
So, could you elaborate just a bit?
I just need to see a direction to go to
I mean if i do its like forcing you into a direction you might not want to go with it. The decision on how you implement it will change performance and scalability characteristics. Mine is not as performant as other possible implementations because in mine i have to run a System for each consideration which costs mainthread time. In other implementations you could possibly run a single system on mainthread and evaluate all considerations on workers. (I cant do that because for me execution order is crucial)
So as soon as i show you some of my code you are locked into some form of my implementation.
public class ConsiderationSys<C, CP, A0, A1, A2, A3> : BaseConsiderationSystem<C, CP>
where C : struct, IBufferElementData, IConsideration
where CP : struct, IBufferElementData, IConsideration
where A0 : struct, IBufferElementData, IConsideration
where A1 : struct, IBufferElementData, IConsideration
where A2 : struct, IBufferElementData, IConsideration
where A3 : struct, IBufferElementData, IConsideration
{
protected override void OnUpdate()
{
var decisionRequestBufferHandle = GetBufferTypeHandle<DecisionRequest>();
var chunks = considerationQuery.CreateArchetypeChunkArray(Allocator.TempJob);
var considerationOutputHandle = GetBufferTypeHandle<C>();
var axis0Handle = GetBufferTypeHandle<A0>(true);
var axis1Handle = GetBufferTypeHandle<A1>(true);
var axis2Handle = GetBufferTypeHandle<A2>(true);
var axis3Handle = GetBufferTypeHandle<A3>(true);
Dependency = new GenConsiderationJob
{
chunks = chunks,
considerationOutputHandle = considerationOutputHandle,
systemSettings = systemSettings,
entityTypeHandle = GetEntityTypeHandle(),
targetFilterMask = targetFilterMask,
axis0Handle = axis0Handle,
axis1Handle = axis1Handle,
axis2Handle = axis2Handle,
axis3Handle = axis3Handle,
}.ScheduleParallel(chunks.Length, 1, Dependency);
Dependency = new OutputBufferToDecisionJob<C>()
{
decisionReqHandle = decisionRequestBufferHandle,
chunks = chunks,
considerationOutputHandle = considerationOutputHandle,
systemSettings = systemSettings,
entityTypeHandle = GetEntityTypeHandle(),
}.ScheduleParallel(chunks.Length, 1, Dependency);
base.OnUpdate();
}
This is how my ConsiderationSystem looks like. I Systems like that for up to 10 Axis.
private struct GenConsiderationJob : IJobFor
{
public BufferTypeHandle<C> considerationOutputHandle;
[ReadOnly] public EntityTypeHandle entityTypeHandle;
[ReadOnly] public EntityQueryMask targetFilterMask;
[ReadOnly]public NativeArray<ArchetypeChunk> chunks;
[ReadOnly]public BlobAssetReference<ConsiderationSettingsBlobAsset> systemSettings;
[ReadOnly]public BufferTypeHandle<A0> axis0Handle;
[ReadOnly]public BufferTypeHandle<A1> axis1Handle;
public void Execute(int chunkIndex)
{
var entityChunkArray = chunks[chunkIndex].GetNativeArray(entityTypeHandle);
var chunkConsiderationOutputData = chunks[chunkIndex].GetBufferAccessor(considerationOutputHandle);
var chunkDataAxis0 = chunks[chunkIndex].GetBufferAccessor(axis0Handle);
var chunkDataAxis1 = chunks[chunkIndex].GetBufferAccessor(axis1Handle);
for (int iEntityInChunk = 0; iEntityInChunk < entityChunkArray.Length; iEntityInChunk++)
{
var currentEntity = entityChunkArray[iEntityInChunk];
var considerationOutputBuffer = chunkConsiderationOutputData[iEntityInChunk];
var genericAxisArray = new UnsafeList<DynamicBuffer<Consideration<GenericAxis>>>(10, Allocator.Temp)
{
chunkDataAxis0[iEntityInChunk].Reinterpret<Consideration<GenericAxis>>(),
chunkDataAxis1[iEntityInChunk].Reinterpret<Consideration<GenericAxis>>()
}
ConsiderationUtility.CalculateConsiderationValues(currentEntity, considerationOutputBuffer,
systemSettings, genericAxisArray, targetFilterMask);
}
}
}
thats how a job looks like that i schedule from those ConsiderationSystems.Hope that shows some architecture. All the magic happens inside ConsiderationUtility.CalculateConsiderationValues
How does each AXIS access required components?
each axis is a DynamicBuffer on the AgentEntity. How that data gets to the AgentEntity in the first place is in a diffrent Step in my Architecture.
well, that's the most interesting part
since my thinking rn is only limited by fixed Queries
and hard coded argument lists
public class BaseScoringSys<C, D> : BaseScoringSys <C>
where C : struct, IBufferElementData, IConsideration
where D : struct, IComponentData, IVectorizable
{
protected override void OnStartRunning()
{
vectorizerQuery = GetEntityQuery(systemSettings.vectorizerFilter);
vectorizableQuery = GetEntityQuery(systemSettings.vectorizableFilter);
vectorizerQuery.ResetFilter();
vectorizerQuery.AddChangedVersionFilter(typeof(LoadBalanced));
}
protected override void OnUpdate()
{
var vectorizerChunks = vectorizerQuery.CreateArchetypeChunkArray(Allocator.TempJob);
var vectorizableChunks = vectorizableQuery.CreateArchetypeChunkArray(Allocator.TempJob);
Dependency = new ScoringJob
{
entityTypeHandle = GetEntityTypeHandle(),
vectorizableHandle = GetComponentTypeHandle<D>(true),
vectorizerChunks = vectorizerChunks,
vectorizableChunks = vectorizableChunks,
curveParameters = systemSettings.responseCurve.parameters,
vectorizerOutput = GetBufferTypeHandle<C>(false),
minVal = systemSettings.minValue,
maxVal = systemSettings.maxValue,
scoringType = systemSettings.scoringType,
outputProcessor = systemSettings.outputProcessor,
}.ScheduleParallel(vectorizerChunks.Length, 1, Dependency);
}
}```
this is how that first step in the most basic form looks like
hm, so basically generic typeHandles
this is how its authored. The query which entities i wanna read from is defined in that scriptable object.
So in this first "vectorization" step i read from other entities and write to the AgentsDynamicBuffers.
In the next step i use those buffers as inputAxis for the ConsiderationSystems
thats why i said you shouldnt go with my implementation of it. i made those decisions for very specific reasons ^^
is that correct?
yes
well one consideration can have up to 10 axis as input to be more precise
But it doesn't hurt to know
of options
Utility AI seems like a great solution for situational AI
but I'm really uncertain about sim kind of AI
Allthough
It's just very different way of thinking, like OOP vs ECS
yes i agree. im doing sim stuff with it though
literally sims is using utilityAi btw π
I know
I just need a lot of time
to think it through
feels bad there are very little examples
will it error if I SetComponentData on entity that doesn't have component?
I noticed, that it doesn't error if I add on ones that already have it
I just want to be certain, that I have SetOrAdd method for it
Add essentially is SetOrAdd
oh
that's great
Is there a way to get ref to some component in NativeArray?
I have quite a lot of arrays to process xD
kind of wish I could just send them all with ref in case I need to write them
you should be able to write to them
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static unsafe void CheckIndexInRange(int index, int length)
{
if (index < 0 || index > length)
throw new ArgumentException( $"Index {index} is out of range of length {length}" );
}
public static unsafe ref T GetAsRef<T>( this NativeArray<T> array, int index ) where T : struct
{
CheckIndexInRange( index, array.Length );
return ref UnsafeUtility.ArrayElementAsRef<T>( array.GetUnsafePtr( ), index );
}
yeah, but I seek for efficiency
That's copied out of my toolbox
oh
wow, seems to work
thanks
any idea tho, whether it's perfomance efficient? xD
I assume it doesn't make any difference
in long shot
but still
and you shouldnt care now. make it run then optimize if you actually see it in profiler...
I do wonder tho, whether it actually gets ref
since if Component is large
that would actually make a difference
I think...
and meanwhile you probably mess up some smart optimizations burst does for u
you have enough other problems to worry about. those microoptimizations are never worth your time
I personally use it so I have to write less code not for performance reasons π€·ββοΈ
any idea how it interacts with burst and etc?
What exactly do you mean? It works just fine in bursted jobs
Does anyone know if there is any up to date tutorial about triggers in DOTS?
hmmm
.ForEach((CompanionGameObjectComp link, in LocalToWorld localToWorld) =>
{
link.companion.transform.position = localToWorld.Position;
link.companion.transform.rotation = localToWorld.Rotation;
})
There's hardly any cases where getting a ref is slower. You'd have to use refs on <32bit datatypes to see a negative effect
public void Execute(ArchetypeChunk batchInChunk, int batchIndex)
{
var entities = batchInChunk.GetNativeArray(entityTypeHandle);
var translations = batchInChunk.GetNativeArray(translationTypeHandle);
var rotations = batchInChunk.GetNativeArray(rotationTypeHandle);
var gtps = batchInChunk.GetNativeArray(gtpTypeHandle);
var speeds = batchInChunk.GetNativeArray(speedTypeHandle);
var maneuverabilities = batchInChunk.GetNativeArray(maneuverabilityTypeHandle);
for (int i = 0; i < entities.Length; i++)
{
var e = entities[i];
ref var target = ref gtps.GetAsRef(i);
ref var translation = ref translations.GetAsRef(i);
ref var rotation = ref rotations.GetAsRef(i);
if (target.follow != Entity.Null)
{
target.value = translation.Value;
}
MovementJobUtility.CalculateDelta(target.value,
translation.Value,
out float3 delta,
out float magnitude);
if (magnitude < 1f)
{
buffer.RemoveComponent<GetToPoint>(batchIndex, e);
buffer.RemoveComponent(batchIndex, e, target.tagsToRemove);
}
rotation.Value = MovementJobUtility.CalculateRotation(delta.Normalized(),
maneuverabilities[i].value,
rotations[i].Value);
translation.Value += MovementJobUtility.CalculateMovement(rotation.Value, speeds[i].value);
}
}
This system breakes LTW component
I'm not sure, maybe it's because of this ref
feels bad
nnnope
same error even without refs
do you use physics?
What does CalculateRotation do and is .Normalized using normalize or normalizesafe?
in case you do add this protected override void OnStartRunning() { this.RegisterPhysicsRuntimeSystemReadOnly(); }
Shouldn't the JobsDebugger complain then instead of silently messing the LTW up?
oh sorry, didn't see the posted error. lol I have never seen this
If you use normalize try using normalizesafe, normalize will cause nans when your input is zero
public static float3 Normalized(this float3 original)
{
return original / (math.sqrt(original.x * original.x + original.y * original.y + original.z * original.z));
}
this is how I normalize
the ref usage is inconsistent. you get a ref but then use it as value in the method call
You are dividing by zero or not (or by nan lol)?
but that has nothing to do with the actual error
nah, ref had nothing to do with it
idk yet, figuring it out rn
if (target.follow != Entity.Null)
{
target.value = translation.Value;
}
Commenting this
solved most problems
but 1 still errored
public static quaternion CalculateRotation(float3 directionNormalized, float maneuverability,
quaternion curRotation)
{
quaternion tarRot = quaternion.LookRotation(forward, directionNormalized);
return math.nlerp(curRotation, tarRot, maneuverability);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 CalculateMovement(quaternion rotation, float speed)
{
return math.mul(rotation, up * speed);
}
here those methods
I find it easier to get refs with just the pointers. doesn't need 2 method calls and the length check is redundant. burst should inline the methods but adding a [MethodImpl(MethodImplOptions.AggressiveInlining)] also helps in case burst doesn't
Ah yees but I like having that little bit of extra safety in the editor
as a generic method it's totally okay. if you are iterating over chunks it's not needed though, might write a method without the check. if conditions are expensive and non-vectorizable
burst has a much easier time though with using pointers I have learned. many codes parts were not vectorized with a struct array but easily did with a struct pointer
that's not much of bursts doing, I think it's the LLVM optimizations
What is the way to get parallel write access to certain Component?
For NativeContainers that prevent parallel writing there's a tag: [NativeDisableParallelForRestriction]
so, I just need to be certain that I don't write to same entity slot?
I use it to read global data by entity. And then write to it's iterated entity
public ComponentDataFromEntity<Translation> translations;
Huh interesting, doesn't the NativeArray literally just wrap the pointer + length once safety checks are disabled?
It does, but the assembly often doesn't show any vectorization. kornflaks went pretty hardcore into it and helped me a bunch of times. There are some very weird quirks. Like, it's better to process everything in another Method than the normal Execute method.
Correct, otherwise you have a nasty race condition. If you can make sure that this will never happen all is good. Otherwise the algorithm has to be changed or other methods like Interlocked have to be used
uuuuugh
for some reason, instead of going to that one capsule in middle (player)
all these guys are just running in circles
even though judging by logic they should constantly try to rotate towards that player
float3 delta = target.value - translation.Value;
delta.z = 0;
float magnitude = delta.Magnitude();
if (magnitude < target.distance)
{
buffer.RemoveComponent<GetToPoint>(batchIndex, e);
buffer.RemoveComponent(batchIndex, e, target.tagsToRemove);
}
quaternion tarRot = quaternion.LookRotation(MathUtility.forward, delta.Normalized());
rotation.Value = math.nlerp(rotation.Value, tarRot, maneuverabilities[i].value);
translation.Value += math.mul(rotation.Value, MathUtility.up * speeds[i].value);
translations[e] = translation;
Here code once again, unwrapped from utils
target.value is player's position
ok
nothing wrong with logic
smth wrong with components?
basically, player entity supposed to go to one of those yellow entities
But instead, it's target is near him
even though, it's supposed to be equal of yellow entity
if (target.follow != Entity.Null)
{
target.value = translations[target.follow].Value;
}
I don't understand
is there any possible reason why Translation given can be that wrong
componentdata can sometimes not update immediately in the editor
are your entities moving?
and are you maybe reading some kind of localTranslation instead of world translation
I had an issue before where the object to move towards was a child of another which was offset
entities can only move forward, based on rotation
I am reading Translation
i suspect it is what calabi is saying
I had to make sure all the objects were the same root position
the problem is translation is in parentspace
yeah you have to be careful with child and parents and making sure there transforms are all similar if you want to move towards them
yep, still that problem
you might notice 3 entities on bottom
are facing that wrong point
meanwhile they should face player in middle
this time
I read LocalToWorld
red line should be the their facing direction
yet they go to that weird point somehwere else
try resetting the transform of the player and all its children
wdym?
If I move
player to somewhere else
they just pick some other point nearby
player
same thing
so movement is wrong also?
well, movement itself isn't
rotation is
movement code is literally just moving entity forward by amount of speed
it sounds like the child transforms are not the same as the parent and are offset in someway
for (int i = 0; i < entities.Length; i++)
{
var e = entities[i];
ref var translation = ref translations.GetAsRef(i);
ref var rotation = ref rotations.GetAsRef(i);
ref var target = ref gtps.GetAsRef(i);
if (target.follow != Entity.Null)
{
target.value = ltws[target.follow].Position;
}
float3 delta = target.value - translation.Value;
delta.z = 0;
float magnitude = delta.Magnitude();
if (magnitude < target.distance)
{
buffer.RemoveComponent<GetToPoint>(batchIndex, e);
buffer.RemoveComponent(batchIndex, e, target.tagsToRemove);
}
quaternion tarRot = quaternion.LookRotation(MathUtility.forward, delta);
rotation.Value = math.nlerp(rotation.Value, tarRot, maneuverabilities[i].value);
translation.Value += math.mul(rotation.Value, MathUtility.up * speeds[i].value);
}
here all code again
but they are same
here player
hmmm
this is one of yellow
their ltw is different
ugh
stuped dum components
I'll just try to switch fully on LTW
they don't look the same to me π
well read from LocalToWorld and write to Translation, Rotation,Scale. thats how it is supposed to be done
cool, I was right for once
i guess you should read some documentation π
I kinda try
but then I literally forget most of it
kek
you mean this one, right?
in this case i mean the Transform Package docs
oh
I wouldn't even try to read the stuff about local to world and transforms to be honest
I can't understand a word of it 
Ah wait they integrated the transofrms documentaion into Entities documentation now
nvm then
You dont have to understand the math to understand how to use it though. i think its pretty well written
to many words and graphs for me π
for entities with a hierarchy. for entities without any parents or childs it's wasted memory
I could cry everytime when I see the chunk capacity with a LTW π
so you dont have a localtoworld but a translation? interesting?
impossible
hybrid renderer depends on it
feels bad ngl
wish we'd have 2D transform
or at least some neutral LTW
so we could write 2d ourselves
Can u clarify how you do it? im intrigued (but i dont think i care about chunk utilization THAT much)
quite simple actually, only use LTW and explicitly remove Translation/Rotation on conversion. LTW has some nice methods to easily get position/rotation too
other way around, just LTW
ah thats why i was confused π
hehe yeah
everything has translation/rotation surely
no thats what was enzis point. you dont need Translation/Rotation components if you never want to move stuff. and in some cases even if you want to move sth you can write directly to L2W
I mean stuff converted from gameobjects though
i think if you use staticoptimizeentity Component only LocalToWorld is left after conversion
interesting I have used that before I did not know π€
you can remove the comps in the Convert method
unity does it too in the boids sample: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/ECSSamples/Assets/Advanced/Boids/Scripts/BoidConversion.cs
hmm why does this work. how do they guarantee the transform components were already added.
are they added in early conversion?
i wish there was a way to write an Entities.Foreach for the destination world instead of the Conversion world
I have some weirdness where some parts of my Instantiated visual entities are not parented correctly, I can see them at origin. Is this something someone else has experienced?
Converted GOs from either a prefab or GOs in a subscene always have a Transform.
has anyone used IJobEntity inside of an ISystem successfully? getting this error Partial declarations of 'TimingSystem' must be all classes, all records, all structs, or all interfaces
yes but somewhere they need to add the translation to the Entity to later remove it again. i wonder where that conversion is. cant look into code right now
do you have rigidbodies/physicbodies on some of them? this would cause them to unparent
ah, yeah that happens internally before the Convert is called
ah ok makes sense. Cant remember my exact usecase but i wanted to remove Components unity added somewhere in conversion and couldnt get it to work properly because of ordering. opted to do it in the first frame of the game then...
No I have my colliders as separate prefab entities, this prefab has only Mesh renderers, filters, visual effects. Maybe I just created some bad system somewhere x)
I need help before I lose my fucking mind
Does anyone have any idea why the score ends up not being incremented?
[BurstCompile]
struct PickupOnTriggerSystemJob : ITriggerEventsJob
{
[ReadOnly] public ComponentDataFromEntity<PickUp_Tag> allPickups;
[ReadOnly] public ComponentDataFromEntity<Player_Tag> allPlayers;
public ComponentDataFromEntity<Player_Component> player;
public EntityCommandBuffer entityCommandBuffer;
public void Execute(TriggerEvent triggerEvent)
{
Entity entityA = triggerEvent.EntityA;
Entity entityB = triggerEvent.EntityB;
if (allPickups.HasComponent(entityA) && allPickups.HasComponent(entityB))
return;
if (allPickups.HasComponent(entityA) && allPlayers.HasComponent(entityB))
{
entityCommandBuffer.DestroyEntity(entityA);
Player_Component mod_player = player[entityB];
mod_player.score++;
player[entityB] = mod_player;
}
else if (allPlayers.HasComponent(entityA) && allPickups.HasComponent(entityB))
{
entityCommandBuffer.DestroyEntity(entityB);
Player_Component mod_player = player[entityA];
mod_player.score++;
player[entityA] = mod_player;
}
}
}
did you try to breakpoint both if conditions?
potentially they might not even trigger
I know they are triggering because the entity does get destroyed
mod_player is the Player_component (from ComponentDataFromEntity) of the entity involved in the trigger event that has a Player_Tag
is Componentdatafromentity a copy π€
I can't remember because I haven't used that in a while but if they are like nativearrays then you have to use a certain structure to copy to them and or copyback to the main componentdata
They are writing it back
ok cool, figured I check for the most basic thing
Yeah makes total sense and was my thought as well
But seems like something odd is happening elsewhere
Here's the rest of the system
Ok, I'm a fucking idiot
I was reading the score from a copy that I forgot to keep updating after the initial assigment
will this remain a valid pointer after my function return:
public unsafe class Test : MonoBehaviour
{
private Segment _segment;
private Segment* _ptr;
public Segment* Ptr => _ptr;
private void Awake()
{
_segment = new();
_ptr = (Segment*)UnsafeUtility.Malloc(sizeof(Segment), UnsafeUtility.AlignOf<Segment>(), Allocator.Persistent);
fixed (Segment* t = &_segment)
_ptr = t;
}
}
or will it not stay fixed?
need to make sure my pointer remains valid for as long as the gameobject exists
its some what unknown to me what C# does with it outside of fixed
Ah no
You've overwritten your valid ptr
A pointer in fixed is only valid in fixed
right but outside of fixed its not valid once the function ends aswell
so i dunno how to keep it persistent
Technically it's not valid before the method ends
You've allocated a block of memory with malloc then simply ignored it
And will be leading that
As you never free it
so how do you set _ptr to the segment i make and not lose a valid pointer
would i have to set each field of _ptr manually without the ctor call and that will remain valid due to it being a class member scope?
until the gameobject is destroyed of course
you can not have ptr to _segment unless it's either a class and you tell the GC to pin it or if it's a reference then you tell the GC to pin Test
ah its a struct in this case as i use it for jobs
i'd seriously take a step back and consider what you're trying to do as it doesn't really make much sense
you're either going to spend months debugging segfaults, memory leaks or concurrency issues or more likely, all of them
well my job returns a list of segments but i have no way to know which monobehaviour they link to. so by using pointers i can change the segment data from the results without needing to know what gameobject they link to
since structs cant even have a reference to a monobehaviour they related to because of jobs
when i edit a segment after the job result, my monobehaviour then updates the mesh to match the segment changes
which is how they link together
do you never have any data tied to monobehaviours when you use jobs ? or you used entities for it all ?
i never use monobehaviours in my own projects
as for hybrid work, i strongly advice people never to call entity code from monobehaviours, only the other way around
ah for me i only need this one aspect using jobs so using entities is a full rewrite and not worth it
if you are not using entities then the general approach for using jobs is to populate native arrays from your monobehaviour data
pass it to jobs
then read back the results and write it back to the monobehaviours
but how do you know which data goes to which monobehaviour
since you can't have a field in the structs containing a reference to a monobehaviour
the monobehaviour that rights to index 5
is the monobehaviour that reads from index 5
oh so link by indices
that could be messy when removing elements from a list however
i really hope the release of 1.0 will have some good written tutorials to explain how to do a hybrid approach like this
i strongly suspect they will just focus on entities however
not sure what you mean by that
well if i remove a node from a graph lets say at element 3 then all nodes at index 4 onwards needs to shift down one or the link to the monobehaviour will be wrong
i would need to go through them all and change the index in the array so the monobehaviour still has the right index it connects to
var data = new NativeArray<Segment>(monos.Length, Allocator.TempJob);
for(var i = 0; i < monos.Length; i++)
{
data[i] = monos.GetBurstData();
}
var output = new NativeArray<Output>(monos.Length, Allocator.TempJob);
new Job {
Input = data,
Output = output,
}.Schedule().Complete();
for(var i = 0; i < monos.Length; i++)
{
monos.WriteResult(output[i]);
}
output.Dispose();
Data.Dispose();```
this is what i would consider a 'general' practice of executing work from monobehaviour in a job
oh i see
i don't know if you have access to a* pathfinding
but it has some really good examples with it's RVO simulation
which takes actors that are monbehaviour
passes them to a simulation in a collection of jobs
and writes it back
you mean the asset A* pathfinding project ?
yeah
oh didnt realise he added job system
he optimized his rvo simulation a good 1-2 years ago with jobs
i haven't looked at it recently
but i just remember it being a good example of how to use jobs with monobehaviours
it allowed him to take the simulation from a few k to 20k+
ah i see, i will look into it π thanks
I agree, it's the most sensible to not using MB code at all and instead passing MB data to jobs. Having said that, I'm not doing that. haha, got to rethink my approach in some cases because when every job is scheduled things that have never been a problem suddenly start being one. Like just reading and modifying some translation in a MB and writing back. Ooops, dependency problem, complete your job first. Annoying as hell
that said, for some cases it's hard to decide. like, your whole rendering of actors is still gameobject based. Now you have an asset like KCC, why implement Rival that is for DOTS? If you want to make this work for others or be open to the eventual DOTS animation there's no real answer what's the best solution. In that case, some MB code is okay, especially when UI is involved too. Some JobHandle.Complete() are okay to support this.
so Rider is complaining about NotAccessedVariable and wants me to rewrite this to the bottom line in the screenshot. Is this actually a problem?
currently not in the mood to read assembler ^^
thing is regular threads allow data from classes
so what did unity do with jobs that meant we couldn't use them
maybe I misunderstand the question but unity jobs are in unmanaged memory while regular threads work on C# heap memory
yeh but my point was why unity made it restricted to only unmanaged ?
the whole scheduling of jobs is nice and simple using unity's api where as i hate dealing with c# threads generally
would've been cool if they could support managed too even if we cant use burst just for the parallel work done
managed memory is handled by C# and the GC which has LOTS of restrictions so Unity said, fk it, we just malloc in C++ code and give this memory block to C# users.
ah i see
i just like the ease of it setting up the threads for me without me needing to know how many my cpu can handle etc
more so than the the burst compiler which is a nice perk on top
what I mean with restrictions is, C# can't even make sure that an array is linear memory. Newer versions of C# have stackalloc but I don't know if Mono has it as Mono is more like the bastard child of C# anyway. And yeah, I'm really not an expert on the C# memory front. I just believe they had very good reasons to just say screw it, we build our own.
isnt native containers linear memory ?
so we could just use them anyway even if not using jobs/entities
yeah sure
as long as you remember to dispose i guess
wait do they dispose if you exit an application automatically
problem is that native containers are quite slow on main thread C# code. I learned this the hard way
yeah, I don't know what all the reasons are but NativeContainers are only good in burst jobs IMO
i would've thought the linear memory would give them the performance boost
was this from your benchtesting or you read it some where?
there's some form of marshalling from unmanaged to managed data as overhead which is the problem
I tested this extensively.
I mean it makes sense if you think about, the data has to go back and forth from unmanaged to managed memory
i thought native containers were already managed so why does it marshal from unmanged ?
native container data lives in unmanaged memory. now if you want to read unmanaged memory like an array element in C# land it has to go through marshalling. At least that's the way I understood it. Could be totall wrong lol but that's also how it works with C++ plugins so I think it's very much the same
to be perfectly honest, I don't know what's the big problem with unmanaged and managed memory. I would think it's all the same but it really isn't. For a more detailed answer you'd have to ask google
yeah seems all very technical and has to do with handling/controlling the garbage collector
the GC constantly scans and frees memory which just doesn't happen in unmanaged space
where it's all manually done
so if you malloc in unmanaged thats persistent until you free it
because GC wont touch it
yes
I find the abstraction highly annoying. So much pain because of it π
but that's what you get for lazy devs who can't manage to free memory ...
I should not complain, C# is an amazing language otherwise
for me the problem was learning how to do it myself, when ever i asked about pointers on StackOverflow for C# all i ever got was replies of "don't do it"
or "why are you using structs when classes are references"
except unity does not let me use classes for jobs so i dont have a choice
so information is rather scarce when you touch unsafe
yeah don't bother those are just opinions. kind of unusual for SO to have those answers
they didnt even answer they were just comments i never even got an answer for it π
my primetime on SO was around 2008. seems it went downhill
every attempt i made my pointers were becoming invalid so im doing the mono pushing data to arrays method instead now
huh, never checked that /// <summary> /// A "blank" Entity object that does not refer to an actual entity. /// </summary> public static Entity Null => new Entity(); Is it better to compare for Index == 0?
No, just do myEntity == Entity.Null
i mean, it's a micro optimisation but doesn't the static involve a random jump in memory just to pull in index/version 0?
last i checked you can have an entity with index 0, version 1
Also the implementation could change. So safer to not assume that index 0, version 0 is "null". Besides it's a lot more readable
you'd have to check but i'm pretty sure burst would probably inline this
huh, never checked how a static looks inlined
what i mean is just that it'd just look the same if you did
myEntity == Entity.Null
vs
myEntity.Index == 0 && myEntity.Version == 0
Yeah this seems like a really easy thing for the compiler to detect and inline
There's some good reading on SO here (not exactly what you're asking for though but still a good read) https://stackoverflow.com/questions/12279438/performance-of-static-methods-vs-instance-methods
burst is also extra aggressive on this
this is hard to read π
I certainly don't see any Index == 0 && Version == 0
looks like it reads from a static pointer
and goes through the normal compare method
did you create a comparison?
eh, I can't read that shit, can't even find the else. having tested the manual 0 comparison it looks like the same code
i can't find the cmp for the if, just the for loop
so I just say, all is good, burst is great and knows much better what it does than me π
iterating through monos to populate my native containers + the job + iterating to put the data back on the monos ended up being no faster than not even using jobs to start with and using regular managed stuff π¦
the job is fast but the other stuff undoes all the benefit
then get rid of the monos π
uhm, is IJobEntityBatch patching referenced entities created by a commandbuffer in a comp or do I have to use commandBuffer.SetComponent to trigger the patching?
only command buffer patches entities
ok thanks
using entities is a big undertaking at this point tho
plus i dont think i even have it in 2021.3
yeah, I see, then the only option is to reduce the overhead of creating and writing back as much as possible
keep the native containers but use an index in the MB instead so it knows where its data is
might be annoying though when GOs are created/destroyed :/
yeah hm i guess i could use events to notify each other
between the graph and the monos
so 2021.3 is required?
well not really unless 2022 breaks everything π
99% of the project just uses regular mono stuff
since none of it is heavy work for me to need to optimise
its just the algorithms working on graph data that is the more expensive stuff
Using Entities in 2020.3 would make a lot of things easier
Ok so to make a tree structure in a value type way, a tree would be a node struct that takes other node struct a as values, right? Like this:
struct Node{
public Node leftChild;
public Node rightChild;
}
usually you'd just store the index to the other nodes
otherwise you can't make changes to your nodes
This is read only, constructed beforehand and put into memory as a native something or a blobbable whatever
But that is a good point.
And it might be easier to deal with traversing it that way but it might not matter
My use case is basically: after I make sure my AStar works Iβm going to replace the super slow βget closest pointβ function with a K-d tree. And while Iβm not on that stage yet Iβm thinking about how to implement it now@
have anyone used Etitas yet? Is it better than dots in its current state?
is there any particular reason why we shouldnt use unity's native containers over regular arrays etc for general code?
native containers are super slow
thought they were faster which was why they were beneficial for burst/ jobs ?
they are thread safe
also unmanaged
which lets them to be used with burst
but in normal code
you don't really want them
ah okay
do you mean slow in terms of doing for loops over them ?
or reading and writing to them too
slow in literally anything
ah
so i should just convert say a Dictionary to NativeHashMap only when i need to use a job then
eeeh, Idk in this one
i see
i just found this post https://forum.unity.com/threads/native-arrays-approximately-an-order-of-magnitude-slower-than-arrays.535019/#post-3526924
seems its actually not that bad except in editor since safety checks are stripped in build or something
Is there a way to ensure my system gets Initiated in certain order?
I basically want to have Input class managed in ECS System
and in order for all events to work correctly
I need to ensure it gets created the last
so all other can subscribe
before events invoked
public class InputController : SystemBase
{
private static PlayerControls _playerInput;
public static PlayerControls PlayerInput => _playerInput;
public static event Action<PlayerControls> OnInputInit;
public static event Action<PlayerControls> OnInputDispose;
protected override void OnCreate()
{
if (_playerInput != null)
{
OnInputDispose?.Invoke(_playerInput);
_playerInput.Disable();
}
_playerInput = new PlayerControls();
OnInputInit?.Invoke(_playerInput);
}
protected override void OnUpdate() { }
}
It looks like this
if for some reason InputController will be created before other systems
they will not get Invoke
why?
a) event
b) static event
c) static fields
but to answer your question
static fields mean you can't run multiple worlds in the same editor
if you ever want multiplayer shooting yourself in the foot
oh, I see what you mean
I guess
other way
is to do
World.GetOrCreate
instead of static
i don't like systems ever accessing other systems but anyway off topic
to answer your actual question regardless if i think you should be doing it
not really
but just don't use events
wel
well
not exposed events*
why not?
use a custom event subscriber