#archived-code-advanced
1 messages · Page 155 of 1
i dunno where this belongs on the code difficulty scale but.
i need to display some "status cards" in a grid, based on what status the player has accrued. (poisoned, flying, injured, etc).
a status card consists of some tooltip text, and a sprite.
the grid starts empty, but i need to add and remove such a card from the grid at any point
my question is, i suppose, what's the best infrastructure to set this up? will i need a scriptable object for each possible status? how do i reference those dynamically to instantiate them into the grid in a way to easily create and apply new statuses as i develop?
depending on how they are used, i might just have a hashset of status enums somewhere that tracks 'active statuses', and in your UI you read it and display the appropriate icons (which would be a prefab added to a parent with a grid layout group, most likely)
if you want to use SOs you can do that too though, same sort of idea
i already am keeping track of a few statuses as enums, i do just need a way to associate a status with a sprite, and have some sort of reference to all existing status associations to instantiate them into the grid.
i suppose im having trouble nailing down the specifics of that setup, as i'd prefer a flexible solution that involves as little boilerplate set up for each time i want to add a new status the game might use.
Well it depends if you want to mix the presentation with the logic. If you don't mind that, I would say just make an SO for each status instead of an enum and include presentation details there (like the sprite, display name, etc). Then you can have another SO which holds the 'currently available statuses' and put whichever ones you want active in the game into there, and assign that reference to whatever needs access to the statuses.
If you want to keep presentation and logic split, I would keep the statuses as enums and just make a 'StatusDisplay' class, and then make an SO with a dictionary of status->StatusDisplay which you reference in your view component. Unity doesn't serialize dictionaries out of the box so that approach is easier if you have Odin, but you can work around that limitation if you want to.
I tend to like the second approach, but I also use Odin which makes it pretty simple. One advantage there is that if you want to sometimes display statuses differently (like maybe there's a 'status glossary' which uses fancier sprites and has a more in depth description). Associating the view data with the status directly can get confusing if you have multiple similar presentations of the same value
no odin for me, but i ought to look into it.
having a dictionary of statuses isn't too bad, i think. though my experience is limited, so lmk if my brainstorming is flawed.
a scriptable object (statuses?) that hold a number of statuses (say a serializable status class) which in there I can hold references to each statuses individual sprites and information?
something like (pseudo code)
public class Statuses: ScriptableObject{
public Status[] status;
}
...
[Serializable]
public class Status{
public string name;
public string desc;
public Sprite sprite;
}
yep, that would be the first approach i proposed
my thought here is that in the inspector i can just add a new entry into the statuses and apply their things once, then reference them elsewhere.
right, and to add a new status you jut have to create a new SO instance and add it to the list
which fits you 'minimal boilerplate' goal
for the status list, I have a class like:
public class ConfigSet<T> : ScriptableObject {
public T[] items;
}
and then i extend that to create lists of SOs which i can plug and play, cause this pattern ends up being really common
also it has some helpers on it like GetRandom
hm, alright, awesome, that's super useful
and to reference a particular status, im gonna simply need to have a reference to that Statuses SO on my player, right?
i donno what your game is, but that seems reasonable
one of the nice things about SOs is that you can reference them from a few places if you need to, just don't go too crazy because they are sorta 'globals'
but if you had the player who needs to know about them, and then a ui panel or something, they could just both reference your Statuses list and that's handy
for sure for sure.
it's a card game, and each card can have certain statuses applied to them. i'm already using SO's to generate card information and visuals and instantiate multiple cards based off of a single card template prefab, so it's pretty cool that all i need to do is just add one reference to the template's script
Ah, well I would expect statuses in a card game to potentially have very different effects, so I wouldn't try to encapsulate them. I'd probably have some systems whose job it is to handle the effects for whatever status and keep the statuses a 'dumb' enums. In that case, you might not actually need a list of 'all enums' referenced anywhere, because each system would only care about the one(s) it needs to interact with (and can just check if they are active on the player and do nothing if not)
depends on the game and what sort of stuff is happening though
and that approach does require more boilerplate because each status is handled explicitly (but in my experience, that's actually what I want)
so your damage system is actually what would query and apply logic for the 'double damage' status
the advantage there is that you keep all of your 'damage logic' in one place, but it can get complex if you have a lot of different statuses
still, having that complexity be explicit is better than hiding it in a bunch of objects with OnApply or whatever (imo)
yeah; im probably currently doing it pretty suboptimally, but for the one status i have built up it's simply an enum (flying or grounded), and im already using that enum elsewhere to determine what happens if they get attacked depending on which effect is applied to them
i just wanted a way to link that to a visual for the player, but also i can definitely see it getting more out of hand as i scale the mechanics, as it becomes a case of checking whether or not a status is applied and applying logic based on that for each status
yeah, it depends on how many statuses you will have, how similar their effects are, how many mechanics your game has that they can interact with, etc
but honestly that sounds fine to me...if you have a lot of statuses and they might be interacting with each other because they all affect 'damage', you might want to force yourself to make all of that explicit and in one place
so if you already have that in place and just want to map the statuses to visuals, the dictionary approach sounds good to me
there are definitely free extensions that will serialize it for you without odin but i can't speak to them
i feel like i'm leading you in circles 😛
lmao
it's fine, i definitely got a good starting point to at least try
im relatively new to the whole unity thing, so i don't mind doing things myself until i'm comfortable enough to use something like odin to just save time
that was how i felt for a few years, but once I finally did buy it I wished I had much earlier
especially for a card game where you want to be able to 'configure' complex cards, i can't imagine building tooling for that in the editor without odin's ability to serialize interfaces
well i can imagine it, it just sounds like a big waste of time
But yeah, it's always good to run into the problems that tools solve before you reach for them. Just know that odin is dope and will be there for you 😄
haha, fair enough. i'll give it a look sooner rather than later. currently there isn't too much complexity that i've needed any third party editor tools, but i know that can change quick
odin: noted ✅
e.g. in the card game i'm working on, my cards are just an array of ICardComponents which odin kindly serializes for me:
you can do that in unity now with [SerializeReference] but I'm not sure what gotchas it comes with
ooh, actually... i think i need something for something similar.
i have a lot of card components in cards that don't need them and it's gonna be a mess when i expand it further
yeah, eventually you end up with a lot of 'potential complexity' and it's hard to tell how much is active on any given card
like, i have a card ability that heals; i don't need components in there to tell me how it affects the card's movement status
or what sort of damage it does
healing damage
yeah, and it's a similar thing for statuses imo
you could try to encapsulate them and have all the possible effects on each status, but I think this way is simpler
i spent a bunch of time trying to work around the default serialization ages ago, so doing this is great
that said, it's still useful to split your 'editor config' from your 'runtime' config, so you can kinda do both
like the 'card editor' might want to have more context for each possible thing that a card can do and show/hide sections, basically just be smarter than 'dropdown with every option in it'
but ultimately i do like the runtime version being 'pure data component' based for something like this
hm, yeah, i get it a bit better now. definitely sounds useful
i've been trying to simplify it with editor scripts but it wasn't worth the effort.
Totally. For me Odin bridges that gap where I wish the editor could handle more, but not enough for it to be worth setting up anything custom. And then when you do want something custom, you can use the odin pieces to whip it up really quick.
I swear I'm not an Odin salesperson 😅
well you're selling me on it!
well apparently it's half off right now...
oh shoot
ey you got 20 days!
what a bargain
hopefully enough to get my paycheck in but it's a pretty good deal anyhow
i took a slightly different approach, but what you guys suggested definitely helped me find new solutions and ideas. I was pretty stuck on this, so thanks!
don't mind the layer mask, i put it to "water" for testing purposes because i didn't know how to add other layer masks immediately
i did make use of the layer mask as you suggested!
looks good
there must be dozens of people making card games
the best thing, in this exact case and nothing else, is to do it in code with a giant switch statement
because expressing string formatting is easiest in code
you should have a Tooltip class with a static string[] Tooltips(Entity entity) { ... } method that generates all the tooltips
trust me, i thought about this a lot, and in my own card game, this was the best way
everything else you could do, you'll wind up reinventing string formatting
specifically the part where it's easy in code to just... get a thing
whereas in a string field, it's hard
you'll do a lot of
if (entity.HasStatus(Status.BLAH)) {
tooltips.Add($"You are suffering from {entity.GetValue(Values.BLAH)} blah"))
}
you don't want to create a DSL for entity.GetValue or whatever
that's just reinventing C#
don't do that
"what if i have a sprite icon i want to show" return it in the string in a way that's compatible with textmesh pro
all this stuff you want to do with text, do it in code
my hot take 🎤
I actually do have a tooltip class/system that I was using for something else, and I was able to use it for this as well.
Though I worry more about my logic system, cause it’s going to get really messy if I don’t learn how to split things up better.
i don't think you'll earn anything by "splitting" this up
there is exactly one UI element that needs the tooltip information
that's some tooltip element
you can make a single static method that deals with generating the tooltips for anything
everything else, you'll just wind up repeating yourself for no gain
there's no reason to have a subclass of tooltip, and then make singleton tooltip instances, etc. etc.
it's just a ton of ceremony for no gain
you don't want the card DSL to be aware of how it affects the tooltips
you don't want to encode that there
it's too unwieldy
just put it all in one place
a lot of card games have way too many tooltips
because people try to be so pure about it
you will inevitably want to customize the tooltips everywhere they appear, so a generic method won't do much for you
Yeah I’m using a static tooltip class courtesy of a great YouTube vid I found. It’s been useful thus far.
okay great
unless your game needs to support card design at runtime
it's almost always better to have just encoded card behavior as C# code the way XMage does
The only card design at runtime I do is setting the sprite and a few details, based on which card is drawn. I’ve one card prefab template and I use SO details on instantiating to fill out the details.
Maybe not perfect but it’s given me the least headache once I set it up
i would suggest something of the form
class Fireball : AbstractBaseCard {
override IEnumerator Play() {
var selectTarget = new SelectTarget(TargetSelection.ALL);
// yields when the player
// selects the target
yield return selectTarget;
var target = selectTarget.target;
// plays the damage animation
// deals the damage
// it's a coroutine
yield return Game.instance.DealDamage(5, target);
}
}
@polar ermine
Thanks! Saved that for reference. Currently I’m making individual ‘actions’ for any given card, and the attack method reads the action parameter and uses the necessary variables like damage and damage type. An extensible class would be better
it's just much easier to express what's going on using code
because card games are already algorithmic
Very true. Literally everything beyond the chosen course of action is behind the scenes calculations lol.
The same could be said about a lot but eh
how does that interact with stuff like a status which boosts player damage by 50%?
maybe that question doesn't really make sense with how e.g. magic models everything
where that would be an artifact which adds +x to cards instead, probably? does magic do any relative calculations?
IEnumerator DealDamage(int baseDamage, Entity target) {
var damageBonuses = this.allCardsInPlay.SelectMany(entity => entity.Enchantments).OfType<AttributeModifierEnchantment>().Sum(modifier => modifier.Value(this));
var totalDamage = baseDamage + damageBonuses;
target.hp -= totalDamage;
yield return StartCoroutine(DamageAnimation(...));
if (target.hp < 0) {
MarkAsDestroyed(target);
}
}
what if i want to show the player ahead of time how much damage a card will do?
i guess you can have a CalculateDamage method and call that and pass that result in
yeah for everything that you need
like, just write the code to do it
don't make a meta coding blah blah lisp system thing UNLESS you need an editor the end user will interact with
because a normal person can't write C#
that isn't really what i would do instead
different solutions for different contexts
'just write the code' isn't really architecture though 😛
where i write the code matters!
lol
i mostly agree with you
how do you get asset bundle load from file to work?
so I have a folder of asset bundles set up and ready to go
and the examples code from here:
https://docs.unity3d.com/Manual/AssetBundles-Workflow.html
to load in asset bundles
I can confirm the asset bundles are in the app.
is this because im running the project in the editor and need to use a different directory than the streaming assets directory shown in the example?
Hey guys, I have a quick question. I'm writing an editor script. When I instantiate an object it loses its prefab reference. As you can see in the photo, if I create an object in the scene by drag and drop from prefab to the scene, the object's color is blue and it still inherits from the prefab. But when I instantiate it via script, it becomes "A clone". Is there a way to instantiate the object and not lose the reference of its prefab.
I need this because im using PrefabUtility.GetCorrespondingObjectFromSource(TObject componentOrGameObject)
https://docs.unity3d.com/ScriptReference/PrefabUtility.InstantiatePrefab.html should do it, right?
thanks it does
idk how I miss that
what happens if an axis positional float of an object over/underflows in unity?
will the game crash? simply reposition the object at the other end of the line?
I would presume it just goes to positive/negative Infinity and prints warnings to the console if associated with a renderer. 🌈 Check it yourself 🌈
In a checked environment (outside of an unchecked block), it would simply throw an OverflowException. At least, that's what it does for int values, so it should logically be the same for float
Update, it doesn't, it just goes to (negative) infinity
What do you mean by "not working"?
Reminder that this is #archived-code-advanced, where debugging and more information than this is expected
I could not find any similar issues on the internet, and the Docs for ClosestPoint does not warn about issues on TilemapCollider2D.
The page specifies that it will work with all types deriving from Collider2D
Update, TilemapCollider2D indeed inherits from Collider2D
When I use the mouse position in the parameter it gives me a position on the corner of the entire tilemap
Check the collider's bounds, debug the positions.
Make sure you're converting mouse position (screen space) to world space correctly using Camera:ScreenToWorldPoint
An alternative would be calling WorldToCell on the tilemap, to get a specific cell from a specific world position
Update to this part: #archived-code-advanced message
I've got it working to end on a 0 in the wavestream
Where this is the code that I use
yield return new WaitUntil(() => Master.time / ((int) (44000 / notes[btn].Frequency) / 44000f / (int) (44000 / notes[btn].Frequency)) == 0f );
@austere jewel @fresh salmon aight thanks alot :D
any suggestion on how to prevent players from overflowing their coords?
in an open space
using death zones?
You'll start getting noticeable float precision issues around 100k, where stuff would start to violently vibrate as you move, so if your map is larger than 200 km long, death zones
ive actually noticed this behavior in very old games too ya, where the player model comprised of multiple 3D objects started to vibrate and dislocate
so this is due to float rounding errors?
Yes
kk
That is the playback time in seconds of the currently playing Audioclip.
Master.time
This is the amount of samples in any given Audioclip.
(int) (44000 / notes[btn].Frequency)
This is the frequency of the samples.
44000f
As you can see, I'm calculating the time in seconds it takes to play 1 sample of the AudioClip. So in theory this should stop as soon as 1 sample has been played.
The problem is that
yield return new WaitUntil()
is apparently too slow to accurately check that, since it's frame depended. My Unity does not run at 44000 fps, though I wish it would 😀
Any suggestions how to solve this problem?
You can try it for yourself,
Debug.Log(1000000f + 0.1f)
You'll get 1000000 in the console because it can't handle it
that sounds viable too
but would have to work with relative coords then right where everything is dependent on the center
ah
yea ive heared about that issue with floats
so at 1m it cant actually calculate floats anymore
1b*
due to the 32bit limit
Isn't there an equivalent to float as there is BigInt?
halfdouble
That's where the name comes from.
At larger values it doesn't have enough bits to store the integer part anymore, so it moves the decimal point to the right to allocate more bits to the integer part, in term reducing the bit for the decimal part -> precision issues ensues.
Float = floating point, the decimal point moves left or right according to the needed precision
Double is more precise than float and can store 64 bits First google result when searching Double or Float
And yes, float is 32 bits, double 64
whats a big int
In C# it's long, a 64 bit integer
ah long
Yeah
There is a BigInteger in System.Numerics
also i think the biggest primitive C supports is 256bit
I like the naming shenanigans there, "You want a bigger int? BigInt it is. You want a 16-bit int? SmallInt it is"
Oh yeah C is weird, you can do long long as a data type I think
unsigned long long int
Oh long johnson
tho idk the c# equivalent for that
There isn't one
🤷♀️
Usually long is sufficient enough
true
Anyway, does anyone know a frame independent alternative to WaitUntil() ?
see here #archived-code-advanced message
I think you're limited by Unity here, as everything is synced to the framerate, or the fixed time step, nowhere near fast enough to achieve what you want
You can't execute something faster than your program can run
im not really sure what you want to achieve
do you want to measure the elapsed time?
And 1 / 44000 is quite fast
The problem is that I want to stop the playback of an AudioClip at any given time. But stopping an AudioClip when it's not on a 0 sample will result in an audible "pop" sound
Which format are you using?
AudioClips generated at runtime
so uncompressed wave?
I actually don't know what format they are
//Generate Sine
for (int j = 0; j < notes.Length; j++)
{
float[] samples = new float[(int) (44000 / notes[j].Frequency)];
for (int i = 0; i < samples.Length; i++)
samples[i] = Mathf.Sin(Mathf.PI * 2 * i * notes[j].Frequency / 44000);
AudioClip ac = AudioClip.Create(string.Format("Sine {0}", notes[j].Name), samples.Length, 1, 44000, false);
ac.SetData(samples, 0);
sineClips.Add(ac);
}
You can sample the timing and stop before that happens earlier in execution order probably instead.
Could you elaborate?
OS based timers do not have the precision to stop at 1/44000th intervals
basically you'll have to work with your clock speed
you cant realistically measure samples/time faster than you cycle trough your lines
I mean test it before it plays.
only to use better hardware/write better code is what you can do
Tbh, a quick fade out would probably work just fine
but you dont need to either
Use any of the entry points before it happens https://docs.unity3d.com/Manual/ExecutionOrder.html
human reaction time is about 15-30ms
Or when you pause, wait for the next "0 sample" as you call them, and stop there
Hrmm. General question to understand the timing between operations.
var test = 1;
test++;
Debug.Log(test);
How long does it take to execute this code?
Three instructions in IL, with Debug.Log the most expensive one
So is it tied to the clock of the CPU
Yes
for example u have 1 core
The exact timing cannot be precisely determined, as it depends on your CPU
But something like
yield return null;
Is tied to the framerate?
this core now executes the OS, discord, steam, 300gb of internet services and your program
compared to only OS and ur program
the second runs faster
I'm not sure how all of that is related tbh
he asked how to determine the speed at which instructions are executed
Plus, CPU speed vary now, it can throttle down if it has nothing consequent to do
I see
also if the cpu overheats itll turn its clock down
so there is neither a way to determine how fast your program runs
So even when the timing may be accurate on my PC, it might not be accurate on other peoples PC.
@shell grove All you need to know is everything happens in one frame, and what you can manage is when in the frame it will happen.
The main thing is, you'll never get a program to run as fast as your CPU clock, as most C# calls result in 1+ instructions
So it would be better to make every timing frame-dependent to ensure accurate timing regardless of hardware limitations?
So 1+ clock cycles
what you can do to improve performance for time critical tasks is to start a new thread on a new physical core and dedicate this thread to that very one task
make it async
and communicate with the main thread via an interface
Yes
frame independent that is
that or start a new thread in a while true
But I thought frame independent meant that it's up to the hardware
but keep in mind that incorrect thread handling might actually turn your processing speed down
unity, c# and c++ based dlls all can offer multithreading solutions if youd want that
You can't know at which frame rate different hardware will run the program. You can force it though to lower rate.
But it's not recommended
Only universal factor is timing
I think Timinator said that deltaTime could work
you basically just store the time
add the elapsed time to that each frame or tick
and compare it to your wanted threshold
if it exeeds you execute your whatever and reset the timer
What exactly do you want to do?
var i = time.deltaTime;
yield return null;
Debug.Log(time.deltaTime - i);
I guess this would give me the time it took for that 1 frame to pass with yield return null;
that just measures the difference in time between 2 relative time points, in this scenario between frames
That's what deltaTime is, here.
yield return null waits for the next frame, so you already have the time in deltaTime. No need for those calculations
just speaking generally btw, unity caps the fps and thus game ticks to the monitors refresh rate right?
Only if you tell it to, or have vsync enabled
I thought about an Idea, tho it may be a stupid one
Instead of
yield return new WaitUntil(() => Master.time / ((int) (44000 / notes[btn].Frequency) / 44000f / (int) (44000 / notes[btn].Frequency)) == 0f );
I could do
while(Master.time / ((int) (44000 / notes[btn].Frequency) / 44000f / (int) (44000 / notes[btn].Frequency)) != 0f)
{}
i spent a great deal of time
asking myself
how do i prevent cheaters from ramping up their clock speed to cheat speed walking
the simple answer is delta timing
Tho I think that would freeze the program
but this applies universally
depends, cheat engine can do that
Huh?
super old games suffer from this vulnerability
it seems incredibly accurate only losing maybe 1-2ms
Sadly not, most of the time the samples take 2-3 seconds to stop
Much more, like 16ms at 60fps
Because WaitUntil is tied to framerate, and is of course, variable
also id refrain from while loops in the main thread of unity
it freezes your entire game
Basically every 16ms we got a shot that maybe we are on a 0 sample, if not we wait again
16ms in 44000hz is quite the amount of samples
then multithread it
and let the thread run async
only way i see in the unity env to get it run faster
I never multithreaded
And I don't think it would be appropriate here
and both unity and .NET offer solutions
You can sample play speed if it's not constant, you cant get current clip time with https://docs.unity3d.com/ScriptReference/AudioSource-time.html And stop it if it's less than sample per frame speed left, or several frames before if you need to.
What's actually a problem
then the approach of measuring time seems incorrect
cuz i at least cant think of a way to measure the time faster(at higher frequency)
Even if they multithreaded, it would be far too fast compared to the 44kHz rate the sound imposes
well and u have to consider weaker hardware too
welp i havent done much with audio playing yet so im mostly out then
yo guys, i wanna ask something, so i make a basketball game then the player can throws the ball when pressed left mouse, but the problem is when i click pause button the player can still throws the ball, so how can i fixed that?
Is that question really suited for that channel, or did you post here because it's active?
Prioritize relevance over activity, folks
if(paused)
return;
Add that in your ThrowBall function, or any equivalence to that.
I think I will try the FadeOut Solution since getting really accurate timings when dealing with this kind of frequency seems near impossible
Or, big brain moment
I could add a few 0 samples between each cycle of the sine. That way the WaitUntil could maybe catch it better?
Anyway big thanks for the help Timinator, SPR2 and Fogsight 👍
Is it possible to write a property drawer for a custom list class (which would basically just be a wrapper for list<T>) that draws the serialized fields for the most derived type of T? Perhaps through reflection?
I sadly have no clue what a property drawer is Hambones. But I think some will know 👍 Good luck on your project
thanks it works, this logic is pretty simple.
Basically I want a polymorphic container that allows you to edit derived types of T in the inspector. I think this is possible at least through reflection, possibly even not, but I’m not sure.
I think that in order to actually allow selection of a subtype in the inspector, reflection would be needed…
ive seen many talk about the old and new input system of unity
whats the actual technical difference?
Basically the new system provides the concept of actions, which lets you separate obtaining inputs from employing them.
oh btw Timinator, I'm limited to C#4 and Unity 2017
Because I'm modding for a game that runs on that version. Dunno if Multithreading would even work there
Multi threading is very old
Hey anyone familiar with an issue where i cant make any kind of webrequest without a WEBGL build crashing on me
Maximum call stack size exceeded
using LitJson;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class SaveFile : MonoBehaviour
{
[Tooltip("Details from where to get the personalID")]
public LoginDetails Login;
public DataTable Data;
[SerializeField] private string JSONLink;
public JsonData jsonvale;
public void Start()
{
Init();
}
public void Init()
{
StartCoroutine(Getlink());
GetAchievements();
}
public IEnumerator Getlink()
{
UnityWebRequest client = UnityWebRequest.Get(Login.URL);
client.SetRequestHeader("AUTHORIZATION", Login.Authenticate());
yield return client.SendWebRequest();
if (client.result == UnityWebRequest.Result.Success)
{
JSONLink = client.downloadHandler.text;
Debug.Log(JSONLink);
}
else
{
Debug.Log("Could not make a connection");
yield return null;
}
if (JSONLink.Length > 0)
{
jsonvale = JsonMapper.ToObject(JSONLink);
Data.isLoggedIn = (bool)jsonvale["loggedIn"];
}
}
public void GetAchievements()
{
if (Data.isLoggedIn)
{
Debug.Log("Nice");
}
}
}
[System.Serializable]
public class DataTable
{
public bool isLoggedIn;
}
[System.Serializable]
public class LoginDetails
{
public string URL;
public string UserName;
public string PassWord;
public string Authenticate()
{
string auth = UserName + ":" + PassWord;
auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
auth = "Basic " + auth;
return auth;
}
}
If you reach the maximum call stack you have an infinite loop somewhere
this is the only script active
and it doesnt have a loop
wait. i just uploaded an empty build and it still does it. let me check
I'm trying to figure out the cause of an android build crash, so I installed Unity's Android Logcat package. I think I found the cause, but have no idea what I'm looking at. Anyone able to give any insight?
@normal cedar
Maximum call stack size exceeded means that somewhere in your code, you are calling a function which in turn calls another function and so forth, until you hit the call stack limit.
The server backend is written in Java, and this mentions a null pointer dereference, and seems to say it's a Java error, but that's all I've got. If I can just confirm that's the cause, that'd be good, but will need more info on which bit of code is causing it if possible
nullpointer reference, something tries to access something that isn't there
chrome doesnt give me the error so it is opera being annoying now
Yeah, I figured that bit out, is it Java you think? I don't think this sort of thing is Unity-based
webgl has been nothing but a pain in the ass for me
Well the backtrace shows it's mostly Unity
Try commenting out various calls you make and see which one causes the stack overflow
like GetAchievements(); or StartCoroutine(GetLink());
Can't you run it in Unity?
in unity it runs fine
it only breaks on build
and when i put it on a server
and i need to it to load in a personalID to load in all their data
so it is too important
it works now
problem was Opera
Weird
I'm late, but yes, that's a Java error. It's a bit like a C# NRE, but it's not the same name as the common java.lang.NullPointerException so idk. Names changed maybe?
Hrm, ok. Thanks
well... just learned something new... [SerializeReference] will polymophically serialize objects.
Now I just need to figure out how to write the custom inspector to allow selection of a desired derived type when adding an element. That will probably require reflection.
Don’t mind me, im just talking to myself I guess…
If you need a confirmation, Reflection can do that. Look for the Type:IsAssignableFrom(Type) method
Works with interfaces as well.
Classes only, Type:IsSubclassOf(Type) will do
Thanks. I think I probably also need to figure out how to create a aerialized object in a custom inspector, so that upon receiving a selection of a type, the aetialized object of the appropriate type is created.
As long as the type can be instantiated (not abstract nor static nor interface), you can use Activator.CreateInstance(Type) to create an instance of an object at runtime
I think the object needs to declare a paramless constructor, unless there is an overload where you can pass arguments
There are a couple of repos on GitHub that do exactly this, like this one:
https://github.com/mackysoft/Unity-SerializeReferenceExtensions
sweet, thanks
i keep looking to the asset store for neat tools like this but usually can't find anything. looks like a lot of these types of things are just on github or whatever
Not exactly sure if this is the right channel: Does anyone have experience with debugging Unity crashes on Linux?
I have a coredump, but I don't know how to get the right stacktrace out of it, as GDB is unable to deal with the managed symbols from mono
oh wow you can make a property drawer apply to a field using an attribute... how did i miss that one... i feel like no matter how much unity i learn, i always feel like i know maybe 5% of it. the more i find out, the more i realize i don't know
For my crash issue, I found the "tombstone" logcat was refering to. Any ideas what I can check for this? Looks like URP stuff, but this is basically gibbrish to me managed backtrace: #00 (wrapper managed-to-native) UnityEngine.Rendering.ScriptableRenderContext:Submit_Internal_Injected (UnityEngine.Rendering.ScriptableRenderContext&) #01 UnityEngine.Rendering.ScriptableRenderContext:Submit_Internal () <0x13> #02 UnityEngine.Rendering.ScriptableRenderContext:Submit () <0x13> #03 UnityEngine.Rendering.Universal.UniversalRenderPipeline:RenderSingleCamera (UnityEngine.Rendering.ScriptableRenderContext,UnityEngine.Rendering.Universal.CameraData,bool) <0x80b> #04 UnityEngine.Rendering.Universal.UniversalRenderPipeline:RenderCameraStack (UnityEngine.Rendering.ScriptableRenderContext,UnityEngine.Camera) <0xbbf> #05 UnityEngine.Rendering.Universal.UniversalRenderPipeline:Render (UnityEngine.Rendering.ScriptableRenderContext,UnityEngine.Camera[]) <0x157> #06 UnityEngine.Rendering.RenderPipeline:InternalRender (UnityEngine.Rendering.ScriptableRenderContext,UnityEngine.Camera[]) <0x3f> #07 UnityEngine.Rendering.RenderPipelineManager:DoRenderLoop_Internal (UnityEngine.Rendering.RenderPipelineAsset,intptr,System.Collections.Generic.List`1<UnityEngine.Camera/RenderRequest>) <0xcb> #08 (wrapper runtime-invoke) <Module>:runtime_invoke_void_object_intptr_object (object,intptr,intptr,intptr)
Looks like the same info as was in my screenshot. Also, I was trying to track down some mobile performance issues with profiler and I'm pretty sure these calls were at the top. I'm thinking they're related, what do you think?
Unity 2021.3.14f1, URP version 10.5.1 btw
Letting the game start in portrait mode, it seems ok (sometimes...), but soon as I turn the phone so it switches to landscape, it seems to crash. Not sure if it's just randomly crashing and it just seems like the orientation is related though Yeah I think it's just random
transform.position = Vector3.Lerp(startPosition, target, t);```
I'm using this inside of a coroutine but sometimes the timeToReachTarget varies significantly (0.2 -0.3 sec).... I suspected it was because the change on the frame rate and changed Time.deltaTime for Time.fixedUnscaledTime.... now just varies 0.01 - 0.02. Does it make sense?
timeToReachTarget is something you would be setting outside of the loop. Like... if you want the lerp to take 1 second, it should be 1.
but when i was using normal deltaTime
i measured that time
and was not exactly 1 dependinng when did i launcched the method
Can you show a little more context? How is your loop structured and what kind of yields are you using etc
can you show the whole coroutine?
what yields do you use int the loop
sure , 1 second
It will never be exactly 1 second btw, there will always be an error of up to however long a single frame takes for your game.
private IEnumerator MyCoroutine()
{
float duration = 1;
float lerp = 0;
while(duration>0 && lerp<1)
{
lerp += Time.fixedUnscaledDeltaTime/duration;
sceneFader.alpha = Mathf.Lerp(1,0,lerp);
yield return null;
}
}
yeah but the difference sometimes was more than 0.2 seconds
i suspected it was beccause sometimes i call it when there is a framedrp
framedrop*
thats why i changed
Time.deltaTime TO Time.fixedUnscaledDeltaTime
now seems to be always same time...
I'm asking if what I did makes sense
because i've heard deltaTime is frameDependant and i thought that could solve it
aha
so with unscaledDeltaTime i would have the same result than now
no... deltaTime is there to fix framerate dependency
since your coroutine is looping every 1 frame, you need deltaTime to adjust for that
but if I use deltaTime the times varies between 0.2 or even 0.3 seconds , i dont understand why
i used StopWatch class to measure it
print out the current time and the value of lerp every loop.
therre's no magic here
it will stop when lerp reaches 1
deltaTime is the time since the last frame
if your frame time / FPS varys so does deltaTime ands thats wht its for
and thats why using deltaTime does this coroutine take more or less time depending on the FPS
corrrect?
if your game is chugging along such that a frame takes 0.2 seconds to render, there can be an error of up to that much time
Are you having framerate issues?
yes, actually i do some sorrt of render heavy stuff in one ocasion
no, just on that particular moment
ill put some context
If you have a particularly slow frame, then yes, this can have an error of up to the length of time it takes to render that one frame
No, there's no avoiding it
or the one im using (which seems to work)
The answer is to fix your framerate problem
sweet, it works. thanks for the info
yes, the one im using makes it work
@sly grove ill put more context
im doing this:
-1 I fade the screen white with the coroutine (no prob)
-2 I ennable a camera and take a render snap to create a texture (this takes some time depending on the device)
-3 whenn the snap is done... i fade to transparent again
1st fade takes 1.000 second, 3rd fade takes 0.6 - 0.8 seconds
I think its because, even if i do it AFTER the render snap is done there still might be some work going on
this happened using Time.deltaTime
then i changed to Time.fixedUnscaledDeltaTime and 1st and 3rd fades take the same amount of time... just with 0.0X difference
and I'm asking.... does this make sense?
😄
I think you're just seeing the cumulated effects of error between all three coroutines
Your first one might take like 1.05 seconds
The second one 1.07 seconds
the third one 1.04 seconds
So the whole process takes 3.16 seconds
just as an example
uhmm the first one and third one are the same code... and i do
sw.Start();
while(duration>0 && lerp<1)
{
lerp += Time.fixedUnscaledDeltaTime/duration;
sceneFader.alpha = Mathf.Lerp(start,end,lerp);
yield return null;
}
Debug.Log("SatviewExit Time " +enable+" :" +sw.ElapsedMilliseconds);
// sw.Stop();
so i can see pretty clear the times of the first and third coroutine
take into account the second doesnt startt until first is done
and 3rd doesnt start until second is done
Note that Unity's time API deals with the time as it was at the beginning of the frame always.
im measurinng the times of 1st and 3rd by separated
If Time.time is 492.3 for example, it will be 492.3 for the entire frame. Unlike System time apis which will keep ticking even during a frame
and what does that mean? sorry i didnt get it 😅
It means that there will be some discrpancies between what you see with things like Stopwatch and what Unity's time APIs are reporting
Could you show all your code btw and what is being printed?
sure
how was the color paste thinng?
public void TriggerSatViewExitFader(bool enable, Action onCompleted)
{
StartCoroutine(SatviewExitFaderCoroutine(enable,onCompleted));
}
private IEnumerator SatviewExitFaderCoroutine(bool enable,Action onCompleted)
{
float duration = enable
? configService.varsCfg.satViewExitFadeInTime
: configService.varsCfg.satViewExitFadeOutTime;
float start = enable ? 0 : 1;
float end = enable ? 1 : 0;
sceneFader.gameObject.SetActive(true);
float lerp = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
while(duration>0 && lerp<1)
{
lerp += Time.fixedUnscaledDeltaTime/duration;
sceneFader.alpha = Mathf.Lerp(start,end,lerp);
yield return null;
}
Debug.Log("SatviewExit Time " +enable+" :" +sw.ElapsedMilliseconds);
sw.Stop();
sceneFader.alpha = end;
sceneFader.gameObject.SetActive(enable);
onCompleted?.Invoke();
}```
there
if i pass enable=true fadesInn if i pass enable=false fadesOut
first i noticed visually it was not totally exact (using deltaTime)
then i placed the StopWatch and confirmed
ill runn, 1 sec
@sly grove this is with fixedUnscaledDeltaTime:
[Log] SatviewEnter Time True :862
[Log] SatviewEnter Time False :969
first linne fadeIn second fadeOut
acceptable
now i try deltaTime
SatviewEnter Time doesn't the code say SatviewExit Time?
yeah i just took the method below but its the same code actually, didnt feel like merging both
its the same code but taking different fadeTimes
Btw does your game mess with Time.timeScale at all
ok what do you get when you do deltaTime
🤔
0.3
the first one makes sense
for a coder is nothing but for my game designer its a lot 😅
well, for a coder, in my context
yes, 100%, i paste againn
i paste the correct
private IEnumerator SatviewEnterFaderCoroutine(bool enable,Action onCompleted)
{
float duration = enable
? configService.varsCfg.satViewEnterFadeInTime
: configService.varsCfg.satViewEnterFadeOutTime;
float start = enable ? 0 : 1;
float end = enable ? 1 : 0;
sceneFader.gameObject.SetActive(true);
float lerp = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
while(duration>0 && lerp<1)
{
lerp += Time.deltaTime/duration;
sceneFader.alpha = Mathf.Lerp(start,end,lerp);
yield return null;
}
Debug.Log("SatviewEnter Time " +enable+" :" +sw.ElapsedMilliseconds);
sw.Stop();
sceneFader.alpha = end;
sceneFader.gameObject.SetActive(enable);
onCompleted?.Invoke();
}
Can you print out what the duration is?
float duration = enable
? configService.varsCfg.satViewEnterFadeInTime
: configService.varsCfg.satViewEnterFadeOutTime;```
I See you're getting two different durations
duration is always 1 , 100%
Print it
but i can print
just to make sure
I have an Assembly Definition Question!
My folder structure looks like:
Assets
- Game1
- Game2
- Plugins
- Common
So, each of the folders above has an asmdef. All of the asmdefs rely on "Plugins".
I have TextMeshPro as a reference in Plugins. What's curious is that even though Game1, Game2, and Hub rely on plugins, they can't seem to find the reference for TMP. Is there a simple setting I'm missing? It feels overkill to have to add TMP as a hard reference in every assembly.
running...
Duration: 1
SatviewEnter Time True :1002
Duration: 1
SatviewEnter Time False :741
I don't understand how lerp could be getting > 1 then
I mean the lerp variable
that loop continues as long as lerp is < 1
If you're adding deltaTime each frame (divided by 1 which doesnt matter), that should take 1 second (or slightly longer)
as it does for the first run
Unless timeScale is getting affected
oh!
Can you search your project for Time.timeScale?
What happens if you use Time.unscaledDeltaTime
let me try
did you use control+f or control+shift+f to search the project?
Time.timeScale= 1
but thats the default so its alright
command F
but i use Rider
That only searches one file, no?
sorry, command + Shift + F
Regardless, if unscaledDeltaTime works then we know it's a time scale issue
cool, just wanted to make sure 🙂
Duration: 1
SatviewEnter Time True :1022
Duration: 1
SatviewEnter Time False :152
this was even worse 😮
wtf
that makes... so little sense lol
ill try fixedDeltaTime
[Log] Duration: 1
[Log] SatviewEnter Time True :872
[Log] Duration: 1
[Log] SatviewEnter Time False :1068
hmmmmmmm
nnot as good as ffixedUnscaledDeltaTime
why the heck is happenning this
with fixedUnscaledDeltaTime i get it almost the same
it has to be something related with the camera.render() im calling
What happens if you print out Time.time before and after the loop
instead of using StopWatch
ok 1 second
look
if i use the same method
but without the camera.Render() between them
Time.deltaTime works fine
oh there's a Camera.Render happening?
of course its the second step i told you
how come
thats why i said the framedrop thing
Ohh
-1 I fade the screen white with the coroutine (no prob)
-2 I ennable a camera and take a render snap to create a texture (this takes some time depending on the device)
-3 whenn the snap is done... i fade to transparent again
ok - I wonder if there's a bug with Camera.render where unity doesn't account for the time that takes appropraitely
if step 2 is out of the equation then works fine
can you see if you wait like 1 extra frame between step 2 and 3
sure
like add a yield return null at the beginning of the coroutine or something to do it quickly
but then the time will be affected
or not
ah okk ok
SatviewEnter Duration: 1
SatviewEnter Time True :1021
SatviewEnter Duration: 1
SatviewEnter Time False :700
same
sure
void Awake()
{
if (snapCam.targetTexture == null)
{
snapCam.targetTexture = new RenderTexture(resWidth, resHeight, 24);
}
else
{
resWidth = snapCam.targetTexture.width;
resHeight = snapCam.targetTexture.height;
}
}
public void SetWaterObject(GameObject waterGO)
{
waterObject = waterGO;
}
public void TakeSnapshot(Action onCompleted)
{
waterObject.SetActive(false);
waterSprite.enabled = true;
snapCam.enabled = true;
snapCam.Render();
snapCam.enabled = false;
waterObject.SetActive(true);
waterSprite.enabled = false;
onCompleted.Invoke();
}
gotta go now @sly grove thanks a lot for your help, if you come up with something please let me know
what's your objective? you're trying to create a reflection?
like a reflection effect for sprites?
i think there's a lot about this online
usually you have to make sure the camera renders at the "right time", and then the most performant way to use the render texture is through a shader property that isn't exposed in the shader graph
you can make 2d sprite shader graphs in URP
Learn how to create water reflection shader graph in Unity 2019.3+ for 2D games.
Water Texture download: https://www.textures.com/download/pbr0159/133194?q=lava
Free Asset - 2D Handcrafted Art: https://assetstore.unity.com/packages/2d/environments/free-asset-2d-handcrafted-art-117049
Links for creating 2D character for platformer games:
Drawi...
@stuck onyx
you have to add text mesh pro to every assembly. but you only have 4
try changing from forward to deferred rendering
we have 27 games sadly. It just feels weird to have all of these hard refs, I feel like dependency management shouldn't work this way 🤔
you don't need to reference tmp in assemblies that don't use them
what is the underlying issue?
you're saying 27 folders with asmdefs in them, each with 27 scenes, that you build to 27 distinct exes?
you can just reference it once in common
The underlying issue is that we licensed several games to our platform. They each used their own version of TMP when they were made. We wanted to unify them all to use the Package Managed version of TMP
you should be able to reference it once in common
So "plugins" is our "common"... and all of the games reference plugins. You're saying that if Plugins references TMP, then the rest should have access? That was my thought as well, but that doesn't seem to be the case
what kind of error do you see?
I think I'm missing something. The docs say I should have the option to change it here, but I don't have Lighting at all https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.0/manual/rendering/deferred-rendering-path.html
hmm
So in this case, the scripts are in Assets/Hub
the Hub.asmdef, relies on plugins.asmdef. plugins.asmdef references TMP. I was hoping that the scripts in Hub could use TMP, without adding a reference to TMP in the hub.asmdef
I just confirmed this; the dependencies do not seem to carry over the way you would expect. You have to manually reference the assembly, even if it’s already referenced by an assembly in your references list
Yeah i created a test project to test this and ran into that. I was just curious if there was a setting or set-up that i was missing. Thanks for the confirmation! ❤️
Glad I could help! There could still be a setting, but I don’t know where. I just ran through a test with one of my projects and everything is manually referenced.
that feels weird, doesn't it? I suppose I'm just used to something like Nuget that makes it easier lol
If I have an abstract base class called Food and it implements an interface called IFood, does that mean when I create a new script and inherit from Food it also inherits the interface (IFood) as well?
the reason is, because I have a trigger looking for the interface
IFood food = other.transform.getcomponent<IFood>();
But i'm wondering if I even NEED that, or I can just look for the base class instead from the derived script
Food food = other.transform.getcomponent<Food>();
yes
If you have Food : IFood and HotDog : Food then implicitly it is true that HotDog : IFood
Thanks @sly grove! I think you're like 5/5 of my questions answered lol, I need to buy you a coffee sometime
i have a scriptable object class Statuses that acts mostly as a dictionary to hold objects of a serializable class Status in an array. I'm able to add and remove statuses in the inspector very easily. Now I'd like to be able to reference these statuses elsewhere in the inspectors of other gameobjects, somehow.
example being i have an enemy type as their own scriptable object. I'd like to have a list of "statuses" in that script, and I'd like to simply pick and choose which status that enemy will have in the inspector from said list that i filled out in the original Statuses SO.
public class Statuses : ScriptableObject
{
public List<Status> statuses;
}
this is the whole class; i basically want to use whatever's in the statuses list as a dropdown to select from. it's probably simpler than i think but im not familiar enough with unity 😔
@polar ermine It would just be better to call the status script in your enemy as a variable
if you have to select it it shows you the statusses anyway
public class Enemy : MonoBehaviour
{
public Status status;
}
but if you wanna go real fancy it is possible but the solution i showed above is just way cleaner
The only problem with that is i need to input the values for that particular Status object all over again
i write it once in the dictionary and need to write it again it kinda defeats the purpose of a dictionary
wait wut?
you know scriptableobjects can not save contents that have been changed during runtime right?
you have to pre-assign them.
im pretty sure i've accidentally permanently mutated values in a SO from in game but that's not what im trynna say, i think
that would be odd but possible if you have a save loading system.
But what would be better is to do this:
[CreateAssetMenu("idk what goes here anymore")]
public class Status : ScriptableObject
{
Data
}
and then have a seperate class for your enemy where you just call status
public class Enemy : MonoBehaviour
{
public Status status;
}
Yeah the worst part about that though is that it only works that way in editor. In a build it won't.
that was it yeah!
i see, good to know
if you have any question about how it goes in working i could show you
im just trynna think if my current set up could work
[Serializable]
public class Status
{
public string name;
public string description;
public Sprite sprite;
}
this is my current status class, and i showed the statuses dictionary SO class earlier
right. in my enemy class i started by doing Status[] statuses, but in the inspector i have to fill all that information again, i can't pick from the list of statuses i already made.
i guess making each Status it's own thing would be good too, though
and assign each status to a list object in the dictionary so?
makes it a little bit less modular than I'd like, but I'll give it a shot
ofcourse what you could do it make it like you want, then have an enum with all the statusses name and then do a for loop
right. and link each status with the associated enum at runtime and just use that. i started doing that but started to wonder if i could just simply use a list of statuses in place of an enum
because i'd need to be sure i had a good system of "linking" that would involve a standardized naming scheme or something, which is good practice but prone to error because im dumb
that said, I did get something to work to some effect of how i wanted it to.
i just worry about scalability and how much work I'll have to do in the long run using one solution over the other.
might be minor, but needing to make a scriptable object for each status, and putting that object in the statuses dictionary once and then also adding them to cards that use that status instead of being able to directly link to the dictionary feels inefficient to me.
Anyone mind looking at https://gist.github.com/vantreeseba/7f420eee29a6fd3963eacc901a198e83 and helping me figure out why it's so slow? (to be fair, I'm testing it on like 100 ai dudes running around, but still it's creating like 10/20KB of GC per frame, and taking like 8ms total).
I know I can timeslice it, but it still feels wonky that simply sorting some lists consisting of not many things is so slow.
well are you running this on each ai dude?
if so, maybe it has to do with how many times you're doing the for loop on lines 53-67
Yeah, I mean 80 * 16 = 1280 but that doesn't seem like thaat many raycast calls, and if I remove the sorting, it takes ~1ms (total time for all 1280) to do that.
a lot less gc without sorting?
That makes total sense yeah, but no idea why the sort is exploding.
If I have the sort in, the profiler is telling me between 8ms/12ms per frame for this func (and 10/20KB of GC), without it, no GC and ~1ms. 😦
So it's 100% the sorting, but I feel like there must be something obvious I'm missing or etc, it shouldn't take that long to sort a max of 16 things.
looks like using an IComparer causes per-frame allocations
maybe convert your class to just a static func, as stated in here?
dunno
or i guess implement your own sort algorithm. shrug.
not the nicest answer i guess
your compare looks like a pure function, so should be easy to convert to a static func
or you could even use an anonymous method i guess
(is that the same as a static func?)
An anonymous method I believe will cause some GC since it must capture surrounding context for the lambda
ahh
Yeah I looked at the non gc list thing you linked earlier, and tried many diff methods, and they all still cause GC, I dunno lol.
It's good, thanks for the help. I did manage to get it a bit faster by hinting compiler to inline the comparer, i.e.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(Transform x, Transform y) {
var xdist = (x.position - _me.position).sqrMagnitude;
var ydist = (y.position - _me.position).sqrMagnitude;
return xdist.CompareTo(ydist);
}
So that's cool, I guess, now it's like ~5ms on avg.
nice
I ended up removing the sort, and just searching for the closest in the list when a function is called, ended up being much faster, and will refactor later if required. 😛
20kb of gc isn't going to matter
it looks like you figured it out
what were you trying to do?
this kind of spatial querying is expensive
you might want to use a dedicated thing
no matter what you'll have a giant switch statement
this is fine. but don't make a scriptable object for every status. it's painful. you should have a single object for all of your game logical assets.
public class CardGame : ScriptableObject {
[SerializeField] private Status[] m_Statuses;
[SerializeField] private Card[] m_Cards;
...
public Status[] statuses => m_Statuses;
// or??
public Status GetStatus(StatusEnum status) { ... }
}
@polar ermine
you only need one
do you see why this is less painful?
still works with the inspector
"but what if i have lots"
you don't right now
and even when you have more, it'll be less painful than having a ton of individual assets
that you will have to gather into an array anyway
yeah, i'd prefer not to have a SO for each status. it doesn't scale well i dont think
how does it work in the inspector exactly?
you click on it
and then you hvae an array
etc. etc.
no custom editor necessary
anyway i think you'll figure it out
okay, that's not what the error message says
it's saying it can't find TMPro
it can't find it because it isn't directly referenced in the assembly
i think just go ahead and reference the assembly
if that works for you
yep that seems like the only solution!
27 clicks... it'll take you 2 minutes
Im not worried about the time, moreover the fact that if we end up with other dependencies it makes connecting all of the wires more complicated 😅
It feels unclean is all lol
Just create a list of nearby entities within the FOV not obstructed by obstacles.
It was 20KB per frame, which would end up being quite a bit over time IMO. (1.2MB / second, so after 10 mins, you're gonna have nearly 1GB of memory leak)
yeah
With the switch to not sorting, and only searching on demand lowered the per frame time (for ~100 of them), from 5ms to like 0.2ms, so it's basically nothing now lol.
what does the profiler say
from my experience overlap sphere even non alloc is slower than any conventional spatial structure
i had scenarios where 1000 units were querying in radius, at first i naively used overlap, then a simplest grid possible, and grid ran at 200 fps vs 20
now i use mix of grid and spatial hash, and i can go to 100k at 60 fps on a single thread, in a pure scenario without transforms
transforms are quite expensive
Well, the overlap sphere itself wasn't the issue, it turned out to be the sorting which is weird, and at the radiuses I'm expecting to use, I'm not too worried about it (for now), though I may end up doing something smarter if required i.e. space binning or some quadtree, though I would assume they do that in the physics engine internally, but who knows
The raycast was the other expensive part, but I fixed that (mostly) by limiting it to the single hit, since ANY hit is "obstruction"
I should have SS the profiler, but again, something like 98% of the time was in the sort, which is wild, but ... .net strangeness I suppose
you can cache the positions and ditch transforms
write your own sqrmag and other simple api calls (angle) with agressive inlining
should batch the whole thing tbh, at end of frame or some sync point, cache positions, update the buffers, next frame they have the info
Yeah, it's fine for now with slight time slicing, can render 400+ npcs at 60fps, which is more than I'll ever need for the type of thing I'm doing. thanks for the advice though!
@undone coral what i'm doing is to create a texture from all my city in 3D taking a capture, then i set that as texture to create a big map of all my city. I do that to create a satellite view, kid of a google maps if you like....
but the problem here is we dont know why the times of execution of a coroutine are so different using deltatime, unscaledDeltatime, and unscaledFixedDeltaTime
private IEnumerator SatviewEnterFaderCoroutine(bool enable,Action onCompleted)
{
float duration = enable
? configService.varsCfg.satViewEnterFadeInTime
: configService.varsCfg.satViewEnterFadeOutTime;
float start = enable ? 0 : 1;
float end = enable ? 1 : 0;
sceneFader.gameObject.SetActive(true);
float lerp = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
while(duration>0 && lerp<1)
{
lerp += Time.deltaTime/duration;
sceneFader.alpha = Mathf.Lerp(start,end,lerp);
yield return null;
}
Debug.Log("SatviewEnter Time " +enable+" :" +sw.ElapsedMilliseconds);
sw.Stop();
sceneFader.alpha = end;
sceneFader.gameObject.SetActive(enable);
onCompleted?.Invoke();
}```
this is the coroutine....
the steps are
- Execute coroutine with enable=true (fades to white)
- Once is done i do the Camera.Render to create the texture
- Once is done i execute the coroutine step 1 with enable false (fades to trasparent)
step 1 and step 3 take different times if we use Time.deltaTime
a 0.3 sec difference approx
if we take step 2 out of the equation coroutine always take same amount of time of course
@sly grove was taking a look to this yesterday too and didnt find too much sese
sense
Hello, I am trying to do a HTTP post from C# into PHP.
I have used this example: https://www.php.net/manual/en/features.file-upload.post-method.php
And have successfully uploaded files on my website to my server.
If I try to send a json file with the C# WebClient, the response from the server is; "Possible file attack" or "Failed to open streams: permission denied".
I have tried to change the folder permissions trough a FTP client and trough Chmod but this still does not work.
Anyone here familiar with anything like what I am implementing?
dont use webclient it's old as hell
use httpclient
who knows what that thing's doing
Okay, will try with httpclient.
Hey, I'm trying to use the FreeImage external library in an Android build. It works fine on the editor, but when I build the dll goes missing and I get a dllNotFound exception through logcat.
I tried to force Unity to include it with a linker, the resources folder, and even tried to include it manually in the apk after the build (lmao), nothing worked. Anybody got an idea?
Camera.render might take a very significant amount of time. And Unity works on time slicing. There's no way you can get a consistent timing. Delta Time is accumulated throughout the frame from start to end and is applied on the next frame at beginning.
From what I gather, something like this is happening at your end. It's just an example and are not at all representing the actual timings from your case.
Second last frame before loop ends:
value of lerp = 0.9 or something else behind 1. Throughout this frame, your Time.time remains same.
Last frame:
Suddenly your deltaTime has a large spike and crosses way above 1f. Say 1.2f which you see as the error in your measurement. It's totally fine and you can't avoid it.
Though I'm curious about the practical issues you are facing because of the unexpected error
Hello, i've created a shader graph and I want to show it "fullscreen", how do i do that?
I don't see the relation between this question and the channel you posted in (programming). If you're asking how to make a window full-screen, ask in #💻┃unity-talk. If that's specific to the shader graph, #archived-shaders.
Oh ok sorry, I'm not really familiar with this ds server so i didn't know where to ask, thx
is anybody familiar with the performance implications of [SerializeReference]? I have a List<BaseClass> that I've annotated with [SerializeReference] and i notice that there is a significant slowdown in play mode when i have that list expanded, but the slowdown goes away when i unexpand the list.
when i had the underlying class viewed in the inspector without [SerializeReference], I didn't get the slowdowns, so it does seem to be that annotation
and the profiler shows that the massive slowdowns are definitely in the editor loop.
maybe this is more or an editor question...
i feel like you have greatly overcomplicated this effect. also, camera.Render() is synchronous
you should really not use SerializeReference
for performance reasons?
why?
it doesn't do what it advertises it does
can you explain further?
it looks like it can solve problems like "lists of differently typed objects" but it can't
it does solve that problem, but very poorly
how many different subclasses of baseclass do you have?
and how many do you think you will have?
maybe 4 or 5
then the best thing to do is
[Serializable] class T {
...
}
class Container<T1, T2, T3>
where T1 : T
where T2 : T
where T3 : T {
[SerializeField] private List<T1> t1s;
private List<T2> t2s;
private List<T3> t3s;
...
public IEnumerable<T> allTs() {
return t1s.Concat(t2s).Concat(t3s);
}
...
}
or, use odin
because the inspector just doesn't interact well with this sort of model
usually when you have a list of things that can be different types, you're doing something wrong anyway
so what if the types all have the same base class? it isn't as useful as it seems
because you tend to have very few subclasses
it makes sense when it's List<UIElements> and there may be hundreds of different UI elements
it makes less sense for gameplay stuff you're writing yourself, when invariably the number of subclasses is like, 1-5
i would recommend just using odin because i bet you are messing with serialize reference
for the purposes of getting the inspector to work and nothing else
yes that's correct
what's it for?
isn't that what you said above?
i'm saying odin gives you exactly what you want
oh i mean...
an inspector for a list of subclass instances without having to write a custom editor
what's serializereference for?
I have a question. I am trying to destroy an object when it is hit by light, colliders are not working, and I do not want to raytrace every instance in the game, as these are enemies that multiply at respawn once destroyed.. any ideas?
it's for serializing objects when you don't know the type ahead of time. for example, if you're creating a Localization package and you have a class Localizable<T>, and you'd like to serialize an instance of something like X : Localizable<string> but at the time your code is written, you don't know that
ahh
well anyway the easiest solution here is probably make the objects derive from monobehaviour and just add each as a component to a gameobject
people use it for "I would like to serialize an IBlah"
yes, that definitely works
but Odin is what you really want
which is a better inspector
i'll look into it. good deal now i guess
i've sort of known about it for a long time but never used it
mostly because i couldn't find a list of its capabilities
carefully look at what the inspector says when you have the .dll and .so files selected for android
is this a purpose built unity library?
i don't really understand your assertion that a list of base classes isn't useful. for example, i use it to polymorphically execute actions that other entities add into a queue. not sure how i'd do that without list<UnitAction>
in unity, you should use unitywebrequest
What Unity version are you using? I saw the same thing, I think in Unity 2020.3, but it was fixed in Unity 2021.2 or something like that.
2019 something. Guess I should upgrade…
Actually lemme check…
oh yeah it's 2020.3f.1f1
so i guess i'm upgrading
I wish there was a place I could search through all Unity version changelogs. I'm trying to find mentions of SerializeReference in recent changelogs.
It's a library completely independent from unity. I just have a .dll, not a .so. But it is set to load on all platforms.
isn't freetype a c++ library? you should have an .so file
otherwise this will just fail
It's FreeImage, and I checked on their site, no way to get a .so with my .dll
Is this the reason it fails ? ^^"
the thing you downloaded is only for windows
better question: why do you need freeimage?
what 1 image format are you trying to read
you would need to build freeimage specifically for android
which is straightforward but arcane
Will definitely look into that, thanks
sorry, can you just explain this a little more? i use this pattern relatively frequently for polymorphism and i just want to understand if i'm doing something wrong.
got it
you're doing everything fine
what i really mean is
I do open source collaborative game dev. Paid assets are kind of a drag for the community.
the super brief version is, almost always you're doing a list of data instead of objects
It’s good for proprietary, closed team though.
unity's scene is data, it's not like, a piece of runnable c# code
it's not an instance of a class
they pretend the stuff you drag and drop into the inspector is instances of a class
it is clunky to turn polymorphic c# objects into data. you always wind up with a "type" field somewhere
SerializeReference ships that type field for you, but in a way that has surprising consequences
polymorphism makes the most sense when you're designing something that's meant to be extensible by other people, or has behavior that is unknowable
it's less useful when you're building a game, because you sort of know 100% what everything will do. the polymorphism turns into an organizational feature, not something essential to getting the code to work
concretely, what do you do with this advice?
usually when i want something to appear in the inspector without causing a huge headache, i make a struct and stuff all the aspects of it into there
If T1, T2, and T3 are asserted as being of the lineage of T, it seems more simple to have the container be a container of type T.
it can for sure. i think the thing i'm writing already exists in some library
for small numbers of types known ahead of time
so for example
like tell me what you are trying to store?
like enemies?
you want to describe enemies?
or something
it's basically a terrain tile type. or a terrain tile feature type
actually it's a controller for those things
I think he's just saying for the instance where you want the things to appear into the inspector and you have a small number of types, it's easiest to have them appear in the inspector by just having a list of each type
it's kind of unfortunate that the unity inspector fights so hard with the basic nature of c#
I'm loading a shit ton of photos at once, and to not freeze the application I use some code to load images async but it's dependent on FreeImage
like c# has all these elegant solutiosn for problems but the inspector just says no
The code referenced above looks a bit off. I probably don’t have a ton of context to say for sure, but you do! Just surfacing this as an opportunity to reevaluate the desired traits of the code you’re producing. If you kick the tires and find everything okay, that’s awesome!
For me, I try to look out for overly generic code as it’s a sign I’m conplexifying code, or trying to buy abstractions prematurely? Not trying to be a jerk or anything, just trying to help
Icic. It’s to buy the ability to see it in the inspector. That makes a little more sense!
yeah this async texture loader package?
did you try just using unitywebrequest to load images
Texture texture = ((DownloadHandlerTexture)uwr.downloadHandler).texture;
usually you want to do something of the form
class Map {
public Tile[] tiles;
}
struct Tile {
int type;
// stuff that is common to
// all tiles
int elevation;
// thing that is interpreted
// differently by the tile
// types. for example, maybe
// value means the height of
// a tree tile, or the depth
// of an ocean tile, or the
// number of berries on a
// berry tile
int value;
}
you can still have
class GameTile {
Tile tile;
}
class BerryTile : GameTile {
int startingBerries => tile.value;
int currentBerries {get; set;}
}
The problem with a texture loading is that even if you create a texture on a different application you need to marshal that texture data from Free image library to Unity in the end.
UnityWebRequest handles texture very well in that aspect. I was able to load more than 200jpgs without any issue on mobile devices.
The main challenge comes when the texture is being uploaded to GPU. That has always been a bottleneck in engines because that upload process is synchronous and will stall your main thread. There are techniques to avoid it for ex texture streaming. It loads a texture in lower mips and then gradually raises up the mips. It's quite evident in most cases as you can see the texture popping up. There's also SVT in hdrp. This will although work with only asset bundles or assets built with project (excluding Resources)
in your case you should be able to load images without much stalling. Male sure you correctly manage the image loading and uploading part. don't try to load all textures at the same time. load them one by one using UWR
(solved)
hi having trouble with HashSet<..> with a custom MonoBehaviour
it seems the Contains method does not work as expected
in my case when there are 2 components in the HashSet, and I call Contains with one it sometimes does not return true
I checked GetHashCode that the custom MonoBehaviour is definitely in there
Is there something I'm missing here with HashSets and Components/custom MonoBehaviours?
oh well I think I made a mistake thanks 😄
Does unity have a way to view logs sent to Console.Error?
if you're trying to read Console.-anything you'll need to use the Console callback methods
Alright thanks I'll look at it
An external C# DLL library in my Unity project does not seem to be protected/exempted from the Low Managed Stripping Level from WebGL. It functions correctly when compiled for Windows (Mono) but not for when compiled for WebGL.
I get this error.
I have a link.xml file in the project root assets folder. Though, it doesn't seem to do much.
<linker>
<assembly fullname="FridayNightFunkin">
<type fullname="FridayNightFunkin.Json" preserve="all"/>
</assembly>
</linker>
@proud iris are you using il2cpp rather than mono?
For Windows, I use Mono. For WebGL, it seems to forcefully use IL2CPP, as the Managed Stripping Level can't be lowered to None/Disabled.
try changing it to mono in player settings -> configuration -> scripting backend
it might be because of the aot compiler not playing nice with external libraries
WebGL does not support Mono.
oh huh i thought you could do either on it
you were right -- upgrading unity got rid of the slowdowns.
Well.. now it seems to pack all the work into one frame… not sure what’s going on… but seems to be better most of the time at least lol.
If you know the type you're constructing before compilation you can make some dummy object which implements that type. Not sure if that would solve your issue though
hello, setting AnimationClip.legacy does not work in Build (has no effect at all), this has been a problem (at least for me) since unity2020, Can someone check if it works for you? if not i'd file a bug ?
Report it as a bug.
is it same for u?
I haven't tried it.
Can you do that?
But if it's as clear-cut as you say, should be easy to make a simple repro project
I don't have time.
alright
Hey, I'm trying to bake a navmesh from a custom mesh. That mesh does not belong on any game object, which means I manually have to create a NavMeshBuildSource at the very least. Does anyone have any idea of how to do that? The documentation isnt really helping me
despite figuring out these inner workings of unity being like landing a plane blindfolded, I figured it out if anyone is ever interested
What’s the solution?
Working with the navmesh surface package on git; if the surface navmeshdata is null you generate it via NavMeshBuilder.BuildNavMeshData and set it; if you're updating an already existing data, you update it via NavMeshBuilder.UpdateNavMeshDataAsync set it
If anyone needs concrete details just ask here
That makes sense. Your initial post hinted at the navmesh not being baked at edit time.
If I understand the solution, it sounds like you figured out how to bake the navmesh at run time.
Yeah, not only that, but using any random meshes you might want to and for any number of different surfaces. Since the regular system won't work for ECS
ECS? You mean the Entity Vaporware System? Sorry, sorry had to >.> Mostly because of how huge Unity had convinced everyone it would be. And yet, 3-4 years later 😆
Hey guys, I have a small "problem" with visual studio code
When I'm defining a field it suggests it to have pascal case naming
for example I type:
private Sprite
And it suggests this name:
private Sprite Sprite;
Does it think you're beginning to create a method
I only suggest that because the icon matches the one above Awake
Why? Then whenever you try and make a method it won't suggest names either
Plus in this instance, PascalCase is correct, it's a public field
well for me pascal goes only to non-fields
doesn't matter the access modifier
I just want to delete name suggestion for methods only, as it doesn't make sense to propose method names anyway
The editor suggests names based on the return type which is useless for me
Question: An IEnumerator is resettable by calling IEnumerator().Reset().
I store the IEnumerator in a Coroutine variable by calling var Couroutine = StartCouroutine(IEnumerator());
Now, Couroutine.Reset() does not exist. Is there a reason for that?
Because the coroutine (iterator method) with all the neat methods like MoveNext and Reset is wrapped into the Unity Coroutine type that handles all the logic
StartCoroutine doesn't return an IEnumerator
It returns that wrapper
Interesting. How can I start the IEnumerators then so that I can later access them again? Since StartCoroutine doesn't work?
IEnumerator coroutine = MyCoroutine();
StartCoroutine(coroutine);
coroutine.Reset();
Untested, may or may not work
Intuition says it won't
If you want to reset a coroutine properly then stop it and start it again
A coroutine stops at it's next yield instruction correct?
What I mean is that when I call StopCoroutine("Coroutine") it continues it's execution until it reaches the next yield. Atleast if I remember correctly.
Functions always run to completion, which is either when the function ends or when there's a return statement. When you stop a coroutine, it stops immediately. But it may have done something before you stop it.
this will not work. last i remember this method was not implemented in unity.
the only way to restart a coroutine is to
private Coroutine coroutine;
void Restart()
{
if(coroutine != null) StopCoroutine(coroutine);
coroutine = StartCoroutine();
}```
its more convenient to use a wrapper class for this logic with MonoBehaviour and IEnumerator reference if this logic is being used at too many places
StartCoroutine(coroutine);
Otherwise, the above is what I do too.
Yes, correct
Thanks 👍
I recommend not using the string version of StartCoroutine though
true
A simple bool in while loop should also suffice
If I could weigh in on the coroutine situation, IEnumerators can be manually progressed by using .MoveNext() on an IEnumerator, allowing you to progress them via your own triggers rather than a coroutine. So you call your IEnumerator method once to create an instance* of it, then call .MoveNext() on that instance at your own whim.
*instance is a simplification for education purposes only
An old screenshot from how I'm using it, where TeleOrb() essentially acts as a logical wrapper for the TriangleDash IEnumerator.
Iterator methods cannot be reset, even though Reset() exists on IEnumerator
It's not anything to do with coroutines (which are a Unity concept)
it's due to the fact that C# iterators are not resettable
Depends on the particular IEnumerator you are talking about
an IEnumerator produced by an iterator method I.e. one that returns IEnumerator and using yield statements, cannot be Reset
private IEnumerator ColorFade(int btn)
{
release = true;
RoundButtons[btn].GetComponent<MeshRenderer>().material.color = colors[btn];
RoundButtons[btn].GetComponent<Light>().color = colors[btn];
RoundButtons[btn].GetComponent<Light>().enabled = true;
var duration = 1f;
var elapsed = 0f;
while (duration > elapsed)
{
yield return null;
elapsed += Time.deltaTime;
RoundButtons[btn].GetComponent<MeshRenderer>().material.color = Color32.Lerp(colors[btn], new Color32(255, 255, 255, 150), elapsed / duration);
RoundButtons[btn].GetComponent<Light>().intensity = Mathf.Lerp(20f, 0f, elapsed / duration);
}
RoundButtons[btn].GetComponent<Light>().enabled = false;
RoundButtons[btn].GetComponent<Light>().intensity = 20f;
release = false;
colorFade = null;
}
for example
Can you maybe explain what your end goal here is? Resetting a coroutine is not possible.
This one here
Is there a reason you can't just call StartCoroutine(ColorFade(n)) again?
private IEnumerator FadeOut(int btn, AudioSource master)
{
var duration = 1f;
var elapsed = 0f;
var btnColor = colors[waveforms.IndexOf((i) => i == currentWaveform)];
var wfv = wFV[waveforms.IndexOf((i) => i == currentWaveform)];
if (currentMode == "Sustain")
while (!release && !btnRelease[btn])
yield return null;
if (btnRelease[btn])
btnRelease[btn] = false;
while (duration > elapsed)
{
yield return null;
elapsed += Time.deltaTime;
master.volume = Mathf.Lerp(wfv, 0f, elapsed / duration);
SquareButtons[btn].GetComponent<MeshRenderer>().material.color = Color32.Lerp(btnColor, new Color32(255, 255, 255, 150), elapsed / duration);
SquareButtons[btn].GetComponent<Light>().intensity = Mathf.Lerp(20f, 0f, elapsed / duration);
if (masterReset[btn])
{
elapsed = 0f;
master.volume = Mathf.Lerp(wfv, 0f, elapsed / duration);
SquareButtons[btn].GetComponent<MeshRenderer>().material.color = Color32.Lerp(btnColor, new Color32(255, 255, 255, 150), elapsed / duration);
SquareButtons[btn].GetComponent<Light>().intensity = Mathf.Lerp(20f, 0f, elapsed / duration);
yield return new WaitUntil(() => !masterReset[btn]);
}
}
master.Stop();
SquareButtons[btn].GetComponent<Light>().enabled = false;
SquareButtons[btn].GetComponent<Light>().intensity = 20f;
Destroy(masters[btn]);
masters[btn] = null;
fadeOuts[btn] = null;
}
Currently I'm resetting the internal values back to 0 and halting the progress of the IEnumerator when masterReset[btn] is true
You can stop a coroutine
Coroutine inProgress;
...
inProgress = StartCoroutine(FadeOut(n, a));
// then later:
StopCoroutine(inProgress);
// And start it again:
inProgress = StartCoroutine(FadeOut(n, a));```
When I try to extract textures from a .fbx file from blender, I get the error ```
EndLayoutGroup: BeginLayoutGroup must be called first.
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
Anywone knows what I can d about it?
I'm using Unity version 2020.3.25f1
Ha! This is good information. I can see how I could use this in some automated tests.
Ignore the error. (It shouldn't affect you)
I'm glad it helps out! I spent ages trying to find out how to do it :P
But it doesnt extract the textures 😂
Yea! I’m kinda stoked. I usually test coroutines functions in what I dub “integration” tests indirectly in a play mode test. You pointing this out let’s me think about writing edit mode “unit” tests, I think.
im demanding a mention if you post anything about this. I've not done much like this yet.
I've got events in my Character class for things like onNameChanged and onCharacterKilled and so on. They work just fine when another class has a 1-to-1 relationship with a specific character, like a UI Gauge that's just tracking a specific character's health stat.
The problem I'm running into is that I now have a class that needs to track the onCharacterKilled for multiple characters in combat, so that it can remove them from the turn order if they die. Problem is that onCharacterKilled doesn't send any parameters, so there's no way for the listener to know which character died.
I could update all my events to include this information, but it would be overkill for the listeners that only deal with one instance and already know which one it is.
Should I add the parameter of who is invoking the event to all my events, across the board, to resolve this? Or is there another way?
This will probably affect all the other classes I have events in, not just Character, eventually
While subscribing to said events, you could construct an identity of the Character who's event you're subbing to, and pass that into a function which can deal with the actual logic of the listener with the benefit of having the information on which character it is
This is a pretty bad example
But should get the point across
I was struggling a bit on the implementation of that. But that {} notation is probably what I need.
I really should look into that notation more.
Thank you!
Oh, is there a way to then reference that delegate later and unsubscribe when the character dies?
Err, you have to store a reference to that delegate, and then use that to sub and desub
Not the cleanest way really
But, personally I'd just put the character info in the event itself if you are managing multiple characters
or have two sets of events
I might have to in the end.
Also for things like onStatIncreased, I'd need to send back the Stat itself, and the Character that owns that stat, along with the amount
Since Stat doesn't have a reference to its owner, in order to avoid a lot of back and forth linking
But if I did do it this way, would you recommend:
public delegate void CharacterDelegate(Character character);
private Dictionary<Character, CharacterDelegate> characterKilledDelegateDictionary = new Dictionary<Character, CharacterDelegate>();
And later on...
for (int index = 0; index < activeActors.Count; index++)
{
CharacterDelegate characterDelegate = (Character character) =>
{
CharacterKilled(character);
};
characterKilledDelegateDictionary.Add(activeActors[index].character, characterDelegate);
}
And then I'd just += and -= off the specified dictionary entry?
Actually, it's running into issues on -=
character.onCharacterKilled -= characterKilledDelegateDictionary[character];
Since CharacterDelegate has a parameter, on onCharacterKilled doesn't.
And +=, actually. I think I'm setting up the delegate wrong
So even though my delegate had the same signature in both classes, it still said it couldn't convert them. So I manually referenced the delegate in Character, and that seems to have got it to work
A dictionary for the delegate registration/deregistration? What’s going on?
Sorry, slightly distracted. I think I may have just typed out my inner head thoughts out loud. I’m not saying anything is wrong but I did not expect to see a dictionary involved in a delegate assignment
…interesting code now that I’m reading it. Haha okay I’ll keep my thoughts to myself
They want to sub/desub anonymous functions. They need said anonymous functions because the parameters in their events don't have enough information
@flint wind and/or @modest lintel what do you think about inverting the relationship between the two scripts? What if the character game object tell this character, erm, container(?) that it spawned and then when it dies?
I haven’t read enough for heaps of context, but I subscribe to the concept of code “telling other code what it wants to happen.” I’m still reading, so if this suggestion doesn’t make sense that’s cool. I figured I would surface the idea of flipping the relationship as it looks right now (on brief read).
That is what is happening right now. Their Character class is telling other classes that it died, spawned, gained health etc. But its not telling the other classes "which" character actually spawned/gained health
But that interaction is happening based on delegates. It seems like the ‘container’ shouldn’t really care about that?
🙂
Some of my code will happily use the event on a specific item, like a Stat, and work just fine, because it's a 1-to-1 relationship. Like a UI health gauge tied to a health stat. It doesn't need to know what stat is firing the event, since that's implied.
But when I scale up to a larger scale and have multiple items to track, a single listener can't make heads or tails of which item is firing the event. Like in the case of my combat class needing to track dead characters, so they get removed from the turn order sequence. Also if a Character needs to track which of its many stats got modified.
I haven't made a call yet on which of the two methods to do:
- Have the delegates for all the events return the class that is firing it. So an event on a Stat for handling a
onStatAmountChangedevent would return the Stat and an int. - Keep the events as they are, but any class that needs to track multiple instances will have a dictionary of delegates that it subs/unsubs and uses
- Something else that hasn't been mentioned yet?
I usually make custom delegate types that pass the event source. e.g.:
public delegate void OnHealthChanged(Character source, int oldHealth, int newHealth);```
The official Microsoft way is something like:
public delegate void OnHealthChanged(object sender, System.EventArgs args);``` But I don't see it as appropriate for games due to extra GC overhead on the EventArgs instances
And some people of course swear by using Action<X, Y, Z> instead of custom delegate types but that's neither here nor there
I was wondering about that too. My gut was telling me that an EventArgs class being made for every event call was going to generate a ton of garbage. Though i'm no expert on delegates... I was only guessing they don't generate garbage on calls?
I don't think they generate garbage on calls, but I think the delegates themselves do live on the heap and will be GCed when discarded
Action<> didn't seem very nice to use. At least with a delegate you can see the parameter names. With Action<> it's just arg arg arg arg arg...
I agree but it's also not super relevant to the real question here
True
e.g. Action<Character, int, int> would be equivalent to my suggestion
Yeah, I've also read that the compiler treats both Action and Delegate as the same for something like this
In the case of Stat, it would need a reference to its owner Character, creating a circular reference structure. Just returning Stat in the event would not be informative enough to determine who owns it.
Alternatively I could have the container class for all of the stats on a character handle any outside event requests.
So a Stat returns a reference to itself and the amount with its events, and if something outside of the container class needs to know if a certain stat was changed, it subscribes to events on the container class, not the stat itself.
So event -> intermediate listener -> event -> final listener
I like how you consider this important. I do too, because I think our code needs to tell a story.
Using project specific language
Er, context aware language?
I mostly don't want to pick up this project after a hiatus and have to decipher what past me was thinking when I wrote an Action signature 😄
Ha, true! I use automated testing as my way of offloading human readable context off into the code. Revisiting a 6 month old project and reading what tests I wrote offsets the context degradation a bit.
I think the best approach in this case is to have the event return the originator. Adding it saves me the hassle of managing a dictionary of runtime-created delegates, and the only price is extra parameters in the delegates involved.
Okay here’s the message I was missing. Good intuition on pushing back a bit — making a dictionary that keeps track of delegates feels off. Great job on using events!! Yess!
Adding details about what character died into the payload of the event is definitely what I would expect. Even if most listeners don’t care who died, having that sort of information is a good idea. An event should provide a minimal yet enough contextual information to be a story. YMMV
I mean this message
Also events are real hecking cool for tests
So Application.persistentDataPath can vary based on the OS right? Is there any way to instead save files directly to the _Data folder of the build folder or create a folder inside the build folder and save to there?