#archived-code-advanced
1 messages ยท Page 23 of 1
It is an internal r&d project right now
Don't use mine!
once you have done
while (hasEvent) {
var event = events.Next();
yield return StartCoroutine(DynamicDispatchFor(event))
}
you've reinvented unirx, even if it looks like you haven't
It's not finished haha
well what is the game
When you publish, let me pay you your first dollar
lol
You don't need StartCoroutine here
lol
OK afk! gotta get back to work .. thank god the India engineers at microsoft are offline, I can't even... parse their english
"test out this if the port on that ACI"
@analog nexus like if you're using a generated c# http client, or something from github
is that what the idea is?
also if you're targeting android...
I think I'm not at liberty to discuss that, but also that has nothing to do with the question
R&D and android don't really go well together
not being able to talk about a game logline, despite it really not being sensitive or actionable at all, is like, a guaranteed way to ship something very very late
i am really not trying to shake you down
We use plain .net httpclient with all async calls
it is just funny
how much harder you've made this for yourself
how your goals are all at odds with each other
this is a very very bad idea
you can use unitask with unitywebrequest, which is a good idea
maybe just describe something generally
like what is the game, generally?
like Android and R&D doesn't make sense, and it sounds like you or your team is fairly new to unity
which is okay
you're here because you want to learn how to do this better
sometimes i feel like statements like that, while they can be true, are strongly at odds with the other facts
do you see what i mean
it is definitely a novice thing to do to use httpclient
it sort of isn't material whether you or your team have personally be using unity for 2 years
The workaround is - hold on - new HttpMethod("PATCH") instead of HttpMethod.Patch
Httpclient had not been an issue
you are in this discord, right now, discussing how httpclient isn't just an issue, but a particular weird issue
do you see what i mean?
you're saying R&D, but there is no viable R&D to do on android
you're saying no issue, and then there's an issue
you're saying 2 years, and you use httpclient
i'm really not trying to bully you, just to open you up
R&D means research and development and that can target anything really
you're saying you're not at liberty to discuss somethin,g and of course, you can describe the game in general terms
it will help me give you good advice
maybe it will only really run on desktops
and on windows
like i understand it might be aspirational to put something on android
on a phone
but you guys might not be ready for that
maybe it doesn't matter if it's buggy
maybe you don't have a deadline, which is a good thing!
If you are saying UWR would solve this issue that is a weak argument because it also does not implement PATCH, do you see that
i asked you early on if you control the source to the http API that uses patch
PATCH in general is pretty bad
it's hard to say, because you haven't told me what this is
If you're saying Android mono does not have httpclient, that would be a very compelling argument
i am operating in the dark, and you are giving me a hard time for not knowing facts. i have to sus them out of you by being a little mean, which i am sorry for
Patch is bad?
i don't know how implemented httpclient is on most platforms, because i know it's not implemented fully and i never use it
weird http methods are bad, yes
if you are using them, you are usually doing something wrong
i don't know, i worked in R&D for a while
nobody i know who thrived at R&D was ever this disrespectful or difficult
is this your goal, to do this well?
do you want to make R&D, or do you want to do something else?
is it a crypto thing?
i can only imagine a few reasons you'd be shy to say what it is
You are very confident but have weird opinions about http methods being bad or platforms you admit you know little about the state of the implementation
What's the question @analog nexus ?
i think i've helped out as much as i can
I scrolled up but I can't find it
It doesn't matter what it is called?
something something, httpclient.patch doesn't work but new httpmethod("PATCH") does, on android
the user found a bug in httpclient on android
and is confused why that would be buggy
HttpMethod.Patch throws for some of our developers
The user lol
the rest of this is the user being kind of stubborn about this. he's welcome to use whatever buggy thing it is
Coworker tested on windows, Mac and Linux
you're using unity editor on linux?
Works for him
I never mentioned Android @undone coral
I reckon you might be able to find a library if you need something nicer
We already have a workaround
you're using unity editor on linux?
I just wonder why it is different between developers
Yes
It works reasonably well
linux isn't really supported
Haha
it will have a lot of bugs and discrepencies
It's an interesting question @analog nexus. Do you know what the exception is?
It's a personal choice but it'll take a lot of bugs for me to go back to windows
Are they on different OS or just different distros?
๐ best I can do is a single worm
This one i believe
it's funny because it makes so much more sense now
i wasn't expecting the linux editor to be the key fact i needed to know
Coworker tested it working on Mac, win and Ubuntu, other coworker says it fails on windows
we can't really rely on his coworkers...
public static System.Net.Http.HttpMethod Patch => throw new PlatformNotSupportedException ();```
i guess this is plain as day
i don't know
i don't know what this is really about anymore
good luck out there
Right, so why do I not see this exception
i think the code is really really buggy
and your comprehension about what is going on is limited
is my honest answer
that is the best explanation
@analog nexus are you building il2cpp?
No
I guess that uses the same library
without the code, or the context of the project, or any substantive facts
it's hard to give you a good answer
Maybe it is pulling in my dotnet system libs?
however, the overwhelming preponderance of other facts - the linux editor, the weirdness around telling us what the game is, the multi-platformness, using PATCH - suggests strongly that you guys have made some bad decisions that you can earn a lot from revisiting
yeah, maybe.
sure
this is like the part of the tarot card reading where someone asks "oh, that's what the cards say?"
"sure"
If it helps you, let's call it super turkey puncher 3
I'm trying to do smth pretty weird, maybe somebody got some input on it.
I have 2 canvases, both have the same setup. (pixel ratio, canvas scaler,...)
1 canvas contains ui objects, usually nested. for example an inventory parent with items nested (its obviously more complicated but for the example keep it simple)
now, i have an image on the other canvas.
I am trying to move the image on the other canvas to overlay a child element (for example an inventory item) of the other canvas.
anybody got an idea how to do that and which coordinates to use
not sure I understand... why not just setup a new image on the UI canvas an put the image in there?
whats the reason for using 2 canvases?
cause i am adding gamepad support as an afterthought and i want to add a virtual cursor that works for my menus. my menus are split up in different canvases and i just try to use it as a visual indicator
its not really about why, its more about how
its because https://xyproblem.info/
Asking about your attempted solution rather than your actual problem
ok but i know my shit
in any case, you'd use screen space coordinates obviously
https://docs.unity3d.com/ScriptReference/RectTransformUtility.html this guy is good for dealing with rect transforms & screen space
so can i just convert world space (transform.pos) to screen space
yes
and then the other way around i guess
yes
that particular class is for RectTransforms.. which is what ya use on canvases, but yeh

assuming you are using an overlay canvas btw
otherwise you'd just use the RectUtility and use the canvas coordinate system
Can someone please confirm this: When in a coroutine, and I call another function of the same type e.g.
IEnumerator MyMainCoroutine() { yield return MySubCoroutine(); DoStuff(); } IEnumerator MySubCoroutine() { if (someCondition) yield return 1; }
Would it be accurate to say that MyMainCoroutine will relinquish control to the main thread, before calling DoStuff(), if, and only if, someCondition is true?
yes
thanks
indeed- that will always happen.. just need to know if control will go back to the main thread when MySubCoroutine calls yield return 1 , and WONT go back, when it doesn't.
tbh, the more i think about it, the more i dont know
k, I shall try and test
it yields to the main thread regardless of what MySubCoroutine returns
interesting
dang now I have no ideas on how to get around that, without, obviously, having the conditional in the main function.
How do I define a LayerMask that is all layers excluding one?
My first though was to do this:
LayerMask all = ~0;
LayerMask layerToIgnore = 4;
LayerMask allExceptLayer4 = all & ~(1 << layerToIgnore);
But when using this LayerMask to raycast against all layers except "layer 4", it does not seem to work.
Am i misunderstanding how the "mask" part of LayerMasks are supposed to be used?
LayerMask all = ~0;
LayerMask layerToIgnore = 4;
LayerMask allExceptLayer4 = all ^ (1 << layerToIgnore);
coroutines are always on the main thread
yield return 1 will have the effect of resuming the coroutine's execution when that script's Update will be called next
does that make sense?
Yea this appears correct. Thanks. I messed up my bit ops
@abstract folio that is almost exactly how it actually works. Update will not appear in the stack
oh never mind
yeah, I just meant control leaves my routine.
it's hard to translate into updateloopese
@undone coral I'm definately thinking about "rolling my own", with update- that might just be the workaround I need
no
don't do that
you don't need update
what do you think you need to do that?
i actually wrote a wrong translation, that's why i deleted it
it is very hard to write what you did in updateloopese
translating from coroutines (good) to updateloopese (bad)
I have a function I'd like to call that basically yields the coroutine, but does so on some condition I compute. if the condition is false- I want the routine to continue processing this "frame". let me put up the code one mo
what ar eyou actually trying to do
okay well that's a well known wart of coroutines. the workaround is documented here: https://forum.unity.com/threads/yielding-a-nested-ienumerator-waits-a-frame-even-on-break.772352/
basically you do
nice, gonna check that out- thx
// type is IEnumerator
var childCoroutine = MySubCoroutine();
while (childCoroutine.MoveNext()) {
yield return childCoroutine.Current;
}
this is the succinct version since that thread is pretty long and noisy with lots of confused people
perfect
okay i wrote the corrected advice at the bottom of that post
weird http methods are bad, yes
the method is literally just a string in the header of the http request. nothing special about what it is except frameworks like to introduce enums for well known ones
if you are using them, you are usually doing something wrong
strong disagree, that is your opinion
nobody i know who thrived at R&D was ever this disrespectful or difficult
not sharing your opinion is not disrespectful. if you have good arguments they can stand on their own merit and not "do it my way because I told you so and because people gestures vaguely say so
is it a crypto thing?
it has nothing to do with what the original question or the issue you try to make it so hard is. The only reason why you're so hard headed about knowing what the project is called or what the intended use case is to dis the project as a whole.
i can only imagine a few reasons you'd be shy to say what it is
dito
Oh that's typical Pangloss banter
You can just ignore when it gets too wild
They do this all the time
you made your point "UWR is supported by unity" well enough. but I dod not ask for your opinion on system.net.httpclient vs UWR
mixing in "well you and your team are probably unity noobs that got their programming from youtube to do a crypto project" is just ๐งโ๐ณ ๐
ugh. I grew up on IRC. you'd expect people to grow up
What am I doing wrong here?
2022/10/18 19:41:16.318 14236 14286 Error Unity [Authentication]: Request failed: 401, {"title":"PERMISSION_DENIED","detail":"unable to validate token","details":[],"status":401}
Trying to SignIn with google play games
PlayGamesPlatform.Instance.Authenticate(success => {
if (success == SignInStatus.Success) {
Debug.Log("Play Games Services Authentication successful");
string userInfo = "Username: " + Social.localUser.userName +
"\nUser ID: " + Social.localUser.id +
"\nIsUnderage: " + Social.localUser.underage;
PlayGamesPlatform.Instance.RequestServerSideAccess(true, code => {
Debug.Log("Authorization code: " + code);
token = code;
UnityAuthenticate();
});
Debug.Log(userInfo);
PlayerPrefs.SetInt("playgames", 1);
} else {
Debug.Log("Play Games Services Authentication failed");
PlayerPrefs.SetInt("playgames", 0);
}
loading.SetActive(false);
});```
private async void UnityAuthenticate() {
if (AuthenticationService.Instance.SessionTokenExists) {
/*
* SIGN IN WITH CACHE
*/
try {
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log("Sign in anonymously succeeded!");
Debug.Log($"PlayerID: {AuthenticationService.Instance.PlayerId}");
} catches {}
} else {
if(PlayerPrefs.GetInt("playgames") == 1) {
/*
* SIGN IN USING GOOGLE PLAY GAMES
*/
try {
await AuthenticationService.Instance.SignInWithGooglePlayGamesAsync(token);
Debug.Log("SignIn is successful.");
} catch (AuthenticationException ex) {
Debug.LogException(ex);
} catch (RequestFailedException ex) {
Debug.LogException(ex);
}
} else {
/*
* SIGN IN ANONYMOUSLY
*/
try {
await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log("SignIn is successful.");
} catch (AuthenticationException ex) {
Debug.LogException(ex);
} catch (RequestFailedException ex) {
Debug.LogException(ex);
}
}
}
}```
Do I need to add any url to the authorised list in google cloud?
why are you passing a server side token here?
this isn't from the documentation
it is telling you what the issue is. you are trying to use a server side token from the client.
also, your code has invalid syntax - } catches {} - so this is not what you are running
what are you actually trying to do?
Well the catches is so it all fits in the message
I need the server token to use it here
Also it does get the code correctly
The problem is using it
I am trying to pass the token to Unity Authenticate so it authenticates correctly with Google Play Games in Unity Authentication
okay
i think you should probably follow the documentation exactly
there is a lot of noise, and i can tell you want something that "just" works
did you find examples doing what you want?
Yes
And I used them ig
Also I think I did follow the documentation
The entire code is basically code from documentation but with some ifs and elses
hmm
can you show me that documentation?
i don't see where you read i found ittoken here
honestly this is a huge mess
it is impossible to see what is going on, where token is being used, etc.
because it's not in this code snippet @indigo citrus
I can send the whole class if needed ig but Ill explain a little bit more
And maybe just go to the part thats throwing the issue
You can ignore everything but the sign in using google play games if Im correct
here is a translation of what your code is trying to do, but actually makes sense.
// install UniTask from https://github.com/Cysharp/UniTask
using UniTask;
void Start() {
AuthenticateWithGooglePlay().Forget();
}
async UniTask AuthenticateWithGooglePlay() {
var pgpAuthenticateFut = new UniTaskCompletionSource<SignInStatus>();
PlayGamesPlatform.Instance.Authenticate(pgpAuthenticateFut.TrySetResult);
var success = await pgpAuthenticateFut.Task;
if (success != SignInStatus.Success) {
Debug.Log("Play Games Services Authentication failed");
return;
}
var pgpSsaFut = new UniTaskCompletionSource<string>();
PlayGamesPlatform.Instance.RequestServerSideAccess(true, pgpSsaFut.TrySetResult);
var code = await pgpSsaFut.Task;
await AuthenticationService.Instance.SignInWithGooglePlayGamesAsync(code);
}
Where it does use the token in the method SignInWithGooglePlayGamesAsync
@indigo citrus does this make more sense?
I mean yeah but thats what its basically doing ;-
don't do it the way you're doing because it's completely incomprehensible
But it has the mess of checking if a user is already cached
so it's impossible to know if there's some bug
well you can do it much easier here
the thing about a user being cached isn't related to your error
in my experience when i see code that is totally bonkers, rewriting it all is the best approach
And if cant for some reason login to google play games it will just go with a anonymous user
you haven't sent me a link to the docs you used yet
Oh wait 1 sec
i understand that
you can write that later
it is really easy to do that in the snippet i wrote
as opposed to the one you have
which is "callback hell"
And the all the docs pages related to it
And signing in a cache user and signing in a anonymous user
okay, based on what i see there
it's probably because your code is nuts
try mine
install unitask
you can't use regular unity async anyway
Ill try later and see how it goes I guess thank you
I am in my phone rn xD
I will check everything you sent
In order to accomplish what I need
But I am in my phone so theres nothing I can do rn will make sure to give you feedback after doing so ๐
gotchyu
understandable lol
i've had bad luck helping people today
xD keep the good work ๐
yes overall i'm surprised this is what unity's authenticate framework wants. but it's understandable
Hello guys, I am developing an Android application and I am trying to test my android library (written in Java) in Unity, but seems like debugging does work differently (I can't just enter the game mode and see the log of my library, since Application is in Editor mode, not in Android device)
How to debug such programs?
yeah, I'd like to know some options for that, otherwise I guess I'm emulating the devices
First, make sure you're using https://docs.unity3d.com/Packages/com.unity.mobile.android-logcat@1.3/manual/index.html
second, you can install Android studio and attach the debugger from there and debug your Java code.
Android Studio also has Logcat built in
hello, I want to write a Hexagonal Grid Library for my game. I guess I have to take out the "monobehaviour" word so I can use it outside of an scene. Then I though about using static methods for the library like public float GetHexagonWidth(Hexagon h) but then I realized all hexagons will have the same width, so I wanted to store it in some place rather than storing it in every hexagon, or calculating it everytime I want to use it. Is there a way to handle my problem?
Unity has a built in Hexagonal Grid library, you could start with just using that
There's a Grid component, built into Unity that can handle hexes out of the box.
thank youi
can I see a code demostration?
of what
The docs are here:
https://docs.unity3d.com/ScriptReference/Grid.html
Most important methods are:
https://docs.unity3d.com/ScriptReference/GridLayout.WorldToCell.html
https://docs.unity3d.com/ScriptReference/GridLayout.CellToWorld.html
sorry guys but i dont know where else ask this... how do you say in technical english when you debug in device instead of normal debug in editor?
does it have a name or simply.... "debug on device" ?
im talking abut debugging with the IDE obviously (VStudio or Rider....)
thanks a lot
That's what I'd infer but haven't really heard that
i've read it around a few times but if debug or build on device is the most common its fine, thanks a lot
There's a standalone profiler, i.e. separate process from the editor https://docs.unity3d.com/2020.1/Documentation/Manual/ProfilerWindow.html#standalone
But probably not waht they mean
ahmmm, what Im looking for is specific problems with people debugging on device with il2cpp builds and unity >= 2021
Rider has a bug that doesnt allow to debug
you connect the device and stops at the first breakpoint but after that it hangs
and VS simply doesnt connect to the device
it sucks
That might be on Unity instead ๐
yeah you might be right, still they have it in their issue tracker (rider)
I know in some versions you can't profile on android because unity messed up
So wouldn't surprise me if they did the same for dbeugging
ill check the forums
Problem: CreatePrimitive(), new GameObject() and Instantiate() fail when called in large numbers during update
Hello everyone, I have come across a problem yesterday afternoon that to this day I have no idea at all how to solve. Let me start by showing you a video.
I am preparing some code for a voxel engine I'm making, and here is a prototype of a chunk generator. As of now, this is just a script that generates cubes in a set grid around a focus point, which in the hierarchy is denoted as "focus".
Everything the script ought to do as of now is to simply delete cubes that are too far away from the focus, and generate new ones in valid but empty places.
The way I have decided to implement this is as follows:
- Let there be a three-dimensional array,
chunkGrid, of classChunkPlace, the latter storing information about chunks, such as LOD stage, the mesh/object, and the chunk as a node for an Octree (sort of). chunkGriddoes not have an instance ofChunkPlaceat every position. Only positions that are inside the circle are not null.- We will use
chunkGridprimarily to calculate the world position of the chunks through the array position and some other data.
Now, here is the algorithm that is run every update for every array entry in chunkGrid:
- Is the entry null, i.e. is not it part of the sphere? If so, skip this entry.
- Is the entry's node field null, i.e. is the place inside of the sphere, but isn't occupied by a chunk? If so, generate a chunk and place it in.
- Is the entry's chunk too far away from focus? If so, remove the chunk (this will never fire simultaneously with the before mentioned point, as it will never generate chunks outside of reach.)
Here is the relevant code:
private void ConstructChunks() {
//We don't start at 0, but at negative extent. Ix, iy and iz will convert this to an index that starts at 0
//and ends at 2*radiusInChunks-1, which are also the bounds of the array.
for (int y = -radiusInChunks; y < radiusInChunks; y++) {
int iy = radiusInChunks + y;
for (int x = -radiusInChunks; x < radiusInChunks; x++) {
int ix = radiusInChunks + x;
for (int z = -radiusInChunks; z < radiusInChunks; z++) {
int iz = radiusInChunks + z;
if (chunkGrid[ix, iy, iz] != null) {
//create a chunk if a place is expected but empty
if (chunkGrid[ix, iy, iz].chunkNode == null) {
Vector3 playerPos = player.transform.position;
VoxelOctreeNode chunk = new VoxelOctreeNode();
chunkGrid[ix, iy, iz].obj = ConstructOctreeNodes(chunk,
chunkGrid[ix, iy, iz].LODStage,
Vector3.zero,
snapToStep(new Vector3(x*chunkExtent*2+playerPos.x,
y*chunkExtent*2+playerPos.y,
z*chunkExtent*2+playerPos.z),
chunkExtent*2),
chunkExtent);
chunkGrid[ix, iy, iz].chunkNode = chunk;
}
//remove chunk if it is too far from player
if (Vector3.Distance(chunkGrid[ix, iy, iz].obj.transform.position, player.transform.position) > loadedTreeExtent) {
Destroy(chunkGrid[ix, iy, iz].obj);
chunkGrid[ix, iy, iz].obj = null;
chunkGrid[ix, iy, iz].chunkNode = null;
}
}
}
}
}
}
Before I continue, I need to mention that the lower bit of the function (the one responsible for removing chunks) is working as intended, so there is nothing to check there.
The upper bit, the creation (which is the one we're having a problem with), calls a function that is originally intended to also execute some Octree logic, which right now is disabled, however. You can ignore it and see it as a call to the following logic, which is also the only logic that is executed before the method jumps out:
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.transform.SetParent(GameObject.Find((currlevel+1).ToString()).transform, true);
obj.transform.localScale = 2 * chunkExtent;
obj.transform.position = snapToStep(new Vector3(x*chunkExtent*2+playerPos.x,
y*chunkExtent*2+playerPos.y,
z*chunkExtent*2+playerPos.z), chunkExtent*2);
The three next lines work as intended. snapToStep() is a function I made to snap the Vector3 to a grid which is formed by a certain step, in our case double of chunkExtent.
However, as we see, for some reason the first line (and the next ones) aren't even called at times.
In the video, you saw small gray gizmo circles. They are drawn here:
void OnDrawGizmos() {
for(int y=-radiusInChunks; y<radiusInChunks; y++) {
for(int x=-radiusInChunks; x<radiusInChunks; x++) {
for (int z=-radiusInChunks; z<radiusInChunks; z++) {
if(chunkGrid[radiusInChunks + x, radiusInChunks + y, radiusInChunks + z] != null) {
Vector3 playerPos = player.transform.position;
Gizmos.DrawSphere(snapToStep(new Vector3(x*chunkExtent*2+playerPos.x,
y*chunkExtent*2+playerPos.y,
z*chunkExtent*2+playerPos.z), chunkExtent*2), 0.33f);
}
}
}
}
}
These circles are there to signify that the traversal of the array and the conditional clauses are executed correctly, as we see the gray gizmo circles exactly where we would expect a chunk to be generated.
Hence, this part of the logic can't be wrong (neither the traversal of the array, nor the first condition)
Suspect: I honestly believe this isn't an error on my part. All the code is correct, especially reinforced by the gizmos. I also revised it with a relative, who has >20 years of experience with programming - he couldn't find the issue either. Could it be that for some reason the Update() execution just breaks out after a certain amount of iterations? Notice in the video how, of the chunks that are supposed to be loaded, only the outside chunks are loaded, not the inner ones.
The pattern of the faulty generation also makes me believe Unity interrupts the loop at some point for some reason.
Or, maybe some exception is thrown, but isn't notified in the log?
Fyi, yes, the log is absolutely clean.
Thing's I've tried already:
-Swap out CreatePrimitive() with new GameObject() and manually adding components per script
-Change the initial condition from checking if the ChunkPlace entry in chunkGrids is null, to simply checking manually if the chunk is inside/outside a sphere.
-Putting the entire logic in OnDrawGizmos(), out of suspicion of Update() especially failing, while the gizmos work with the iterations.
-Adding private fields of types MeshRenderer, MeshFilter and BoxCollider at the top of the script, as I've heard that Unity might throw these APIs, which are needed for CreatePrimitive(), away if they are not used anywhere
-Swapping out the code so that it first tries to delete, then generate. This actually made things worse.
if (chunkGrid[ix, iy, iz] != null) {
you check the element is null
Destroy(chunkGrid[ix, iy, iz].obj);
chunkGrid[ix, iy, iz].obj = null;
chunkGrid[ix, iy, iz].chunkNode = null;
but removing the chunk you don't actually make that element null?
Yes, because chunkGrid does not store chunks, but objects of class ChunkPlace, which is a placeholder for every information about a chunk.
Its presence doesn't signify a chunk, but a valid place for a chunk. We don't want that to change ^^
ah i get it sorry
I have tried swapping that condition for a simple sphere check (specifically if(x*x+y*y+z*z <= radiusInChunks*radiusInChunks), no change.)
the indentation on discord is shit
It is..
really dumb check but you tried to find out using debug.log how far along it goes?
I would try, but the amount of chunks that are generated is so much that it becomes hard to debug using Debug.Log.. ^^'
I could try lowering the radius drastically and see where it goes, though.
OnDrawGizmos you only check if the chunkGrid Element is not null
check if the node is null also
chunkNode
Hm, alright!
increase the distance and see if thats the issue
Hey can someone please help me out with some mathematics and astronomy? In short, I'm making a realistic game based on the island Tetepare, and I want the path of the sun (yes, I know the Earth rotates around the sun not vice versa, but in videogames the sun rotates around the Earth) to be as accurate as possible. I found this website that lets you see the exact position and rotation of the sun at any date and time https://drajmarsh.bitbucket.io/sunpath3d.html . I want to implement this into my game though I'm not sure how. I don't know what formulas are being used here, and what the best way would be to implement this. Any help is appreciated ๐
A dynamic 3D Sun-path experiment.
Weird. The gizmos are still displaying correctly, meaning that the condition that checks if it should generate is working - however, it doesn't generate.
Another thing I've found out that as long as the focus is at (0, 0, 0), chunks are generated once and done, which is what we want. We don't want them deleted and re-generated every frame. However, even if just a teeny tiny bit, if the focus is moved in any direction, well...
The log says it constantly generates them infinitely many times. In the inspector I can see a finite amount of cubes, however - for some reason, it generates cubes, which it instantly removes afterwards.
Hm, maybe those are the cubes that are missing?
In that case, the removal function could have a wrong condition.
The radiusInChunks might not match the actualy distance for the loadedTreeExtent
It doesn't, but those are two seperate things.
loadedTreeExtent is the world radius of the sphere in which chunks should be loaded. It is defined as chunkExtent*2 * radiusInChunks.
radiusInChunks is the radius of the sphere measured in chunks.
chunkExtent is the extent of a chunk from its center to its surface, aka 2*chunkExtent is the width of a chunk.
chunkGrid uses radiusInChunks because it is independent of the size of chunks - it is merely an abstraction of the chunk sphere around the player.
loadedTreeExtent is used only for the Vector3.Distance() function, in which we expect world values.
But!
It is indeed true that for all other functions we use our own equation to find out of it's inside the sphere, while here we use Vector3.Distance(). Let me try to change that and see what happens
Wait, I think I'm onto something..
Nope, I'm not.
However, on second inspection, the issue is indeed with the removal condition. The log shows it's hectically creating and removing chunks every frame, even though focus stays still.
Oh my, I think I know why.
You see, chunkGrid is fully abstracted. That means it doesn't take into account anything in the world, it only sees a static grid of cubes arranged in a sphere.
That also means however, that if we move the focus forwards, the sphere function calculates that new chunks should be placed at the new front
however, for chunkGrid, there is no "new" front, as it never listened to focus anyways. It is an abstraction
And since the chunks one position behind the front (which, for chunkGrid, is still the front) still exist, chunkGrid never released their position, and as such, no new frontal chunks are generated.
I should just generate a grid-snapped sphere of chunks around the focus like a normal person instead of relying on weird arrays such as these...
Off to the Unity forums I go. @sweet niche Thank you for your help, though. ^^
Would it not be better to generate the whole map. Then focus would only turn on the renderer for the cubes near it
Well, it is a procedural voxel engine.
The "whole map" could require terrabytes of memory if loaded at once - remember that right now we're only dealing with chunks, later on each chunk will have an Octree structure beneath it of several levels ^^
With each Octree node taking up ~26 bytes (that's the best I could do), and every level being 8/9% more memory than the previous, assuming a depth of 5, every single chunk could take up to ~832 kilobytes
Well, that is assuming that every node is in full resolution, which it certainly won't be. I could guess that ~30% of nodes would be in full resolution
Let us take a guess of ~100 kilobytes of memory for each chunk. For a render distance of 32 chunks in each direction, we could expect already some 2 GB to be gone just for the voxel engine
And this is just talking about memory, not even rendering
But I will add in certain optimisations (such as LOD) that will certainly let you have a larger render distance than 32 chunks, and more resolute chunks to allow you depths of even 8 maybe. Yet even then, we won't be able to load in the whole world ๐
{
"data": {
"id": 3,
"userId": "1960a78f-4d21-4e2e-a81d-f75a8e55da30",
"creationDateTime": "2022-10-18T13:51:45",
"updateDateTime": "2022-10-18T15:50:11.003",
"settings": "{"selam": "ahmet"}",
"version": "1",
"courseId": null
},
"success": true,
"message": null
}```
Hello I want to remove the quote of the settings
because of those quotes the string did get parsed by JSON parser
how can I remove them?
by giving the correct data to the serializer
the settings field is stored as a string value in the database. I do serialization twice.
But I solved it anyway in a not elegant way (string parsing.)
Tahks
public async Task MakeRequest<TRequest, TResponse>(VRCloudRequestBase vrCloudrequest, [CanBeNull] Button clickedButton, [CanBeNull] UnityEvent<VRCloudResponseBase> callbackFromSignature)
What about here? when I call this function, I have to sign the callback as null in the caller method
I want let people decide whether they write a callback or not. I do not want them to explicitly write "null" in the caller function
how can I do that without overload methods.
default parameters
although UnityEvent is kind of a weird way to handle callbacks
I made overload it worked. will take a look into default also.
I made an interesting logic. People are surprised ๐
@sly grove
default parameters are basically just syntax sugar around overloads
what even method gets called before unloading domain that happens before recompiling?
or callback
Hello! I have generated a file that I want to save in a directory inside my computer. Everything I find online is related to online stuff, but this is local. How do I go about this?
The System.IO.File class
If I have a non-regular polygon defined by a list of vertex locations defining the perimeter of the shape, how would I go about turning that into a mesh face? How would I know which vertices to combine into a triangle? I'm assuming there's some sort of algorithm for this but I'm not sure what to look up
Delaunay triangulation
Perfect, just what I was looking for
hi, is there any way to use the "get spectrum data" but with the computer sound like if i put a video on youtube it detects it?
Hello
still writing?!
I have a thread running System.Net.Socket.UdpClient.Receive()
When I send messages from my computer, the unity instance running that thread receives the message
When I send from another computer the message is not received
If I try to run that same code in a netFramework 4.7.1 console application the message is received corretcly either if I send from another computer or locally from my computer
what is going on with unity?
public void StartListener()
{
UdpClient listener = new UdpClient(_configuration.UDPPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, _configuration.UDPPort);
try
{
while (run)
{
byte[] bytes = listener.Receive(ref groupEP);
string input = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
Debug.Log("got "+ input);
DataStructure.inbound.Enqueue(input);
}
}
catch (Exception e)
{
Debug.LogError(e);
}
finally
{
Debug.LogWarning("=========== Listener Socket closed! ==========");
listener.Close();
}
}
private Thread InstanceCaller;
private void Awake()
{
InstanceCaller = new Thread(
new ThreadStart(listener.StartListener));
InstanceCaller.Start();
}
Does anyone have any idea what is going on?
I'm running the InputActionTrace example code and I get a memory leak even after disposing, anyone know how to fix this?
Here's the error log
Hi good day I'm trying to convert Setpixel to SetPixel32
above is my setpixel code
and this is what I did
But it's not working
you should explain whats not working
also there is a implict cast
Color32 color = new Color(0.5f, 1f, 0.5f, 1f); for example works
@brisk pasture sorry the dirtMaskArr[arrElement] = new Color32(0, Convert.ToByte(a), 0, 255); part is not working
why are you converting a byte to a byte then
a is double
double a = (pixelDirtMaskA * pixelDirtA) * 255;
then I converted it to byte
would just confirm you are in the right range before converting
ok I'll check it later
yes its right range 0-255
ear clipping triangulation is one alternative (thats what I implemented). Delaunay is prettier and more uniform though.
SetPixels32(int x, int y, int blockWidth, int blockHeight, Color32[] colors, int miplevel = 0);
I dont understant the blockWidht, blockHeight, Color32[] colors part
if you compare it to setpixel
public void SetPixel(int x, int y, Color color, int mipLevel = 0);
and one takes a color32 while one takes a color
Anyone has experience in using native Android libraries for their projects?
I am trying to write my own library and use it in my unity app, however I can't find a way to provide a context to my application.
I checked the resources in the internet and they use AndroidJavaClass "com.unity3d.player.UnityPlayer", then make a static function call to currentActivity; my problem is that I get the error saying "java.lang.NoSuchMethodError: no static method with name='currentActivity'"
anybody has any idea what to do?
Does anyone have experience of making NFT games? In my case, I just wanna import some random 3D NFT into my project, and I have no idea how do I have to do it
var currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
// -- use your activity here
You did this?
for the guy with raised eyebrow emoji: I mean, I don't need to download them, I need to, like, get their mesh during the runtime
from where, out of what format? What have you tried? a few seconds of searching says maybe they are GLB/glTF, so you need to parse that format at runtime and create a mesh from the geometry inside
from where - idk, some open sources like opensea
out of what format? - doesn't even matter, just wanna do it, as a mesh or as a gameobject
what have you tried? - the only thing I tried is to import some 2d NFTs firstly, and generally, it worked, but I have no goddamn clue how to do it with 3D ones
if you think that I do know at least smth about NFTs, you are wrong, I have no idea about blockchain technologies, although I'm working with Unity since 2019
Is there any, like, tutorial, guide, etc of doing that on the Internet?
if you haven't got a clue what the input is, you have no chance of solving this. Start by figuring out what exactly you're trying to import, and then you'll be able to figure out the rest
ok, so for example I'm trying to import 3d NFT model, .gltf, during the runtime, how do i?
I'd start by typing Unity3d import gltf runtime into google and seeing if you can get any of those solutions to work
Can you please set example in blockwidth, blockheight, Color32[] in setpixels?
blockWidth and blockHeight are the height and width of the subregion inside the texture you're changing, with x and y pointing to the origin of the subregion
so, if you want to change a 100x100 texture's center 50x50 subregion, use x = 24, y = 24, blockWidth = 50, blockHeight = 50
and if you want to set the entire texture, just use this overload public void SetPixels32(Color32[] colors, int miplevel = 0); instead
Has anyone ever made a RayMarching script for their games? If yes, could you give this friend a hand? ๐ญ
Ok Thank u sir
so if I have blockWidth = 50, blockHeight = 50 I should have 2,500 elements in Color32[]
that's correct
I've got Delaunay working for a solid color mesh, now I just need to figure out how to properly uv map it...
Is there any way to uniquely identify a StateMachineBehaviour which is persistent across runs? It seems impossible to get the state information from within a StateMachineBehaviour, and the only solution I've found is naive & depends on the fact that the StateMachineBehaviour has been entered at least once
Use either a IMGUIContainer or use the ListView control
yes ListView made the work
Hey, I have a problem. I have a Gameobject with a list of weapons(custom class) and in this class there is a Gameobject variable called bulletPrefab and on this bulletPrefab is a script with some public variables like damage and so on. I now want to be able to change the variables on the prefab from script. Hope someone knows what i mean and can help me (:
Keep in mind you have to cache it and dispose it, so you can't just create it each frame
first of all, it should not be GameObject propery but rather a ScriptName property. As long as your script inherits from MonoBehavior it is automatically a gameObject. Only after that you can directly change its properties
or you can do a stupid but workable
someGameObject.GetComponent<SomeScriptOnIt>().SomeProperty = "SomeValue";
It there a way to make it shorter?
My Gameobject with the avalible weapons script on it Instantiates bullets like so
Instantiate(avalibleWeapons[selectedWeapon].bulletPrefab_, gunTransform.position, Quaternion.identity);
And i want to be able to change the properties of this bullets like damage speed etc from script, so that all the Instantiated bullets have values i choose in script
Currently i modify the values like this:
Weapon w = new Weapon...
w.bulletPrefab_.GetComponent<Bullet>().effectDamage = w.damage_;
w.bulletPrefab_.GetComponent<Bullet>().effectDuration = w.effectDuration_;
hope you understand what i mean
You can sacrifice performance by decoupling it via SendMessage, BroadcastMessage if that's your issue
no the problem is that it is not working the newly created bullets all have the same values no matter what i change it to
From your code it looks like you're modifying the prefab instead of the instance
var go = Instantiate(avalibleWeapons[selectedWeapon].bulletPrefab_, gunTransform.position, Quaternion.identity);
And then do GetComponent<> on go instead
Yes I did it like this but it seems like a bit of a workaround no?
because like this i have to set it new for every bullet i Instantiate and I just wanted to know if it is possible to set it for the prefab attached so it automatically spawns all bullets with the right values
It's possible but generally a bad idea because any such changes to a prefab in the editor will be permanent
If you want different values you should do one of two thigns:
- switch to a different prefab that has the different values
- Use a pattern where you use the prefab as a base, then apply some transformation/augmentation on the values after spawning the new bullet.
How does Unity detect when a NativeCollection is disposed or not?
A Native Collection has not been disposed, resulting in a memory leak. Enable Full StackTraces to get more details.
I am disposing my NativeArrays eventually so I am not sure how Unity is determining that I am leaking them.
you can look at the source. when do you get this message?
you should use a persistent allocator if you want bonafide manual memory management
everything else is a combination of some nuanced heuristics and the consequence of the C# object being finalized
drag and drop the prefab into your scene, and deactivate it. now use the inactive copy as your prefab in code. if you need to customize changes to it, instantiate (duplicate) the inactive copy, and modify it
there's no meaningful difference between a prefab and a copy of it that was never active once (i.e. Awake was never called)
The message comes from here,
https://github.com/Unity-Technologies/UnityCsReference/blob/4dfc79719d58582472e6c6774633b5c8044fc297/Runtime/Export/NativeArray/DisposeSentinel.cs
I'm not seeing where the sentinel is created though.
I am using the persistent allocator by the way
you can use trilib2, a runtime mesh importing asset. most NFTs do not directly reference a 3D object, but an animation of one. if there is a bonafide reference, it is probably a usdz, gltf or other web native 3d format. hard to say how it is packaged. you can explore how to convert it or use it with trilib2
i'm asking what is going on in your code or player when you see this message?
do you see it when you unclick play in the editor?
Seems to happen when editor is not focused, or when I return from editor. I create the NativeArray during ScriptableRenderPass.OnCameraSetup and dispose it during the next beginframe callback. I suspect that a delay between the end of the frame and the start of the next one due to the editor becoming inactive might cause Unity to misidentify the leak
in this case, when you see that message, the number of references to the C# NativeArray object goes to zero, and you personally did not call dispose, so it is considered a leak
i think it actually leaks
but it might not matter because it is in the editor
it's possible to not render the next frame
this makes sense to me
so it's a real leak
I also dispose on the ScriptableRenderPass.Dispose
you can use the tempallocator instead, which will last for 4 frames by default.
the leak is real
what is your objective?
pass some data to a native render plugin, there the dispatch is asynchronous so I can't simply dispose right after the call
I add the NativeArray to a List<IDiposable> could the boxing/lack of type information cause an issue?
lol
i don't see your code, but the message it is saying about there being a leak is true
what you have told me so far makes it sound like the leak is real
because the c# object gets finalized before you called dispose, and you chose persistent allocation
I will add a finalizer to my object that tracks the arrays and see what is happening there
that isn't necessarily why it's being finalized
the nativearray is being finalized
there is... probably a bug in your code. it could be that you have two instances of a nativearray somewhere, by accident, or whatever.
you haven't shown any of it yet
you haven't really explained what you are trying to do - what does the render plugin do?
what you are doing doesn't really make sense yet
i guess if you think the leak isn't real, well i can't help you sorry
i don't know then
there's a bit going on here, I am trying to explain without going too deep into the weeds
this is a real bug in input system. they have the wrong allocator chosen.
i sent them a bugfix for this
do you think the leak is real or not?
I think that if my understanding of how the lifecycle of RendererFeature and ScriptableRenderPass does not match reality, then it certainly is a leak
great, so it's real lol
but i need you to say
"the leak is real"
your understanding of that lifecycle may be correct, but there might also be a bug
I'm gonna prove it first, and then I will
do you agree that is the most likely possibility? that the lifecycle isn't as important as your own code
i think that is a colossal waste of your time
I just need to wait for the error condition to occur and see that the finalizer happens without dispose being called
I don't think that's going to waste too much time
honestly
i'm not even sure
that's really how it works
i THINK that's how it works
i know that when it says there's a leak, it's real
because it's bulletproof
it is so easy to implement that kind of detection
i am pretty sure that you have to call dispose on your own persistent allocated things
but i'm not sure
that seems unnecessarily clunky
because i haven't used nativecollections to author my own thing. if you told me what you were trying to do, maybe there would be an easier approach
instead you're talking about a bug in your code which i can't see so i have no idea, i can't provide any insights
there's no std::move in native collections, they are not smart pointers, that should illuminate how their implementation works, and why i believe that you have to call Dispose on your own persistent nativearray, even if it could call dispose in its own finalizer, because it assumes you are using the native pointer elsewhere. but i am not sure about that
Sorry, when I was talking about dispose and finalizers, i was talking about my own custom class that manages the native arrays
ok, the leak is real
the problem is that ScriptableRendererFeature::Create() is called more than once, which is where I allocate the object that tracks the native arrays
I am creating custom unity editor using UnityEditor.UIElements. I have a class with public fields that are obviously serialised. Is it possible to get a list of serialised properties of a class somehow? Just like a normal inspector does when you create public fields in a class?
I mean do you know how inspector draws serialised fields? I want the same thing
I made it. It's csharp functionality here
Damn, I see, hopefully they fix it soon, it's such a useful feature, thank you for the clarification
Static constructor would be the static KinectSensor() method
Please use a paste site to post code this big
The only constructor I found is this
Yeah the error reports a static constructor but your decompiler might just be stripping it out for clarity
Open it
void .cctor() cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: call uint64 Microsoft.Kinect.NativeMethods::NuiDebugGetFailureStack()
IL_0005: stsfld uint64 Microsoft.Kinect.KinectSensor::failureStackPointer
IL_000a: ldc.i4.0
IL_000b: stsfld bool Microsoft.Kinect.KinectSensor::_unsupportedMessageShown
IL_0010: ret
} // end of method KinectSensor::.cctor```
Okay before we dive in blindly, run the debugger for your Unity project, run the game and it'll break where the exception happens (was in a script's Start method if I remember correctly)
Then post the full exception stacktrace
Yeah that should be good
I pressed it. Now Where do I find the stacktrace?
It'll break when the exception happens
VS will get the focus and on the line of code it happened it'll be clearly marked
Unless Unity messes with that of course
it didn;t break
Did it log the exception?
yes
So it's not breaking, great
Click on the error in the console and look at the bottom part, the full message will be there
Meh not very explicit. back to the static constructor code then
IL_0000: call uint64 Microsoft.Kinect.NativeMethods::NuiDebugGetFailureStack()
It calls this. Find NativeMethods.NuiDebugGetFailureStack() and post its contents
uint64 NuiDebugGetFailureStack() cil managed preservesig
{
}```
I don't remember seeing it anywhere
Jsut started a search on this pc
it's i nsystem32
Hm can you try something out for me? In the C# code comment out the Debug.Log call. Add this thing in the class
[DllImport("Kinect10.dll")]
static extern ulong NuiDebugGetFailureStack();
And in Start call that, and log what it returns
(misspelled method name -- edited)
Yes, hover over the error and click the light bulb icon
fixed
We're advancing, the Kinect10.dll is the one throwing
using System.Collections.Generic;
using UnityEngine;
using Microsoft.Kinect;
using System.Runtime.InteropServices;
public class kinectScript : MonoBehaviour
{
[DllImport("Kinect10.dll")] static extern ulong NuiDebugGetFailureStack();
// Start is called before the first frame update
void Start()
{
// Debug.Log(KinectSensor.KinectSensors.Count);
Debug.Log(NuiDebugGetFailureStack());
}
// Update is called once per frame
void Update()
{
}
} ```
Not sure what to do from this point though
decompile it?
Probably not IL that one
What is IL?
Intermediate Language, the language all .NET languages compile to. The static constructor you decompiled had some
The first one if your system is 64bit, maybe??
It fails to decompile
Yeah so it wasn't developed with any .NET language
I'm pretty much out of ideas here, the last one being "drag-drop the Kinect10.dll into your Assets aside the Microsoft.Kinect one"
If you put a path in the DllImport attribute, I don't think it'll work
It doesn't use paths, if I remember correctly
but that same method is not called when the dll works as intended, right?
It gets called and something inside of it throws
Here it doesn't even manage to locate the file
waht if I do taht in the console application?
It'll work, if the Kinect DLL works in the console app
You get the DllNotFound when you run the code because .NET uses lazy loading, it'll attempt to locate and load the very first time you reference it, not when the whole game starts up
Oh well, then that's probably what also happens within Unity. You said you were using Kinect V2 no?
no, i'm using the xbox 360 kinect
yes, in fact it's the same exception
OR maybe becasue there are two such named files in my pc
and it doesn't know which one to refrence?
Nah it's for the executable to auto-select one depending on whether you're running 32bit or 64bit
So that throws, and the DLL uses that, so logically you can't use the DLL.
But when the Microsoft.Kinect.dll is used in the console app, it calls the same method and it doesn't throw?
And I have no idea why
Found this https://www.youtube.com/watch?v=aHGlLxh6a88
In this video, we setup the Kinect SDK v2.0 and start the base of our Unity project using the provided packages.
You can get the Kinect SDK and the Unity Pro Packages at the link below:
https://developer.microsoft.com/en-us/windows/kinect/tools
If you would like to support me, feel free to checkout my Patreon.
https://www.patreon.com/VRwithAndrew
They install a SDK and unity packages
Even the V2 is discontinued. See if you can find the SDK, but for V1
I can and I do have it on my pc, but the documentation doesn't exist
https://www.microsoft.com/en-us/download/details.aspx?id=40278 this is the one i have
hi, is there any way to use the "get spectrum data" but with the computer sound like if i put a video on youtube it detects it?
I hope this is the right channel to ask for feedback
I had a working title 3 days ago, now I need help, fast
You should actually ask a question
People don't want to have to ask for details, you should be transparent upfront
It's hard to define the issue
Basically I have all the code set up right
but now basic functions like ontriggerenter events are not working
as well as cloth component meshes are missing
it used to work on the editor, now it's unplayable
I did not change single line of code
Call a priest to perform an exorcism.
Failing that, revert your project to an earlier working state via version control.
Failing that, start debugging each issue one by one.
๐ฆ
I see no errors, does unity games have a backdoor that would let this happen?
or the engine itself maybe
Trying to grasp at some magic thing to blame isn't going to get your game working again.
this doesn't sound like #archived-code-advanced but it can be frustrating when things stop working
These will
if you need to use the original Kinect in unity, install Windows 7 in a VM, and work on it there with the official Kinect 1.x SDK. otherwise, you can buy an Azure Kinect, which is officially supported by a modern SDK
you don't need to interact with any of the DLLs this way
Have you tried closing Unity, deleting the Library folder and reopening the project?
https://pastecode.io/s/nwgpy2rw
I would like feedback on this system I am planning: It is a behavior timeline for enemies and projectiles to save myself time and confusion. It will have an instruction type and tweakable fields for the specific instruction.
Questions:
- Is there a cleaner way to implement the TimelineEntry classes than what I am testing here? (Worried about having a lot of repeated code)
- Is this a common way to achieve more complex enemy and projectile movements/behavior?
might wanna make TimeLineEntry an abstract class
Unfortunately Unity gives an error if you try and serialize a list with an abstract type. I already did something similar with the items in my game.
rip
How would you serialize all those timeline entry list items as [SerializeField]s any way? It will always Serialize the base class.
You'd need [SerializeReference] if you're doing what I think you're doing.
I know there aren't any "best" way to implement an ability system, but does anyone have any good resources/references to check out for ways to design a event based ability system. Alternatively some learning resources. Imagine the type of abilities that would be common in TCGs. OnAttack, OnDamage, OnDeath, OnFriendlySummon etc etc.
Hello everyone!
We are developing a mobile game and
we have integrated Unity Remote to our game, we are using it for version number check. Not a big deal.
The issue we have found is in some cases this is not accessible for every users. Like we have one problem from Malaysia, and another one from Vietnam. But we have multiple other people from the same (and other) country who have no problem at all with this feature.
In all cases we have asked for clean installs.
Have anybody ever encountered an issue like this?
(please add me or use reply)
do you mean the Unity Remote 5 app, or do you mean unity remote config? And how specifically are you using it - what do you mean version number check?
remote config -you are right-
We simply store a number in the cloud and we are comparing it with the current running app'a versi version number.
sounds like a firewall problem
you mean the foreg. the Wi Fi they use?
Interesting idea...
their ISP
@low mesa Did you try implementing a ping to your cloud service to see if it is reachable?
Could be many things really, from ad blockers to local network problems to anyone in the middle blocking the signal
With remote config systems you'll always have a % of users that won't be able to reach it for various reasons
not yet, but it's also a good idea.
We have quite some people using/developing the game, and only a few of them had this issue so far. Or at least reported it.
but this also applies to other solutions too, like Playfab or other BaaS right?
Yes
We've got users that can authenticate and load their cloud saves but can't load remote configs for some reason
holy biscuits, marketing team will bite my head off.
It applies to any network usage, you cannot take it for granted that it will work so you need to cater for that
Exactly, even if they seemingly have got internet access there's still so many things that can fail
There can of course still be an issue in your configuration somewhere
config issue should, generally, apply to all users not just a sub-set
Not necessarily? You can have plenty of issues based on device settings that cause connection issues
yes, now my Malaysian partner reports me that the rem. conf. works with VPN.
Does this means it's an ISP problem?
yes, probably
Thank you guys for your insights!
We will try the game with a different ISP / access point and see what we get.
The majority of the game runs on cloud scripts (Azure btw) and this is quite an issue we have to deal with.
Try switching to AWS
yes I had that recommendation too. I just don't like to diversify our tech stack that much. Eventually we will, but eh... lots of hustle...
I've used both Azure and AWS, I find AWS to be much better
We use GCP and Playfab(Azure), both work fine imo
In the far East and China? Because we have experienced very different behaviour
Nope, mostly western countries
We do have a bunch of JP users though and afaik they don't have any issues
Japan uses a different internet backbone than most of the Far East
[ExecuteAlways]
public abstract class Single : MonoBehaviour
{
protected static Single single { get; private set; }
#if UNITY_EDITOR
void Update() {
var copies = FindObjectsOfType<Single>();
if (copies.Length > 1) {
Debug.LogException(new System.Exception($"{this} ON {gameObject} MUST BE SINGLE"));
}
}
#endif
protected virtual void Awake() {
var copies = FindObjectsOfType<Single>();
if (copies.Length > 1) {
Debug.LogException(new System.Exception($"{this} ON {gameObject} MUST BE SINGLE"));
}
else {
single = this;
}
}
}
Have I just created a singleton that works good?
Nope
y
I guess it's fine as long as it doesn't persist between scenes or you'll never load it additively into another scene
But it not persisting (or creating itself) means that you can't access it in all scenes
And thus it is not a true singleton as it'll be null in some scenes
I don't see the use of the Update method
Most singleton issues imo come from switching scenes (and testability but yours sucks there too)
Any new Single uses it's Awake method and should then destroy itself if there's another
So the Update method is just useless
I didn't say to new it
you did
I mean new as in the English word
No, because I would have used a code block
New is a word in the English language
ok got you
Any subsequent instances of Single that appear use their Awake method to destroy themselves
Well, they ought to destroy themselves
But they certainly won't assign themselves
There's this long ass twitter thread about good singletons somewhere
By the friendly neighbor hood @final steeple https://twitter.com/thezombiekiller/status/1488926918712107009
#unitytips Let's talk singletons. Love or hate them, they're an extremely common pattern in Unity gamesโfor good reason. The engine itself even has some built-in! (kinda)
However, I often see developers implement the pattern in very error-prone or overly-complex ways.
(thread)
Their name is a bit of an oxymoron
I see I am most likely misunderstanding Singleton idea. So listen, I have written this script to have a single gameobject in my scene that has the exclusive privilege to have this mono on it. So called unique scripts. One script of mine is Themes.cs that rules the part of UI coloring and I don't ever need more than one of it. Is my code good in such a case?
I mean it's fine
Yeah, there's nothing wrong with that. I usually avoid them, but they make total sense if you are just using a single instance of the class.
we have integrated Unity Remote to our game
you mean Remote Config
is your game otherwise a single player game?
you are reinventing tracing. you can use something like opentracing / jaeger to record spans, which are like profiler samples except they can be arbitrarily long and finished anywhere. then you can load those traces in a variety of tools, like Chrome. otherwise, you can use the Unity Profiler to show you traces (samples) of any duration by creating a dummy thread, and starting and stopping samples on it.
Hi guys! So, I have ran into a issue with my game where I have some gas enemies and I want them (if they are at a certain range) to blend which eachother. How could one write a script amount blending shapes?
My enemies as of right now are simple spheres, so maybe thats easier to work with?
what do you mean by blend? like this?
yeah! something of that sort
these are called metaballs
is there a cheap way to swap buffers in a compute shader ? an equivalent to this c# code
List<int> a = { ... };
List<int> b = { ... };
List<int> temp = b;
b = a;
a = temp;
in the quick and dirty implementation I just readback and swap on the CPU, but now I want to get rid of that step to make it faster
current plan is to use a counter, check for even/odd pass inside the shader, and use conditions to switch between buffers, but if I can change references like in any general language, I'd like to do just that
Something like this?
int val = a[i] * weight + b[i] * (1 - weight);```
So to select buffer a set weight to 1. Buffer b set weight to 0
You can't just swap references afaik
Could be wrong
I like that ๐ but I also need to write to one of the buffers
You can do a similar trick for writing
I'll try, thanks for the cool idea
worked just as I wanted, thanks again ๐
Are you suggesting this in place of the Func<bool>? Iโm not sure I get how that connects to what Iโm trying to do.
@obtuse flume just looked at your post. only thing I would do different is notifications: public override void Execute(GameObject receiver, out Func<bool> finishCheck) rather than return a function the caller wil need to poll- I would have the caller subscribe to an event, then trigger it when the step is done.
(you could do the subscription in that function, the user need only pass the event-subscription function to be called.)
Generic classes. How can I assign a value to single during inheritance?
don't understand "during inheritance" can you clarify?
i mean class : class
I understand the inheritance, not the "during" part. do you mean on startup?
I mean when it happens
what are you trying to do here
when what happens?
ok wait'
inheritance is not a thing that "happens" at a certain time. It's a description of the nature of your types
So you could pass a Func, then call it when finished? I would still need to check every frame for timers and things though, right?
Actually the logic would need updates for actions that happen over time so maybe there will also need to be an Update function in the entries
yes, you sure can pass a function in. You can call it directly, or you can have a member that is a UnityEvent type and "subscribe" to it with MANY functions. They will then ALL be called when the event is triggered/invoked.
timers and stuff-- prolly- depends on your logic/what your doing.
I have to pass an inheritor class to a generic classes single
this makes no sense at all
Do you know what a Type variable is?
object
Ah shit ok that's a generic type parameter
so it's not actually System.Type
this is very confusing
yeah it is T
the fact that you've used the names Single and Type here are incredibly confusing to my C# brain
Those are both builtin C# things.
Unsure where to post this, I like to think optimizing is an advanced topic, anyone know how to optimize these big 3 (excluding the rendering, can't really fix that)
if your not constructing the Single<T> object yourself (your not since it's derived from monobehavior), your only option is to ASSIGN the value. you can do it during Start(), Reset(), or OnEnable() depending on your needs.
is this just supposed to be a Singleton implementation?
kind of but limited, single scene only
ok so again I'm still unsure what the question is here
what did you mean by during inheritance
is this in Awake?
You blurred it all ๐คฃ
[ExecuteAlways]
public abstract class Single<Type> : MonoBehaviour
{
protected static Type single { get; private set; }
#if UNITY_EDITOR
void Update() {
var copies = FindObjectsOfType<Single<Type>>();
if (copies.Length > 1) {
Debug.LogException(new System.Exception($"{this} ON {gameObject} MUST BE SINGLE"));
}
}
#endif
protected virtual void Awake() {
var copies = FindObjectsOfType<Single<Type>>();
if (copies.Length > 1) {
Debug.LogException(new System.Exception($"{this} ON {gameObject} MUST BE SINGLE"));
}
else {
//single = ;
}
}
}
it is on awake
Well I assume it was blurred because earlier they were told it's not optimal
I see you still have the useless Update
unity editor update
Still useless
still thank you for help
definatly want to change "Type" to "T" (Type is a class that exists)
that will also make it clearer where you can/cannot assign a value.. in this class you cannot, because this class does not know what Type T is. you CAN define a specific descendant, that DOES define the type e.g. class Desc: Single<int> in HERE you could assign an int value to the "single" member.
class Desc: Single<float> <- in this class you can assign a float value to "single"
oh wait do you say it has been assigned from the beginning just because my single variable is T and class'es <*> is T too?
[ExecuteAlways]
public abstract class Single<T> : MonoBehaviour
{
protected static T single { get; private set; }
#if UNITY_EDITOR
protected virtual void Update() {
var copies = FindObjectsOfType<Single<T>>();
if (copies.Length > 1) {
Debug.LogException(new System.Exception($"{this} ON {gameObject} MUST BE SINGLE"));
}
}
#endif
}
then I can do just that
(thank you(if it helps))
if this is a monobehavior on a scene objet/pre-fab asset, then yes, you probably filled in that value in the inspector.
i don't understand, is this supposed to be recording things that did happen, or a way to sequence things you want to have happen?
when you have a game built out of a bunch of decoupled stuff ("distributed" by another name), OpenTracing.ISpan is designed to help you record what happened. this has the benefit of working with all your code everywhere
@obtuse flume i misunderstood, you want a way to sequence things
you should use code to do that
you shouldn't design it in the inspector
try to build out the things you need from static async methods first
Can someone DM who is familiar with Photon Fusion?? i need help spawning game objects
My enemies are all based on ScriptableObjects though. If I did it all within code, how would I re-use logic with different values? Ex. one enemy that moves to the left at 5 units per second vs one enemy that moves to the right at 3 units per second. I would still need a way to pass those values in the inspector.
@obtuse flume I like yo use jsons with all my config n.n
@obtuse flume For example:
{
"items": [
{
"id": "smg01",
"name": "SMG 01",
"damage": 20,
"equipment": {
"slot_name": 2,
"player_prefab_path": "SM_Wep_SMG_01 - Player Variant"
}
},
{
"id": "pistol01",
"name": "Pistol 01",
"damage": 10,
"equipment": {
"slot_name": 2,
"player_prefab_path": "SM_Wep_Pistol_01 - Player Variant"
}
}
]
}
seems really similar to ScriptableObjects
It is n.n I recommend the newtonsoft package, it's pretty good
I also use it to save my game
https://gyazo.com/cb029aef8e0111dbca50d91c7a909ddd/ Here is this if it helps you understand what I am doing
Trees. I have too many! Iโm currently grouping treeโs into 400 different โarrangementsโ. The arrangements contain up to X (currently 40) trees, in various layout and quantities. At startup, I dynamically generate a mesh for each of these arrangements, and randomly assign one arrangement to each โtileโ of my map.
I then group together โchunksโ of these arrangements for the various sections of the map. I use a loop to go through each chunk and another loop inside the chunk to display all 400 arrangements using DrawMeshInstanced (arrangements that arenโt used in the chunk are skipped).
As the camera moves around, I change which โchunksโ will be drawn. (tests show this has relatively minimal impact on performance)
Alas, this is STILL too much for my poor GPU to handle- enabling tree display knocks my frame rate down from 70โs-80โs down to 10โs 20โs.
I should also mention all trees are simple tetrahedrons (only 4 tris โ per tree), through obviously the arrangement meshes usually contain more.
The only solution I can dream up is to, at startup, generate a few textures for each arrangement: an image of the arrangement rendered from a few different view angles. Then, if the tile is more than X units from the camera- I display a quad, with the texture, rather than a mesh with many triโs to be rendered. This seems like a lot of work, so wanted to see if anyone had other ideas before I get down into it.
oh.. re-reading this.. I don't need the "bottom" tri for my trees- that knocked em down from 4 to 3 tris- a 25% tri reduction right there.... thanks rubber ducks lol!
You may want to use gpu procedural instancing
you should really do it as
// really this is an "Actor" or "Entity" or "Npc"
public class Enemy : MonoBehaviour {
async UniTask Teleport(Vector2 localTranslation) {...}
async UniTask ShootProjectile(...) {...}
}
// inherit, or compose, doesn't matter
[RequireComponent(typeof(Enemy))]
public class Bat : MonoBehaviour {
public Enemy enemy;
public float pauseDurationSeconds;
public Vector2 localTranslation;
void Start() {
enemy = GetComponent<Enemy();
BatPatrol().Forget();
}
// you can later promote this to a Patrol method,
// that takes args, into your Enemy class
private async UniTask BatPatrol() {
// easy to wire this up with being destroyed,
// external events like getting killed, etc.
while (isActiveAndEnabled) {
await enemy.Teleport(localTranslation);
await UniTask.Delay(TimeSpan.FromSeconds(pauseDurationSeconds));
await enemy.Teleport(-localTranslation);
await UniTask.Delay(TimeSpan.FromSeconds(pauseDurationSeconds));
}
}
}
do you see how the code is a timeline, and also, it already has built in reusability and ways to express stuff like a patrol, that is intuitive, etc. etc.
you mean geometry shader, or somthing else?
@obtuse flume like why reinvent C# in your inspector
Have a look at assets like infinigrass or vegetation studio and their approach to rendering lots of vegetation, a gdc talk on a similar system in HZD https://m.youtube.com/watch?v=wavnKZNSYqU
In this 2018 GDC session, Guerrilla Games' Gilbert Sanders discusses what the studio learned in order to bring the lush and vibrant vegetation of Horizon Zero Dawn to PS4 while keeping an eye on overall performance.
Register for GDC: https://ubm.io/341ZiaZ
Join the GDC mailing list: http://www.gdconf.com/subscribe
Follow GDC on Twitter: https...
I guess because I use the same MonoBehaviour class for every single Enemy. The only thing that changes is their data, and I am using a ScriptableObject to organize the data. I want adding new enemies to be as streamlined and error-proof as possible:
on the contrary
you are reinventing C# in a scriptable object, which is notoriously error prone
and the decoupled-by-default architecture of Unity + Prefabs is pretty much purpose built for platformer / hack and slash adventures like what you are making
I looked at some stuff like these, I thought they wouldn't work because I need the tree arrangements to stay the same (strat game). Can they do that? I'll give em another look.
Yes, itโs actual prefabs that get placed with colliders and all if you need, but data needs and cpu processing are minimal and draw calls are also reduced by instancing per shader/material
it is one of the few places where decoupled (drag and drop a prefab, and the prefab's code figures out its context "locally") really shines
so you should have had a prefab for "Flappy Book" @obtuse flume
instead of a scriptable object
then all that other data would go into a component on that prefab
prefabs and scriptable objects are duals.
@obtuse flume anyway you are in advanced code
you might not be open to making these changes at this point. what i am saying is true, you cannot bend reality and reinvent c# in your inspector window, it will come out poorly
that means you will have to ditch this stuff, eventually, to have a bug free game. or you will have a lot of bugs
i think the async method i wrote is really comprehensible and ismple
much simpler than a scriptable object, and having a timeline data, etc.
however, i think you like that scriptable objects are like, a thing you can sort of fill out
imo you should do the spreadsheet-esque design work separately from unity
a scriptable object, the way you are using it, is to help you think, but it's not a very useful way to engineer a game. does that make sense?
in the same way that outlines help you think, but you do not deliver a short story that is made out of bullet points.
I'm trying to load a png blob from a MySQL server but it keeps giving me a red question mark.
When I used the system.IO to create a png file, it works and the image does load meaning the bytes are correct for png.
The Image is 112 x 112 pixels in size
byte[] img = (byte[])rdr["HeroArt"];
Texture2D tex = new Texture2D(112, 112, TextureFormat.RGBA32, false);
tex.LoadImage(img);
System.IO.File.WriteAllBytes("E:\\Testimage.png", img); //For testing purposes
tex.Apply();
test.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0f, 0f));
there's a method for loading png encoded bytes into a texture. however you should be using UnityWebRequest to load from a file, otherwise you will block the main thread and stutter.
@obtuse flume also consider that i could write maybe 10 more lines of code, to implement Teleport
and i have achieved all the behaviors everywhere, in maybe 1/100th of the space that you have
intellectual space inside our heads, and space in the C# file
Definitely do like that part of it. Also: I believe the reason I wanted to make every enemy have the same structure is to make it friendlier with programming multiplayer (Mirror). When serializing enemies over the network, all I have to pass is the enemy's name and then the receiving client can look up the data and have it all present from the ScriptableObject, instead of having to ensure all the pieces serialize correctly.
Thinking about it now though, I could just have prefabs and achieve the same thing. The one hesitation I have is figuring out how to make sure the prefabs I create have all the required parts. If I want to make a change to all enemies' prefab, how would I prevent needing to go through tens of other prefabs and making the change?
Er, can you elaborate pls? tq
Thinking about it now though, I could just have prefabs and achieve the same thing. The one hesitation I have is figuring out how to make sure the prefabs I create have all the required parts. If I want to make a change to all enemies' prefab, how would I prevent needing to go through tens of other prefabs and making the change?
you would use prefab variants
also, tens of prefabs is not many
prefabs support an inheritance hierarchy that is really useful
you can also still mutli select and edit
lots of ways to skin this cat
all I have to pass is the enemy's name and then the receiving client can look up the data and have it all present from the ScriptableObject, instead of having to ensure all the pieces serialize correctly.
i suppose you would use that string to instantiate a prefab instead.
Yeah exactly.
is to make it friendlier with programming multiplayer (Mirror).
a decoupled architecture game is really hard to make multiplayer
not impossible just hard
you wouldn't synchronize the state of the timeline. there are ways to "synchronize" (serialize and resume later) a c# async method, but that's some Mega Real C# science. you would only synchronize the visible, in world side effects, like the current position, which is a NetworkTransform
if you want to synchronize something logical, you cannot make a decoupled game
since your timeline is C# in disguise, if it is going to do anything more complex than an animation (which you CAN synchronize), you are going to be engineering a distributed database in disguise, which you do not want to do
so for example, if you want a behavior async UniTask WaitUntilPlayerIsNear(float radius)
synchronizing that logically is going to be crazy hard. at that point you should commit to synchronizing transforms, rotations and other side effects
that are like, part of a category we should call "animations"
Ok so here is my plan:
- Make enemies prefabs. I can turn parts of the ScriptableObjects into just a struct or something to be on the Enemy component.
- Behavior components unique to each enemy that have a lot of async methods available to them to utilize. This will take attacks, animations, and in general stuff that varies from enemy to enemy away from the generic Enemy class.
and to make it multiplayer, start with NetworkTransform
focus on, "the stuff that i am going to make synchronized over the network is only stuff that is visible to the player"
so โ health, position and current sprite being shown, but โ progress along this timeline
Yep, all from the enemies that is being sent is "Stats", health, damage, etc. Position, and Animation state
great
and of course projectile spawns
yeah so i think you're in a good place
we're just making a little change to not try to overengineer the timeline
it will be a big distraction
it does look really nice
but everything looks hot so far
it might be too low ROI for you to make this editor panel
@obtuse flume one last thing, are you familiar with TaskCompletionSource?
only took me like 1 hour to do the timeline since i was recycling from this (this may be overengineering too)
this is a way to signal that something happened into an async method
for example
and i think this is essential to understanding why this approach is so good
@obtuse flume let's say we had a complete, working door component
// UniTaskCompletionSource example
class Door : MonoBehaviour {
// this can also be an IObservable... see later
public UniTaskCompletionSource<bool> doorState = new();
private bool m_DoorState;
[SerializeField] private Animator m_Animator;
[SerializeField] private InputActionReference m_OpenAction;
[SerializeField] private float m_InteractionRadius;
[SerializeField] private Collider m_Collider;
public bool isOpen => m_DoorState;
void Start() {
// todo: unsubscribe, but also use UniRx instead
m_OpenAction.action.performed += ctx => {
// or whatever it is, it's not user id but it's something like that
// if the door is too far away when you pressed the door open button,
// don't do anything
var playersDistanceFromDoor =
(GameController.instance.players[ctx.userId].transform.position
- transform.position).magnitude;
if (playersDistanceFromDoor > m_InteractionRadius) { return; }
Toggle();
};
}
void Toggle() {
m_DoorState = !m_DoorState;
m_Animator.SetBool("Door State", m_DoorState);
m_Collider.enabled = !m_DoorState;
doorState.TrySetResult(m_DoorState);
}
}
observe how simple this is.
and it pretty much does everything robustly in a decoupled way
now lets make an enemy that waits until the door is opened before it does something
class SleepingSkeleton : MonoBehaviour {
...
[SerializeField] private Door m_Door;
private async UniTask SleepingSkeletonBehavior() {
// wait for the door to open
var isOpen = m_Door.isOpen;
while (!isOpen) {
isOpen = await m_Door.doorState;
}
...
}
}
it would be even more succinct to use UniRx reactive properties ofr something like this, which can be awaited
but i don't wanna introduce too much stuff at once
the reason i'm talking about it is because the english language description of the behavior of the enemy will look almost exactly like your little async methods
once you start using these three things - unitask, unirx and async/await generally - you can also solve bugs by thinking in english, instead of updateloopese
for example, i knew i had to deal with the door possibly being already open
and what if the player is already inside the room with the skeleton?
you can UniTask.WhenAll multiple conditions
it's very powerful, more powerful than a timeline
or a behavior tree. it looks justl ike English!
@obtuse flume is this helpful?
What is the benefit of using whatever this UniTaskCompletionSource<bool> instead of just having an actual bool? Basically it allows you to wait for it to be open in another async task, instead of having an update loop that checks for a change?
once you decide you want this WaitUntilDoorOpens behaviour everywhere
exactly
which you can't do stuff every frame that isn't rendering in a multiplayer game
also, it doesn't look like english
Yea that does sound more like english. Like
While the door isn't open, await for it to be open
instead of: if door is open and it wasn't last open do this
yeah
you can still wait until a condition is true. that's fine. but it's not something you can do in multiplayer anyway
you might "miss" something
so the diff between this and a coroutine is that this would allow tasks to interact with eachother's progress?
it's like using Input.GetMouseButtonDown
yes, they are overall easier to control too
you owuld use an event via unirx for something like this
Wait so UniTaskCompletionSource isn't in base unity? What is the diff between that an TaskCompletionSource?
@obtuse flume UniTask's stuff works
it's from the UniTask package
you don't want to use plain async in unity
too warty
Hello!
It's kinda, but all process runs on an azure server (like mission rewards, inventory and such)
And the Unity Remote Config is there for checking the version number. So we can force the update for the app. Not the nicest way to do it, but it's okay for now.
Some ISP as I can see, are having problem with this solution (unity remote config)
i would ticket it to unity
good luck out there
@undone coral got the enemies restructured, I just have this one problem:
Is there a way to cancel an async task when the enemy is destroyed?
protected async UniTask FollowForSeconds(Transform followTransform, float speed, float seconds)
{
var doneTime = Time.time + seconds;
while (Time.time < doneTime)
{
Rb2d.velocity = (followTransform.transform.position - transform.position).normalized * speed;
await UniTask.WaitForFixedUpdate();
}
}
You can pass a cancellation token and call .Cancel on it once the enemy is destroyed
Thanks I got it working with that
cool. there's also this.GetCancellationTokenOnDestroy()
if you want the unitask helper for it
how do you feel? big improvement?
i like that the way it is written makes PERFECT SENSE
and i can comprehend that FollowForSeconds does EXACTLY what it says
This is also built in for versions after 2022.2
But I feel like coroutines still have their place with cancellation being such a pain
that's true. really unitask should support just setting it with a flag
What do you mean?
like the custom async executor that he uses
it could just check if the object is destroyed if you configured the task to do so
on every await
Not ideal from a performance aspect
for tasks started from an object. i actually odn't know how to get the context of the object whose Start / Update / etc is currently being called
from some static or whatever. not sure if unity sets that. but it would not be that hard to do
well you are awaiting anyway, presumable it is a bonafide async thing you are awaiting
Could just be another function returning Task ๐คท
yes. "don't do that." but yeah, that happens
@compact ingot look at @obtuse flume he's doing it. he's doing the async game logic thing and it looks great.
Why shouldn't you do it?
you definitely can, it is probably the only option for things that are meant to be overloaded. but if it's stuff like this, if you know it's a regular function, make the simplest signature that it can have
since you control all the source
if you need to promote it to be async, you can always refactor that later
Guess it depends on how your game works. Lots of things can be async
Agree
Yea removing the SOs makes enemies much more intuitive and the behavior system is so far much more expandable and less messy
Do you plan to just duplicate the prefab then if you need enemies with e.g. different health?
ugh that is so good to hear
I am doing Prefab Variants, which makes it so when I change something on the base "Enemy" prefab it makes the change on all the prefabs unless it is overriden by me.
And then yeah I just change what needs changing
Alright, I was just wondering how you were planning to store your different entity types then
@obtuse flume okay i don't wanna push my luck but here's one more thing...
Now instead of EnemyData being a ScriptableObject is is just a serializable class that is part of the Enemy component. I was able to remove a bunch of fields in the process. (It stores stats, name, money award, etc.)
using UniRx;
class Actor {
[SerializeField] IntReactiveProperty m_Health = new();
public IReadOnlyReactiveProperty<int> health => m_Health;
private Subject<Actor> m_SentToGraveyardBy = new();
public IObservable<Actor> OnSentToGraveyardBy() => m_SentToGraveyard;
...
public void DamageThis(Actor source, int amount) {
m_Health.Value -= amount;
if (m_Health.Value < 0) {
m_SentToGraveyardBy.OnNext(source);
}
}
...
}
[RequireComponent(typeof(Actor))]
class Boss : MonoBehaviour {
public Actor actor;
void Start() {
BossBehavior().Forget();
}
public async UniTask BossBehavior() {
await actor.health.Where(hp < 20).First();
// todo: what about overkill? it's legible if you
// deal with the bug here.
await TransformIntoForm2();
await actor.health.Where(hp < 3).First();
await TransformIntoForm3();
var whoKilledMe = await actor.OnSentToGraveyardBy().First();
whoKilledMe.GetComponent<PlayerInventory>().AddGold(100);
}
}
@obtuse flume
"oh i have to deal with overkill"
What exactly is a Subject? I've never seen that.
it's like a little event message box
So this waits for the Actor that kills the boss then awards them gold.
So obviously for overkill you wouldn't want it to go through transformation steps
class Actor /*...*/ {
...
// whenever you subscribe to this,
// a behaviour subject REPLAYS all the events
// that were OnNext'd through it
private BehaviourSubject<Actor> m_SentToGraveyardBy = new();
}
[RequireComponent(typeof(Actor))]
class Boss : MonoBehaviour {
public Actor actor;
void Start() {
BossBehavior().Forget();
}
public async UniTask BossBehavior() {
await foreach (var health in actor.health.DistinctUntilChanged()) {
if (3 < health && health <= 20) {
await TransformIntoForm1();
} else if (0 < health <= 3) {
await TransformIntoForm2();
} else if (health < 0) {
var whoKilledMe = await actor.OnSentToGraveyardBy().First();
whoKilledMe.GetComponent<PlayerInventory>().AddGold(100);
break;
}
}
}
}
What does the foreach do? Loop every time health is changed?
yep. it will wait until the health is changed, and when it has, sets health to that value and executes the loop
there are many ways to skin this cat, but what's essential is you can sort of make this look like something reactive sometimes, and sometimes you canb make it linear. you can be creative
and it's very expressive
Hard to read for me, I've never heard of half of these things
okay yeah this is a much tougher one
you don't have to go this far
...yet
but you are already equipped to do it
like when you want to make things more reactive / wait for events
unirx is useful
This feels like an indoctrination to reactive programming ๐
you might have seen in the unitask docs already await button.OnClickAsync()
there's a way to do that a lot of different ways
maybe it's a door that every player has to click on to open.
or you have to kill 10 enemies
or whatever
you don't want to make a KilledTenEnemies taskcompletionsource. maybe you just want a counter of the number of enemies killed, and then Where it.
anyway you see some stuff there
you can deal with the overkill a lot of ways, but this was just an example
@obtuse flume here it is without await foreach:
class Actor /*...*/ {
...
// whenever you subscribe to this,
// a behaviour subject REPLAYS all the events
// that were OnNext'd through it
private BehaviourSubject<Actor> m_SentToGraveyardBy = new();
}
[RequireComponent(typeof(Actor))]
class Boss : MonoBehaviour {
public Actor actor;
void Start() {
BossBehavior().Forget();
}
public async UniTask BossBehavior() {
while (true) {
var health = actor.health.Value;
if (3 < health && health <= 20) {
await TransformIntoForm1();
} else if (0 < health <= 3) {
await TransformIntoForm2();
} else if (health < 0) {
var whoKilledMe = await actor.OnSentToGraveyardBy().First();
whoKilledMe.GetComponent<PlayerInventory>().AddGold(100);
break;
}
// wait until the health has changed.
// lots of ways to skin this cat
await actor.health.Where(x => x != health).First();
}
}
}
elsewhere you might have a script that animates the helath specially
class ActorHealthAnimator : MonoBehaviour {
public Actor actor;
public TMP_Text healthText;
void Start() {
actor.health
// whenever the health changes to a new value
.DistinctUntilChanged()
.Subscribe(async health => {
if (healthText) {
healthText.text = $"{health}";
await healthText.DOShake(...);
}
})
// no more OnDestroy() { event -= handler; } !!!
.AddTo(this);
}
}
This syntax is so unusual ๐ต
@obtuse flume for stuff that is just rendering side effects, like a little text shake animation, the reactive thing is really useful. it gets rid of the updateloopese and the cantankerousness of event += handler, event -= handler;
it is but it lets you use linq
which can be a pretty nice way to express stuff
for decoupled rendering i use unirx / reactive code a lot. very similar to modern web dev
however, you can also put the shake on the text label directly into the actor code. really depends - what if the health label is somewhere else? might want to assign a health label that's special for the player, and omit it sometimes for enemies with 1hp, etc.
ok hold on let me make sense of these new things:
okay, and then i want to show you how you can combine these two things to robustly solve a seemingly really complicated problem. the example i'll work through is
when an actor dies, i potentially want to process a bunch of async stuff, like animations but also spawning other enemies, before i play the death animation, and then destroy it for real
how do i set up those effects in a decoupled way? i want to be able to drag a "Deathrattle" component onto a prefab, and then it Just Works.
hmm actually it doesn't striclty need to be reactive
to do that
it is easy to understand and achieve with async alone
really tough to come up with a complex example.
reactive stuff is really for rendering
if you were making a card game or some kind of turn based game, where you need to have events from the network (which come instantaneously) splay out into animations (which last a long time), it's super useful
like if you need to render data, but delayed.
@misty glade has some insight into that. he made a multiplayer game that's pretty sick, and the next one will use techniques like these to make things easier to manage
ReactivePropertyjust holds a value and you can asynchronously check its value with LINQ. Parallel toyield return new WaitUntil(abc == xyz)IObservable<Type>returns enumerable(?) values of Type that have beenOnNexted to it.Subject<Type>is used to make this work. UsingBehaviourSubjectinstead makes it so you can access the history of the values that have been "pushed" to it in order.
"deathrattle" (do something when the actor dies) looks like
class Actor : MonoBehaviour {
async UniTask TakeDamage(Actor source, int amount) {
health.Value -= amount;
if (health.Value < 0) {
var deathrattles = GetComponents<IDeathrattle>();
foreach (var deathrattle in deathrattles) {
await deathrattle
.OnMovingToGraveyard(source, amount);
}
Destroy(this);
}
}
}
class DeathAnimation : MonoBehavior, IDeathrattle {
override async UniTask OnMovingToGraveyard(Actor killedBy, int amount) {
// shake and spawn a tombstone sprite
await this.DOShake(...);
tombstoneSprite.SetActive(true);
await tombstoneSprite...
}
}