Scribe
Scribe

ชอบมันไหม? ทำให้ Scribe ดียิ่งขึ้นโดย การให้คะแนน

รับส่วนขยาย Chrome

เรียกดู

  • วิดีโอยอดนิยม
  • วิดีโอล่าสุด
  • ช่องทั้งหมด

เครื่องมือฟรี

  • ตัวดาวน์โหลดคำบรรยาย
  • ตัวสร้างเวลา
  • ตัวสรุปวิดีโอ
  • ตัวนับคำ
  • ตัววิเคราะห์ชื่อเรื่อง
  • ค้นหาบทถอดความวิดีโอ
  • การวิเคราะห์วิดีโอ
  • ตัวสร้างบท
  • ตัวสร้างแบบทดสอบ
  • แชทกับวิดีโอ

ผลิตภัณฑ์

  • ราคา
  • บล็อก
  • รับส่วนขยาย Chrome

Developers

  • Transcript API
  • API Documentation

กฎหมาย

  • ข้อกำหนด
  • ความเป็นส่วนตัว
  • การสนับสนุน
  • แผนผังเว็บไซต์

ลิขสิทธิ์ © 2026 สร้างด้วยความรักโดย Scribe

— ถ้านี่ทำให้ชีวิตของคุณง่ายขึ้น (หรืออย่างน้อยก็วุ่นวายน้อยลง) กรุณาให้คะแนนเรา! เราสัญญาว่ามันจะทำให้วันของเราดีขึ้น 😊

Related Videos

Game Engine Programming 016 - C scripting and script component | C Game Engine

Video thumbnail
11.89k5,670 คำ28m readGrade 18
แชร์
Channel
Game Engine Series
hello everyone and welcome back to the game engine programming series where we write a game engine from scratch up until now we have been working mainly on the basic implementation of the entities and components in both the game engine as well as the level editor so now we have game entities that can have different kinds of components as of today i would like to start exploring a different topic and that's how we could write game code and use it together with the game engine to create a final game [Music] this is you and this is
your brain your brain is where your consciousness resides without it you wouldn't experience existence without your brain you wouldn't exist therefore you could say that your brain is you we could turn this into an analogy and apply it to the entities and their scripts since the script acts as an entity's brain and defines its behavior from the script's point of view the script is the entity in object-oriented programming whenever we are talking about an is a relationship it often corresponds to an inheritance hierarchy now remembering that we expose the entities to the game code via
the entity class it would be an obvious choice to inherit from this class and construct a base class for all the entity scripts that the game programmer will be writing so the entity class will be the base for the entity script which will be the base for all other script classes and together all script classes will form the game code these script classes act as a data for the script components of the entities in the engine whenever we are adding an entity it will try and create all these components and if one of its components
is a script component that means that we also have to create one of these entity scripts and to do that we need two pieces of information first we need to know what entity this script belongs to and second we need a creation function that would create an instance of the script class because the script component doesn't know anything about the script class we need this creation function that will allocate memory and create an instance of that particular script class to recap an entity will have script components and each script component will have a pointer to
an instance of the game code script somewhere in memory now let's see what this looks like in code since we need to inherit from the entity class i'll just create a new namespace here and add a new entityscript class in this header i'll give this class two virtual functions one that will be called whenever the game starts and one that will be called each frame to update the script the update function will get a parameter that is a floating point value and that's going to be the time that was passed since the last frame basically
this is just the frame rate in milliseconds or in seconds and like all good c plus plus citizens would do when they have virtual functions we make the destructor also virtual and public and since this is the base class and we don't want to instantiate this i'll make the constructor protect it so it can't be accessed by anything outside the class hierarchy so here we have the first piece of information that we need to create an instance of a script which is what entity it belongs to and since we are inheriting from the entity class
itself we can just set its id an example of an entity class that the game programmer would write is something like this so in order for the script component to be able to instantiate this class which it doesn't know about it will need a creation function and the creation function could be something like the following this function will create an instance of the script class that we give it in the template parameter and it will return a pointer to that instance so i'll have to define what this script pointer exactly is so this script pointer
is simply a unique pointer of type entity script and since our game code classes inherit from entityscript we can use this pointer to point to any of these classes i need to include a header to be able to use these kind of smart pointers and because i need to give the function pointer as a parameter to the script component i'll have to define a type that's basically a function pointer now here i can create an instance of whatever this script class is by calling make unique and since i don't want to expose this stuff explicitly
to the game codes i just put it in a detailed namespace and then i can remove this example because we don't need this and now we are ready to add the script component to the list of components that we have for a game entity so exactly like i did for transform i'll add the script component here and i can copy and paste whatever we have here for the transform because it's almost identical so this is the component class and for the in engine components i'll add a header and scpp file just like we did again
for transform again i can repeat everything that i did for transform here for the script i'm also going to rename these functions to make them simpler and while we are here i can already add a new component to the entity for the script and i can make sure that whenever an entity is created also the script if it has one will be created i of course need to include the header for the script and here after i create a transform component i'll check if this entity has a script component and if so i'll create it
here we also need to define what this init info exactly is and that's just the second piece of information that we need and that's the function pointer to the creation function for the script class so if this script initialization info is not known and it also has a valid function pointer in it then we can create a script here i answered that the slot for this script index doesn't already have a valid index in it in other words it doesn't already contain a component then i'll call createscript with this information and our new entity and
finally i check that the script now has a valid id in this slot and now that we can add a script component we can also add a member function in the entity class that will get that component and like we did for transform i can implement this function in the translation unit for the entities i would also like to simplify these parameters a bit by just giving these functions an entity id and since this case that the entity is not alive when we call this function never should occur i don't need to have a check
for it here because it's just an error okay so now the only errors that we are left with are because of the fact that we still have to implement the create and remove functions for the script component so we can do that next here i'll add a cpp file for the script component so here as we did for the entity class i'll have the list of data for this component and the generation array and the available free ids for any component that we already have removed so the way we did this for the entity is
to use an array with holes and whenever we remove components or we remove an entity we leave holes in this array and whenever we add a new entity we can reuse those hosts to place a new entity and their components in them but i don't want to do that for a script for the script i would like to do something that's called double indexing and that's just to keep this array compact in other words it won't have any holes in it now if we were to use an array with hose like this then we would
have to check for each slot whether it has a valid pointer to an instance of a script class and that would result in a code like this where we go through all scripts one by one and check if that script in that slot is not a null pointer and if that's not the case then we can call its update function and although this works just fine it has two major disadvantages which is cache misses and branch mispredictions cache misses are caused by the fact that the cpu reads a limited amount of memory each time it
needs some data so for example these four slots would be fetched by the cpu and only two of those contain useful information whereas if we had an array without holes then we had four pointers already which is twice as much in this case and each time a cache miss occurs that means that the cpu has to go to the main memory and fetch more data which is slower if you'd like to know more about cpu cache and memory then the following paper might be useful to you it's called what every programmer should know about memory
and if you want to know everything about cache and how the cpu memory works for modern architectures and you really need to read this paper it explains everything that has to do with accessing memory efficiently for software applications i'll provide the link to this paper below in the description of the video so feel free to check that out and the second disadvantage is branch mispredictions because there doesn't have to be a regularity in what slots contain valid pointers the cpu will often be unable to predict if this branch will be taken or not if the
cpu predicts that this branch should be taken so this function is called and the cpu is running this in a pipeline and it turns out that that wasn't a good prediction then all that work needs to be thrown away well obviously in this case the update function can't be called if the script is a null pointer but there are more things to be done before a function call like pushing parameters onto stack which the cpu might already have been doing so to mitigate these disadvantages i'll have an script array that's separate from this array with
holes and of course the scripts are not components the scripts are game code so keep this in mind and i'll keep the array of script pointers a tight array so there aren't any empty slots in here and the way we keep track of things is to use this array as an id mapping to the array of script pointers so these are tightly packed and we just point to these script pointers and the script ids are indices into this id mapping so here will be script id 0 and script id 2 etc and for this id
mapping we will of course use this generational ids that we used for game entities as well now how do we make sure that this array stays tightly packed for example if we want to remove one of these scripts because the entity that has that script is being removed so suppose that we want to remove this one if you would remove this here in place then there would be an empty spot in here and that's not what we want so what we want to do is to swap these two pointers so we swap this triangle script
pointer with the last one next we delete the triangle script and after that we only need to update these indices like so and now we are left with again a tightly packed array of script pointers and an id mapping that tells us for what script id what script pointer should be used now we have id mapping and script ids and that's why we call this double indexing okay let's implement this in code here i'll have another vector for our id mapping now i'm going to do the same thing that we did for entity where we
add a new entity here we check if the number of three ids exceeded a certain number that is determined by this constant and if so we will reuse the three ids and otherwise we'll add new slots at the end of our arrays so when reusing three ids i'll get one from the front of the three ids and then assert that that particular script pointer doesn't exist so it shouldn't point to a valid pointer and then remove it from the three ids because i'm going to use it increase its generation because we are reusing it in
that same slot so its generation should be increased and also in generations array we increase the generation for that part so we can compare them later on if we want to access that script id now we need a new function that helps us check if an script id belongs to an existing script component and i'll put that one here here in this function i assert that the id is valid then i'll get the index part of it so we can access these arrays by this index and then i can return whether the generations agree so
we are talking about the same generation of id and if so whether the entity script slot that this id mapping points to has a pointer that's not null because this is a pointer to an entity script and if we look at our entity script we see that it inherits the is valid function from entity class and in this way we can check if this entity script is a valid entity so in the case that we can't reuse any free ids either because the free ids is empty or it contains less items than minimum deleted elements
we have to add new slots at the end of the arrays in that case i'll generate a new id so this will add another empty id at the end of id mapping and also at the end of generations array now we are done with getting a new id we can check if the id is valid and now we can call the script creator from this init information to create a new instance of the script class so this function will create an instance for us and we add it to the end of entity scripts by using
this employees back and then i check if the id of that script class is the same as the id of the entity that it belongs to which should always be true of course and finally we need to know the position where this new entity script was added which is of course the end of the entity script array so we can point to it in the id mapping array so this is all we have to do to create a new instance of an entityscript class and use it in the script component now we can also implement
the remove function okay here comes the tricky part of swapping the pointers and modifying the indices in the id mapping to swap two elements in a vector i'm going to write a new method in utilities here i can write a function that will swap two elements in a vector and delete the last element from the vector i'll call it erase unordered because it doesn't preserve the order of elements in a vector when we erase one of them so if the vector contains two or more elements then we will swap the element at this index with
the last element and then delete the last element by calling pop back and if the vector is empty or just has one element then we just clear it and then i can use this function to do that swap and delete action that i want here okay and now we need to reset the indices in id mapping so we point to the right pointer in entity scripts array so remember that we need to set the id mapping of the script class that we are deleting to an invalid id like we did here this was originally pointing
to the second element in scripts so now we set it to an invalid id and the last step that we need to do is to set this new index that originally pointed to the last element to the new position for that i need to know what the index was that was pointing to the last element in the entity scripts and of course i don't know that i don't know what slot in id mapping was pointing to what we just moved from the end of entity scripts to the new position but i can figure out what
that was by just asking that particular script because it contains that information so before we swap erase that entity script we ask the one that's at the end of entityscripts array what its script id is and that's exactly the value that points to the slot in id mapping and now we can just set it here and we of course need to set this before this one because in the case that we only have one script or script component then we set this index which is valid but then because we only have one script and we
removed it we should override that valid index now with an invalid one okay it's still giving me unresolved externals so i need to figure out what's going wrong here because we have implemented these two methods which are in primal script namespace and we have this oh wait it has another kind of signature yes these signatures don't match so i need to make them the same remove the constant modifier and the reference and it will be just fine okay if all this works correctly now we are able to create instances of our script classes and add
it as the script component to the entities okay before we go any further i need to just quickly change the signatures for these functions as well just to be consistent okay so now that we have the basics set up for writing game code we can actually try and create a game project and see if we can actually build a game with the game engine that we have because we have a game engine and we can write game code for it right now so let's try and see what it takes to create a game project and
then i'll write down everything that we need to do to have a project that we can build at least with visual studio and then try and automate that using our level editor later on to set up things automatically okay wonderful let's create a brand new visual studio solution create new projects and it is of course a c plus plus project and i'll just create an empty one okay so the first thing we need to do obviously is to generate a solution msvc solution slash project so get rid of these filters all right and the next
thing i need to do is to add files that contain script classes or basically the game code okay so now i added two files a header file and a cpp file for our hobbit hippot for our habit for our hypothetical player character and now i would like to actually access the entityscript base class and inherit from that of course i'll have a namespace for my game so the second item that we write down is that we should be able to add files to the game project and now i see of course i can't access this
primal because this project doesn't know anything about our engine so first i need to go to the properties and set a path to engine in order to be able to access anything that we need so we need an additional include directory and another directory that points to where the dot lib files for the engine are for each configuration if you are building a debug configuration we should of course use a debug lib and if you are using release configuration we use the release build for the library but before that i need to again get rid
of this x86 or win32 platform so it doesn't bother us anymore okay then i'll just type in here where the include directory for our engine api is let's double check if that is correct primal engine engine api that's good and the location of the library is just in primal and a platform name and then the configuration name so hit apply here here in additional library directories i'll just type in z primal and then we will use whatever platform we are using which is x64 of course and then the configuration name and in additional dependencies we
of course need to say that it should use engine.lib so here i can edit and now here i can include the game entity header and that way we have now access to primal namespace and everything in it but of course i don't want to add this includes manually to each script header that i create for that we can use something that's called force header file and it's in the c plus plus the compiler options and here we can use a file that's automatically included in all files that we add to our project here i can
say include game entity header in all files okay now i can remove this include and it's still okay let's write down again what we just did okay let's try to compile this or build it so we get an error that it can't find common headers.h and that is because we didn't use the right path in the engine so if we go back to this file components common it just uses the name of the header but we really need to actually say where it is now if we go back to our game project and build again
then it will find everything but we of course forgot to set the language version so we need to set that as well here again set the calling convention to fast call and the language standards to c 17 press apply and see what happens now all right that's a good sign that means that everything went right except that we don't have the main function that every executable should have because we need an entry point so let's write down again the last step that we did okay in order to solve this problem i'll just add an entry
point in engine because i don't want to bother with the main function in the game project in the game project we are only concerned with writing code for the game so in the engine project here i'll add a new folder and there i'll create a new file for the main function and for now i don't really need to do anything here i just create a function okay going back to the game project now and now if i build it then it will just use that main function and we have a complete game executable here in
the game project in the output directory debug and we see that we have the first game project and we can sort it but of course it doesn't do anything but at least we have made our first game the very first game with our engine so i've never been more excited about an application that does absolutely nothing but hey this is our first game project hooray alright so yeah that's that's all we have to do to make a game project at least a game executable uh but then we also have to let the engine know what
classes we have here and how to add those classes as the script component in our game entities so to do that i'll just make some steps to let our engine know what classes we have here so we want to let the engine know what the name of this class is and also remember the creation function that is needed to create an instance of this class so what i wanted to do is something like register this script we need a function that will allow us to register this script and it will take two parameters one is
a name and the other one is the creation function the creation function we already can do like so but unless we put this line after the class definition it doesn't know what this character script is so if you want to put it on top i need to forward declare this class and that's it so because i don't really want to use strings in this engine i'll have the name of this class converted to a hashtag which is just an integer and i can do that by using the string hash this will be a function that
accepts an integer and a creation function so we need to implement this function to register our scripts and because this is and static initialization that happens before everything else starts i need to actually make this happen by just returning a boolean so when we start the application the game application are our game executable this static variable is initialized and therefore this function is called and our script will be registered in the engine because this is a function that we will implement in engine and for good measure i just put this in an anonymous namespace as
well and now in our cpp file we can just implement this character class so because the constructor of this base class is protected we need to implement our own constructor in order to be able to instantiate this class and we can also override the virtual functions if we need to implement our own for example here i can override the updates function like this and the only thing we need to do is to register this script so let's go back to the game engine and implement this function let's close this and go back to the game
entity so here i'll just add a new function that returns a byte and its register script and then i can implement this in script cpp file here in detail namespace i can make the implementation so the way i would like to register a script is to use an unordered map from the stl the standard template library of c plus and then i can use this tag as the key and the script creator as the value so i can look up script creators by using the tag so i need an unordered map which i need to
include in common headers i don't think i can use it right away here let's try first no so i need to include that here first and then i can use this script registry to have my unordered map here but remember that this register script is called whenever this variable is going to be initialized and the order of initialization is not known beforehand so we could be initializing this variable before this one and then when we would use this unordered map before it has been constructed then we would get an error or our program would crash
therefore i'll put this in a function that will be called and whenever this function is called then this variable will be initialized so there is no way that the initialization order would be wrong now i can use this function to access the registry in register script and then add new key value pairs to it so here we get a registry and we add a pair that is a tag and a function pointer which is the script creator and then it returns a pair in which the second member variable is a boolean that indicates whether this
insertion succeeded or failed and then we return the result and here in our game project now we can call this function and we should use the right namespace of course okay although it says that it can't find the register script that's visual cdo being a bit confused but it can build the project now next thing i would like to do is to actually make sure that i don't have to type all of this every time i add a new script so i can put this into a macro and then just do something like register script
using this name and we would be done so as a last thing we do today i'm going to create this macro that allows us to register a script by using a single line of code so let's move this to the engine this will do the trick i need to move this again as well and then we are done okay if you ever make macros in c plus plus make sure that when you use these slashes to make multiple line macros that after those slashes there is no white space because otherwise it will give you weird
errors that you don't know what to do with and then you can look for what's wrong with it until the cows come home i see i have here a couple of spaces after this slash and if i remove those then everything should be okay and i would like to replace this with a type name as well so i'll just use my own type so going back to our fantastic game project now i can probably use this macro of course i need to register the character script not the register script so that was a typo there
there you go all right now if i would step through the code then this class would get registered in engine to check if that's true i just set a breakpoint in primal in the game engine so going back to the script i can just set a breakpoint here and see if this function is called okay i don't see that happening but it should so let's see what is going on i can throw this away well of course i have to set the breakpoints here in this project so in this instance of visual studio and not
in the other one that is for the engine code go back to the engine and then i can open this in the game project so open containing folder and then in the game project i can open this file and here i can set a breakpoint and now if i run the project then we will hit this breakpoint so it probably works correctly you see our tag that is a size t integer that's the name of the script class that we just made and the creation function that is saying that we use create script for first
game project character script which has as its parameter and entity class so that works great now i can remember these these are the items that we need to do next time in order to automate this whole process that we just did so i'm going to put this somewhere in the code for the engine maybe here okay we are done for today so let's do a recap today we created a base class for our game script classes which is this entity script it inherits from the entity and we can use it to update the behavior of
entity each frame and then in our game project we can inherit again from that base class and implement the behavior for that particular game entity and we made it possible to register this class in the engine using this macro that will add the creation function for that script and use its name as a hash in order to be able to look that creation function up later on when we want to create instances of that class so this is a code for the game basically and we also added the script component to keep track of that
code so we have this array of script class instances and we use double indexing to make sure that this array is tightly packed and the rest of it is basically the same as we did for the game entity which is just to add new items and remove them so thank you for watching i'm uploading videos every thursday so make sure to click subscribe and hit the bell button to get notified as soon as the videos come out and i'll see you next time thanks for watching if you like this video please feel free to like
and subscribe if you join me on patreon you'll get access to the code on github so you don't have to type everything over from the video plus there are also other nice goodies and rewards exclusive to my patreon supporters please use the link in the video description to check them out i hope to see you next time until then take care and happy game engineering [Music] you
วิดีโอที่เกี่ยวข้อง
Game Engine Programming 017 - Generating the Visual Studio solution for game code | C++ Game Engine
46:27
Game Engine Programming 017 - Generating t...
Game Engine Series
4,704 views
C# Scripting! // Game Engine series
2:56:01
C# Scripting! // Game Engine series
Cherno Unplugged
37,665 views
AI Is Making You An Illiterate Programmer
27:22
AI Is Making You An Illiterate Programmer
ThePrimeTime
239,996 views
Better Java Streams with Gatherers - JEP Cafe #23
1:13:32
Better Java Streams with Gatherers - JEP C...
Java
30,776 views
Native Scripting | Game Engine series
36:32
Native Scripting | Game Engine series
The Cherno
49,456 views
Programming Perlin-like Noise (C++)
27:54
Programming Perlin-like Noise (C++)
javidx9
106,137 views
Linus Torvalds on the kernel, GenAI, EVs, programming languages and more…
49:59
Linus Torvalds on the kernel, GenAI, EVs, ...
TFiR
58,341 views
LEARN UNITY - The Most BASIC TUTORIAL I'll Ever Make
2:04:31
LEARN UNITY - The Most BASIC TUTORIAL I'll...
Imphenzia
3,212,883 views
Rant: Entity systems and the Rust borrow checker ... or something.
1:01:51
Rant: Entity systems and the Rust borrow c...
Jonathan Blow
246,766 views
Game Engine Programming 009 - Identifiers with index and generation parts | C++ Game Engine
1:11:23
Game Engine Programming 009 - Identifiers ...
Game Engine Series
14,974 views
Native Scripting (now with virtual functions!) | Game Engine series
18:42
Native Scripting (now with virtual functio...
The Cherno
26,095 views
Bjarne Stroustrup: C++ | Lex Fridman Podcast #48
1:47:13
Bjarne Stroustrup: C++ | Lex Fridman Podca...
Lex Fridman
1,091,020 views
The Art of Code - Dylan Beattie
1:00:49
The Art of Code - Dylan Beattie
NDC Conferences
4,792,251 views
CppCon 2014: Mike Acton "Data-Oriented Design and C++"
1:27:46
CppCon 2014: Mike Acton "Data-Oriented Des...
CppCon
713,416 views
How To Speak Fluently In English About Almost Anything
1:49:55
How To Speak Fluently In English About Alm...
EnglishAnyone
3,387,095 views
I made my game engine
5:28
I made my game engine
Benjamin Blodgett
31,541 views
The V-22 Osprey and why it keeps crashing
24:02
The V-22 Osprey and why it keeps crashing
Real Engineering
368,438 views
Dr. Chuck reads C Programming (the classic book by Kernigan and Ritchie)
9:38:35
Dr. Chuck reads C Programming (the classic...
freeCodeCamp.org
618,899 views
Game Engine Programming 010 - Entity and component implementation in C++ | C++ Game Engine
55:01
Game Engine Programming 010 - Entity and c...
Game Engine Series
10,111 views
Clean Code - Uncle Bob / Lesson 1
1:48:42
Clean Code - Uncle Bob / Lesson 1
UnityCoin
2,025,610 views