UE C++ Tutorial Series: Items, Interaction, Inventory #3: The Interaction Interface

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right so now that we've got the  the item data in place and we've got   the item class which is going to be  the container for all the item data   we've got that's like a crucial step in  moving forward to getting an inventory but   the next big thing that we need to  implement is going to be the actual   uh interaction capability on the player so we  could go ahead and make the inventory and start   doing that stuff but i figure it's better to do  this because once you have this in place then   once you build the inventory it's very easy  to make a pickup or something in the world   that you can test it all out with and so i  figure we'll go ahead and do this part so the   first thing i want to do is come down here to  my classes c plus classes add import new class and so we're going to be making an interface to  to create this interaction capability uh the one   thing i've noticed about this is weird normally  like you have a couple of classes right here that   kind of like there's a common yeah it says over  here common usually i need to go here to find   classes but the interface is not easily found in  that list for some reason uh so if you just scroll   all the way to the bottom this is the actually  the one we're after right here unreal interface and so i'm going to make a new folder  for this and call it interfaces just in case there's ever a need to make more  than one in the future so i'm going to select that   we've got public private okay that's what i wanted  and this will be called the interaction interface interaction interface dot h  looks good create the class all right and so once uh let's see well live  coding will run and get everything finalized once   that's done and you see finished here go ahead  and close this and we can actually go ahead and   do need anything else in the editor actually  yeah before we close the editor i'm going to   make an a test class that will that will  um let us test out our interactions on   and so this is just going to be an actor  what we're going to do is after we build   the interface and build the line trace capability  on the player then we can drag this actor into the   into the game we'll call it interface test actor  and we can override the functions on this guy that   will let us test out our interaction so uh what  i like to do for this is anything that's an actor   in the in the game world that the player will be  interacting with i like to make a world folder this is where stuff like the pickup um you  know the test actor like uh if you made like   a switch class where you go up and you can  actually interact with that i'd put stuff   like that in here and then obviously you can  add subfolders later on as you want i'm just   going to put it right here in the top interface  test actor public private looks good create all right compilations good done in  the editor for now let's close this out all right and so you can see here it's opened up   our classes for us already we don't need to worry  about the interface test actor right this minute   so i'm going to close those done with item base  i had those still open from earlier all right   so let's go to the header file of this  interface so in standard c plus plus   at least to my knowledge and someone can  correct me in the comments if i really am wrong   i believe that an interface is going to be a  class where you always have to implement all the   functions that are provided in it it's a way to  standardize say you have multiple different kinds   of classes that are technically unrelated but if  they all interact inherit from this interface then   that's a standard set of functions that you can  guarantee they'll all they will all use it's a   way to guarantee that anything that inherits this  will be able to have this function called on it   and so unreal it's a little bit different because  you can kind of pick and choose if you inherit   from this you have access to everything that's in  the interface but you can only implement what you   need so you'll notice there's two things  here which is a little bit different from   what it typically does it says right  here this does not need to be modified   i don't know the reasons for  this but it creates this kind of   you inter u version like a u interface class you  don't touch this part this right here it's a you   know add your functions to this this will be  inherited to implement so we're going to come   down here and we're going to start implementing  our functions so when we think about interaction   in a game in most games when you want to interact  with something you walk up you aim at it and   something happens whether it be a little pop-up  something highlights like the object itself or   some kind of effect appears around it sparkles  a faint glow whatever or all of the above so   with that you want to implement that into your  interaction and that would be like a focus right   so you're focusing on an object but you haven't  done anything with it yet so we'll call this begin focus this will be one function if  you're not familiar with virtual virtual means   it's a way to establish that a function  can be overridden by child classes there's more to it than that but for right  now that's what this implies this is saying   okay anything that inherits this interface   can override this function and give  it their own custom implementation so if we want if we have a begin  we know we also need it in focus   then once we are focusing on something now we  say we push e or whatever our interaction key is   so now we want to actually begin our interaction  now we could just do interact by itself but   i'm going to implement timed interactions so it's  very common in games you walk up it's like hold   button to do something like a  door um or you know a a container   like hold e to open and it waits like  a second or two using the using a begin interact and of course an end interact you can  establish a interaction time and in the begin   you can do some checks for that and say okay  is the interaction time on this object zero   if it is just interact otherwise we  start a timer and we display some   feedback to the user and once this  is done then we can call interact and obviously like i said if the  timing if the interaction duration   time is zero then you just call interact  directly so it's just a way to kind of   build this in on the front end so we don't we  won't have to go back and add that later on   now this is uh the last thing we're going to need  is i'm going to call this it's a it's a struct   called f interactable data and it's called  and i'll just label it interactable data   so we don't have this now  so we're going to build it so come up above here right under the includes  and start building this so it is a u struct f interactable data we need the generated u struct body and now we're gonna build a  constructor for this thing i've got a one of my a key is messed  up so a lot of times i try to type an a   and it types like two or three on my  mechanical keyboard i have to replace it okay so here's our constructor and you can leave  it like this it's just a default constructor that   doesn't do anything i'm going to come back to  this in just a second so first we're going to add   our fields so we want a an enum that will be the  type of interactable so actually let me go ahead   and build that as well because if we're going  to use it in here we might as well so you enum no i don't need that again in my reference i must  have thought at some point i might want to use   blueprints but there's no point because there's  no blueprint usage in this so e interactable type   un8 and i talked about this in the in the  video where we built the item data structs it's just forcing each of the items contained  in here to be the the size of a unsigned   integer an unsigned eight bit integer so  one byte so the different types of enums   are sorry the different types of  interactables are going to be pickup uh non-player character again we're not  implementing this but this is all the stuff   i built in when i made this because i'm thinking  ahead of what kind of things i'm going to interact   with right a device and then a device i i called  it this because i was like okay i want to be able   to interact with like a door but i also want to  be able to interact with a gate uh or a window or   anything like that and i thought okay device  is kind of a good word that sums all that up   uh i don't want to have i mean i guess you could  technically have an e-name for every single thing   but i was thinking of it as like okay i'll have a  single class that can perform all these different   actions so if i make one class that's movable in  different ways then it can be a door a gate you   know a rotating door a swinging door can do all  this in one single class and so i called it device   the other one i along the same lines i called it  a toggle a toggle would be anything like a button   a switch um you know something that you like a a  lever right anything that is used to activate a   device or activate some other functionality so i  called that one toggle and then lastly container so let's add in our umeta display name [Applause] need that the comma to go after that i'm going to come back and  fix this in just a second okay so there's our interactable type and so come  back down here to our interactable data and we're   going to create an interactable type and just  call it interactable type we also want a name we want an action we want a quantity now one thing i will say real quick if you are  using blueprint and you want to do that through   these tutorials that's totally fine the you will  have to use an uh n32 for everything because   in c plus you can use some of these other  types like n8 una et cetera uh but if you're   in blueprint it only supports using n32 so i  just figured you know an n8 can go up to 255   because it's eight bits uh i didn't think that i  would need more than that for a quantity so that's   why i went with that it really doesn't  matter too much you can make it a 32. the last thing we want is the  interaction duration i talked about okay and so let's make these u properties now this is defining what the struct is right  and then we have one that travels along with   the interface but we're not going to be editing  this one directly what we're going to do is we're   going to create another another one on like a  you know device class or something or a pickup   we're going to set it and then we're going  to take that one and set it equal to this   it all makes sense later but essentially the  reason why this is edit instance only is because   for anything that inherits this interface it's  going to get its own copy of of interactable   data that's set according to that instance  so all of these should be edit instance only quantity obviously is going to be only for  pickups and then i put a comment here uh   used for things like uh valves like  if you turn a valve wheel doors [Music]   so now the last thing i want to do and i mentioned  i would come back to this constructor i'm going to   use what's called a member initialization list so  if you ever see this format in c plus plus what   this is saying is when the constructor runs i'll  do the first one here when the constructor runs   we'll make it pick up by default it is going to do  this stuff i think it technically happens before   the body of the constructor what i mean by body  of the constructor is the constructor still has   code you know that it runs in its brackets  right that's pretty common but this stuff i   believe happens first and then the body runs and  so i'm going to indent this to keep it clean and   it's got a squiggle here because it's saying okay  hey you've got other things in here that are not   being initialized so i'm going to add those so  the name is going to be f text there's a nice   function in the ftex class called get empty  which is just going to return an empty string again same thing because we are going to override  all of this and set these values but we don't   there's no reason to do that right now so they can  all just be initialized uh to be empty quantity   zero and then interaction duration 0.0 f and then you don't need anything after just the  brackets and then the semicolon closes that off   and so there you go so now if you were if some  reason comes up where you do create one of these   by default it will have all of this set  already and then it can be overwritten all right and so this is actually uh i think  i don't know if you have to implement these   functions we'll try it and if it fails we'll come  back i think just by doing this what we have here   it allows you to now inherit this and then  give this an implementation in whatever class so we may have to revisit that just add implement  add empty implementations no big deal okay   so now here's the here's the  next crucial part of this   what we need to do now so i'm back in the  character in the character we need to add   the ability to shoot a line trace and check and  see if we've hit something that is interactable we can do that using the interface leveraging  the interface we can we can say okay   line trace hit something now check and see  does that thing implement the interface   first thing i'm going to do is i'm going to  clean this up it hurts me really bad when there's   multiple sections here like a protected and then  another protected that are complete this one's   gray because writer's smart enough to tell you  like hey this is pointless it's not doing anything   so i'm going to get rid of that i'm going to get  rid of these i'm going to move them up i'll just   keep them in public because they were in public  and this is all the default stuff that was in this   in this class by the way these guys the actual  variables themselves can go back down to protected   these are just the getters for those turn  rate doesn't really need to be public let's see i'm going to fix all this up  let me go find the item class because   that's where i have these you saw in  the previous video i really like to   do this because it just cleans everything  up so nicely and keeps it easily readable   other than having this giant massive list  of just stuff that blends in together   okay so now we can move our okay now these are  getter functions they're good move this guy down clean it up all right and do the  same thing for the protected section turn rate functions everything down  here is a function nope not these okay okay again completely up to you if  you like to do this or want to do this   it's just i find that now it's so readable  right here's my public section variables   functions protected variable selections okay so there is a special class that the engine  provides you called the t script interface   what that is is is a you can kind of think of  it as a wrapper for interface handling but it   makes it extremely simple to check if something  interacts uh sorry if something inherits that   interface and then call the functions so we  need to it can be protected and it is called t script interface   it needs a type and the type is our i interaction  interface that's the one that we just built right here and then now i'm just  going to call this target interactable i'm going to make this a u property visible anywhere and it's in the interaction category oh uh i don't  i don't think i talked about this already but   basically in the editor if you do this you will  have to find the interaction category which will   then be you know it'll be alphabetically sorted i  really like this trick that i don't even remember   where i found this but if you do something like  character and then the vertical bar it will make   this a subcategory so now you will find you will  have character which is you know c so it's closer   to the top easy to find you expand it you'll  then have an interaction category where this   stuff will fall into so really useful um i don't  think anything else has a category to fix but   we'll be doing this from here on out to keep the  uh keep everything nice and clean in the editor okay so to go along with the interaction i  built a little struct that will carry a few   crucial pieces and it doesn't have to be a struct  they can be separate but this way i thought you   know it's bundled together you're you're always  calling it's called let's see it's called f interaction data and so this is  different from interactable data   right interactable is like an object or or  an actor that we're going to interact with   interaction data is like info  about the interaction and so you struck body so what it's going to contain  is it's going to contain the current interactable and then it's going  to contain last interaction   check time because what we're going to do the line  trace is going to be controlled through the tick   in the character but you can control it by  saying what is this you know what is the duration   how often do you want it to happen and so we're  storing that in here and then we're going to do a   check and say okay if we're over this elapsed time  shoot another line trace because there's no reason   i mean it's not really necessary to shoot it every  single frame that's just that's too much so it   makes more sense to kind of space it out a little  bit uh and then this one's also your property and i am going to put a default constructor  in here [Music] and it really freaks out   it's like finds when you start typing it  finds a million things in that autocomplete current interactable is going  to start off as a null pointer   last interaction check time  is going to start off as 0.0 and then put the brackets there we go okay so the  this is different from our target interactable   because target interactable means like this  is what we're hitting and actively processing   uh through the line trace this one is kind of  like the one that we have stored saying like   this is the one that we just found and it allows  you to check and see if it's valid or say like   if it matches you can say like  okay if we just found something   we can check and see does this match the current  interactable so it's really useful for that all right so those are the crucial pieces of  data to start with now we're going to need   some functions and i think yeah we're  going to need a couple variables too   all right so we want a interaction  check frequency that i talked about this will be in seconds we want an interaction check distance this is used   to determine how far the line trace is  going to shoot out from the character and then we're gonna want the timer so let's see uh to make a timer you start by doing f timer  handle and we're going to call this timer handle   interaction uh oh and we also let's see all right so writer  does this for you i don't really like this but it   already included my interface header for me here  i would have to come up here and do this anyway   but we need to include this here because we  need the definition of our interactable data i am usually not a fan of including headers  and headers because that's how you end up with   a circular dependency if you're not careful  obviously class a includes the header for b   b includes the header for a you will get  a you know that that causes a problem   and so i usually try to always include headers  in the cpp file because you don't include it it's it's only including it where it's  going to be used basically anyway the point   is i can talk about that more later if it  comes up but basically we need to include   the interaction interface header here because  we're also going to have our interaction data uh sorry no so the interaction interface  it's for this it's for this definition but   then um we need the interaction data   struct that we just created i was misreading  it because the names are kind of similar okay okay so this one is what i  just built up here this struct right   with our current interactable and  our last check time that's this and then the timer handle we're going to use  to actually create a timer for the interaction now we need our functions for our  interaction so remove this default stuff down we need something that's  going to be called every tick   to to shoot the line trace and so i  called this one perform interaction check we need uh at that point all right so a line trace  is gonna shoot it's gonna see what the it hits an   actor it's gonna check does this implement the  interface if it does then we're going to call   found interactable this is going to  take in an a actor new interactable   however if we don't find an interactable  then we call no interactable found we also want to have begin interact and interact and oops speaking and  typing what i'm speaking interact now these   are different from the one in the interface  these are you can think of them as the main one   right because this is like coming from the  character so this is the action of interacting   then when we're implementing this on something  this is the one that's going to be called   when you interact with it so that's the difference  here these are the ones this is the acting on   this is the one that is receiving that action  all right i think that is it we got our timer   we got all the core functions we got the variables  so now i'm going to implement all of these this is   a feature that i really like with writer you go to  generate definitions by declarations it sees all   these and it's like hey you haven't implemented  these yet make sure they're all checked bam now the one thing that's annoying is that it  doesn't always put it into a logical place so   i want the these turn and lookup functions to be  put back down here with the move stuff and now   here we go i also am missing tick so if you're missing um tick so so character class  inherits uh you know your character class inherits   from the main character class which has tick  in it so in order to get tick back all we have   to do is you can either go to this class find  the definition and override it or if you have   a ability to override i think visual assist in  visual studio does this as well search for it and   let's see add tick remove tick that's not what i  want tick here's the main tick right here so i'm   going to override that and see it does this thing  again puts this down into its own public section and all these generated comments i know that  they're probably helpful at one point but   it's just so much extra stuff okay so we're in  protected functions that's our stuff there all   right so now tick has its override oh and when  i said i would talk a little bit more about this   in the interface so virtual means something can  be overridden it simultaneously means something   is being overwritten so obviously like i said  tick is implemented on the character itself if   you override that and you put it into your child  class you also designate it as virtual but then   you designate it as an override i don't know the  reasons behind that it's probably something in the   c plus standard but that's just this that that's  just how it's done when you override functions   so we will then generate our definition for that   and at this point we're ready to start adding  code in here to the the actual cpp file   so uh and leave this leave this super here  because there's stuff that happens in the   parent tick that we want to still happen you can  go to the definition and look at it if you want to   but that should be left okay so  the first thing i'm going to do   the perform interaction check is the  most crucial crucial function here   so this is the one that's actually creating  the line trace and when i say creating i mean   we have to figure out where we're going to  start it from and where we're going to end it   and then this will tell us you know it's  going to check for the interface and   and set the target interactable that we saw that  we created so the first thing we're going to do we're going to set in our interaction data  our last check time this one is going to be   get world get time seconds okay because  like i said we're gonna come down to tick   and let me move this actually i want this to be   just my personal preference i like tick to be  right up at the top near all the like setup stuff   and you know what i just realized begin play is  also missing i don't know why these were missing all right oops we need our begin play for sure move this up put it next to tick there we go get our definition and obviously  put it in a weird place i'm gonna put it up   here right above tick okay that's we're gonna  need that for later on when we set up stuff   okay so uh we're setting our last interaction  check time which we're going to use in tech   by getting the current world time in seconds  that's basically like it'll start counting   from the time you press play i believe so  now we need to make a couple of vectors so   we're going to make a vector called  trace start and initialize it with   a 0 vector because basically we just need to  create these and then we're about to set them oh you know what actually um i could i'm probably  going to refine this later but in my in my in my   game the one that i'm kind of like referencing the  reason i did this is because i have like an aim   mode and based on whether or not i'm aiming the  vectors or set to different things we're not going   to do that right now again i'll probably come  back and do it it's not the main focus right now   but before i delete this if you're not  familiar with this syntax these brackets here   it's just an another way to initialize a variable   and it's i believe it's a little bit more  performant than creating it and setting it   with an equals it's probably negligible but one  thing that it is useful for is it guarantees that   the type will be this type so like say with an  enum enums can be casted to an integer and so   if you want your enum value to be explicitly  initialized with another with an enum you can   use these brackets and it will enforce that and  it'll be like you know if you said you had an   enum here and then you tried to say like i want my  enum to be one it will cause an error and it won't   let you do that so that's just something i guess  kind of neat about the bracket initialization   all right so um the reason why i got rid  of that is because since we're not doing an   aiming mode right now the trace start is going  to be get pawn view location and what this is   when you create a character and it has like  its collision capsule that's like human size it   automatically is initialized with a certain value  that represents like the height of the eyes so   we're going in and it'll return that to you as a  vector and so that's what we're going to use there we're going to do trace end and trace  end is now going to be trace start plus git view rotation dot vector times interaction  check distance now what's going on here is it's going to essentially so the view  rotation comes from the character controller   not the player pawn mesh because you're  changing your view with the mouse and   you're changing the location of the camera with  the mouse and a third person character right so   based on the rotation of that uh and then you can  see here look it tells you get the view rotation   direction of the direction they are looking which  is normally controller rotation that's what i'm   talking about so whichever way you're looking  it's going to take that rotation convert it   into the forward vector of the of that view and  wherever that's at we're going to multiply it by   the distance that we want to kind  of trace out to for interactables so we've got our two traces now we can go ahead  and [Music] get ready to do the line trace itself   so to set up a line trace you  need to create f collision query params this is basically uh oops it needs to be  it's a struct so just call it query params the query params contain some useful things  for doing line traces so you can see here   use complex collision find initial overlaps etc  etc a bunch of different things you can set um   masking all kinds of useful stuff right but   we don't need to get too far into that  right now what we need what we need is we want to call add ignored ignored actor and  pass in the this pointer that's just saying   since i'm the person i'm the class shooting  out this line trace i don't want to hit myself   with it that'd be a little bit ridiculous  the next thing we need is an f hit result   trace hit so this is another struct but this  struct is used to store the result of a line   trace so see it contains information about one  hit of a trace such as the point of impact and   the normal and etc what we're using it for  is it also contains the actor that you hit now we're ready to actually call it so  it's get world line trace single by channel   the first argument is so right here when it says  out hit what's that what that's saying is the   ampersand is we're passing in our hit result by  reference this function will modify it and then   that it becomes the output so right after this if  it hits something it will be stored in trace hit so line trace single by channel uh let's see oh  i'm confused here trace hit we're passing in first   now we need the start and the invector we  already have that trace start trace end   then you're passing in the collision  channel so we're passing in the ec   it's called ecc which stands  for e collision channel visibility and then the last thing is you can see put my  comma back as it wants so here's the channel   we passed then we need our params and since  we already have that we pass in query params now notice we put an if here what the reason is  because if you look returns true if a blocking hit   is found so usually that's the best  way to implement your line trace   because it gets the return and checks it all  in one call right so now if we have a trace hit we're going to find the distance oh wait sorry the first thing we want to  do so after we found a hit now we need to   actually see does it implement our interface  so if trace hit and you can call get actor get the class of that actor implements interface and now what we're  going to do is it see it wants the class here   so you pass in u interaction interface static class that's just a syntax that lets  you return uh why is this mad oh i forgot my other parentheses uh the static  class this is like saying return the c plus class   itself um i'll talk more about this later when  we're creating ui elements because it actually   is something that can trip you up but  basically now we're getting the ac plus   class of the interface and then it can  check and see is this actor inheriting it   so now if this is true now we know we  found something we can interact with   but we're still going to do a couple of  other checks so distance is going to be the start of our trace minus the trace hit dot impact point and then we're going to get the size of that  distance this will because remember we have a   interaction check distance because  we don't want people to be able to you know if the line trace hits something but you know i don't know i guess technically  if the line trace is only as long as the   interaction check distance how is something ever  going to be longer than it may have may actually   be able to completely remove that but since i  have it in my reference i'm going to leave it   for now i'll do a little bit more checking  and maybe mention that again later find it so we've got our distance okay so now we're going  to check and see okay is this actor equal to   our interaction data current interactable   oh sorry is it not equal to uh and a lot of  people have asked me about this so what i have a   font here that uses what's called ligature so  don't be alarmed by it it's just nice looking   but it's actually doing an exclamation in an equal  sign um so yeah if you want to know i can i can   mention i'll probably put this in the description  of this video uh where you can go to download this   font and stuff like that but if your editor  supports ligatures you can turn that on and   then you get nice symbols like this okay so  if we are not looking at the same interactable   and our distance is less than or equal  to our interaction check distance now we call found interactable  and we pass pass in the actor we're then going to return just because uh after this runs we uh you know we don't have anything else  to do because we found one and we handled it   now if this part fails then we're going to say if if our actor is equal to our current interactable just return because basically at this point it  means we're still looking at the same interactable   and we don't really need uh you know we don't  need to do any other logic we just need to keep   to maintain the current status now if all of this  falls through or if any of these fall through   right so if the line trace doesn't find anything  if it doesn't implement the interface and etc   we want to call no interactable found now you may have noticed that these  return statements were grayed out   because it's essentially saying like hey you  know there's no point to this because down here   it just ends well now there's a function down  here so execution could stop here now or here   or it could go all the way down here so i don't  know if visual studio will is smart enough to do   that for you or not but anyway so this is the  the core of it this is our perform interaction   check function now what we can do is we  can let me see where is this timer set actually the timer is not being used for the  interaction checks just the interaction check   time so come up back here to tick and we're going  to put in the actual ability to call this so if it's another function through get world time sense get the interaction data last interaction check  time is greater than interaction check frequency you may have guessed we will  call perform interaction check and now we should test this out real  quick just to make sure it looks good   this is everything you need to constantly you  know be performing your interaction checks   at a certain interval now interaction check  frequency has no value right now so let's come   up here i'm going to delete their little helpful  integration informational comment i'm going to   set mine to 0.1 uk 0.1 seconds you can set this to  whatever you want you can set it less just keep in   mind that i believe 16 milliseconds is one frame  that's how fast tick is running you don't need   to be setting it lower than that it really won't  have any point i believe um i read that you know   something below that it'll just kind of ignore  it and you'll just still get it once a frame   um don't quote me on that but basically  you know if you think about it unless a   player is whipping the mouse around between a  bunch of different interactables all the time   you know 100 milliseconds is fine it's  pretty much like not noticeable um   the delay between finding you know a new object [Applause] interaction check distance so this  one is something that you can just kind of tweak   to your liking i use 225 for mine which is i believe the player character  himself is like 200 units tall so it's just a   little bit longer um again just kind of tweak it  uh we're gonna test it here in a second you'll see   how to how to tweak it and then  you can come back and and you know   or sorry you'll see how to visualize it and  you can come back and tweak it to your liking   oh let's see is there anything else i need i think that's good so now what we're going to  do so okay i said we're going to visualize this   line trace so how are we going to do  that there is a uh include you can use again with my my style of organization i  like to kind of just put some labels here game framework is engine stuff capsule  component input component is engine stuff   camera component okay there's an include that is  draw debug helpers oh geez so in draw debug helpers there's some  really nice functionality that lets you   it does like a debug line trace um so it's  it's very similar to what we're doing here   but it doesn't you know you don't  have to worry about all this like   the channel and all that it handles all that  for you um and so right here after we actually uh actually no it's it's separate it's separate  from this right all it requires is a certain   end so what we're going to do we're going  to draw a debug line and and if you look   right here there's quite a few things you  can draw like a whole bunch of stuff um   3d text i didn't even know that a frustum frustum  is like um the view of a camera so like the square   view of like a camera lens looking out so this is  all pretty cool crosshairs interesting all right   anyway so line is what we want because we want to  visualize our line trace right so it needs a world   so it's just git world now it wants to start and  so we have this start as trace start it wants an   end trace end and now wants a color and so i  use red so to find that it's a struct f color   and then you got all your colors here you can  pick whatever you want red now we need a [Music] uh what is this persistent lines i think that's  basically saying like they will never disappear   so false and then the lifetime i'm using  one second and let's see death priority   okay so this other stuff has defaults  here so you can actually leave it uh let's see wait what was it is   oh thickness if you want you can make the line  a little bit thicker i think i will so death   priority just leave it at zero and then thickness  i'll make it to two i believe it's gonna be pixels all right so let's go ahead and  build and launch and go try this out okay so i started building and sure enough i don't  know if you remember earlier but i said oh yeah   in this interaction interface i don't know if you  actually have to provide implementations for these   but i might i might have to come back and do that  so sure enough since i haven't built until this   point what this is saying so anytime you see  unresolved external symbol from your compiler   that is from the linker so building actually  goes in different phases right you have your compilation we can really guess kind of  sum it up is compilation and then linking   so compilation is like checking the  syntaxes and types and everything linking   is where it actually goes and tries to connect up  implementations with dec uh with the declarations   and it's saying now you know this looks like  a bunch of gibberish but what it's saying here   is like hey i need an implementation for begin  focus in focus interact and all this stuff so five   see link one one two zero is the error code five  result unresolved externals so thankfully that's   really easy uh because we can generate all of  these definitions here and you know sorry for you   those of you who don't have the ability to do this  super quick just go ahead and knock it out you   know one at a time or whatever your method is but  now since i've given these default implementations   even if they're empty now we can do  the build and everything should be good   all right so i'm back in the editor  everything built uh i am going to press play   and what we hope to see is that right there  we've got our nice little interaction line trace   now you'll notice it's coming out of his neck  we could probably fix that by going and trying   to figure out how to tweak the eye height i  talked about because it's um what was it it's   get pawn view location let me go  and look at this function real quick   yeah see this right here get actor location plus  so that that's at his feet that's like at the   bottom center of the of the actor plus we don't  care about x and y we want to raise the location   by the base i height i'm pretty sure we can change  this and just kind of tweak it up a little bit   uh it doesn't really matter let's  see yeah it looks like i did do   that i don't even know what's the default  there's probably a default somewhere oh   nice i was able to find it doing this so  here's in our pawn.cpp it's setting it to 64. i   found in my experimentation that raising it up  about 10 will keep it from coming out of his neck   but again if you this isn't going to be seen by  anyone so this is a matter purely a matter of   like how much this kind of stuff irritates you if  it doesn't at all then don't worry about it but   if you want to tweak it um what you  would do is you would come back in here come back into your constructor and it's a public it's public right so you can essentially override  this directly without having to use like a getter   or setter and you would just say base eye high  equals so we saw it's 64. just try like 74. all right so now if it's coming out of his  neck let's let me let me recompile and see   what that looks like okay so uh i guess like  i said it doesn't really matter that much this   looks a little bit better now um the way that i  interpreted it the reason why i did all this is   because i was like hey you know i want i want  it to be based off stuff my character can see   and so i just thought it needs to come out of his  eyes and so i don't want it coming out of his neck   um i want it to be like when i'm moving the camera  around i'm kind of moving my character's point of   view around and i don't know it's fine either way  now you know how to tweak it if you want to but   the point of this is our interaction line  trace is looking good and it's working now   i'm talking all about how i wanted to come out of  his eyes and it can shoot out the back of his head   so there isn't actually an easy way to fix  that which i did fix because i was like okay   in some games it probably doesn't matter and if  it doesn't matter to you then it's fine but say   you're standing like this does it really make  sense to be able to interact with something   behind your dude i mean okay it's it's it's not  a big deal if it doesn't really it depends on how   casual you want your game to be but for me i was  like nah i want the players to have to face things   and like interact with them so uh you can  use a dot product to actually fix that   so come back into our perform  interaction check function where we are doing the line trace before we ever even call the line trace before we ever even call the debug line creating a float and it's called look direction so look direction again you can use the brackets here we're going to use the dot  product function in the f vector class   and what we're going to pass in is get actor  forward vector because see what it wants what it wants is it wants uh two vectors an a  and a b and it's going to get the dot product   of that so dot product is a way to check if  vectors are pointing in the same direction so   we're going to get our actors forward vector and  then we're going to get our view forward vector and we're going to say okay oh oh yeah yeah okay okay  so it's returning a double interesting this is what i was  talking about with the brackets   here right it's going to force the type  so it thinks because it must be something   to do with the way the wave dot product is  calculating it says it returns a template   but it thinks it's getting a double and so  it's mad here it's like hey you know you   you're trying to initialize this float with a  double so good i'm glad this happened so i can show what i was talking about earlier all right  it's not a huge deal um i just try to use the   vectors when i can but it's it's fine all right so  now we've got our dot product of these two right   so where are our pawn is facing and where our view  is facing now what we're going to do is we're just   going to check if look direction is greater  than zero because the way the math works is   based on if it is positive or negative that  determines if the actor if the vectors are   pointing in the same direction or if  they're pointing away or perpendicular   anything positive means that they are pointing  in the same direction and so negative would be   they're pointing in opposite directions anything  negative and it goes all the way from like   zero to negative one and i think from zero  to positive one now this is saying if they're   pointing in the same direction then you can do all  of this and so uh i'm gonna wrap that in brackets and oops i missed my debug move this  down just a little bit there we go okay so now we're not even going to ever shoot  a line trace at all unless we are looking   in the same direction the pawn is facing now  again i said it already but if you don't care   about this don't worry about it just skip this  whole piece it's not going to affect anything all right i'm going to stop my editor  and recompile and test this out do all right so now you'll notice okay it looks  good we're looking ahead we're looking where   the character can see but now what if  we're like oh you know i want to trace   uh and interact with this box here well  look the line trace is stopped now it starts zero or anything very very slightly above units  like 0.001 is considered valid because it's   kind of like it perpendicular almost so you can   see it doesn't seem to be it's like  when i aim right about here [Music] it starts so it really does represent  your character's field of view really well and so i kind of like that  and that's what i uh that's   that's why i decided to do that it's because i  wanted it to be like my character's field of view   oh yeah of course the dudes the dudes turned  a little bit that's like his stance so yeah of   course it looks a little different on this side  but yeah it's going to be i believe it's going   to be 90 degrees and there are ways you know  obviously we could check we could tweak that   if you wanted it to be more narrow you could  come in here and raise this and say if it's   greater than 0.1 right that would on that would  be kind of like narrowing the field of view but   i think this is fine the way it is i'm just going  to leave it at zero so uh sorry i said 90 but what   i meant was 180. 180 directly to the characters  left all the way over to the character's right so there you go now we can start  adding in the functionality   to actually set our target interactable  and get ready to call the functions and   implement them on the other class all right so  perform interaction check is looking good and   so now we're going to move forward and fill out  the rest of these functions so uh let's see first i'm going to clean up some of this  i don't need these helper comments uh i'm going to add in a function here  this one we want to be public because   other classes need to be able to  get this so it's a force in line returns a boolean and it is called is interacting and i'm sure you can guess what it's going to do  is it's going to return a value based on whether   or not we are currently interacting and so  that value will be determined by the timer   so it's const because the function itself does  not need to change anything it's essentially a   read-only function and what it's going  to return is get world timer manager uh i think it's it is timer active   and what we're gonna pass into it is our timer  handle that we made for interaction right here now this will say this will basically  right if there's zero interaction time this   doesn't really matter this is going to be more  used for if there's a timed interaction going on   so needed to make that because we're  going to fill out found interactable and   for that function there's a little bit of uh of  checks and stuff we want to do at the beginning so   we've assuming we did a line trace we found  something implements the interface and it's now   our new interactable what we want to do first  is we want to use the function we just made   if our character is interacting we want to call  in to interact because you know we're basically   looking it's like that would imply that we've  found something new right we're not going to get   into this function at all unless this part is  true which is the current hit actor is not the   same as our current interactable so this would  imply that we already had an interactive little   something weird happened and we just now found  a new one in the middle of the timed interaction next we want to say interaction data  are current interactable if there is one   all right so the very first time or i guess  when we when we implement no interactive   found i think we know this guy out but for right  now this is implying okay there is one already   if we have one we need to set our  target interactable to our current one the reason being this is how we call the interface  right so then we want to call target interactable   end focus so that'll end that'll the focus is going to be  used like i mentioned before to like highlight   or something like that and so  we want to make sure that we   are ending the previous interactable if  we have something new that's not the same and then at that point we can now take  our interaction data current interactable and we can set that to our new  one and then we can use our   interface interactable and  set that also to our new one then we call target interactable begin focus so that's a pretty simple function we're just  making sure that the previous stuff doesn't   interfere with the new stuff right this is  all just checks on the previous interaction   okay so we want to begin focus there  but now what about if we don't find   something if we don't find something it  implies we either didn't hit anything   we did hit something and it's not part of the it  doesn't implement the interface or it's the same   it's outside the distance uh or yeah this  one's for if it's the same so if it's the same then we should just end up here okay but then again no if it's the same it just does  nothing so it's basically if it fails the line   trace or if it's not part of the interface  that's really when it's going to come here all right so say nothing is found we want to check is interacting uh we want to get the timer so okay so no nothing is  found if we are currently in the middle of using   a timed interaction we're going to get the  timer manager we're going to clear the timer   for our interaction timer handle all right so that's pretty standard now again  we're going to do this exact same kind of thing   if our current interactable is valid then we want to check is valid on the target interactable dot get  object so the reason why so okay so here go   ahead and do this part target interactable in  focus the reason why this is necessary is because   with pickups the way the code's going to be  implemented is when you pick something up   you go through a whole bunch of  calculations in the inventory to check   you know can do we have enough weight capacity  for it do we have enough inventory slots for it   we adjust the quantity we add it to the inventory  array we add everything you know all that stuff   then it returns a status that says  okay this is how many items you added   out of this this one's quantity if all of them  were added then the pickup itself from the world   needs to vanish so you call destroy on it which  is a way to just say you know delete this actor   what i found through my debugging on all of  this is that there's times when destroy happens   before this so what would happen is it would  destroy the pickup before it was able to wreck   you know process that this interactable was gone  so it would try to call in focus and cause a crash   and so this is just a very simple way you know  in the weird part about it was though it didn't   happen consistently it's just one of those things  where the the timing you know the order of events   is not always guaranteed uh in the engine i guess  with certain things and this is one of them so   by doing this we basically remove the capability  for it to crash up crash our executable   because now if destroy happens and that actor is  null target interactable is technically null at   that point and we try to call in focus on it  well we're not even going to get here because   this actually will happen properly you know and  if it's not valid anymore this is skipped and it   doesn't really matter because the interaction  is going to end anyway because it's gone here we don't have this yet this is something i'm  going to set up next but this is where we would   hide the interaction widget on the hood  and so just putting a comment here as a   placeholder we'll come back and fill this  in we now want to get our interaction data   current interactable set it to null  and set our target interactable to null all right so that one's also  pretty simple and straightforward   so now we have our begin end and actual interact so with begin interact you'll notice that there's  no focus right because the character doesn't   get focused the focus happens on the other things  this is kind of how i said that like these are   almost like the origin of the interactions  the inner the interface is the result of the   interactions what's happening on those other  actors so when the character begins interact we're going to perform another interaction check  and this is mainly kind of a just a fail-safe to   make sure that nothing has changed right because  say we want to we are we we basically want this   to to come back that we're looking at the same  thing right because as i was just looking at   if it's the same thing then it just returns  right so it's kind of just a quick check here to   say you know uh verify nothing has  changed with the interactable state since beginning interaction because if it has if if it's somehow  you know in like i said it's just a   fail-safe doesn't really make sense that  this would happen but if somehow it did   then this would catch it and we'd go back right  we'd kind of go back in the process all right now if interaction data [Music] [Applause]   current interactable is valid  right always good to check this   you'll notice we check this in every single piece  here because it's just it can just like i said you   know this type of thing just in case stuff happens  you want to make sure you're avoiding crashes   if it is still valid and again this  check if our target interactable is valid then we want to begin interact on the target on  the target interactable and now we're gonna ch now   is where the timer actually comes into play the  timed interaction is gonna come into play right   here so what we're gonna do is we're gonna use a  function in the f math class called is nearly zero and what we're checking is remember  we built in the interaction data interactable data struct here right we're  checking the target interactable interactable data   interaction duration we're checking is this nearly  zero but we're going to give it a tolerance so   the second argument here let me go back the second  argument on is nearly zero is an error tolerance   maximum allowed difference for considering  this as nearly zero we'll put it 0.1 float   now the reason for that is 0.1 is really fast   that's what our [Music] line traces is being shot  out at 0.1 second you know 0.1 second intervals   with a interaction time that short it's really  kind of pointless it's like why even bother   it it doesn't add anything to gameplay  even point two honestly even point three   doesn't really matter but those are at least long  enough to kind of recognize that you know i don't   know you can make it like if you want to turn a  doorknob you could kind of put in a little delay   and maybe that's when you use like 0.2 or 0.3  but i'm going to go with 0.1 for now tweak this   to your to you know as you want if it's within  0.1 then we will just immediately call interact   because as i said you know at that point it's like   it doesn't if this is true then you're saying  okay i don't want to consider this a valid delay otherwise if that is false we're  going to get world timer manager set timer and now there's a bunch of different  overloads that's what all this is popping up   a bunch of different overloads for this  what we're going to use is the one where   you pass in a handle you pass in a class i  believe it's this one passing a handle a class   you pass in the function and then you pass in  the rate so rate it's a little bit confusing   when i first started this i was like this  seems weird why isn't it called duration   uh rate is like the duration that's like how long  the timer is going to run so right here when you   see float in rate that's what that's talking  about and then the last one is a a boolean here   for whether or not you want it to loop all right  so we're going to pass in timer handle interaction   now there's a bunch of arguments on this  so i'm going to indent to the next line   and if you're not familiar c plus is fine with  line breaks uh in especially with like function   arguments and stuff so now we need the class so  the class is just this because it's the player   now we need the function so what the  function is a cs tutorial character interact and now this is the function that's going to be  called when the timer expires now the rate see   and this is writers showing me hey this is the one  you're on let me get how do i pop that up again yeah so it should highlight it's it's  highlighted but it's hard to see but   basically now it's hot it's kind of made it bold  float in rate so we're on right now and i'm sure   you have an idea the rate we want is our target  interactable well it's just this copy paste this   the current interactable duration that  we've gotten from the target interactable   and now do we want it to loop  false that's the last argument so there we go if there is a valid delay above the  error tolerance that we've sat here uh set here   we're going to start the timer and as soon as  that timer's done then we interact and that's   all you really have to do to implement a timed  interaction now the graphical part of it is going   to be a little bit more complicated to show on  the screen something like a progress bar but this   is how you actually handle the delay just start  a timer all right so that's pretty pretty easy now end interact within interact we're gonna i'm gonna go up here  and take a few of these things that are it's   pretty simple i mean pretty similar sorry  i'm trying to talk and do stuff at the   same time my brain doesn't always  go the same speed as my words okay   so we want to again at this point we don't  really we don't really need to check if we are   interacting because if we're ending interact right  we assume that it's time to clear this stuff out   so go ahead and call clear timer on this  on the timer handle but we do want to check again that the target interactable  is valid so i'm putting that there target interactable now we  just end interact on the target and lastly interact so again this is  assuming right we we've finished our timer so we're going to clear that timer and then you guessed it we're going to call   check that our target in our still valid  and then we're going to call interact all right so that forms the core of the  interaction system in this we've gotten everything handled and it was all  relatively clean using this target interactable   t-script interface class right this thing it handles a bunch of stuff behind the scenes   for you to make this very easy  right because now we just called   call these functions right on it and anytime  you find a new interactable it's set to that all right so how do we test this out so  if you remember when we first started this   i had us create a interface test actor  so this is completely untouched still   so now we want to be able to test our  interaction interface and we want to be able to   see you know how do we implement this stuff  moving forward if we want to make a custom actor   right because if you if you want to experiment you  know you can feel free to add more uh actors and   stuff that implement this so it's pretty simple  open up the header for the interface test actor   and right here where we inherit from a actor   we just put a comma and it's going to be  public i interaction interface that we created now it should again it auto included this for  me but if that doesn't happen for you which   i assume it won't make sure you include  the header for our interaction interface and then we would just simply come down here now and  override the functions that the interface   you know declares so i'm going to go generate  overriding members now in writer this is this   is really cool if it inherits from multiple  things you have starting out a big list here   you can click this button to group and now look  everything like engine related is down here but   here's my interface so i have begin and end focus  and then i have all of the other ones too right now it puts them in the header  here but it doesn't implement them   so i'm going to go back and say  generate definitions all of that all right it looks good so this is default stuff  you don't need anything inside of these yet it just kind of assumes that it's  going to make a call to a parent class all right so how are we going to do this so  what we're going to need is we're going to   need a mesh on this actor because we want to be  able to test the highlighting right the begin   focus and the in focus so we need to come over to  the header file and we're going to add a static   mesh component so i'm not going to bother with  cleaning this up too much just because it's a   just a test but i am going to at least  kind of delete some of the redundant uh public private stuff  public protected specifiers now if we would we can put this in protected call it mesh it's going to be a u property  because we're going to edit this in the editor   it can be added anywhere uh you know test actor yeah that's fine for a  category okay so all we really need is this mesh   because the way line traces work is they trigger  when they they trigger a blocking hit you know for   to say okay i've hit something that triggers off  of a collision volume and we're just going to use   like one of the built-in cubes or something  in the engine and those all have collision   volumes already and so then what we want to do is  highlight the object so to do that it's a little   bit there's something a little more unique and  it involves the mesh so first what we want to do come into the constructor and we're  going to say mesh is equal to create   default sub object it is a u-static mesh component and for the this is just the  name we can just call it mesh then we want to set root component to mesh so that's done now we will essentially the  the point of the when you the move widget and   everything it's going to consider that to be you  know the the root object of this whole actor to be   that mesh so that's what we want now here in begin  focus we're going to say okay if our mesh is valid   and this is really not too important in this  actor because we know we're going to set it but   this could be more we'll do this again in  the pickup excuse me in the pickup class   where it might be you know there might  be a reason why it doesn't get the mesh   off the data table because that's where we're  going to be pulling this from on the pickups we want to call a function called set   render custom depth and it just takes in  a boolean so we're going to set it to true   likewise in the in focus we're going  to set it to false so just copy that   now what this is i'm going to have to  show this when we get into the editor   we're going to configure it there is a essentially  a render depth i think it's either like a buffer   or a layer this is a little bit beyond me  when it comes to like these rendering layers   but it's called custom because it allows you to  set custom effects and custom kind of things that   happen on that layer and so what we're going to do  is i have some materials that are already pre-made   that will highlight the bounds of what you can see  on a mesh and when you turn this layer on it sees   that material and it applies it over top of the  mesh to you know create the highlighting effect i'll talk about that more when we get back into  the editor so the only thing left to do is so   this will be easily this this is how we easily see  okay is the focus working if it highlights if it   if it unhighlights for begin interact we just put  some logs in here for now we'll just put some some log temp warning and we can just say calling   begin interact override on test  actor on interface test after do the same thing down here for these guys calling end interact and then calling  interact right because it doesn't   really matter what this is doing we  just want to see something happening i think that should be it it's very basic  again this is just a test actor we're going   to delete this later on so i'm going to  go ahead and build and reopen the editor   okay i've got the editor back open now i'm going  to go in here and you'll see in my i have a   folder i called materials and it's got these two  materials this outline custom depth okay and so   what these do is this is something  that i not this one this one i am not   knowledgeable about the way these kind  of it's like essentially shader math   this thing is massive i found this in another  uh course or in another project somewhere and i   basically stole it but the i believe you know from  from my experience in unreal engine this is kind   of us a common implementation of the of how this  is done i've watched a video about it and there's   videos out there that will teach you how to build  this but what it does is it kind of you can see   there's like these labels they put for like top  left bottom it looks at the the extent of the   space on the screen that this  actor takes up or the mesh takes up   and then it allows it to kind of mask  that out and then apply some stuff to that   one of the main things being an outline  and a color here and so if you want to   change the color of your highlighting you  can actually come in here and tweak this   but now how do we apply this so what we need  to do we need to find the world settings okay so i pause for a minute there because it's  been a little while since i've tweaked this and   i kind of forgot it's not under world settings  at all it's under the post process volume right   because this stuff is considered post-processing  so this is just it just sits in your level and uh   it can be set to where's it at there's a way  to set this thing oh here are details okay   so there's like an extent setting for a world  of post-process volume to basically be infinite   and that's what they've done here because you can  customize it right you can have a specific area   get post-processing in which case it would just  be the contents of the cube or you can set it   to unbounded and then in that at that point it  covers the entire game world with just you know   no matter how big it is and so i was trying to  find that and there's just so much stuff in here yeah so you can i'm just filtering it so  it's under the volume settings infinite   extent unbounded right so whether this covers the  whole world or just the area inside so obviously   by default that is checked now what we want  to find is the custom oh man all right hold on all right it's under rendering features post  process materials and then there's an array here   okay so what we want to do is we want to add  an asset reference and for that asset i don't   remember why these things were called pp at the  front it's probably just a naming convention for   something to do with shaders but we want  to add in the custom depth instance okay   now save that now let's create a blueprint  off of our uh interface test actor create blueprint class based on this and  i'm going to put it under my blueprints   so bp interface test actor right so  it opens up and it's completely empty   except we have a mesh and so we will just put in  oh yeah i added in those infinity blade assets   yeah let's use an armor suit that's cool so now i'm going to open up and find that drag it in make sure that it looks okay   all right and now let's try this out so now  what we expect he's a little short ending there we go now what we expect to happen is once this line  trace hits this it will highlight it did not work   so let me sort this out really quick okay so i've sorted it out and the mistake  i made was i chose the wrong in material so   do not choose the material instance choose the  material itself and i i forgot to mention this   i'm going to put these on in google drive and make  them available to download is in the description   of this video so i'm sorry i should have mentioned  that a bit earlier so now when we look you can see   we get the nice highlight as if this were an  interactable so the render depth the customer   interdepth is working well and thanks to whoever  it was that built that shader because you saved   a huge amount of work for many people now we  i put those logs in there because i wanted to   test the interaction but now how do we do that  well another sort of mistake i made not really   a bad mistake just being a little hasty uh let  me save my world here i'm closing the editor   um we have functions for interacting but we have  no way to actually interact we have no button or   anything right to interact and the reason  is because i didn't actually bind anything   so what i'm going to do we're going to add some  bindings here so we're going to bind an action because it's just going to be like a single  key with a press and release similar to jump   this is going to be interact when it is pressed we want to call a cs tutorials character not  interact but begin interact   and likewise copy this and now change this  to released and you guessed it in interact okay so writer's kind of just trying to be  helpful here it's like hey this isn't bound   so we're going to relaunch the editor and bind  that because that is something you bind in the   editor uh kind of on the fly and it doesn't  require compilation so i'm going to relaunch   okay so now we're in here we want to have that  button bound so you go to edit project settings   over on the left come down  to engine input right here   and now you can see we've got all of our different  stuff that came in this project by default which   is just again the default third person project so  action mappings add a new one interact and now you   can press this and just hit the key you want or  you can choose it from a giant list so i'm just   going to hit this e now i'm okay with using e you  can add another one here and say you want to bind   it to like your gamepad or something if you want  to play this with a controller this is where you   know you'd add two because if you see they did  that here right they have a gamepad binding   and then two keyboard bindings uh for forward  and backward and then it uses the y-axis on this   on the thumb stick so if you want multiple things  or just a different key change that but otherwise   this is like linked to like um like an xml or  something in the background so all you do is   when you add something here and kind of like click  away it's saved so all you gotta do is close it   now what we expect to see right down here  in the log when i highlight this and hit e there you go that is the interface in action  we called begin interact followed by interact   and then lastly end when i let go  of the key so now if i hold the key   right you see we've called begin and  then interact and then i let go end   because we only call the end function  when the key is on the release trigger so there we go so i think that wraps up the crucial  parts of the interaction interface   now what's really great about this now getting  this out of the way is that we can take this   we're going to add a few refinements here  and there but for the most part this is done   and now this opens the way to building  the inventory itself followed by pickups   and that's what's really important about  the interaction is that we can now walk   up to a pickup we can see a highlight we can  press e on it and run custom functionality   that will allow the pickup to be added to the  inventory and removed from the world and then   likewise we can you know later on drag it out  throw it back into the world pick it up again you   know the whole the whole nine yards so that wraps  up this whole interaction uh interface section   and we'll pick it back up in the next video with  the inventory of starting out with the inventory
Info
Channel: George Pence
Views: 8,326
Rating: undefined out of 5
Keywords:
Id: elHUISqrZD4
Channel Id: undefined
Length: 100min 3sec (6003 seconds)
Published: Thu May 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.