[GameDev Stream] Designing for Change (6/27/2020)
Developers discuss strategies for designing flexible, maintainable game code with a focus on movement mechanics and level design in Unity, including encapsulating logic into classes, using interfaces, and implementing grid-based movement.
Video Information
Title: [GameDev Stream] Designing for Change (6/27/2020)
The livestream focused on game development strategies, particularly designing for change, flexibility, and maintainability.
Charles and Jason discussed a game project involving a sheep character, exploring movement mechanics and code refactoring.
The importance of writing unit tests before refactoring code was emphasized to ensure existing functionality is preserved.
The session highlighted the use of interfaces and the strategy pattern to allow easy swapping of movement implementations.
A builder pattern was introduced to simplify object creation and testing, allowing for more flexible and maintainable code.
The concept of null object pattern was discussed as a way to handle missing components without throwing exceptions.
The importance of using source control, like Git, for tracking changes and managing code versions was stressed.
Level design principles were briefly covered, focusing on creating engaging and intuitive game environments.
The session included a giveaway announcement for a copy of Quantum Console, an asset for Unity.
all right we are live hey everyone welcome to another game day of livestream my name is Charles this is 0:29 infallible code and I'm joined today by Jason story y'all know him know him and 0:35 love him by now today we're going to be looking at some code again we're gonna 0:41 be taking a look at a game I've been working on you may have seen it in some of my previous videos I'm a little sheep game that I'm making for my wife 0:48 and we're going to take a look at some strategies that Jason and I use to 0:53 design for change design for flexibility and maintainability and all that fun 1:00 stuff so
we'll talk about what that means in a little bit but before we get started just wanted to mention that I'll 1:07 be giving away a copy of quantum console thanks to the developer he's a great guy you may have also seen him commenting 1:13 recently did a video on quantum console and he was gracious enough to hang out in the comments section and answer a lot 1:19 of questions so check that out definitely and I'll be dropping a link 1:25 in the chat for that around 15 to 30 minutes into the stream if you did not 1:30 catch this live you're watching this vlog right now don't worry this particular giveaway I'm gonna let this
1:36 go for a week so we will liable announce the winner on Twitter on July 4th at some point maybe even July 5th just 1:44 because it's a holiday and I know people are busy so just join in there I will drop a link it's it's like we've done in 1:49 the past we'll take you to a website you don't all you have to do just enter your information I won't keep any of that 1:55 it's just so I can contact you when you when I you know choose the winner so yeah so how you doing Jason it's been a 2:03 while since we did one of these yeah yeah pretty good with everything going
2:09 on oh yeah a lot of working from home yeah yeah 2:14 it's it's been interesting been taking a lot of time to to study and learn you 2:21 know I think I went through a period of a couple years where obviously doing the channel I'm teaching and I really do 2:27 think that teaching is one of my is one of the best ways to learn are really like so defy your knowledge of anything but I 2:34 hadn't really gone through a period where I was like okay I'm gonna dive in on some subject so yeah I've been doing 2:41 that I know you do that pretty often anyway I I do I pick a
topic every so often did this for the next I think a 2:47 month or so I'm really interested in the concept of game and level design no I'm 2:52 slightly hearing away from the program when I do that all the time so I thought for a bit of fun yeah 3:01 become more well-rounded developer hope you cut out there for a second I not but 3:07 by the way Jason Jason's internet is he's getting some better internet and where he's at but literally installing some good at 3:15 don't worry hopefully be one of the last times I'm fading in and out of existence 3:21 yeah good yeah so that level design stuff is really cool
you're sharing me 3:26 with me a video and so you will see when we switch over to unity that I have like a little sheep level and it just it's 3:34 just bare bones and and a lot of it is just I'm copying what I have an asset pack and I just copied what was in the 3:39 asset store as far as the examples go they always give examples but I watched a video that Jason shared with me about 3:45 level design and I can link that in the chat later but you'll see at the end of 3:50 the stream I one of my commits is after I've applied what I learned from that and
I don't know I think it looks pretty 3:56 good so cool stuff there all right well without further ado let me just check on 4:02 the chat I see a lot of has always familiar faces your eyes oh your eyes or 4:09 I always butcher that I'm sorry but thank you for being here zero-one what's up what's up man has 4:14 gone all hey cool and everyone else who's uh who's here when does Jason make his youtube channel 4:20 he has one yeah he's a great playlist 4:26 though yeah I know I I'm actually kind of at a point now where I've got a bit 4:32 of free time and I should really start putting
out some videos I might I might just sort of spike the bullet and make a 4:38 few 5 10 minute videos just to start it off in the next month or so we'll see 4:43 you know promises as long as you're consistent Beck and I will say though if anybody's interested 4:49 I just share one thing possible for me to share my screen with this set up chairs uh I can send a screenshot yeah 4:58 my point my point is if anyone's worried that you know I'm not at least somewhat 5:03 thinking about the YouTube channel thing here's a little image that shows what the ideas I've been playing with video 5:10 send it over
to me I'll pull a pull up on the screen switch over to the game view here 5:15 unity view this just gives you an 5:26 external mic gotten round of which 5:31 fights they're gonna attack this is you know this is Jason's mind map this is what's going on in his worrisome stage 5:43 I'll start turning these little topics into articles or videos I just happen to 5:49 pathfinding that's that's something I wanted to dive into next as we'll see him what the code we're looking at today 5:54 is like I did some grid-based movement and the next thing I want to look at is how can I do like a star path finding 6:01
you know with that implementation things like that so that'd be cool that's cool cool man 6:07 yeah oh and I can see the more game develop you type things here item design you see the cluster in the middle is 6:13 currently what I'm working out there's a lot of stuff to do with player psychology and how to design interesting 6:19 abilities and possibility spaces and that kind of stuff nice alrighty then well let's dive into 6:25 the code again I'll be dropping that link for the competition or the competition 6:31 the giveaway soon but for now let's talk about some code here so again this might look familiar if you've seen some of my 6:37
latest videos if not be sure to go do that but this is a game that I've been working on for my wife it's kind of a 6:44 long story inside joke between us but it's a low ship game and right now it's very simple just press play here it has 6:51 very basic movement and I'm using do tween to to do like this hopping 6:57 jump and you'll see that in the code but basically what I did here for this movement I don't even know this gonna 7:03 work okay I don't have a box Collider on this but I'm using ray casting to do 7:09 movement and obstacle detection let me go ahead and just
drop a grid on this is 7:18 that gonna be too complicated yeah so fence not agree I'm gonna drop a box Collider here make sure this works yeah 7:28 there we go all right let's see if this works so you have your sheep hops around now it looks like it's grid-based 7:33 movement but you're gonna see in a moment that it's technically not it's just I'm hopping and I'm I'm being sure to 7:38 only hop in one world unit at a time I'm not actually like adhering to any grid 7:43 but I would like to adhere to a grid so let's just take a look at the code here 7:50 switch on over to writer
though I know this question always comes up in the comments what is this thing that I'm 7:56 using here how does it know that I'm using the this script is being used in for unity assets and why is my name 8:02 appearing and all this craziness this here is an IDE called writer and as much 8:08 as I wish that I was I am not sponsored by them but I do highly recommend it it 8:14 has great great integration with unity so and I know a lot of people probably 8:20 use Visual Studio and maybe have heard of resharper it's the same concept made by the same company this is just an 8:27 entire IDE
that is basically you know what resharper gives to you and there's 8:34 so much more all right so you'll see in the code here just the top bar in the sheep class and the Sheep is the Sheep 8:40 class is what's responsible for controlling sheep now you you won't see 8:46 any input in this because if you go to the player class the player class I've kind of delegated that responsibility in 8:52 its update method it basically just takes some input based on the waz D keys and then calls them dot move on the 9:00 sheep so it has a reference to a sheep which it pulls from its cell phone game component and then
it delegates that 9:07 movement into sheep so if we click into there you can see it's not too fancy I have a 9:15 state variable that just makes sure to keep track of whether or not the sheep is currently moving if it is we don't 9:20 want to we don't want you a lot of move move twice so just just a brief pause here because because the one thing to note when when talking over a lot of 9:27 architecture like this is that we're kind of skimming over a lot of ideas like it may it may seem simple to say oh 9:32 we take Direction only positive to move but thing is there's actually a
lot of very interesting design decisions that 9:37 have gone into just that sort of seemingly where do I draw the line decision and scores worth noting the 9:44 idea of having a sheep with a move method that retrieves the direction is an arbitrary because what that lets us 9:49 do is if you think about it anything can generate a direction so you could take a sheep put it in the scene 9:55 give it another script called you know sheep that always move who's left I need 10:03 you to collected on sheep that always generates the left vector or left direction so the idea is that sheep only 10:09 knows how to move it doesn't decide
where to move and something else will give it that decision so in this case by 10:14 having that separation between player and shaytans you have a player who is generating input which has been turned 10:20 into a direction which is then handed to a sheep in that way the player is simply generating data and then that data is 10:26 handed to the Sheep and so you could have anything generate that data and it means that you can have different characters controlled by a player or you 10:32 can have different controllers like AI or you could have the player control multiple sheep at once and it doesn't affect either-or right there's no 10:38 rewriting required to
expand that functionality so it's worth thinking about where your division lines are in 10:44 your application and right now a thing that moves and the thing that generates input which happens to be a player are 10:50 two separate entities that work together in this instance to work to the same effect right and we can see that I actually 10:56 have an NPC class that has some very rude Ameri rudimentary logic that just 11:01 basically looks for the player and has the NP sheep moved towards the player 11:07 and you can see here that again yeah I just call a sheep dot move so there's definitely this is like what you'd call 11:13 I guess the
system boundary where you would decide that did like Jason just 11:19 said the decision to where to go is decided a completely different system than the sheep who just is responsible for moving 11:25 itself in in world space and you're familiar with us already if you've used either rigid bodies or character 11:31 controllers because they follow the same kind of concept which is a character controller knows all about surface 11:37 collision and calculating normals and all that stuff that it needs to do and your job is to hand in a vector and say 11:42 go that way and you don't have to do all the math to calculate and reposition your vector to make sure
it's aligned 11:48 with the surface or anything else like you just handed a vector and it knows how to use its own internal logic to turn that into a movement and so in the 11:54 same way with rigidbody you handed a force and it will figure out how to aggregate that force apply it as an 12:01 acceleration and you know do all of the work for you so as long as you figure out what's the bit that's that's 12:06 isolated that's likely to change you can you can make your code a lot easier to work with and in this case yeah it's a 12:12 it's effectively is sheep character controllers but you've made you know
yeah so that's a good point thanks for 12:19 bringing that up and if I if I gloss over anything like that definitely want to stop and make sure we talk about that 12:24 another weird thing you guys might see is this direction that was another design decision and I explained it I 12:29 think in the previous stream but just for those who didn't catch it just a class that encapsulates the idea of 12:35 moving north south east and west all the cardinal directions actually and then I've just encapsulated you know what 12:42 that actually means as far as moving one position in world space so that that 12:48 allowed me to get away with a
lot of this grid based movement quote-unquote because I'm not actually using a grid 12:54 yet so if we go back to the Sheep I'm actually showing this code like a little 13:02 deeper into its history because when I first wrote it it was prototypical and 13:07 it was not designed for change it was just I was I always slammed into this one move method but I've since gone to 13:14 the trouble of actually command capsulate on that into this method called get destination so get 13:20 destination makes a couple intelligent decisions about obstacles and then 13:26 allows the sheep to move in the way that I wanted to move so we can see here that
again I've even capsulated a little bit 13:32 more so it steps through it says hey we're gonna get the desk this is the destination where the sheep will 13:37 go so right now it just starts that transformed opposition so in the worst case scenario the Sheep is gonna stay 13:43 put so first two checks hey is there an obstacle is there not an obstacle in the 13:49 vertical direction if there's not then we'll go ahead and we'll add that to direction our destination same for 13:55 horizontal and then it just returns the destination and then if we look into obstacle exists we'll see that's where I 14:00 actually do the raycast and I know velcro
you asked is there specific reason to divert a cast 14:06 other than using a quick box overlap I just didn't I'm just more familiar with Ray casts and I didn't think too deeply 14:12 on it but that probably would have been a better solution I'm aware of the Box overlap thing I just I never used it so 14:19 this is my first time really doing anything like 2d so yeah I just slammed in the 2d ray cast and also because I'd 14:26 note with that is you've got a function called obstacle exists that takes in a direction and turn the terms of pool right now the strategy for that 14:31 implementation is a right cast
and you could literally encapsulate those two lines of return something that does about the overlap instead and it was 14:38 pretty easy to change so I'm telling 14:43 systems like this you don't forget a right first time you just have to make it work and have it be available to 14:48 change like it doesn't matter which application you pick as long as you're aware which lines of code are likely to 14:55 change you support that going forward so for example right now the change from using overlap boxing versus using the 15:03 raycast versus using basically any other application is those two lines and everything else in the entire system 15:09 still works the same so
it doesn't matter what you pick it matters that you make sure that that's easy to change if it's a system that's likely to change 15:15 yeah so this is I guess this would be strategy number one for making your code 15:20 resilient to change would be even in a single class try to isolate certain 15:26 logic that could be a change point like for instance when I when I wrote this I I'm depending on unities raycast 15:34 built-in ray cast but I didn't know if this would always be the case you know and even now like the floral came 15:41 through and said you know what ray cast is really unoptimized and if I started
noticing like some issues with my game running 15:48 slowly that were caused by this then would say okay well now I can figure out a way to optimize this and maybe that 15:54 box Collider method or the box cast method is more performance so that I 16:00 could easily change it so yeah strategy number one even inside of your own classes now and with the funny thing too 16:07 is that I made this decision based off a readability I mean granted there was 16:13 it's obviously I'm reusing the same logic twice so I didn't want to repeat myself keep it dry but also I mostly 16:20 just want this code to be easy to read
but what I got out of that was the 16:27 ability to easily change it as well so and a couple of notes on that one for people who are curious about the symbol 16:33 on line 49 um so basically that is just a standard not equals but if you're 16:38 using certain fonts to the concepts called font ligatures and he's put a space between them so it's those two 16:44 symbols but when you merge them together they change and that's effectively some fonts have sort of nicer or simpler ways 16:51 to indicate certain symbols so sorry so fun yeah this case there's a lot of 16:56 stuff we do repeatedly in code that starts to
get a bit visually every so so 17:03 humor to merge them that's just a personal preference and then on the on the point of encapsulation like this I I 17:10 did a stream a while ago and I made a method in a class called log and all it 17:17 did was called debug log and somebody asked me why did you do that it's already debug log you can already call 17:22 print there's no reason to encapsulate it in something else um and the point I was making was that at some point later 17:28 I changed that from being a log print to being a Xmas pro displaying on a label 17:33 and when I made
that decision I had one place two changes inside of my own print function rather than doing is in like 17:41 having to go through the Tanners twelve places that I wrote debug log and replace them all out so it's not that it 17:47 may seem arbitrary to put a wrapping around a method that you know you're using but if you know it's a likely 17:52 point to change that one little decision to say you know what I'll just make one method name for it wrap it and use it in 17:58 all these places because I know it's an explosion of like it's going to be used in 30 places well if I change this I
18:03 change into any place otherwise I use my own method and then I change it in one place it's just very small decisions that make your life 18:09 easier yeah yeah exactly yeah so so the strategies there again 18:16 you want to isolate the code that will potentially change into you know methods 18:22 is one strategy what we're gonna see in a moment is another strategy is to actually push that into classes which I 18:29 guess that's the strategy pattern you call it that where basically you're taking you know I guess algorithms and 18:35 putting them into individual classes that you know maybe implement an interface so that you can just talk to 18:41 the
interface and then the implementation changes based on the needs of your project all right so 18:48 without further ado I think that's the next thing we're going to talk about because as I wrote this code I thought 18:55 you know this works and this was written during a period where I was trying to find the fun where Jason and I were just 19:01 trying to knock out features as quickly as possible originally I had wanted to do some 19:06 grid-based movement where there's an actual grid in memory that holds the position of the sheep and you know I 19:12 would do off cycle detection that way but you know I didn't I had never really
19:18 worked with grids in that way and um I didn't really want to go down that rabbit hole just yet I wanted to get the 19:24 thing working I wanted to see if it was fun and I mean to be honest it's just movement so I know that's fun but I was 19:31 just for me I wanted to say hey I want to get this little sheep hopping around the screen I had never made a sheep hop right and as I again this is kind of 19:38 like an inside joke with my wife but I always described the game that I would make for her and I would always say like 19:43 she'd always say
I want him to hop I want him to hop around the screen so I was like okay well let me just get that you know let me get some brownie points 19:49 with a wife real quick and as you saw we've got this dude jump here which is part of the dude the dot took do tween 19:55 library tweeting library and so we got that but like I said I want to do grid-based movement so the next step is 20:02 how do i implement that well implementing that's gonna require me to refactor this whole class right so the 20:10 first thing I wanted to do is so that I made sure that I didn't break
the 20:15 the sheep logic was I needed to write tests that was important to me and I 20:21 think it should be important to anyone that once you've established what your basic game mechanics or your business 20:26 rules are that you and cap you capture those in unit tests for instance one of 20:31 the things that I wanted to make sure is that when the Sheep got against an obstacle was like rubbing up against an 20:38 obstacle I don't know how to better way to say that but I wanted to still be able to move in the in the diagonal 20:46 direction and still kind of move across if that if that makes sense so
right now 20:51 I'm hold I was holding in the diagonals keys and it was still kind of going down until it got past the obstacle so I 20:58 wanted to make sure that I captured those sorts of details so let's switch up and go to the next commit here's 21:05 another important thing I should mention as well as you work on code I highly 21:10 suggest you use some sort of source control obviously get is the de facto standard and in cat and capture every 21:18 little bit of iterative work that you do in a commit and so even if you don't 21:23 push your code to a repository in the cloud even if you
keep this local which 21:29 you can do I don't know if you if the lot of people don't know that you can still commit and now I can go back and I 21:34 could see the history of the things that I did so you can see here that in in this commit here I added collision 21:40 detection in this next commit I updated to dot tween Pro and then this next commit this is when I wanted to go ahead 21:46 and start doing some work on that grid implementation I added test coverage for sheep movement because I wanted to add it I wanted to 21:52 add test coverage before I did all my work so
two notes on this while you're here one of them I'm really glad to see 22:00 that you're following one thing that a lot of people that frustrate me when they just get through is if is basically 22:06 a snapshotting tool right take this folder at the state it's in and and you 22:12 know make a copy of it so that I can always return to this state and so when you're naming your commit messages you 22:19 were naming it as what a what it was or what that's not what that's not just what you did to it and so rather than 22:26 naming it in the present tense because that doesn't make much sense when
his name when you have stacked commits like this 22:31 if you name it in the past tense you're saying what this what this snapshot added or what this snapshot removed etc 22:37 so it's very clear because of the tense naming between these different commits what each one will do so you can you can 22:43 grab one of them and say oh this is the one where the collision detection was added there so on so wanted to so 22:52 whenever you're using commit messages I suggest previously using the past tense but then also being very verb 22:58 heavy so fixed or refactored or implemented or capsulated or renamed that tells you what that did at the
time 23:05 at which you made it and secondly if you're worried about the effort involved 23:10 in like setting up yes and doing all this kind of stuff if you're using either Visual Studio or writer it'll do 23:16 it for you there will be a checkbox to say create a repository and in the case of something like this I don't know how 23:23 you actually make your commits Charles but the way I do it it's I just press ctrl T and write with the word commit and it'll pop up inside of writer 23:29 control company what's your search bar the one that shows them using Visual 23:34 Studio I have a what the right yeah
that one there just to miss the top okay 23:41 Center and it'll bring up the thing type in your message is and it just commits whatever current message is there so it 23:46 doesn't have to be laborious separate concept right like you create a project set it up with yes and as Charles said 23:52 they can be local it's not like you don't have to go to some kind of cloud service it just means the folder you're 23:58 in will mean oh it's just a place to 24:03 start later on yeah make a github and set it up there if you're if you're a 24:09 good starting point is just simply make sure that even
on a simple project commit regularly in the past tense and 24:16 you know it's not that difficult to do even have it others yeah yeah definitely 24:21 and you know I edit eight it's a hard 24:26 it's if you've never used git I'm not sure what the right approach to learning it is when I've learned get I was very 24:33 particular about one to learn all the command line you know you know commands and all that and for a 24:39 long time I refused to use a GUI application we're always doing from command line it 24:44 oh I would open up git bash and do it like that but with a tool like Ryder I
mean it is so it's such a pleasure you 24:51 don't have to know anything about anything all you need to know is hey you're gonna make a commit everything 24:57 that it'll all show up here the changes you make like if I say this is a change you'll see that I have the change here 25:03 you can even do really cool things like this you can go into your diff here and 25:11 you can select which ones you actually want to include in a particular commit which is you know kind of more advanced 25:16 but you have the ability to actually commit on a line by line basis but basically once I've made this change
you 25:22 can say added comments to the sheep class you know you get the idea and now 25:28 well sheep class and now you actually have this captured in time you hit commit it gets added to this list the 25:34 stack of commits and you can go back to it whenever you want as you saw that I just did so yeah I highly recommend 25:41 learning again and I don't know I feel like what do you think you think people should go to the trouble zone in the command line so they understand what's 25:47 happening behind the scenes or you think we're at a point with something like riot or where you can just kind
of work at it at work with it at a high level I 25:54 would say what you're doing first thing I would say starts easier so there's 26:00 actually like to get a github app that's sort of a language right it climbed and 26:05 that'll get you started with like three clicks get you a repo and do all of the setting up the remotes for you and stuff 26:11 and that I think that's a good place to get you started with the ideas and then once you're comfortable with it and you hit your first commit issues then it's 26:18 time to go down the rabbit hole but I will let me say I wasn't I
guess where 26:24 it's doing I don't think it's a good place to start because it's necessary it's one of those things where your goal is to make sure your files are safe and 26:30 then you go off on a tangent for a week or two learning yeah like it's worth doing with just being pragmatic about 26:36 what you need to do is probably a better approach to start yeah that makes it and I said again for people talking about 26:43 the names again and jazz the idea is the commits you're making is is a snapshot 26:48 of what the code looks like now but the reason I personally don't like again it's entirely preference it
doesn't 26:53 matter whether you make past tense the present tense or whatever consistent it's just my personal preference past tense but for example 26:59 once examples of Jat is you could name a khmer style map and add demo scene and that's fine that's a perfectly fine 27:05 commit name but the problem I have is that if you are reverting back to fixed I'll map and a demo scene that 27:11 conceptually sounds like you're adding a fix to the tile map and adding a demo scene when if it's fixed how map and add 27:17 they're all saying that means that sounds like you're going back to a time when you fixed a very subtle change but
27:24 it's one of those things where I think for people who are when you've got a lot of commit messages with a lot of things going on short very imprison tense 27:34 oriented can get a bit confusing it's the wait am i doing this is this before that happened or after that happened but past tense tends to imply when you pull 27:39 this one down it'll be the commit with which that event had occurred but it's again very small subtle things this was 27:45 really matter that much just whatever you pick same as conventions in code just be consistent with the rest of your team yep 27:51 yeah consistency is by far the most important
unless you're unless your consistency is like you come up with 27:57 some sort of numbering system I can I 28:03 can live with you putting like maybe an ID to a JIRA reference or something if 28:08 you've got yeah if you're using some sort of task system or whatever then 28:13 fine I do that oh I do that I work and it's nice because when you get in there you can actually click around like it'll 28:19 actually take you to the build machine if you're on the build machine you can click it'll take you to the ticket so that's definitely good but most people 28:26 if you're if you've got a really big build environment
and you're using like the advanced Wiis and they're using 28:31 slack for teams and all this kind of stuff it can be pretty cool because what happens is when you make a commit a commit message is automatically dropped 28:38 into a slack channel update people are mmm-hmm what's going on which are paying 28:43 for the item of the to-do list that you wish to eat mark lee we're doing so people can look at it see that you've done something see this complete click 28:49 the link which brings up your your JIRA board or whatever which lets you look at what the item was which looked at the 28:55 time estimates which looks it's not you
can like really put it all together that's really good for a team that keeps everything in coordination yeah but 29:01 that's a lot lot further along it unnecessarily compared to just getting it done very basics just add yes to meet 29:09 you last v5 that'll get you 80% of the way there yeah yeah totally I see a lot of 29:14 comments about unity collab I used it a long time ago when it was very fresh and it was it was it was okay yeah but to be 29:22 perfectly honest yeah I see you can't even do branching branching is something I use pretty often you can see I'm using 29:29 branches here or I'll create
a branch for each one of my videos because it allows me to go ahead and make the example project that I'll post a patreon 29:36 and not have to mess up my actual master branch but even for like this grid based 29:41 stuff if I wanted to do some experimental things and but I didn't want to mess up my actual productivity I 29:48 can make a branch off where I'm just messing around with a code and I can have commits being separately done there 29:54 but if I need to get work done on the actual project I could still be uninterrupted there yeah and then 30:02 there's other stuff too like if you've got a
new person who joins the team and they do not familiar with the codebase it's often a good idea to just like copy 30:09 the code base and call it you know Martin's copy and a half let them go nuts they can delete stuff add stuff 30:15 play with it was a really good learning exercise and then once you once you get into more enterprise stuff again this is 30:20 concept called git flow and get flow as a particular strategy with which to apply get branching and lets you extract 30:28 out features work on them merge them back in and keep with consistent golden path through your code there's a ton of this stuff that gets
really interesting 30:34 and complicated but but again it's not not super relevant just to this stuff 30:39 I'd say okay all right well we are thirty minutes into the stream here so I 30:44 promised I'd drop a link for that giveaway if you don't mind before I do that slam that that like button or 30:51 invoke the light but button there so there so that people can also get an 30:56 opportunity to find this stream and then perhaps you know sign up for the giveaway as well so I'm gonna drop this 31:02 in the chat go ahead I will say just yeah yeah and on a side note about the 31:08 lab as well I
do I do actually use collab so I'm not I wouldn't I need to I 31:18 need to revisit collab actually cuz I like the concept of it I like that it's all in unity and 31:23 but I haven't touched in a long time and I see that it uses get on beat under the covers so that's cool 31:29 yeah in general like it depends what you're in my case I'll often work on client work where there's two to four of 31:35 us in the game majority artists and I'll find that what works well for me is I'll have a little 31:44 project that's just a c-sharp class library and I'll work on that I love
my own gift on my own meaning for it and 31:51 then what I'll do is I'll copy the dll into the main project and that's the 31:56 artist's version they're ours and let's new version my code and then the integration of code to ours is also 32:02 kind of versions by the proxy whenever I add my own DLL committed it might be a 32:07 bit redundant but I kind of find that you don't but once a muddy it's happened 32:13 at a different rate than than code changes and oftentimes they're never there any reason why you do it and the reason you want to do with them are different so putting them all together 32:20
into one giant commit list especially when you can't do branches is a bit frustrating so I find you can keep your 32:26 code to yourself and then you can add in the bits that change yeah yeah all right 32:32 we're gonna move on here someone asks how long the stream goes for we're probably go for about another hour so we 32:39 usually go an hour and a half he as Dustin said so all right so all right moving on with this story of my working 32:46 on this game I wanted to encapsulate the I wanted to move my logic into a more 32:52 grid-based movement but I wanted to get tests first so here you're
looking at the tests that I created and there's 32:57 some decisions that I made in writing these tests actually it's just the latest version can we see here yeah am I 33:05 on this one let me check this out and make sure I got the right one yeah okay so basically you can see here that I've 33:11 tested from moving horizontally moving diagonally vertically into an obstacle 33:17 horizontally into an obstacle I don't I don't want to go too deep into this but 33:22 the idea here is that I've got a test for each one of those use cases that I describe described do you see diagonal 33:28 into a diagonal obstacle diagonally into a
vertical obstacle so just wanted to 33:34 capture all that let me let unity reload here and now you can see that in my test 33:41 runner make sure you can't see that because I know our faces are sometimes in the way I know we're good you can see 33:47 my test runner down here in the bottom right we can run those save that scene up and God willing they'll pass there we 33:55 go so these are these are slowly going to become more integration tests and 34:00 they our unit unit tests but we'll see that in a moment so with that out of the way with that all that tested now I can move on
to the 34:07 actual work so that's just one thing I suggest as you go to refactor make sure you have tests for the code that you're 34:13 going to refactor so we can skip this one fixed I'll map and create a demo scene here extracted movement logic into 34:19 its own class so I as you can see I don't want to just dive into implementing grid based logic what I 34:28 want to do first is move all of my existing logic out into its own class so 34:35 that it's out of sheep and the reason why is because we want to set up ourselves set ourselves up in order to 34:42 turn this into an
interface that our sheep class can talk to and we'll see 34:48 that in a second so you can see here that I've created this physics 2d movement and and then I've stored it as 34:54 movement then I attach it to the game object and I grab it using getcomponent and then moving down to the here's the 35:03 move logic you can see we still call get destination but now we're calling get destination on movement so I've just 35:09 I've taken that exact same code that exact same fun function call and I just moved it off and delegated that to 35:16 another class so all of that logic that was associated with it the plumbing that
we did let's go over there 35:22 the plumbing such as let me see here oh 35:27 I must have done some changes oh I remember I renamed it to can move instead of is there an obstacle but 35:35 basically it's the same idea the physics 2d ray cast is there and then all that 35:41 logic we did is is pushed into here as well it looks different because I didn't like what was a call obstacle exists I 35:48 just felt that that was weird I didn't really it didn't relay the message clearly so I renamed 35:53 it can move which I think that's way more obvious like can you move there 35:58 okay yeah so
so that was just a design decision strictly for readability that's 36:04 all I did that for but it's all the same stuff so now that we've moved this into 36:09 this physics 2d movement you can see that I had to update my tests so if we go to sheep tests here you can see that 36:17 Oh actually didn't this happen yesterday when I was explaining this to Jason because I added this type of physics 2d 36:26 movement as a required component now just by adding a sheep to a game object that component will be added so I didn't 36:33 even need to change my tests everything is above board so forgive me if I kind
36:39 of glossed over that if there's anything in the chat you guys want to bring up that you want to look at but just the general idea here is we took existing 36:46 logic and threw it into another class yeah so the basic idea and established 36:53 from start of this video that we were that when we make a character in this case a shape that can move at the core 37:00 level something generates input so we'll say he impact someone's pressing top left on the analog stick their intention 37:07 is to move up and left right that's their left that's their intention so they can hand that to any character and 37:14 that character
will figure out how using its own internal logic to move up and left and so up until now there has been 37:20 one method for doing that and that would be the particular method that Charles 37:26 was mentioning just adding adding a position to that and so by encapsulating that idea into a movement strategy it 37:33 now that the pipeline goes inputs generated handed to a sheep she chooses his strategy which in this case is the 37:40 default one and that will then move using that strategy and this means that while the input provider can change the 37:47 characters themselves can change but you can also decide to change what strategy of character uses so
the classic example 37:53 from the head first programming book is you could have a duck and a duck can 37:59 flower to fly our duck with a jetpack attached 38:04 to it it doesn't matter the end of the day you're saying hey duck here's the key but I want you to take here's the direction I want you to go in and 38:10 whatever whatever method that decides to move is up to it and so in this case by 38:16 by keeping the same contract with the chief character you you maintain that 38:21 same logic you had before of decoupling import from the character but by internally using a different strategy 38:26 you are giving the
Sheep the ability to change out implementation irrespective of either the import of the contract 38:33 with the player I have Walker I'll mention that he saw 38:38 he saw a bug with my movement I was just trying to see if I could figure out what it was but we don't have to dig into 38:44 that I got the test I don't know the test it seemed pretty good to me but hey 38:51 I missed something it is possible but all right so we've delegated the head we've delegated the 38:57 movement to this class called physics 2d movement but of course this is the thing that we want to change right I want to 39:04 change
physics 2d raycast just some sort of grid implementation that just looks at a position on a grid 39:10 so what the next step in this is now I need to pull this physics 2d movement 39:16 and I need to derive an interface from it so I have a commit for that but I can 39:22 actually show you what that would look like so in writer there's the ability to and I know guys don't you know like 39:28 cling on to this too much you can do this by hand but in writer you can actually do creates missing interface 39:35 extract interface extract interfaces the word so if we click extract interface it's gonna ask me
what do you want to be 39:42 the members and really the only members that I want to expose our get death is get destination that's it 39:48 because can move that's just some plumbing that this particular implementation has so I'll go ahead and 39:56 hit next and then we can see now we have this it's called eye physics 2d movement I shouldn't have named it that let's go 40:03 ahead and rename that what's the what's 40:08 the hockey there it is we're gonna call this eye movement because it's just that's just how movement is implemented 40:14 so change that so now I said before this is the movements track yeah all fi has a 40:23
thing that can move another thing you have to win handed a direction figure out a vector but all it takes to be a 40:29 movement implementation right in this case that our current one is the one underneath which is the physics 2d move 40:35 yes so now we have this it's basically it's implementing eye movements so now 40:40 in our sheep class now what I can do is instead of instead of having physics 2d 40:46 movement be the movement variable I'm just gonna have in reference the interface instead and this is actually 40:53 part of the solid principles of actually writing two implementations so we're 41:00 gonna go ahead and remove this required component for
now because now it can be anything and also this this very clearly 41:07 represents open closed principle because what this demonstrates is we now have a shape where it's using a certain 41:13 movement method and we may want to change that so it's it's open for that 41:20 ability for us to change it but it's closed for us to change the code itself because all we have to do is provide a 41:25 new strategy and the actual implementation changes without the code actually gene right so in this case 41:31 right now we're filling it with this gate component with a physics 2d movement but at the end of the day we 41:37 can change that
by really passing in anything that implements eye movement so 41:42 let's go ahead and move up the commit chain here I'm gonna you can see where I created an interface for movement and so 41:48 let's check that out and here's where I actually did have to check make some changes to my sheep tests let me switch 41:55 back to unity real quick it's probably gonna need to reload okay make sure that 42:03 looks good all right perfect right so let's go back to writer and then we can 42:08 actually take a look at those sheep tests and you can see this is this is probably a negative comment about unit 42:14 testing but like I
said these are starting to turn more into integration tests and they are unit tests because 42:21 realistically I can start moving towards a place where I can write a test for 42:27 physics 2d movement in instead of writing a test for the sheep eventually I'm gonna move all of the 42:33 quote unquote business logic the real game mechanics that I care about into their own individual classes and as long 42:40 as those classes are tested then it really doesn't matter what sheep does because sheep is just an aggregate of a 42:46 bunch of different will become an aggregate of a bunch of different implementations that it just delegates 42:52 to so that's just I know
some people will see this and be like oh my gosh 42:57 look at that every change you make you have to update your tests and it's like yeah you're right but I don't eventually 43:02 I'm not even gonna need these tests you know so I'm getting coverage and for right now and then later I will just 43:09 were factor them out and I'll continue to have coverage moving forward there's also there's different Saturdays good 43:14 it'll avoid a lot of this complexity as Charles will point out at some stage fader is things like the Builder 43:22 patterns and avoid coupling certain issues I will show that next like for 43:29 example one reason my tests
can be a little bit of a pain at this stage of development is right now there's a test 43:35 has three parts to it it's the arrange the arranged act and assert and the idea is in all the pieces together choose 43:42 what you're doing with them and then verify the status is expected after the action is repaired and in a lot of cases 43:48 your range for is the same thing so in 43:54 in the case of testing with a chiefdom you you could arguably have a space kind of a nested level beyond sheep test 44:00 called the sheep movement tests or sheep Direction test you can encapsulate that and the thing that's different
is right 44:05 now I don't know if it's even true it can you do setup and teardown and yeah 44:11 you can yeah you can yeah so so conceptually idea is if you were if you 44:17 were acting on a movement system and oh yeah I'm doing right here yes you could 44:22 effectively take those in those set up pieces of a sheep and physics system or whatever and move it as need to end up 44:28 only having one or two lines inside of each test now it's not necessary at this stage but the point is is when you get 44:34 to the point you're like well what happens when I've got 500 tests and
I change around the code and I have to rewrite my test well that's a failing in writing cat so 44:41 to get to that stage there are strategies to avoid that happening and that basically boils down to move as 44:46 much of the same things we did earlier movement was much of the the set up logic into one piece just like I said 44:51 before about the log versus debug log and that way if that changes if you change your contracts for your for your 44:58 interfaces or you change the the way setup or whatever you can do that in one 45:04 place and all of your other tests will be fine with it because
each each test should only test one or two lines of code so it is entirely it can be I'd say 45:12 90% avoided I think the biggest issues is if you outright rewrite classes and honestly I think that's a fine case like 45:19 it doesn't matter if you get to the point where you're completely rewriting your code base of course your tests will change because your requirements have 45:24 changed when up to that point it's it's not as bigger problems I think people think it is yeah yeah there's there's 45:31 man I forget what it is but I think I'll go Bob or Steve Martin one of them comes up has like an example of
how I think 45:39 it's like accountants have to do like a double check on their books just to make sure your bookkeeping that's that's 45:46 that's Bob Martin's go to only says that I'm if you treat programming as a craft 45:52 just like accounting would be is accountants write it write everything 45:58 twice and to make sure the balances at the end and if you care about your work as a programmer in the same way because 46:03 let's be honest bad code can literally take lives if you if you've written code 46:09 for a supposed to be for calculating 46:15 dosages for some medical application I mean I'm not saying everyone's in that situation brave
but if you conceptually 46:21 at least acknowledge that code can be lethal it's just good practice to treat 46:26 it with a little bit of reverence and careful you know if that's the case then his argument is double double account 46:33 bookkeeping is the equivalent of you write your code you write your tests if they both qualify at the end and they both match and you've got two two sets 46:41 of accounts to make sure that your and and this may be a little woowoo but how 46:46 many times have you come across a bug and it stressed you the hell out like I've had some really bad long nights 46:53 trying fix something and
you know I know the argument I've heard this in my day job 46:58 that is an even game development where we deal with code that is you know deployed to power companies where it's 47:05 like hey we're not making a medical app here we're not trying to shoot something you know rocket into space like it's not 47:11 gonna kill anyone so that's the argument for why should we waste all this time writing tests but in that same situation 47:17 I torn my hair out trying to fix something that man if I had just taken the time to test it like if I would have 47:24 never had to deal with some of the problems that
I had to deal with so for your own mental health you know what I mean like why not just take the time to 47:32 craft your code it makes you feel more confident about what you're working on it allows you to really solidify your 47:39 the knowledge of the stuff that you're writing it's it's I don't know I just I 47:44 really struggled to find any pro that outweighs the cons of the time you need 47:49 to write those tests and and to be honest on a personal level the reason I 47:54 like writing unit tests more than anything else it's a lot of people think it's dogmatism early on board everything 48:02 must be
perfect in code it's not about that at all what what really benefits from my perspective is if you write unit 48:08 tests you have this this checklist that your code works and there's this whole 48:14 concept of mental memory or mental Ram where you better certain amount of things you can keep your head at the one time and making sure that oh I have to 48:21 make sure that the input still works just make sure the UI still works I'm sure that the things still function and every time you change your code you have 48:27 to go through this mental checklist oh just double-check everything still does it's gonna fall by the wayside and 48:33
something will break but if you can put all that stuff in tests then you don't have to keep it in your head anymore when you want to verify the coastal 48:39 those aren't supposed to you press on tests and you end up saying does the database call still work does the move 48:44 and still work does the screen still draws like good I don't have to mentally keep that in mind to check every time 48:50 and that freedom then gives you the freedom to play with your code and this is one of those really interesting things that people don't realize it's 48:55 quite powerful is what do you do at the moment when you come
across an old message that you know you didn't write 49:01 or they're part of an API that you don't know or that was written so old so long ago that you don't member what it does 49:06 normally you're afraid of it and you don't want to touch this because if you touch a little break and you won't have to fix it now having source control is 49:12 one thing that'll let you feel comfortable enough to try playing with it but you're still worried if you break it you break the entire application but 49:18 if you had a suite of tests you could feel more comfortable playing with that double-checking the tests think they 49:24 still
work and you can actually explore that code without fear and that's kind of the real benefit in my opinion of having tests for your code is that you 49:32 actually all of a sudden aren't afraid of unknown code anymore it doesn't become this thing of oh there's a big block of scary code I don't want to 49:38 touch it's more like I can try I mean ever change like make it still does 49:44 exactly what it did before I change the code and this gets even more code when you add stuff like continuous testing or you could have a separate monitor 49:50 running and all the tests run every time you click Save you keep
writing code say hey Freddie saving you just the minute 49:56 anything turns red you stop for a second undo something double check it turns green again and try again and you get 50:01 this continuous cycle of guarantees that your coastal works very very cool yeah takes a bit of time to get used to that 50:06 but it's it's a cool situation to be in yeah I think it's worth it it requires discipline but something that I you know 50:12 quote that I believe in is that discipline is freedom because you have the freedom to do all the things that 50:18 Jason mentioned you can play around you can set up another branch and try out
new things and explore new avenues that 50:24 you may not have previously because you were too scared to touch the code so so 50:31 alright I meant I saw mentioned in the chat builders and Jason mentioned as well so I'm gonna show you a skipped 50:37 ahead one commit will go back but I wanted to show this builder that I created because I knew that I would want 50:43 to create a sheep eventually that used the grid based movement because that was the whole point of this thing is that I 50:48 wanted to implement grid movement and so I'd want to test for that so I decided to create this builder so you can see
50:54 here there's this it's it's some static class called a dot sheep dot with 51:01 physics 2d movement so I mean it reads so great I need a sheep with with 2d movement our 51:07 physics 2d movement and how did I make that I don't want to dive too deep into it but if we go and take a look at what 51:13 a is and and anyone who's interested yes I would make an unfit was a class that 51:18 started with an A was Apple I'd make a one called unsewn I could say an apple but basically this 51:25 returns a sheep builder so if we go in and take a look at what sheep
builder is it it basically when you create a new 51:33 one it creates a game object and it stores it in this private game object member variable and then as I add 51:39 methods so we have the width and it takes in a component I'm basically adding components to that game object 51:46 and then finally there's a method called build and that's where I finally had the 51:51 sheep and then I returned I return the actual sheep now there if I needed to do 51:57 more complicated stuff like for instance if sheep had some variables on it I would have to do this earlier on but in 52:03 this case I could get away with
just kind of making a shortcut and when you 52:08 call build you do you can add the Komodo component of them but as we can see I've 52:14 also added an implicit operator that casts sheep builder to a sheep and what 52:20 that does is it just called it calls the Builder dot build and that would return a sheep so that's why I'm able to and 52:27 this is getting a little low level but that's why I'm able to cast a sheep to a sheep it's really casting the Sheep I 52:34 still don't know why you do that I prefer sheep sheep equals and then not having to apply the oath oh this yeah 52:41
just because you know you have to do the past anyway I think you're right well it's a yeah it's all matter of 52:46 preference but you're right you're absolutely right that's one way to to skin the cat I'm just a sucker for bar 52:54 that I always just do it instinctually [Laughter] question about how do you introduce 53:02 tests to a large legacy project well one 53:12 byte at a time so the truth is you pick a system the smaller one preferably and you say write 53:18 this chunk of code when I do this it returns for how did this it returns and if I call this function I should get it 53:25 true but
if I passing this information first and return false and you just write a test to verify that it works 53:30 once you know that it works and you're comfortable you can possibly find factored a little bit because now is kind of safe with what it does and then 53:36 you just keep doing that like you don't have to turn you know it's a look at it like an entire code bases oh my god this is a nightmare 53:41 quite frankly the Pareto principle or the 80/20 rule usually is in effect here there is about I'd say 20% of your code 53:48 base which is mission-critical compared to everything else there's usually something that is your
business logic or your the thing that like if this breaks 53:55 a lot of stuff breaks and all of a sudden everyone is to stop working with to take things offline and start like panicking but the rest of it you know 54:02 there's glitches in the UI and stuff and it doesn't really affect any data it's probably annoying what it's not the end of the world so take the 20% that matter 54:09 and slowly take pieces of it and just wrap it and very kind of comfortably you know play with it there's actually a 54:14 really good book it's literally called refactoring I forgot like enterprise 54:20 application or something yeah working working effectively with
54:26 legacy code mm-hmm it's the the Michael feathers book really good it'll it'll actually kind of 54:31 show you how to take an existing system and slowly by the way it until it's got easier to work with so I recommend that 54:38 one it's one more time that's working effectively with legacy code by Michael feathers yeah and my strategy has been 54:46 cuz I actually do work on legacy code in my day job my strategy has been in the past like I let I let change Drive where 54:53 I need to where I start adding you to tests like for instance if I get a ticket to work on some legacy service 54:58 deep in
the application that's what I'll say ok well if I'm changing this I need to write a unit test for it and then 55:04 I'll let that drive the decisions the decisions at least of what I test first how you approach it you know that that 55:10 book is gonna be hugely beneficial to showing you how to do that I'm real 55:16 quick before we dive into the next little bit here and we're almost at an hour I wanted to drop the giveaway link 55:22 in the chat one more time and also ask you guys if you could just invoke that like button if you haven't already just 55:29 got to do that whole YouTube ish
feel um but um for those who came in a little later we're giving away a copy of 55:36 quantum console and that's a really cool asset for unity I have a video on it I should have had a link prepared for that 55:42 but if you go into my channel and look up quantum console actually probably if you just look up 55:48 quantum console on youtube I might come up as one of the top hits check out that video it's a really cool asset if we 55:54 have time maybe I'll show you guys about it alright so the next piece here is I 56:00 actually had to make a modification to a movement signature in
preparation for that grid-based movement so this is just 56:05 kind of show the advantages of being able to very quickly make certain design 56:11 decisions a little much easier way so with the grid I wanted to be able to 56:17 update I know I'm gonna have to update the grid first and then get the destination I don't want to do it all in 56:22 one shot with physics 2d movement it's all kind of in real-time I do the raycast and I get the destination but in 56:29 this case with the grid I need to do some logic on updating the grid and then 56:35 I can get the destination based on what the new state
of the grid is so that 56:40 kind of makes more sense to me because I had already thought this through but basically just a very simple change I do 56:46 I'm back to this so that I'm updating the position and then finally I get the 56:52 position so all I'm doing is the logic just updates a local a local private 56:58 member field called position so that's what happened there and again I have my 57:03 unit tests on I'm able to rerun those tests and confirm that sure enough it does work and if we go to my sheet class 57:09 the only difference here is that I call movement update position and then I get
the destination after calling calling 57:17 that another cool thing is we I implemented the null what does it called 57:24 oh man I'm no object pattern and Jason 57:30 actually shares a cool idea about this I guess the naming of it or actually putting into an instance variable but 57:35 just to really quickly tell you what that is with unity you don't wanna you don't want to throw exceptions you don't 57:42 want to be either doing try caches and things like that you just wanna as to the best of your ability handle any error case Eric cases 57:50 or situations that might throw an exception so in this case what I've done is I try
to get an eye movement 57:57 component off of the game object if one does not exist on it that means that it was misconfigured so the person 58:04 who was setting up this unity project was unaware that sheep require the some 58:09 sort of eye movement on the same game object so in that case what I've done is I've created a null movement 58:15 implementation and it doesn't do anything so if we click and look at that 58:20 it implements eye movement and literally every time you call update position and get destination it just logs a warning 58:27 saying hey movement has not been assigned to this game game object which this is probably overkill
to be honest but it's gonna blow up your console with 58:35 messages saying hey hey you you have a sheep that doesn't have that doesn't have movement and then that's it that's 58:41 all it does then it'll augs another warning I'm gone the awake and the idea here is we don't want the game to fail 58:46 and throw an exception but we do want to you know let the developer know that something has happened something is in 58:52 the wrong State yeah so so to put things in sort of a real-world perspective that we have in 59:02 reality you may think we do but we don't really so the Gumble of this would be if
you if you walk into a living room and 59:12 there was no couch there the difference is if there was no couch there imagine 59:17 from a programming perspective the fact that there's no couch there causes a black hole to appear and that's what 59:23 Knoll is for an application it doesn't just mean there isn't something here it means this is broken the fabric of the 59:29 understanding of how this entire application works there is an absence of anything and that can be very scary from 59:35 that perspective so when you're doing something that it's not mission-critical as that thing exists sometimes it's 59:41 easier to just have a not a couch there instead or
catch where it is a couch but 59:47 that the flag of can sit on it is false so the idea is imagine of an empty rectangle where it doesn't have any 59:53 affordance for sitting and can sit under this falls it's not really a couch it's work how it should be but you've managed 59:58 to forego the black hole problem by just putting something there that conceptually is the space where it would 1:00:04 have been and so that's what in object is it says you can either propagate this idea of 1:00:12 this little time bombs through as if anybody ever tries to access it and it's not there the whole thing explodes or you can
once assign this value to well 1:00:19 here's a version of this which doesn't do anything and that just means for the rest of your entire application you just 1:00:25 feed it like it's already there and then only one place is that ever dealt with as the oh I'm not really there so I move 1:00:30 on now personal preference I don't like putting log messages and stuff in my in my null object because to me an 1:00:36 implementation of a warning logging movement is a different implementation than a non implementation should 1:00:42 literally do nothing but semantics like at the end of the day it comes down to when when a case would normally be a
1:00:48 null object if it's easier to just assign that a blank object the rest of 1:00:53 your code will be a lot cleaner you just don't have to worry about passing this nothing concept around the entire code 1:00:59 oh yeah I didn't even realize this exists baka roll says try getcomponent I 1:01:04 didn't know that it was a thing yeah you can even do var out bar on the score 1:01:09 movement and not actually catch it oh yeah that's that's cool well I didn't 1:01:15 know about that I probably be I use this I guess you can call it a pattern quite a bit now and so 1:01:22 I know that Jason you created something
called like requires component or would require just so lazy I'd object dot 1:01:29 requires and then this and that way it basically wraps it that's cool I got a 1:01:34 gutter maybe that could be a video one day but invoke role mentioned also that 1:01:46 I should use a poco here and I agree I don't even know why I made this a component that's the person that I would 1:01:57 do for pretty much a similar because I'd find this two stages there's something that's required at the start of an application then there's a null object 1:02:03 that I can pass in as a proxy we open this up here I just drag this in 1:02:09
probably very nice yes here's a small example of something that I would do in 1:02:15 a lot of projects which is this this basically says for every single object in my project which extends from a unity 1:02:22 object type if I just call dot requires and passing a type so require so I would 1:02:29 say I could then pass in the object it's 1:02:34 on the model behavior and it will log a message and say hey this model behavior requires the subject please designer and 1:02:39 it's just it's the same thing it just means that everywhere in my code base it'll show me what the object is what was missing and if I click on
it because 1:02:47 I'm using the overload of the debug log format it'll pick in the hierarchy so by having this one line I could just write 1:02:53 requires requires requires and it'll just mean that I don't have to worry about the rest now I'm gonna copy that what I totally 1:03:02 missed something oh I missed the whole class yeah is that a class for the oops 1:03:09 yeah I totally missed that one somebody's entire that is a namespace 1:03:16 alright so now we can basically replace 1:03:25 this require so you would say spell 1:03:33 right of course choirs well it's on the objects to say underscore and row toyed be movement 1:03:43 blender uh yeah
whichever yet doc require this I'll do the spray render first though because I don't require 1:03:50 this okay reads like I'm saying require that sprite renderer even though conceptually is actually working the way around but that's just 1:03:56 because it do I am because it's only four types that extend from you know 1:04:01 type is better yeah all right God so do you yeah I got there that's cool 1:04:07 would there be a way to make that work for the interface like all right yeah if you just a few copy the function and 1:04:13 remove the the casting or see where says where T's objects got it yeah you can 1:04:19 remove that the only
reason I have forty is object is it allows me to do the 1:04:25 places she just get rid of there get rid of the T's objects just that was just sort of model behavior types and I'm 1:04:31 curious why not yeah I'm not referencing anymore so yeah that ought to work now I was just using expressly as a you know 1:04:37 do you think got it so complaining about it right Oh Chris 1:04:42 you know the objects are just object to the object oops ow oh I'm and I got to 1:04:50 do our mind I was doing on your object context all right let's see if that 1:04:57 works though yeah there you go never
code live oh yeah I know is why prepare commits ahead of time all right 1:05:05 you guys realize how bad of a coder I am all right well that's cool 1:05:10 so the TLDR is yeah I would use that as I like if I've got an awake function I'm grabbing a bunch of stuff I would use 1:05:17 last require concept and then once I find out something is no I would just say if it isn't if it have to be 1:05:23 provided to by the user set it to a new version and if you combined both of those concepts with a lazy loading 1:05:31 concept you can basically have it all automatically happen and it'll
only try to load the object when it's needed and 1:05:37 then if it's not their provider default accommodation yep 1:05:43 cool all right so I think we're finally ready to get to the point where I 1:05:48 actually implement credit based movement so let's look at this commit implemented 1:05:53 basic grid-based movement let's do it switch on over to unity so this thing 1:06:00 can reload and I think I have my my fancy level in here that I created after 1:06:06 watching that video yeah I got at press 1:06:11 play here well not moving probably have to actually update the uh do I have a grin 1:06:21 well we can figure out what the story is
there let's look at the code it's like I'll run the tests and they'll they'll 1:06:27 they'll all pass but it's like it still doesn't work all right so if we go oh go ahead this 1:06:33 again and even though the level doesn't look like there's a lot in it and we can give a really fun rundown about massive 1:06:39 amounts of subtle complexity that have gone into just a little decision yeah that'd be fun that'd be fun cuz yeah all 1:06:45 right so you can see here that nothing has changed on sheep I actually haven't if we look at the B commit all that I've 1:06:50 changed here is I've added a grid added Griffith
grid movement which is that implementation of eye movement and then of course I updated my 1:06:57 world and I made a player prefab but if we look at the grid movement basically 1:07:03 we open that up actually not the diff movement we can see that it implements 1:07:11 update position and get destination okay and so what it does is it has a 1:07:17 serialized field so it has reference to some grid and then on start it makes 1:07:24 sure to keep capture its current position and then it sets the grid the 1:07:30 grid basically has an indexer that takes in a vector3 and then it sets that to one so I'm 1:07:35 using the grid
very rudimentary brute emitter early right now in that if 1:07:40 there's a one in a position I'm I'm seeing that as it's blocked I can't go 1:07:46 there and you can see that in the update does position I say if grid next 1:07:52 position equals one then just return so keep what that'll do is it'll make sure 1:07:57 that the position just stays the same so as you can see it says next position per 1:08:03 current position plus direction then it checks like I has just said if there's a bit there's something there otherwise 1:08:08 it's gonna set its current position to zero meaning it's freed up and it sets the next position position to
one and 1:08:15 then it's going to keep track of its current position and then if you look at 1:08:20 the grid it's nothing fancy it literally has a width and a height and that's it it's just an int an int 1:08:27 array 2-d array and again I have that 1:08:32 indexer that just takes in a vector two position and determines what the cell is 1:08:38 so if we go back to the sheep class trying to figure out what I did wrong here oh I think I know what it is or 1:08:51 maybe I don't well one suggestion was that yours I thought you'd publish different commits but it's the same one than the triquet 1:08:56
components place inverse and possibly oh okay fair enough I assumed you'd you'd forced a different 1:09:03 commit I probably oh somehow it stayed right okay thanks 1:09:17 guys live coding Hey look at that 1:09:24 now we're hopping all over the scene here and then if we go and run the test 1:09:29 you know if I didn't even want to play the scene I think I was watching a um 1:09:37 Jason Wiseman video the other day and he came up with a really good reason for you know using why he should write unit 1:09:43 tests in context of unity he was mentioning that like you might have some sort of creature or thing or enemy that
1:09:51 only appears on like level eight and you don't have to yeah or you don't have to 1:09:57 figure out some way to like force your game state into level 8 although that's nice to have but it's actually something 1:10:03 as well that I highly recommend is I was working on a little project and you were on the discord you would have been able 1:10:09 to have a copy of it one of the things I did with that is additive scenes so there's one thing that covers the player 1:10:15 and it's basic lighting and there's one thing that covers like logic for UI and 1:10:21 for saving and menus and that kind of thing and
there's one thing that handle levels benefit of this means I could 1:10:27 open any level which has no complexity in terms of sort of the Sun or other lighting or the character controller or 1:10:33 the camera or any of that and I can just additively load the player scene press play and my character is just in and 1:10:38 playing so that mean needs to be part of this lifecycle of working my way through 1:10:44 it right okay testing it's almost always a good idea to try to make your game playable potentially from anything yeah 1:10:50 that may only happen for you but as you move on and you start adding things like a debug console
or whatever it might be 1:10:55 nice to give yourself an ability to jump to a certain level and that's not just like a a random indie tip for an example 1:11:02 of this if you watch the doom eternal speeder on the there's a common trick 1:11:07 for that which is there's a debug dev forum somewhere I'm like one of the last level or something like that where you 1:11:12 can clip through the wall but you're never supposed to do you know put this dev room which gives you like all of the guns all of the armor and 1:11:19 like three or four place in the level so even the pros do it testing at different
1:11:25 places you know do you would you this is something that I'm curious about because I was thinking through this game here 1:11:30 and I think the what I want to do is is like when it whenever he gets the sheep 1:11:35 gets to the end of the screen or gets to the place where he can move like it kind of fades and fades in and fades out to 1:11:40 the next you know part of the level and I was thinking like should I fake all that should I should I have this whole 1:11:47 thing contain my entire level and every time you get to some boundary it like does a camera fade or do
you think I 1:11:53 should store every single individuals one of these positions in the world and its own scene I think its own scene is a 1:12:03 bit overkill but if you ever got to the point where you had sort of biomes or sections I would say you can probably 1:12:09 have like ten ten to twelve screens per region or biome basically what you want 1:12:14 to do is give yourself enough time that you can traverse across multiple screens mm-hm and that gives you a good 1:12:20 indication of direction of so for example if there's a middle Square and there's two squares either side if they're moving two squares over here you 1:12:26 can safely
unload everything over here because you now know that it would take you time to get back and likewise you can start free loading this area you've 1:12:33 got individual scenes you're doing a lot of loading and unloading but if you've got enough bandwidth that you have to force them to move upwards in a 1:12:38 direction and you know you'll have enough time to figure out when they're going down the other way you know you can later there's a great talk from the 1:12:45 Firewatch team about how they used sector streaming for their version of 1:12:51 that but their effective thing is is that they have additive scenes that represent large chunks of the area and
1:12:58 then what they do is they then split that up logic pass and that kind of 1:13:04 stuff and then they would use low poly versions of the background stuff at range once you get closer than who knows 1:13:10 and low the higher one and there's like they'll go through and give a detail about how they do it but um in essence it boils down to 1:13:16 you should load the next area when you know the player can't go back to the old one and you can safely guess what that's 1:13:22 what direction they're going in right no I personally would go whatever it is through six by six or something or nine 1:13:29 by
nine whatever it is you would just pick a size grid and then then you'd have regions right right it's one thing I was 1:13:36 thinking was along those lines is if I designed every one of these scenes to be a little I think this is like a 10 by 16 1:13:42 chunk of tiles then I could additively load all the ones around it and then as 1:13:49 I'm moving yeah like you mentioned like okay I'll unload this one and then as he gets all the way over here it unloads this one but yeah so for me what I was 1:13:57 while I was approaching it as what would be the easiest way for me to
design 1:14:02 levels would it be easier for me to just compartmentalize every single level into a scene and I can just create them 1:14:08 individually or is it easier for me to say okay I'm gonna just design a big-ass car you can still compartmentalized each 1:14:15 one into its own prefab and then stack mm-hmm apps into the scene and then later on we decide what level loading 1:14:21 solution you want to take oh man I love it how many how many prefabs feel the same and how many scenes out of designing for change man that's what I'm 1:14:29 talking about just code that's good that's actually a really nice solution I like that 1:14:35 thanks
Jason's do I keep your alright so 1:14:40 alright that's the end of it basically we got two we did it we encapsulated the 1:14:47 code into an interface and now I implement a new interface and I have grid movement a curious thing yes well I 1:14:55 was gonna go into the next part I had two more commits that I did and I actually introduced a bug so I was gonna show how I how I had how I responded to 1:15:03 that bug but um no what were you gonna say oh you're gonna say just to kind of highlight the the summary lesson from 1:15:08 all of that yeah we've got to the point now where
there was an old method of moving there is no a new method of 1:15:14 moving but we haven't lost the old code is there as an option to switch back to 1:15:23 so the idea is that we've implemented multiple variant solutions that can be implied so in this case we're moving but 1:15:30 we could go back to if you want to do and this is this can be a big issue when you're not familiar with some of the 1:15:36 stuff we were talking about which is your write code you'll get frustrated at it's not working what you want too you'll rewrite it you'll realize you've 1:15:42 gone down a large rabbit-hole of stuff I'll just
go back to the old version and then you realize oh no I can't go back 1:15:49 to the old version because I don't have either a backup copy of it or it's going to take me I'm gonna to undo like five 1:15:55 other things because I've not been committing committing on a regular schedule or that that was seven commits back so if you instead of completely 1:16:01 rewriting and implementation you encapsulate the implementation as a strategy and move it out and put in a 1:16:07 different strategy you can literally swap out the one line and you get back the old behavior that you have no and so 1:16:12 that's the that's the what we talk
about when we say designing for change we know it's going to change so rather than kind 1:16:18 of pigeon holing ourselves with the one implementation we design in such a way that we can take some change out put a 1:16:23 new change in yeah yeah that's a good point so that's a good summary and a 1:16:30 good place to kind of just move on real quickly wanted to show you that I was 1:16:36 gonna do the same thing in the sheep for this transform do jump I I figured well 1:16:43 something like updating the sprite doing this animation that will likely change 1:16:49 as well what if I don't want to use do jump anymore
what if what if I will have a different type of creature that's like a fox and they don't jump they prowl or 1:16:57 whatever the case might be or if I have NPC farmer who needs to walk around they're not gonna be jumping so I 1:17:02 thought well in my case if I share that code I don't like having the between dependency the other advantage of 1:17:07 separating that out means you remove the referencing to do tree in this script you can create a really basic implementation which just uses a KO 1:17:13 routine or you can just moves it using the normal vector position and you can add your assembly definitions and all of 1:17:19
a sudden you can take out the entire core of your game give it to somebody else and they can choose to implement it with I tween or whatever else or surge and 1:17:27 and it's not going to they don't have to go to our references because you've removed those strategy implementations right so in this case what I wanted to 1:17:38 do was do the exact same thing we just did move this logic out into a class and then start to pull that up into an 1:17:44 interface so if we look at the commits here you can see that I encapsulated sheep's movement handling logic so we'll 1:17:51 go ahead and check that out and I
actually introduced a bug so we'll take a look at that go back to the unity so 1:17:57 it can recompile okay good there so so we have we have 1:18:05 this movement the sheep movement handlers what I call it I'm doing the same thing as I did before with the with 1:18:11 the no check and then you can see now all my code does is because I don't know 1:18:17 why I did this twice what oh because I hadn't made the null objects so at this point it technically could still be null 1:18:24 I do a log warning but in the case that it's null I just want to do nothing but 1:18:29 if I
actually do have it and and I know Jason is gonna tell me to do this energy flip or do a guard Clause yeah so we'll 1:18:37 say this is this is a guard clause so we're gonna say I have no logic after this routine so I can safely say if it's 1:18:44 null just returned don't do anything otherwise let's call on move on the movement handler in this case I'm going 1:18:51 to pass in a direction a destination and a lambda and the lambda is just going to allow me to update the sheep to let it 1:18:59 know that we've we've stopped moving right so some of you might already see the book so if
we go into the on move 1:19:05 again just like we saw before I'm updating the sprite I'm doing the dude do jump and that's it it's nothing 1:19:11 nothing's different however there was a bug so we go in let 1:19:18 me see if I can show you that bug see if this loads correctly my player make sure 1:19:26 I add my sheep movement handler all 1:19:31 right let's see so we can see here that I move you saw that but then afterwards 1:19:38 I'm not moving anymore I'm still pressing the key I only hopped one space 1:19:43 right so what the heck happened there so I needed to fix this and I didn't know what
the story was so the first thing I 1:19:49 decided to do was write a unit test all right so we go to my commitments for 1:19:55 history and so you can see it's fixed a bug that caused sheep to only move once so if I check that one out we can see 1:20:05 that I created a test in sheep tests 1:20:13 see if I can find it on a side note one thing you're gonna have to do its upstage to drive me insane yes you've 1:20:19 got some times you're using if with the racket sometimes you're not and there's no consistency all right so so basically 1:20:30 I say can can move multiple times that's
the test so I create a sheep and you can see that builder method that we created 1:20:36 with it by the way we can actually discuss why a builder is important there and basically the benefit of the builder 1:20:42 here is rather than using a constructor this comes back to the thing we talked about earlier if you construct your object using either constructor method 1:20:47 or an initialized function or something you've got a contract there you've got a in order to create a sheep you need a B 1:20:54 and C these are the three parameters you need and if you go ahead and change that later you will add a fourth parameter and all of
a sudden every single place 1:21:01 you've ever created a sheep in your code has a new parameter to pass in and you'll have a nightmare going through and having to update all of them the 1:21:07 thing about a builder is you basically say create a sheet and you can that 1:21:13 there are variants of the builder where you have create a default sheep create a dream sheep you can create the built version of that and that way the intent 1:21:21 of the test is give me a version of this pre-configured to be this and that way 1:21:27 if the implementation of that configuration changes it changes in the configurator which is the builder not in
1:21:33 every single reference beneath it and this allows you to very quickly say take a prototypical version but swap out 1:21:38 these two parameters and then make another protocol version swap out these parameters and it makes the test cleaner because you're not saying plumb the same 1:21:45 ten things like every sheep needs an ID and every sheet means of color and everything's a sprite but you can just say create whatever the default for 1:21:51 sheep is yeah these are the only two parameters to care about swap them out and move on so yeah that's that's predominant reason why they call those 1:21:58 text test fixtures as that is the concept and basically your fixtures are
things that you have agreed upon as 1:22:05 being standard element of classes that are configured in a standard way so that we 1:22:13 know that we have a physics 2d movement supid physics 2d movement with the sheep movement handler we might say hey this 1:22:19 is our standard sheep let's go ahead and call that default sheep is well how I might call it so it makes the code much cleaner and 1:22:26 do things people get very kind of worried about writing code in tests they sort of feel like you know well the 1:22:33 tests have had to test my code I can't write code in tests and that's kind of a silly thing when you
think about it in 1:22:39 real terms tests are code as well now it's a bit circular to think about testing your test that's bit nuts but is 1:22:45 your you are allowed to write code to simplify your tests yeah so if there is a particular thing you're doing a lot 1:22:51 just wrap it in a message just as you would to clean up your code normally in this case these are all very clean small 1:22:57 tests I have this one your create obstacle yeah exactly for my obstacle test there's a bunch of steps that need 1:23:03 to be done and it's perfectly fine don't don't feel too worried about doing that 1:23:09 I notice a
lot of people are very reticent to do any kind of testing code because they're just afraid that it's like breaking the rules what if I have 1:23:15 bugs in my test code well you have to fix that make sure that your other tests work like eventually you'll kind of 1:23:21 circulate on to the solving the problem yeah that's a good point so yeah so I wrote this can move 1:23:28 multiple times just creates a standard sheep here does a does a for loop calls sheep da move twice and then you know 1:23:35 we'll see that it well we won't see because I fixed it but it basically failed and then then I was in a
better position to go to my sheep class here 1:23:42 and actually figure out what the problem was and the problem was I was actually setting this to true instead of false 1:23:48 and so that you know I figured out that that was what the problem was no big deal 1:23:53 fixed it ran the test it passed and I didn't even have to run my scene to know that or and and that's a good thing too 1:24:01 here's another subtle thing about that if you've got a test suite and you have a bug to do with movement you can run 1:24:07 your tests and if all of them passed except for one or two you've immediately
localized the problem to the stuff that 1:24:13 is touched by those two tests yeah that's one of those subtle benefits you don't really think about because if you had a problem where you at a character 1:24:18 who's like a rigid body moving around a scene he's interacting with stuffs and swinging the sword and something goes wrong character starts like wobbling or 1:24:25 doing some weird stuff you were like this could be the animator this could be the movement system this could be the 1:24:30 rigidbody simulation this could be maybe I'm using some weird soft body physics maybe 1:24:37 there's issues with some objects 1:24:43 critical drum if you've got a test suite running some of
the tests will at least let you know well at least it's not that 1:24:48 stuff and you'd be amazed how just that one step will cut out like three hours 1:24:53 of testing and turn it into mayor three hours of debugging in turn it's like 10-15 minutes because you don't go down 1:24:58 a bunch of rabbit holes of well it's it's probably this thing so just having those tests lets you sort of say at 1:25:04 least I know it's in this class somewhere in this function and all of a sudden like you're not hunting around your codebase yep alright well that I 1:25:13 think about wraps it up I think we've shared some pretty good
strategies here you want to look at that 1:25:19 level and talk about some of that yeah the levels this little bit tangential 1:25:25 before we do that hit that like button and I'm dropping that link one more time for the giveaway so be sure to sign up 1:25:31 for that okay so up to this point we've been talking great code if you remember 1:25:38 that but that opening graphing the Charles show that was just some of my thoughts and so this really interesting 1:25:47 kiss so here's the thing we've all been there we you end up with a blank slate 1:25:53 really frustrating is you start putting some stuff down and it's just not very
satisfying doesn't feel like there's anything to do there's no real point 1:25:58 like why am I doing this or your levels just don't feel like a fun place and you can't really figure out why well one of 1:26:03 the first things you should think about when designing any kind of level or scenario is you want to give the player an objective or a goal that may sound 1:26:10 obvious but when you create a scene even if it's a prototype demo saying make sure there's something to do now if that's the first thing is if you launch 1:26:16 the scene you've never played this game before of no idea what it is there's this concept of
affordance so a chair a Ford sitting on it it's shaped it's 1:26:23 designed the way we know about chairs implies they're suitable that's the thing they are same with benches you 1:26:28 mean it you may see a weird artistic bench at some Museum you don't know what it is but you get a good sense it's a bench right you know you can sit on it 1:26:34 by the shape of it by where it is by the location on the ground it gives you a lot of information by proxy the most 1:26:39 common example of this is the door the door handle grab it and the implication is you'll pull it to open it but the
1:26:44 problem is if you see a door with the handle I both sides it can be confusing to know whether you push or pull but if you door which is a handle on one side and 1:26:50 it's got a little rectangle a panel on the other that's that implies the affordance of that is to push it so 1:26:57 effectively long story short well never 1:27:02 you've got a plank of something sticking upwards the affordance of that is it's a sign it's readable and we all know this 1:27:07 this is an intent like whatever it is it's a readable thing right but even if you just look at the scene and you know nothing about this
game I think about it 1:27:12 your intention is I want to go over there and read whatever on design now there's a moment the implication is if 1:27:17 the minute the project starts the first thing you gonna think is I want to read what's on that side right that's just what you're going to do and so all of a 1:27:24 sudden that's come from an empty scenario nothing to do - like oh there's a point to play this now yeah before I even press a button I have an objective 1:27:30 so that's step one the second thing is um there's this whole concept of these 1:27:35 these bushes over here now this is another one
of those things which has an affordance applied to it now you don't think about it but because they're 1:27:42 because of the position of them they're basically abnormally placed everything else and there's the particular pattern 1:27:48 of matching branches with it with this space above it that's very very clearly alluding to this space up there and I 1:27:55 think anybody who's ever played a video game will probably the clear indication you'll get something to be able to get 1:28:01 through those bushes yeah that's what's one of the things I wanted to articulate with this - is that in this part of the game you would not be able to attack 1:28:09 those bushes but very
shortly after this scene my idea was you'd be given the ability to cut bushes you'd cut bushes 1:28:16 in some other area and hopefully you'd remember oh there were bushes where I started I need to go back there yeah and 1:28:23 so there's other levels you can attach on to this so one of them is that sets up anticipations players have 1:28:28 anticipation of going oh there is going to be something interesting up there I will keep this in mind that if later on 1:28:34 I get a cut ability I'll be intrigued to come back and check it out so second so that's all in that one little scene here 1:28:39 we've got we are
enticing the player to go right we are telling them there is something to go up but we're not there's 1:28:46 nothing there yet that we very clearly are indicating we can't interact with that and then later on you can get to this concept called one-way valves and 1:28:52 that would be where you move them along to an area where they can't backtrack anymore and you'd cycle them back around they might end up cutting that from the 1:28:58 top down all of a sudden that's the reveal of aha I'm back in the original area that makes that all interconnects now and I need a 1:29:03 shortcut to some other path zelda does this a lot like
it's a lot of these concepts that are baked into how their design and so 1:29:10 there's there's a lot in this in terms of how do you make even a small thing like this compelling but for a shorthand 1:29:17 if you just look at the level design and look at the concepts like and signifiers 1:29:23 you're gonna get a lot of a lot of surprising information about just really small situations like this so yeah cool 1:29:30 stuff yeah that's good that was a succinct and I see 400 mentioned 1:29:35 something cool yeah so that's basically things that are grouped together that we not well one in general even things like 1:29:41 seeing things that we
do like we will find patterns but things that are grouped in pairs are often are always 1:29:48 more interesting so if you had a scene full of equally placed stuff versus a group of three and it before we we imply 1:29:54 behavior together and are like so there's a lot of interesting there's 1:30:01 also this whole concept of differentiating shapes larger and smaller implies different different 1:30:06 behaviors so having large blocking structures with smaller interactable structures is very clearly a pattern here and the whole load of this stuff 1:30:12 like you can go into this first I even went so far as to make sure there was like thicker grass in the areas just to
1:30:19 kind of make sure that the player knew that these are there is you can go there's like an opening down here but I 1:30:25 don't intend for the player to be able to go through that that isn't something that is like you can diagonally move 1:30:30 through so I may have to do more work to block that but I'm hoping that at the very least I've afforded you know maybe 1:30:36 some player would be like oh I'm gonna try to break through here but realistically I'm trying to communicate 1:30:42 that you're not supposed to go down here so cool great stream guys thank you all 1:30:48 for coming out I hope this was beneficial
I know that you know we're not gonna have a video tomorrow I didn't 1:30:54 mention that but hopefully by the way I won last night because he ended up just at the end just reminds me in other 1:31:01 interesting topic about what you said there with that little gap mm-hmm an interesting topic a lot of people don't think about is when you're blocking 1:31:07 areas there's a whole there's a classic problem of the invisible wall blocking air yeah well there's there's a kind of 1:31:15 grreat barrier there's hard barrier and soft barrier and effectively what people don't realize one of the best kind of 1:31:21 soft barriers you can produce in a game is to
make a place not interesting cuz I actually have to block it you just have 1:31:27 to make it usually not something interesting and the only problem is right now those those stones do you make it interesting yeah I think if you 1:31:33 didn't have those stones no one would really think to go there because there's nothing and telling you to go in that direction and so you'll find this a lot 1:31:39 if you watch there's tons of places 1:31:44 where the game guides you in a direction without any visible walls because what it's doing is its guiding you towards light and interest and stuff going on 1:31:50 but there's entire other swathes of the of
the game where there's just nothing going on and you can go down some 1:31:56 corridors and go down different routes but the game just doesn't want you to go there so rather than actually it you just wants nothing interesting there and 1:32:02 it makes you just less interested in going there there you go no more are 1:32:08 cool alright guys well yeah again thank you all for coming out on this Saturday 1:32:13 yeah no video tomorrow but please feel free to sign up for that giveaway again 1:32:19 I'm gonna announce a winner for that next week either joy fourth or July fifth and then we should be back to 1:32:26 weekly video so July fifth
look out for that and yeah other than that have a 1:32:31 great week and catch you guys later look everybody well