#archived-code-advanced

1 messages ยท Page 168 of 1

cedar ledge
#

so is 3840x2160

fresh salmon
#

And 1600x900

undone coral
#

@cedar ledge the most important thing here is that the scope of the API is very small

regal olive
#

what happens if somone has 1280x720 and i set it on unity 16:9

undone coral
#

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

final steeple
#

1280x720 is a 16:9 resolution so... unless the game hardcoded stuff for 1920x1080, it should be fine

cedar ledge
#

what happens is the game is 1280x720

cedar ledge
regal olive
final steeple
#

No, but it's a 16:9 resolution

cedar ledge
#

it's 50% smaller but it's the same ratio

undone coral
#

if htey made pre-existing changes

cedar ledge
#

you think people are using source control?

#

nah

undone coral
#

you already renamed variables and such

#

yeah that's what i mean

fresh salmon
undone coral
#

so if they modified stuff in this folder

cedar ledge
#

they dont

undone coral
#

okay

#

then you should definitely just facade Mirror

cedar ledge
#

i would have to learn mirror

undone coral
#

which is valuable!

cedar ledge
#

i already kinda know it but id rather just fix his code than gut it

undone coral
#

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)

cedar ledge
#

i don't learn anything by just translating mirror

undone coral
#

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?

cedar ledge
#

what does converting coroutines to async/await have to do with that?

undone coral
#

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

final steeple
#

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

undone coral
#

mirror has clean internals

final steeple
#

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

undone coral
#

i am advocating for authoring an facade

final steeple
#

That could potentially work, but it depends on how much work it would be

cedar ledge
#

I think your approach is valid but I'm avoiding using libraries because he's already written literally everything

final steeple
#

A facade might end up being more work if the API and workflow sufficiently differs from Mirror

cedar ledge
#

The code works without using any libraries

#

I'm just slightly changing and optimizing it

undone coral
#

you're underestimating how hard it is

regal olive
#

Is it possible to change the players forward direction to be relative to the cameras direction? Im using the cinemachine camera

cedar ledge
#

Why would I need a networking library for my networking library that works as is

undone coral
#

if this were the matrix, morpheus would stare off into space, grinning

cedar ledge
regal olive
#

But would that work with cinemachine?

final steeple
#

I don't see why not

undone coral
final steeple
#

There's not really anything "special" about Cinemachine here, to my knowledge

undone coral
#

you'll be around longer than this class

regal olive
#

Let me get my code real quick

undone coral
#

think critically about what you want to learn and do

regal olive
proud gust
#

But you have the camera transform

undone coral
#

i really want you to thrive

final steeple
#

Why would you need to edit code? You have a camera, you have the player

undone coral
#

you can succeed in authoring a facade to mirror

regal olive
cedar ledge
#

is using mirror some sort of cult that i'm being inducted into?

undone coral
#

i think that's photon

final steeple
#

You can add a component field to your class that references the cinemachine camera

#

[SerializeField] CinemachineVirtualCamera targetCamera; or whatever is appropriate

regal olive
#

Ok... then I add the
transform.forward = camera.transform.forward;?

final steeple
#

[SerializeReference] ICinemachineCamera targetCamera; would probably also work

undone coral
#

@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

cedar ledge
#

i think you're misunderstanding my goal

undone coral
#

you aren't able to litigate that

#

i know that's your goal

cedar ledge
#

I'm not learning how to write a networking engine per se

final steeple
cedar ledge
#

I'm learning how to use async/await/tasks

final steeple
#

FixedUpdate should be reserved for physics code

cedar ledge
#

and improving the course

regal olive
#

Oh

final steeple
#

Update is where input and anything visual goes

cedar ledge
#

camera code should be in LateUpdate()

final steeple
#

^

regal olive
#

Ohh I didnt know that

final steeple
#

That ensures it sees the "final" state of the frame

regal olive
#

Ill try this out

cedar ledge
#

because unity doesnt have monobehavior priority

final steeple
#

It does

cedar ledge
#

booo

final steeple
#

You can define execution orders in project settings, or in code via [DefaultExecutionOrder]

#

[DefaultExecutionOrder(int.MaxValue)] should cause the component to receive events last

cedar ledge
#

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?

final steeple
#

You would need to use a CancellationToken, make all of the APIs cancellable, and create a CancellationTokenSource that is cancelled when play mode exits

cedar ledge
#

is there a callback for exiting playmode?

proud gust
#

I'd recommend using that, it's simple enough

regal olive
final steeple
#

Yes, it's an interface

regal olive
#

Ok

final steeple
#

I saw it referenced in the Cinemachine docs

final steeple
regal olive
#

Ok

final steeple
#

You instead need to subscribe to the play mode state change callback

cedar ledge
#

yeah lol

regal olive
#

It says that the namespace ICinemachineCamera could not be found

final steeple
regal olive
#

Oh

cedar ledge
#

that sounds really appealing

final steeple
#

hahahaha

cedar ledge
#

removing 90% of the playmode wait time by simply managing your own static fields and unregistering callbacks when you stop

final steeple
#

It's significantly faster

quiet bolt
cedar ledge
#

im 100% looking into that

final steeple
#

I highly recommend it, assuming it's not a massive refactor for your project

#

I design all new codebases with it in mind

cedar ledge
#

idc if it is, I'm changing my mind

final steeple
#

lmao

cedar ledge
#

this is the proper way to write code and you should implement the proper design patterns to account for it

final steeple
#

I agree

cedar ledge
#

how dare you subscribe to an event without unsubscribing when playmode exits!

#

so we've got
onenable
awake
start
oninit
[RuntimeInitializeOnLoadMethod]
onvalidate

#

any more?

final steeple
#

The constructor picardy

cedar ledge
#

*dead*

final steeple
#

It's safe to use as long as you only do thread-safe things in it

cedar ledge
#

i guess this is a good time to ask this question

final steeple
#

Meaning you just can't use most of the Unity API from it, since it's not guaranteed to run from the main thread

cedar ledge
#

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?

proud gust
#

Why not init in awake?

final steeple
#

I would initialize in Awake, not Start

cedar ledge
#

ok

final steeple
#

Awake is the de-facto "user constructor" for components

cedar ledge
#

i meant to say awake, that's what i am doing

proud gust
#

What's the benefit of using a private field + property for it?

final steeple
#

Finer-grained access control

cedar ledge
#

nullable references

final steeple
#

Though I'm skeptical of the advantage of throwing MissingReferenceException manually

proud gust
#

Sorry I meant serializedField

cedar ledge
#

is this asking why not just make everything public?

proud gust
#

No, why using SerializeField lol

final steeple
#

The editor will allocate "fake null" objects that throw MissingReferenceException when accessing properties that require the native object to be alive

cedar ledge
final steeple
#

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

proud gust
#

Hmm okay

#

Not the biggest fan of setting references in the inspector, thus the question

final steeple
#

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

cedar ledge
cedar ledge
#

which throws

final steeple
#
[SerializeField]
SomeComponent someComponent;

public SomeComponent SomeComponent
{
    get
    {
        if (someComponent == null)
            ThrowMissingReferenceException();

        return someComponent;
    }
}

static void ThrowMissingReferenceException()
{
    throw new MissingReferenceException();
}
cedar ledge
proud gust
#

But not in general

cedar ledge
#

the thing that has these references is a singleton lol

final steeple
#

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

proud gust
#

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

final steeple
#

You learn a lot of stuff in there, especially since various Microsoft staff that work on .NET hang out there

cedar ledge
final steeple
#

It's a no-op on a public field, but I guess it expresses intent

proud gust
#

Save for what?

cedar ledge
#

unity's editor

final steeple
#

It gets stored in the scene data

misty glade
#

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:

  1. 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.

  2. 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.

final steeple
#

If it's not saved, it won't keep its value between runs of play mode

#

Or have that value in a build

proud gust
#

Sure

final steeple
misty glade
#

@ me for responses/ideas. AFK a little bit.

hollow garden
# cedar ledge critique my code?
        // 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
final steeple
#

You can also use a helper method to simplify them

hollow garden
#

yeah

final steeple
#

Let me write an example

cedar ledge
final steeple
#

Yes

proud gust
#

my question exactly

fresh salmon
#

Yeah, with [CallerMemberName] to auto-retrieve the property name from the method that throws the exception

final steeple
#

^ that's exactly what I was just writing lol

misty glade
#

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)

final steeple
#
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);
}
hollow garden
fresh salmon
#

Like

static void ThrowMissingRef([CallerMemberName] string caller = "") {
  throw new MissingReferenceException($"{caller} not referenced in inspector");
}
cedar ledge
#

that's what i have

proud gust
#

I've been working with typescript lately and was a bit shocked how little null coalescing works in unity, in comparison

final steeple
#

Blame Unity for lying about null when

cedar ledge
#

why can't they fix that?

proud gust
#

Or maybe it gets better with C#8, no idea

final steeple
#

They can, but they decided not to

final steeple
hollow garden
#

i thought you couldn't overload ??

final steeple
#

You can't, because it's a true null check

#

Overloading that would defeat the purpose

proud gust
#

Maybe they did some changes

flint sage
#

It's probably not fixed

final steeple
#

They didn't

proud gust
#

I'm stuck in 2019 currently

final steeple
#

I regularly use Unity 2022.1

compact ingot
#

its not something that requires a fix

flint sage
#

Since it would mean breaking all projects

proud gust
#

And it's really a pain in the ass sometimes

final steeple
#

Basically, Unity lies and pretends that null doesn't just mean null, it means null or destroyed

proud gust
final steeple
#

So the actual C# object is not null, the pointer to the C++ object is

cedar ledge
#

i just have to tell roslynator to shut up whenever it recommends fucky wucky null stuff

final steeple
#

UnityEngine.Object and all classes that derive from it are just wrappers around a pointer to a C++ class

proud gust
#

Do you know if C# will at some point get type guard functionality?

final steeple
#

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

proud gust
#

like if obj.GetType() == typeof(SomeScript) obj.someValue would work as obj would be automatically cast to SomeScript

final steeple
#

It's been discussed before and turned down

#

So it likely won't happen

proud gust
#

Damn

final steeple
#

It would never have that syntax though, even if it were added

proud gust
#

It wouldn't have to

final steeple
#

The closest thing you get currently is this:

if (obj is SomeScript script)
{
    // you can use 'script' in this scope
}
hollow garden
#
if (obj is SomeScript someScript) {

}
``` maybe this is something similar
proud gust
#

fair enough

#

but not enough lol

final steeple
#

If someday C# gets variable shadowing, you would be able to do if (obj is SomeScript obj) or something similar

proud gust
#

I'm really enjoying typescript for these kind of features lol

final steeple
#

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

proud gust
#

I don't really know much about C#10 tbh

final steeple
proud gust
#

But I'm also not working much with C# outside unity projects

final steeple
#

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

proud gust
#

Nice

#

That's a huge improvement

final steeple
#

File-scoped namespaces are also really nice

proud gust
#

Damn

final steeple
#
namespace SomeNamespace
{
    class SomeComponent : MonoBehaviour
    {
    }
}

can be

namespace SomeNamespace;

class SomeComponent : MonoBehaviour
{
}
#

less nesting

proud gust
#

Cool

final steeple
#

Plus there's stuff like generic attributes and static members in interfaces

#

Those two are the real big ones

proud gust
#

Ah, I knew about static members in interfaces

final steeple
#

It's the foundational feature for generic math

proud gust
#

Never knew if I liked that they are changing interfaces though

final steeple
#

With static abstracts and generic math, you can define a Vector3<T>

proud gust
#

Does this work in Unity already numbers ??= new List<int>();

final steeple
#

If you're using a version of Unity that supports C# 8, yes

proud gust
#

which is some 2020+?

final steeple
#

2020.2+

proud gust
#

aye

final steeple
#

You can use C# 8 with older versions if you replace the bundled Roslyn

cedar ledge
#

before, you couldnt change in-production interfaces because it would break everyone's code. now, you can have a default implementation and such

proud gust
#

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

cedar ledge
#

the alternative is having IMyInterfaceV2

final steeple
cedar ledge
#

i started with 4.5.4f1

final steeple
#

Unity 4 is dark ages tier stuff

#

if I had to go back to that, I think I'd die

cedar ledge
#

back when you didn't even need to getcomponent, iirc

proud gust
#

Or didnt have lighting lol

final steeple
#

Unity 4 had Beast

proud gust
#

I honestly don't know with which we started

#

I assume it was some 5.x

final steeple
#

5.x was when the engine really took off in popularity

proud gust
#

I've been using it in university since 2016

#

Nah 2015

final steeple
#

I first used Unity in... 2012 or so?

#

2012 or 2013, I don't remember exactly

cedar ledge
#

old person

proud gust
#

maybe started with 7yo

final steeple
#

I'm turning 23 this year lol

cedar ledge
#

lol

proud gust
#

ok, old person

cedar ledge
#

I started in 2014 (14 y/o)

final steeple
#

This can technically work in Unity if you use 2021.2 or newer

proud gust
#

Haha nice

final steeple
#

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

cedar ledge
#

they're not called function pointers though, right?

final steeple
#

C# 9 added real function pointers

#

Delegates are different

proud gust
#

So is it writing asm in c# that is being compiled back to asm ? ๐Ÿ˜„

cedar ledge
#

i use the asm to write the asm

final steeple
#

that you can then execute, yes

#

Check that out if you want to get a face full of assembly

proud gust
#

Had my full face of assembly with my pokemon hacking tutor couple of years ago lol

final steeple
#

lmao nice

#

That's a fun way to learn

proud gust
#

well it's called games engineering so

cedar ledge
#

can I use C#10 to defeat rust's borrow checker?

final steeple
#

lol

cedar ledge
#

code I code a borrow checker in C#10?

#

asking the real questions here

cedar ledge
final steeple
#

Because otherwise it won't inline

cedar ledge
#

is that a failure of the jit or is that how it should be?

final steeple
#

Gimme a sec, there is some documentation in the Windows Community Toolkit that covers this

cedar ledge
#

then why isn't public static ThrowException<T>(message) where T : Exception a thing?

#

just have it once in the Exception class

final steeple
#

Because an exception isn't guaranteed to have the constructor necessary to implement that

cedar ledge
#

derived exceptions would have to implement that?

final steeple
#

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)

cedar ledge
#

are you sure? just put this in Exception.cs:

public static void ThrowException<T>(message) where T : Exception
{
    throw new T(message);
}
final steeple
final steeple
#

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

cedar ledge
#
        public static void ThrowException<T>(string message) where T : Exception, new()
        {
            throw new T();
        }

this kinda works lol

final steeple
#

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

cedar ledge
#

well you wouldn't have to worry about the inline issue

final steeple
#

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

nocturne elm
#

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.

final steeple
#

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;
cedar ledge
#

cool

#

now if only unity could serialize properties

final steeple
#

It can

#
[field: SerializeField]
public int SomeProperty { get; private set; }
#

I forget which version support was added in

#

I think 2019 or 2020

proud gust
#

2020

cedar ledge
#

that's serializing the implicit backing field but yeah

proud gust
#

(i think)

final steeple
#

Right, did you mean computed properties?

cedar ledge
#

so could you combine that with validateandreturn?

final steeple
#

Not currently, but in C# 11 maybe

cedar ledge
#

exactly

final steeple
#

In C# 11 it would potentially look like this:

#
[field: SerializeField]
public int SomeProperty
{
    get => ValidateAndReturn(field);
    private set;
}
cedar ledge
#

don't tease me with something I can't have ๐Ÿ˜ข

proud gust
#

Whats validate and return then?

final steeple
#

A method that will handle the throwing etc if the value is null

cedar ledge
#
        public static T ValidateAndReturn<T>([NotNull] this T? checkme!, [CallerMemberName] string caller = "")
        {
            if(checkme == null)
            {
                ThrowMissingReferenceException($"{caller} not referenced in inspector");
            }
            return checkme;
        }
final steeple
#

If we had source generators it could be even better

#
[SerializeProperty(ThrowIfNull = true)]
public partial int SomeProperty { get; }
cedar ledge
#

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

final steeple
#

Unity has support for non-incremental source generators, but they suck

#

I don't know if incremental ones are supported

cedar ledge
#

I went down the rabbithole so far

#

I had to stop myself once I started coding in IL, in C#

final steeple
#

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

cedar ledge
#

hey theres a small small issue

#

it doesnt realize it throws if null

final steeple
#

return checkme!;

cedar ledge
#

ah

#

the brute force method

final steeple
#

Yeah

proud gust
#

See, that's what I was talking about earlier

final steeple
#

I think there's a nullability attribute you can use to define this properly

cedar ledge
#

yep

final steeple
#

but it's probably not available in Unity

proud gust
#

It should know that it cannot be null at this point

final steeple
#

There is no way for it to know that without nullability annotations

#

It would need to perform interprocedural analysis

#

Which would be expensive

cedar ledge
#

but then it would also probably not inline if it knew it could throw

proud gust
#

why? 3 lines above it's checked and throws if so

final steeple
#

As far as the compiler is concerned, that's just a normal function

#

It doesn't know that function just throws

proud gust
#

And it would be too expensive to check that?

final steeple
#

It would make the compiler (and by extension, Visual Studio) much slower, yes

proud gust
#

How is it that this works in different languages then? Because they're not structured like c#?

final steeple
#

It depends on the language, many of them just require annotating functions like C# does

proud gust
#

It wouldn't even work if you'd put an else there would it

final steeple
#

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

cedar ledge
final steeple
#

It's a package, not built-in

cedar ledge
#

how do I add the package?

final steeple
#

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

cedar ledge
#

1 sec

proud gust
#

Theres a package that allows nuget support

cedar ledge
#

sadge

final steeple
#

You could try that, though I've not had much luck with many of the NuGet solutions for Unity

proud gust
#

I did end up only using one package so I did it manually, but I remember finding tools for that that did look promising

cedar ledge
#

should I get that or should I just have my own method that throws?

final steeple
#

This is for .NET Standard 2.0

cedar ledge
#

2.1 ....

final steeple
#

So it works with the .NET 4.x and .NET Standard 2.0 presets

#

Are you on Unity 2022.1?

cedar ledge
#

no

#

2021.2.11f1

final steeple
#

Does 2021.2 support .NET Standard 2.1?

#

I thought that was 2022 exclusive

cedar ledge
#

yes

final steeple
#

Huh, okay, gimme a sec and I'll grab the NS2.1 files

cedar ledge
final steeple
#

There you go

cedar ledge
#

what does the throwhelper do better than writing my own 1 line method?

final steeple
#

Nothing, it's just convenient since it offers pre-written methods for a bunch of common exception types, and has them annotated with DoesNotReturn

cedar ledge
#

cool

#

and I just put that in /Packages?

final steeple
#

In Plugins

#

Rather, in Assets/Plugins/

cedar ledge
#

can you come please teach at my uni

final steeple
#

hahahaha

cedar ledge
#

we have great things like

#

admin firing teachers who unionize

#

high turnover

#

1600 students

final steeple
#

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โ„ข๏ธ

proud gust
#

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

final steeple
#

I ask myself "why" in reference to Unity every day

cedar ledge
#

there seems to be a ton of stuff to learn that just NEVER gets talked about on "advanced" unity youtube and university classes

final steeple
#

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

proud gust
#

good luck lol

cedar ledge
#

please PLEASE tell me that all this knowledge makes you the big bucks at your senior level position at $Company

proud gust
final steeple
#

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

final steeple
proud gust
#

neat

#

I won't need it for my project, but I'd save it for later if you don't mind

final steeple
#

Sure, if I didn't want people to use it I wouldn't have posted it lmao

proud gust
#

Could always delete it haha

#

But it's a cool idea

#

Also works with custom shaders

final steeple
#

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

proud gust
#

git copilot lol

final steeple
#

lol

cedar ledge
#

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

proud gust
#

Not really different in c++ actually

cedar ledge
#

yeah the way you have to include the .cginc files, guard against multiple includes, etc. is just not something i want to do

final steeple
#

Yeah I really hate how Unity does shaders

#

But honestly I hate how shaders work in general

cedar ledge
#

yeah

#

raytracing ONLY

undone coral
proud gust
final steeple
#

Something like ComputeSharp (where you write shaders entirely in C#) would be really nice to have as a more general thing

cedar ledge
#

actually

nocturne elm
#

@undone coral It is in 3d yes, but if you look at the text document it details the question in much more detail

cedar ledge
#

you know what?

#

fuck polygons

#

raymarching ONLY

nocturne elm
#

I can paste it here, it's just like 3 paragraphs. Felt like hijacking.

undone coral
#

have you looked at a pre-existing implementation?

nocturne elm
#

Correct

undone coral
#

from the asset store?

proud gust
#

fuck coding game logic
use an ai and let it do everything

#

figure it out

nocturne elm
#

@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

undone coral
#

i suggest using vfx graph, which can cull particles to the camera. then provide a texture that corresponds to visibility in world space

proud gust
#

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

undone coral
#

then you just need to express map visibility as a bitmap, which is not hard

nocturne elm
#

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

undone coral
#

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

nocturne elm
#

hmm I'll post my full question here vice the above attachment, articulates it

#

I do

undone coral
#

is that your approach?

nocturne elm
#

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):

#
  1. 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.

  2. 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

  3. 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?

cedar ledge
#

๐Ÿ‘€

nocturne elm
#

^ is the question I had in the document that outlines the problem in detail

#

LOL I was trying not to paste that blob

undone coral
#

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

nocturne elm
#

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.

undone coral
#

how do you know it's not performant? profiler?

nocturne elm
#

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

final steeple
#

Sounds like you could use a spatial partitioning algorithm

nocturne elm
#

mmm dips isn't the word, just low fps overall

undone coral
#
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

nocturne elm
#

I liked that idea above, but the problem is you need to embed uniqueness I think

undone coral
#

nah

nocturne elm
#

otherwise every time the calculation is run it increments

#

and only decrements when it goes out of visibility

undone coral
#

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

nocturne elm
#

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.

undone coral
#

how do you figure?

#

what did your profiler say was wrong?

#

concretely?

#

do you have a screenshot of it?

nocturne elm
#

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

undone coral
#

what did the profiler say

nocturne elm
#

I can profile it quick, but it says that class is doing too much work

undone coral
#

okay so you haven't profiled it

nocturne elm
#

99% compute time to checking all grid squares basically

#

no I did, it's just been a week

undone coral
#

๐ŸŒˆ profile it๐ŸŒˆ

#

we'll see what it says

nocturne elm
#

give me 30 seconds here...

undone coral
#

okay

#

but don't do it in editor mode

#

you have to profile standalone

proud gust
#

ain't nobody got time for dat

nocturne elm
#

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.

proud gust
#

Have you tried using a SPA?

nocturne elm
#

can you break that acronym down please? IDK what that is. Single Page Application?

#

that doesn't seem right here..

proud gust
#

spatial partitioning algorithm, sorry

nocturne elm
#

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?

proud gust
#

If you are not implementing existing algorithms but tried something your own it is most likely slower too

nocturne elm
#

for sure, don't disagree there

undone coral
#

don't.

#

just start by commenting out all the Debug.Logs

nocturne elm
#

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

proud gust
#

Well typically you can either divide the space or assign groups to the objects in the space

nocturne elm
#

Yeah I can do that, it accounts for a small amount of cpu time though

undone coral
#

did you really profile standalone?

nocturne elm
#

no, I profiled editor

#

I don't think profiling the build changes the algorithmic complexity

undone coral
#

also why does it say [Invoke]

nocturne elm
#

just shifts the problem so to speak

undone coral
#

are you using SendMessage ?

#

or Invoke

nocturne elm
#

Uhh, that I am less sure on... I don't think either honestly, just a method call...

undone coral
#

okay

nocturne elm
#

I do have a singleton in play

undone coral
#

just checking..

#

i would strongly recommend profiling standalone

undone coral
#

but it's not how it works

nocturne elm
#

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

undone coral
#

and it's representative of why i asked you to profile standalone

nocturne elm
#

Eh I'll disable it, not a difficult thing

fast falcon
#

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?

undone coral
#

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

nocturne elm
#

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

zenith ginkgo
#

is the profiling problem due to editor overhead?

nocturne elm
#

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)

zenith ginkgo
#

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

nocturne elm
#

^ 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

zenith ginkgo
#

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

final steeple
#

Note that incremental GC has lower baseline performance in exchange for GC itself being less expensive

nocturne elm
#

yeah sounds like working around the problem, although I do love coming here to know about these things

proud gust
#

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?

nocturne elm
#

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

proud gust
#

Otherwise I'd start with a clustering algorithm for the agents

nocturne elm
#

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

proud gust
#

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

nocturne elm
#

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

proud gust
#

yeah

nocturne elm
#

(I am new to clustering algorithms as of this very moment so I'm catching up in real time)

proud gust
#

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?

nocturne elm
#

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)

proud gust
#

Depends on the person I guess haha

nocturne elm
#

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)

proud gust
#

I don't know, I like event systems

nocturne elm
#

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)

proud gust
#

lol

nocturne elm
#

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.

#

I haven't bothered with blocked vision yet, that's a problem for another day

proud gust
#

I think you talked about it earlier, or somebody else did, but using a dictionary instead of a 2-dim array should be faster

nocturne elm
#

Yeah, and I could take it further and store anyone who see a square as the value

proud gust
#

And not just a tiny amount

nocturne elm
#

I'm just not sure if that will land me in a perf mess again

#

(the value would be an array presumably)

proud gust
#

doesn't have to be

nocturne elm
#

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

proud gust
#

could be another dictionary

nocturne elm
#

yeah just realized that now lol

#

Maybe I'll try that if it doesn't sound absurd

proud gust
#

or a bit-int even

nocturne elm
#

big int (typo)? or bit-int? IDk what a bit int is. Bitwise int?

proud gust
#

bitwise int

nocturne elm
#

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

proud gust
#

like instead of using an array, use an int to store the player ids

nocturne elm
#

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

compact ingot
#

bigint does not work for storing a bitmask

nocturne elm
#

e.g. 9 units are interested in square [1,1], now only 7 are interested, how do I decrement two compressed units?

compact ingot
#

or rather its pointless

nocturne elm
#

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

compact ingot
#

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.

nocturne elm
#

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

misty glade
gray pulsar
#

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?

nocturne elm
#

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)

proud gust
#

btw I think GetInstanceId() doesn't work in build

nocturne elm
#

I could store what squares a unit can see also

proud gust
#

iirc

nocturne elm
#

that might work well

#

That was a poormans ID, but super good tip

proud gust
#

okok

nocturne elm
#

a better solution would probably be a pool of ids or something

final steeple
#

GetInstanceID works in a build perfectly fine, what do you mean?

gray pulsar
proud gust
final steeple
#

GetInstanceID is a unique value, it's just not a stable one (meaning it will change between different runs of the game)

nocturne elm
#

@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

proud gust
nocturne elm
#

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

final steeple
#

It's a unique 32-bit identifier for any UnityEngine.Object-derived class, yeah

proud gust
#

It is

#

I just thought that it wouldn't work outside the editor lol

nocturne elm
#

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

final steeple
#

GlobalObjectId is one that doesn't work outside the editor

nocturne elm
#

Probably makes destruction easier

final steeple
#

Which sucks because it'd be extremely useful in builds PepeHands

proud gust
#

Also, following this approach, if you calculated all visible grids, then: all grids - visible grids = invisible grids eh

nocturne elm
#

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)

proud gust
#

I like how positive you are on every idea haha

nocturne elm
#

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

proud gust
#

Hope you find a solution soon ๐Ÿ™‚

nocturne elm
misty glade
#

I'm tempted to just defer this issue until later since I couldn't think of an obvious/easy/bug-resistant approach

nocturne elm
#

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

misty glade
#

heh

proud gust
#

What is the issue exactly though? That there is a loading icon for waiting on the response of the server?

misty glade
#

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

proud gust
#

Well force it to be longer then

#

like 1.5sec

misty glade
#

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]

proud gust
#

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

nocturne elm
#

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)

proud gust
#

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

nocturne elm
#

and then if they don't match, ban the player ๐Ÿ˜†

#

kidding

proud gust
#

lol

nocturne elm
#

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

proud gust
#

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

nocturne elm
#

provided you don't have a crap ton of data being serialized every frame or something

proud gust
#

Then you have the answer right away

misty glade
misty glade
nocturne elm
#

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?

proud gust
misty glade
#

Hm.. That's an idea, actually.. I could just immediately send the completed result as soon as the player starts the mission

proud gust
#

Depending on your game desing

nocturne elm
#

well, I would presume you don't know the results until the end, unless the results are always the same

misty glade
#

the player can't cancel the mission, and the server is capable of validating that the user "finalized" the mission

proud gust
#

But please go talk with your game designer that such a design is impossible

nocturne elm
#

I suppose it depends what results mean in this case

misty glade
#

ie - nothing changes over the time that the user waits for the mission to be complete

nocturne elm
#

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

proud gust
#

if really nothing changes then you can preload them

misty glade
#

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

nocturne elm
#

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)

misty glade
#

like my normal workflow is:

  1. server sends client "available missions"
  2. client assigns some crew to a mission and requests starting the mission
  3. 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
  4. client waits until mission is done, then asks server for an update
  5. 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
  6. 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

misty glade
nocturne elm
#

makes sense to me for sure

misty glade
#

cool thanks all.. helpful

nocturne elm
#

you bet

misty glade
#

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 !=

final steeple
#

I hope that's not LINQ in a hot path I'm looking at

fresh salmon
#

And I think FirstOrDefault can take in the lambda directly so it eliminates the Where, if I recall correctly

proud gust
final steeple
#

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

misty glade
final steeple
#

In addition to being slow, it generates a lot of garbage so the GC will have to keep up

proud gust
#

Ah okay, knew that it was slow, didn't know about the garbage

undone coral
proud gust
#

Are there alternatives to LINQ?

misty glade
#

Didn't know that about the hotpath LINQ performance. I had written it originally myself but it was a lot messier

undone coral
#

iterate through only the circular vision region

final steeple
nocturne elm
undone coral
#

and use the technique i described - decrement the old location by 1, increment by 1

nocturne elm
#

I don't think the decrement works for reasons described previously though, need a second data structure

proud gust
misty glade
#
                //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

final steeple
#

Because it makes writing code a lot easier when performance isn't that important

#

For example, business applications

undone coral
#

if you enable deep profiling you'll see exactly what is slow

final steeple
#

Or small tools

misty glade
#

it's not hotpath code, though, so i'm not too concerned about performance here

final steeple
#

While LINQ is basically always slower than an optimized for/foreach, it's not normally as slow as it is in Unity

proud gust
#

So even if I wrote my own extension for a map oder filter function, still slow?

nocturne elm
#

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

final steeple
#

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

nocturne elm
#

As you say, I avoided linq in unity personally but use it a lot in business work stuff

final steeple
#

So basically everything is slower

undone coral
#

are you calling UpdateShroudTexture / GenerateTexture every frame ?

final steeple
#

The difference in quality between RyuJIT (the JIT used in .NET) and Mono's JIT is insane

undone coral
#

here's what i recommend you do

nocturne elm
final steeple
#

If we had RyuJIT, most of the time there'd be no reason to care about Burst

proud gust
#

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

misty glade
#

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?

final steeple
#

Yeah it's fine as long as it's like, used during loading or initialization and places where performance/maximum efficiency aren't necessary

nocturne elm
#

Personally I would profile before you rip it out

misty glade
#

Why? The DLL is compiled externally.. (not by mono)

final steeple
#

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

misty glade
#

so it gets recompiled into the client?

#

interesting

final steeple
#

That's how .NET achieves its crossplatform-ness

fresh salmon
#

Yeah by the JIT

final steeple
#

The .dll files aren't platform-specific

misty glade
#

TIL

final steeple
#

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

misty glade
#

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"

final steeple
#

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

misty glade
#

wait I take it back, i'm foreaching in this hotpath code

#

it's still not good but.. oh well.

final steeple
#

You get no integration with NuGet (the package manager), no modern csproj, bad JIT, bad GC, no modern runtime features, the list goes on

proud gust
#

hey but we got dem nested prefabs, so we got that going for us

nocturne elm
proud gust
foggy pivot
#

hey

nocturne elm
#

go to window --> analysis --> profiler, that's most of it

final steeple
nocturne elm
#

I haven't messed with deep profiler though which you might need to get into the hotpath vs the method, idk

foggy pivot
#

anyone know how to make a maze generator using 2d sprites?

misty glade
#

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

undone coral
#

@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);
}
proud gust
nocturne elm
#

Yeah I mean hey all for making it better

nocturne elm
#

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

misty glade
#

yeah.. i appreciate that

nocturne elm
#

but maybe you mean you haven't profiled in the context of this project

#

if that's the case disregard entirely

final steeple
#

In my project it's basically instant

undone coral
#

i wouldn't ever create a new texture 2d

#

it should only happen once

foggy pivot
#

?

misty glade
#

in the whole theme of:

  1. Make it work
  2. Make it correct
  3. Make it beautiful
  4. Profile it

I'm right squarely in 1/2 ๐Ÿ™‚

final steeple
#

But you need to write your code with that in mind, so you need to manually clear statics

nocturne elm
proud gust
undone coral
#

you can even write in the second half of the function, because you're guaranteed it's visible if it's exactly 1

proud gust
#

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

undone coral
#

i'm not trying to write an exhaustive example

nocturne elm
#

I forget the issue exactly but basically it wasn't updating correctly

final steeple
#

If you create the Texture2D once you can use the Resize method to change its pixel format and dimensions

undone coral
#

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

nocturne elm
#

When I was looping every frame I may or may not have consumed 12gb of GPU memory

undone coral
#

okay

nocturne elm
#

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

undone coral
#

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

misty glade
#

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
}
undone coral
#

having just tried solving this problem, i dont' need to allocate every frame

misty glade
#

3 loops and 2 allocs in an Update()

#

๐Ÿ˜ฆ

undone coral
#

i mean allocating a texture

misty glade
#

(and some LINQ)

undone coral
#

allocating some random tiny crap doesn't matter

#

there i said it

misty glade
#

heh

final steeple
nocturne elm
#

I think what McAri suggested will work honestly for the core problem. I can look at that texture instantiation again.

misty glade
#

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

final steeple
#

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

undone coral
final steeple
#

Each LINQ method returns a new enumerable, which is an allocation

nocturne elm
#

oh I missed that code block, will check it out ๐Ÿ™‚

misty glade
#

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)

nocturne elm
#

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.

final steeple
#

I would definitely not want LINQ in an Update method, but you'd need to profile it to be sure

compact ingot
final steeple
#

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

nocturne elm
#

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.

final steeple
#

Yeah

#

As always, profile

#

Optimize what is slow

misty glade
#

opinion: when do you start profiling? constantly during dev? or only once you start approaching functional completion for a feature/project/sprint/other

final steeple
#

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();

nocturne elm
#

(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.

final steeple
#

Same for the list actually

nocturne elm
#

But it also depends if you just want to really learn what zombie is getting at in a permanent way for all future projects

final steeple
#

Instead of List<T> list = dict.OrderBy(x => blahblahblhah).Select(x.Key).ToList(); you can do

nocturne elm
#

You happen to have a lovely case here that's fairly easy to fix

final steeple
#
list.Clear();
list.AddRange(dict.OrderBy(x => blahblahblhah).Select(x.Key));
nocturne elm
#

Is this a private project or something you get a salary for?

final steeple
#

where list is stored as a field

misty glade
#

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)

nocturne elm
#

If it was salaried I would probably leave it, they just care about their features

final steeple
#

Right

nocturne elm
#

if it was my own stuff I might want the improvement

misty glade
#

well, tolist isn't really part of the LINQ, i could roll my own there

final steeple
#

ToList is part of LINQ

#

It's a method on System.Linq.Enumerable

misty glade
nocturne elm
#

(want the self improvement)

#

At the end of the day to me it's always about features and the client experience

final steeple
#

A custom-rolled one would probably be slower than the LINQ one though since you would lack all of the specific optimizations inside LINQ

nocturne elm
#

if the client never experiences it and it won't slow you down later, who cares

final steeple
#

this is the source for ToList

nocturne elm
#

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

misty glade
#

yeah the FOW conversation (I peeked in on it above) sounds like it'd be way more important to optimize

final steeple
#

In fact that type mainly exists just to allow LINQ to optimize some cases internally

misty glade
#

before you ask - the mission1/2/3/4 objects are there as a silly sort of caching mechanism

final steeple
#

What type is _playerStateManager.PlayerModel.WorkingMissions.CompletedMissions?

#

List<PlayerCompletedMission>?

misty glade
#

aye

nocturne elm
#

My feeling is this data structure is so small it's just not that important functionally speaking

final steeple
#

You could change it to SortedList<PlayerCompletedMission>

nocturne elm
#

(the things happening in the mission are likely much more perf sensitive)

misty glade
#
    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
final steeple
#

and then you don't need to use OrderBy

misty glade
#

hm, that's an idea

#

sorted list sorts on all CRUD operations?

final steeple
proud gust
#

also it doesn't really matter how large the data is, there will always be client-server communication delay

nocturne elm
#

You do pay for sortedlist every insertion though no?

final steeple
#

I believe so yes

undone coral
nocturne elm
#

But it might be way more efficient overall

misty glade
#

paying for the sort on insertion is way better than on my client's Update()

final steeple
#

More expensive addition for cheaper enumeration

undone coral
#

i'm pretty sure that when the unit moves, all of its previous locations get decremented, and its new location gets incremented

nocturne elm
#

that makes sense

undone coral
#

and now, when it is still, the number of increments and the decrements is the same

undone coral
#

so its net impact on anywhere it is for the second and later times is zero

final steeple
#

So yeah

#

it sorts and inserts

misty glade
#

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

nocturne elm
#

I did try it but my implementation was probably shot

undone coral
final steeple
#

So adding to a SortedList is more expensive, but it's cheaper to enumerate because it's pre-sorted

undone coral
#

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

final steeple
#

With your current List<T> you're paying the sorting cost every frame

misty glade
#

yeah