#archived-code-advanced
1 messages ยท Page 168 of 1
And 1600x900
@cedar ledge the most important thing here is that the scope of the API is very small
what happens if somone has 1280x720 and i set it on unity 16:9
so making something drop in compatible with existing students' code, provided they didn't modify anything in this folder - which wouldn't be compatible with your current approach anyway - is really tractable
1280x720 is a 16:9 resolution so... unless the game hardcoded stuff for 1920x1080, it should be fine
what happens is the game is 1280x720
why wouldn't my approach be compatible?
but 1280x720 is not 1920x1080
No, but it's a 16:9 resolution
it's 50% smaller but it's the same ratio
if they modified something in this folder, that would require the end user to do a merge
if htey made pre-existing changes
I fail to see the (advanced) programming issue here.
so if they modified stuff in this folder
they dont
i would have to learn mirror
which is valuable!
i already kinda know it but id rather just fix his code than gut it
part of your engineering journey here
is to take the red pill and use a library
instead of taking the blue pill and modifying this existing stuff
(insert picture of morpheus)
i don't learn anything by just translating mirror
as i said, this is the start of a very long journey for you
do you want to be "interested in solving hard problems in machine learning and data science" or do you want to make games?
what does converting coroutines to async/await have to do with that?
someone who uses Mirror makes games
someone who... i'm not going to take a dump on something your'e doing, it's going to be good either way
it's worth doing both for sure
Making a game isn't the objective here though, updating a library used by students of a course to have cleaner internals is the goal
mirror has clean internals
Mirror doesn't have the same API as the library
and would require revising the course
The instructor doesn't want to do that, hence this refactor
i am advocating for authoring an facade
That could potentially work, but it depends on how much work it would be
I think your approach is valid but I'm avoiding using libraries because he's already written literally everything
A facade might end up being more work if the API and workflow sufficiently differs from Mirror
The code works without using any libraries
I'm just slightly changing and optimizing it
okay... but reinventing mirror is hard
you're underestimating how hard it is
Is it possible to change the players forward direction to be relative to the cameras direction? Im using the cinemachine camera
Why would I need a networking library for my networking library that works as is
that's an excellent question
if this were the matrix, morpheus would stare off into space, grinning
transform.forward = camera.transform.forward;?
But would that work with cinemachine?
I don't see why not
anyway, i think for you personally, what i recommended will be best
There's not really anything "special" about Cinemachine here, to my knowledge
you'll be around longer than this class
Let me get my code real quick
think critically about what you want to learn and do
Theres no code you can really edit
But you have the camera transform
i really want you to thrive
Why would you need to edit code? You have a camera, you have the player
you can succeed in authoring a facade to mirror
Not in my code
is using mirror some sort of cult that i'm being inducted into?
lol
i think that's photon
You can add a component field to your class that references the cinemachine camera
[SerializeField] CinemachineVirtualCamera targetCamera; or whatever is appropriate
Ok... then I add the
transform.forward = camera.transform.forward;?
[SerializeReference] ICinemachineCamera targetCamera; would probably also work
@cedar ledge i think you already know, there is limited value of a bespoke networking library for a game design class. i haven't taken the class, so it's hard to say. but if it's all the same to the students, who are not going to change the code, they will be better off using mirror. since that's not an option, just a teeny tiny gentle facade will be helpful
In my fixed update
i think you're misunderstanding my goal
I'm not learning how to write a networking engine per se
Why fixed update? That would result in jitter
I'm learning how to use async/await/tasks
FixedUpdate should be reserved for physics code
and improving the course
Oh
Update is where input and anything visual goes
camera code should be in LateUpdate()
^
Ohh I didnt know that
That ensures it sees the "final" state of the frame
Ill try this out
because unity doesnt have monobehavior priority
It does
booo
You can define execution orders in project settings, or in code via [DefaultExecutionOrder]
[DefaultExecutionOrder(int.MaxValue)] should cause the component to receive events last
I had a problem where my async infinite loop was continuing after playmode was stopped. Is there a way to fix that without needing UniTasks? Or should I add UniTasks to my project for that 1 functionality?
You would need to use a CancellationToken, make all of the APIs cancellable, and create a CancellationTokenSource that is cancelled when play mode exits
is there a callback for exiting playmode?
I'd recommend using that, it's simple enough
Is the I supposed to be there?
Yes, it's an interface
Ok
I saw it referenced in the Cinemachine docs
Not a convenient one like there is for entering it, afaik
Ok
Unity is the ultimate game development platform. Use Unity to build high-quality 3D and 2D games, deploy them across mobile, desktop, VR/AR, consoles or the Web, and connect with loyal and enthusiastic players and customers.
yeah lol
It says that the namespace ICinemachineCamera could not be found
For entering play mode there's https://docs.unity3d.com/ScriptReference/InitializeOnEnterPlayModeAttribute.html which also supports scenarios where you have domain reloads disabled
Oh
damn there's a whole new rabbithole
that sounds really appealing
hahahaha
removing 90% of the playmode wait time by simply managing your own static fields and unregistering callbacks when you stop
It's significantly faster
ooh i didnt know about this, you can learn a lot just by reading random msgs here lmao
im 100% looking into that
I highly recommend it, assuming it's not a massive refactor for your project
I design all new codebases with it in mind
idc if it is, I'm changing my mind
lmao
this is the proper way to write code and you should implement the proper design patterns to account for it
I agree
how dare you subscribe to an event without unsubscribing when playmode exits!
so we've got
onenable
awake
start
oninit
[RuntimeInitializeOnLoadMethod]
onvalidate
any more?
The constructor 
*dead*
It's safe to use as long as you only do thread-safe things in it
i guess this is a good time to ask this question
Meaning you just can't use most of the Unity API from it, since it's not guaranteed to run from the main thread
if I need to reference a monobehavior should I have a
-serialized private field
-property that checks to make sure the field isn't null. if it is null, throw a missingreferenceexception
-use the property everywhere
-initialize the property in Start()
is that a good pattern?
Why not init in awake?
I would initialize in Awake, not Start
ok
Awake is the de-facto "user constructor" for components
i meant to say awake, that's what i am doing
What's the benefit of using a private field + property for it?
Finer-grained access control
nullable references
Though I'm skeptical of the advantage of throwing MissingReferenceException manually
Sorry I meant serializedField
is this asking why not just make everything public?
No, why using SerializeField lol
The editor will allocate "fake null" objects that throw MissingReferenceException when accessing properties that require the native object to be alive
so you can set the reference in the inspector?
At runtime those are elided and you get real null, because it's less expensive and you shouldn't be getting those errors at runtime because you should have debugged it in the editor
If a method throws, it is automatically disqualified from inlining
So you pay the cost of a method call for every access of that property
Hmm okay
Not the biggest fan of setting references in the inspector, thus the question
You can avoid that cost by making a separate static method that throws the exception, and calling that instead of throwing directly
Which will allow the property to be inlined by the JIT
how else would you set references to gameobjects?
so instead of throwing, i should call HandleReferenceNotSet()?
which throws
[SerializeField]
SomeComponent someComponent;
public SomeComponent SomeComponent
{
get
{
if (someComponent == null)
ThrowMissingReferenceException();
return someComponent;
}
}
static void ThrowMissingReferenceException()
{
throw new MissingReferenceException();
}
also where do you guys learn this stuff?
Singletons or scriptable objects
But not in general
the thing that has these references is a singleton lol
I spend a lot of time working on low-level projects that involve assembly, etc (I've written a process injection + method detouring library entirely in C#)
but I also spend a lot of time in the #lowlevel channel on the C# discord
I've seen a lot of scripts recently using SerializeField for every field, adding HideInInspector attributes as well. I was just wondering what's up with that
You learn a lot of stuff in there, especially since various Microsoft staff that work on .NET hang out there
critique my code?
[SerializeField] is the only way to save a field if it's non-public
It's a no-op on a public field, but I guess it expresses intent
Save for what?
unity's editor
It gets stored in the scene data
Need a thought problem solved.
I have a player object with a member that's a "missions" data structure - working missions, completed missions, etc. The server manages all the data (stripping out the incoming object to prevent cheating), sending it back to the client periodically or whenever the client requests an update (typically when a mission finishes - they're on timers).
The problem is that the client sees a countdown of the mission icon, then when it finishes, requests the update from the server, which does the calculations (did the mission succeed/fail, what was the reward, how many crew died, etc) THEN sends the update back to the player.
If the player is lagged, they'll see a "downloading" icon for a few hundred ms (or more!) while they wait for an update from the server.
I'm looking for ideas on how to solution this in a clean and not-bug-prone way. Some things I've thought of, with drawbacks:
-
The client reads directly from the data structure in memory. I could make a copy of it that's "5 seconds behind" the truth, and request the updates from the server on the "real" object (not the lagged/fake UI object). Seems complicated, though, and bug prone.
-
I could just have the server finish the mission when there's 5 seconds left, and have the finished mission appear in the "completed missions" AND the "active" missions data structures, so the client can just immediately look up the completed mission data, but again, this seems complicated and bug prone.
Example movie shown - it's very fast (just a flicker of a red placeholder icon) but that's because my game server is on localhost at the moment.
If it's not saved, it won't keep its value between runs of play mode
Or have that value in a build
Sure
Looks fine to me
@ me for responses/ideas. AFK a little bit.
// Screens
private GameObject Login => _login == null ? throw new MissingReferenceException("Login not referenced in inspector") : _login;
private GameObject Signup => _signup == null ? throw new MissingReferenceException("Signup not referenced in inspector") : _signup;
private GameObject Submit => _submit == null ? throw new MissingReferenceException("Submit not referenced in inspector") : _submit;
private GameObject Home => _home == null ? throw new MissingReferenceException("Home not referenced in inspector") : _home;
``` you can just `private GameObject Login => _login ?? throw new MissingReferenceException("Login not referenced in inspector");` here i think
You can also use a helper method to simplify them
yeah
Let me write an example
isn't null coalescing unity objects a bad idea?
Yes
my question exactly
Yeah, with [CallerMemberName] to auto-retrieve the property name from the method that throws the exception
^ that's exactly what I was just writing lol
I use asserts on Awake(), not the only approach, but just how I've always done it.
private void Awake()
{
if (!_isInitialized) Initialize();
LocalAssert();
PREFAB_DEBUG.SetActive(false); // just a background to make designing the prefab easier
}
private void LocalAssert()
{
Assert.IsNotNull(_playerStateManager);
Assert.IsNotNull(_idleGameManager);
Assert.IsNotNull(_crewManager);
Assert.IsNotNull(_missionPopupStarsContainer);
Assert.IsNotNull(MissionChoiceButtonPrefab);
Assert.IsNotNull(MissionIgnoreButtonPrefab);
Assert.IsNotNull(MissionSelectOrDisplayCrewButtonPrefab);
Assert.IsNotNull(InProgressOrCompleteCrewIconPrefab);
Assert.IsNotNull(BottomMenuResponseChoicesContainer);
Assert.IsNotNull(BottomMenuSelectCrewContainer);
... etc x100
}
since they get compiled out
(for production builds)
private GameObject Login => ValidateAndReturn(_login);
// ...
static T ValidateAndReturn<T>(T value, [CallerMemberName] string memberName = null)
where T : class
{
if (value == null)
ThrowMissingReferenceException($"{memberName} not referenced in inspector");
return value;
}
static void ThrowMissingReferenceException(string message)
{
throw new MissingReferenceException(message);
}
ah yeah unity does overload ==, mb
Like
static void ThrowMissingRef([CallerMemberName] string caller = "") {
throw new MissingReferenceException($"{caller} not referenced in inspector");
}
that's what i have
I've been working with typescript lately and was a bit shocked how little null coalescing works in unity, in comparison
Blame Unity for lying about null 
why can't they fix that?
Or maybe it gets better with C#8, no idea
They can, but they decided not to
It's not a C# problem, it's a Unity problem
i thought you couldn't overload ??
You can't, because it's a true null check
Overloading that would defeat the purpose
I meant with newer Unity versions using c#8
Maybe they did some changes
It's probably not fixed
They didn't
I'm stuck in 2019 currently
I regularly use Unity 2022.1
its not something that requires a fix
Since it would mean breaking all projects
And it's really a pain in the ass sometimes
Basically, Unity lies and pretends that null doesn't just mean null, it means null or destroyed
especially things like this
So the actual C# object is not null, the pointer to the C++ object is
i just have to tell roslynator to shut up whenever it recommends fucky wucky null stuff
UnityEngine.Object and all classes that derive from it are just wrappers around a pointer to a C++ class
Do you know if C# will at some point get type guard functionality?
That depends on what you mean by that, C# 11 is getting support for automating ArgumentNullException
void SomeFunction(SomeClass parameter!) {} will throw if parameter is null
like if obj.GetType() == typeof(SomeScript) obj.someValue would work as obj would be automatically cast to SomeScript
Damn
It would never have that syntax though, even if it were added
It wouldn't have to
The closest thing you get currently is this:
if (obj is SomeScript script)
{
// you can use 'script' in this scope
}
if (obj is SomeScript someScript) {
}
``` maybe this is something similar
If someday C# gets variable shadowing, you would be able to do if (obj is SomeScript obj) or something similar
I'm really enjoying typescript for these kind of features lol
I miss being able to use C# 10, I have C# 9 and .NET Standard 2.1 in Unity 2022.1 at the moment
But not having all of the convenience of modern .NET APIs and new features in C# 10 sucks
Writing software outside of Unity is blissful and then I go back to hacky land
I don't really know much about C#10 tbh
But I'm also not working much with C# outside unity projects
The interpolated string handler stuff is really nice
With the appropriate runtime types available, $"Some text: {variable}" is no-alloc aside from the string
So if variable is an int, it won't allocate a box
File-scoped namespaces are also really nice
Damn
namespace SomeNamespace
{
class SomeComponent : MonoBehaviour
{
}
}
can be
namespace SomeNamespace;
class SomeComponent : MonoBehaviour
{
}
less nesting
Cool
Plus there's stuff like generic attributes and static members in interfaces
Those two are the real big ones
Ah, I knew about static members in interfaces
It's the foundational feature for generic math
Never knew if I liked that they are changing interfaces though
With static abstracts and generic math, you can define a Vector3<T>
Does this work in Unity already numbers ??= new List<int>();
If you're using a version of Unity that supports C# 8, yes
it's necessary
which is some 2020+?
2020.2+
aye
You can use C# 8 with older versions if you replace the bundled Roslyn
before, you couldnt change in-production interfaces because it would break everyone's code. now, you can have a default implementation and such
I don't want to mess with my current project haha, I'll be fine for now but definitely switching to 2020 for the next one
the alternative is having IMyInterfaceV2
makes sense
Where I work we have projects on 5.x, 2017.x, 2018.x, 2019.x, 2021.x and 2022.x 
i started with 4.5.4f1
back when you didn't even need to getcomponent, iirc
Or didnt have lighting lol
Unity 4 had Beast
5.x was when the engine really took off in popularity
old person
maybe started with 7yo
I'm turning 23 this year lol
lol
ok, old person
I started in 2014 (14 y/o)
Speaking of, if you want to see something crazy: https://github.com/DaZombieKiller/ExecutableMemory.NET
This can technically work in Unity if you use 2021.2 or newer
Haha nice
I like showing it off because it uses a bunch of features a lot of C# programmers don't even know exist, like function pointers
they're not called function pointers though, right?
So is it writing asm in c# that is being compiled back to asm ? ๐
i use the asm to write the asm
that you can then execute, yes
I have a library I wrote that can load the .NET runtime into any running process, which I also do that in: https://github.com/DetourSharp/DetourSharp.Hosting/blob/master/DetourSharp.Hosting/RemoteRuntime.cs#L207
Check that out if you want to get a face full of assembly
Had my full face of assembly with my pokemon hacking tutor couple of years ago lol
well it's called games engineering so
can I use C#10 to defeat rust's borrow checker?
lol
why put throw new MissingReferenceException(message); in another method instead of inside of validateandreturn?
Because otherwise it won't inline
is that a failure of the jit or is that how it should be?
Gimme a sec, there is some documentation in the Windows Community Toolkit that covers this
then why isn't public static ThrowException<T>(message) where T : Exception a thing?
just have it once in the Exception class
Because an exception isn't guaranteed to have the constructor necessary to implement that
derived exceptions would have to implement that?
Yes, but there's also no way to statically provide that method to ThrowException other than a static abstract interface method
And static abstracts are a brand new feature
(Sure there's reflection, but that would never be considered because it's slow and this would be a performance-focused thing)
are you sure? just put this in Exception.cs:
public static void ThrowException<T>(message) where T : Exception
{
throw new T(message);
}
and to answer this: neither. There is work underway (afaik) to improve it though
That wouldn't work
Try to implement that right now and you'll see why
You can't do new with arguments for a generic T
Plus having separate throw methods is mostly a performance-oriented thing, so you wouldn't need to do this everywhere
Just in "hot" locations like property accessors etc
public static void ThrowException<T>(string message) where T : Exception, new()
{
throw new T();
}
this kinda works lol
You could do that if you then added some kind of virtual Init to Exception, but it's far too late for that to happen
The design of Exception is set in stone, and has been for decades
(imo it wouldn't be better than what we have now anyway)
There's a proposal to support constraining on new with arguments, which would be a better approach
well you wouldn't have to worry about the inline issue
The ThrowHelper pattern is basically just a way to optimize for code size, which improves usage of the CPU instruction cache, and allows the JIT to inline the caller since there is no direct throw inside it
Hey all. Have a performance / design question around fog of war. It's a bit of an essay so dropping it in as a text document. If anyone has any thoughts about my proposed solutions or a better way to do this overall would love to hear them!! If nothing else got some rubber duck going here.
So basically x = SomeProperty; is roughly equivalent to:
x = get_SomeProperty();
but if get_SomeProperty throws, it is disqualified from inlining, so you'll always pay the cost of calling get_SomeProperty
if the throwing is moved to a helper method, the disqualification is instead applied to that helper, allowing it to be optimized to:
if (_somePropertyField == null)
ThrowException();
x = _somePropertyField;
and if the JIT is able to determine that _somePropertyField == null is never true, it can even eliminate that code entirely, leading to the optimal case of
x = _somePropertyField;
It can
[field: SerializeField]
public int SomeProperty { get; private set; }
I forget which version support was added in
I think 2019 or 2020
2020
that's serializing the implicit backing field but yeah
(i think)
Right, did you mean computed properties?
so could you combine that with validateandreturn?
Not currently, but in C# 11 maybe
exactly
In C# 11 it would potentially look like this:
[field: SerializeField]
public int SomeProperty
{
get => ValidateAndReturn(field);
private set;
}
don't tease me with something I can't have ๐ข
Whats validate and return then?
A method that will handle the throwing etc if the value is null
public static T ValidateAndReturn<T>([NotNull] this T? checkme!, [CallerMemberName] string caller = "")
{
if(checkme == null)
{
ThrowMissingReferenceException($"{caller} not referenced in inspector");
}
return checkme;
}
If we had source generators it could be even better
[SerializeProperty(ThrowIfNull = true)]
public partial int SomeProperty { get; }
so thats the term for the thing i was trying to invent earlier lol
I was trying to create a source generator for a propertydrawer
Ah right, missed that
Unity has support for non-incremental source generators, but they suck
I don't know if incremental ones are supported
I went down the rabbithole so far
I had to stop myself once I started coding in IL, in C#
non-incremental generators are basically really slow and will run on basically every keystroke
source generators output C# code, so you can use them to write boilerplate for you
but that also means you can't do anything in a source generator that you couldn't write yourself
they're really useful for serialization, configuration, etc
return checkme!;
Yeah
See, that's what I was talking about earlier
I think there's a nullability attribute you can use to define this properly
yep
but it's probably not available in Unity
It should know that it cannot be null at this point
There is no way for it to know that without nullability annotations
It would need to perform interprocedural analysis
Which would be expensive
but then it would also probably not inline if it knew it could throw
why? 3 lines above it's checked and throws if so
As far as the compiler is concerned, that's just a normal function
It doesn't know that function just throws
And it would be too expensive to check that?
It would make the compiler (and by extension, Visual Studio) much slower, yes
How is it that this works in different languages then? Because they're not structured like c#?
It depends on the language, many of them just require annotating functions like C# does
It wouldn't even work if you'd put an else there would it
Other languages have complex systems that make it doable without being slow
C# could get the functionality, but there's no incentive to when nullability attributes work perfectly fine and are less expensive
???
It's a package, not built-in
Also the attribute I was thinking of: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.doesnotreturnattribute?view=net-6.0
how do I add the package?
If you mark your Throw* method with that, then you won't need to silence the nullability warnings
Unity doesn't support NuGet, annoyingly, so you'll need to download the dll and add it to your project manually
1 sec
Theres a package that allows nuget support
You could try that, though I've not had much luck with many of the NuGet solutions for Unity
I did end up only using one package so I did it manually, but I remember finding tools for that that did look promising
should I get that or should I just have my own method that throws?
Here are the DLLs (and the XML files so you get documentation in the IDE) needed to use CommunityToolkit.Diagnostics in Unity
This is for .NET Standard 2.0
2.1 ....
So it works with the .NET 4.x and .NET Standard 2.0 presets
Are you on Unity 2022.1?
yes
Huh, okay, gimme a sec and I'll grab the NS2.1 files
what does the throwhelper do better than writing my own 1 line method?
Nothing, it's just convenient since it offers pre-written methods for a bunch of common exception types, and has them annotated with DoesNotReturn
In Plugins
Rather, in Assets/Plugins/
Using CommunityToolkit.Diagnostics also means you can use the Guard APIs, which is nice https://docs.microsoft.com/en-us/windows/communitytoolkit/developer-tools/guard
can you come please teach at my uni
hahahaha
we have great things like
admin firing teachers who unionize
high turnover
1600 students
At one point I did consider becoming a programming teacher, but quickly decided against it
I just do writeups now for anyone who cares
Knowledge is freeโข๏ธ
btw @final steeple the MaterialPropertyBlock really blew my mind today lol. why tf does unity not offer renderer extensions for directly using that by default
I ask myself "why" in reference to Unity every day
there seems to be a ton of stuff to learn that just NEVER gets talked about on "advanced" unity youtube and university classes
Speaking of, this one is more commonly known but you can do even better than that: cache the hashes for shader property names
and pass those hashes instead of "_Color" etc to the property block
I have a source file for this on my GitHub Gist, sec
I have like 500 pages of random stuff on there so this could take a while
good luck lol
please PLEASE tell me that all this knowledge makes you the big bucks at your senior level position at $Company
Probably because of Unity's target group
Shader.PropertyToID is thread-safe which is why it can be initialized in statics like that
So then you can do block.SetColor(ShaderProperties.Color, value) instead of block.SetColor("_Color", value)
and you won't pay the cost of hashing "_Color" every time
You can do the exact same thing for the strings you pass to Animator, though I forget the name of the function for getting the hash value
Well I'm definitely not "rich" lol but I love my job and I live comfortably so that's enough for me
Sure, if I didn't want people to use it I wouldn't have posted it lmao
At some point it might be nice to have a source generator for this, when Unity supports them properly
The source generator could look for stuff like ShaderProperty.unity_LightColor and generate the unity_LightColor field automatically
git copilot lol
lol
honestly these sorts of paradigms are why I backed off from learning shader programming in unity
the really obtuse way to accomplish or reference certain things is really different
Not really different in c++ actually
yeah the way you have to include the .cginc files, guard against multiple includes, etc. is just not something i want to do
Yeah I really hate how Unity does shaders
But honestly I hate how shaders work in general
are you asking how to implement fog of war in 3d?
lol
Something like ComputeSharp (where you write shaders entirely in C#) would be really nice to have as a more general thing
actually
@undone coral It is in 3d yes, but if you look at the text document it details the question in much more detail
I can paste it here, it's just like 3 paragraphs. Felt like hijacking.
for like an RTS?
have you looked at a pre-existing implementation?
Correct
from the asset store?
@undone coral I have, this was a bit of a learning exercise. Being somewhat judicious about not just buying everything, as I want to improve my knowledge of algorithmic complexity and so forth
Although I could buy it and look at it I suppose
i suggest using vfx graph, which can cull particles to the camera. then provide a texture that corresponds to visibility in world space
A good friend actually created something like that during a gamejam lol. Typed in any word and it would create a game based on that
then you just need to express map visibility as a bitmap, which is not hard
I will take a look at that. However, the problem I am trying to solve is really one of cpu time and not a rendering issue per se
(although I have some of that going on with custom shaders but that's another battle)
I don't think that would help with the cpu compute time problem I have currently unless I am mistkaen
fog of war for an RTS is just a bitmap
not sure why you'd have a CPU problem, but instead of going into it
you should have a bitmap representing what areas have been explored by units
is that your approach?
Hey All. I'm working on a fog of war implementation, and while I have a working solution, making it very performant has proved troublesome. Hoping some more performance minded folks can help me out. What I have now is a grid system overlaid against all the terrain and a projector that renders everything dark that is not visible. This is a two layer FOW system where there is 'full black / invisible' terrain that is unmasked on discovery and is permanently visible, and then a second layer that greys out whenever all units on a team cannot see it. Teams are managed via a bitwise which works well. The structure is stored in a standard array, and characters query a singleton grid via a vision calculator class an mark squares visible or not visible.
Now, that all works fine if the calculation is run by ever character every frame. However, the performance drain on the cpu is insane. I could make it multithreaded / async etc, but now I'm just abusing the entire cpu instead of one core. To help the performance, I wrote a check that detects when a given 'vision' character crosses a new grid square, and then calculates. If they remain in the same square, a high performance check is done and no calculation is made. The check is extremely performant since I just divide the character position coordinates by the grid spacing to determine what square they are in.
While the above method increased performance by about 10-50x, what I am struggling with is tracking who cannot see a given square. I tried left shift / right shift bitwise and a concept of a 'check out' system using First In First Out as well as Last In First Out to assign who saw the square originally or most recently and assigned responsibility of marking it invisible to them. For example, character A saw square [1,1], so it's his responsible to mark it dark and set it to zero when he can't see it. This mostly works, but I end up in some funny race condition scenarios if A sees it first but then character B also sees it and then A marks it unseen, there is a gap until B recalculates and marks it visible again. And there is thrashing back and forth as they fight over visibility. So grid squares end up dropping in and out of visibility.
Solutions I have thought up to the problem (But all constrained by performance which is where uncertainty of where to go next comes in):
-
Have every character calculate vision synchronously on a timer (every n frames) instead of when they cross a new grid square, that way everything is synced up visually
This will likely cause frame drop spikes since 200 characters will be trying to check every grid square all at once. Still, maybe acceptable. -
Set up an event system where when a given character 'sees' a square it subscribes to an event system that if anyone marks it unseen they notify them first. So say character A B C and D all see the square, A B and D will get notified if C marks the square invisible and get a chance to recalculate and mark it visible despite what C says
-
Instead of using a traditional array with a bitwise for team visibility, use a dictionary with lists as the value that tracks all vision characters that can see that given square, where the gridsquare is the key
I should point out every character is checking every square on the map. I could easily improve this by heuristically checking what is within the range they can see before running it through a circle check algorithm. This will likely improve performance substantially, but does not solve the overall above problem to achieve maximum performance.
Any thoughts?
๐
^ is the question I had in the document that outlines the problem in detail
LOL I was trying not to paste that blob
i didn't read it because there's just a right way to do this
and it sort of doesn't matter what you're already trying
you can easily represent the area that's navigable as a bitmap, and simply write to that pixel and adjacent ones as units explore... even if there are 200 units
hmm I do understand that, although I'm not sure it helps with fundamental understanding. I did follow a guide on how to do this though; the guide just resulted in a solution that was not terribly performant.
how do you know it's not performant? profiler?
That works well for general FOW and is what I am doing, the trouble is tracking temporary visibility
Yeah I profiled it and until I implemented a heuristic to only check sometimes I was getting massive frame dips. If I set the grid squares large enough it does work
And is reasonably performant I suppose
Sounds like you could use a spatial partitioning algorithm
mmm dips isn't the word, just low fps overall
void OnUnitDidMove(Location previousLocation, Location newLocation) {
DecrementVisibility(previousLocation);
IncrementVisibility(newLocation);
}
that's it. as long as visibility in a region is greater than 0, it's visible by a unit
I liked that idea above, but the problem is you need to embed uniqueness I think
nah
otherwise every time the calculation is run it increments
and only decrements when it goes out of visibility
it will net out the same if the location hasnt' changed... but if the location hasn't changed, don't call onunitdidmove
you can use a variety of approaches to determine if a unit hasn't moved
anyway, is uggest using an asset for this
there's just a tradition to doing this stuff and i wouldn't overthink it too much
well, moved is still not very performant, which is where the grid spacing comes in, e.g. only triggering when a new grid space is entered. That part works very well.
how do you figure?
what did your profiler say was wrong?
concretely?
do you have a screenshot of it?
I am essentially looping over every grid square for every unit every frame
that's a lot of every's
and the cpu was burning so to speak
what did the profiler say
I can profile it quick, but it says that class is doing too much work
okay so you haven't profiled it
99% compute time to checking all grid squares basically
no I did, it's just been a week
give me 30 seconds here...
ain't nobody got time for dat
So, I can profile outside of editor sure
but it's just burying the performance problem by allowing more total compute as far as I know
Other than dynamic script compilation and debug statements, if it's not performant in editor it will only be a given multiplier better outside of profiler if the algorithm is slow..
All of that aside, I think the spatial partitioning algorithm and the increment / decrement is likely what I want..
I just haven't found a good way to increment / decrement since multiple characters can see a given square and they are calculating many squares every n interval. I'll poke at that line of thinking more
The advice of buying a solution is probably sound too, but I don't think you learn as much that way. I have bought 80+ assets too, it's not a cost thing.
Have you tried using a SPA?
can you break that acronym down please? IDK what that is. Single Page Application?
that doesn't seem right here..
spatial partitioning algorithm, sorry
ahh right, that should have been obvious
I think that's what I am doing with the grid notion, but let me read more on that topic
Is the overall concept here dividing the entire terrain into partitions and only calculating against the partition?
Where partition size < maximum vision range?
If you are not implementing existing algorithms but tried something your own it is most likely slower too
for sure, don't disagree there
okay... are you using Debug.Log in Vision.Update?
don't.
just start by commenting out all the Debug.Logs
I should point out though it's extremely performant with the heuristic check of triggering on calculate between grid squares
But that breaks partial visibility as described
Well typically you can either divide the space or assign groups to the objects in the space
Yeah I can do that, it accounts for a small amount of cpu time though
did you really profile standalone?
no, I profiled editor
I don't think profiling the build changes the algorithmic complexity
also why does it say [Invoke]
just shifts the problem so to speak
Uhh, that I am less sure on... I don't think either honestly, just a method call...
okay
I do have a singleton in play
i know it looks that way
but it's not how it works
I think the real question is what is the correct way to track the state of visibility across many agents, or update that state in a performant way
and it's representative of why i asked you to profile standalone
Eh I'll disable it, not a difficult thing
Hi, how could I make it so a certain object is only visible to the camera when looked at through another object? I know I need to use a shader, but I'm not sure how to use the shader (I also want to use code, not shadergraph). Does anyone know of a tutorial or something that could help?
but you have ot profile standalone
please, just trust me, make the build and profile it standalone
you have to remove all the Debug.Logs in there
it's saying you're allocating 7.3MBs per frame, which is unusual, it means that there is something else really strange going on
you can share the code snippet from Vision.Update... but also do the other things i suggested first
yeah, it helped disabling it, but not enough to matter. Like the difference between the heuristic calc is 150fps vs 8-12
updating every frame is just naieve and poorly done no matter how you slice it
I don't doubt it will be faster, maybe even 'fast enough'
but I'm trying to follow this axiom:
Everything Should Be Made as Simple as Possible, But Not Simpler
is the profiling problem due to editor overhead?
I'm not trying to make it more performant as a broad statement, rather I want the algorithm to run on something aproximating a performance calculation cross section that is close(er) to the minimum possible
(and still work with two fog layers)
for more accurate results, yes, profile a build, But if its not specifically editor overhead, profiling in editor isnt bad its just not how to get accurate results
^ exactly
if this isn't a great forum for algo perf I can leave well enough alone and soldier on
I just thought it might be the place for this sort of discussion. Also can share the code, would be great to get a review honestly
I don't doubt I am doing 100 stupid things
But I think there is a core problem to solve ehre
here
your GC is quite high too. Go into your player settings and enable incremental GC, might help
I forget the exact location where it is
Note that incremental GC has lower baseline performance in exchange for GC itself being less expensive
yeah sounds like working around the problem, although I do love coming here to know about these things
Would it be less work if the grids would keep track of nearby players and check if they are visible instead of letting the players calculate everything?
Ultimately the problem to solve is how to get many agents to track many square visibility on an intermittent basis using a low calculation cross section / structure that does that well. the spatial partitioning is quite interesting and may be a great start. Not sure how that works as units traverse partitions though. Maybe they span at least 4 at all times?
Well, the terrain itself can be visible or not visible at least from a rendering standpoint (it darkens when an agent can't see it)
very good point though
maybe a dictionary of who can see a square wouldn't be awful. Might be really bad too though, I would have to try and profile it which will take time.
If I could do it via bitwise or increment / decrement or something that would be better
Otherwise I'd start with a clustering algorithm for the agents
That's an interesting way of conceptualizing the problem, thinking of a group of units as a single unit. Not quite clear how that would work practically speaking but something to consider. Will look into both of these algos.
each agent can have a different vision range and so forth though which is why individual agents is better for that piece of things
Hm
I'm not sure that matters
You could add this to the clustering algorithm too
The different vision ranges
Would ofc make it less accurate but still might help it
hmm I see because there are only n unit types, so highly likely there will be clusters of units and you'll gain some calculation benefits from the overlap there or something along those lines
yeah
(I am new to clustering algorithms as of this very moment so I'm catching up in real time)
Did I understand it correctly that there are several agents on a terrain and parts of the terrain that no agent can see are invisible?
As a broad statement, do people like code share stuff here / code review for problems like this, or is that a bunch of work no one is interested in looking at? (Which frankly I can totally understand / appreciate)
Depends on the person I guess haha
Yes, trying to generate a picture quick but I have to undo some changes I made (I do have source control but I have been fiddling a bit since last check in)
Not sure if that illustrates the questions sufficiently well?
The trouble is when 2-3 agents go into a shared space and are moving the grid square 'checkout' system I have at the moment breaks because of race conditions around who can see the square and who can't between calculations. An event system might work, it just seems crazy to have all these agents chattering back and forth about when they stop seeing a square.
but notice I am getting 118fps with this heuristic in editor, which I think is okay
(although still maybe terrible on a 7700k / 3080ti, probably not so good on a potato, idk)
I don't know, I like event systems
events did work super well for my animation
And maybe that is the minimum calculation, talk to the other folks who care about this square, or purport to whenever you mark it dark
make sure they don't have an interest in said square. If one does, break / exit and keep going
I've never written even generators inside a loop but I presume it can be done. I'll have to think through the design more.
If you have any interest. This has not been curated for readability so it has all my stupid comments and things in there. 3 cs files in there.
(there is some grossness around single responsibility and the camera in there as well, disregard that)
lol
the bitwise stuff is splitting the data structure into two halves, one half for who last saw the square (storing the id) and the other half for a bitwise of 'teams' that can see whatever squares so that can be toggled. Given that it doesn't work with FIFO and I can only store 64 flags or what have you in an int or big int, that won't solve the problem. But I did learn a lot about bitwise and it works well for the teams.
This is more or less the implementation I was trying to follow:
https://blog.gemserk.com/2018/11/20/implementing-fog-of-war-for-rts-games-in-unity-2-2/
The author solves all the problems except the performance by wiping the screen every frame as far as I can tell
As I said in the previous blog post, some time ago I started working on a new Fog of War / Vision System solution aiming the following features: Being able ...
I haven't bothered with blocked vision yet, that's a problem for another day
I think you talked about it earlier, or somebody else did, but using a dictionary instead of a 2-dim array should be faster
Yeah, and I could take it further and store anyone who see a square as the value
And not just a tiny amount
I'm just not sure if that will land me in a perf mess again
(the value would be an array presumably)
doesn't have to be
I also considered storing a pool of agents with an id starting at one to pool size, and then incrementing / decrementing the visible value by their id. But increments and decrements don't have uniqueness so that sort of falls apart without an array or a nested dictionary
could be another dictionary
or a bit-int even
big int (typo)? or bit-int? IDk what a bit int is. Bitwise int?
bitwise int
The trouble I ran into with bitwise is you can only compress so much information into a bitwise int
so if I have 200 units, there just aren't enough bits as far as I know
like instead of using an array, use an int to store the player ids
I really love that idea, but is it possible?
I think big int is the best you can do and you're limited to 128 flags or whatever it is
(if you bitwise 'or' you can't undo them multiple times as far as I know)
you can reverse one value, but not many stored values many times
bigint does not work for storing a bitmask
e.g. 9 units are interested in square [1,1], now only 7 are interested, how do I decrement two compressed units?
or rather its pointless
good to know @compact ingot , why is that?
(compressed units is poorly stated; if this is possible, I would love to here it. I spent two weekends trying to do that and concluded it was impossible)
I can left shift / right shift, but am bounded by the size of the id of the unit / agent and the number of flags in the int to the best of my knowledge
best
a bigint is not an efficient primitive number, its a immutable struct and will not help with data compression in the same way as a primitive type used as a bitmask would, you can think of a big int as an array of bytes.
gah typos let me fix that lol
that is super useful to know
didn't know bigint was not a primitive, honestly I rarely use them except in databases
Reping question ๐ ๐
why are you trying to get unique IDs for who can see the square into these visibility calculations? Is it just to solve the flickering problem you described earlier, or are you using it for some other reason?
yes flickering problem exactly
if I could do it with 1 dimensional data that would be great, I just haven't conceived how
(in int increment would be great, but I can't figure out how to only increment when a unit discovers a square without store another data structure, perhaps I should think about that more)
btw I think GetInstanceId() doesn't work in build
I could store what squares a unit can see also
iirc
okok
a better solution would probably be a pool of ids or something
GetInstanceID works in a build perfectly fine, what do you mean?
when a unit moves, can't you just calculate which squares are newly visible (and newly invisible) to it (based on previous and new positions) and increment/decrement accordingly? Sorry if I'm being thick-headed here...
System.Guid.NewGuid();
GetInstanceID is a unique value, it's just not a stable one (meaning it will change between different runs of the game)
@gray pulsar totally, but then I need another data structure I think
Which I could attach to the agent
And that might be great. The best solution is likely dead obvious and simple lol
Hmm I remember reading that somewhere. My bad then
not this zany crap I'm coming up with
I went for the InstanceID because it said it was guaranteed to be unique
In any case, a guid would probably be fine too, but was bigger etc
It's a unique 32-bit identifier for any UnityEngine.Object-derived class, yeah
I think that may be the rub though, have each unit track what it can see, and only increment the gridsquare if it's new and decrement if it's not
Similar to the dictionary approach on the grid, not sure which is better
GlobalObjectId is one that doesn't work outside the editor
Probably makes destruction easier
Which sucks because it'd be extremely useful in builds 
Also, following this approach, if you calculated all visible grids, then: all grids - visible grids = invisible grids eh
That's an interesting conclusion, I'm not sure if it helps anything since you have to figure out what is visible at the appropriate time and remove it from visibility etc, but that's an accurate statement I believe
(it is useful for querying what is not visible I suppose, but not good for changing the state)
I like how positive you are on every idea haha
Well, when you are the guy asking the questions with broken code, all ideas are great. They are ideas I didn't have previously.
I work as a DevOps Engineer day to day and I have found that sometimes you get really really stuck, and any sort of poke at the problem is really helpful. Especially when you hit senior levels and the help becomes more and more sparse lol.
(my coding leaves a lot to be desired, lot of learning going on on this end of things. Not much of a senior anything when it comes to unity / C# lol
I think what I'll do is ponder which of those I like better though. My gut instinct is have the agents track what they can see, keep the grid class as simple as possible. I'll give that a whirl and report back. Really appreciate all the help!! Also someone had a question above, can probably pivot this to that convo. I'll update you guys w
whoops cut that message earlier, missing have a sentence
update you with the result
Hope you find a solution soon ๐
This was the question that got missed, I'll give it a read
I'm tempted to just defer this issue until later since I couldn't think of an obvious/easy/bug-resistant approach
I have done that in a number of cases, ignore it for a month or two until I come across it by happenstance
surprisingly effective a good chunk of the time but not always
heh
What is the issue exactly though? That there is a loading icon for waiting on the response of the server?
yeah it's... both probably not a big deal, and also a bit complex, so I'm tempted to maybe just not do it
yeah
It's literally just for a split second
but the game designer indicated to me that he doesn't want any pauses if possible - like when the timer counts down 5-4-3-2-1-0 there should be immediate feedback
For what to be longer..? I don't want any delay
[mission in progress] -> ["downloading state" while waiting for server to give info] -> [mission complete]
Well in UX there is sometimes the case that you prolong animations on purpose, like loading screens. Or rather give them a minimum display time
And if you create an app that is communicating with a server there is absolutely no guarantee that you will encounter loading
Can you prefetch the next mission somehow as you approach the timer expiration? Or would that facilitate cheating?
(I can't imagine a player could get a lot of cheating done in 200 milliseconds but I might be wrong)
You could send data before it is over and calculate everything. Then you could send it again when its over and verify if the data now is the same as sent previously
That would speed it up
As long as in let's say 99% of time the mission result data won't change within the last 5 secs
lol
yeah and if they don't match THEN they end up encountering the loading screen or what have you
so at least it's minimized to extremely rare instances
You could also directly track progress while doing the mission. Maybe there are some special triggers that will flag the mission as success or failure
provided you don't have a crap ton of data being serialized every frame or something
Then you have the answer right away
I can prefetch missions no problem, but I can't necessarily (easily) prefetch the results
This already isn't too far from what I have
Yeah.. you would have to know it's about to be completed. Can you just not complete immediately? Maybe they sit for half a second before it completes officially?
Why not? If you verify it 5secs before the mission is finished, there won't be much changes
Hm.. That's an idea, actually.. I could just immediately send the completed result as soon as the player starts the mission
Depending on your game desing
well, I would presume you don't know the results until the end, unless the results are always the same
the player can't cancel the mission, and the server is capable of validating that the user "finalized" the mission
But please go talk with your game designer that such a design is impossible
I suppose it depends what results mean in this case
So, that's not too much of a problem.. missions basically just have a % success rate which changes based on the crew they've assigned to the mission, but.. technically we know that as soon as the user starts the mission
ie - nothing changes over the time that the user waits for the mission to be complete
I still think it might be better to check closer towards the end if you can ascertain what that is
e.g. 8/10 baddies are dead or whatever
but you would know that answer better than I would
if really nothing changes then you can preload them
right but I think this is a good solution path
it'll require a little bit of rework to some logic I have but that's NBD and not too invasive or bug-prone
One thing to consider is how you do the comparison
e.g. is the entire class serialized, do you have a json structure, is all that data immutable etc
(if you are generating some ID or something that might bork it)
(say you track time elapsed since mission start, you'll have to omit that from comparison since time is constantly changing)
like my normal workflow is:
- server sends client "available missions"
- client assigns some crew to a mission and requests starting the mission
- server validates it, and updates the data structure and sends it to the client (ie, allows the client to start the mission) <-- from here, nothing can change on the mission
- client waits until mission is done, then asks server for an update
- server looks at the client's missions and says "yup, this is done", figures out if they've succeeded or failed, then sends the data back to the client
- client displays the mission icon being "done" and can click on the mission to finalize it (collect the reward)
I can basically take the step #5 and put it into step #3 and there's no cheating or issues - aside from the client already knowing (if they were looking at memory/data) if they've succeeded the mission once it starts, but .. there's no real issue with that
I've got that all taken care of.. yes, the data is serialized and missions have a guid
makes sense to me for sure
cool thanks all.. helpful
you bet
i'll film a little demo of it currently .. gimme a moment
well, nevermind, some other bug cropped up now haha
oops, I'm dumb.
bool isComplete = CompletedMissions.Where(x => x.MissionGuid == mission.Guid).FirstOrDefault() == null;
should have been !=
I hope that's not LINQ in a hot path I'm looking at
And I think FirstOrDefault can take in the lambda directly so it eliminates the Where, if I recall correctly
would you mind elaborating on that?
LINQ is extremely slow in comparison to writing the loop manually
Especially in Unity where allocations are more expensive than normal .NET
You should avoid LINQ whenever performance is important
In addition to being slow, it generates a lot of garbage so the GC will have to keep up
Ah okay, knew that it was slow, didn't know about the garbage
okay... i think you just need to not iterate through the entire grid
Are there alternatives to LINQ?
Didn't know that about the hotpath LINQ performance. I had written it originally myself but it was a lot messier
iterate through only the circular vision region
Writing for & foreach loops by hand
Yeah, I need to definitely improve that piece of it
and use the technique i described - decrement the old location by 1, increment by 1
I don't think the decrement works for reasons described previously though, need a second data structure
Oh wow okay. Why does it exist then haha
//foreach (PlayerCompletedMission completedMission in CompletedMissions) // remove missions in the bag that are already in completed (shouldn't be able to happen)
//{
// if (completedMission.MissionGuid == kvp.Key)
// {
// if (!removeMe.Contains(completedMission.MissionGuid)) removeMe.Add(completedMission.MissionGuid);
// break;
// }
//}
bool isComplete = CompletedMissions.Where(x => x.MissionGuid == kvp.Key).FirstOrDefault() != null;
if (isComplete) removeMe.Add(kvp.Key);
the bottom two lines look nicer than the top (commented out) loop to me
Because it makes writing code a lot easier when performance isn't that important
For example, business applications
if you enable deep profiling you'll see exactly what is slow
Or small tools
it's not hotpath code, though, so i'm not too concerned about performance here
While LINQ is basically always slower than an optimized for/foreach, it's not normally as slow as it is in Unity
So even if I wrote my own extension for a map oder filter function, still slow?
It would be interesting to profile and see the difference. If you're not loading libraries or adding unnecessary data structures each iteration the difference may not be as pronounced as it would otherwise seem
But this is all theoretical for me
In real .NET it's a lot faster, Unity uses Mono -- the old variant of Mono too
and uses the Boehm GC instead of SGen
As you say, I avoided linq in unity personally but use it a lot in business work stuff
So basically everything is slower
are you calling UpdateShroudTexture / GenerateTexture every frame ?
The difference in quality between RyuJIT (the JIT used in .NET) and Mono's JIT is insane
here's what i recommend you do
no, only when an agent intersects a new grid boundary
If we had RyuJIT, most of the time there'd be no reason to care about Burst
I knew it was slow, not that it was that slow, but considered using it anway because I like to keep it clean. And for most of my projects performance wasn't an issue
So.. here's a question. This LINQ code is in a DLL that's compiled and imported into my Unity code as a plugin. Would it still suffer performance issues?
Yeah it's fine as long as it's like, used during loading or initialization and places where performance/maximum efficiency aren't necessary
Personally I would profile before you rip it out
Yes
Why? The DLL is compiled externally.. (not by mono)
The DLL just contains IL, it doesn't contain any machine code
Mono is the runtime that compiles that IL to machine code which is what actually runs
That's how .NET achieves its crossplatform-ness
Yeah by the JIT
The .dll files aren't platform-specific
TIL
They just contain an "intermediate language" (IL), which is then translated to the appropriate format to run on the host platform
The JIT is also responsible for optimizing the code, IL is typically not "optimized" per se
and the JIT Unity uses is bad at that optimization
So the quality of the code it outputs isn't great
So things like LINQ suffer harshly
Unity are in the process of migrating to CoreCLR (the modern .NET runtime) but it'll probably be a few years before we get that
good to know.. i haven't really done any profiling but I have one method that's a little gnarly in a hotpath that's leaning into LINQ pretty heavily
i might want to take a peek at that
basically in the above movie clip - i'm sifting through some data structures to create a reordered data structure so the client can display icons "in order"
Basically, the poor GC and poor JIT are the biggest reasons that allocations are such a big deal in Unity compared to elsewhere in the .NET ecosystem
Unity is the bottom of the barrel as far as modern .NET development goes
wait I take it back, i'm foreaching in this hotpath code
it's still not good but.. oh well.
You get no integration with NuGet (the package manager), no modern csproj, bad JIT, bad GC, no modern runtime features, the list goes on
hey but we got dem nested prefabs, so we got that going for us
profiling is actually really easy and worth learning how to do
does that mean linq would be better then?
hey
go to window --> analysis --> profiler, that's most of it
Basically every single piece of code would improve greatly with no changes
I haven't messed with deep profiler though which you might need to get into the hotpath vs the method, idk
anyone know how to make a maze generator using 2d sprites?
yeah, but i'm not going to prematurely profile.. i have a fair bit too much on my plate, and this particular piece of code is easy enough to rip out later
@nocturne elm
Material material;
IEnumerator Start() {
var texture = new Texture2D(mapWidth, mapHeight);
// todo: you can back a texture with a native int array and
// use the shader graph to render it directly
var visibility = new int[mapWidth, mapHeight];
SetTextureOnMaterial(material, texture);
while (Application.isPlaying) {
Render(texture); // or, write the pixel when visibility is exactly 1 and clear it when it's exactly 0 the first time
foreach (var unit in GameManager.instance.Units) {
for (x = unit.position.x - unit.vision.x; x < unit.position.x + unit.vision.x; x++) {
for (y = ...) {
visibility[x,y] = Math.Max(0, visibility[x,y]-1);
}
}
}
// units will have had to move!!
yield return new WaitForEndOfFrame();
foreach (var unit in GameManager.instance.Units) {
for (x = unit.position.x - unit.vision.x; x < unit.position.x + unit.vision.x; x++) {
for (y = ...) {
visibility[x,y] = visibility[x,y] + 1;
}
}
}
}
Destroy(texture);
}
Also the loading time between changing 1 line of code and going back to unity eh? ๐
Yeah I mean hey all for making it better
I'm just saying profiling is one of those things worth doing once so you've done it and got that out of the way
Real useful
yeah.. i appreciate that
but maybe you mean you haven't profiled in the context of this project
if that's the case disregard entirely
That's less of a problem if you disable domain reloads
In my project it's basically instant
?
in the whole theme of:
- Make it work
- Make it correct
- Make it beautiful
- Profile it
I'm right squarely in 1/2 ๐
But you need to write your code with that in mind, so you need to manually clear statics
Yeah, that logic even has a memory leak to boot. I tried generating it once but was running into trouble
that's why I haven't gone for that option yet
you can even write in the second half of the function, because you're guaranteed it's visible if it's exactly 1
?
Not that I find it hard to do, but I'm usually working with other people who don't even know what this option is
i'm not trying to write an exhaustive example
I forget the issue exactly but basically it wasn't updating correctly
If you create the Texture2D once you can use the Resize method to change its pixel format and dimensions
okay
well anyway, the allocations are extremely weird
because what i wrote doesn't have to allocate anything new
i couldn't see from just eyeballing your code why you were doing 7.6MBs per frame
that sounds like you were creating a texture every frame
and is almost certainly the issue
When I was looping every frame I may or may not have consumed 12gb of GPU memory
okay
I should look at that to be fair
It's just not the 'current' problem since the checking every grid change more or less addressed it for now
i don't know...
it sounds like there are a lot of problems with what you're doing
it was hard to tell from the sample
i woul dmaybe download an asset
and look at it
but the code in question that might be... ugly (from a profiling perspective) later is (pseudocode):
void Update()
{
Dictionary<T, T> dict = new(); // ew
foreach (some data structure) if (something) dict.add(something);
foreach (some other data structure) if (something) dict.add(something);
foreach (some otherother data structure) if (something) dict.add(something);
List<T> list = dict.OrderBy(x => blahblahblhah).Select(x.Key).ToList(); // double ew
}
having just tried solving this problem, i dont' need to allocate every frame
i mean allocating a texture
(and some LINQ)
heh
There are way more than 2 allocs there
I think what McAri suggested will work honestly for the core problem. I can look at that texture instantiation again.
no doubt heh
just saying.. the code now "works" but definitely isn't pretty or correct or profiled
i'll be visiting it again someday in the future
The dictionary, then OrderBy will allocate, then you have potentially another allocation for the delegate you're passing to it, and then Select will allocate, then ToList will allocate
And potentially more allocations within those functions
i think start from scratch and try what i posted
Each LINQ method returns a new enumerable, which is an allocation
oh I missed that code block, will check it out ๐
do you think the above code is worth "worrying about" (ie, profiling and investigating)? for reference, the data structures i'm iterating are tiny (never more than 4 elements)
I don't think that decrement idea works, because it get's incremented every time it is 'discovered' but only decremented once. But I will try it for sure. I thought I tried that already though.
I would definitely not want LINQ in an Update method, but you'd need to profile it to be sure
if they are that small, you could inline everything and make it way way way faster than any structured code would ever be
No matter how small those things are that you're enumerating, there's still the allocations
And more allocations = more work for the GC
Which means worse performance when a collection occurs
I think worth it is project dependent personally. If it's never going to get larger and you don't need to scale it, you have 1k other problems. Take the advice and apply it going forward when you have large structures. But that's just my 2c.
opinion: when do you start profiling? constantly during dev? or only once you start approaching functional completion for a feature/project/sprint/other
At the very least though you can avoid that dictionary allocation
Allocate it once and store it as a field
Then instead of new()ing one, do dict.Clear();
(i.e. do you really need to optimize something that will never be larger than 10 total loops? probably not. Presuming the work of each loop is small, all of the usual asterisks. If there is a chance there might be 10 million missions as a garbage example, then that's a concern.
Same for the list actually
But it also depends if you just want to really learn what zombie is getting at in a permanent way for all future projects
Instead of List<T> list = dict.OrderBy(x => blahblahblhah).Select(x.Key).ToList(); you can do
You happen to have a lovely case here that's fairly easy to fix
list.Clear();
list.AddRange(dict.OrderBy(x => blahblahblhah).Select(x.Key));
Is this a private project or something you get a salary for?
where list is stored as a field
oh, no doubt, that's easy enough
I was more interested in the impact of the LINQ part of that
(like the OrderBy and Select and ToList)
If it was salaried I would probably leave it, they just care about their features
Right
if it was my own stuff I might want the improvement
well, tolist isn't really part of the LINQ, i could roll my own there
not a salary, but it's a for-revenue project, per se
(want the self improvement)
At the end of the day to me it's always about features and the client experience
A custom-rolled one would probably be slower than the LINQ one though since you would lack all of the specific optimizations inside LINQ
if the client never experiences it and it won't slow you down later, who cares
In my case for example my fog of war deal, the client will experience it every frame lol
so it would be best if it worked well
yeah the FOW conversation (I peeked in on it above) sounds like it'd be way more important to optimize
IIListProvider is an internal type that you don't have access to, so you wouldn't be able to optimize for that specifically
In fact that type mainly exists just to allow LINQ to optimize some cases internally
here's the "full" source from the above conversation: https://pastebin.com/BRVfMTQ5
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.
before you ask - the mission1/2/3/4 objects are there as a silly sort of caching mechanism
What type is _playerStateManager.PlayerModel.WorkingMissions.CompletedMissions?
List<PlayerCompletedMission>?
aye
My feeling is this data structure is so small it's just not that important functionally speaking
You could change it to SortedList<PlayerCompletedMission>
(the things happening in the mission are likely much more perf sensitive)
public class PlayerWorkingMissions
{
[Key(0)] public DateTime LastMissionGrantedDateTime { get; set; } = DateTime.MaxValue; // Last time a mission was awarded. Set to "now" when a player is created.
[Key(1)] public Dictionary<Guid, Guid> InProgressMissionChoices { get; set; } = new Dictionary<Guid, Guid>(); // Choice of the mission
[Key(2)] public Dictionary<Guid, List<PersonIDType>> InProgressPersonIDs { get; set; } = new Dictionary<Guid, List<PersonIDType>>(); // List of crew IDs assigned to the mission
[Key(3)] public Dictionary<Guid, DateTime> InProgressStartedDateTime { get; set; } = new Dictionary<Guid, DateTime>(); // Started DT of the mission
[Key(4)] public Dictionary<Guid, int> InProgressDurationSeconds { get; set; } = new Dictionary<Guid, int>(); // Duration of the mission (calculating crew bonuses)
[Key(5)] public HashSet<Guid> AvailableMissions { get; set; } = new HashSet<Guid>(); // Available missions.
[Key(6)] public List<PlayerCompletedMission> CompletedMissions { get; set; } = new List<PlayerCompletedMission>(); // Completed missions
and then you don't need to use OrderBy
also it doesn't really matter how large the data is, there will always be client-server communication delay
You do pay for sortedlist every insertion though no?
I believe so yes
i think work through it because while i might have gotten something off by 1 there or comparable
But it might be way more efficient overall
paying for the sort on insertion is way better than on my client's Update()
More expensive addition for cheaper enumeration
i'm pretty sure that when the unit moves, all of its previous locations get decremented, and its new location gets incremented
that makes sense
and now, when it is still, the number of increments and the decrements is the same
so its net impact on anywhere it is for the second and later times is zero
I mean, realistically what I should do is have a cached accessor or enumerator in the data structure that's sorted so the client can just iterate through that instead of needing to create its OWN sorted data structure every frame
If that is true this problem becomes way simpler
I did try it but my implementation was probably shot
well what do you want me to do write a math proof
So adding to a SortedList is more expensive, but it's cheaper to enumerate because it's pre-sorted
id' just read this out of a book or an asset at this point
i think try to do it with a rectangle and don't forge tthat Max
With your current List<T> you're paying the sorting cost every frame
yeah