Yandere Simulator Complete Source Code Analysis - Code Review

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

tldr; It's good to refactor your code so it scales with your project. But when is a good point to todo that.

👍︎︎ 181 👤︎︎ u/tyoungjr2005 📅︎︎ Jul 18 2020 🗫︎ replies

Very educational video. For all the controversy Yandere Simulator gets, I think it’s a good case study of what happens when you commit to a game with a scope bigger than what you can handle. It’s not just the technical issues caused by inexperience, but how the developer has invested so much time into this game that he probably thinks giving up is no longer an option.

To be honest, I’m a bit concerned about the guy’s mental health because I can’t picture this game ever getting finished.

👍︎︎ 307 👤︎︎ u/Nier64 📅︎︎ Jul 18 2020 🗫︎ replies

Enjoyed this analysis, I had shared it over at /r/programming. One of my main takeaways boils down to how you’ll never truly know what is causing your game (or program) to be slow unless you profile and benchmark. I liked how he examined the common assumptions on why the game was slow, debunked them, and offered feedback on how the game could be better maintained in the long run.

👍︎︎ 51 👤︎︎ u/ToughAfternoon 📅︎︎ Jul 18 2020 🗫︎ replies

A while ago I saw this other "code analysis" video that was clearly made by a beginner programmer with a lot of attitude who ended up fixating on a big ugly if-else-tree and how the obvious improvement would be... a switch. And that this was basically the reason for low performance even though the profiler showed otherwise. The rest of the video was basically just playing into the drama surrounding this game.

Nice to see a proper analysis.

👍︎︎ 63 👤︎︎ u/Netcob 📅︎︎ Jul 18 2020 🗫︎ replies

As someone who has spent 2 years on their game with at least 2 more to go... I don’t think there’s anything wrong with undertaking large projects as a single developer, as long as you’re prepared to refactor your code often. Some games just can’t be done quickly with a small team, and that’s fine, but you can’t pretend that the code you wrote on day one will work on day 1,000.

Also, this dude YandereDev has spent so much time creating optional features instead of actual gameplay goals for the player to achieve that I really can’t feel sorry for him. Outside looking in, I can see how some people might think that he’s extended the amount of time he needs to work on the game just to get a few more donations on Patreon.

👍︎︎ 25 👤︎︎ u/lead-holder 📅︎︎ Jul 18 2020 🗫︎ replies

The most glaring issue that I haven't seen anyone mention is that there are not enough optimizations. There is a big difference between performance slowdowns and optimizations (one is negative, the other positive). Notice that in the video none of the fixes make a huge impact on the performance. Even if all perf problems were fixed, almost all systems yandev uses are not optimized for his game. His game is very specific, and has conditions that most games don't have (e.g. 100s of NPCs). By using systems that are designed to be as general as possible, like A* and Unity's component/animation system, he loses out on all the optimizations and assumptions that could be made for his game (ofc assuming he was capable of doing them).

👍︎︎ 21 👤︎︎ u/Retreed 📅︎︎ Jul 18 2020 🗫︎ replies

I'm putting my comment on here as well because god damn it, I put the work in and may as well farm some karma:

Slight nitpick: At 20:43 you say "One Instruction, one clock cycle". This is also a case of may be correct on old/embedded systems.

If we look at the instruction timings in this document on page 27, the SQRTSD ("Compute Square Root of Scalar Double-Precision Floating-Point Value", XMM names instruction names are a real mouthful) is 1 operation, that takes 24 cycles to complete and the result is available after 27 cycles.

So from a micro point of view, the squared magnitude is 24 cycles faster than the euclidean one. In practice, this only matters if you perform the operation millions of times, so the developers of Unity may care about it, but us mortals should rather focus on keeping it simple and understandable.

Because I enjoy doing that kind of stuff, I've gone and written out approximately how long each magnitude function takes. This calculation is of course wholly inaccurate when it comes to actual execution, it assumes that each instruction takes exactly as long as its throughput, no instructions are executed in parallel, etc, but it should be close enough.
If you actually want to base your code on this, measure if it actually matters first and don't complain if you didn't!
https://godbolt.org/z/jq74sn

👍︎︎ 15 👤︎︎ u/FamiliarSoftware 📅︎︎ Jul 18 2020 🗫︎ replies

As someone who actually likes the idea behind the game overall, it's a shame that it seems like it'll never be a fully realized idea. There's no way yandev will actually deliver this project due to his own issues, anyone else who attempts will either be bullied by yandev into ceasing development(YOU HAD SEVEN YEARS TO NOT GET THIS PROJECT SNIPED), or seem to actually dislike the foundational tropes and genres that the idea itself is built off. I hate seeing a unique project like this suffer due to these issues. Some people say yandev bit off more than he could chew, but nothing about the project actually seems... overly ambitions? Can anyone chime in on this specific point and clarify what might actually require so much time to develop in a game like this?

👍︎︎ 35 👤︎︎ u/crim-sama 📅︎︎ Jul 18 2020 🗫︎ replies

On one hand, the dev doesn't deserve your money, he has a lot of difficulty dealing with many aspects of game development and I doubt YanSim will ever come out.

On the other hand, the whole community built around harassing and hating him is frankly unsettling and pretty sad.

👍︎︎ 18 👤︎︎ u/Xylord 📅︎︎ Jul 18 2020 🗫︎ replies
Captions
hmm well well well what do we have here well it's finally here the Andheri simulator code review I have a lot of stuff to talk about but before we begin I want to make it very clear because last time it didn't get through your thick [ __ ] skull no bullying anyway as you can see from how long this video is this video has been two months in the making I started working on this using the may fifteenth build but since then three other builds have come out on June 1st June 15th and July 2nd I've been corresponding with you on dairy dev to fix some of these issues and some of them have already been implemented in these builds so what problems does yandere simulator have players are frustrated with the slow progression of development and how poorly the game runs I spent a lot of time investigating every little nook and cranny of this game the c-sharp compiler and the unity editor in the live stream I looked at D compiled code but now we have the actual elite source code I'm pretty confident in saying that you will not find a better analysis of the performance and architectural problems in yonder East emulator [Music] I need to get a feel for how the game runs in order to know what to look for it's important to note that for my initial benchmarks I used wine to run the game via the Lutra installer and that's why the text is missing I booted up you on dairy simulator avoiding unfocus in the window because that would cause it to crash hopefully a wine specific bug not a game bug after making sure vsync was off and adjusting and Aries brush to the correct size I walked around school right away I looked at the very conveniently placed FPS counter on the right side of my screen and I noticed that I was not hitting 60fps I thought that was pretty strange considering my PC specs now admittedly this initial load into the school scene is very heavy a fade from white is cleverly used to cover up the first few frames where game initialization stuff happens looking at the output of a choppin and vidya SMI we can clearly see that neither my cpu nor my GPU are being maxed out to confirm my suspicions I ran doom 2016 here you can clearly see that my GPU is being used way more I also ran hitman 2 a game whose technical problems are very similar to the Andheri simulator and the same thing happened this could mean that the bottleneck for frame rate in yandere simulator isn't the CPU or GPU but I don't think so because that could just be a wine bug and not an issue with yandere simulator I quickly realized that I would be unable to do anything but speculation unless I could run Unity's profiler on the game in order to do that I would need the full unity project so I kindly asked you on dairy dev for the source code unfortunately he declined and he was concerned when I mentioned D compiling the game and sort of implied that he didn't want me to do it so I did it anyway after about five days of performing a dark magic ritual known only to the compiler gods I had a working unity project free on dairy simulator I think I'm the first person to ever get Y on dairy simulator running inside of the unity of course it's not perfect most of the shaders don't work a little bit of z-fighting here and there who cares now before you ask no I can't redistribute this sorry modders after running the profiler I was pleasantly surprised to find that most of the time was being spent rendering more specifically drawing opaque geometry was taking the most amount of time changing the wireframe mode we can see a lot of geometry like a lot on the students in particular since these models were sourced from volunteers I would be willing to bet that most the artists aren't aware of best practices optimizing assets for games I won't pretend to know either but I do know that the more vertices they are the worst performance becomes for example the time to compute animations depends on how many vertices each bone affects unary simulators assets need to go through a major optimization suite with a particular focus on how much detail needs to be present given the context in which the asset is used for example the school perimeter wall is partly optimized looking from above we can see the wall has zero thickness so this means it should just be one quad since you only need four vertices to make a flat rectangle but at the top of the wall there's additional unnecessary geometry additionally the fancy pillars on the walls have triangles that are never visible to the player optimized assets would not only improve framerate but it would also improve load times memory usage and game size because the file sizes would be smaller however one optimization that is being used is combined meshes this means that fewer draw calls are executed when a build is created unity automatically combines meshes that are only used in one scene and are not referenced by any scripts but this could probably be optimized a bit more manually [Music] in games LOD refers to the practice of lowering the level of detail and 3d models based on their distance from the camera most of the time this requires artists to make two to three alternative lower poly models that get progressively less detailed there are experimental tools to generate these models automatically but I wouldn't use anything this experimental on a real project actual LOD is not implemented in yandere simulator it's partially there via the low poly student feature but this only works for students and it makes it impossible to tell students apart from each other by any other way than their gender honestly it doesn't make a huge difference to frame rates either occlusion culling is where the camera doesn't render objects if they can't be seen AKA occluded because other objects are in the way this is basically a must-have in most games thankfully unity provides occlusion culling as a built-in feature and yandere simulator uses this system the heart rate monitor line is rendered using a separate camera and overlaid onto the main cameras render which is completely separate from the UI camera it uses a unity line renderer to draw the line this is probably fine although it could probably be remade as a shader but shaders are hard the problem is that this camera takes a long time to render a whole lotta nothing this is probably the result of having its culling mask set to render the default layer everything else is on the default layers so the heart rate monitor should go on its own layer and have the camera render only that layer a significant amount of time is also dedicated to physics calculations there seems to be a significant amount of colliders or triggers that are not in use or do not have an apparent use for example the main entrance doorway has a Collider above the door that is not possible to hit this Collider only exists to prevent the camera from clipping inside the geometry so it can be put on its own layer so it only has to check for collisions with the camera the profiler says that animation calculations take 2.25 to two point five milliseconds each frame when the school scene initially loads this is partially mitigated by the stop animation script which disables animations on students that are too far away however I was able to find a few students that were still animating even though they weren't visible some of the character animation components had coaling typeset to always animate and not based on renderers we'll come back to this when we talk about implementation details a common theory for why performances poor in yawn dairy simulator is that the scripts that yawn dairy deaf rights are inefficient we already know that this is mostly not true because a lot of time is spent on rendering however a good 8 to 10 milliseconds is being spent running scripts looking at the profiler we can see that most of the execution time for updating individual mana behaviors is not even that bad the worst functions are you Iraq update and AI based update UI rect is a class from ngoi a UI framework for unity and AI base is from a star a pathfinding library for unity neither UI rect nor AI base classes were written by non-dairy dev although they could probably be used more efficiently let's take a closer look at the UI I bet there doesn't need to be 1600 UI components active all the time I noticed a high number of prompt script instances so I figured that would be a good place to start here the awake function in Stan sheets all of the UI objects required for that prompt all five of them if the button only accepts one button but if it accepts all four possible buttons the prompt will instantiate up to 14 UI prefabs and an additional one if the prompt is considered noisy I reckon we found the culprit if you're looking at optimizing memory usage this is the first thing I would investigate looking where the prompts get instantiated we can see that most of these UI components are already disabled except for the objects that are named letter fortunately this is definitely one of the more easy problems to fix which we'll do later next up is the student script of which there is one call for each of the 85 spawned students on average all the calls to student script not update in one frame have a total execution time of less than a millisecond despite being by far the longest script in the game the next most intensive scripts also take less than a millisecond which are the prompt scripts to use for interacting with objects the dynamic bones which are used for hair or anything else that could jiggle except for tits yond re script which is used for controlling the player and the highlighter which is used for highlighting objects in yonder revision all of the other scripts take less than a hundred microseconds to execute with the majority of scripts taking less than ten microseconds to execute on average so where is all that time being spent I made a quick script to count the number of game objects and components in the school scene there are about 49,000 300 game objects in the school scene give or take a hundred or so on all of the game objects there are fifty 3015 components with 318 types of unique components excluding transforms not all 318 unique scripts run every frame but about 200 of them do most of them only get called one or two times and take less than ten microseconds to execute but with that many scripts the microseconds add up the easy thing to do here is to figure out what scripts don't need to run and disable them until they are needed so far we've only been talking about what happens in the update step there's also about a third of the script execution time being spent on the late update step all the instances of dynamic bone take up 50% of this time and a single UI panel takes up about 40% of this time the dynamic bone updates make some sense but the UI updates are really strange at first the object that was responsible for this UI panel was called timeless panel but after messing with it a little bit the load moved to another object I was very confused how could this even be possible looking for answers I switched a deep profiling and I found that there's about 1,500 calls to update transform and update geometry here looking at the code for these functions there isn't any obvious big loops or anything just matrix methods switching to the raw hierarchy mode we can see that even though there are a lot of calls to these functions only the first call to update self takes a long time to calculate matrix multiplication is taking a lot of time and there's a lot of calls to dynamic lists but after looking at the code there isn't really much that is inefficient on its own the next thing we can try is to reduce the number of calls to these functions one by one I started disabling the panels to see if the number of calls to update transform would go down strangely it didn't really go down as much as I would expect it to turns out I was running around in circles because after I disabled the prompt parent all of the calls went away these are the things that actually matter for performance there's a lot of speculation on the internet about what is bad code or not performing and most of it is wrong I'm going to go one by one through each of these criticisms that people seem to cling on to and validate whether or not they are actually true note that I'm only focused on performance concerns right now we'll get into architectural concerns later by far the most abundant criticism is the lack of switch statements and opting to use else--if chains instead I've been smart three different scenarios elsif versus switch case with one integer input and a boolean output and elsif versus switch case with an integer input and a string output and a switch case versus integer to enum typecasting with one integer input and enum output each of these scenarios are run for ten million iterations the results show that yes technically switch cases are in fact faster than elsif chains but we had to do that ten million times in order to see any difference that was statistically significant and even then it fluctuates a lot nothing in this game runs ten million times in one frame additionally for the first scenario where we output a boolean when the Unity player is built in production mode the elsif chain gets optimized to a boolean expression which would be as fast if not faster than a switch statement therefore this point is at best invalid and at worst naive a fairly common pattern that is seen in the code is a switch case or elsif chain that converts an integer into an enum or vice-versa this is completely valid but c-sharp allows you to explicitly cast integers into enum values again technically this is faster but more importantly it's more dynamic meaning you can add new values to the enum and not have to update the functions manually and you wouldn't even need functions to convert them in the first place so this is an architectural problem not a performance problem by extension this conclusion also means that every little if statement that gets run every frame does not contribute jack-shit to execution time but it's not best practice you say let's take a minute to talk about this because I know some of you guys want watch a full video let's see what it would look like if we use switch cases as much as possible i painstakingly converted as many of the if statements in pseudo script as possible without regard whether or not i should only taking into consideration if i could after looking at the profiler we can clearly see that the difference in execution time is not statistically significant as for how the code looks that's subjective is it any easier to read no not really in fact I would argue that it's even harder to read in some places if the switch case matches integers you have to scroll all the way to the tops to see what variable is comparing there are several situations that resulted in nested switch statements which is a big no-no in my opinion additionally some IDs don't let you fold individual cases which makes big long switch statements even harder to work with a great example of where a switch case shouldn't have been used is here for the logic of report phase for students with the teacher's pet persona already we can see that a switch case doesn't really capture all of the logic required for the report phase but that's not the worst of it look at how long the switch statement is look at me follow through cases are required to be functionally equivalent to the LCF chain this is because switch statements only work based off of exact matches this is also why you shouldn't use floats in switch cases if it doesn't exactly match that any of the cases it goes to the default case hang on stop writing that comment stop writing that comment yes I could have made the default case containing that if report phase is less than a hundred statement like this but that's another if statement and it would completely undermine those precious performance benefits and we can't have that oh and a little side note here if there enough cases in an else--if chain it gets optimized into a switch case anyway the compiler is smarter than you this one's actually true any frequently used components should be cashed as private instance variables in a similar vein calls to any form of game object not find or any references to camera not main should also be cash so does the code use any of these in the update functions of objects in the school scene no not really otherwise we would see it in the profiler Yan dairy dev appears to have already taken care of the low-hanging fruit another common point is using vector three distance in update functions because calculating the square root is slow one of the ways to fix this is to use a distance function that does not use square root like using the square magnitude of the vectors or using the Manhattan distance in embedded systems ARM processors and ye olden days of game development this is true but on modern CPUs in normal computers this [ __ ] is a myth it is a myth this is no longer true I specifically tested four different ways of calculating the distance vector three distance aka the Euclidean distance the Manhattan distance a minus B dot square magnitude and a minus B dot magnitude which is functionally equivalent to the vector three distance my results showed that there was practically no difference in execution time between any of these most of the time vector 3 dot distance was straight up faster than everything else the reason that square root operations don't matter anymore on modern desktop CPUs is because the operation is computed with a single CPU instruction this is the source code for the GCC compiler and right here in the square root function a single instruction is being used one instruction one instruction one clock cycle you can't get faster than let's talk about actual performance concerns code that runs every frame should avoid operations and functions that allocate memory as much as possible and if memory allocation is required it should do it all at once here are some examples of stuff that allocates memory string concatenation Lin Q expressions like anything that makes an array and instantiating and destroying objects in a similar vein the code that runs every frame should also cache things that take a long time to process for example getcomponent game object not find camera dot main c sharp reflection and path lining results in yon dairy simulator both of these concerns appear to already be taken care of now that we're done talking about performance for the time being let's jump into the game's architecture now there's no denying that this game is a mess even yandi read everything so but in my stream I focused a little bit too much on cosmetic style things rather than architectural problems let's take a look at how yawn dairy simulator is structured most of the score important stuff appears to reside in yonder East crypt student script and student manager script Before we jump into this I would like to point out how yawn dairy script and student script are a few thousand lines shorter than the ones I showed on stream this is a great demonstration that line count doesn't matter I used a different decompiler this time around and this D compiler likes to remove squiggly brackets around one line if statement clauses it's also generally more accurate as you'll see later on when we talk about decompiler artifacts Yan dairy script and student manager scripts are actually completely fine to be the size that they are given their complexity and the context of the game of course that doesn't mean they can't be improved student script is another matter this component handles the behavior of every person who is not the play including teachers and the nurse for anyone reading the code this is a little misleading but more importantly this also means that all persons specific code gets stuffed into this one massive component like I mentioned before this isn't a performance concern per se but rather a and architectural concern when each student is initialized some of their properties are read from streaming assets students Jason this is a good thing because otherwise the student script would be at least 2,000 lines longer 20 properties times a hundred students additionally this file is read by the student manager and the student manager applies these properties as the students are being instantiated if this wasn't the case then each student would have to read from the same file iterate through the list searching for the matching ID and finally apply these properties for those of you don't know file operations are very expensive to perform to open a file the program has to make a syst call to the kernel when the open siskel is made the thread stops executing to allow the context switch from user space in the kernel space check permissions and whatnot and receive a file handler of course the details vary between operating systems anyway the problem with this approach for students is that any student specific behavior is muddled and interwoven with generic student behavior let's come up with an alternative that would enable the separation of student specific behavior from generic student behavior first let's define what a student needs to do actually what will call them people instead in order to function a person needs to store some state more specifically to store the state of their appearance and stats like hair color health strength personality stuff like that they also need to store their normal routine and they need to store the state of their current activity and Kern objective like painting or dying also every person needs to be able to complete their daily routine react to the player committing crimes according to personality react to blood weapons dead bodies according to personality die and become a rag doll when dead what kinds of people do we have we have students with a special rival subtype a special suit and council subtype and a special delinquent subtype we also have teachers the nurse and the coach excluding the guy named counselor because she's not a real person even though she has a student script component attached it's disabled now we can start laying the foundations of our new architecture let's start with a person's routine a routine is a chronological list of activities a person's routine could also change depending at ease contain the start time and a method that defines how to complete the activity the name of the activities conveyed via the class name finally activities are initialized via their constructor and must implement and on activity start method and on activity end method the advantage of this approach is that it allows us to define unique routines for each person if we wanted to we could also generate random routines but that's not a design requirement this will also allow us to separate logic used to update appearance like determining what animation to play from brain type logic you know the part that gives the illusion of intelligence additionally this will allow us to override the activity at any given time based on the state or based on special event triggers like the player pointing their phone camera at them further building upon this activities will not modify the person's state directly instead there's a think method and a do method the think method modifies the state and the do method acts upon the state this will allow game logic and other logic for animations and such to be separated in addition this will allow activities to be easily converted to take advantage of the new unity job system should that be necessary the job system automatically takes care of offloading processing to other threads avoiding some overhead involved in spinning up new threads and providing a nice API an additional benefit is that we won't ever have to check the state to update the animations every single frame because we can do that just by checking the state whenever it's committed now that we have this foundation set all of the remaining requirements are pretty trivial to implement reacting to seeing a crime blood weapons dead bodies can be implemented in a special activity that gets executed no matter what the current activity is this activity would commit state when a person is alerted to trigger an override of the current routines activity dying and becoming a rag doll would stop the execution of all activities for that person I'm not saying that this architecture is the best possible solution there is certainly a lot of room for improvement for example this architecture won't work well for activities that require multiple people this is merely an example of what it could look like instead instead of what it is currently this is a fairly object-oriented approach which I know isn't the hot new design paradigm but given the problem this felt like the most natural solution [Music] now we're going to talk about implementation details what that means is how specific features are implemented some of this comes down to personal preference but also there were a lot of features that were implemented poorly and I want to explain it precisely why that is when you make a save in Andheri simulator it creates a yawn save file in the same directory where the game is installed where most of the information is stored this file is pretty hefty and contains a lot of bloat and saving to one slot creates a file that is one megabyte large if you make multiple saves in different slots this number increases inspecting the contents of these saves reveals that information is stored as JSON this is not usually a problem but given the absolute size of these lads it would be better to use a custom byte format instead since JSON is a text-based format a lot of bytes are wasted on squiggly brackets quotes colons string representations of floats and property names in addition there's a lot of float that doesn't need to be saved in the save data because technically it could be calculated on load based on the state for example camera position remember that bit I talked about in the hypothetical architecture where the state of a person is copied before changing it the same concept could be applied to saving and loading but that file isn't the only place where save data is stored some save data like cork board photos or school atmosphere seems to be soared using player prefs a unity class intended to be used to store the players preferences like video options this is a problem because on Windows Player press are stored in the Windows registry this makes it very hard for players to backup transfer or share saves [Music] interaction button prompts are implemented in a way that is not very dynamic the current implementation has a unique instance of prompt script for each interactable object including any GUI elements required to render the prompt this is why the prompt script even shows up near the top of the profiler when a prompt is being interacted with the interactable object itself checks to see if the circle is filled before triggering the code that's run on interaction here's how I would implement this system instead now this is one of those features that is deceptive it seems easy to implement on the surface but it's true complexity reveals itself when you actually go to implement it I would start out by creating a component called interactable this component would be placed on any object that needs to be interacted with interactable handles the interaction input from the player and triggers the on interact event when the circle is filled for each object that is interactable a new component is created or an existing component is used to add an on interact method that handles the event now this may seem pretty simple but this is actually a pretty naive implementation so far there are a few problems with this there's no indication to the user that a given object is interactable if it's not in range and if there's too interactable objects in range to prompts appear when only the one that the user wants should appear this is where that hidden complexity starts to reveal itself the first problem can be solved very easily and in many different ways so I'll leave that as an exercise to the viewer the latter problem is a little more interesting to fix this we can add an additional field to the interactable component to indicate whether or not it's the closest then the player can calculate the distance between it and the interactable x' and set which one is closest based on the player's position after doing a little bit of profiling we can see that this implementation is very fast even with hundreds of interactable objects but there's still more that we need to do with the system to match what the system in yandere simulator does since only one prompt needs to be visible at one time we can save ourselves the trouble of instantiating and destroying the prompts by just having one available and disabling it when it's not needed while we're at it we can add little flavor text for what the interaction will do finally we also need to be able to prompt for more than one button at a time let's see how it performs now it's still super fast we can have up to 2050 interactable objects and still get 60fps as long as we look in the opposite direction this also further proves that vector3 distance is very fast the very first time I open the school map it was incredibly laggy that's what led me to investigate the implementation of the school map here's how it works there's an orthographic camera in the center of the map pointing down the size of the cameras frustum spans the entire map the camera can show different floors by moving to preset positions on the y-axis the camera also defines a short enough clipping distance so that other floors won't get rendered when they don't need to and it only renders objects in the default and top-down layer according to its culling mask the benefit of this implementation is that it's very easy to do and doesn't require a whole lot of maintenance nor upfront development time it's actually fairly common development practice the downside in this scenario is that the default layer is being rendered and most objects reside in the default layer most games have an image they use for the matte background instead so they aren't rendering so many objects you might be thinking oh I'm making changes to the school map like all the time if I change the map to be a picture instead I'll have to take a new picture every time I update the map thankfully unity has a feature where you can write editor scripts that run only in the editor these editor scripts allow you to automate most repetitive or tedious tasks or let you augment your user experience you could easily compare them to macros you can make an editor script to render a frame from the map camera to a file and just have the map render that image then you can just run the script when you build your game or better yet automate your build pipeline too so you can include all the steps needed however considering that the map is not open every frame this is probably not an issue but it could probably be improved with proper LOD there are a bunch of empty classes that just inherit a generic class for example int asset and int and string dictionary I thought this was weird at first but it turns out that this is actually a workaround for a unity bug that doesn't allow generics to serialize on top of that this bug was just fixed in unity 20/20 point 1.0 a three yandere simulator runs unity version twenty nineteen point three point seven F one and it would not be worth upgrading to a buggy pre-release version just for this bug fix therefore this is not a problem [Music] so I want to preface this by saying this is going to sound a a smidge nitpicky puppet fields on components for individual items and arrays are used pretty heavily a large portion of these cases are used to store a bunch of game objects or components looking at the students script in the inspector can show how this practice has gotten a little bit out of hand just look at all this [ __ ] it's really hard to find what I'm looking for when I'm scrolling through this massive list of editable fields but this can be solved by a little bit of organization and I think the decompiler messed with the order of the fields a little bit now this isn't a bad thing regarding performance but it is a terrible thing when it comes to maintenance and adding new features most of these references are aesthetic and what I mean by that is a person has to manually drag and drop each and every one of these fields to fill them out most of these fields are required in order for the component to function instead of creating a new instance of the component normally it's usually just easier to copy one that you've already set up but this is pretty error-prone but wait there's more there are copious amounts of hard-coded references to specific indexes in these arrays this is technically faster than searching the list or using any variation of the built-in game object out find functions because it's runtime complexity is o one however this practice does make it very tedious to work with these components but I'm not done yet the majority of these arrays are treated like they are one indexed no arrays should be treated like there are 1 index arrays because arrays are zero indexed in c-sharp now I actually have a theory as to why this is still a case after all this time ultimately it's probably an artifact of when the code used to be written in JavaScript JavaScript is a dynamically typed language and the way it treats arrays is kind of weird for context in JavaScript objects are treated like dictionaries you can assign an object's property by just doing it JavaScript treats arrays like an object that can only accept numbers as the key a common issue that now as programmers have is the concept of zero indexed arrays a zero indexed arrays first element can be accessed using the index 0 a 1 index arrays first element starts at the index 1 many novice programmers gravitate to wanting to use one indexed arrays because you start counting at 1 however most languages use 0 indexed arrays and JavaScript is no different the reason that 0 indexed arrays are preferred is that the index refers to the offset from the arrays starting memory address this started in the C language and patterns arose that were just preferable to work with now JavaScript arrays are zero indexed but you can put any number you want for the index in an empty array and JavaScript will fill in undefined for all of the previous elements either one of these problems on their own would be fairly easy to fix but the problem is that a fix for both of these problems would require a lot of work because changing the code to treat these arrays as zero index would also require changing all of the hard-coded references so how should we write code to avoid these problems the most obvious tip in this case would be to use 0 indexed arrays because that's how c-sharp was designed but what about all those hard-coded and manually assigned references the answer is that it depends for example in my interaction system demo notice that I find all the interactable objects in the start method and cache the results in a private instance variable in this case I want to be able to add more interactable without having to mess with something unrelated another example in yandere simulator lots of components require a reference to the player component in this case it would be better to just have one reference to the player component in a static class that is globally accessible it all depends on the specific context of how these references are used how often they are used and convenience this is probably the biggest problem with the games code looking at yan dari script there are 130 public bully ins and students crypt has 196 public boolean's most of these are dedicated to just keeping track of State now the naive way to fix this is to just put all the boolean Xin a single enum and call it a day unfortunately we can't do that because sometimes two of these boolean z' need to be true to properly represent the state currently the part of the students state that is represented with boolean Zoar enums is character attributes like gender whether or not the student is a teacher whether or not the student is in a club what club they're in personality stuff like that the characters appearance like are they wearing club attire or are they wet interpersonal relationship status whether or not the teachers Trust has been lost whether or not the student is in a relationship or whether or not the player has complimented the student whether or not the student is alerted and the cause eg whether he heard a noise or something whether or not the student has witnessed anything and what type of crime was witnessed whether or not the student is suffering and the cause like bleeding or poison the current action being performed like eating changing clothes dying and the current objective of the action like turning off a radio or apprehending the player we can already see that this is pretty complicated and I'm sure there's stuff that I missed as previously demonstrated in C sharp you can assign numbers to enum values and cast between them at ease the side effect of this is that you can combine enum values with bitwise operators let me explain what this means okay so originally I did like a really long tutorial thing here and it was way too long and boring so instead of dude who really fast you can assign values to the enum that are powers of two to make each enum value correspond to a bit in the binary representation you can use this to do bitwise operators on the enum values so you can represent two enum values at once by setting the bits and the number to make this a little bit easier to think about you can use bit shift operators to assign the values to the Ino now we can apply this to the witness state currently the witness state is stored in an enum called Student witness type and it looks like this instead of having values like blood and insanity we can assign each one of these values to their own bits and use a value of blood bitwise or insanity honestly the way that the state is currently represented doesn't exactly transfer very easily to enums despite what everyone really wants to believe the current implementation doesn't exactly lend itself to what is trying to accomplish that well so instead of trying to represent the current state but as state better let's redesign how students observe and react to the world building on top of our hypothetical Architecture from before our hypothetical architecture actually pretty much solves this problem most of the students state can be inferred by the action for example we can infer that a person is in a heightened alert phase if their current activity is investigating a noise and we can infer that a person has witnessed a crime if their current activity is reporting the crime like I mentioned before the profiler says that animation calculations take 2.5 to 2.5 milliseconds each frame when the school scene first loads my benchmark showed that the more complex your animation systems get the faster the current animation system Beckenham becomes over the legacy animation system however the legacy animation system is way faster for simple animations I'm honestly not sure how big of a performance benefit this would be though in the context of yandere simulator more importantly a common pattern in yawn dairy simulators code is that first it plays an animation then after a certain amount of time passes code gets run to update the state or other game objects in my opinion it would be a lot cleaner and easier to work with to use event triggers in the animation clips to run code instead just to make sure this works in both the legacy system and mekin and I even tested it out with this cutie I found on the assets or God look at that cutie multi-threading in games is really hard and if you aren't doing it from the start it's even harder fortunately unity has been putting a lot of work into new systems to improve performance in unities games by default called dots or the the data oriented technology stack unfortunately to take advantage of this new system in the Andheri simulator practically the entire game would have to be rewritten because the ECS entity component system is fundamentally different from how unities classic component system works however I wouldn't recommend using features that are still in preview but I think the performance and architectural improvements would be beneficial to this game in particular this may warrant using a different engine or using a custom engine however the new job system for dots is able to be fairly easily implemented into existing projects the job system lets you really easily send stuff to another thread to be processed the catch is that you cannot directly update any unity objects from the jobs and you can only pass in data that is little meaning that it has the same representation in memory in both managed c-sharp code and behind-the-scenes unity native code so it doesn't need a conversion you have to put all the data you want to work with in a struct pass it to the job and get the result and then reapply your results to the game objects and components there are some scripts that are great candidates for this kind of an upgrade the UI library and geo I could probably be rewritten to take advantage of jobs the pathfinding library a star which is already multi-threaded would be a perfect candidate to rework to use the job system AI path which inherits AI base is used to move objects along a path that has already been calculated it's a perfect candidate to be refactored into a parallel for transform job which is specifically made for moving a bunch of objects in jobs by far the best candidate is the dynamic bone component because all of the data that it works with is already in its own class called particle unit tests are little pieces of code that make sure things work as they should the purpose of these automated tests is to make sure you don't introduce new bugs or reintroduce old bugs by accident unit testing is really uncommon in game development I think this is really unfortunate because adequate testing has caught so many bugs for me in my projects before they reach the end user unfortunately most of Gandhari simulators components change the state directly and this makes them pretty difficult to test I saved the best for last let's take a look at how the FPS is calculated frame rate script calculates the FPS over an interval of 500 milliseconds this does not properly account for spikes in FPS as a result the FPS that is displayed is a lot lower than what is actually being rendered there's a very good reason most games display the current average minimum and maximum frame rate doom is an excellent overkill example funnily enough if another hour so were spent on the frame rate script it probably would have saved them like a few years of grief [Music] for the sake of being as comprehensive as possible I'm going to go over what is and what isn't a decompiler artifact and some general tips for writing better code is string concatenation yes only a plus B string concatenation and string interpolation is affected string dot format is not affected why they're basically 0 constants and tons of hard-coded values well that's because the compiler resolves all constants to their literal values at compile time this means that every constant is basically find replaced with their value for example there is a class called annum names that contains all of the animation names as constants this is actually good practice because hard coded strings that are used to affect program flow are a really bad idea you don't want to be debugging a typo for hours why why the hell is everything explicitly cast the the decompiler did that I don't know why you could implicitly catch them in it would be no different well what's up with these really precise float literals used in these comparisons floating-point numbers have to be stored in a fixed amount of bytes specifically single precision floats are four bytes long and double precision floats are eight bytes long in order to fit these numbers into these bytes I Triple E used black [ __ ] magic back in 1985 to cram as much information into these bytes as possible and this standard is called the I Triple E 754 floating point standard unfortunately the ritual used to pull this standard out of some point in ears ass wasn't perfect as a result only seven places after the decimal can be represented for a single precision and 15 places for double precision additionally the numbers can get rounded as demonstrated here these are called floating-point rounding errors and they are 100% unavoidable our string comparisons slower than comparing a types technically yes but like if statements versus switch statements it doesn't really matter in c-sharp strings get hashed into an integer and then get compared and this is a lot faster than checking each character in the string this is also how dictionaries where the key is a string work why why the hell are there's so many goddamn in line if they're so hard to read I think you can probably guess what I'm gonna say it's a decompiler artifact they're just they're functionally equivalent to if statements anyway it doesn't matter there are a bunch of while in for loops that reference the instance variable this dot ID instead of normal for loops with a local variable this is actually not a decompiler artifact the D compiler can recreate both using an instance variable and using a local variable accurately this means that this must be intentional i I don't know why you would do this well why is this loop a while or a for loop it should be the other one the.net intermediate language does not differentiate between while loops and for loops so the d compiler has to guess which one it's supposed to be based on the context since we have the leaked source code now we can actually criticize style I'm going to go through this very quickly because while I don't know why you would torture yourself everything that I'm gonna display here is completely valid and purely cosmetic meaning it does not affect compiled code performance at all it's just kind of sloppy explicit checks for boolean 's equal true or equal false these can be omitted string concatenation instead of string interpolation string interpolation is much nice to work with extra indentation in the cosmetic trip lots of little loops to disable cosmetic could be made in one big loop inconsistent use of tabs and spaces [ __ ] pick one using this dot ID an instance variable instead of using a local variable in for loops seemingly random concatenation of string literals ridiculously deep indentation student globals thought memorial students should be an array it's just easy to work with again why would you torture yourself remember creating new arrays allocates memory not using existing ones comments that describe what if statements mean when the conditionals should be able to explain themselves without comments using the unity editor compiler flag to disable asana instead of using a custom compiler flag this is bad practice because if you want to create a standalone build with those Hana enabled you have to manually edit the code this just makes it take longer fro sana to come out we made it so what does this all mean anyway yandere simulator is a perfect textbook case of how technical debt influences a project technical debt is an industry term that refers to engineering decisions that are really quick and easy to implement now but may cause more time to be wasted later on over the years it's felt like development has grinded to a screeching halt and after really reading the code and really digging into it I can see why looking at different parts you can kind of tell what Yan Derrida was thinking there are parts that actually make a lot of engineering sense given the architecture but a critical aspect of managing technical debt is knowing when to refactor it becomes kind of a sixth sense that you only gain from experience the game's architecture needed to be refactored way back in 2016 now especially with asana being functionally complete that's not really an option good luck you Andheri dev you're gonna need it if you made it this far thank you so much for watching it really means a lot that people care what I have to say it's pretty amazing how my yandere simulator stream blew up I want to thank everybody for over a thousand subscribers here on YouTube 150 followers on Twitch and almost 100 followers on Twitter it really means a lot to see all the support that I've gotten recently subscribe for more stuff I'll see you guys next time subside since they saved a little bit [Music] [ __ ] subscriber he gets it my editor gets it
Info
Channel: dyc3
Views: 628,056
Rating: 4.8910766 out of 5
Keywords: Yandere Simulator, anime, game dev, game development, optimization, code, code review, C#, .NET, Unity, game engine, yandere sim, programming, indie game, software, yansim, reverse engineering, yandev, yandere dev, YandereDev, fps, framerate, performance, lovesick, drapeis, slow
Id: LleJbZ3FOPU
Channel Id: undefined
Length: 57min 2sec (3422 seconds)
Published: Thu Jul 09 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.